Class: Lutaml::KeyValue::Mapping

Inherits:
Model::Mapping
  • Object
show all
Defined in:
lib/lutaml/key_value/mapping.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(format = nil) ⇒ Mapping

Returns a new instance of Mapping.



6
7
8
9
10
11
12
13
14
# File 'lib/lutaml/key_value/mapping.rb', line 6

def initialize(format = nil)
  super()
  @mappings = {}
  @key_mapping = {}
  @value_mapping = {}
  @register_mappings = ::Hash.new { |h, k| h[k] = {} }
  @format = format
  @finalized = false
end

Instance Attribute Details

#formatObject (readonly)

Returns the value of attribute format.



4
5
6
# File 'lib/lutaml/key_value/mapping.rb', line 4

def format
  @format
end

#key_mappingObject (readonly)

Returns the value of attribute key_mapping.



4
5
6
# File 'lib/lutaml/key_value/mapping.rb', line 4

def key_mapping
  @key_mapping
end

#register_mappings=(value) ⇒ Object (writeonly)

Writers for deep_dup in subclasses



330
331
332
# File 'lib/lutaml/key_value/mapping.rb', line 330

def register_mappings=(value)
  @register_mappings = value
end

#value_mappingObject (readonly)

Returns the value of attribute value_mapping.



4
5
6
# File 'lib/lutaml/key_value/mapping.rb', line 4

def value_mapping
  @value_mapping
end

Instance Method Details

#deep_dupObject



332
333
334
335
336
337
# File 'lib/lutaml/key_value/mapping.rb', line 332

def deep_dup
  dup_instance.tap do |new_mapping|
    new_mapping.mappings = duplicate_mappings
    new_mapping.register_mappings = Lutaml::Model::Utils.deep_dup(@register_mappings)
  end
end

#dup_instanceObject



339
340
341
# File 'lib/lutaml/key_value/mapping.rb', line 339

def dup_instance
  self.class.new(@format)
end

#finalize(_mapper_class) ⇒ Object



16
17
18
# File 'lib/lutaml/key_value/mapping.rb', line 16

def finalize(_mapper_class)
  @finalized = true
end

#finalized?Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/lutaml/key_value/mapping.rb', line 20

def finalized?
  @finalized
end

#find_by_name(name) ⇒ Object



355
356
357
# File 'lib/lutaml/key_value/mapping.rb', line 355

def find_by_name(name)
  mappings.find { |m| m.name.to_s == name.to_s }
end

#find_by_to(to) ⇒ Object



343
344
345
# File 'lib/lutaml/key_value/mapping.rb', line 343

def find_by_to(to)
  mappings.find { |m| m.to.to_s == to.to_s }
end

#find_by_to!(to) ⇒ Object



347
348
349
350
351
352
353
# File 'lib/lutaml/key_value/mapping.rb', line 347

def find_by_to!(to)
  mapping = find_by_to(to)

  return mapping if !!mapping

  raise Lutaml::Model::NoMappingFoundError.new(to.to_s)
end

#import_model_mappings(model, register_id = nil) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/lutaml/key_value/mapping.rb', line 260

def import_model_mappings(model, register_id = nil)
  reg_id = register(register_id).id
  return import_mappings_later(model, reg_id) if model_importable?(model)

  if reg_id == :default
    @mappings.merge!(::Lutaml::Model::Utils.deep_dup(model.mappings_for(
      @format, reg_id
    ).mappings_hash))
  else
    @register_mappings[register_id].merge!(
      ::Lutaml::Model::Utils.deep_dup(model.mappings_for(@format,
                                                         register_id).mappings_hash),
    )
  end
end

#instance_mapping?Boolean

Returns:

  • (Boolean)


228
229
230
# File 'lib/lutaml/key_value/mapping.rb', line 228

def instance_mapping?
  @instance && (!@key_mapping.empty? || !@value_mapping.empty?)
end

#key(name = nil) ⇒ Object

Set the wrapper key for key-value serialization (JSON, YAML, TOML).

When set, serialized output wraps all instances under this key (e.g., key "items" produces {"items": [...]}). When not called (or called with nil), instances are serialized directly at the top level (e.g., [...]).

