lutaml-store 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +11 -175
  3. data/README.adoc +233 -1124
  4. data/lib/lutaml/store/adapter/base.rb +4 -0
  5. data/lib/lutaml/store/adapter/memory.rb +8 -0
  6. data/lib/lutaml/store/cache_store.rb +9 -6
  7. data/lib/lutaml/store/format.rb +19 -0
  8. data/lib/lutaml/store/http_cache.rb +3 -13
  9. data/lib/lutaml/store/model_registration.rb +5 -2
  10. data/lib/lutaml/store/model_registry.rb +22 -20
  11. data/lib/lutaml/store/package_store.rb +2 -18
  12. data/lib/lutaml/store/package_transport/base.rb +48 -0
  13. data/lib/lutaml/store/package_transport/directory_transport.rb +196 -0
  14. data/lib/lutaml/store/package_transport/zip_transport.rb +178 -0
  15. data/lib/lutaml/store/package_transport.rb +11 -438
  16. data/lib/lutaml/store/version.rb +1 -1
  17. metadata +12 -77
  18. data/.github/workflows/main.yml +0 -27
  19. data/.gitignore +0 -12
  20. data/CORRECTED_HTTP_CACHE_IMPLEMENTATION.md +0 -209
  21. data/CORRECTED_HTTP_CACHE_PLAN.md +0 -164
  22. data/Gemfile +0 -15
  23. data/Gemfile.lock +0 -227
  24. data/TODO.impl/0-lutaml-store-self-quality.md +0 -112
  25. data/TODO.impl/1-lutaml-hal-migration.md +0 -96
  26. data/TODO.impl/2-glossarist-migration.md +0 -359
  27. data/TODO.impl/3-lutaml-jsonschema-migration.md +0 -273
  28. data/bin/console +0 -11
  29. data/bin/setup +0 -8
  30. data/demo/Gemfile +0 -15
  31. data/demo/Gemfile.lock +0 -61
  32. data/demo/README.adoc +0 -301
  33. data/demo/data/vcards/co/contact_10_thompson.data +0 -1
  34. data/demo/data/vcards/co/contact_10_thompson.meta +0 -1
  35. data/demo/data/vcards/co/contact_1_doe.data +0 -1
  36. data/demo/data/vcards/co/contact_1_doe.meta +0 -1
  37. data/demo/data/vcards/co/contact_2_smith.data +0 -1
  38. data/demo/data/vcards/co/contact_2_smith.meta +0 -1
  39. data/demo/data/vcards/co/contact_3_johnson.data +0 -1
  40. data/demo/data/vcards/co/contact_3_johnson.meta +0 -1
  41. data/demo/data/vcards/co/contact_4_garcia.data +0 -1
  42. data/demo/data/vcards/co/contact_4_garcia.meta +0 -1
  43. data/demo/data/vcards/co/contact_5_wilson.data +0 -1
  44. data/demo/data/vcards/co/contact_5_wilson.meta +0 -1
  45. data/demo/data/vcards/co/contact_6_brown.data +0 -1
  46. data/demo/data/vcards/co/contact_6_brown.meta +0 -1
  47. data/demo/data/vcards/co/contact_7_davis.data +0 -1
  48. data/demo/data/vcards/co/contact_7_davis.meta +0 -1
  49. data/demo/data/vcards/co/contact_8_anderson.data +0 -1
  50. data/demo/data/vcards/co/contact_8_anderson.meta +0 -1
  51. data/demo/data/vcards/co/contact_9_taylor.data +0 -1
  52. data/demo/data/vcards/co/contact_9_taylor.meta +0 -1
  53. data/demo/data/vcards.db +0 -0
  54. data/demo/pottery_class_demo.rb +0 -164
  55. data/demo/vcard_models.rb +0 -140
  56. data/demo/vcard_store_demo.rb +0 -526
  57. data/lutaml-store.gemspec +0 -36
  58. data/plan.adoc +0 -606
  59. data/spec/lutaml/store/adapter_interface_spec.rb +0 -89
  60. data/spec/lutaml/store/anti_pattern_guard_spec.rb +0 -35
  61. data/spec/lutaml/store/anti_pattern_spec.rb +0 -78
  62. data/spec/lutaml/store/autoload_spec.rb +0 -34
  63. data/spec/lutaml/store/cache_store_spec.rb +0 -271
  64. data/spec/lutaml/store/compression_spec.rb +0 -78
  65. data/spec/lutaml/store/config_enhanced_spec.rb +0 -158
  66. data/spec/lutaml/store/corrected_http_cache_integration_spec.rb +0 -336
  67. data/spec/lutaml/store/custom_serializer_spec.rb +0 -108
  68. data/spec/lutaml/store/database_store_spec.rb +0 -279
  69. data/spec/lutaml/store/file_io_spec.rb +0 -220
  70. data/spec/lutaml/store/format/yamls_spec.rb +0 -80
  71. data/spec/lutaml/store/format_round_trip_spec.rb +0 -110
  72. data/spec/lutaml/store/format_spec.rb +0 -70
  73. data/spec/lutaml/store/http_cache_entry_spec.rb +0 -203
  74. data/spec/lutaml/store/http_cache_hal_integration_spec.rb +0 -404
  75. data/spec/lutaml/store/http_cache_spec.rb +0 -422
  76. data/spec/lutaml/store/http_header_processor_spec.rb +0 -290
  77. data/spec/lutaml/store/import_spec.rb +0 -90
  78. data/spec/lutaml/store/integrity_spec.rb +0 -157
  79. data/spec/lutaml/store/key_collision_serializer_spec.rb +0 -98
  80. data/spec/lutaml/store/load_save_spec.rb +0 -107
  81. data/spec/lutaml/store/lutaml_model_integration_spec.rb +0 -291
  82. data/spec/lutaml/store/model_serializer_spec.rb +0 -140
  83. data/spec/lutaml/store/package_definition_spec.rb +0 -89
  84. data/spec/lutaml/store/package_store_spec.rb +0 -153
  85. data/spec/lutaml/store/package_transport/directory_transport_spec.rb +0 -139
  86. data/spec/lutaml/store/package_transport/zip_transport_spec.rb +0 -85
  87. data/spec/lutaml/store/store_spec.rb +0 -182
  88. data/spec/lutaml/store_spec.rb +0 -21
  89. data/spec/spec_helper.rb +0 -16
  90. data/spec/support/simple_test_model.rb +0 -15
  91. data/spec/support/yamls_test_model.rb +0 -35
