lutaml-model 0.7.1 → 0.7.7

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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +1 -0
  3. data/.github/workflows/dependent-tests.yml +4 -0
  4. data/.github/workflows/rake.yml +12 -0
  5. data/.github/workflows/release.yml +3 -0
  6. data/.gitignore +6 -1
  7. data/.irbrc +1 -0
  8. data/.pryrc +1 -0
  9. data/.rubocop.yml +1 -1
  10. data/.rubocop_todo.yml +58 -84
  11. data/Gemfile +4 -1
  12. data/README.adoc +3115 -365
  13. data/RELEASE_NOTES.adoc +346 -0
  14. data/docs/custom_adapters.adoc +144 -0
  15. data/docs/custom_registers.adoc +228 -0
  16. data/docs/schema_generation.adoc +898 -0
  17. data/docs/schema_import.adoc +364 -0
  18. data/flake.lock +114 -0
  19. data/flake.nix +103 -0
  20. data/lib/lutaml/model/attribute.rb +242 -100
  21. data/lib/lutaml/model/choice.rb +30 -0
  22. data/lib/lutaml/model/collection.rb +212 -0
  23. data/lib/lutaml/model/comparable_model.rb +3 -3
  24. data/lib/lutaml/model/config.rb +71 -42
  25. data/lib/lutaml/model/constants.rb +2 -0
  26. data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
  27. data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
  28. data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
  29. data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
  30. data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
  31. data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
  32. data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
  33. data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
  34. data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
  35. data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
  36. data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
  37. data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
  38. data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
  39. data/lib/lutaml/model/error.rb +10 -0
  40. data/lib/lutaml/model/errors.rb +36 -0
  41. data/lib/lutaml/model/format_registry.rb +44 -0
  42. data/lib/lutaml/model/global_register.rb +41 -0
  43. data/lib/lutaml/model/hash_adapter/document.rb +11 -0
  44. data/lib/lutaml/model/hash_adapter/mapping.rb +19 -0
  45. data/lib/lutaml/model/hash_adapter/mapping_rule.rb +9 -0
  46. data/lib/lutaml/model/hash_adapter/standard_adapter.rb +17 -0
  47. data/lib/lutaml/model/hash_adapter/transform.rb +8 -0
  48. data/lib/lutaml/model/hash_adapter.rb +21 -0
  49. data/lib/lutaml/model/json/document.rb +11 -0
  50. data/lib/lutaml/model/json/mapping.rb +19 -0
  51. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  52. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  53. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  54. data/lib/lutaml/model/json/transform.rb +8 -0
  55. data/lib/lutaml/model/json.rb +21 -0
  56. data/lib/lutaml/model/jsonl/document.rb +14 -0
  57. data/lib/lutaml/model/jsonl/mapping.rb +19 -0
  58. data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
  59. data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
  60. data/lib/lutaml/model/jsonl/transform.rb +19 -0
  61. data/lib/lutaml/model/jsonl.rb +21 -0
  62. data/lib/lutaml/model/key_value_document.rb +28 -0
  63. data/lib/lutaml/model/mapping/key_value_mapping.rb +85 -13
  64. data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +5 -0
  65. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  66. data/lib/lutaml/model/mapping/mapping_rule.rb +15 -9
  67. data/lib/lutaml/model/register.rb +105 -0
  68. data/lib/lutaml/model/{toml_adapter.rb → registrable.rb} +1 -1
  69. data/lib/lutaml/model/schema/base_schema.rb +64 -0
  70. data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
  71. data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
  72. data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
  73. data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
  74. data/lib/lutaml/model/schema/generator/definition.rb +53 -0
  75. data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
  76. data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
  77. data/lib/lutaml/model/schema/generator/property.rb +110 -0
  78. data/lib/lutaml/model/schema/generator/ref.rb +24 -0
  79. data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
  80. data/lib/lutaml/model/schema/json_schema.rb +42 -49
  81. data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
  82. data/lib/lutaml/model/schema/renderer.rb +36 -0
  83. data/lib/lutaml/model/schema/shared_methods.rb +24 -0
  84. data/lib/lutaml/model/schema/templates/model.erb +9 -0
  85. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
  86. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
  87. data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
  88. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
  89. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
  90. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
  91. data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
  92. data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
  93. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
  94. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
  95. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
  96. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
  97. data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
  98. data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
  99. data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
  100. data/lib/lutaml/model/schema.rb +1 -0
  101. data/lib/lutaml/model/sequence.rb +60 -30
  102. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  103. data/lib/lutaml/model/serialize.rb +296 -548
  104. data/lib/lutaml/model/services/base.rb +11 -0
  105. data/lib/lutaml/model/services/logger.rb +54 -0
  106. data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
  107. data/lib/lutaml/model/services/transformer.rb +48 -0
  108. data/lib/lutaml/model/services/type/validator/number.rb +25 -0
  109. data/lib/lutaml/model/services/type/validator/string.rb +52 -0
  110. data/lib/lutaml/model/services/type/validator.rb +43 -0
  111. data/lib/lutaml/model/services/validator.rb +145 -0
  112. data/lib/lutaml/model/services.rb +5 -0
  113. data/lib/lutaml/model/toml/document.rb +11 -0
  114. data/lib/lutaml/model/toml/mapping.rb +27 -0
  115. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  116. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  117. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  118. data/lib/lutaml/model/toml/transform.rb +8 -0
  119. data/lib/lutaml/model/toml.rb +30 -0
  120. data/lib/lutaml/model/transform/key_value_transform.rb +297 -0
  121. data/lib/lutaml/model/transform/xml_transform.rb +228 -0
  122. data/lib/lutaml/model/transform.rb +93 -0
  123. data/lib/lutaml/model/type/boolean.rb +1 -1
  124. data/lib/lutaml/model/type/date.rb +1 -1
  125. data/lib/lutaml/model/type/date_time.rb +1 -1
  126. data/lib/lutaml/model/type/decimal.rb +11 -9
  127. data/lib/lutaml/model/type/float.rb +2 -1
  128. data/lib/lutaml/model/type/integer.rb +24 -21
  129. data/lib/lutaml/model/type/string.rb +4 -2
  130. data/lib/lutaml/model/type/time.rb +1 -1
  131. data/lib/lutaml/model/type/time_without_date.rb +1 -1
  132. data/lib/lutaml/model/type/value.rb +11 -10
  133. data/lib/lutaml/model/type.rb +5 -2
  134. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  135. data/lib/lutaml/model/utils.rb +56 -4
  136. data/lib/lutaml/model/validation.rb +6 -4
  137. data/lib/lutaml/model/version.rb +1 -1
  138. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  139. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  140. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  141. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +42 -25
  142. data/lib/lutaml/model/xml/element.rb +32 -0
  143. data/lib/lutaml/model/xml/mapping.rb +471 -0
  144. data/lib/lutaml/model/xml/mapping_rule.rb +149 -0
  145. data/lib/lutaml/model/xml/nokogiri_adapter.rb +234 -0
  146. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  147. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +7 -2
  148. data/lib/lutaml/model/xml/oga_adapter.rb +172 -0
  149. data/lib/lutaml/model/xml/ox_adapter.rb +229 -0
  150. data/lib/lutaml/model/xml/transform.rb +8 -0
  151. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  152. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +9 -31
  153. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  154. data/lib/lutaml/model/xml.rb +31 -0
  155. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  156. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  157. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  158. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  159. data/lib/lutaml/model/yaml/document.rb +10 -0
  160. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  161. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  162. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  163. data/lib/lutaml/model/yaml/transform.rb +8 -0
  164. data/lib/lutaml/model/yaml.rb +21 -0
  165. data/lib/lutaml/model/yamls/document.rb +14 -0
  166. data/lib/lutaml/model/yamls/mapping.rb +19 -0
  167. data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
  168. data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
  169. data/lib/lutaml/model/yamls/transform.rb +19 -0
  170. data/lib/lutaml/model/yamls.rb +21 -0
  171. data/lib/lutaml/model.rb +15 -4
  172. data/lutaml-model.gemspec +0 -4
  173. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +5 -6
  174. data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
  175. data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
  176. data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
  177. data/spec/fixtures/xml/product_catalog.xsd +151 -0
  178. data/spec/fixtures/xml/specifications_schema.xsd +38 -0
  179. data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
  180. data/spec/lutaml/model/attribute_spec.rb +41 -44
  181. data/spec/lutaml/model/cdata_spec.rb +7 -7
  182. data/spec/lutaml/model/choice_spec.rb +44 -0
  183. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  184. data/spec/lutaml/model/custom_collection_spec.rb +830 -0
  185. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  186. data/spec/lutaml/model/global_register_spec.rb +108 -0
  187. data/spec/lutaml/model/group_spec.rb +27 -10
  188. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  189. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  190. data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
  191. data/spec/lutaml/model/jsonl_spec.rb +229 -0
  192. data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
  193. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  194. data/spec/lutaml/model/multiple_mapping_spec.rb +6 -6
  195. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  196. data/spec/lutaml/model/polymorphic_spec.rb +178 -0
  197. data/spec/lutaml/model/register/key_value_spec.rb +275 -0
  198. data/spec/lutaml/model/register/xml_spec.rb +185 -0
  199. data/spec/lutaml/model/register_spec.rb +147 -0
  200. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  201. data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
  202. data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
  203. data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
  204. data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
  205. data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
  206. data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
  207. data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
  208. data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
  209. data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
  210. data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
  211. data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
  212. data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
  213. data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
  214. data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
  215. data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
  216. data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
  217. data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
  218. data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
  219. data/spec/lutaml/model/sequence_spec.rb +36 -0
  220. data/spec/lutaml/model/serializable_spec.rb +201 -94
  221. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  222. data/spec/lutaml/model/toml_spec.rb +51 -0
  223. data/spec/lutaml/model/transformation_spec.rb +72 -15
  224. data/spec/lutaml/model/type_spec.rb +8 -4
  225. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  226. data/spec/lutaml/model/utils_spec.rb +3 -3
  227. data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
  228. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  229. data/spec/lutaml/model/xml/root_mappings/nested_child_mappings_spec.rb +217 -0
  230. data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
  231. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  232. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  233. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  234. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +12 -12
  235. data/spec/lutaml/model/xml_adapter_spec.rb +30 -6
  236. data/spec/lutaml/model/xml_mapping_rule_spec.rb +14 -7
  237. data/spec/lutaml/model/xml_mapping_spec.rb +27 -15
  238. data/spec/lutaml/model/xml_spec.rb +63 -0
  239. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  240. data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
  241. data/spec/lutaml/model/yamls_spec.rb +294 -0
  242. data/spec/spec_helper.rb +4 -3
  243. metadata +162 -60
  244. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  245. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  246. data/lib/lutaml/model/loggable.rb +0 -15
  247. data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
  248. data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
  249. data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
  250. data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
  251. data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
  252. data/lib/lutaml/model/mapping.rb +0 -14
  253. data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
  254. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  255. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  256. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  257. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  258. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  259. data/lib/lutaml/model/yaml_adapter.rb +0 -8
