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,11 @@
1
+ module Lutaml
2
+ module Model
3
+ module Services
4
+ class Base
5
+ def self.call(*args)
6
+ new(*args).call
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,54 @@
1
+ module Lutaml
2
+ module Model
3
+ class Logger
4
+ def self.warn(message)
5
+ new.call(message, :warn)
6
+ end
7
+
8
+ # @param [String] old: The name of the deprecated class or method
9
+ # @param [String] replacement: The name of the replacement class or method
10
+ #
11
+ # Outputs a warning message that
12
+ # Usage of `old` name is deprecated will be removed in the next major
13
+ # release. Please use the `replacement`` instead.
14
+ def self.warn_future_deprecation(old:, replacement:)
15
+ warn("Usage of `#{old}` is deprecated and will be removed in the next major release. Please use `#{replacement}` instead.")
16
+ end
17
+
18
+ # @param [String] name
19
+ # @param [String] caller_file
20
+ # @param [Integer] caller_line
21
+ #
22
+ # Outputs a warning message that
23
+ # `<name>` is handled by default. No need to explicitly
24
+ # define at `<caller_file>:<caller_line>`.
25
+ def self.warn_auto_handling(name:, caller_file:, caller_line:)
26
+ warn("`#{name}` is handled by default. No need to explicitly define at `#{caller_file}:#{caller_line}`")
27
+ end
28
+
29
+ def call(message, type)
30
+ Warning.warn format_message(message, type)
31
+ end
32
+
33
+ private
34
+
35
+ def colorize(message, type)
36
+ type_color = {
37
+ error: 31, # Red: 31
38
+ success: 32, # Green: 32
39
+ warn: 33, # Yellow: 33
40
+ }
41
+
42
+ io = type == :warn ? $stderr : $stdout
43
+ return message unless io.tty?
44
+
45
+ color = type_color[type]
46
+ "\e[#{color}m#{message}\e[0m"
47
+ end
48
+
49
+ def format_message(message, type)
50
+ colorize("\n[Lutaml::Model] #{type.upcase}: #{message}\n", type)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,92 @@
1
+ require_relative "base"
2
+
3
+ module Lutaml
4
+ module Model
5
+ class RuleValueExtractor < Services::Base
6
+ def initialize(rule, doc, format, attr, register, options)
7
+ super()
8
+
9
+ @rule = rule
10
+ @doc = doc
11
+ @format = format
12
+ @attr = attr
13
+ @register = register
14
+ @options = options
15
+ end
16
+
17
+ def call
18
+ rule_names.each do |rule_name|
19
+ value = rule_value_for(rule_name)
20
+ value = transform_mapped_value(value) if should_transform_value?(value)
21
+ return value if Utils.initialized?(value)
22
+ end
23
+
24
+ uninitialized_value
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :rule, :doc, :format, :attr, :register, :options
30
+
31
+ def rule_names
32
+ rule.multiple_mappings? ? rule.name : [rule.name]
33
+ end
34
+
35
+ def rule_value_for(name)
36
+ return doc if root_or_nil?(name)
37
+ return convert_to_format(doc, format) if rule.raw_mapping?
38
+ return fetch_value(name) if Utils.string_or_symbol_key?(doc, name)
39
+ return attr.default(register) if attr&.default_set?(register)
40
+
41
+ uninitialized_value
42
+ end
43
+
44
+ def root_or_nil?(name)
45
+ name.nil? || rule.root_mapping?
46
+ end
47
+
48
+ def fetch_value(name)
49
+ Utils.fetch_str_or_sym(doc, name)
50
+ end
51
+
52
+ def transform_mapped_value(value)
53
+ value.map do |k, v|
54
+ if v.is_a?(Hash)
55
+ transform_hash_value(v, k, options)
56
+ else
57
+ transform_simple_value(k, v, options)
58
+ end
59
+ end
60
+ end
61
+
62
+ def transform_hash_value(hash_value, key, options)
63
+ hash_value.merge(
64
+ {
65
+ options[:key_mappings].to_instance.to_s => key,
66
+ },
67
+ )
68
+ end
69
+
70
+ def transform_simple_value(key, value, options)
71
+ {
72
+ options[:key_mappings].to_instance.to_s => key,
73
+ options[:value_mappings].as_attribute.to_s => value,
74
+ }
75
+ end
76
+
77
+ def should_transform_value?(value)
78
+ (options[:key_mappings] || options[:value_mappings]) &&
79
+ value.is_a?(Hash)
80
+ end
81
+
82
+ def convert_to_format(doc, format)
83
+ adapter = Lutaml::Model::Config.adapter_for(format)
84
+ adapter.new(doc).public_send(:"to_#{format}")
85
+ end
86
+
87
+ def uninitialized_value
88
+ Lutaml::Model::UninitializedClass.instance
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,48 @@
1
+ module Lutaml
2
+ module Model
3
+ class Transformer
4
+ class << self
5
+ def call(value, rule, attribute)
6
+ new(rule, attribute).call(value)
7
+ end
8
+ end
9
+
10
+ attr_reader :rule, :attribute
11
+
12
+ def initialize(rule, attribute)
13
+ @rule = rule
14
+ @attribute = attribute
15
+ end
16
+
17
+ def call(value)
18
+ transformation_methods.reduce(value) do |transformed_value, method|
19
+ method.call(transformed_value)
20
+ end
21
+ end
22
+ end
23
+
24
+ class ImportTransformer < Transformer
25
+ # Precedene of transformations:
26
+ # 1. Rule transform
27
+ # 2. Attribute transform
28
+ def transformation_methods
29
+ [
30
+ rule.transform[:import],
31
+ attribute&.transform&.[](:import),
32
+ ].compact
33
+ end
34
+ end
35
+
36
+ class ExportTransformer < Transformer
37
+ # Precedene of transformations (reverse order):
38
+ # 1. Attribute transform
39
+ # 2. Rule transform
40
+ def transformation_methods
41
+ [
42
+ attribute&.transform&.[](:export),
43
+ rule.transform[:export],
44
+ ].compact
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Services
6
+ module Type
7
+ class Validator
8
+ class Number < Validator
9
+ module ClassMethods
10
+ def validate!(value, options)
11
+ return if Utils.blank?(options)
12
+
13
+ validate_values!(value, options[:values])
14
+ validate_min_max_bounds!(value, options)
15
+ end
16
+ end
17
+
18
+ extend ClassMethods
19
+ include ClassMethods
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,52 @@
1
+ module Lutaml
2
+ module Model
3
+ module Services
4
+ module Type
5
+ class Validator
6
+ class String < Validator
7
+ module ClassMethods
8
+ def validate!(value, options)
9
+ return if Utils.blank?(value)
10
+
11
+ validate_values!(value, options[:values])
12
+ validate_length!(value, options)
13
+ validate_pattern!(value, options)
14
+ end
15
+
16
+ def validate_length!(value, options)
17
+ min, max = options&.values_at(:min, :max)
18
+ return if min.nil? || max.nil?
19
+
20
+ validate_min_length!(value, min) if min
21
+ validate_max_length!(value, max) if max
22
+ end
23
+
24
+ def validate_pattern!(value, options)
25
+ pattern = options[:pattern]
26
+ return if Utils.blank?(pattern)
27
+ return if value.match?(pattern)
28
+
29
+ raise Lutaml::Model::Type::PatternNotMatchedError.new(value, pattern)
30
+ end
31
+
32
+ def validate_min_length!(value, min)
33
+ return if value.length >= min
34
+
35
+ raise Lutaml::Model::Type::MinLengthError.new(value, min)
36
+ end
37
+
38
+ def validate_max_length!(value, max)
39
+ return if value.length <= max
40
+
41
+ raise Lutaml::Model::Type::MaxLengthError.new(value, max)
42
+ end
43
+ end
44
+
45
+ extend ClassMethods
46
+ include ClassMethods
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,43 @@
1
+ require_relative "validator/string"
2
+ require_relative "validator/number"
3
+
4
+ module Lutaml
5
+ module Model
6
+ module Services
7
+ module Type
8
+ class Validator
9
+ module ClassMethods
10
+ def validate_values!(value, values)
11
+ return if Utils.blank?(values) || values.include?(value)
12
+
13
+ raise Lutaml::Model::Type::InvalidValueError.new(value, values)
14
+ end
15
+
16
+ def validate_min_max_bounds!(value, options)
17
+ min, max = options&.values_at(:min, :max)
18
+ return if min.nil? && max.nil?
19
+
20
+ validate_min_bound!(value, min) if min
21
+ validate_max_bound!(value, max) if max
22
+ end
23
+
24
+ def validate_min_bound!(value, min)
25
+ return if value >= min
26
+
27
+ raise Lutaml::Model::Type::MinBoundError.new(value, min)
28
+ end
29
+
30
+ def validate_max_bound!(value, max)
31
+ return if value <= max
32
+
33
+ raise Lutaml::Model::Type::MaxBoundError.new(value, max)
34
+ end
35
+ end
36
+
37
+ extend ClassMethods
38
+ include ClassMethods
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,145 @@
1
+ require_relative "base"
2
+
3
+ module Lutaml
4
+ module Model
5
+ class ValidationRule
6
+ attr_reader :attribute, :options, :custom_method
7
+
8
+ def initialize(attribute: nil, custom_method: nil, options: {})
9
+ if Utils.blank?(attribute) && Utils.blank?(custom_method)
10
+ raise ArgumentError, "Missing attribute or custom method"
11
+ end
12
+
13
+ @attribute = attribute
14
+ @options = options
15
+ @custom_method = custom_method
16
+ end
17
+
18
+ def custom?
19
+ Utils.present?(custom_method)
20
+ end
21
+
22
+ def has_options?
23
+ options.is_a?(Hash)
24
+ end
25
+ end
26
+
27
+ class Validator < Services::Base
28
+ attr_reader :value, :validations, :errors, :memoization_container
29
+
30
+ def initialize(value, validations, memoization_container)
31
+ super()
32
+
33
+ @errors = Errors.new
34
+ @value = value
35
+ @memoization_container = memoization_container
36
+
37
+ resolve_validations(validations)
38
+ end
39
+
40
+ def call
41
+ return [] if Utils.blank?(validations)
42
+
43
+ @validations.each do |validation|
44
+ if validation.custom?
45
+ public_send(validation.custom_method, value)
46
+ else
47
+ validation.options.each do |key, rule|
48
+ send(:"validate_#{key}", value, validation.attribute, rule)
49
+ end
50
+ end
51
+ end
52
+
53
+ errors.messages
54
+ end
55
+
56
+ private
57
+
58
+ def resolve_validations(validations_block)
59
+ @validations ||= []
60
+ return unless validations_block
61
+
62
+ instance_eval(&validations_block)
63
+ end
64
+
65
+ def validate(method_name)
66
+ @validations << ValidationRule.new(custom_method: method_name)
67
+ end
68
+
69
+ def validates(attribute, options)
70
+ @validations <<
71
+ ValidationRule.new(attribute: attribute, options: options)
72
+ end
73
+
74
+ def validate_presence(model, attr, rule)
75
+ return if rule.nil?
76
+
77
+ if model.is_a?(Array)
78
+ model.each { |v| validate_presence(v, attr, rule) }
79
+ return
80
+ end
81
+
82
+ value = model.public_send(attr)
83
+ return if Utils.present?(value)
84
+
85
+ errors.add(attr, "`#{attr}` is required")
86
+ end
87
+
88
+ def validate_numericality(model, attr, rule)
89
+ return if rule.nil?
90
+
91
+ if model.is_a?(Array)
92
+ model.each { |v| validate_numericality(v, attr, rule) }
93
+ return
94
+ end
95
+
96
+ value = model.public_send(attr)
97
+ return unless validate_integer(attr, value)
98
+
99
+ validate_comparison_rules(attr, value, rule)
100
+ end
101
+
102
+ def validate_integer(attr, value)
103
+ return true if value.is_a?(Integer)
104
+
105
+ errors.add(
106
+ attr,
107
+ "`#{attr}` value is `#{value.class}`, but expected integer",
108
+ )
109
+ end
110
+
111
+ def validate_comparison_rules(attr, value, options)
112
+ validate_less_than(attr, value, options[:less_than])
113
+ validate_greater_than(attr, value, options[:greater_than])
114
+ validate_equal_to(attr, value, options[:equal_to])
115
+ end
116
+
117
+ def validate_less_than(attr, value, limit)
118
+ return if !limit || value < limit
119
+
120
+ errors.add(
121
+ attr,
122
+ "#{attr} value is `#{value}`, which is not less than #{limit}",
123
+ )
124
+ end
125
+
126
+ def validate_greater_than(attr, value, limit)
127
+ return if !limit || value > limit
128
+
129
+ errors.add(
130
+ attr,
131
+ "#{attr} value is `#{value}`, which is not greater than #{limit}",
132
+ )
133
+ end
134
+
135
+ def validate_equal_to(attr, value, target)
136
+ return if !target || value == target
137
+
138
+ errors.add(
139
+ attr,
140
+ "#{attr} value is `#{value}`, which is not equal to #{target}",
141
+ )
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "services/logger"
2
+ require_relative "services/rule_value_extractor"
3
+ require_relative "services/transformer"
4
+ require_relative "services/validator"
5
+ require_relative "services/type/validator"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ # Base class for TOML documents
7
+ class Document < Lutaml::Model::KeyValueDocument
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "../mapping/key_value_mapping"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ class Mapping < Lutaml::Model::KeyValueMapping
7
+ def initialize
8
+ super(:toml)
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
+
17
+ def validate!(key, to, with, render_nil, render_empty)
18
+ super
19
+
20
+ if [true, :as_nil].include?(render_nil) || render_empty == :as_nil
21
+ raise IncorrectMappingArgumentsError, "nil values are not supported in toml format"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "../mapping/key_value_mapping_rule"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ class MappingRule < Lutaml::Model::KeyValueMappingRule; end
7
+ end
8
+ end
9
+ end
@@ -1,10 +1,10 @@
1
1
  require "toml-rb"
