lutaml-model 0.8.0 → 0.8.2

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos.json +9 -0
  3. data/.github/workflows/downstream-performance.yml +0 -3
  4. data/.rubocop_todo.yml +18 -186
  5. data/README.adoc +212 -15
  6. data/bench/bench_xmi.rb +6 -6
  7. data/bench/gate_config.rb +2 -9
  8. data/docs/_pages/configuration.adoc +155 -41
  9. data/docs/_pages/serialization_adapters.adoc +65 -14
  10. data/docs/index.adoc +3 -1
  11. data/docs/yamls_sequence.adoc +335 -0
  12. data/lib/lutaml/hash_format.rb +4 -0
  13. data/lib/lutaml/json/adapter/multi_json_adapter.rb +4 -2
  14. data/lib/lutaml/json/adapter/oj_adapter.rb +4 -2
  15. data/lib/lutaml/json.rb +4 -0
  16. data/lib/lutaml/key_value/adapter/json/multi_json_adapter.rb +4 -2
  17. data/lib/lutaml/key_value/adapter/json/oj_adapter.rb +4 -2
  18. data/lib/lutaml/model/adapter_resolver.rb +410 -0
  19. data/lib/lutaml/model/adapter_scope.rb +64 -0
  20. data/lib/lutaml/model/config.rb +84 -21
  21. data/lib/lutaml/model/configuration.rb +17 -249
  22. data/lib/lutaml/model/format_registry.rb +44 -117
  23. data/lib/lutaml/model/mapping/listener.rb +4 -2
  24. data/lib/lutaml/model/serialize/format_conversion.rb +42 -3
  25. data/lib/lutaml/model/serialize.rb +4 -2
  26. data/lib/lutaml/model/services/base.rb +4 -2
  27. data/lib/lutaml/model/version.rb +1 -1
  28. data/lib/lutaml/model.rb +2 -0
  29. data/lib/lutaml/toml.rb +10 -3
  30. data/lib/lutaml/xml/serialization/instance_methods.rb +6 -0
  31. data/lib/lutaml/xml.rb +3 -4
  32. data/lib/lutaml/yaml.rb +4 -0
  33. data/lib/lutaml/yamls/adapter/mapping.rb +7 -0
  34. data/lib/lutaml/yamls/adapter/standard_adapter.rb +23 -2
  35. data/lib/lutaml/yamls/adapter/transform.rb +105 -7
  36. data/lib/lutaml/yamls/adapter/yamls_sequence.rb +20 -0
  37. data/lib/lutaml/yamls/adapter/yamls_sequence_rule.rb +48 -0
  38. data/lib/lutaml/yamls/adapter.rb +2 -0
  39. data/spec/fixtures/geolexica_v2_concept.rb +136 -0
  40. data/spec/fixtures/geolexica_v2_sample.yaml +36 -0
  41. data/spec/fixtures/geolexica_v2_sample2.yaml +38 -0
  42. data/spec/fixtures/yamls_range_concept.rb +139 -0
  43. data/spec/lutaml/model/xml_decoupling_spec.rb +5 -4
  44. data/spec/lutaml/model/yamls_range_spec.rb +393 -0
  45. data/spec/lutaml/model/yamls_sequence_spec.rb +245 -0
  46. data/spec/spec_helper.rb +5 -0
  47. metadata +13 -3
  48. data/bench/bench_uniword.rb +0 -69
@@ -2,75 +2,25 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- # Single source of truth for Lutaml::Model configuration
5
+ # Single source of truth for Lutaml::Model configuration.
6
+ #
7
+ # Adapter methods delegate to AdapterResolver. This class retains
8
+ # the configure block API, register, and non-adapter settings.
6
9
  #
7
10
  # @example Basic configuration
8
11
  # Lutaml::Model::Configuration.configure do |config|
9
12
  # config.xml_adapter = :nokogiri
10
13
  # config.json_adapter = :standard
11
- # config.toml_adapter = :toml_rb
12
14
  # end
13
15
  #
