lutaml-store 0.1.1 → 0.2.0

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.
@@ -100,10 +100,16 @@ module Lutaml
100
100
 
101
101
  parts.each_with_index do |part, index|
102
102
  if part.match?(/\A\d+\z/)
103
- raise InvalidKeyError, "Expected array at #{parts[0..index - 1].join(".")}, got #{current.class}" unless current.is_a?(Array)
103
+ unless current.is_a?(Array)
104
+ raise InvalidKeyError,
105
+ "Expected array at #{parts[0..index - 1].join(".")}, got #{current.class}"
106
+ end
104
107
 
105
108
  index_val = part.to_i
106
- raise InvalidKeyError, "Array index #{index_val} out of bounds for #{parts[0..index - 1].join(".")}" if index_val >= current.length
109
+ if index_val >= current.length
110
+ raise InvalidKeyError,
111
+ "Array index #{index_val} out of bounds for #{parts[0..index - 1].join(".")}"
112
+ end
107
113
 
108
114
  current = current[index_val]
109
115
  else
@@ -130,7 +136,10 @@ module Lutaml
130
136
  end
131
137
 
132
138
  current_model = model.public_send(attr_name)
133
- validate_polymorphic_key_compatibility!(current_model, new_polymorphic_model) if current_model && @registry.registered?(current_model.class)
139
+ if current_model && @registry.registered?(current_model.class)
140
+ validate_polymorphic_key_compatibility!(current_model,
141
+ new_polymorphic_model)
142
+ end
134
143
 
135
144
  model.public_send("#{attr_name}=", new_polymorphic_model)
136
145
  end
@@ -98,6 +98,10 @@ module Lutaml
98
98
  @adapter.keys
99
99
  end
100
100
 
101
+ def each_key(&block)
102
+ @adapter.keys.each(&block)
103
+ end
104
+
101
105
  def bulk_get(keys)
102
106
  @adapter.bulk_get(keys)
103
107
  end
@@ -170,7 +170,7 @@ module Lutaml
170
170
  def cleanup_expired
171
171
  expired_keys = []
172
172
 
173
- @adapter.keys.each do |key|
173
+ @adapter.each_key do |key|
174
174
  entry_data = @adapter.get(key)
175
175
  next unless entry_data
176
176
 
@@ -106,7 +106,7 @@ module Lutaml
106
106
  registration = @registry.registration_for(model)
107
107
 
108
108
  models = []
109
- @store.keys.each do |storage_key|
109
+ @store.each_key do |storage_key|
110
110
  parsed = StorageKey.parse(storage_key.to_s)
111
111
  next unless parsed.class_name == model.name
112
112
 
@@ -13,11 +13,11 @@ module Lutaml
13
13
  end
14
14
 
15
15
  def serialize(model)
16
- model.to_yaml
16
+ model.to_yamls
17
17
  end
18
18
 
19
19
  def serialize_many(models)
20
- models.map(&:to_yaml).join
20
+ models.map(&:to_yamls).join
21
21
  end
22
22
 
23
23
  def deserialize(data, model_class)
@@ -236,7 +236,10 @@ module Lutaml
236
236
 
237
237
  # Extract vary header values from request
238
238
  request_vary_headers = {}
239
- request_vary_headers = HttpHeaderProcessor.extract_vary_headers(request_headers, vary_headers) if vary_headers.any?
239
+ if vary_headers.any?
240
+ request_vary_headers = HttpHeaderProcessor.extract_vary_headers(request_headers,
241
+ vary_headers)
242
+ end
240
243
 
