lutaml-model 0.6.7 → 0.7.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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos-todo.json +7 -0
  3. data/.github/workflows/dependent-repos.json +17 -9
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +51 -65
  6. data/Gemfile +4 -1
  7. data/README.adoc +5083 -2612
  8. data/RELEASE_NOTES.adoc +346 -0
  9. data/docs/custom_adapters.adoc +144 -0
  10. data/lib/lutaml/model/attribute.rb +101 -16
  11. data/lib/lutaml/model/choice.rb +7 -0
  12. data/lib/lutaml/model/comparable_model.rb +48 -9
  13. data/lib/lutaml/model/config.rb +48 -42
  14. data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +1 -1
  15. data/lib/lutaml/model/error/polymorphic_error.rb +14 -0
  16. data/lib/lutaml/model/error.rb +1 -0
  17. data/lib/lutaml/model/format_registry.rb +41 -0
  18. data/lib/lutaml/model/hash/document.rb +11 -0
  19. data/lib/lutaml/model/hash/mapping.rb +19 -0
  20. data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
  21. data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
  22. data/lib/lutaml/model/hash/transform.rb +8 -0
  23. data/lib/lutaml/model/hash.rb +21 -0
  24. data/lib/lutaml/model/json/document.rb +11 -0
  25. data/lib/lutaml/model/json/mapping.rb +19 -0
  26. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  27. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  28. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  29. data/lib/lutaml/model/json/transform.rb +8 -0
  30. data/lib/lutaml/model/json.rb +21 -0
  31. data/lib/lutaml/model/key_value_document.rb +27 -0
  32. data/lib/lutaml/model/{key_value_mapping.rb → mapping/key_value_mapping.rb} +64 -16
  33. data/lib/lutaml/model/{key_value_mapping_rule.rb → mapping/key_value_mapping_rule.rb} +18 -2
  34. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  35. data/lib/lutaml/model/mapping/mapping_rule.rb +300 -0
  36. data/lib/lutaml/model/schema/xml_compiler.rb +15 -15
  37. data/lib/lutaml/model/sequence.rb +2 -2
  38. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  39. data/lib/lutaml/model/serialize.rb +219 -444
  40. data/lib/lutaml/model/services/logger.rb +54 -0
  41. data/lib/lutaml/model/services/transformer.rb +48 -0
  42. data/lib/lutaml/model/services.rb +2 -0
  43. data/lib/lutaml/model/toml/document.rb +11 -0
  44. data/lib/lutaml/model/toml/mapping.rb +27 -0
  45. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  46. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  47. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  48. data/lib/lutaml/model/toml/transform.rb +8 -0
  49. data/lib/lutaml/model/toml.rb +30 -0
  50. data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
  51. data/lib/lutaml/model/transform/xml_transform.rb +239 -0
  52. data/lib/lutaml/model/transform.rb +78 -0
  53. data/lib/lutaml/model/type/date.rb +1 -1
  54. data/lib/lutaml/model/type/date_time.rb +2 -2
  55. data/lib/lutaml/model/type/hash.rb +1 -1
  56. data/lib/lutaml/model/type/time.rb +2 -2
  57. data/lib/lutaml/model/type/time_without_date.rb +2 -2
  58. data/lib/lutaml/model/type/value.rb +6 -9
  59. data/lib/lutaml/model/uninitialized_class.rb +64 -0
  60. data/lib/lutaml/model/utils.rb +44 -0
  61. data/lib/lutaml/model/validation.rb +1 -0
  62. data/lib/lutaml/model/version.rb +1 -1
  63. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  64. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  65. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  66. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +41 -21
  67. data/lib/lutaml/model/xml/element.rb +32 -0
  68. data/lib/lutaml/model/xml/mapping.rb +410 -0
  69. data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
  70. data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
  71. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  72. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
  73. data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
  74. data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
  75. data/lib/lutaml/model/xml/transform.rb +8 -0
  76. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  77. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +23 -10
  78. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  79. data/lib/lutaml/model/xml.rb +31 -0
  80. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  81. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  82. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  83. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  84. data/lib/lutaml/model/yaml/document.rb +10 -0
  85. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  86. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  87. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  88. data/lib/lutaml/model/yaml/transform.rb +8 -0
  89. data/lib/lutaml/model/yaml.rb +21 -0
  90. data/lib/lutaml/model.rb +40 -4
  91. data/lutaml-model.gemspec +0 -4
  92. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +7 -7
  93. data/spec/fixtures/person.rb +5 -5
  94. data/spec/lutaml/model/attribute_spec.rb +37 -1
  95. data/spec/lutaml/model/cdata_spec.rb +9 -9
  96. data/spec/lutaml/model/collection_spec.rb +50 -2
  97. data/spec/lutaml/model/comparable_model_spec.rb +92 -27
  98. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  99. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  100. data/spec/lutaml/model/defaults_spec.rb +1 -1
  101. data/spec/lutaml/model/enum_spec.rb +1 -1
  102. data/spec/lutaml/model/group_spec.rb +333 -20
  103. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  104. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  105. data/spec/lutaml/model/key_value_mapping_spec.rb +65 -3
  106. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  107. data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
  108. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  109. data/spec/lutaml/model/polymorphic_spec.rb +526 -0
  110. data/spec/lutaml/model/render_empty_spec.rb +194 -0
  111. data/spec/lutaml/model/render_nil_spec.rb +206 -22
  112. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  113. data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
  114. data/spec/lutaml/model/serializable_spec.rb +179 -103
  115. data/spec/lutaml/model/simple_model_spec.rb +9 -9
  116. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  117. data/spec/lutaml/model/toml_spec.rb +51 -0
  118. data/spec/lutaml/model/transformation_spec.rb +72 -15
  119. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  120. data/spec/lutaml/model/value_map_spec.rb +240 -0
  121. data/spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb +85 -0
  122. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  123. data/spec/lutaml/model/xml/xml_element_spec.rb +93 -0
  124. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  125. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  126. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  127. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  128. data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
  129. data/spec/lutaml/model/xml_mapping_rule_spec.rb +105 -5
  130. data/spec/lutaml/model/xml_mapping_spec.rb +70 -16
  131. data/spec/lutaml/model/xml_spec.rb +63 -0
  132. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  133. data/spec/sample_model_spec.rb +3 -3
  134. data/spec/spec_helper.rb +3 -3
  135. metadata +76 -59
  136. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  137. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  138. data/lib/lutaml/model/loggable.rb +0 -15
  139. data/lib/lutaml/model/mapping_rule.rb +0 -109
  140. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  141. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  142. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  143. data/lib/lutaml/model/toml_adapter.rb +0 -6
  144. data/lib/lutaml/model/xml_mapping.rb +0 -307
  145. data/lib/lutaml/model/xml_mapping_rule.rb +0 -122
  146. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  147. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  148. data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -12,21 +12,61 @@ module Lutaml