14
16
  class Configuration
15
- # Static defaults captured at boot. Use RuntimeCompatibility directly for
16
- # per-call runtime checks that tests may stub.
17
- OPAL_RUNTIME = Lutaml::Model::RuntimeCompatibility.opal?
18
- WINDOWS_PLATFORM = Lutaml::Model::RuntimeCompatibility.windows?
19
-
20
- # Bootstrap format list for key-value adapters.
21
- # NOTE: XML is NOT listed here — it registers dynamically via FormatRegistry.
22
- AVAILABLE_FORMATS = %i[json jsonl yaml toml hash].freeze
23
- KEY_VALUE_FORMATS = AVAILABLE_FORMATS
24
-
25
- # Available key-value formats and their adapters.
26
- # XML adapter metadata is registered dynamically via FormatRegistry
27
- # when `require "lutaml/xml"` is called.
28
- ADAPTERS = begin
29
- h = {
30
- json: {
31
- available: %i[standard standard_json multi_json oj],
32
- default: :standard,
33
- },
34
- yaml: {
35
- available: %i[standard standard_yaml],
36
- default: :standard,
37
- },
38
- toml: {
39
- available: OPAL_RUNTIME ? [] : %i[tomlib toml_rb],
40
- default: if OPAL_RUNTIME
41
- nil
42
- elsif WINDOWS_PLATFORM
43
- :toml_rb
44
- else
45
- :tomlib
46
- end,
47
- },
48
- hash: {
49
- available: %i[standard standard_hash],
50
- default: :standard,
51
- },
52
- jsonl: {
53
- available: %i[standard],
54
- default: :standard,
55
- },
56
- yamls: {
57
- available: %i[standard],
58
- default: :standard,
59
- },
60
- }
61
- h.freeze
62
- end
63
-
64
- attr_reader :adapter_types
17
+ attr_reader :default_register
65
18
 
66
19
  def initialize
67
- @adapter_types = {}
68
- @adapters = {}
69
20
  @default_register = :default
70
21
  @configured = false
71
22
  end
72
23
 
73
- # Configure the library using a block
74
24
  def configure
75
25
  yield self if block_given?
76
26
  @configured = true
@@ -81,48 +31,21 @@ module Lutaml
81
31
  @configured
82
32
  end
83
33
 
84
- # Check if running on Windows platform
85
- def self.windows_platform?
86
- WINDOWS_PLATFORM
87
- end
88
-
89
- # Dynamic accessor for adapter types
34
+ # Dynamic accessor for adapter types — delegates to AdapterResolver
90
35
  def adapter_for(format)
91
- @adapter_types[format.to_sym]
36
+ AdapterResolver.configured_type(format)
92
37
  end
93
38
 
94
- # Dynamic setter for adapter types with validation
39
+ # Dynamic setter for adapter types delegates to AdapterResolver
95
40
  def set_adapter(format, adapter_type)
96
- format = format.to_sym
97
- adapter_type = adapter_type.to_sym
98
-
99
- validate_adapter!(format, adapter_type)
100
- @adapter_types[format] = adapter_type
101
-
102
- # Load the adapter immediately
103
- load_adapter(format, adapter_type)
41
+ AdapterResolver.set_adapter_type(format, adapter_type)
104
42
  end
105
43
 
106
- # Define dynamic accessor methods for each format
107
- ADAPTERS.each_key do |format|
108
- # Getter method: config.xml_adapter
109
- define_method(:"#{format}_adapter") do
110
- @adapter_types[format] || ADAPTERS[format][:default]
111
- end
112
-
113
- # Setter method: config.xml_adapter = :nokogiri
114
- define_method(:"#{format}_adapter=") do |adapter_type|
115
- set_adapter(format, adapter_type)
116
- end
117
-
118
- # Aliased setter with _type suffix: config.xml_adapter_type = :nokogiri
119
- alias_method :"#{format}_adapter_type=", :"#{format}_adapter="
120
- alias_method :"#{format}_adapter_type", :"#{format}_adapter"
44
+ # Get adapter class for a format
45
+ def get_adapter(format)
46
+ AdapterResolver.adapter_for(format)
121
47
  end