Parameters:

  • name (String, nil) (defaults to: nil)

    the wrapper key name



32
33
34
# File 'lib/lutaml/key_value/mapping.rb', line 32

def key(name = nil)
  @key = name
end

#key_nameObject



36
37
38
# File 'lib/lutaml/key_value/mapping.rb', line 36

def key_name
  @key
end

#kv_partition_replace_index(existing, rule) ⇒ Object

Index of the prior partition a when_attribute rule replaces (same target), or nil. Extracted: a block in an elsif condition mis-binds (the known do/end trap).



182
183
184
185
186
# File 'lib/lutaml/key_value/mapping.rb', line 182

def kv_partition_replace_index(existing, rule)
  existing.index do |r|
    r.to == rule.to && !r.when_attribute.empty?
  end
end

#map(name = nil, to: nil, render_nil: false, render_default: false, render_empty: false, treat_nil: nil, treat_empty: nil, treat_omitted: nil, with: {}, delegate: nil, child_mappings: nil, root_mappings: nil, polymorphic: {}, polymorphic_map: {}, transform: {}, value_map: {}, when_attribute: {}, unmatched: :drop, serialize: true) ⇒ Object Also known as: map_element



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/lutaml/key_value/mapping.rb', line 65

def map(
  name = nil,
  to: nil,
  render_nil: false,
  render_default: false,
  render_empty: false,
  treat_nil: nil,
  treat_empty: nil,
  treat_omitted: nil,
  with: {},
  delegate: nil,
  child_mappings: nil,
  root_mappings: nil,
  polymorphic: {},
  polymorphic_map: {},
  transform: {},
  value_map: {},
  when_attribute: {},
  unmatched: :drop,
  serialize: true
)
  mapping_name = name_for_mapping(root_mappings, name)
  validate!(mapping_name, to, with, render_nil, render_empty)

  # lutaml-model#88: grouped form — `when_attribute: "type"` plus
  # `to: { value => attribute }` expands to one rule per value at
  # DSL time; the compiled rules are identical to the per-rule
  # form, so parse/serialize behavior and speed are unchanged.
  if when_attribute.is_a?(::String) || when_attribute.is_a?(::Symbol)
    when_attribute_group(when_attribute, to).each do |value, target|
      map(
        name,
        to: target,
        render_nil: render_nil,
        render_default: render_default,
        render_empty: render_empty,
        treat_nil: treat_nil,
        treat_empty: treat_empty,
        treat_omitted: treat_omitted,
        with: with,
        delegate: delegate,
        child_mappings: child_mappings,
        root_mappings: root_mappings,
        polymorphic: polymorphic,
        polymorphic_map: polymorphic_map,
        transform: transform,
        value_map: value_map,
        when_attribute: { when_attribute.to_s => value.to_s },
        unmatched: unmatched,
        serialize: serialize,
      )
    end
    return
  end
  reject_ambiguous_when_attribute!(when_attribute, to)

  validate_when_attribute!(when_attribute) unless when_attribute.empty?
  validate_unmatched!(unmatched, when_attribute)
  if !when_attribute.empty? && (!with.empty? || delegate)
    raise Lutaml::Model::IncorrectMappingArgumentsError,
          "when_attribute cannot be combined with :with or :delegate " \
          "in key-value mappings"
  end

  rule = MappingRule.new(
    mapping_name,
    to: to,
    render_nil: render_nil,
    render_default: render_default,
    render_empty: render_empty,
    treat_nil: treat_nil,
    treat_empty: treat_empty,
    treat_omitted: treat_omitted,
    with: with,
    delegate: delegate,
    child_mappings: child_mappings,
    root_mappings: root_mappings,
    polymorphic: polymorphic,
    polymorphic_map: polymorphic_map,
    transform: transform,
    value_map: value_map,
    when_attribute: when_attribute,
    unmatched: unmatched,
    serialize: serialize,
  )
  # lutaml-model#88: rules may share a wire key (when_attribute
  # partitions) — store per-key arrays, like the XML mapping.
  # Replacement policy, MECE by rule kind:
  #   * a bare rule (no when_attribute) replaces prior BARE rules
  #     on the same wire key — subclass remaps of a key to a
  #     different attribute (e.g. base `ref → Ref`, subclass
  #     `ref → text`) must not leave both rules active — while
  #     when_attribute partitions on the key survive regardless
  #     of declaration order;
  #   * a when_attribute rule replaces only a prior partition
  #     with the same target (`to:`), otherwise it accumulates.
  existing = @mappings[mapping_name]
  if existing.nil?
    @mappings[mapping_name] = [rule]
  elsif rule.when_attribute.empty?
    # Bare claim: prior bare rules on the key are superseded;
    # partitions survive regardless of declaration order.
    @mappings[mapping_name] =
      existing.reject { |r| r.when_attribute.empty? } + [rule]
  else
    index = kv_partition_replace_index(existing, rule)
    if index
      existing[index] = rule
    else
      existing << rule
    end
  end
