Skip to content

Commit 2d9f660

Browse files
authored
Migrate dump_attributes command to new tool (#566)
### TL;DR Migrated command to dump attributes to YAML from deprecated tool version to new one. ### What changed? - Added new `dump_attributes` CLI command - Migrated `DumpAttributesCommand` - Implemented `DataSerializer` for attributes that handles both base and extended attributes - Base attributes are sorted by ID, while extended attributes maintain their addition order ### How to test? Run the CLI command: `bin/product_taxonomy dump_attributes` Sample output: ``` ❯ bin/product_taxonomy dump_attributes Dumping attributes... Updated `/Users/danielgross/product-taxonomy/data/attributes.yml` Completed in 1.07 seconds ```
2 parents 4608c38 + 4190c2d commit 2d9f660

File tree

6 files changed

+282
-0
lines changed

6 files changed

+282
-0
lines changed

dev/lib/product_taxonomy.rb

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def data_path = DATA_PATH
3232
require_relative "product_taxonomy/models/serializers/category/docs/search_serializer"
3333
require_relative "product_taxonomy/models/serializers/category/dist/json_serializer"
3434
require_relative "product_taxonomy/models/serializers/category/dist/txt_serializer"
35+
require_relative "product_taxonomy/models/serializers/attribute/data/data_serializer"
3536
require_relative "product_taxonomy/models/serializers/attribute/data/localizations_serializer"
3637
require_relative "product_taxonomy/models/serializers/attribute/docs/base_and_extended_serializer"
3738
require_relative "product_taxonomy/models/serializers/attribute/docs/reversed_serializer"
@@ -47,5 +48,6 @@ def data_path = DATA_PATH
4748
require_relative "product_taxonomy/commands/generate_docs_command"
4849
require_relative "product_taxonomy/commands/generate_release_command"
4950
require_relative "product_taxonomy/commands/dump_categories_command"
51+
require_relative "product_taxonomy/commands/dump_attributes_command"
5052
require_relative "product_taxonomy/commands/sync_en_localizations_command"
5153
require_relative "product_taxonomy/commands/add_category_command"

dev/lib/product_taxonomy/cli.rb

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ def dump_categories
4646
DumpCategoriesCommand.new(options).run
4747
end
4848

49+
desc "dump_attributes", "Dump attributes to YAML file"
50+
def dump_attributes
51+
DumpAttributesCommand.new(options).run
52+
end
53+
4954
desc "sync_en_localizations", "Sync English localizations for categories, attributes, and values"
5055
option :targets, type: :string, desc: "List of targets to sync. Valid targets are: categories, attributes, values"
5156
def sync_en_localizations
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
module ProductTaxonomy
4+
class DumpAttributesCommand < Command
5+
def execute
6+
logger.info("Dumping attributes...")
7+
8+
load_taxonomy
9+
10+
path = File.expand_path("attributes.yml", ProductTaxonomy.data_path)
11+
FileUtils.mkdir_p(File.dirname(path))
12+
13+
data = Serializers::Attribute::Data::DataSerializer.serialize_all
14+
File.write(path, YAML.dump(data, line_width: -1))
15+
16+
logger.info("Updated `#{path}`")
17+
end
18+
end
19+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
module ProductTaxonomy
4+
module Serializers
5+
module Attribute
6+
module Data
7+
module DataSerializer
8+
class << self
9+
def serialize_all
10+
# Base attributes are sorted by ID, extended attributes are sorted by the order they were added
11+
extended_attributes, base_attributes = ProductTaxonomy::Attribute.all.partition(&:extended?)
12+
base_attributes.sort_by!(&:id)
13+
14+
{
15+
"base_attributes" => base_attributes.map { serialize(_1) },
16+
"extended_attributes" => extended_attributes.map { serialize(_1) },
17+
}
18+
end
19+
20+
# @param [Attribute] attribute
21+
# @return [Hash]
22+
def serialize(attribute)
23+
if attribute.extended?
24+
{
25+
"name" => attribute.name,
26+
"handle" => attribute.handle,
27+
"description" => attribute.description,
28+
"friendly_id" => attribute.friendly_id,
29+
"values_from" => attribute.values_from.friendly_id,
30+
}
31+
else
32+
{
33+
"id" => attribute.id,
34+
"name" => attribute.name,
35+
"description" => attribute.description,
36+
"friendly_id" => attribute.friendly_id,
37+
"handle" => attribute.handle,
38+
"sorting" => attribute.manually_sorted? ? "custom" : nil,
39+
"values" => attribute.sorted_values.map(&:friendly_id),
40+
}.compact
41+
end
42+
end
43+
end
44+
end
45+
end
46+
end
47+
end
48+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
require "tmpdir"
5+
6+
module ProductTaxonomy
7+
class DumpAttributesCommandTest < TestCase
8+
setup do
9+
@tmp_base_path = Dir.mktmpdir
10+
@real_base_path = File.expand_path("..", ProductTaxonomy.data_path)
11+
12+
FileUtils.mkdir_p(File.expand_path("data", @tmp_base_path))
13+
ProductTaxonomy.stubs(:data_path).returns(File.expand_path("data", @tmp_base_path))
14+
15+
Command.any_instance.stubs(:load_taxonomy)
16+
end
17+
18+
teardown do
19+
FileUtils.remove_entry(@tmp_base_path)
20+
end
21+
22+
test "execute dumps attributes to YAML file" do
23+
mock_data = { "test" => "data" }
24+
Serializers::Attribute::Data::DataSerializer.stubs(:serialize_all)
25+
.returns(mock_data)
26+
27+
command = DumpAttributesCommand.new({})
28+
command.execute
29+
30+
expected_path = File.expand_path("data/attributes.yml", @tmp_base_path)
31+
assert File.exist?(expected_path)
32+
assert_equal "---\ntest: data\n", File.read(expected_path)
33+
end
34+
end
35+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# frozen_string_literal: true
2+
3+
require "test_helper"
4+
5+
module ProductTaxonomy
6+
module Serializers
7+
module Attribute
8+
module Data
9+
class DataSerializerTest < TestCase
10+
setup do
11+
@value = ProductTaxonomy::Value.new(
12+
id: 1,
13+
name: "Black",
14+
friendly_id: "color__black",
15+
handle: "color__black",
16+
)
17+
18+
@base_attribute = ProductTaxonomy::Attribute.new(
19+
id: 1,
20+
name: "Color",
21+
description: "Defines the primary color or pattern, such as blue or striped",
22+
friendly_id: "color",
23+
handle: "color",
24+
values: [@value],
25+
)
26+
27+
@extended_attribute = ProductTaxonomy::ExtendedAttribute.new(
28+
name: "Clothing Color",
29+
description: "Color of the clothing",
30+
friendly_id: "clothing_color",
31+
handle: "clothing_color",
32+
values_from: @base_attribute
33+
)
34+
35+
ProductTaxonomy::Value.add(@value)
36+
ProductTaxonomy::Attribute.add(@base_attribute)
37+
ProductTaxonomy::Attribute.add(@extended_attribute)
38+
end
39+
40+
test "serialize returns the expected data structure for base attribute" do
41+
expected = {
42+
"id" => 1,
43+
"name" => "Color",
44+
"description" => "Defines the primary color or pattern, such as blue or striped",
45+
"friendly_id" => "color",
46+
"handle" => "color",
47+
"values" => ["color__black"]
48+
}
49+
50+
assert_equal expected, DataSerializer.serialize(@base_attribute)
51+
end
52+
53+
test "serialize returns the expected data structure for extended attribute" do
54+
expected = {
55+
"name" => "Clothing Color",
56+
"handle" => "clothing_color",
57+
"description" => "Color of the clothing",
58+
"friendly_id" => "clothing_color",
59+
"values_from" => "color"
60+
}
61+
62+
assert_equal expected, DataSerializer.serialize(@extended_attribute)
63+
end
64+
65+
test "serialize_all returns all attributes in data format" do
66+
expected = {
67+
"base_attributes" => [{
68+
"id" => 1,
69+
"name" => "Color",
70+
"description" => "Defines the primary color or pattern, such as blue or striped",
71+
"friendly_id" => "color",
72+
"handle" => "color",
73+
"values" => ["color__black"]
74+
}],
75+
"extended_attributes" => [{
76+
"name" => "Clothing Color",
77+
"handle" => "clothing_color",
78+
"description" => "Color of the clothing",
79+
"friendly_id" => "clothing_color",
80+
"values_from" => "color"
81+
}]
82+
}
83+
84+
assert_equal expected, DataSerializer.serialize_all
85+
end
86+
87+
test "serialize includes sorting field when attribute is manually sorted" do
88+
@base_attribute = ProductTaxonomy::Attribute.new(
89+
id: 2,
90+
name: "Size",
91+
description: "Defines the size of the product",
92+
friendly_id: "size",
93+
handle: "size",
94+
values: [@value],
95+
is_manually_sorted: true
96+
)
97+
ProductTaxonomy::Attribute.add(@base_attribute)
98+
99+
expected = {
100+
"id" => 2,
101+
"name" => "Size",
102+
"description" => "Defines the size of the product",
103+
"friendly_id" => "size",
104+
"handle" => "size",
105+
"sorting" => "custom",
106+
"values" => ["color__black"]
107+
}
108+
109+
assert_equal expected, DataSerializer.serialize(@base_attribute)
110+
end
111+
112+
test "serialize_all sorts base attributes by ID" do
113+
second_base_attribute = ProductTaxonomy::Attribute.new(
114+
id: 2,
115+
name: "Size",
116+
description: "Defines the size of the product",
117+
friendly_id: "size",
118+
handle: "size",
119+
values: []
120+
)
121+
third_base_attribute = ProductTaxonomy::Attribute.new(
122+
id: 3,
123+
name: "Material",
124+
description: "Defines the material of the product",
125+
friendly_id: "material",
126+
handle: "material",
127+
values: []
128+
)
129+
# Add attributes in random order to ensure sorting is by ID
130+
ProductTaxonomy::Attribute.add(third_base_attribute)
131+
ProductTaxonomy::Attribute.add(second_base_attribute)
132+
133+
result = DataSerializer.serialize_all
134+
base_attributes = result["base_attributes"]
135+
136+
assert_equal 3, base_attributes.length
137+
assert_equal "color", base_attributes[0]["friendly_id"]
138+
assert_equal "size", base_attributes[1]["friendly_id"]
139+
assert_equal "material", base_attributes[2]["friendly_id"]
140+
end
141+
142+
test "serialize_all preserves extended attributes order" do
143+
second_extended_attribute = ProductTaxonomy::ExtendedAttribute.new(
144+
name: "Shoe Color",
145+
description: "Color of the shoe",
146+
friendly_id: "shoe_color",
147+
handle: "shoe_color",
148+
values_from: @base_attribute
149+
)
150+
third_extended_attribute = ProductTaxonomy::ExtendedAttribute.new(
151+
name: "Accessory Color",
152+
description: "Color of the accessory",
153+
friendly_id: "accessory_color",
154+
handle: "accessory_color",
155+
values_from: @base_attribute
156+
)
157+
# Add extended attributes in specific order
158+
ProductTaxonomy::Attribute.add(second_extended_attribute)
159+
ProductTaxonomy::Attribute.add(third_extended_attribute)
160+
161+
result = DataSerializer.serialize_all
162+
extended_attributes = result["extended_attributes"]
163+
164+
assert_equal 3, extended_attributes.length
165+
assert_equal "clothing_color", extended_attributes[0]["friendly_id"]
166+
assert_equal "shoe_color", extended_attributes[1]["friendly_id"]
167+
assert_equal "accessory_color", extended_attributes[2]["friendly_id"]
168+
end
169+
end
170+
end
171+
end
172+
end
173+
end

0 commit comments

Comments
 (0)