122
48
 
123
- # Default register/context ID accessor
124
- attr_reader :default_register
125
-
126
49
  def default_register=(value)
127
50
  @default_register = case value
128
51
  when Symbol then value
@@ -134,190 +57,35 @@ module Lutaml
134
57
  end
135
58
  end
136
59
 
137
- # Alias for terminology alignment
138
60
  alias default_context_id default_register
139
61
  alias default_context_id= default_register=
140
62
 
141
- # Get adapter class for a format
142
- def get_adapter(format)
143
- @adapters[format.to_sym]
144
- end
145
-
146
63
  # Reset configuration to defaults
147
64
  def reset!
148
- @adapter_types = {}
149
- @adapters = {}
150
65
  @default_register = :default
151
66
  @configured = false
67
+ AdapterResolver.reset!
68
+ AdapterScope.reset!
152
69
  end
153
70
 
154
- # Get all current settings as a hash
155
71
  def to_h
156
72
  {
157
- adapter_types: @adapter_types.dup,
158
73
  default_register: @default_register,
159
74
  configured: @configured,
160
75
  }
161
76
  end
162
77
 
163
- # Mappings class for a format
164
78
  def mappings_class_for(format)
165
79
  Lutaml::Model::FormatRegistry.mappings_class_for(format)
166
80
  end
167
81
 
168
- # Transformer for a format
169
82
  def transformer_for(format)
170
83
  Lutaml::Model::FormatRegistry.transformer_for(format)
171
84
  end
172
85
 