@@ -0,0 +1,212 @@
1
+ module Lutaml
2
+ module Model
3
+ class Collection < Lutaml::Model::Serializable
4
+ include Enumerable
5
+
6
+ class << self
7
+ INHERITED_ATTRIBUTES = %i[
8
+ instance_type
9
+ instance_name
10
+ order_by_field
11
+ order_direction
12
+ ].freeze
13
+
14
+ def inherited(subclass)
15
+ super
16
+
17
+ INHERITED_ATTRIBUTES.each do |var|
18
+ subclass.instance_variable_set(
19
+ :"@#{var}",
20
+ instance_variable_get(:"@#{var}"),
21
+ )
22
+ end
23
+ end
24
+
25
+ attr_reader :instance_type,
26
+ :instance_name,
27
+ :order_by_field,
28
+ :order_direction
29
+
30
+ def instances(name, type, &block)
31
+ attribute(name, type, collection: true, validations: block)
32
+
33
+ @instance_type = Lutaml::Model::Attribute.cast_type!(type)
34
+ @instance_name = name
35
+
36
+ define_method(:"#{name}=") do |collection|
37
+ self.collection = collection
38
+ end
39
+ end
40
+
41
+ def ordered(by:, order: :asc)
42
+ @order_by_field = by.to_sym
43
+ @order_direction = order
44
+ end
45
+
46
+ def sort_configured?
47
+ !!@order_by_field
48
+ end
49
+
50
+ def to(format, instance, options = {})
51
+ mappings = mappings_for(format)
52
+
53
+ if mappings.no_root? && format == :xml
54
+ mappings.mappings.map do |mapping|
55
+ serialize_for_mapping(mapping, instance, format, options)
56
+ end.join("\n")
57
+ else
58
+ super(format, instance, options.merge(collection: true))
59
+ end
60
+ end
61
+
62
+ def serialize_for_mapping(mapping, instance, format, options)
63
+ options[:tag_name] = mapping.name
64
+
65
+ attr_value = instance.public_send(mapping.to)
66
+ return if attr_value.nil? || attr_value.empty?
67
+
68
+ attr_value = [attr_value] unless attr_value.is_a?(Array)
69
+ attr_value.map { |v| v.public_send(:"to_#{format}", options) }
70
+ end
71
+
72
+ def as(format, instance, options = {})
73
+ mappings = mappings_for(format)
74
+ data = super
75
+
76
+ if mappings.no_root? && format != :xml && !mappings.root_mapping
77
+ Utils.fetch_str_or_sym(data, instance_name)
78
+ else
79
+ data
80
+ end
81
+ end
82
+
83
+ def from(format, data, options = {})
84
+ mappings = mappings_for(format)
85
+ if mappings.no_root? && format == :xml
86
+ tag_name = mappings.find_by_to(instance_name).name
87
+ data = "<#{tag_name}>#{data}</#{tag_name}>"
88
+ end
89
+
90
+ super(format, data, options.merge(from_collection: true))
91
+ end
92
+
93
+ def of(format, data, options = {})
94
+ mappings = mappings_for(format)
95
+
96
+ if mappings.no_root? && format != :xml && !mappings.root_mapping
97
+ data = { mappings.find_by_to(instance_name).name => data }
98
+ end
99
+
100
+ super(format, data, options.merge(from_collection: true))
101
+ end
102
+
103
+ def apply_mappings(data, format, options = {})
104
+ super(data, format, options.merge(collection: true))
105
+ end
106
+ end
107
+
108
+ attr_reader :__register
109
+
110
+ def initialize(items = [], __register: Lutaml::Model::Config.default_register)
111
+ super()
112
+
113
+ @__register = __register
114
+ items = [items].compact unless items.is_a?(Array)
115
+
116
+ register_object = Lutaml::Model::GlobalRegister.lookup(@__register)
117
+ type = register_object.get_class(self.class.instance_type)
118
+ self.collection = items.map do |item|
119
+ if item.is_a?(type)
120
+ item
121
+ elsif type <= Lutaml::Model::Type::Value
122
+ type.cast(item)
123
+ else
124
+ type.new(item)
125
+ end
126
+ end
127
+
128
+ sort_items!
129
+ end
130
+
131
+ def to_format(format, options = {})
132
+ super(format, options.merge(collection: true))
133
+ end
134
+
135
+ def collection
136
+ instance_variable_get(:"@#{self.class.instance_name}")
137
+ end
138
+
139
+ def collection=(collection)
140
+ instance_variable_set(:"@#{self.class.instance_name}", collection)
141
+ sort_items!
142
+ end
143
+
144
+ def union(other)
145
+ self.class.new((items + other.items).uniq)
146
+ end
147
+
148
+ def intersection(other)
149
+ self.class.new(items & other.items)
150
+ end
151
+
152
+ def difference(other)
153
+ self.class.new(items - other.items)
154
+ end
155
+
156
+ def each(&block)
157
+ collection.each(&block)
158
+ end
159
+
160
+ def size
161
+ collection.size
162
+ end
163
+
164
+ def first
165
+ collection.first
166
+ end
167
+
168
+ def last
169
+ collection.last
170
+ end
171
+
172
+ def <<(item)
173
+ push(item)
174
+ end
175
+
176
+ def push(item)
177
+ collection.push(item)
178
+ sort_items!
179
+ end
180
+
181
+ def [](index)
182
+ collection[index]
183
+ end
184
+
185
+ def []=(index, value)
186
+ collection[index] = value
187
+ sort_items!
188
+ end
189
+
190
+ def empty?
191
+ collection&.empty?
192
+ end
193
+
194
+ def order_defined?
195
+ self.class.sort_configured?
196
+ end
197
+
198
+ def sort_items!
199
+ return if collection.nil?
200
+ return unless order_defined?
201
+
202
+ unless collection&.one?
203
+ field = self.class.order_by_field
204
+ direction = self.class.order_direction
205
+
206
+ collection.sort_by! { |item| item.send(field) }
207
+ collection.reverse! if direction == :desc
208
+ end
209
+ end
210
+ end
211
+ end
212
+ 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,16 +3,13 @@ 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 jsonl yaml toml hash].freeze
12
7
  KEY_VALUE_FORMATS = AVAILABLE_FORMATS - %i[xml]