12
12
  # Checks if two objects are equal based on their attributes
13
13
  # @param other [Object] The object to compare with
14
14
  # @return [Boolean] True if objects are equal, false otherwise
15
- def eql?(other)
16
- other.class == self.class &&
17
- self.class.attributes.all? do |attr, _|
18
- send(attr) == other.send(attr)
15
+ def eql?(other, compared_objects = {})
16
+ return true if equal?(other)
17
+ return false unless same_class?(other)
18
+ return true if already_compared?(other, compared_objects)
19
+
20
+ compared_objects[comparison_key(other)] = true
21
+ self.class.attributes.all? do |attr, _|
22
+ attr_value = send(attr)
23
+ other_value = other.send(attr)
24
+
25
+ if attr_value.respond_to?(:eql?) && same_class?(attr_value)
26
+ attr_value.eql?(other_value, compared_objects)
27
+ else
28
+ attr_value == other_value
19
29
  end
30
+ end
20
31
  end
21
32
 
22
33
  alias == eql?
23
34
 
35
+ def same_class?(other)
36
+ other.instance_of?(self.class)
37
+ end
38
+
39
+ def comparison_key(other)
40
+ "#{object_id}:#{other.object_id}"
41
+ end
42
+
43
+ def already_compared?(other, compared_objects)
44
+ compared_objects[comparison_key(other)]
45
+ end
46
+
24
47
  # Generates a hash value for the object