173
- private
174
-
175
- # Validate that the adapter is available for the format.
176
- # Checks both static ADAPTERS hash and FormatRegistry for dynamically
177
- # registered formats (e.g., XML).
178
- def validate_adapter!(format, adapter_type)
179
- adapter_config = ADAPTERS[format] || FormatRegistry.adapter_options_for(format)
180
-
181
- unless adapter_config
182
- all_formats = (ADAPTERS.keys + FormatRegistry.formats).uniq
183
- available_formats = all_formats.map { |f| "`:#{f}`" }.join(", ")
184
- raise ArgumentError,
185
- "Unknown format: `:#{format}`. Available formats: #{available_formats}."
186
- end
187
-
188
- # Check for Windows + tomlib incompatibility
189
- if format == :toml && adapter_type == :tomlib && self.class.windows_platform?
190
- raise ArgumentError,
191
- "The `:tomlib` adapter is not supported on Windows due to " \
192
- "segmentation fault issues. Please use `:toml_rb` instead."
193
- end
194
-
195
- available = adapter_config[:available]
196
- return unless available
197
- return if available.include?(adapter_type)
198
-
199
- available_list = available.map { |a| "`:#{a}`" }.join(", ")
200
- closest = find_suggestion(adapter_type.to_s, available.map(&:to_s))
201
-
202
- msg = "Unknown adapter: `:#{adapter_type}` for `:#{format}` format. " \
203
- "Available adapters: #{available_list}."
204
- msg += " Did you mean `:#{closest}`?" if closest
205
-
206
- raise ArgumentError, msg
207
- end
208
-
209
- # Load an adapter for a format
210
- def load_adapter(format, adapter_type)
211
- adapter = format.to_s
212
- type = normalize_type_name(adapter_type, format)
213
-
214
- load_adapter_file(adapter, type)
215
- load_moxml_adapter(adapter_type, format)
216
-
217
- adapter_class = class_for(adapter, type)
218
- @adapters[format] = adapter_class
219
-
220
- # Also set in Config's adapters for FormatRegistry compatibility
221
- Config.set_adapter_for(format, adapter_class)
222
- end
223
-
224
- def normalize_type_name(type_name, adapter_name)
225
- if type_name.to_s.start_with?("multi_json")
226
- "multi_json_adapter"
227
- else
228
- "#{type_name.to_s.gsub("_#{adapter_name}", '')}_adapter"
229
- end
230
- end
231
-
232
- def load_adapter_file(adapter, type)
233
- # Check FormatRegistry for a custom adapter loader
234
- loader = FormatRegistry.adapter_loader_for(adapter.to_sym)
235
- if loader.respond_to?(:load_adapter_file)
236
- loader.load_adapter_file(adapter, type)
237
- return
238
- end
239
-
240
- # Default key-value adapter loading
241
- adapter_path = if Lutaml::Model::RuntimeCompatibility.opal?
242
- "lutaml/key_value/adapter/#{adapter}/#{type}"
243
- else
244
- File.join(__dir__, "../key_value/adapter", adapter,
245
- type)
246
- end
247
- require adapter_path
248
- rescue LoadError
249
- raise UnknownAdapterTypeError.new(adapter, type), cause: nil
250
- end
251
-
252
- def load_moxml_adapter(type_name, adapter_name)
253
- # Delegate to FormatRegistry adapter loader if available
254
- loader = FormatRegistry.adapter_loader_for(adapter_name)
255
- if loader.respond_to?(:load_moxml_adapter)
256
- loader.load_moxml_adapter(type_name, adapter_name)
257
- nil
258
- end
259
-
260
- # Key-value formats don't need moxml loading
261
- # Non-key-value formats without a registered loader are no-ops
262
- end
263
-
264
- def class_for(adapter, type)
265
- # Check FormatRegistry for a custom adapter loader
266
- loader = FormatRegistry.adapter_loader_for(adapter.to_sym)
267
- if loader.respond_to?(:class_for)
268
- return loader.class_for(adapter, type)
269
- end
270
-
271
- # Default key-value adapter class resolution
272
- Lutaml::KeyValue::Adapter.const_get(to_class_name(adapter))
273
- .const_get(to_class_name(type))
274
- end
275
-
276
- def to_class_name(str)
277
- str.to_s.split("_").map(&:capitalize).join
278
- end
279
-
280
- # Find closest string match for suggestions
281
- def find_suggestion(input, candidates)
282
- return nil if input.nil? || input.empty?
283
-
284
- candidates.min_by do |candidate|
285
- levenshtein_distance(input.downcase, candidate.downcase)
286
- end.tap do |closest|
287
- max_dist = ([input.length, closest.length].max / 2) + 1
288
- return nil if closest && levenshtein_distance(input.downcase,
289
- closest.downcase) > max_dist
290
- end
291
- end
292
-
293
- # Calculate Levenshtein distance between two strings
294
- def levenshtein_distance(a, b)
295
- return a.length if b.empty?
296
- return b.length if a.empty?
297
-
298
- matrix = Array.new(a.length + 1) do |i|
299
- Array.new(b.length + 1) do |j|
300
- if i.zero?
301
- j
302
- else
303
- (j.zero? ? i : 0)
304
- end
305
- end
306
- end
307
-
308
- (1..a.length).each do |i|
309
- (1..b.length).each do |j|
310
- cost = a[i - 1] == b[j - 1] ? 0 : 1
311
- matrix[i][j] = [
312
- matrix[i - 1][j] + 1,
313
- matrix[i][j - 1] + 1,
314
- matrix[i - 1][j - 1] + cost,
315
- ].min
316
- end
317
- end
318
-
319
- matrix[a.length][b.length]
320
- end
86
+ # Adapter accessors are defined dynamically by FormatRegistry.register
87
+ # via define_method. These forward to AdapterResolver.
88
+ # The generic set_adapter/get_adapter handle any format.
321
89
  end
322
90
  end
323
91
  end
@@ -2,37 +2,28 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- # Registry for serialization formats and their associated components
5
+ # Registry for serialization formats and their associated components.
6
6
  #
