lutaml-store 0.1.1 → 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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +11 -371
- data/README.adoc +233 -1124
- data/lib/lutaml/store/adapter/base.rb +4 -0
- data/lib/lutaml/store/adapter/memory.rb +8 -0
- data/lib/lutaml/store/attribute_updater.rb +12 -3
- data/lib/lutaml/store/basic_store.rb +4 -0
- data/lib/lutaml/store/cache_store.rb +10 -7
- data/lib/lutaml/store/database_store.rb +1 -1
- data/lib/lutaml/store/format/yamls.rb +2 -2
- data/lib/lutaml/store/format.rb +19 -0
- data/lib/lutaml/store/http_cache.rb +7 -14
- data/lib/lutaml/store/model_registration.rb +5 -2
- data/lib/lutaml/store/model_registry.rb +22 -17
- data/lib/lutaml/store/package_definition.rb +62 -0
- data/lib/lutaml/store/package_store.rb +133 -0
- data/lib/lutaml/store/package_transport/base.rb +48 -0
- data/lib/lutaml/store/package_transport/directory_transport.rb +196 -0
- data/lib/lutaml/store/package_transport/zip_transport.rb +178 -0
- data/lib/lutaml/store/package_transport.rb +23 -0
- data/lib/lutaml/store/version.rb +1 -1
- data/lib/lutaml/store.rb +3 -0
- metadata +29 -70
- data/.github/workflows/main.yml +0 -27
- data/.gitignore +0 -12
- data/CORRECTED_HTTP_CACHE_IMPLEMENTATION.md +0 -209
- data/CORRECTED_HTTP_CACHE_PLAN.md +0 -164
- data/Gemfile +0 -15
- data/Gemfile.lock +0 -220
- data/TODO.impl/0-lutaml-store-self-quality.md +0 -112
- data/TODO.impl/1-lutaml-hal-migration.md +0 -60
- data/TODO.impl/2-glossarist-migration.md +0 -359
- data/TODO.impl/3-lutaml-jsonschema-migration.md +0 -273
- data/bin/console +0 -11
- data/bin/setup +0 -8
- data/demo/Gemfile +0 -15
- data/demo/Gemfile.lock +0 -61
- data/demo/README.adoc +0 -301
- data/demo/data/vcards/co/contact_10_thompson.data +0 -1
- data/demo/data/vcards/co/contact_10_thompson.meta +0 -1
- data/demo/data/vcards/co/contact_1_doe.data +0 -1
- data/demo/data/vcards/co/contact_1_doe.meta +0 -1
- data/demo/data/vcards/co/contact_2_smith.data +0 -1
- data/demo/data/vcards/co/contact_2_smith.meta +0 -1
- data/demo/data/vcards/co/contact_3_johnson.data +0 -1
- data/demo/data/vcards/co/contact_3_johnson.meta +0 -1
- data/demo/data/vcards/co/contact_4_garcia.data +0 -1
- data/demo/data/vcards/co/contact_4_garcia.meta +0 -1
- data/demo/data/vcards/co/contact_5_wilson.data +0 -1
- data/demo/data/vcards/co/contact_5_wilson.meta +0 -1
- data/demo/data/vcards/co/contact_6_brown.data +0 -1
- data/demo/data/vcards/co/contact_6_brown.meta +0 -1
- data/demo/data/vcards/co/contact_7_davis.data +0 -1
- data/demo/data/vcards/co/contact_7_davis.meta +0 -1
- data/demo/data/vcards/co/contact_8_anderson.data +0 -1
- data/demo/data/vcards/co/contact_8_anderson.meta +0 -1
- data/demo/data/vcards/co/contact_9_taylor.data +0 -1
- data/demo/data/vcards/co/contact_9_taylor.meta +0 -1
- data/demo/data/vcards.db +0 -0
- data/demo/pottery_class_demo.rb +0 -164
- data/demo/vcard_models.rb +0 -140
- data/demo/vcard_store_demo.rb +0 -526
- data/lutaml-store.gemspec +0 -35
- data/plan.adoc +0 -606
- data/spec/lutaml/store/adapter_interface_spec.rb +0 -89
- data/spec/lutaml/store/anti_pattern_guard_spec.rb +0 -35
- data/spec/lutaml/store/anti_pattern_spec.rb +0 -78
- data/spec/lutaml/store/autoload_spec.rb +0 -34
- data/spec/lutaml/store/cache_store_spec.rb +0 -271
- data/spec/lutaml/store/compression_spec.rb +0 -78
- data/spec/lutaml/store/config_enhanced_spec.rb +0 -158
- data/spec/lutaml/store/corrected_http_cache_integration_spec.rb +0 -336
- data/spec/lutaml/store/custom_serializer_spec.rb +0 -108
- data/spec/lutaml/store/database_store_spec.rb +0 -279
- data/spec/lutaml/store/file_io_spec.rb +0 -219
- data/spec/lutaml/store/format_round_trip_spec.rb +0 -110
- data/spec/lutaml/store/format_spec.rb +0 -70
- data/spec/lutaml/store/http_cache_entry_spec.rb +0 -203
- data/spec/lutaml/store/http_cache_hal_integration_spec.rb +0 -404
- data/spec/lutaml/store/http_cache_spec.rb +0 -422
- data/spec/lutaml/store/http_header_processor_spec.rb +0 -290
- data/spec/lutaml/store/import_spec.rb +0 -90
- data/spec/lutaml/store/integrity_spec.rb +0 -157
- data/spec/lutaml/store/key_collision_serializer_spec.rb +0 -98
- data/spec/lutaml/store/load_save_spec.rb +0 -107
- data/spec/lutaml/store/lutaml_model_integration_spec.rb +0 -291
- data/spec/lutaml/store/model_serializer_spec.rb +0 -140
- data/spec/lutaml/store/store_spec.rb +0 -182
- data/spec/lutaml/store_spec.rb +0 -21
- data/spec/spec_helper.rb +0 -16
|
@@ -93,6 +93,14 @@ module Lutaml
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
def each_key(&block)
|
|
97
|
+
current_keys = @mutex.synchronize do
|
|
98
|
+
cleanup_expired if @ttl_enabled
|
|
99
|
+
@data.keys
|
|
100
|
+
end
|
|
101
|
+
current_keys.each(&block)
|
|
102
|
+
end
|
|
103
|
+
|
|
96
104
|
def close
|
|
97
105
|
@mutex.synchronize do
|
|
98
106
|
@data.clear
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -170,7 +170,7 @@ module Lutaml
|
|
|
170
170
|
def cleanup_expired
|
|
171
171
|
expired_keys = []
|
|
172
172
|
|
|
173
|
-
@adapter.
|
|
173
|
+
@adapter.each_key do |key|
|
|
174
174
|
entry_data = @adapter.get(key)
|
|
175
175
|
next unless entry_data
|
|
176
176
|
|
|
@@ -222,15 +222,18 @@ module Lutaml
|
|
|
222
222
|
end
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
-
def fetch(key, ttl: :default, metadata: {}
|
|
225
|
+
def fetch(key, default = nil, ttl: :default, metadata: {})
|
|
226
226
|
value = get(key)
|
|
227
227
|
return value unless value.nil?
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
229
|
+
if block_given?
|
|
230
|
+
value = yield
|
|
231
|
+
set(key, value, ttl: ttl, metadata: metadata)
|
|
232
|
+
value
|
|
233
|
+
elsif !default.nil?
|
|
234
|
+
set(key, default, ttl: ttl, metadata: metadata)
|
|
235
|
+
default
|
|
236
|
+
end
|
|
234
237
|
end
|
|
235
238
|
|
|
236
239
|
def close
|
|
@@ -106,7 +106,7 @@ module Lutaml
|
|
|
106
106
|
registration = @registry.registration_for(model)
|
|
107
107
|
|
|
108
108
|
models = []
|
|
109
|
-
@store.
|
|
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
|
|
data/lib/lutaml/store/format.rb
CHANGED
|
@@ -28,6 +28,25 @@ module Lutaml
|
|
|
28
28
|
|
|
29
29
|
const_get(entry).new
|
|
30
30
|
end
|
|
31
|
+
|
|
32
|
+
def self.for_extension(ext)
|
|
33
|
+
extension_map[ext] || extension_map[".#{ext.to_s.sub(/\A\./, "")}"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private_class_method def self.extension_map
|
|
37
|
+
@extension_map ||= begin
|
|
38
|
+
map = {}
|
|
39
|
+
FORMATS.each_value do |class_name|
|
|
40
|
+
fmt = const_get(class_name).new
|
|
41
|
+
ext = fmt.extension
|
|
42
|
+
next if map.key?(ext)
|
|
43
|
+
|
|
44
|
+
map[ext] = fmt
|
|
45
|
+
map[".yml"] = fmt if ext == ".yaml"
|
|
46
|
+
end
|
|
47
|
+
map.freeze
|
|
48
|
+
end
|
|
49
|
+
end
|
|
31
50
|
end
|
|
32
51
|
end
|
|
33
52
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
3
|
module Lutaml
|
|
6
4
|
module Store
|
|
7
5
|
class HttpCache
|
|
@@ -145,9 +143,7 @@ module Lutaml
|
|
|
145
143
|
data = @adapter.get(key)
|
|
146
144
|
next unless data
|
|
147
145
|
|
|
148
|
-
|
|
149
|
-
entry = HttpCacheEntry.from_hash(entry_data)
|
|
150
|
-
entries << entry
|
|
146
|
+
entries << HttpCacheEntry.from_json(data)
|
|
151
147
|
rescue StandardError
|
|
152
148
|
# Skip invalid entries
|
|
153
149
|
end
|
|
@@ -174,20 +170,14 @@ module Lutaml
|
|
|
174
170
|
data = @adapter.get(cache_key)
|
|
175
171
|
return nil unless data
|
|
176
172
|
|
|
177
|
-
|
|
178
|
-
entry_data = data.is_a?(String) ? JSON.parse(data) : data
|
|
179
|
-
HttpCacheEntry.from_hash(entry_data)
|
|
173
|
+
HttpCacheEntry.from_json(data)
|
|
180
174
|
rescue StandardError
|
|
181
|
-
# Log error and continue without cache
|
|
182
175
|
nil
|
|
183
176
|
end
|
|
184
177
|
|
|
185
178
|
def store_entry(cache_key, entry)
|
|
186
|
-
|
|
187
|
-
data = entry.to_hash
|
|
188
|
-
@adapter.set(cache_key, data)
|
|
179
|
+
@adapter.set(cache_key, entry.to_json)
|
|
189
180
|
rescue StandardError
|
|
190
|
-
# Log error but don't fail the request
|
|
191
181
|
false
|
|
192
182
|
end
|
|
193
183
|
|
|
@@ -236,7 +226,10 @@ module Lutaml
|
|
|
236
226
|
|
|
237
227
|
# Extract vary header values from request
|
|
238
228
|
request_vary_headers = {}
|
|
239
|
-
|
|
229
|
+
if vary_headers.any?
|
|
230
|
+
request_vary_headers = HttpHeaderProcessor.extract_vary_headers(request_headers,
|
|
231
|
+
vary_headers)
|
|
232
|
+
end
|
|
240
233
|
|
|
241
234
|
HttpCacheEntry.new(
|
|
242
235
|
cache_key: cache_key,
|
|
@@ -4,14 +4,17 @@ module Lutaml
|
|
|
4
4
|
module Store
|
|
5
5
|
# Represents a single model registration with its metadata
|
|
6
6
|
class ModelRegistration
|
|
7
|
-
attr_reader :model_class, :key_field, :polymorphic_class_key, :serializer, :dir
|
|
7
|
+
attr_reader :model_class, :key_field, :polymorphic_class_key, :serializer, :dir,
|
|
8
|
+
:composites
|
|
8
9
|
|
|
9
|
-
def initialize(model_class, key_field, polymorphic_class_key: nil, serializer: nil,
|
|
10
|
+
def initialize(model_class, key_field, polymorphic_class_key: nil, serializer: nil,
|
|
11
|
+
dir: nil, composites: [])
|
|
10
12
|
@model_class = model_class
|
|
11
13
|
@key_field = key_field.to_sym
|
|
12
14
|
@polymorphic_class_key = polymorphic_class_key&.to_sym
|
|
13
15
|
@serializer = serializer
|
|
14
16
|
@dir = dir
|
|
17
|
+
@composites = composites.map(&:to_sym)
|
|
15
18
|
validate!
|
|
16
19
|
end
|
|
17
20
|
|
|
@@ -81,31 +81,36 @@ module Lutaml
|
|
|
81
81
|
@registrations.clear
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
# Find
|
|
84
|
+
# Find declared composite models nested within the given model
|
|
85
85
|
def find_composite_models(model)
|
|
86
|
-
|
|
86
|
+
registration = find_registration(model.class)
|
|
87
|
+
return {} unless registration&.composites&.any?
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
add_composite_entry(composite_models, attr_name, attr_value) if attr_value.is_a?(Object) && registered?(attr_value.class)
|
|
89
|
+
registration.composites.each_with_object({}) do |attr_name, result|
|
|
90
|
+
collect_composite(model, attr_name, result)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
private
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
def collect_composite(model, attr_name, result)
|
|
97
|
+
attr_value = model.public_send(attr_name)
|
|
98
|
+
return if attr_value.nil?
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
if attr_value.is_a?(Array)
|
|
101
|
+
collect_array_composites(attr_value, attr_name, result)
|
|
102
|
+
elsif registered?(attr_value.class)
|
|
103
|
+
add_composite_entry(result, attr_name, attr_value)
|
|
101
104
|
end
|
|
102
|
-
|
|
103
|
-
composite_models
|
|
104
|
-
rescue NoMethodError
|
|
105
|
-
{}
|
|
106
105
|
end
|
|
107
106
|
|
|
108
|
-
|
|
107
|
+
def collect_array_composites(values, attr_name, result)
|
|
108
|
+
values.each_with_index do |item, index|
|
|
109
|
+
next unless registered?(item.class)
|
|
110
|
+
|
|
111
|
+
add_composite_entry(result, "#{attr_name}.#{index}", item)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
109
114
|
|
|
110
115
|
def add_composite_entry(composite_models, attr_path, model_instance)
|
|
111
116
|
registration = find_registration(model_instance.class)
|
|
@@ -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,133 @@
|
|
|
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
|
+
PackageTransport.resolve(transport).read(path, store, format: format)
|
|
24
|
+
store
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def save(path, transport: :directory, format: nil, formats: {})
|
|
28
|
+
resolved_formats = resolve_formats(format, formats)
|
|
29
|
+
PackageTransport.resolve(transport).write(path, self, formats: resolved_formats)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# ── Model CRUD ──
|
|
33
|
+
|
|
34
|
+
def add_model(model_instance)
|
|
35
|
+
@db.save(model_instance)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_models(model_instances)
|
|
39
|
+
model_instances.each { |m| add_model(m) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def fetch_model(model_class, key)
|
|
43
|
+
entry = definition.entry_for(model_class)
|
|
44
|
+
@db.fetch(model: model_class, **{ entry.key => key })
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def models_for(model_class)
|
|
48
|
+
@db.all(model: model_class)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def model_count(model_class)
|
|
52
|
+
@db.count(model: model_class)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def model_exists?(model_class, key)
|
|
56
|
+
entry = definition.entry_for(model_class)
|
|
57
|
+
@db.exists?(model: model_class, **{ entry.key => key })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def remove_model(model_class, key)
|
|
61
|
+
entry = definition.entry_for(model_class)
|
|
62
|
+
@db.destroy(model: model_class, **{ entry.key => key })
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# ── Metadata ──
|
|
66
|
+
|
|
67
|
+
# ── Assets ──
|
|
68
|
+
|
|
69
|
+
def asset(path)
|
|
70
|
+
@assets[path]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def add_asset(path, content)
|
|
74
|
+
@assets[path] = content
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def asset_paths
|
|
78
|
+
@assets.keys
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def remove_asset(path)
|
|
82
|
+
@assets.delete(path)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# ── Bulk ──
|
|
86
|
+
|
|
87
|
+
def clear_models(model_class)
|
|
88
|
+
@db.all(model: model_class).each do |m|
|
|
89
|
+
entry = definition.entry_for(model_class)
|
|
90
|
+
key = m.public_send(entry.key)
|
|
91
|
+
@db.destroy(model: model_class, **{ entry.key => key })
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def clear_assets
|
|
96
|
+
@assets.clear
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def clear_all
|
|
100
|
+
definition.model_classes.each { |mc| clear_models(mc) }
|
|
101
|
+
clear_assets
|
|
102
|
+
@metadata = nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# ── Stats ──
|
|
106
|
+
|
|
107
|
+
def stats
|
|
108
|
+
model_stats = definition.model_classes
|
|
109
|
+
.map { |mc| [mc.name, model_count(mc)] }
|
|
110
|
+
.to_h
|
|
111
|
+
{
|
|
112
|
+
package: definition.name,
|
|
113
|
+
models: model_stats,
|
|
114
|
+
assets: @assets.size,
|
|
115
|
+
metadata: @metadata ? true : false
|
|
116
|
+
}
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def resolve_formats(global_format, per_model_formats)
|
|
122
|
+
if global_format
|
|
123
|
+
definition.model_entries
|
|
124
|
+
.map { |e| [e.model, global_format] }
|
|
125
|
+
.to_h
|
|
126
|
+
.merge(per_model_formats)
|
|
127
|
+
else
|
|
128
|
+
per_model_formats
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Store
|
|
7
|
+
module PackageTransport
|
|
8
|
+
class Base
|
|
9
|
+
def read(path, package_store, format: nil)
|
|
10
|
+
raise NotImplementedError
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def write(path, package_store, formats: {})
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def resolve_format(format_name)
|
|
20
|
+
Format.resolve(format_name)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def effective_format(entry, formats)
|
|
24
|
+
formats[entry.model] || entry.default_format
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def format_for_file(filename)
|
|
28
|
+
Format.for_extension(File.extname(filename)) || Format.resolve(:yaml)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def extract_key(model, entry)
|
|
32
|
+
model.public_send(entry.key).to_s
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_key_from_filename(model, filename, entry)
|
|
36
|
+
return if model.public_send(entry.key)
|
|
37
|
+
|
|
38
|
+
basename = File.basename(filename, ".*")
|
|
39
|
+
model.public_send(:"#{entry.key}=", basename)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def sanitize_filename(key)
|
|
43
|
+
key.gsub(%r{[/:#?]}, "_")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|