lutaml-model 0.7.3 → 0.7.5

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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.github/workflows/release.yml +3 -0
  4. data/.gitignore +6 -1
  5. data/.irbrc +1 -0
  6. data/.pryrc +1 -0
  7. data/.rubocop_todo.yml +25 -52
  8. data/README.adoc +2177 -192
  9. data/docs/custom_registers.adoc +228 -0
  10. data/docs/schema_generation.adoc +898 -0
  11. data/docs/schema_import.adoc +364 -0
  12. data/flake.lock +114 -0
  13. data/flake.nix +103 -0
  14. data/lib/lutaml/model/attribute.rb +230 -94
  15. data/lib/lutaml/model/choice.rb +30 -0
  16. data/lib/lutaml/model/collection.rb +194 -0
  17. data/lib/lutaml/model/comparable_model.rb +3 -3
  18. data/lib/lutaml/model/config.rb +26 -3
  19. data/lib/lutaml/model/constants.rb +2 -0
  20. data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
  21. data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
  22. data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
  23. data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
  24. data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
  25. data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
  26. data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
  27. data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
  28. data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
  29. data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
  30. data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
  31. data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
  32. data/lib/lutaml/model/error.rb +10 -0
  33. data/lib/lutaml/model/errors.rb +36 -0
  34. data/lib/lutaml/model/format_registry.rb +5 -2
  35. data/lib/lutaml/model/global_register.rb +41 -0
  36. data/lib/lutaml/model/{hash.rb → hash_adapter.rb} +5 -5
  37. data/lib/lutaml/model/jsonl/document.rb +14 -0
  38. data/lib/lutaml/model/jsonl/mapping.rb +19 -0
  39. data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
  40. data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
  41. data/lib/lutaml/model/jsonl/transform.rb +19 -0
  42. data/lib/lutaml/model/jsonl.rb +21 -0
  43. data/lib/lutaml/model/key_value_document.rb +3 -2
  44. data/lib/lutaml/model/mapping/key_value_mapping.rb +64 -4
  45. data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +4 -0
  46. data/lib/lutaml/model/mapping/mapping_rule.rb +8 -3
  47. data/lib/lutaml/model/register.rb +105 -0
  48. data/lib/lutaml/model/registrable.rb +6 -0
  49. data/lib/lutaml/model/schema/base_schema.rb +64 -0
  50. data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
  51. data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
  52. data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
  53. data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
  54. data/lib/lutaml/model/schema/generator/definition.rb +53 -0
  55. data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
  56. data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
  57. data/lib/lutaml/model/schema/generator/property.rb +110 -0
  58. data/lib/lutaml/model/schema/generator/ref.rb +24 -0
  59. data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
  60. data/lib/lutaml/model/schema/json_schema.rb +42 -49
  61. data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
  62. data/lib/lutaml/model/schema/renderer.rb +36 -0
  63. data/lib/lutaml/model/schema/shared_methods.rb +24 -0
  64. data/lib/lutaml/model/schema/templates/model.erb +9 -0
  65. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
  66. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
  67. data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
  68. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
  69. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
  70. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
  71. data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
  72. data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
  73. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
  74. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
  75. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
  76. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
  77. data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
  78. data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
  79. data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
  80. data/lib/lutaml/model/schema.rb +1 -0
  81. data/lib/lutaml/model/sequence.rb +60 -30
  82. data/lib/lutaml/model/serialize.rb +175 -53
  83. data/lib/lutaml/model/services/base.rb +11 -0
  84. data/lib/lutaml/model/services/logger.rb +2 -2
  85. data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
  86. data/lib/lutaml/model/services/type/validator/number.rb +25 -0
  87. data/lib/lutaml/model/services/type/validator/string.rb +52 -0
  88. data/lib/lutaml/model/services/type/validator.rb +43 -0
  89. data/lib/lutaml/model/services/validator.rb +145 -0
  90. data/lib/lutaml/model/services.rb +3 -0
  91. data/lib/lutaml/model/transform/key_value_transform.rb +60 -50
  92. data/lib/lutaml/model/transform/xml_transform.rb +46 -57
  93. data/lib/lutaml/model/transform.rb +22 -8
  94. data/lib/lutaml/model/type/boolean.rb +1 -1
  95. data/lib/lutaml/model/type/date.rb +1 -1
  96. data/lib/lutaml/model/type/date_time.rb +1 -1
  97. data/lib/lutaml/model/type/decimal.rb +11 -9
  98. data/lib/lutaml/model/type/float.rb +2 -1
  99. data/lib/lutaml/model/type/integer.rb +24 -21
  100. data/lib/lutaml/model/type/string.rb +4 -2
  101. data/lib/lutaml/model/type/time.rb +1 -1
  102. data/lib/lutaml/model/type/time_without_date.rb +1 -1
  103. data/lib/lutaml/model/type/value.rb +5 -1
  104. data/lib/lutaml/model/type.rb +5 -2
  105. data/lib/lutaml/model/utils.rb +30 -8
  106. data/lib/lutaml/model/validation.rb +6 -4
  107. data/lib/lutaml/model/version.rb +1 -1
  108. data/lib/lutaml/model/xml/document.rb +37 -19
  109. data/lib/lutaml/model/xml/mapping.rb +74 -13
  110. data/lib/lutaml/model/xml/mapping_rule.rb +10 -2
  111. data/lib/lutaml/model/xml/nokogiri_adapter.rb +5 -3
  112. data/lib/lutaml/model/xml/oga/element.rb +4 -1
  113. data/lib/lutaml/model/xml/oga_adapter.rb +4 -3
  114. data/lib/lutaml/model/xml/ox_adapter.rb +20 -6
  115. data/lib/lutaml/model/xml/xml_element.rb +3 -28
  116. data/lib/lutaml/model/xml_adapter/element.rb +1 -1
  117. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +1 -1
  118. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
  119. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -1
  120. data/lib/lutaml/model/yamls/document.rb +14 -0
  121. data/lib/lutaml/model/yamls/mapping.rb +19 -0
  122. data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
  123. data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
  124. data/lib/lutaml/model/yamls/transform.rb +19 -0
  125. data/lib/lutaml/model/yamls.rb +21 -0
  126. data/lib/lutaml/model.rb +7 -31
  127. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -5
  128. data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
  129. data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
  130. data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
  131. data/spec/fixtures/xml/product_catalog.xsd +151 -0
  132. data/spec/fixtures/xml/specifications_schema.xsd +38 -0
  133. data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
  134. data/spec/lutaml/model/attribute_spec.rb +41 -44
  135. data/spec/lutaml/model/choice_spec.rb +44 -0
  136. data/spec/lutaml/model/custom_collection_spec.rb +830 -0
  137. data/spec/lutaml/model/custom_model_spec.rb +15 -3
  138. data/spec/lutaml/model/defaults_spec.rb +5 -1
  139. data/spec/lutaml/model/global_register_spec.rb +108 -0
  140. data/spec/lutaml/model/group_spec.rb +9 -3
  141. data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
  142. data/spec/lutaml/model/jsonl_spec.rb +229 -0
  143. data/spec/lutaml/model/multiple_mapping_spec.rb +1 -1
  144. data/spec/lutaml/model/register/key_value_spec.rb +275 -0
  145. data/spec/lutaml/model/register/xml_spec.rb +187 -0
  146. data/spec/lutaml/model/register_spec.rb +147 -0
  147. data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
  148. data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
  149. data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
  150. data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
  151. data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
  152. data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
  153. data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
  154. data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
  155. data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
  156. data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
  157. data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
  158. data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
  159. data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
  160. data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
  161. data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
  162. data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
  163. data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
  164. data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
  165. data/spec/lutaml/model/sequence_spec.rb +36 -0
  166. data/spec/lutaml/model/serializable_spec.rb +31 -0
  167. data/spec/lutaml/model/type_spec.rb +8 -4
  168. data/spec/lutaml/model/utils_spec.rb +3 -3
  169. data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
  170. data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
  171. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  172. data/spec/lutaml/model/xml_adapter_spec.rb +24 -0
  173. data/spec/lutaml/model/xml_mapping_rule_spec.rb +11 -4
  174. data/spec/lutaml/model/xml_mapping_spec.rb +1 -1
  175. data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
  176. data/spec/lutaml/model/yamls_spec.rb +294 -0
  177. data/spec/spec_helper.rb +1 -0
  178. metadata +105 -9
  179. data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
  180. /data/lib/lutaml/model/{hash → hash_adapter}/document.rb +0 -0
  181. /data/lib/lutaml/model/{hash → hash_adapter}/mapping.rb +0 -0
  182. /data/lib/lutaml/model/{hash → hash_adapter}/mapping_rule.rb +0 -0
  183. /data/lib/lutaml/model/{hash → hash_adapter}/standard_adapter.rb +0 -0
  184. /data/lib/lutaml/model/{hash → hash_adapter}/transform.rb +0 -0