25
48
  # @return [Integer] The hash value
26
49
  def hash
27
- ([self.class] + self.class.attributes.map do |attr, _|
28
- send(attr).hash
29
- end).hash
50
+ calculate_hash
51
+ end
52
+
53
+ def calculate_hash(processed_objects = {}.compare_by_identity)
54
+ return if processed_objects.key?(self)
55
+
56
+ processed_objects[self] = true
57
+ ([self.class] + attributes_hash(processed_objects)).hash
58
+ end
59
+
60
+ def attributes_hash(processed_objects)
61
+ self.class.attributes.map do |attr, _|
62
+ attr_value = send(attr)
63
+
64
+ if attr_value.respond_to?(:calculate_hash)
65
+ attr_value.calculate_hash(processed_objects)
66
+ else
67
+ attr_value.hash
68
+ end
69
+ end
30
70
  end
31
71
 
32
72
  # Class methods added to the class that includes ComparableModel
@@ -419,8 +459,7 @@ module Lutaml
419
459
  # @param label [String] The label for the value
420
460
  # @param type_info [String, nil] Additional type information
421
461
  # @return [String] Formatted value tree
422
- def format_value_tree(value1, value2, parent_node, label,
423
- type_info = nil)
462
+ def format_value_tree(value1, value2, parent_node, label, type_info = nil)
424
463
  return if value1 == value2 && !@show_unchanged
425
464
 
426
465
  if value1 == value2
@@ -3,12 +3,7 @@ module Lutaml
3
3
  module Config
4
4
  extend self
5
5
 
6
- # Default values are set for these so the readers are defined below
7
- attr_writer :json_adapter, :yaml_adapter
8
-
9
- attr_accessor :xml_adapter, :toml_adapter
10
-
11
- AVAILABLE_FORMATS = %i[xml json yaml toml].freeze
6
+ AVAILABLE_FORMATS = %i[xml json yaml toml hash].freeze
12
7
  KEY_VALUE_FORMATS = AVAILABLE_FORMATS - %i[xml]
13
8
 
14
9
  def configure
@@ -42,55 +37,66 @@ module Lutaml
42
37
  # one of [:tomlib, :toml_rb]
43
38
  # @example
44
39
  # Lutaml::Model::Config.toml_adapter = :tomlib
40
+ #
41
+ # AVAILABLE_FORMATS.each do |adapter_name|
42
+ # define_method(:"#{adapter_name}_adapter_type=") do |type_name|
43
+ # Lutaml::Model::FormatRegistry.send(:"#{adapter_name}_adapter_type=", type_name)
44
+ # end
45
+ # end
46
+
45
47
  AVAILABLE_FORMATS.each do |adapter_name|
46
48
  define_method(:"#{adapter_name}_adapter_type=") do |type_name|
47
- adapter = "#{adapter_name}_adapter"
48
- type = "#{type_name}_adapter"
49
+ adapter = adapter_name.to_s
50
+ type = normalize_type_name(type_name, adapter_name)
51
+ load_adapter_file(adapter, type)
52
+ load_moxml_adapter(type_name, adapter_name)
53
+ set_adapter_for(adapter_name, class_for(adapter, type))
54
+ end
55
+ end
49
56
 