end

#map_all(to: nil, render_nil: false, render_default: false, with: {}, delegate: nil) ⇒ Object Also known as: map_all_content



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/lutaml/key_value/mapping.rb', line 190

def map_all(
  to: nil,
  render_nil: false,
  render_default: false,
  with: {},
  delegate: nil
)
  @raw_mapping = true
  validate!(Lutaml::Model::Constants::RAW_MAPPING_KEY, to, with,
            render_nil, nil)
  @mappings[Lutaml::Model::Constants::RAW_MAPPING_KEY] = [MappingRule.new(
    Lutaml::Model::Constants::RAW_MAPPING_KEY,
    to: to,
    render_nil: render_nil,
    render_default: render_default,
    with: with,
    delegate: delegate,
  )]
end

#map_instances(to:, polymorphic: {}) ⇒ Object



212
213
214
215
216
# File 'lib/lutaml/key_value/mapping.rb', line 212

def map_instances(to:, polymorphic: {})
  @instance = to
  map(key_name || to, to: to, polymorphic: polymorphic)
  map_to_instance
end

#map_key(to_instance: nil, as_attribute: nil) ⇒ Object



218
219
220
221
# File 'lib/lutaml/key_value/mapping.rb', line 218

def map_key(to_instance: nil, as_attribute: nil)
  @key_mapping = { "#{to_instance || as_attribute}": :key }
  map_to_instance
end

#map_to_instanceObject



232
233
234
235
236
237
238
239
# File 'lib/lutaml/key_value/mapping.rb', line 232

def map_to_instance
  return if !instance_mapping?

  mapping_name = name_for_mapping(nil, key_name || @instance)
  @mappings[mapping_name].each do |rule|
    rule.child_mappings = @key_mapping.merge(@value_mapping)
  end
end

#map_value(to_instance: nil, as_attribute: nil) ⇒ Object



223
224
225
226
# File 'lib/lutaml/key_value/mapping.rb', line 223

def map_value(to_instance: nil, as_attribute: nil)
  @value_mapping = { "#{to_instance || as_attribute}": :value }
  map_to_instance
end

#mappings(register_id = nil) ⇒ Object



247
248
249
250
# File 'lib/lutaml/key_value/mapping.rb', line 247

def mappings(register_id = nil)
  ensure_mappings_imported!(register_id) if finalized?
  mappings_hash(register_id).values.flatten
end

#mappings_hash(register_id = nil) ⇒ Object



252
253
254
255
256
257
258
# File 'lib/lutaml/key_value/mapping.rb', line 252

def mappings_hash(register_id = nil)
  if register_id.nil? || register_id == :default
    @mappings
  else
    @mappings.merge(@register_mappings[register_id])
  end
end

#name_for_mapping(root_mappings, name) ⇒ Object



241
242
243
244
245
# File 'lib/lutaml/key_value/mapping.rb', line 241

def name_for_mapping(root_mappings, name)
  return "root_mapping" if root_mappings

  name
end

#no_key?Boolean

Returns true when no wrapper key is set (instances serialized at top level).

Returns:

  • (Boolean)