13
8
 
14
9
  def configure
15
10
  yield self
11
+
12
+ self
16
13
  end
17
14
 
18
15
  # This will generate the following methods
@@ -42,55 +39,87 @@ module Lutaml
42
39
  # one of [:tomlib, :toml_rb]
43
40
  # @example
44
41
  # Lutaml::Model::Config.toml_adapter = :tomlib
42
+ #
43
+ # AVAILABLE_FORMATS.each do |adapter_name|
44
+ # define_method(:"#{adapter_name}_adapter_type=") do |type_name|
45
+ # Lutaml::Model::FormatRegistry.send(:"#{adapter_name}_adapter_type=", type_name)
46
+ # end
47
+ # end
48
+
45
49
  AVAILABLE_FORMATS.each do |adapter_name|
46
50
  define_method(:"#{adapter_name}_adapter_type=") do |type_name|
47
- adapter = "#{adapter_name}_adapter"
48
- type = "#{type_name}_adapter"
49
-
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)
63
-
64
- instance_variable_set(
65
- :"@#{adapter}",
66
- Lutaml::Model.const_get(to_class_name(adapter))
67
- .const_get(to_class_name(type)),
68
- )
51
+ adapter = adapter_name.to_s
52
+ type = normalize_type_name(type_name, adapter_name)
53
+ load_adapter_file(adapter, type)
54
+ load_moxml_adapter(type_name, adapter_name)
55
+ set_adapter_for(adapter_name, class_for(adapter, type))
69
56
  end