50
- begin
51
- adapter_file = File.join(adapter, type)
52
- require_relative adapter_file
53
- rescue LoadError
54
- raise(
55
- Lutaml::Model::UnknownAdapterTypeError.new(
56
- adapter_name,
57
- type_name,
58
- ),
59
- cause: nil,
60
- )
61
- end
62
- Moxml::Adapter.load(type_name) unless KEY_VALUE_FORMATS.include?(adapter_name)
57
+ def adapter_for(format)
58
+ public_send(:"#{format}_adapter")
59
+ end
63
60
 
64
- instance_variable_set(
65
- :"@#{adapter}",
66
- Lutaml::Model.const_get(to_class_name(adapter))
67
- .const_get(to_class_name(type)),
68
- )
69
- end
61
+ def set_adapter_for(format, adapter)
62
+ public_send(:"#{format}_adapter=", adapter)
70
63
  end
71
64
 
72
- # Return JSON adapter. By default StandardJsonAdapter is used
73
- #
74
- # @example
75
- # Lutaml::Model::Config.json_adapter
76
- # # => Lutaml::Model::YamlAdapter::StandardJsonAdapter
77
- def json_adapter
78
- @json_adapter || Lutaml::Model::JsonAdapter::StandardJsonAdapter
65
+ def mappings_class_for(format)
66
+ Lutaml::Model::FormatRegistry.mappings_class_for(format)
79
67
  end
80
68
 
81
- # Return YAML adapter. By default StandardYamlAdapter is used
82
- #
83
- # @example
84
- # Lutaml::Model::Config.yaml_adapter
85
- # # => Lutaml::Model::YamlAdapter::StandardYamlAdapter
86
- def yaml_adapter
87
- @yaml_adapter || Lutaml::Model::YamlAdapter::StandardYamlAdapter
69
+ def transformer_for(format)
70
+ Lutaml::Model::FormatRegistry.transformer_for(format)
71
+ end
72
+
73
+ def class_for(adapter, type)
74
+ Lutaml::Model.const_get(to_class_name(adapter))
75
+ .const_get(to_class_name(type))
88
76
  end
89
77
 
90
- # @api private
91
78
  def to_class_name(str)
92
79
  str.to_s.split("_").map(&:capitalize).join
93
80
  end
81
+
82
+ private
83
+
84
+ def normalize_type_name(type_name, adapter_name)
85
+ "#{type_name.to_s.gsub("_#{adapter_name}", '')}_adapter"
86
+ end
87
+
88
+ def load_adapter_file(adapter, type)
89
+ adapter_file = File.join(adapter, type)
90
+ require_relative adapter_file
91
+ rescue LoadError
92
+ raise UnknownAdapterTypeError.new(adapter, type), cause: nil
93
+ end
94
+
95
+ def load_moxml_adapter(type_name, adapter_name)
96
+ return if KEY_VALUE_FORMATS.include?(adapter_name)
97
+
98
+ Moxml::Adapter.load(type_name)
99
+ end
94
100
  end
95
101
  end
96
102
  end
@@ -10,7 +10,7 @@ module Lutaml
10
10
  end
11
11
 
12
12
  def to_s
13
- "#{@attr_name} count is #{@value.size}, must be #{range_to_string}"
13
+ "#{@attr_name} count is #{@value&.size || 0}, must be #{range_to_string}"
14
14
  end
15
15
 
16
16
  private
@@ -0,0 +1,14 @@
1
+ module Lutaml
2
+ module Model
3
+ class PolymorphicError < Error
4
+ def initialize(value, options, type)
5
+ error = if options[:polymorphic].is_a?(Array)
6
+ "#{value.class} not in #{options[:polymorphic]}"
7
+ else
8
+ "#{value.class} is not valid sub class of #{type}"
9
+ end
10
+ super(error)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -26,3 +26,4 @@ require_relative "error/invalid_choice_range_error"
26
26
  require_relative "error/unknown_sequence_mapping_error"
27
27
  require_relative "error/choice_lower_bound_error"
28
28
  require_relative "error/no_root_namespace_error"
