lutaml-store 0.1.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 (110) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +27 -0
  3. data/.gitignore +12 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +10 -0
  6. data/.rubocop_todo.yml +450 -0
  7. data/CLAUDE.md +57 -0
  8. data/CODE_OF_CONDUCT.md +132 -0
  9. data/CORRECTED_HTTP_CACHE_IMPLEMENTATION.md +209 -0
  10. data/CORRECTED_HTTP_CACHE_PLAN.md +164 -0
  11. data/Gemfile +15 -0
  12. data/Gemfile.lock +220 -0
  13. data/README.adoc +1430 -0
  14. data/Rakefile +12 -0
  15. data/TODO.impl/0-lutaml-store-self-quality.md +112 -0
  16. data/TODO.impl/1-lutaml-hal-migration.md +60 -0
  17. data/TODO.impl/2-glossarist-migration.md +359 -0
  18. data/TODO.impl/3-lutaml-jsonschema-migration.md +273 -0
  19. data/bin/console +11 -0
  20. data/bin/setup +8 -0
  21. data/demo/Gemfile +15 -0
  22. data/demo/Gemfile.lock +61 -0
  23. data/demo/README.adoc +301 -0
  24. data/demo/data/vcards/co/contact_10_thompson.data +1 -0
  25. data/demo/data/vcards/co/contact_10_thompson.meta +1 -0
  26. data/demo/data/vcards/co/contact_1_doe.data +1 -0
  27. data/demo/data/vcards/co/contact_1_doe.meta +1 -0
  28. data/demo/data/vcards/co/contact_2_smith.data +1 -0
  29. data/demo/data/vcards/co/contact_2_smith.meta +1 -0
  30. data/demo/data/vcards/co/contact_3_johnson.data +1 -0
  31. data/demo/data/vcards/co/contact_3_johnson.meta +1 -0
  32. data/demo/data/vcards/co/contact_4_garcia.data +1 -0
  33. data/demo/data/vcards/co/contact_4_garcia.meta +1 -0
  34. data/demo/data/vcards/co/contact_5_wilson.data +1 -0
  35. data/demo/data/vcards/co/contact_5_wilson.meta +1 -0
  36. data/demo/data/vcards/co/contact_6_brown.data +1 -0
  37. data/demo/data/vcards/co/contact_6_brown.meta +1 -0
  38. data/demo/data/vcards/co/contact_7_davis.data +1 -0
  39. data/demo/data/vcards/co/contact_7_davis.meta +1 -0
  40. data/demo/data/vcards/co/contact_8_anderson.data +1 -0
  41. data/demo/data/vcards/co/contact_8_anderson.meta +1 -0
  42. data/demo/data/vcards/co/contact_9_taylor.data +1 -0
  43. data/demo/data/vcards/co/contact_9_taylor.meta +1 -0
  44. data/demo/data/vcards.db +0 -0
  45. data/demo/pottery_class_demo.rb +164 -0
  46. data/demo/vcard_models.rb +140 -0
  47. data/demo/vcard_store_demo.rb +526 -0
  48. data/lib/lutaml/store/adapter/base.rb +65 -0
  49. data/lib/lutaml/store/adapter/filesystem.rb +288 -0
  50. data/lib/lutaml/store/adapter/memory.rb +225 -0
  51. data/lib/lutaml/store/adapter/sqlite.rb +193 -0
  52. data/lib/lutaml/store/adapter.rb +12 -0
  53. data/lib/lutaml/store/attribute_updater.rb +198 -0
  54. data/lib/lutaml/store/basic_store.rb +190 -0
  55. data/lib/lutaml/store/cache.rb +108 -0
  56. data/lib/lutaml/store/cache_store.rb +282 -0
  57. data/lib/lutaml/store/composite_model_handler.rb +169 -0
  58. data/lib/lutaml/store/compression.rb +137 -0
  59. data/lib/lutaml/store/config.rb +178 -0
  60. data/lib/lutaml/store/database_store.rb +425 -0
  61. data/lib/lutaml/store/events.rb +92 -0
  62. data/lib/lutaml/store/format/base.rb +33 -0
  63. data/lib/lutaml/store/format/json.rb +25 -0
  64. data/lib/lutaml/store/format/jsonl.rb +37 -0
  65. data/lib/lutaml/store/format/marshal_format.rb +37 -0
  66. data/lib/lutaml/store/format/yaml.rb +29 -0
  67. data/lib/lutaml/store/format/yamls.rb +35 -0
  68. data/lib/lutaml/store/format.rb +33 -0
  69. data/lib/lutaml/store/http_cache.rb +279 -0
  70. data/lib/lutaml/store/http_cache_config.rb +53 -0
  71. data/lib/lutaml/store/http_cache_entry.rb +69 -0
  72. data/lib/lutaml/store/http_header_processor.rb +175 -0
  73. data/lib/lutaml/store/integrity.rb +102 -0
  74. data/lib/lutaml/store/model_registration.rb +75 -0
  75. data/lib/lutaml/store/model_registry.rb +123 -0
  76. data/lib/lutaml/store/model_serializer.rb +69 -0
  77. data/lib/lutaml/store/monitor.rb +192 -0
  78. data/lib/lutaml/store/storage_key.rb +40 -0
  79. data/lib/lutaml/store/version.rb +7 -0
  80. data/lib/lutaml/store.rb +41 -0
  81. data/lutaml-store.gemspec +35 -0
  82. data/plan.adoc +606 -0
  83. data/sig/lutaml/store.rbs +6 -0
  84. data/spec/lutaml/store/adapter_interface_spec.rb +89 -0
  85. data/spec/lutaml/store/anti_pattern_guard_spec.rb +35 -0
  86. data/spec/lutaml/store/anti_pattern_spec.rb +78 -0
  87. data/spec/lutaml/store/autoload_spec.rb +34 -0
  88. data/spec/lutaml/store/cache_store_spec.rb +271 -0
  89. data/spec/lutaml/store/compression_spec.rb +78 -0
  90. data/spec/lutaml/store/config_enhanced_spec.rb +158 -0
  91. data/spec/lutaml/store/corrected_http_cache_integration_spec.rb +336 -0
  92. data/spec/lutaml/store/custom_serializer_spec.rb +108 -0
  93. data/spec/lutaml/store/database_store_spec.rb +279 -0
  94. data/spec/lutaml/store/file_io_spec.rb +219 -0
  95. data/spec/lutaml/store/format_round_trip_spec.rb +110 -0
  96. data/spec/lutaml/store/format_spec.rb +70 -0
  97. data/spec/lutaml/store/http_cache_entry_spec.rb +203 -0
  98. data/spec/lutaml/store/http_cache_hal_integration_spec.rb +404 -0
  99. data/spec/lutaml/store/http_cache_spec.rb +422 -0
  100. data/spec/lutaml/store/http_header_processor_spec.rb +290 -0
  101. data/spec/lutaml/store/import_spec.rb +90 -0
  102. data/spec/lutaml/store/integrity_spec.rb +157 -0
  103. data/spec/lutaml/store/key_collision_serializer_spec.rb +98 -0
  104. data/spec/lutaml/store/load_save_spec.rb +107 -0
  105. data/spec/lutaml/store/lutaml_model_integration_spec.rb +291 -0
  106. data/spec/lutaml/store/model_serializer_spec.rb +140 -0
  107. data/spec/lutaml/store/store_spec.rb +182 -0
  108. data/spec/lutaml/store_spec.rb +21 -0
  109. data/spec/spec_helper.rb +16 -0
  110. metadata +166 -0
