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,291 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "tmpdir"
5
-
6
- module IntegrationTestModels
7
- class TestDocument < Lutaml::Model::Serializable
8
- attribute :id, :string
9
- attribute :title, :string
10
- attribute :content, :string
11
- attribute :created_at, :string
12
-
13
- key_value do
14
- map :id, to: :id
15
- map :title, to: :title
16
- map :content, to: :content
17
- map :created_at, to: :created_at
18
- end
19
- end
20
-
21
- class TestAuthor < Lutaml::Model::Serializable
22
- attribute :name, :string
23
- attribute :email, :string
24
- attribute :bio, :string
25
-
26
- key_value do
27
- map :name, to: :name
28
- map :email, to: :email
29
- map :bio, to: :bio
30
- end
31
- end
32
-
33
- class TestBook < Lutaml::Model::Serializable
34
- attribute :isbn, :string
35
- attribute :title, :string
36
- attribute :author, :string
37
- attribute :published_year, :integer
38
- attribute :genre, :string
39
-
40
- key_value do
41
- map :isbn, to: :isbn
42
- map :title, to: :title
43
- map :author, to: :author
44
- map :published_year, to: :published_year
45
- map :genre, to: :genre
46
- end
47
- end
48
- end
49
-
50
- RSpec.describe "Lutaml::Store with Lutaml::Model::Serializable integration" do
51
- let(:doc_model) { { model: IntegrationTestModels::TestDocument, key: :id, dir: "documents" } }
52
- let(:author_model) { { model: IntegrationTestModels::TestAuthor, key: :name, dir: "authors" } }
53
- let(:book_model) { { model: IntegrationTestModels::TestBook, key: :isbn, dir: "books" } }
54
-
55
- describe "single model CRUD" do
56
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [doc_model]) }
57
- let(:document) do
58
- IntegrationTestModels::TestDocument.new(
59
- id: "doc1", title: "Test Document",
60
- content: "This is a test", created_at: "2024-01-01"
61
- )
62
- end
63
-
64
- it "saves and fetches a Lutaml::Model::Serializable instance" do
65
- store.save(document)
66
-
67
- retrieved = store.fetch(model: IntegrationTestModels::TestDocument, id: "doc1")
68
- expect(retrieved).to be_a(IntegrationTestModels::TestDocument)
69
- expect(retrieved.id).to eq("doc1")
70
- expect(retrieved.title).to eq("Test Document")
71
- expect(retrieved.content).to eq("This is a test")
72
- expect(retrieved.created_at).to eq("2024-01-01")
73
- end
74
-
75
- it "returns nil for non-existent key" do
76
- expect(store.fetch(model: IntegrationTestModels::TestDocument, id: "missing")).to be_nil
77
- end
78
-
79
- it "updates a model with hash attributes" do
80
- store.save(document)
81
-
82
- updated = store.update(
83
- model: IntegrationTestModels::TestDocument,
84
- id: "doc1",
85
- attributes: { title: "Updated Title" }
86
- )
87
- expect(updated.title).to eq("Updated Title")
88
- expect(updated.content).to eq("This is a test")
89
- end
90
-
91
- it "updates a model with block" do
92
- store.save(document)
93
-
94
- updated = store.update(model: IntegrationTestModels::TestDocument, id: "doc1") do |model|
95
- model.content = "New content"
96
- model
97
- end
98
- expect(updated.content).to eq("New content")
99
- end
100
-
101
- it "deletes a model" do
102
- store.save(document)
103
- expect(store.exists?(model: IntegrationTestModels::TestDocument, id: "doc1")).to be true
104
-
105
- result = store.destroy(model: IntegrationTestModels::TestDocument, id: "doc1")
106
- expect(result).to be true
107
- expect(store.fetch(model: IntegrationTestModels::TestDocument, id: "doc1")).to be_nil
108
- end
109
-
110
- it "reports count and exists" do
111
- expect(store.count(model: IntegrationTestModels::TestDocument)).to eq(0)
112
-
113
- store.save(document)
114
- expect(store.count(model: IntegrationTestModels::TestDocument)).to eq(1)
115
- expect(store.exists?(model: IntegrationTestModels::TestDocument, id: "doc1")).to be true
116
- end
117
- end
118
-
119
- describe "model type validation" do
120
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [doc_model]) }
121
-
122
- it "raises when saving an unregistered model" do
123
- author = IntegrationTestModels::TestAuthor.new(name: "John Doe", email: "john@example.com")
124
-
125
- expect { store.save(author) }.to raise_error(Lutaml::Store::ModelNotRegisteredError)
126
- end
127
- end
128
-
129
- describe "collection operations" do
130
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [author_model]) }
131
- let(:authors) do
132
- [
133
- IntegrationTestModels::TestAuthor.new(name: "Alice Johnson", email: "alice@example.com", bio: "Fiction writer"),
134
- IntegrationTestModels::TestAuthor.new(name: "Bob Wilson", email: "bob@example.com", bio: "Technical writer"),
135
- IntegrationTestModels::TestAuthor.new(name: "Carol Davis", email: "carol@example.com", bio: "Science writer")
136
- ]
137
- end
138
-
139
- it "stores multiple models and retrieves all" do
140
- authors.each { |a| store.save(a) }
141
-
142
- all_authors = store.all(model: IntegrationTestModels::TestAuthor)
143
- expect(all_authors.size).to eq(3)
144
- expect(all_authors.map(&:name).sort).to eq(["Alice Johnson", "Bob Wilson", "Carol Davis"])
145
- end
146
-
147
- it "queries with where" do
148
- authors.each { |a| store.save(a) }
149
-
150
- fiction = store.where(model: IntegrationTestModels::TestAuthor, bio: "Fiction writer")
151
- expect(fiction.size).to eq(1)
152
- expect(fiction.first.name).to eq("Alice Johnson")
153
- end
154
-
155
- it "updates a model and persists the change" do
156
- authors.each { |a| store.save(a) }
157
-
158
- store.update(model: IntegrationTestModels::TestAuthor, name: "Alice Johnson") do |model|
159
- model.bio = "Updated bio: Fiction and mystery writer"
160
- model
161
- end
162
-
163
- retrieved = store.fetch(model: IntegrationTestModels::TestAuthor, name: "Alice Johnson")
164
- expect(retrieved.bio).to eq("Updated bio: Fiction and mystery writer")
165
- end
166
- end
167
-
168
- describe "multi-model store" do
169
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [doc_model, author_model, book_model]) }
170
-
171
- it "stores and retrieves different model types independently" do
172
- document = IntegrationTestModels::TestDocument.new(id: "doc1", title: "Test Doc")
173
- author = IntegrationTestModels::TestAuthor.new(name: "John Doe", email: "john@example.com")
174
- book = IntegrationTestModels::TestBook.new(isbn: "978-123", title: "Test Book", author: "John Doe",
175
- published_year: 2024, genre: "Fiction")
176
-
177
- store.save(document)
178
- store.save(author)
179
- store.save(book)
180
-
181
- expect(store.count(model: IntegrationTestModels::TestDocument)).to eq(1)
182
- expect(store.count(model: IntegrationTestModels::TestAuthor)).to eq(1)
183
- expect(store.count(model: IntegrationTestModels::TestBook)).to eq(1)
184
-
185
- fetched_doc = store.fetch(model: IntegrationTestModels::TestDocument, id: "doc1")
186
- fetched_author = store.fetch(model: IntegrationTestModels::TestAuthor, name: "John Doe")
187
- fetched_book = store.fetch(model: IntegrationTestModels::TestBook, isbn: "978-123")
188
-
189
- expect(fetched_doc).to be_a(IntegrationTestModels::TestDocument)
190
- expect(fetched_author).to be_a(IntegrationTestModels::TestAuthor)
191
- expect(fetched_book).to be_a(IntegrationTestModels::TestBook)
192
- end
193
-
194
- it "isolates where queries by model type" do
195
- store.save(IntegrationTestModels::TestDocument.new(id: "d1", title: "Ruby Guide"))
196
- store.save(IntegrationTestModels::TestBook.new(isbn: "b1", title: "Ruby Guide", author: "Author"))
197
-
198
- docs = store.where(model: IntegrationTestModels::TestDocument, title: "Ruby Guide")
199
- books = store.where(model: IntegrationTestModels::TestBook, title: "Ruby Guide")
200
-
201
- expect(docs.size).to eq(1)
202
- expect(docs.first).to be_a(IntegrationTestModels::TestDocument)
203
- expect(books.size).to eq(1)
204
- expect(books.first).to be_a(IntegrationTestModels::TestBook)
205
- end
206
- end
207
-
208
- describe "file I/O round-trip" do
209
- let(:tmpdir) { Dir.mktmpdir }
210
- after { FileUtils.rm_rf(tmpdir) }
211
-
212
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [book_model]) }
213
- let(:books) do
214
- [
215
- IntegrationTestModels::TestBook.new(isbn: "978-0123456789", title: "Ruby Programming", author: "Jane Smith",
216
- published_year: 2023, genre: "Programming"),
217
- IntegrationTestModels::TestBook.new(isbn: "978-9876543210", title: "Go Programming", author: "Bob Jones",
218
- published_year: 2024, genre: "Programming")
219
- ]
220
- end
221
-
222
- it "round-trips through YAML separate layout" do
223
- store.save_all(books, path: tmpdir, format: :yaml, layout: :separate)
224
-
225
- fresh_store = Lutaml::Store.new(adapter: :memory, models: [book_model])
226
- loaded = fresh_store.load_all(IntegrationTestModels::TestBook, path: tmpdir, format: :yaml, layout: :separate)
227
-
228
- expect(loaded.size).to eq(2)
229
- expect(loaded.map(&:title).sort).to eq(["Go Programming", "Ruby Programming"])
230
- end
231
-
232
- it "round-trips through JSON separate layout" do
233
- store.save_all(books, path: tmpdir, format: :json, layout: :separate)
234
-
235
- fresh_store = Lutaml::Store.new(adapter: :memory, models: [book_model])
236
- loaded = fresh_store.load_all(IntegrationTestModels::TestBook, path: tmpdir, format: :json, layout: :separate)
237
-
238
- expect(loaded.size).to eq(2)
239
- expect(loaded.map(&:author).sort).to eq(["Bob Jones", "Jane Smith"])
240
- end
241
- end
242
-
243
- describe "import_all workflow" do
244
- let(:tmpdir) { Dir.mktmpdir }
245
- after { FileUtils.rm_rf(tmpdir) }
246
-
247
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [doc_model]) }
248
- let(:documents) do
249
- [
250
- IntegrationTestModels::TestDocument.new(id: "doc1", title: "First Document", content: "Content 1"),
251
- IntegrationTestModels::TestDocument.new(id: "doc2", title: "Second Document", content: "Content 2")
252
- ]
253
- end
254
-
255
- it "loads from directory and makes models queryable" do
256
- store.save_all(documents, path: tmpdir, format: :yaml, layout: :separate)
257
-
258
- fresh_store = Lutaml::Store.new(adapter: :memory, models: [doc_model])
259
- loaded = fresh_store.import_all(IntegrationTestModels::TestDocument, path: tmpdir, format: :yaml,
260
- layout: :separate)
261
-
262
- expect(loaded.size).to eq(2)
263
-
264
- # Queryable via fetch
265
- doc1 = fresh_store.fetch(model: IntegrationTestModels::TestDocument, id: "doc1")
266
- expect(doc1).not_to be_nil
267
- expect(doc1.title).to eq("First Document")
268
-
269
- # Queryable via where
270
- second = fresh_store.where(model: IntegrationTestModels::TestDocument, title: "Second Document")
271
- expect(second.size).to eq(1)
272
- expect(second.first.content).to eq("Content 2")
273
-
274
- # Queryable via count
275
- expect(fresh_store.count(model: IntegrationTestModels::TestDocument)).to eq(2)
276
- end
277
- end
278
-
279
- describe "statistics" do
280
- let(:store) { Lutaml::Store.new(adapter: :memory, models: [doc_model]) }
281
-
282
- it "reports registered models and counts" do
283
- store.save(IntegrationTestModels::TestDocument.new(id: "doc1", title: "Test"))
284
-
285
- stats = store.stats
286
- expect(stats[:models_registered]).to eq(1)
287
- expect(stats[:total_models]).to eq(1)
288
- expect(stats[:registered_models]).to include("IntegrationTestModels::TestDocument")
289
- end
290
- end
291
- end
@@ -1,140 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- module ModelSerializerTestModels
6
- class SerialBook < Lutaml::Model::Serializable
7
- attribute :isbn, :string
8
- attribute :title, :string
9
- attribute :author, :string
10
- end
11
-
12
- class SerialEbook < SerialBook
13
- attribute :format, :string
14
- end
15
- end
16
-
17
- RSpec.describe Lutaml::Store::ModelSerializer do
18
- subject(:serializer) { described_class.new }
19
-
20
- describe "#serialize" do
21
- it "serializes a model to a hash with class metadata" do
22
- book = ModelSerializerTestModels::SerialBook.new(isbn: "978-123", title: "Test Book", author: "Author")
23
- result = serializer.serialize(book)
24
-
25
- expect(result).to be_a(Hash)
26
- expect(result["_class"]).to eq("ModelSerializerTestModels::SerialBook")
27
- expect(result["isbn"]).to eq("978-123")
28
- expect(result["title"]).to eq("Test Book")
29
- expect(result["author"]).to eq("Author")
30
- end
31
-
32
- it "serializes a subclass with correct class name" do
33
- ebook = ModelSerializerTestModels::SerialEbook.new(isbn: "978-456", title: "Digital Book", format: "epub")
34
- result = serializer.serialize(ebook)
35
-
36
- expect(result["_class"]).to eq("ModelSerializerTestModels::SerialEbook")
37
- expect(result["format"]).to eq("epub")
38
- end
39
- end
40
-
41
- describe "#deserialize" do
42
- it "deserializes a hash to the correct model class" do
43
- data = {
44
- "_class" => "ModelSerializerTestModels::SerialBook",
45
- "isbn" => "978-123",
46
- "title" => "Test Book",
47
- "author" => "Author"
48
- }
49
-
50
- result = serializer.deserialize(data, ModelSerializerTestModels::SerialBook)
51
-
52
- expect(result).to be_a(ModelSerializerTestModels::SerialBook)
53
- expect(result.isbn).to eq("978-123")
54
- expect(result.title).to eq("Test Book")
55
- end
56
-
57
- it "deserializes to a subclass when class metadata indicates it" do
58
- data = {
59
- "_class" => "ModelSerializerTestModels::SerialEbook",
60
- "isbn" => "978-456",
61
- "title" => "Digital",
62
- "format" => "epub"
63
- }
64
-
65
- result = serializer.deserialize(data, ModelSerializerTestModels::SerialBook)
66
-
67
- expect(result).to be_a(ModelSerializerTestModels::SerialEbook)
68
- expect(result.format).to eq("epub")
69
- end
70
-
71
- it "rejects incompatible polymorphic types" do
72
- data = {
73
- "_class" => "ModelSerializerTestModels::SerialBook",
74
- "isbn" => "978-123"
75
- }
76
-
77
- expect do
78
- serializer.deserialize(data, ModelSerializerTestModels::SerialEbook)
79
- end.to raise_error(Lutaml::Store::PolymorphicUpdateError, /not compatible/)
80
- end
81
-
82
- it "raises on invalid data (missing _class)" do
83
- data = { "isbn" => "978-123" }
84
-
85
- expect do
86
- serializer.deserialize(data, ModelSerializerTestModels::SerialBook)
87
- end.to raise_error(Lutaml::Store::CompositeModelError, /Invalid serialized data/)
88
- end
89
-
90
- it "raises on invalid data (not a Hash)" do
91
- expect do
92
- serializer.deserialize("not a hash", ModelSerializerTestModels::SerialBook)
93
- end.to raise_error(Lutaml::Store::CompositeModelError, /Invalid serialized data/)
94
- end
95
-
96
- it "raises on unresolvable class name" do
97
- data = { "_class" => "NonExistentClass", "isbn" => "123" }
98
-
99
- expect do
100
- serializer.deserialize(data, ModelSerializerTestModels::SerialBook)
101
- end.to raise_error(Lutaml::Store::CompositeModelError, /Cannot resolve class/)
102
- end
103
-
104
- it "strips internal metadata keys from model data" do
105
- data = {
106
- "_class" => "ModelSerializerTestModels::SerialBook",
107
- "_composite_models" => { "studio" => { "storage_key" => "key1" } },
108
- "isbn" => "978-123",
109
- "title" => "Test"
110
- }
111
-
112
- result = serializer.deserialize(data, ModelSerializerTestModels::SerialBook)
113
-
114
- expect(result.isbn).to eq("978-123")
115
- expect(result.title).to eq("Test")
116
- end
117
- end
118
-
119
- describe "round-trip" do
120
- it "serializes and deserializes preserving all attributes" do
121
- book = ModelSerializerTestModels::SerialBook.new(isbn: "978-789", title: "Round Trip", author: "Traveler")
122
- serialized = serializer.serialize(book)
123
- deserialized = serializer.deserialize(serialized, ModelSerializerTestModels::SerialBook)
124
-
125
- expect(deserialized.isbn).to eq("978-789")
126
- expect(deserialized.title).to eq("Round Trip")
127
- expect(deserialized.author).to eq("Traveler")
128
- end
129
-
130
- it "preserves subclass identity through round-trip" do
131
- ebook = ModelSerializerTestModels::SerialEbook.new(isbn: "978-000", title: "E-Book", author: "Digital",
132
- format: "mobi")
133
- serialized = serializer.serialize(ebook)
134
- deserialized = serializer.deserialize(serialized, ModelSerializerTestModels::SerialBook)
135
-
136
- expect(deserialized).to be_a(ModelSerializerTestModels::SerialEbook)
137
- expect(deserialized.format).to eq("mobi")
138
- end
139
- end
140
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "support/simple_test_model"
5
- require "support/yamls_test_model"
6
-
7
- RSpec.describe Lutaml::Store::PackageDefinition do
8
- subject(:definition) do
9
- described_class.new(
10
- name: :test,
11
- metadata_model: SimpleTestModel,
12
- metadata_file: "meta.yaml"
13
- ) do |pkg|
14
- pkg.model(model: YamlsTestModel, dir: "items",
15
- layout: :separate, key: :id, default_format: :yamls)
16
- pkg.model(model: SimpleTestModel, file: "config.yaml",
17
- key: :id, default_format: :yaml)
18
- pkg.asset("images", type: :directory)
19
- pkg.asset("readme.txt", type: :file)
20
- end
21
- end
22
-
23
- it "stores name" do
24
- expect(definition.name).to eq(:test)
25
- end
26
-
27
- it "stores metadata model and file" do
28
- expect(definition.metadata_model).to eq(SimpleTestModel)
29
- expect(definition.metadata_file).to eq("meta.yaml")
30
- end
31
-
32
- it "stores model entries" do
33
- expect(definition.model_entries.length).to eq(2)
34
-
35
- yamls_entry = definition.model_entries.first
36
- expect(yamls_entry.model).to eq(YamlsTestModel)
37
- expect(yamls_entry.dir).to eq("items")
38
- expect(yamls_entry.layout).to eq(:separate)
39
- expect(yamls_entry.key).to eq(:id)
40
- expect(yamls_entry.default_format).to eq(:yamls)
41
-
42
- simple_entry = definition.model_entries.last
43
- expect(simple_entry.model).to eq(SimpleTestModel)
44
- expect(simple_entry.file).to eq("config.yaml")
45
- end
46
-
47
- it "stores asset entries" do
48
- expect(definition.asset_entries.length).to eq(2)
49
- expect(definition.asset_entries[0].path).to eq("images")
50
- expect(definition.asset_entries[0].type).to eq(:directory)
51
- expect(definition.asset_entries[1].path).to eq("readme.txt")
52
- expect(definition.asset_entries[1].type).to eq(:file)
53
- end
54
-
55
- describe "dir/file mutual exclusivity" do
56
- it "raises when both dir and file are specified" do
57
- expect do
58
- definition.model(model: SimpleTestModel, dir: "d", file: "f.yaml", key: :id)
59
- end.to raise_error(ArgumentError, /Specify dir: or file:, not both/)
60
- end
61
- end
62
-
63
- describe "#entry_for" do
64
- it "finds entry by model class" do
65
- entry = definition.entry_for(YamlsTestModel)
66
- expect(entry).not_to be_nil
67
- expect(entry.dir).to eq("items")
68
- end
69
-
70
- it "returns nil for unknown model" do
71
- expect(definition.entry_for(String)).to be_nil
72
- end
73
- end
74
-
75
- describe "#model_classes" do
76
- it "returns registered model classes" do
77
- expect(definition.model_classes).to contain_exactly(YamlsTestModel, SimpleTestModel)
78
- end
79
- end
80
-
81
- describe "#database_store_models" do
82
- it "includes model entries only (metadata is stored separately)" do
83
- configs = definition.database_store_models
84
- expect(configs.length).to eq(2) # 2 model entries, no metadata
85
- models = configs.map { |c| c[:model] }
86
- expect(models).to contain_exactly(YamlsTestModel, SimpleTestModel)
87
- end
88
- end
89
- end
@@ -1,153 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
- require "support/simple_test_model"
5
- require "support/yamls_test_model"
6
-
7
- RSpec.describe Lutaml::Store::PackageStore do
8
- let(:definition) do
9
- Lutaml::Store::PackageDefinition.new(name: :test) do |pkg|
10
- pkg.model(model: SimpleTestModel, dir: "items",
11
- layout: :separate, key: :id, default_format: :yaml)
12
- end
13
- end
14
-
15
- let(:store) { described_class.new(definition) }
16
-
17
- describe "#add_model / #models_for" do
18
- it "stores and retrieves model instances" do
19
- model = SimpleTestModel.new(id: "test-1", name: "Test")
20
- store.add_model(model)
21
- expect(store.models_for(SimpleTestModel)).to include(model)
22
- end
23
-
24
- it "stores multiple instances" do
25
- 3.times { |i| store.add_model(SimpleTestModel.new(id: "test-#{i}")) }
26
- expect(store.model_count(SimpleTestModel)).to eq(3)
27
- end
28
- end
29
-
30
- describe "#fetch_model" do
31
- it "retrieves by key" do
32
- model = SimpleTestModel.new(id: "abc", name: "ABC")
33
- store.add_model(model)
34
- expect(store.fetch_model(SimpleTestModel, "abc").name).to eq("ABC")
35
- end
36
-
37
- it "returns nil for missing key" do
38
- expect(store.fetch_model(SimpleTestModel, "missing")).to be_nil
39
- end
40
- end
41
-
42
- describe "#model_exists?" do
43
- it "returns true for existing key" do
44
- store.add_model(SimpleTestModel.new(id: "exists"))
45
- expect(store.model_exists?(SimpleTestModel, "exists")).to be true
46
- end
47
-
48
- it "returns false for missing key" do
49
- expect(store.model_exists?(SimpleTestModel, "missing")).to be false
50
- end
51
- end
52
-
53
- describe "#remove_model" do
54
- it "removes by key" do
55
- store.add_model(SimpleTestModel.new(id: "remove-me"))
56
- store.remove_model(SimpleTestModel, "remove-me")
57
- expect(store.model_exists?(SimpleTestModel, "remove-me")).to be false
58
- end
59
- end
60
-
61
- describe "#metadata=" do
62
- let(:def_with_metadata) do
63
- Lutaml::Store::PackageDefinition.new(
64
- name: :test, metadata_model: SimpleTestModel,
65
- metadata_file: "meta.yaml", metadata_key: :id
66
- ) do |pkg|
67
- pkg.model(model: SimpleTestModel, dir: "items",
68
- layout: :separate, key: :id, default_format: :yaml)
69
- end
70
- end
71
-
72
- it "stores metadata" do
73
- store = described_class.new(def_with_metadata)
74
- store.metadata = SimpleTestModel.new(id: "meta", name: "Metadata")
75
- expect(store.metadata.name).to eq("Metadata")
76
- end
77
- end
78
-
79
- describe "#add_asset / #asset" do
80
- it "stores and retrieves raw content" do
81
- store.add_asset("images/logo.png", "PNG_DATA")
82
- expect(store.asset("images/logo.png")).to eq("PNG_DATA")
83
- end
84
-
85
- it "lists asset paths" do
86
- store.add_asset("a.txt", "A")
87
- store.add_asset("b.txt", "B")
88
- expect(store.asset_paths).to contain_exactly("a.txt", "b.txt")
89
- end
90
- end
91
-
92
- describe "#remove_asset" do
93
- it "removes an asset" do
94
- store.add_asset("remove.txt", "data")
95
- store.remove_asset("remove.txt")
96
- expect(store.asset("remove.txt")).to be_nil
97
- end
98
- end
99
-
100
- describe "#clear_all" do
101
- it "removes everything" do
102
- store.add_model(SimpleTestModel.new(id: "x"))
103
- store.add_asset("file.txt", "data")
104
- store.clear_all
105
- expect(store.model_count(SimpleTestModel)).to eq(0)
106
- expect(store.asset_paths).to be_empty
107
- end
108
- end
109
-
110
- describe "#stats" do
111
- it "reports model counts and asset count" do
112
- store.add_model(SimpleTestModel.new(id: "1"))
113
- store.add_asset("f.txt", "x")
114
- stats = store.stats
115
- expect(stats[:package]).to eq(:test)
116
- expect(stats[:models][SimpleTestModel.name]).to eq(1)
117
- expect(stats[:assets]).to eq(1)
118
- end
119
- end
120
-
121
- describe "#resolve_formats" do
122
- let(:def_with_two) do
123
- Lutaml::Store::PackageDefinition.new(name: :test) do |pkg|
124
- pkg.model(model: SimpleTestModel, dir: "a", key: :id, default_format: :yaml)
125
- pkg.model(model: SimpleTestModel, dir: "b", key: :id, default_format: :yamls)
126
- end
127
- end
128
-
129
- let(:two_model_store) { described_class.new(def_with_two) }
130
-
131
- it "global format overrides all models" do
132
- formats = two_model_store.send(:resolve_formats, :json, {})
133
- expect(formats[SimpleTestModel]).to eq(:json)
134
- end
135
-
136
- it "per-model format overrides specific models" do
137
- formats = two_model_store.send(:resolve_formats, nil,
138
- { SimpleTestModel => :marshal })
139
- expect(formats[SimpleTestModel]).to eq(:marshal)
140
- end
141
-
142
- it "both: global + per-model merge" do
143
- formats = two_model_store.send(:resolve_formats, :json,
144
- { SimpleTestModel => :marshal })
145
- expect(formats[SimpleTestModel]).to eq(:marshal)
146
- end
147
-
148
- it "nil: use defaults (empty hash)" do
149
- formats = two_model_store.send(:resolve_formats, nil, {})
150
- expect(formats).to be_empty
151
- end
152
- end
153
- end