29
+ require_relative "error/polymorphic_error"
@@ -0,0 +1,41 @@
1
+ module Lutaml
2
+ module Model
3
+ class FormatRegistry
4
+ class << self
5
+ attr_reader :format, :mapping_class, :adapter_class
6
+
7
+ def register(format, mapping_class:, adapter_class:, transformer:)
8
+ registered_formats[format] = {
9
+ mapping_class: mapping_class,
10
+ transformer: transformer,
11
+ }
12
+
13
+ ::Lutaml::Model::Type::Value.register_format_to_from_methods(format)
14
+ ::Lutaml::Model::Serialize.register_format_mapping_method(format)
15
+ ::Lutaml::Model::Serialize.register_from_format_method(format)
16
+ ::Lutaml::Model::Serialize.register_to_format_method(format)
17
+
18
+ Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter") do
19
+ instance_variable_get(:"@#{format}_adapter") || adapter_class
20
+ end
21
+
22
+ Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter=") do |adapter_klass|
23
+ instance_variable_set(:"@#{format}_adapter", adapter_klass)
24
+ end
25
+ end
26
+
27
+ def mappings_class_for(format)
28
+ registered_formats.dig(format, :mapping_class)
29
+ end
30
+
31
+ def transformer_for(format)
32
+ registered_formats.dig(format, :transformer)
33
+ end
34
+
35
+ def registered_formats
36
+ @registered_formats ||= {}
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module HashAdapter
6
+ # Base class for Hash documents
7
+ class Document < KeyValueDocument
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "../mapping/key_value_mapping"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module HashAdapter
6
+ class Mapping < Lutaml::Model::KeyValueMapping
7
+ def initialize
8
+ super(:hash)
9
+ end
10
+
11
+ def deep_dup
12
+ self.class.new.tap do |new_mapping|
13
+ new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "../mapping/key_value_mapping_rule"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module HashAdapter
6
+ class MappingRule < Lutaml::Model::KeyValueMappingRule; end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ require_relative "document"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module HashAdapter
6
+ class StandardAdapter < Document
7
+ def self.parse(hsh, _options = {})
8
+ hsh
9
+ end
10
+
11
+ def to_hash(_options = {})
12
+ @attributes
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module Lutaml
2
+ module Model
3
+ module HashAdapter
4
+ class Transform < Lutaml::Model::KeyValueTransform
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module HashAdapter
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative "hash/standard_adapter"
11
+ require_relative "hash/document"
12
+ require_relative "hash/mapping"
13
+ require_relative "hash/mapping_rule"
14
+ require_relative "hash/transform"
15
+
16
+ Lutaml::Model::FormatRegistry.register(
17
+ :hash,
18
+ mapping_class: Lutaml::Model::HashAdapter::Mapping,
19
+ adapter_class: Lutaml::Model::HashAdapter::StandardAdapter,
20
+ transformer: Lutaml::Model::HashAdapter::Transform,
21
+ )
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Json
6
+ # Base class for JSON documents
7
+ class Document < Lutaml::Model::KeyValueDocument
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "../mapping/key_value_mapping"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Json
6
+ class Mapping < Lutaml::Model::KeyValueMapping
7
+ def initialize
8
+ super(:json)
9
+ end
10
+
11
+ def deep_dup
12
+ self.class.new.tap do |new_mapping|
13
+ new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "../mapping/key_value_mapping_rule"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Json
6
+ class MappingRule < Lutaml::Model::KeyValueMappingRule; end
7
+ end
8
+ end
9
+ end
@@ -1,13 +1,12 @@
1
1
  require "multi_json"
2
- require_relative "json_document"
2
+ require_relative "document"
3
3
 
4
4
  module Lutaml
5
5
  module Model
6
- module JsonAdapter
7
- class MultiJsonAdapter < JsonDocument
6
+ module Json
7
+ class MultiJsonAdapter < Document
8
8
  def self.parse(json, _options = {})