241
244
  HttpCacheEntry.new(
242
245
  cache_key: cache_key,
@@ -89,7 +89,10 @@ module Lutaml
89
89
  attr_value = model.public_send(attr_name)
90
90
  next if attr_value.nil?
91
91
 
92
- add_composite_entry(composite_models, attr_name, attr_value) if attr_value.is_a?(Object) && registered?(attr_value.class)
92
+ if attr_value.is_a?(Object) && registered?(attr_value.class)
93
+ add_composite_entry(composite_models, attr_name,
94
+ attr_value)
95
+ end
93
96
 
94
97
  next unless attr_value.is_a?(Array)
95
98
 
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Store
5
+ class PackageDefinition
6
+ attr_reader :name, :model_entries, :asset_entries,
7
+ :metadata_model, :metadata_file, :metadata_key
8
+
9
+ def initialize(name:, metadata_model: nil, metadata_file: nil,
10
+ metadata_key: :shortname)
11
+ @name = name
12
+ @metadata_model = metadata_model
13
+ @metadata_file = metadata_file
14
+ @metadata_key = metadata_key
15
+ @model_entries = []
16
+ @asset_entries = []
17
+ yield self if block_given?
18
+ end
19
+
20
+ def model(model:, key:, dir: nil, file: nil, layout: :separate,
21
+ default_format: :yaml, serializer: nil)
22
+ raise ArgumentError, "Specify dir: or file:, not both" if dir && file
23
+
24
+ @model_entries << ModelEntry.new(
25
+ model: model, dir: dir, file: file, layout: layout,
26
+ key: key, default_format: default_format, serializer: serializer
27
+ )
28
+ end
29
+
30
+ def asset(path, type: :file)
31
+ @asset_entries << AssetEntry.new(path: path, type: type)
32
+ end
33
+
34
+ def entry_for(model_class)
35
+ @model_entries.find { |e| e.model == model_class }
36
+ end
37
+
38
+ def model_classes
39
+ @model_entries.map(&:model)
40
+ end
41
+
42
+ def database_store_models
43
+ @model_entries.map do |e|
44
+ config = { model: e.model, key: e.key }
45
+ config[:serializer] = e.serializer if e.serializer
46
+ config[:dir] = e.dir if e.dir
47
+ config
48
+ end
49
+ end
50
+
51
+ ModelEntry = Struct.new(
52
+ :model, :dir, :file, :layout, :key, :default_format, :serializer,
53
+ keyword_init: true
54
+ )
55
+
56
+ AssetEntry = Struct.new(
57
+ :path, :type,
58
+ keyword_init: true
59
+ )
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Store
5
+ class PackageStore
6
+ attr_accessor :metadata
7
+ attr_reader :definition, :assets
8
+
9
+ def initialize(definition)
10
+ @definition = definition
11
+ @db = DatabaseStore.new(
12
+ adapter: :memory,
13
+ models: definition.database_store_models
14
+ )
15
+ @assets = {}
16
+ @metadata = nil
17
+ end
18
+
19
+ # ── Load / Save ──
20
+
21
+ def self.load(definition, path, transport: :directory, format: nil)
22
+ store = new(definition)
23
+ transporter = resolve_transport(transport)
24
+ transporter.read(path, store, format: format)
25
+ store
26
+ end
27
+
28
+ def save(path, transport: :directory, format: nil, formats: {})
29
+ resolved_formats = resolve_formats(format, formats)
30
+ transporter = self.class.resolve_transport(transport)
31
+ transporter.write(path, self, formats: resolved_formats)
32
+ end
33
+
34
+ # ── Model CRUD ──
35
+
36
+ def add_model(model_instance)
37
+ @db.save(model_instance)
38
+ end
39
+
40
+ def add_models(model_instances)
41
+ model_instances.each { |m| add_model(m) }
42
+ end
43
+
44
+ def fetch_model(model_class, key)
45
+ entry = definition.entry_for(model_class)
46
+ @db.fetch(model: model_class, **{ entry.key => key })
47
+ end
48
+
49
+ def models_for(model_class)
50
+ @db.all(model: model_class)
51
+ end
52
+
53
+ def model_count(model_class)
54
+ @db.count(model: model_class)
55
+ end
56
+
57
+ def model_exists?(model_class, key)
58
+ entry = definition.entry_for(model_class)
59
+ @db.exists?(model: model_class, **{ entry.key => key })
60
+ end
61
+
62
+ def remove_model(model_class, key)
63
+ entry = definition.entry_for(model_class)
64
+ @db.destroy(model: model_class, **{ entry.key => key })
65
+ end
66
+
67
+ # ── Metadata ──
68
+
69
+ # ── Assets ──
70
+
71
+ def asset(path)
72
+ @assets[path]
73
+ end
74
+
75
+ def add_asset(path, content)
76
+ @assets[path] = content
77
+ end
78
+
79
+ def asset_paths
80
+ @assets.keys
81
+ end
82
+
83
+ def remove_asset(path)
84
+ @assets.delete(path)
85
+ end
86
+
87
+ # ── Bulk ──
88
+
89
+ def clear_models(model_class)
90
+ @db.all(model: model_class).each do |m|
91
+ entry = definition.entry_for(model_class)
92
+ key = m.public_send(entry.key)
93
+ @db.destroy(model: model_class, **{ entry.key => key })
94
+ end
95
+ end
96
+
97
+ def clear_assets
98
+ @assets.clear
99
+ end
100
+
101
+ def clear_all
102
+ definition.model_classes.each { |mc| clear_models(mc) }
103
+ clear_assets
104
+ @metadata = nil
105
+ end
106
+
107
+ # ── Stats ──
108
+
109
+ def stats
110
+ model_stats = definition.model_classes
111
+ .map { |mc| [mc.name, model_count(mc)] }
112
+ .to_h
113
+ {
114
+ package: definition.name,
115
+ models: model_stats,
116
+ assets: @assets.size,
117
+ metadata: @metadata ? true : false
118
+ }
119
+ end
120
+
121
+ # Public access for PackageTransport (avoids instance_variable_get).
122
+ attr_reader :db
123
+
124
+ private
125
+
126
+ def self.resolve_transport(transport)
127
+ case transport
128
+ when :directory, "directory"
129
+ PackageTransport::DirectoryTransport.new
130
+ when :zip, "zip"
131
+ PackageTransport::ZipTransport.new
132
+ else
133
+ raise ConfigurationError, "Unknown transport: #{transport}"
134
+ end
135
+ end
136
+
137
+ def resolve_formats(global_format, per_model_formats)
138
+ if global_format
139
+ definition.model_entries
140
+ .map { |e| [e.model, global_format] }
141
+ .to_h
142
+ .merge(per_model_formats)
143
+ else
144
+ per_model_formats
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end