@@ -1,139 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "support/simple_test_model"
5
-
6
- RSpec.describe Lutaml::Store::PackageTransport::DirectoryTransport do
7
- let(:transport) { described_class.new }
8
- let(:tmpdir) { Dir.mktmpdir }
9
- after { FileUtils.rm_rf(tmpdir) }
10
-
11
- let(:definition) do
12
- Lutaml::Store::PackageDefinition.new(
13
- name: :test, metadata_model: SimpleTestModel,
14
- metadata_file: "meta.yaml", metadata_key: :id
15
- ) do |pkg|
16
- pkg.model(model: SimpleTestModel, dir: "items",
17
- layout: :separate, key: :id, default_format: :yaml)
18
- pkg.asset("data.bin", type: :file)
19
- pkg.asset("images", type: :directory)
20
- end
21
- end
22
-
23
- describe "round-trip: write then read" do
24
- it "preserves models, metadata, and assets" do
25
- store = Lutaml::Store::PackageStore.new(definition)
26
- store.add_model(SimpleTestModel.new(id: "item-1", name: "First"))
27
- store.add_model(SimpleTestModel.new(id: "item-2", name: "Second"))
28
- store.metadata = SimpleTestModel.new(id: "meta", name: "Test Package")
29
- store.add_asset("data.bin", "BINARY_CONTENT")
30
- store.add_asset("images/logo.png", "PNG_BYTES")
31
-
32
- output_dir = File.join(tmpdir, "output")
33
- store.save(output_dir, transport: :directory)
34
-
35
- # Verify file structure
36
- expect(File.exist?(File.join(output_dir, "meta.yaml"))).to be true
37
- expect(File.exist?(File.join(output_dir, "items", "item-1.yaml"))).to be true
38
- expect(File.exist?(File.join(output_dir, "items", "item-2.yaml"))).to be true
39
- expect(File.exist?(File.join(output_dir, "data.bin"))).to be true
40
- expect(File.exist?(File.join(output_dir, "images", "logo.png"))).to be true
41
-
42
- # Load back
43
- loaded = Lutaml::Store::PackageStore.load(definition, output_dir,
44
- transport: :directory)
45
- expect(loaded.model_count(SimpleTestModel)).to eq(2)
46
- expect(loaded.metadata.name).to eq("Test Package")
47
- expect(loaded.asset("data.bin")).to eq("BINARY_CONTENT")
48
- expect(loaded.asset("images/logo.png")).to eq("PNG_BYTES")
49
- end
50
- end
51
-
52
- describe "missing optional entries" do
53
- it "loads successfully with empty directory" do
54
- empty_dir = File.join(tmpdir, "empty")
55
- FileUtils.mkdir_p(File.join(empty_dir, "items"))
56
- loaded = Lutaml::Store::PackageStore.load(definition, empty_dir,
57
- transport: :directory)
58
- expect(loaded.model_count(SimpleTestModel)).to eq(0)
59
- expect(loaded.metadata).to be_nil
60
- end
61
- end
62
-
63
- describe "format override" do
64
- it "writes models in JSON format when overridden" do
65
- store = Lutaml::Store::PackageStore.new(definition)
66
- store.add_model(SimpleTestModel.new(id: "item-1", name: "First"))
67
-
68
- output_dir = File.join(tmpdir, "json")
69
- store.save(output_dir, transport: :directory,
70
- formats: { SimpleTestModel => :json })
71
-
72
- files = Dir.glob(File.join(output_dir, "items", "*"))
73
- expect(files.length).to eq(1)
74
- expect(File.extname(files.first)).to eq(".json")
75
- end
76
-
77
- it "loads models saved in JSON format" do
78
- store = Lutaml::Store::PackageStore.new(definition)
79
- store.add_model(SimpleTestModel.new(id: "item-1", name: "First"))
80
-
81
- output_dir = File.join(tmpdir, "json")
82
- store.save(output_dir, transport: :directory,
83
- formats: { SimpleTestModel => :json })
84
-
85
- loaded = Lutaml::Store::PackageStore.load(definition, output_dir,
86
- transport: :directory,
87
- format: :json)
88
- expect(loaded.model_count(SimpleTestModel)).to eq(1)
89
- expect(loaded.fetch_model(SimpleTestModel, "item-1").name).to eq("First")
90
- end
91
- end
92
-
93
- describe "corrupt file handling" do
94
- it "warns and continues when a model file is corrupt" do
95
- dir = File.join(tmpdir, "corrupt")
96
- FileUtils.mkdir_p(File.join(dir, "items"))
97
- File.write(File.join(dir, "items", "good.yaml"), "---\nid: good\nname: OK\n")
98
- File.write(File.join(dir, "items", "bad.yaml"), "{{{{not yaml")
99
-
100
- expect do
101
- Lutaml::Store::PackageStore.load(definition, dir, transport: :directory)
102
- end.to output(/failed to load/).to_stderr
103
-
104
- loaded = Lutaml::Store::PackageStore.load(definition, dir,
105
- transport: :directory)
106
- expect(loaded.model_count(SimpleTestModel)).to eq(1)
107
- end
108
- end
109
-
110
- describe "dir: nil (root-level models)" do
111
- let(:root_definition) do
112
- Lutaml::Store::PackageDefinition.new(name: :root_test) do |pkg|
113
- pkg.model(model: SimpleTestModel, dir: nil,
114
- layout: :separate, key: :id, default_format: :yaml)
115
- end
116
- end
117
-
118
- it "writes models to package root" do
119
- store = Lutaml::Store::PackageStore.new(root_definition)
120
- store.add_model(SimpleTestModel.new(id: "root-1", name: "Root"))
121
-
122
- output_dir = File.join(tmpdir, "root")
123
- store.save(output_dir, transport: :directory)
124
-
125
- expect(File.exist?(File.join(output_dir, "root-1.yaml"))).to be true
126
- end
127
-
128
- it "reads models from package root" do
129
- dir = File.join(tmpdir, "root_read")
130
- FileUtils.mkdir_p(dir)
131
- File.write(File.join(dir, "abc.yaml"), "---\nid: abc\nname: Found\n")
132
-
133
- loaded = Lutaml::Store::PackageStore.load(root_definition, dir,
134
- transport: :directory)
135
- expect(loaded.model_count(SimpleTestModel)).to eq(1)
136
- expect(loaded.fetch_model(SimpleTestModel, "abc").name).to eq("Found")
137
- end
138
- end
139
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "support/simple_test_model"
5
- require "zip"
6
-
7
- RSpec.describe Lutaml::Store::PackageTransport::ZipTransport do
8
- let(:tmpdir) { Dir.mktmpdir }
9
- after { FileUtils.rm_rf(tmpdir) }
10
-
11
- let(:definition) do
12
- Lutaml::Store::PackageDefinition.new(
13
- name: :test, metadata_model: SimpleTestModel, metadata_file: "meta.yaml",
14
- metadata_key: :id
15
- ) do |pkg|
16
- pkg.model(model: SimpleTestModel, dir: "items",
17
- layout: :separate, key: :id, default_format: :yaml)
18
- pkg.asset("data.bin", type: :file)
19
- end
20
- end
21
-
22
- describe "round-trip through ZIP" do
23
- it "preserves models, metadata, and assets" do
24
- store = Lutaml::Store::PackageStore.new(definition)
25
- store.add_model(SimpleTestModel.new(id: "item-1", name: "First"))
26
- store.metadata = SimpleTestModel.new(id: "meta", name: "Package")
27
- store.add_asset("data.bin", "BINARY")
28
-
29
- zip_path = File.join(tmpdir, "test.zip")
30
- store.save(zip_path, transport: :zip)
31
-
32
- expect(File.exist?(zip_path)).to be true
33
-
34
- # Verify ZIP contents
35
- Zip::File.open(zip_path) do |zf|
36
- expect(zf.find_entry("meta.yaml")).not_to be_nil
37
- expect(zf.find_entry("items/item-1.yaml")).not_to be_nil
38
- expect(zf.find_entry("data.bin")).not_to be_nil
39
- end
40
-
41
- # Load from ZIP
42
- loaded = Lutaml::Store::PackageStore.load(definition, zip_path,
43
- transport: :zip)
44
- expect(loaded.model_count(SimpleTestModel)).to eq(1)
45
- expect(loaded.metadata.name).to eq("Package")
46
- expect(loaded.asset("data.bin")).to eq("BINARY")
47
- end
48
- end
49
-
50
- describe "directory → ZIP → directory round-trip" do
51
- it "preserves data through format conversion" do
52
- dir = File.join(tmpdir, "source")
53
- dir_store = Lutaml::Store::PackageStore.new(definition)
54
- dir_store.add_model(SimpleTestModel.new(id: "abc", name: "Test"))
55
- dir_store.metadata = SimpleTestModel.new(id: "meta", name: "Dir Package")
56
- dir_store.save(dir, transport: :directory)
57
-
58
- loaded_from_dir = Lutaml::Store::PackageStore.load(definition, dir,
59
- transport: :directory)
60
- zip_path = File.join(tmpdir, "converted.zip")
61
- loaded_from_dir.save(zip_path, transport: :zip)
62
-
63
- loaded_from_zip = Lutaml::Store::PackageStore.load(definition, zip_path,
64
- transport: :zip)
65
- expect(loaded_from_zip.model_count(SimpleTestModel)).to eq(1)
66
- expect(loaded_from_zip.fetch_model(SimpleTestModel, "abc").name).to eq("Test")
67
- expect(loaded_from_zip.metadata.name).to eq("Dir Package")
68
- end
69
- end
70
-
71
- describe "format override in ZIP" do
72
- it "writes models in JSON format" do
73
- store = Lutaml::Store::PackageStore.new(definition)
74
- store.add_model(SimpleTestModel.new(id: "item-1", name: "First"))
75
-
76
- zip_path = File.join(tmpdir, "json.zip")
77
- store.save(zip_path, transport: :zip,
78
- formats: { SimpleTestModel => :json })
79
-
80
- Zip::File.open(zip_path) do |zf|
81
- expect(zf.find_entry("items/item-1.json")).not_to be_nil
82
- end
83
- end
84
- end
85
- end
@@ -1,182 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe Lutaml::Store::BasicStore do
6
- let(:store) { described_class.new }
7
-
8
- describe "initialization" do
9
- it "creates a store with default memory adapter" do
10
- expect(store.adapter).to be_a(Lutaml::Store::Adapter::Memory)
11
- end
12
-
13
- it "creates a store with configuration hash" do
14
- config = {
15
- "adapter" => { "type" => "memory" },
16
- "cache" => { "enabled" => false }
17
- }
18
- store = described_class.new(config)
19
- expect(store.cache).to be_nil
20
- end
21
-
22
- it "creates a store with direct adapter instance" do
23
- adapter = Lutaml::Store::Adapter::Memory.new
24
- store = described_class.new(adapter)
25
- expect(store.adapter).to eq(adapter)
26
- end
27
- end
28
-
29
- describe "basic operations" do
30
- it "stores and retrieves values" do
31
- store.set("key1", "value1")
32
- expect(store.get("key1")).to eq("value1")
33
- end
34
-
35
- it "returns nil for non-existent keys" do
36
- expect(store.get("nonexistent")).to be_nil
37
- end
38
-
39
- it "deletes values" do
40
- store.set("key1", "value1")
41
- expect(store.delete("key1")).to be true
42
- expect(store.get("key1")).to be_nil
43
- end
44
-
45
- it "checks key existence" do
46
- store.set("key1", "value1")
47
- expect(store.exists?("key1")).to be true
48
- expect(store.exists?("nonexistent")).to be false
49
- end
50
-
51
- it "returns all data" do
52
- store.set("key1", "value1")
53
- store.set("key2", "value2")
54
- expect(store.all).to eq("key1" => "value1", "key2" => "value2")
55
- end
56
-
57
- it "clears all data" do
58
- store.set("key1", "value1")
59
- store.clear
60
- expect(store.size).to eq(0)
61
- end
62
-
63
- it "returns correct size" do
64
- expect(store.size).to eq(0)
65
- store.set("key1", "value1")
66
- expect(store.size).to eq(1)
67
- end
68
- end
69
-
70
- describe "caching" do
71
- let(:config) do
72
- {
73
- "adapter" => { "type" => "memory" },
74
- "cache" => { "enabled" => true, "max_size" => 10 }
75
- }
76
- end
77
- let(:store) { described_class.new(config) }
78
-
79
- it "caches retrieved values" do
80
- store.set("key1", "value1")
81
-
82
- # First get should hit backend
83
- value1 = store.get("key1")
84
- expect(value1).to eq("value1")
85
-
86
- # Second get should hit cache
87
- value2 = store.get("key1")
88
- expect(value2).to eq("value1")
89
-
90
- # Cache should contain the value
91
- expect(store.cache_stats[:size]).to eq(1)
92
- end
93
-
94
- it "invalidates cache on delete" do
95
- store.set("key1", "value1")
96
- store.get("key1") # Cache the value
97
-
98
- store.delete("key1")
99
- expect(store.cache_stats[:size]).to eq(0)
100
- end
101
- end
102
-
103
- describe "events" do
104
- let(:events) { [] }
105
-
106
- before do
107
- store.on(:set) { |data| events << data }
108
- store.on(:get) { |data| events << data }
109
- store.on(:delete) { |data| events << data }
110
- end
111
-
112
- it "emits events for operations" do
113
- store.set("key1", "value1")
114
- store.get("key1")
115
- store.delete("key1")
116
-
117
- expect(events.size).to eq(3)
118
- expect(events[0][:event]).to eq(:set)
119
- expect(events[1][:event]).to eq(:get)
120
- expect(events[2][:event]).to eq(:delete)
121
- end
122
- end
123
-
124
- describe "monitoring" do
125
- let(:config) do
126
- {
127
- "adapter" => { "type" => "memory" },
128
- "monitoring" => { "enabled" => true }
129
- }
130
- end
131
- let(:store) { described_class.new(config) }
132
-
133
- it "tracks operation statistics" do
134
- store.set("key1", "value1")
135
- store.get("key1")
136
-
137
- stats = store.stats
138
- monitor_stats = stats[:monitor_stats]
139
- expect(monitor_stats[:operations][:set]).to eq(1)
140
- expect(monitor_stats[:operations][:get]).to eq(1)
141
- # NOTE: total_operations includes the size call from stats method
142
- expect(monitor_stats[:total_operations]).to be >= 2
143
- end
144
- end
145
-
146
- describe "configuration from file" do
147
- let(:config_file) { "test_config.yml" }
148
- let(:config_content) do
149
- <<~YAML
150
- lutaml_store:
151
- adapter:
152
- type: memory
153
- cache:
154
- enabled: true
155
- max_size: 500
156
- monitoring:
157
- enabled: true
158
- YAML
159
- end
160
-
161
- before do
162
- File.write(config_file, config_content)
163
- end
164
-
165
- after do
166
- File.delete(config_file) if File.exist?(config_file)
167
- end
168
-
169
- it "loads configuration from YAML file" do
170
- store = described_class.from_file(config_file)
171
- expect(store.adapter).to be_a(Lutaml::Store::Adapter::Memory)
172
- expect(store.cache).not_to be_nil
173
- expect(store.monitor).not_to be_nil
174
- end
175
- end
176
-
177
- describe "#close" do
178
- it "closes resources properly" do
179
- expect { store.close }.not_to raise_error
180
- end
181
- end
182
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- RSpec.describe Lutaml::Store do
4
- it "has a version number" do
5
- expect(Lutaml::Store::VERSION).not_to be nil
6
- end
7
-
8
- it "provides access to Store and DatabaseStore classes" do
9
- expect(Lutaml::Store::BasicStore).to be_a(Class)
10
- expect(Lutaml::Store::DatabaseStore).to be_a(Class)
11
- end
12
-
13
- it "returns a DatabaseStore from .new" do
14
- test_model = Struct.new(:id, :name)
15
- store = Lutaml::Store.new(
16
- adapter: :memory,
17
- models: [{ model: test_model, key: :id }]
18
- )
19
- expect(store).to be_a(Lutaml::Store::DatabaseStore)
20
- end
21
- end
data/spec/spec_helper.rb DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "lutaml/model"
4
- require "lutaml/store"
5
-
6
- RSpec.configure do |config|
7
- # Enable flags like --only-failures and --next-failure
8
- config.example_status_persistence_file_path = ".rspec_status"
9
-
10
- # Disable RSpec exposing methods globally on `Module` and `main`
11
- config.disable_monkey_patching!
12
-
13
- config.expect_with :rspec do |c|
14
- c.syntax = :expect
15
- end
16
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Simple model with key_value DSL for testing package components
4
- # without depending on glossarist or lutaml-jsonschema.
5
- class SimpleTestModel < Lutaml::Model::Serializable
6
- attribute :id, :string
7
- attribute :name, :string
8
- attribute :value, :string
9
-
10
- key_value do
11
- map "id", to: :id
12
- map "name", to: :name
13
- map "value", to: :value
14
- end
15
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Minimal model with yamls DSL for testing Format::Yamls.
4
- # Structure mirrors ConceptDocument: header + collection of parts.
5
- class YamlsTestHeader < Lutaml::Model::Serializable
6
- attribute :id, :string
7
- attribute :name, :string
8
-
9
- yaml do
10
- map "id", to: :id
11
- map "name", to: :name
12
- end
13
- end
14
-
15
- class YamlsTestPart < Lutaml::Model::Serializable
16
- attribute :label, :string
17
- attribute :value, :string
18
-
19
- yaml do
20
- map "label", to: :label
21
- map "value", to: :value
22
- end
23
- end
24
-
25
- class YamlsTestModel < Lutaml::Model::Serializable
26
- attribute :header, YamlsTestHeader
27
- attribute :parts, YamlsTestPart, collection: true
28
-
29
- yamls do
30
- sequence do
31
- map_document 0, to: :header, type: YamlsTestHeader
32
- map_document 1.., to: :parts, type: YamlsTestPart, collection: true
33
- end
34
- end
35
- end