9
- data = MultiJson.load(json)
10
- new(data)
9
+ MultiJson.load(json)
11
10
  end
12
11
 
13
12
  def to_json(*args)
@@ -1,10 +1,12 @@
1
1
  require "json"
2
- require_relative "json_document"
2
+ require_relative "document"
3
3
 
4
4
  module Lutaml
5
5
  module Model
6
- module JsonAdapter
7
- class StandardJsonAdapter < JsonDocument
6
+ module Json
7
+ class StandardAdapter < Document
8
+ FORMAT_SYMBOL = :json
9
+
8
10
  def self.parse(json, _options = {})
9
11
  JSON.parse(json, create_additions: false)
10
12
  end
@@ -0,0 +1,8 @@
1
+ module Lutaml
2
+ module Model
3
+ module Json
4
+ class Transform < Lutaml::Model::KeyValueTransform
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Json
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative "json/standard_adapter"
11
+ require_relative "json/document"
12
+ require_relative "json/mapping"
13
+ require_relative "json/mapping_rule"
14
+ require_relative "json/transform"
15
+
16
+ Lutaml::Model::FormatRegistry.register(
17
+ :json,
18
+ mapping_class: Lutaml::Model::Json::Mapping,
19
+ adapter_class: Lutaml::Model::Json::StandardAdapter,
20
+ transformer: Lutaml::Model::Json::Transform,
21
+ )
@@ -0,0 +1,27 @@
1
+ module Lutaml
2
+ module Model
3
+ class KeyValueDocument
4
+ attr_reader :attributes
5
+
6
+ def initialize(attributes = {})
7
+ @attributes = attributes
8
+ end
9
+
10
+ def [](key)
11
+ @attributes[key]
12
+ end
13
+
14
+ def []=(key, value)
15
+ @attributes[key] = value
16
+ end
17
+
18
+ def key?(key)
19
+ @attributes.key?(key)
20
+ end
21
+
22
+ def to_h
23
+ @attributes
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,12 +1,15 @@
1
+ require_relative "mapping"
1
2
  require_relative "key_value_mapping_rule"
2
3
 
3
4
  module Lutaml
4
5
  module Model
5
- class KeyValueMapping
6
- attr_reader :mappings
6
+ class KeyValueMapping < Mapping
7
+ attr_reader :mappings, :format
7
8
 
8
- def initialize
9
- @mappings = []
9
+ def initialize(format = nil)
10
+ super()
11
+
12
+ @format = format
10
13
  end
11
14
 
12
15
  def map(
@@ -14,25 +17,39 @@ module Lutaml
14
17
  to: nil,
15
18
  render_nil: false,
16
19
  render_default: false,
20
+ render_empty: false,
21
+ treat_nil: nil,
22
+ treat_empty: nil,
23
+ treat_omitted: nil,
17
24
  with: {},
18
25
  delegate: nil,
19
26
  child_mappings: nil,
20
27
  root_mappings: nil,
21
- transform: {}
28
+ polymorphic: {},
29
+ polymorphic_map: {},
30
+ transform: {},
31
+ value_map: {}
22
32
  )
23
33
  mapping_name = name_for_mapping(root_mappings, name)
24
- validate!(mapping_name, to, with)
34
+ validate!(mapping_name, to, with, render_nil, render_empty)
25
35
 
26
36
  @mappings << KeyValueMappingRule.new(
27
37
  mapping_name,
28
38
  to: to,
29
39
  render_nil: render_nil,
30
40
  render_default: render_default,
41
+ render_empty: render_empty,
42
+ treat_nil: treat_nil,
43
+ treat_empty: treat_empty,
44
+ treat_omitted: treat_omitted,
31
45
  with: with,
32
46
  delegate: delegate,
33
47
  child_mappings: child_mappings,
34
48
  root_mappings: root_mappings,
49
+ polymorphic: polymorphic,
50
+ polymorphic_map: polymorphic_map,
35
51
  transform: transform,
52
+ value_map: value_map,
36
53
  )