7
- # This class manages the registration of formats (xml, json, yaml, etc.)
8
- # and their associated mapping classes, adapters, and transformers.
9
- #
10
- # @example Registering a custom format
11
- # Lutaml::Model::FormatRegistry.register(:custom,
12
- # mapping_class: MyMapping,
13
- # adapter_class: MyAdapter,
14
- # transformer: MyTransformer
15
- # )
16
- #
17
- # @example Checking if a format is registered
18
- # Lutaml::Model::FormatRegistry.registered?(:xml) #=> true
19
- #
20
- # @example Listing all registered formats
21
- # Lutaml::Model::FormatRegistry.formats #=> [:xml, :json, :yaml, ...]
7
+ # Manages the registration of formats (xml, json, yaml, etc.) and their
8
+ # mapping classes, transformers, and adapter metadata. Adapter loading
9
+ # and resolution is delegated to AdapterResolver.
22
10
  #
23
11
  class FormatRegistry
24
12
  class << self
25
- # Register a new format with its associated components
13
+ # Register a new format with its associated components.
26
14
  #
27
15
  # @param format [Symbol] the format name (e.g., :xml, :json)
28
16
  # @param mapping_class [Class] the mapping class for this format
29
- # @param adapter_class [Class, nil] the adapter class (nil for abstract formats)
17
+ # @param adapter_class [Class, nil] the adapter class (nil for selectable formats)
30
18
  # @param transformer [Class] the transformer class for serialization
31
- # @raise [ArgumentError] if format is invalid or required params missing
32
- # @return [Hash] the registered format configuration
33
- # @param adapter_loader [Module, nil] optional module with load_adapter_file and class_for methods
19
+ # @param adapter_loader [Module, nil] optional module with load_adapter_file and class_for
20
+ # @param castable_type [Class, nil] the castable type class
21
+ # @param key_value [Boolean] whether this is a key-value format
22
+ # @param error_types [Array<String, Class>] error types for this format
23
+ # @param adapter_options [Hash, nil] { available: [...], default: :name }
34
24
  def register(format, mapping_class:, adapter_class:, transformer:,
35
- adapter_loader: nil, castable_type: nil, key_value: nil, error_types: nil, adapter_options: nil)
25
+ adapter_loader: nil, castable_type: nil, key_value: nil,
26
+ error_types: nil, adapter_options: nil)
36
27
  validate_registration!(format, mapping_class, transformer)
37
28
 
38
29
  registered_formats[format] = {
@@ -47,206 +38,142 @@ adapter_loader: nil, castable_type: nil, key_value: nil, error_types: nil, adapt
47
38
  registered_at: Time.now,
48
39
  }
49
40
 
41
+ # Register type methods on model classes
50
42
  ::Lutaml::Model::Type::Value.register_format_to_from_methods(format)
51
43
  ::Lutaml::Model::Serialize.register_format_mapping_method(format)
52
44
  ::Lutaml::Model::Serialize.register_from_format_method(format)
53
45
  ::Lutaml::Model::Serialize.register_to_format_method(format)
54
46
 
55
- # Push format-specific serialization method name to warn list
56
47
  ::Lutaml::Model::Attribute.format_specific_warn_names.push(:"to_#{format}")
57
48
 
58
- Lutaml::Model::Config.set_adapter_for(format, adapter_class)
59
-
60
- # Define raw adapter getter/setter on Config module
61
- Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter") do
62
- @adapters[format] || adapter_class
63
- end
64
-
65
- Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter=") do |adapter_klass|
66
- @adapters ||= {}
67
- @adapters[format] = adapter_klass
68
- end
69
-
70
- # Define _type suffixed methods on Config module (delegate to Configuration#set_adapter)
71
- Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter_type=") do |type_name|
72
- instance.set_adapter(format, type_name)
49
+ # Register adapter metadata with AdapterResolver
50
+ if adapter_options
51
+ # Selectable adapters have multiple options (xml, toml, json, yaml, hash)
52
+ AdapterResolver.register_metadata(format, adapter_options)
53
+ elsif adapter_class
54
+ # Fixed adapter (jsonl, yamls) — register as sole available adapter
55
+ adapter_name = derive_adapter_name(adapter_class)
56
+ AdapterResolver.register_fixed(format, adapter_class, adapter_name)
73
57
  end