@@ -0,0 +1,194 @@
1
+ module Lutaml
2
+ module Model
3
+ class Collection < Lutaml::Model::Serializable
4
+ include Enumerable
5
+
6
+ class << self
7
+ attr_reader :instance_type,
8
+ :instance_name,
9
+ :order_by_field,
10
+ :order_direction
11
+
12
+ def instances(name, type, &block)
13
+ attribute(name, type, collection: true, validations: block)
14
+
15
+ @instance_type = Lutaml::Model::Attribute.cast_type!(type)
16
+ @instance_name = name
17
+
18
+ define_method(:"#{name}=") do |collection|
19
+ self.collection = collection
20
+ end
21
+ end
22
+
23
+ def ordered(by:, order: :asc)
24
+ @order_by_field = by.to_sym
25
+ @order_direction = order
26
+ end
27
+
28
+ def sort_configured?
29
+ !!@order_by_field
30
+ end
31
+
32
+ def to(format, instance, options = {})
33
+ mappings = mappings_for(format)
34
+
35
+ if mappings.no_root? && format == :xml
36
+ mappings.mappings.map do |mapping|
37
+ serialize_for_mapping(mapping, instance, format, options)
38
+ end.join("\n")
39
+ else
40
+ super(format, instance, options.merge(collection: true))
41
+ end
42
+ end
43
+
44
+ def serialize_for_mapping(mapping, instance, format, options)
45
+ options[:tag_name] = mapping.name
46
+
47
+ attr_value = instance.public_send(mapping.to)
48
+ return if attr_value.nil? || attr_value.empty?
49
+
50
+ attr_value = [attr_value] unless attr_value.is_a?(Array)
51
+ attr_value.map { |v| v.public_send(:"to_#{format}", options) }
52
+ end
53
+
54
+ def as(format, instance, options = {})
55
+ mappings = mappings_for(format)
56
+ data = super
57
+
58
+ if mappings.no_root? && format != :xml && !mappings.root_mapping
59
+ Utils.fetch_str_or_sym(data, instance_name)
60
+ else
61
+ data
62
+ end
63
+ end
64
+
65
+ def from(format, data, options = {})
66
+ mappings = mappings_for(format)
67
+ if mappings.no_root? && format == :xml
68
+ tag_name = mappings.find_by_to(instance_name).name
69
+ data = "<#{tag_name}>#{data}</#{tag_name}>"
70
+ end
71
+
72
+ super(format, data, options.merge(from_collection: true))
73
+ end
74
+
75
+ def of(format, data, options = {})
76
+ mappings = mappings_for(format)
77
+
78
+ if mappings.no_root? && format != :xml && !mappings.root_mapping
79
+ data = { mappings.find_by_to(instance_name).name => data }
80
+ end
81
+
82
+ super(format, data, options.merge(from_collection: true))
83
+ end
84
+
85
+ def apply_mappings(data, format, options = {})
86
+ super(data, format, options.merge(collection: true))
87
+ end
88
+ end
89
+
90
+ attr_reader :register
91
+
92
+ def initialize(items = [], register: Lutaml::Model::Config.default_register)
93
+ super()
94
+
95
+ @register = register
96
+ items = [items].compact unless items.is_a?(Array)
97
+
98
+ register_object = Lutaml::Model::GlobalRegister.lookup(register)
99
+ type = register_object.get_class_without_register(self.class.instance_type)
100
+ self.collection = items.map do |item|
101
+ if item.is_a?(type)
102
+ item
103
+ elsif type <= Lutaml::Model::Type::Value
104
+ type.cast(item)
105
+ else
106
+ type.new(item, register: register)
107
+ end
108
+ end
109
+
110
+ sort_items!
111
+ end
112
+
113
+ def to_format(format, options = {})
114
+ super(format, options.merge(collection: true))
115
+ end
116
+
117
+ def collection
118
+ instance_variable_get(:"@#{self.class.instance_name}")
119
+ end
120
+
121
+ def collection=(collection)
122
+ instance_variable_set(:"@#{self.class.instance_name}", collection)
123
+ sort_items!
124
+ end
125
+
126
+ def union(other)
127
+ self.class.new((items + other.items).uniq)
128
+ end
129
+
130
+ def intersection(other)
131
+ self.class.new(items & other.items)
132
+ end
133
+
134
+ def difference(other)
135
+ self.class.new(items - other.items)
136
+ end
137
+
138
+ def each(&block)
139
+ collection.each(&block)
140
+ end
141
+
142
+ def size
143
+ collection.size
144
+ end
145
+
146
+ def first
147
+ collection.first
148
+ end
149
+
150
+ def last
151
+ collection.last
152
+ end
153
+
154
+ def <<(item)
155
+ push(item)
156
+ end
157
+
158
+ def push(item)
159
+ collection.push(item)
160
+ sort_items!
161
+ end
162
+
163
+ def [](index)
164
+ collection[index]
165
+ end
166
+
167
+ def []=(index, value)
168
+ collection[index] = value
169
+ sort_items!
170
+ end
171
+
172
+ def empty?
173
+ collection&.empty?
174
+ end
175
+
176
+ def order_defined?
177
+ self.class.sort_configured?
178
+ end
179
+
180
+ def sort_items!
181
+ return if collection.nil?
182
+ return unless order_defined?
183
+
184
+ unless collection&.one?
185
+ field = self.class.order_by_field
186
+ direction = self.class.order_direction
187
+
188
+ collection.sort_by! { |item| item.send(field) }
189
+ collection.reverse! if direction == :desc
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -467,7 +467,7 @@ module Lutaml
467
467
  return format_single_value(
468
468
  value1,
469
469
  parent_node,
470
- "#{label}#{type_info ? " (#{type_info})" : ''}",
470
+ "#{label}#{" (#{type_info})" if type_info}",
471
471
  )
472
472
  end
473
473
 
@@ -482,7 +482,7 @@ module Lutaml
482
482
  when ComparableModel
483
483
  format_object_attributes(value1, value2, parent_node)
484
484
  else
485
- node = Tree.new("#{label}#{type_info ? " (#{type_info})" : ''}:")
485
+ node = Tree.new("#{label}#{" (#{type_info})" if type_info}:")
486
486
  parent_node.add_child(node)
487
487
  node.add_child(Tree.new("- #{format_value(value1)}", color: :red))
488
488
  node.add_child(Tree.new("+ #{format_value(value2)}", color: :green))
@@ -495,7 +495,7 @@ module Lutaml
495
495
  # @param label [String] The label for the value
496
496
  # @return [String] Formatted single value
497
497
  def format_single_value(value, parent_node, label, color = nil)
498
- node = Tree.new("#{label}#{label.empty? ? '' : ':'}", color: color)
498
+ node = Tree.new("#{label}#{':' unless label.empty?}", color: color)
499
499
  parent_node.add_child(node)
500
500
 
501
501
  case value
@@ -3,11 +3,13 @@ module Lutaml
3
3
  module Config
4
4
  extend self
5
5
 
6
- AVAILABLE_FORMATS = %i[xml json yaml toml hash].freeze
6
+ AVAILABLE_FORMATS = %i[xml json jsonl yaml toml hash].freeze
7
7
  KEY_VALUE_FORMATS = AVAILABLE_FORMATS - %i[xml]
8
8
 
9
9
  def configure
10
10
  yield self
11
+
12
+ self
11
13
  end
12
14
 
13
15
  # This will generate the following methods
@@ -55,11 +57,12 @@ module Lutaml
55
57
  end
56
58
 
57
59
  def adapter_for(format)
58
- public_send(:"#{format}_adapter")
60
+ @adapters[format]
59
61
  end
60
62
 
61
63
  def set_adapter_for(format, adapter)
62
- public_send(:"#{format}_adapter=", adapter)
64
+ @adapters ||= {}
65
+ @adapters[format] = adapter
63
66
  end
64
67
 
65
68
  def mappings_class_for(format)
@@ -71,10 +74,27 @@ module Lutaml
71
74
  end
72
75
 
73
76
  def class_for(adapter, type)
77
+ # TODO: Remove once HashAdapter namespace is renamed to Hash
78
+ adapter = "hash_adapter" if adapter == "hash"
79
+
74
80
  Lutaml::Model.const_get(to_class_name(adapter))
75
81
  .const_get(to_class_name(type))
76
82
  end
77
83
 
84
+ def default_register
85
+ @default_register ||= :default
86
+ end
87
+
88
+ def default_register=(value)
89
+ @default_register = case value
90
+ when Symbol then value
91
+ when Lutaml::Model::Register then value.id
92
+ else
93
+ raise "Unknown register: #{value}, expected a Symbol or a Lutaml::Model::Register instance"
94
+ end
95
+ end
96
+
97
+ # @api private
78
98
  def to_class_name(str)
79
99
  str.to_s.split("_").map(&:capitalize).join
80
100
  end
@@ -86,6 +106,9 @@ module Lutaml
86
106
  end
87
107
 
88
108
  def load_adapter_file(adapter, type)
109
+ # TODO: Remove once HashAdapter namespace is renamed to Hash
110
+ adapter = "hash_adapter" if adapter == "hash"
111
+
89
112
  adapter_file = File.join(adapter, type)
90
113
  require_relative adapter_file
91
114
  rescue LoadError
@@ -2,6 +2,8 @@ module Lutaml
2
2
  module Model
3
3
  module Constants
4
4
  RAW_MAPPING_KEY = "__raw_mapping".freeze
5
+ KEY_MAPPING_KEY = "__key_mapping".freeze
6
+ VALUE_MAPPING_KEY = "__value_mapping".freeze
5
7
  end
6
8
  end
7
9
  end
@@ -0,0 +1,29 @@
1
+ module Lutaml
2
+ module Model
3
+ class ElementCountOutOfRangeError < Error
4
+ def initialize(attr_name, appearance_count, range)
5
+ @attr_name = attr_name
6
+ @appearance_count = appearance_count
7
+ @range = range
8
+
9
+ super()
10
+ end
11
+
12
+ def to_s
13
+ "`#{@attr_name}` expected to appear between '#{@range.min}' and '#{@range.max}' time#{times_plural(@range.max)}, but #{times_appeared}."
14
+ end
15
+
16
+ private
17
+
18
+ def times_appeared
19
+ return "never appeared" if @appearance_count&.zero?
20
+
21
+ "appeared only #{@appearance_count} time#{times_plural(@appearance_count)}"
22
+ end
23
+
24
+ def times_plural(count)
25
+ "s" if count > 1
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ module Lutaml
2
+ module Model
3
+ class InvalidAttributeNameError < Error
4
+ def initialize(name)
5
+ @name = name
6
+
7
+ super()
8
+ end
9
+
10
+ def to_s
11
+ "Attribute name '#{@name}' is not allowed"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Lutaml
2
+ module Model
3
+ class InvalidAttributeOptionsError < Error
4
+ def initialize(name, invalid_opts)
5
+ @name = name
6
+ @invalid_opts = invalid_opts
7
+
8
+ super()
9
+ end
10
+
11
+ def to_s
12
+ "Invalid options given for `#{@name}` #{@invalid_opts}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -9,11 +9,9 @@ module Lutaml
9
9
  end
10
10
 
11
11
  def to_s
12
- if @min.negative?
13
- "Choice lower bound `#{@min}` must be positive"
14
- else
15
- "Choice upper bound `#{@max}` must be positive"
16
- end
12
+ bound_name, value = @min.negative? ? ["lower", @min] : ["upper", @max]
13
+
14
+ "Choice #{bound_name} bound `#{value}` must be positive"
17
15
  end
18
16
  end
19
17
  end
@@ -0,0 +1,11 @@
1
+ module Lutaml
2
+ module Model
3
+ class Register
4
+ class NotRegistrableClassError < Error
5
+ def initialize(model_name)
6
+ super("`#{model_name}` must be a `Lutaml::Model::Registrable` class or module")
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,14 +4,16 @@ module Lutaml
4
4
  module Model
5
5
  module Type
6
6
  class InvalidValueError < Error
7
- def initialize(message)
8
- @message = message
7
+ def initialize(value, allowed_values)
8
+ @value = value
9
+ @allowed_values = allowed_values
9
10
 
10
11
  super()
11
12
  end
12
13
 
13
14
  def to_s
14
- @message
15
+ "`#{@value}` is invalid, must be one of the " \
16
+ "following #{@allowed_values.inspect}"
15
17
  end
16
18
  end
17
19
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ class MaxBoundError < Error
7
+ def initialize(value, max_bound)
8
+ @value = value
9
+ @max_bound = max_bound
10
+
11
+ super()
12
+ end
13
+
14
+ def to_s
15
+ "Value #{@value} is greater than the set maximum limit #{@max_bound}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ class MaxLengthError < Error
7
+ def initialize(value, max_length)
8
+ @value = value
9
+ @max_length = max_length
10
+
11
+ super()
12
+ end
13
+
14
+ def to_s
15
+ "String \"#{@value}\" length (#{@value.length}) is greater than the maximum allowed length #{@max_length}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ class MinBoundError < Error
7
+ def initialize(value, min_bound)
8
+ @value = value
9
+ @min_bound = min_bound
10
+
11
+ super()
12
+ end
13
+
14
+ def to_s
15
+ "Value #{@value} is less than the set minimum limit #{@min_bound}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ class MinLengthError < Error
7
+ def initialize(value, min_length)
8
+ @value = value
9
+ @min_length = min_length
10
+
11
+ super()
12
+ end
13
+
14
+ def to_s
15
+ "String \"#{@value}\" length (#{@value.length}) is less than the minimum required length #{@min_length}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class PatternNotMatchedError < Error
5
+ def initialize(value, pattern)
6
+ @pattern = pattern
7
+ @value = value
8
+
9
+ super()
10
+ end
11
+
12
+ def to_s
13
+ "\"#{@value}\" does not match #{@pattern.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ module Lutaml
2
+ module Model
3
+ class ValidationFailedError < Error
4
+ def initialize(errors)
5
+ super("Validation failed: #{errors.map { |e| "`#{e}`" }.join(', ')}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -11,6 +11,7 @@ require_relative "error/incorrect_mapping_argument_error"
11
11
  require_relative "error/pattern_not_matched_error"
12
12
  require_relative "error/unknown_adapter_type_error"
13
13
  require_relative "error/collection_count_out_of_range_error"
14
+ require_relative "error/element_count_out_of_range_error"
14
15
  require_relative "error/validation_error"
15
16
  require_relative "error/type_not_enabled_error"
16
17
  require_relative "error/type_error"
@@ -18,6 +19,11 @@ require_relative "error/unknown_type_error"
18
19
  require_relative "error/multiple_mappings_error"
19
20
  require_relative "error/collection_true_missing_error"
20
21
  require_relative "error/type/invalid_value_error"
22
+ require_relative "error/type/min_bound_error"
23
+ require_relative "error/type/max_bound_error"
24
+ require_relative "error/type/pattern_not_matched_error"
25
+ require_relative "error/type/min_length_error"
26
+ require_relative "error/type/max_length_error"
21
27
  require_relative "error/incorrect_sequence_error"
22
28
  require_relative "error/choice_upper_bound_error"
23
29
  require_relative "error/no_root_mapping_error"
@@ -27,3 +33,7 @@ require_relative "error/unknown_sequence_mapping_error"
27
33
  require_relative "error/choice_lower_bound_error"
28
34
  require_relative "error/no_root_namespace_error"
29
35
  require_relative "error/polymorphic_error"
36
+ require_relative "error/validation_failed_error"
37
+ require_relative "error/invalid_attribute_name_error"
38
+ require_relative "error/invalid_attribute_options_error"
39
+ require_relative "error/register/not_registrable_class_error"
@@ -0,0 +1,36 @@
1
+ module Lutaml
2
+ module Model
3
+ class Errors
4
+ include Enumerable
5
+
6
+ def initialize
7
+ @errors = {}
8
+ end
9
+
10
+ def add(attr, error)
11
+ @errors[attr] ||= []
12
+ @errors[attr] << error
13
+ end
14
+
15
+ def empty?
16
+ @errors.empty?
17
+ end
18
+
19
+ def each(&block)
20
+ @errors.each(&block)
21
+ end
22
+
23
+ def full_messages
24
+ @errors.map { |attr, errors| "#{attr}: #{errors.join(', ')}" }
25
+ end
26
+
27
+ def messages
28
+ @errors.map { |_, errors| errors.join(", ") }.flatten
29
+ end
30
+
31
+ def to_s
32
+ full_messages.join("\n")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -15,12 +15,15 @@ module Lutaml
15
15
  ::Lutaml::Model::Serialize.register_from_format_method(format)
16
16
  ::Lutaml::Model::Serialize.register_to_format_method(format)
17
17
 
18
+ Lutaml::Model::Config.set_adapter_for(format, adapter_class)
19
+
18
20
  Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter") do
19
- instance_variable_get(:"@#{format}_adapter") || adapter_class
21
+ @adapters[format] || adapter_class
20
22
  end
21
23
 
22
24
  Lutaml::Model::Config.define_singleton_method(:"#{format}_adapter=") do |adapter_klass|
23
- instance_variable_set(:"@#{format}_adapter", adapter_klass)
25
+ @adapters ||= {}
26
+ @adapters[format] = adapter_klass
24
27
  end
25
28
  end
26
29
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ class GlobalRegister
6
+ include Singleton
7
+
8
+ def initialize
9
+ @registers = {}
10
+ default_register = Lutaml::Model::Register.new(:default)
11
+ register(default_register)
12
+ end
13
+
14
+ def register(model_register)
15
+ @registers[model_register.id] = model_register
16
+ end
17
+
18
+ def lookup(id)
19
+ @registers[id.to_sym]
20
+ end
21
+
22
+ def remove(id)
23
+ @registers.delete(id.to_sym)
24
+ end
25
+
26
+ class << self
27
+ def register(model_register)
28
+ instance.register(model_register)
29
+ end
30
+
31
+ def lookup(id)
32
+ instance.lookup(id)
33
+ end
34
+
35
+ def remove(id)
36
+ instance.remove(id)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -7,11 +7,11 @@ module Lutaml
7
7
  end
8
8
  end
9
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"
10
+ require_relative "hash_adapter/standard_adapter"
11
+ require_relative "hash_adapter/document"
12
+ require_relative "hash_adapter/mapping"
13
+ require_relative "hash_adapter/mapping_rule"
14
+ require_relative "hash_adapter/transform"
15
15
 
16
16
  Lutaml::Model::FormatRegistry.register(
17
17
  :hash,
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Jsonl
6
+ class Document
7
+ def initialize(jsons = [], register: nil)
8
+ @jsons = jsons
9
+ @register = register || Lutaml::Model::Config.default_register
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end