51
52
53
# File 'lib/lutaml/key_value/mapping.rb', line 51

def no_key?
  @key.nil?
end

#no_rootObject

Deprecated.

Omit key call instead. Not calling key means no wrapper key.



46
47
48
# File 'lib/lutaml/key_value/mapping.rb', line 46

def no_root
  @key = nil
end

#no_root?Boolean

Deprecated.

Use #no_key? instead.

Returns:

  • (Boolean)


56
57
58
# File 'lib/lutaml/key_value/mapping.rb', line 56

def no_root?
  no_key?
end

#polymorphic_mappingObject



359
360
361
# File 'lib/lutaml/key_value/mapping.rb', line 359

def polymorphic_mapping
  mappings.find(&:polymorphic_mapping?)
end

#root(name = nil) ⇒ Object

Deprecated.

Use #key instead. In key-value formats, the wrapper is a key, not a root.



41
42
43
# File 'lib/lutaml/key_value/mapping.rb', line 41

def root(name = nil)
  @key = name
end

#root_mappingObject



363
364
365
# File 'lib/lutaml/key_value/mapping.rb', line 363

def root_mapping
  mappings.find(&:root_mapping?)
end

#root_nameObject

Deprecated.

Use #key_name instead.



61
62
63
# File 'lib/lutaml/key_value/mapping.rb', line 61

def root_name
  @key
end

#validate!(key, to, with, render_nil, render_empty) ⇒ Object



276
277
278
279
280
281
282
283
# File 'lib/lutaml/key_value/mapping.rb', line 276

def validate!(key, to, with, render_nil, render_empty)
  validate_mappings!(key)
  validate_to_and_with_arguments!(key, to, with)

  # Validate `render_nil` for unsupported value
  validate_blank_mappings!(render_nil, render_empty)
  validate_root_mappings!(key)
end

#validate_blank_mappings!(render_nil, render_empty) ⇒ Object



313
314
315
316
317
318
319
320
# File 'lib/lutaml/key_value/mapping.rb', line 313

def validate_blank_mappings!(render_nil, render_empty)
  if render_nil == :as_blank || render_empty == :as_blank
    raise Lutaml::Model::IncorrectMappingArgumentsError.new(
      ":as_blank is not supported for key-value mappings. " \
      "Use :as_empty instead to create explicit empty values.",
    )
  end
end

#validate_mappings!(_type) ⇒ Object



322
323
324
325
326
327
# File 'lib/lutaml/key_value/mapping.rb', line 322

def validate_mappings!(_type)
  if (@raw_mapping && Lutaml::Model::Utils.present?(@mappings)) ||
      (!@raw_mapping && mappings.any?(&:raw_mapping?))
    raise StandardError, "map_all is not allowed with other mappings"
  end
end

#validate_root_mappings!(name) ⇒ Object



305
306
307
308
309
310
311
# File 'lib/lutaml/key_value/mapping.rb', line 305

def validate_root_mappings!(name)
  if root_mapping || (name == "root_mapping" && @mappings.any?)
    raise Lutaml::Model::MultipleMappingsError.new(
      "root_mappings cannot be used with other mappings",
    )
  end
end

#validate_to_and_with_arguments!(key, to, with) ⇒ Object



285
286
287
288
289
290
291
292
293
# File 'lib/lutaml/key_value/mapping.rb', line 285

def validate_to_and_with_arguments!(key, to, with)
  if to.nil? && with.empty? && !@raw_mapping
    raise Lutaml::Model::IncorrectMappingArgumentsError.new(
      ":to or :with argument is required for mapping '#{key}'",
    )
  end

  validate_with_options!(to, key, with)
end

#validate_with_options!(to, key, with) ⇒ Object



295
296
297
298
299
300
301
302
303
# File 'lib/lutaml/key_value/mapping.rb', line 295

def validate_with_options!(to, key, with)
  return true if to

  if !with.empty? && (with[:from].nil? || with[:to].nil?) && !@raw_mapping
    raise Lutaml::Model::IncorrectMappingArgumentsError.new(
      ":with argument for mapping '#{key}' requires :to and :from keys",
    )
  end
end