74
58
 
75
- Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter_type") do
76
- instance.adapter_for(format)
77
- end
59
+ # Define adapter type accessor methods on Config module
60
+ ::Lutaml::Model::Config.define_adapter_type_methods(format)
78
61
 
79
- # Define adapter methods on Configuration class for this format
62
+ # Define adapter accessor methods on Configuration class
80
63
  define_configuration_adapter_methods(format, adapter_options)
81
64
 
82
65
  registered_formats[format]
83
66
  end
84
67
 
85
- # Unregister a format
86
- #
87
- # @param format [Symbol] the format to unregister
88
- # @return [Hash, nil] the removed format configuration or nil if not found
89
68
  def unregister(format)
90
69
  registered_formats.delete(format)
91
70
  end
92
71
 
93
- # Check if a format is registered
94
- #
95
- # @param format [Symbol] the format to check
96
- # @return [Boolean]
97
72
  def registered?(format)
98
73
  registered_formats.key?(format)
99
74
  end
100
75
 
101
- # Get the mapping class for a format
102
- #
103
- # @param format [Symbol] the format name
104
- # @return [Class, nil] the mapping class or nil if not registered
105
76
  def mappings_class_for(format)
106
77
  registered_formats.dig(format, :mapping_class)
107
78
  end
108
79
 
109
- # Get the transformer for a format
110
- #
111
- # @param format [Symbol] the format name
112
- # @return [Class, nil] the transformer class or nil if not registered
113
80
  def transformer_for(format)
114
81
  registered_formats.dig(format, :transformer)
115
82
  end
116
83
 
117
- # Get the adapter class for a format
118
- #
119
- # @param format [Symbol] the format name
120
- # @return [Class, nil] the adapter class or nil if not registered
121
84
  def adapter_class_for(format)
122
85
  registered_formats.dig(format, :adapter_class)
123
86
  end
124
87
 
125
- # Get the adapter loader for a format
126
- #
127
- # @param format [Symbol] the format name
128
- # @return [Module, nil] the adapter loader or nil
129
88
  def adapter_loader_for(format)
130
89
  registered_formats.dig(format, :adapter_loader)
131
90
  end
132
91
 
133
- # Get the adapter options for a format
134
- #
135
- # @param format [Symbol] the format name
136
- # @return [Hash, nil] { available: [...], default: :name } or nil
137
92
  def adapter_options_for(format)
138
93
  registered_formats.dig(format, :adapter_options)
139
94
  end
140
95
 
141
- # Get the castable type for a format
142
- #
143
- # @param format [Symbol] the format name
144
- # @return [Class, nil] the castable type or nil
145
96
  def castable_type_for(format)
146
97
  registered_formats.dig(format, :castable_type)
147
98
  end
148
99
 
149
- # Get all registered format names
150
- #
151
- # @return [Array<Symbol>]
152
100
  def formats
153
101
  registered_formats.keys
154
102
  end
155
103
 
156
- # Get all key-value format names (non-XML formats)
157
- #
158
- # @return [Array<Symbol>]
159
104
  def key_value_formats
160
105
  registered_formats.select { |_, info| info[:key_value] }.keys
161
106
  end
162
107
 
163
- # Check if a format is a key-value format
164
- #
165
- # @param format [Symbol] the format name
166
- # @return [Boolean]
167
108
  def key_value?(format)
168
109
  registered_formats.dig(format, :key_value) == true
169
110
  end
170
111
 
171
- # Get format-specific error types
172
- #
173
- # @param format [Symbol] the format name
174
- # @return [Array<Class>, nil] error types for this format
175
112
  def error_types_for(format)
176
113
  registered_formats.dig(format, :error_types)
177
114
  end
178
115
 
179
- # Get all registered error types across all formats
180
- #
181
- # @return [Array<Class>]
182
116
  def all_error_types
183
117
  registered_formats.values.filter_map do |info|
184
118
  info[:error_types]