data/plan.adoc ADDED
@@ -0,0 +1,606 @@
1
+ = Lutaml::Store database-style API implementation plan
2
+
3
+ == Overview
4
+
5
+ This document outlines the implementation plan for transforming Lutaml::Store
6
+ from a basic key-value store into a sophisticated store-centric database-style
7
+ API with model registry, polymorphic support, and composite model relationships.
8
+
9
+ == Current state analysis
10
+
11
+ === Existing implementation
12
+
13
+ The current Lutaml::Store provides:
14
+
15
+ * Basic key-value operations (get, set, delete, exists)
16
+
17
+ * Multiple storage adapters (memory, filesystem, sqlite)
18
+
19
+ * ModelStore class with basic model serialization
20
+
21
+ * Caching, monitoring, and event systems
22
+
23
+ * Thread-safe operations
24
+
25
+ === Limitations
26
+
27
+ The current implementation lacks:
28
+
29
+ * Model registry system with configurable key fields
30
+
31
+ * Polymorphic model support with inheritance handling
32
+
33
+ * Composite model relationships (nested registered models stored independently)
34
+
35
+ * Database-style CRUD operations (fetch, save, update, destroy)
36
+
37
+ * Dot notation for nested updates ("studio.location")
38
+
39
+ * Block-based and hash-based update patterns
40
+
41
+ == Target API specification
42
+
43
+ Based on sanity.rb requirements, the new API must support:
44
+
45
+ [source,ruby]
46
+ ----
47
+ # Store initialization with model registry
48
+ store = Lutaml::Store.new(
49
+ adapter: :memory,
50
+ models: [
51
+ { model: PotteryClass, key: :class_id },
52
+ { model: Studio, key: :studio_key, polymorphic_class_key: :_class }
53
+ ]
54
+ )
55
+
56
+ # Save operations
57
+ store.save(pottery_classes_array)
58
+
59
+ # Fetch operations
60
+ pottery_class = store.fetch(model: PotteryClass, class_id: "pottery_class")
61
+
62
+ # Update operations with attributes
63
+ store.update(
64
+ model: PotteryClass,
65
+ class_id: "clay_class",
66
+ attributes: [
67
+ { key: :description, value: "Updated description" },
68
+ { key: "studio.location", value: "Downtown" }
69
+ ]
70
+ )
71
+
72
+ # Polymorphic model updates
73
+ store.save(
74
+ model: PotteryClass,
75
+ class_id: "pottery_class",
76
+ attributes: [
77
+ {
78
+ key: :studio,
79
+ value: CeramicStudio.new(
80
+ studio_key: "pottery_studio",
81
+ name: "Ceramic studio",
82
+ clay_type: "Stoneware"
83
+ )
84
+ }
85
+ ]
86
+ )
87
+ ----
88
+
89
+ == Implementation phases
90
+
91
+ === Phase 1: Core architecture redesign
92
+
93
+ ==== 1.1 New store entry point
94
+
95
+ Create new main entry point that accepts model registry:
96
+
97
+ * Modify `lib/lutaml/store.rb` to provide `Lutaml::Store.new` class method
98
+
99
+ * Accept `adapter` and `models` parameters
100
+
101
+ * Delegate to enhanced ModelStore for model operations
102
+
103
+ * Maintain backward compatibility with existing Store class
104
+
105
+ ==== 1.2 Model registry system
106
+
107
+ Implement model registration and metadata management:
108
+
109
+ * Create `lib/lutaml/store/model_registry.rb`
110
+
111
+ * Create `lib/lutaml/store/model_registration.rb`
112
+
113
+ * Support model class, key field, and polymorphic_class_key configuration
114
+
115
+ * Validate model registrations (key fields must exist)
116
+
117
+ * Handle polymorphic inheritance chains
118
+
119
+ ==== 1.3 Composite model detection
120
+
121
+ Implement composite model relationship handling:
122
+
123
+ * Create `lib/lutaml/store/composite_model_handler.rb`
124
+
125
+ * Detect when registered models are nested within other models
126
+
127
+ * Track model relationships and references
128
+
129
+ * Store composite models independently while maintaining references
130
+
131
+ === Phase 2: Database-style operations
132
+
133
+ ==== 2.1 CRUD operations
134
+
135
+ Implement database-style model operations:
136
+
137
+ * `fetch(model:, key_value)` - retrieve model by key
138
+
139
+ * `save(model_or_array)` - save single model or array of models
140
+
141
+ * `update(model:, key_value, attributes:)` - update with attributes or block
142
+
143
+ * `destroy(model:, key_value)` - delete model
144
+
145
+ * Support both single models and arrays
146
+
147
+ ==== 2.2 Update mechanisms
148
+
149
+ Implement flexible update patterns:
150
+
151
+ * Create `lib/lutaml/store/attribute_updater.rb`
152
+
153
+ * Support attribute arrays with key/value pairs
154
+
155
+ * Support dot notation for nested updates ("studio.location")
156
+
157
+ * Support block-based updates with model yielding
158
+
159
+ * Handle polymorphic model updates (changing model types)
160
+
161
+ ==== 2.3 Key generation and management
162
+
163
+ Implement model key handling:
164
+
165
+ * Extract key values from models using configured key fields
166
+
167
+ * Generate storage keys with model namespacing
168
+
169
+ * Handle polymorphic class keys for inheritance
170
+
171
+ * Ensure unique keys across model types
172
+
173
+ === Phase 3: Advanced features
174
+
175
+ ==== 3.1 Polymorphic support
176
+
177
+ Implement inheritance and polymorphic handling:
178
+
179
+ * Detect polymorphic relationships using polymorphic_class_key
180
+
181
+ * Store and retrieve correct subclass instances
182
+
183
+ * Handle polymorphic updates that change model types
184
+
185
+ * Maintain polymorphic class information in storage
186
+
187
+ ==== 3.2 Nested attribute updates
188
+
189
+ Implement dot notation and nested updates:
190
+
191
+ * Parse dot notation ("studio.location") into nested attribute paths
192
+
193
+ * Navigate object graphs to update nested attributes
194
+
195
+ * Support updates on composite registered models
196
+
197
+ * Handle deep nesting and complex object structures
198
+
199
+ ==== 3.3 Reference management
200
+
201
+ Implement model relationship tracking:
202
+
203
+ * Track relationships between composite models
204
+
205
+ * Update references when composite models change
206
+
207
+ * Maintain referential integrity
208
+
209
+ * Handle cascading updates for related models
210
+
211
+ === Phase 4: Integration and testing
212
+
213
+ ==== 4.1 Adapter integration
214
+
215
+ Ensure compatibility with existing adapters:
216
+
217
+ * Test all adapters (memory, filesystem, sqlite) with new operations
218
+
219
+ * Maintain existing adapter interfaces
220
+
221
+ * Update adapter implementations if needed
222
+
223
+ * Preserve performance characteristics
224
+
225
+ ==== 4.2 Event system integration
226
+
227
+ Integrate with existing event system:
228
+
229
+ * Emit appropriate events for model operations
230
+
231
+ * Maintain existing event patterns for compatibility
232
+
233
+ * Add new events for model-specific operations
234
+
235
+ * Support both sync and async event handling
236
+
237
+ ==== 4.3 Comprehensive testing
238
+
239
+ Implement thorough test coverage:
240
+
241
+ * Test all sanity.rb scenarios
242
+
243
+ * Test polymorphic inheritance patterns
244
+
245
+ * Test composite model relationships
246
+
247
+ * Test nested updates with dot notation
248
+
249
+ * Test error conditions and edge cases
250
+
251
+ == Detailed implementation specifications
252
+
253
+ === Core classes to implement
254
+
255
+ ==== Lutaml::Store (main entry point)
256
+
257
+ [source,ruby]
258
+ ----
259
+ module Lutaml
260
+ module Store
261
+ def self.new(adapter:, models: [])
262
+ # Create enhanced ModelStore with registry
263
+ ModelStore.new(adapter: adapter, models: models)
264
+ end
265
+ end
266
+ end
267
+ ----
268
+
269
+ ==== Lutaml::Store::ModelRegistry
270
+
271
+ [source,ruby]
272
+ ----
273
+ class ModelRegistry
274
+ def initialize(model_configs)
275
+ @registrations = {}
276
+ register_models(model_configs)
277
+ end
278
+
279
+ def register(model_class, key_field, options = {})
280
+ # Create ModelRegistration instance
281
+ # Validate key field exists on model
282
+ # Handle polymorphic_class_key option
283
+ end
284
+
285
+ def find_registration(model_class)
286
+ # Find registration for model class or superclass
287
+ end
288
+
289
+ def registered?(model_class)
290
+ # Check if model is registered
291
+ end
292
+ end
293
+ ----
294
+
295
+ ==== Lutaml::Store::ModelRegistration
296
+
297
+ [source,ruby]
298
+ ----
299
+ class ModelRegistration
300
+ attr_reader :model_class, :key_field, :polymorphic_class_key
301
+
302
+ def initialize(model_class, key_field, options = {})
303
+ @model_class = model_class
304
+ @key_field = key_field
305
+ @polymorphic_class_key = options[:polymorphic_class_key]
306
+ validate!
307
+ end
308
+
309
+ def extract_key(model)
310
+ # Extract key value from model instance
311
+ end
312
+
313
+ def polymorphic?
314
+ # Check if registration supports polymorphism
315
+ end
316
+ end
317
+ ----
318
+
319
+ ==== Lutaml::Store::CompositeModelHandler
320
+
321
+ [source,ruby]
322
+ ----
323
+ class CompositeModelHandler
324
+ def initialize(registry)
325
+ @registry = registry
326
+ end
327
+
328
+ def extract_composite_models(model)
329
+ # Find nested registered models
330
+ # Return hash of composite models with their keys
331
+ end
332
+
333
+ def store_composite_models(composite_models, store)
334
+ # Store each composite model independently
335
+ end
336
+
337
+ def resolve_composite_models(model, store)
338
+ # Replace composite model references with actual instances
339
+ end
340
+ end
341
+ ----
342
+
343
+ ==== Lutaml::Store::AttributeUpdater
344
+
345
+ [source,ruby]
346
+ ----
347
+ class AttributeUpdater
348
+ def initialize(registry, store)
349
+ @registry = registry
350
+ @store = store
351
+ end
352
+
353
+ def update_attributes(model, attributes)
354
+ # Apply attribute updates to model
355
+ # Handle dot notation for nested updates
356
+ # Support polymorphic model changes
357
+ end
358
+
359
+ def parse_nested_path(path)
360
+ # Parse "studio.location" into ["studio", "location"]
361
+ end
362
+
363
+ def apply_nested_update(model, path_parts, value)
364
+ # Navigate object graph and apply update
365
+ end
366
+ end
367
+ ----
368
+
369
+ ==== Enhanced Lutaml::Store::ModelStore
370
+
371
+ [source,ruby]
372
+ ----
373
+ class ModelStore
374
+ def initialize(adapter:, models: [])
375
+ @store = Store.new(adapter: adapter)
376
+ @registry = ModelRegistry.new(models)
377
+ @composite_handler = CompositeModelHandler.new(@registry)
378
+ @attribute_updater = AttributeUpdater.new(@registry, @store)
379
+ end
380
+
381
+ def save(model_or_array)
382
+ # Handle single model or array
383
+ # Extract and store composite models
384
+ # Generate storage keys
385
+ # Serialize and store models
386
+ end
387
+
388
+ def fetch(model:, **key_params)
389
+ # Extract key value from parameters
390
+ # Generate storage key
391
+ # Retrieve and deserialize model
392
+ # Resolve composite model references
393
+ end
394
+
395
+ def update(model:, attributes: nil, **key_params, &block)
396
+ # Fetch existing model
397
+ # Apply updates (attributes or block)
398
+ # Handle composite model updates
399
+ # Store updated model
400
+ end
401
+
402
+ def destroy(model:, **key_params)
403
+ # Generate storage key
404
+ # Delete model from store
405
+ # Handle composite model cleanup
406
+ end
407
+ end
408
+ ----
409
+
410
+ === Storage key generation
411
+
412
+ Model storage keys will follow this pattern:
413
+
414
+ * Simple models: `"#{model_class_name}:#{key_value}"`
415
+
416
+ * Polymorphic models: `"#{polymorphic_class}:#{key_value}"`
417
+
418
+ * Composite models: Stored with their own keys, referenced by parent
419
+
420
+ === Serialization strategy
421
+
422
+ * Use existing Serializer class for model serialization
423
+
424
+ * Store polymorphic class information with model data
425
+
426
+ * Handle composite model references during serialization/deserialization
427
+
428
+ * Maintain compatibility with existing serialization formats
429
+
430
+ === Error handling
431
+
432
+ Define specific error types:
433
+
434
+ * `ModelNotRegisteredError` - when trying to operate on unregistered model
435
+
436
+ * `InvalidKeyError` - when key field doesn't exist or is nil
437
+
438
+ * `PolymorphicUpdateError` - when polymorphic update fails
439
+
440
+ * `CompositeModelError` - when composite model handling fails
441
+
442
+ == Migration strategy
443
+
444
+ === Backward compatibility
445
+
446
+ * Maintain existing Store and ModelStore classes
447
+
448
+ * Provide new entry point through `Lutaml::Store.new`
449
+
450
+ * Existing code continues to work unchanged
451
+
452
+ * Gradual migration path for users
453
+
454
+ === Deprecation plan
455
+
456
+ * Mark old interfaces as deprecated in documentation
457
+
458
+ * Provide migration examples in README
459
+
460
+ * Plan removal in future major version
461
+
462
+ * Clear upgrade path for all use cases
463
+
464
+ == Testing strategy
465
+
466
+ === Unit tests
467
+
468
+ * Test each new class in isolation
469
+
470
+ * Mock dependencies for focused testing
471
+
472
+ * Cover all error conditions
473
+
474
+ * Test edge cases and boundary conditions
475
+
476
+ === Integration tests
477
+
478
+ * Test complete workflows from sanity.rb
479
+
480
+ * Test all adapter types with new operations
481
+
482
+ * Test polymorphic inheritance scenarios
483
+
484
+ * Test composite model relationships
485
+
486
+ === Performance tests
487
+
488
+ * Benchmark new operations against current implementation
489
+
490
+ * Ensure no performance regression
491
+
492
+ * Test with large datasets
493
+
494
+ * Profile memory usage
495
+
496
+ == Documentation updates
497
+
498
+ === README.adoc
499
+
500
+ Complete rewrite following documentation formatting rules:
501
+
502
+ * Sentence-case headings
503
+
504
+ * Proper list formatting with blank lines
505
+
506
+ * Example blocks with `[example]` and `====`
507
+
508
+ * Source code blocks with `[source,{lang}]` and `----`
509
+
510
+ * Line wrapping at 80 characters
511
+
512
+ * Comprehensive API documentation with examples
513
+
514
+ === API documentation
515
+
516
+ * Document all new classes and methods
517
+
518
+ * Provide usage examples for each operation
519
+
520
+ * Document error conditions and handling
521
+
522
+ * Include migration guide from old API
523
+
524
+ == Implementation timeline
525
+
526
+ === Week 1: Core architecture
527
+
528
+ * Implement ModelRegistry and ModelRegistration
529
+
530
+ * Create new Store entry point
531
+
532
+ * Basic model save/fetch operations
533
+
534
+ === Week 2: Update mechanisms
535
+
536
+ * Implement AttributeUpdater
537
+
538
+ * Support dot notation updates
539
+
540
+ * Block-based updates
541
+
542
+ * Polymorphic model updates
543
+
544
+ === Week 3: Composite models
545
+
546
+ * Implement CompositeModelHandler
547
+
548
+ * Nested model detection and storage
549
+
550
+ * Reference resolution
551
+
552
+ * Relationship management
553
+
554
+ === Week 4: Integration and testing
555
+
556
+ * Comprehensive test suite
557
+
558
+ * Documentation updates
559
+
560
+ * Performance optimization
561
+
562
+ * Bug fixes and refinements
563
+
564
+ == Success criteria
565
+
566
+ The implementation will be considered successful when:
567
+
568
+ * All sanity.rb scenarios work correctly
569
+
570
+ * Existing tests continue to pass
571
+
572
+ * New comprehensive test suite passes
573
+
574
+ * Documentation is complete and accurate
575
+
576
+ * Performance is maintained or improved
577
+
578
+ * Migration path is clear and documented
579
+
580
+ == Risk mitigation
581
+
582
+ === Technical risks
583
+
584
+ * **Complexity**: Break down into small, testable components
585
+
586
+ * **Performance**: Benchmark and optimize critical paths
587
+
588
+ * **Compatibility**: Maintain existing interfaces during transition
589
+
590
+ === Project risks
591
+
592
+ * **Scope creep**: Stick to sanity.rb requirements
593
+
594
+ * **Timeline**: Prioritize core functionality first
595
+
596
+ * **Quality**: Comprehensive testing at each phase
597
+
598
+ == Conclusion
599
+
600
+ This implementation plan provides a clear roadmap for transforming Lutaml::Store
601
+ into a sophisticated database-style API while maintaining backward compatibility
602
+ and ensuring high quality through comprehensive testing and documentation.
603
+
604
+ The phased approach allows for incremental development and testing, reducing
605
+ risk and ensuring each component works correctly before moving to the next
606
+ phase.
@@ -0,0 +1,6 @@
1
+ module Lutaml
2
+ module Store
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe Lutaml::Store::Adapter::Base do
6
+ subject(:adapter) { described_class.new }
7
+
8
+ it "raises NotImplementedError for get" do
9
+ expect { adapter.get("key") }.to raise_error(NotImplementedError)
10
+ end
11
+
12
+ it "raises NotImplementedError for set" do
13
+ expect { adapter.set("key", "value") }.to raise_error(NotImplementedError)
14
+ end
15
+
16
+ it "raises NotImplementedError for delete" do
17
+ expect { adapter.delete("key") }.to raise_error(NotImplementedError)
18
+ end
19
+
20
+ it "raises NotImplementedError for exists?" do
21
+ expect { adapter.exists?("key") }.to raise_error(NotImplementedError)
22
+ end
23
+
24
+ it "raises NotImplementedError for keys" do
25
+ expect { adapter.keys }.to raise_error(NotImplementedError)
26
+ end
27
+
28
+ it "raises NotImplementedError for all" do
29
+ expect { adapter.all }.to raise_error(NotImplementedError)
30
+ end
31
+
32
+ it "raises NotImplementedError for clear" do
33
+ expect { adapter.clear }.to raise_error(NotImplementedError)
34
+ end
35
+
36
+ it "raises NotImplementedError for size" do
37
+ expect { adapter.size }.to raise_error(NotImplementedError)
38
+ end
39
+
40
+ it "provides default close that does not raise" do
41
+ expect { adapter.close }.not_to raise_error
42
+ end
43
+
44
+ describe "default bulk operations" do
45
+ let(:concrete_adapter) do
46
+ Class.new(described_class) do
47
+ def initialize
48
+ super
49
+ @data = {}
50
+ end
51
+
52
+ def get(key)
53
+ @data[key]
54
+ end
55
+
56
+ def set(key, value)
57
+ @data[key] = value
58
+ end
59
+
60
+ def delete(key)
61
+ @data.delete(key)
62
+ end
63
+ end.new
64
+ end
65
+
66
+ it "bulk_get iterates over get" do
67
+ concrete_adapter.set("a", 1)
68
+ concrete_adapter.set("b", 2)
69
+ expect(concrete_adapter.bulk_get(%w[a b c])).to eq("a" => 1, "b" => 2, "c" => nil)
70
+ end
71
+
72
+ it "bulk_set iterates over set" do
73
+ concrete_adapter.bulk_set("a" => 1, "b" => 2)
74
+ expect(concrete_adapter.get("a")).to eq(1)
75
+ expect(concrete_adapter.get("b")).to eq(2)
76
+ end
77
+
78
+ it "bulk_delete iterates over delete" do
79
+ concrete_adapter.set("a", 1)
80
+ concrete_adapter.set("b", 2)
81
+ result = concrete_adapter.bulk_delete(%w[a c])
82
+ expect(result).to eq("a" => 1, "c" => nil)
83
+ end
84
+ end
85
+
86
+ it "provides default stats" do
87
+ expect(adapter.stats).to eq({ adapter: described_class.name })
88
+ end
89
+ end