2
- require_relative "toml_document"
2
+ require_relative "document"
3
3
 
4
4
  module Lutaml
5
5
  module Model
6
- module TomlAdapter
7
- class TomlRbAdapter < TomlDocument
6
+ module Toml
7
+ class TomlRbAdapter < Document
8
8
  def self.parse(toml, _options = {})
9
9
  TomlRB.parse(toml)
10
10
  end
@@ -0,0 +1,19 @@
1
+ require "tomlib"
2
+ require_relative "document"
3
+
4
+ module Lutaml
5
+ module Model
6
+ module Toml
7
+ class TomlibAdapter < Document
8
+ def self.parse(toml, _options = {})
9
+ Tomlib.load(toml)
10
+ end
11
+
12
+ def to_toml(*)
13
+ Tomlib.dump(to_h)
14
+ # Tomlib::Generator.new(to_h).toml_str
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Lutaml
2
+ module Model
3
+ module Toml
4
+ class Transform < Lutaml::Model::KeyValueTransform
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Toml
6
+ def self.detect_toml_adapter
7
+ return :tomlib if Object.const_defined?(:Tomlib)
8
+ return :toml_rb if Object.const_defined?(:TomlRb)
9
+
10
+ nil
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ require_relative "toml/document"
17
+ require_relative "toml/mapping"
18
+ require_relative "toml/mapping_rule"
19
+ require_relative "toml/transform"
20
+
21
+ Lutaml::Model::FormatRegistry.register(
22
+ :toml,
23
+ mapping_class: Lutaml::Model::Toml::Mapping,
24
+ adapter_class: nil,
25
+ transformer: Lutaml::Model::Toml::Transform,
26
+ )
27
+
28
+ if (adapter = Lutaml::Model::Toml.detect_toml_adapter)
29
+ Lutaml::Model::Config.toml_adapter_type = adapter
30
+ end