185
119
  end.flatten.compact
186
120
  end
187
121
 
188
- # Get registration info for a format
189
- #
190
- # @param format [Symbol] the format name
191
- # @return [Hash, nil] the registration info or nil if not found
192
122
  def info(format)
193
123
  registered_formats[format]
194
124
  end
195
125
 
196
- # Get all registered formats with their details
197
- #
198
- # @return [Hash{Symbol => Hash}]
199
126
  def all
200
127
  registered_formats.dup
201
128
  end
202
129
 
203
- # Reset the registry (primarily for testing)
204
- #
205
- # @return [void]
206
130
  def reset!
207
131
  @registered_formats = nil
208
132
  end
209
133
 
210
134
  private
211
135
 
212
- # Get the internal registry hash
213
- #
214
- # @return [Hash]
215
136
  def registered_formats
216
137
  @registered_formats ||= {}
217
138
  end
218
139
 
219
- # Define adapter getter/setter methods on Configuration class
220
- # for a dynamically registered format.
221
- #
222
- # @param format [Symbol] the format name
223
- # @param adapter_options [Hash, nil] { available: [...], default: :name }
224
140
  def define_configuration_adapter_methods(format, adapter_options)
225
141
  cfg = ::Lutaml::Model::Configuration
226
- return if cfg.method_defined?(:"#{format}_adapter")
142
+ return if cfg.method_defined?(:"#{format}_adapter=")
227
143
 
228
- default_adapter = adapter_options&.dig(:default)
144
+ adapter_options&.dig(:default)
229
145
 
146
+ # Adapter class getter on Configuration instance
230
147
  cfg.define_method(:"#{format}_adapter") do
231
- @adapter_types[format] || default_adapter
148
+ AdapterResolver.adapter_for(format)
232
149
  end
233
150
 
151
+ # Adapter type name setter on Configuration instance
234
152
  cfg.define_method(:"#{format}_adapter=") do |adapter_type|
235
153
  set_adapter(format, adapter_type)
236
154
  end
237
155
 
156
+ # Aliased _type methods
238
157
  cfg.send(:alias_method, :"#{format}_adapter_type=",
239
158
  :"#{format}_adapter=")
240
159
  cfg.send(:alias_method, :"#{format}_adapter_type",
241
160
  :"#{format}_adapter")
242
161
  end
243
162
 
244
- # Validate registration parameters
163
+ # Derive a symbol adapter name from an adapter class.
245
164
  #
246
- # @param format [Symbol] the format name
247
- # @param mapping_class [Class] the mapping class
248
- # @param transformer [Class] the transformer class
249
- # @raise [ArgumentError] if validation fails
165
+ # @param adapter_class [Class] e.g., Lutaml::Json::Adapter::StandardAdapter
166
+ # @return [Symbol] e.g., :standard
167
+ def derive_adapter_name(adapter_class)
168
+ name = adapter_class.name
169
+ return :standard unless name
170
+
171
+ # Extract the adapter type from class name: "...::StandardAdapter" → :standard
172
+ short = name.split("::").last
173
+ short = short.delete_suffix("Adapter")
174
+ short.downcase.to_sym
175
+ end
176
+
250
177
  def validate_registration!(format, mapping_class, transformer)
251
178
  unless format.is_a?(Symbol)
252
179
  raise ArgumentError,
@@ -91,13 +91,15 @@ module Lutaml
91
91
  #
92
92
  # @param args [Array] Arguments to pass to the handler block
93
93
  # @return [Object] Result of the handler invocation
94
- def call(*)
94
+ # rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+
95
+ def call(*args)
95
96
  if simple?
96
97
  raise NoHandlerError,
97
98
  "Cannot call simple listener #{id.inspect}"
98
99
  end
99
100
 
100
- @handler.call(*)
101
+ @handler.call(*args)
102
+ # rubocop:enable Style/ArgumentsForwarding
101
103
  end
102
104
 
103
105
  # Error raised when attempting to invoke a simple listener as a complex one.