70
57
  end
71
58
 
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
59
+ def adapter_for(format)
60
+ @adapters[format]
79
61
  end
80
62
 
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
63
+ def set_adapter_for(format, adapter)
64
+ @adapters ||= {}
65
+ @adapters[format] = adapter
66
+ end
67
+
68
+ def mappings_class_for(format)
69
+ Lutaml::Model::FormatRegistry.mappings_class_for(format)
70
+ end
71
+
72
+ def transformer_for(format)
73
+ Lutaml::Model::FormatRegistry.transformer_for(format)
74
+ end
75
+
76
+ def class_for(adapter, type)
77
+ # TODO: Remove once HashAdapter namespace is renamed to Hash
78
+ adapter = "hash_adapter" if adapter == "hash"
79
+
80
+ Lutaml::Model.const_get(to_class_name(adapter))
81
+ .const_get(to_class_name(type))
82
+ end
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
88
95
  end
89
96
 
90
97
  # @api private
91
98
  def to_class_name(str)
92
99
  str.to_s.split("_").map(&:capitalize).join
93
100
  end
101
+
102
+ private
103
+
104
+ def normalize_type_name(type_name, adapter_name)
105
+ "#{type_name.to_s.gsub("_#{adapter_name}", '')}_adapter"
106
+ end
107
+
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
+
112
+ adapter_file = File.join(adapter, type)
113
+ require_relative adapter_file
114
+ rescue LoadError
115
+ raise UnknownAdapterTypeError.new(adapter, type), cause: nil
116
+ end
117
+
118
+ def load_moxml_adapter(type_name, adapter_name)
119
+ return if KEY_VALUE_FORMATS.include?(adapter_name)
120
+
121
+ Moxml::Adapter.load(type_name)
122
+ end
94
123
  end
95
124
  end
96
125
  end
@@ -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
@@ -1,8 +1,13 @@
1
1
  module Lutaml
2
2
  module Model
3
3
  class PolymorphicError < Error
4
- def initialize(value, options)
5
- super("#{value.class} not in #{options[:polymorphic]}")
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)
6
11
  end
7
12
  end
8
13
  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"