37
54
  end
38
55
 
@@ -46,7 +63,7 @@ module Lutaml
46
63
  delegate: nil
47
64
  )
48
65
  @raw_mapping = true
49
- validate!(Constants::RAW_MAPPING_KEY, to, with)
66
+ validate!(Constants::RAW_MAPPING_KEY, to, with, render_nil, nil)
50
67
  @mappings << KeyValueMappingRule.new(
51
68
  Constants::RAW_MAPPING_KEY,
52
69
  to: to,
@@ -65,28 +82,49 @@ module Lutaml
65
82
  name
66
83
  end
67
84
 
68
- def validate!(key, to, with)
85
+ def validate!(key, to, with, render_nil, render_empty)
69
86
  validate_mappings!(key)
87
+ validate_to_and_with_arguments!(key, to, with)
88
+
89
+ # Validate `render_nil` for unsupported value
90
+ validate_blank_mappings!(render_nil, render_empty)
91
+ validate_root_mappings!(key)
92
+ end
70
93
 
94
+ def validate_to_and_with_arguments!(key, to, with)
71
95
  if to.nil? && with.empty? && !@raw_mapping
72
- msg = ":to or :with argument is required for mapping '#{key}'"
73
- raise IncorrectMappingArgumentsError.new(msg)
96
+ raise IncorrectMappingArgumentsError.new(
97
+ ":to or :with argument is required for mapping '#{key}'",
98
+ )
74
99
  end
75
100
 
101
+ validate_with_options!(to, key, with)
102
+ end
103
+
104
+ def validate_with_options!(to, key, with)
105
+ return true if to
106
+
76
107
  if !with.empty? && (with[:from].nil? || with[:to].nil?) && !@raw_mapping
77
- msg = ":with argument for mapping '#{key}' requires :to and :from keys"
78
- raise IncorrectMappingArgumentsError.new(msg)
108
+ raise IncorrectMappingArgumentsError.new(
109
+ ":with argument for mapping '#{key}' requires :to and :from keys",
110
+ )
79
111
  end
80
-
81
- validate_mappings(key)
82
112
  end
83
113
 
84
- def validate_mappings(name)
114
+ def validate_root_mappings!(name)
85
115
  if @mappings.any?(&:root_mapping?) || (name == "root_mapping" && @mappings.any?)
86
116
  raise MultipleMappingsError.new("root_mappings cannot be used with other mappings")
87
117
  end
88
118
  end
89
119
 
120
+ def validate_blank_mappings!(render_nil, render_empty)
121
+ if render_nil == :as_blank || render_empty == :as_blank
122
+ raise IncorrectMappingArgumentsError.new(
123
+ ":as_blank is not supported for key-value mappings",
124
+ )
125
+ end
126
+ end
127
+
90
128
  def validate_mappings!(_type)
91
129
  if (@raw_mapping && Utils.present?(@mappings)) || (!@raw_mapping && @mappings.any?(&:raw_mapping?))
92
130
  raise StandardError, "map_all is not allowed with other mappings"
@@ -94,7 +132,7 @@ module Lutaml
94
132
  end
95
133
 
96
134
  def deep_dup
97
- self.class.new.tap do |new_mapping|
135
+ self.class.new(@format).tap do |new_mapping|
98
136
  new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
99
137
  end
100
138
  end
@@ -106,6 +144,16 @@ module Lutaml
106
144
  def find_by_to(to)
107
145
  @mappings.find { |m| m.to.to_s == to.to_s }
108
146
  end
147
+
148
+ def polymorphic_mapping
149
+ @mappings.find(&:polymorphic_mapping?)
150
+ end
151
+
152
+ Lutaml::Model::Config::KEY_VALUE_FORMATS.each do |format|
153
+ define_method(:"format_#{format}?") do
154
+ @format == format
155
+ end
156
+ end
109
157
  end
110
158
  end
111
159
  end