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
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
- require "lutaml/model/toml_adapter/toml_rb_adapter"
3
- require "lutaml/model/toml_adapter/tomlib_adapter"
2
+ require "lutaml/model/toml/toml_rb_adapter"
3
+ require "lutaml/model/toml/tomlib_adapter"
4
4
  require_relative "../../fixtures/sample_model"
5
5
 
6
6
  RSpec.describe "TomlAdapter" do
@@ -9,9 +9,9 @@ RSpec.describe "TomlAdapter" do
9
9
  let(:model) { SampleModel.new(attributes) }
10
10
 
11
11
  let(:expected_toml) do
12
- if adapter_class == Lutaml::Model::TomlAdapter::TomlRbAdapter
12
+ if adapter_class == Lutaml::Model::Toml::TomlRbAdapter
13
13
  TomlRB.dump(attributes)
14
- elsif adapter_class == Lutaml::Model::TomlAdapter::TomlibAdapter
14
+ elsif adapter_class == Lutaml::Model::Toml::TomlibAdapter
15
15
  Tomlib.dump(attributes)
16
16
  end
17
17
  end
@@ -31,11 +31,11 @@ RSpec.describe "TomlAdapter" do
31
31
  end
32
32
  end
33
33
 
34
- describe Lutaml::Model::TomlAdapter::TomlRbAdapter do
34
+ describe Lutaml::Model::Toml::TomlRbAdapter do
35
35
  it_behaves_like "a TOML adapter", described_class
36
36
  end
37
37
 
38
- describe Lutaml::Model::TomlAdapter::TomlibAdapter do
38
+ describe Lutaml::Model::Toml::TomlibAdapter do
39
39
  it_behaves_like "a TOML adapter", described_class
40
40
  end
41
41
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "lutaml/model/toml"
5
+
6
+ RSpec.describe Lutaml::Model::Toml do
7
+ describe ".detect_toml_adapter" do
8
+ before do
9
+ # Hide any existing constants first
10
+ hide_const("Tomlib") if Object.const_defined?(:Tomlib)
11
+ hide_const("TomlRb") if Object.const_defined?(:TomlRb)
12
+ end
13
+
14
+ context "when Tomlib is available" do
15
+ before do
16
+ stub_const("Tomlib", Module.new)
17
+ end
18
+
19
+ it "returns :tomlib" do
20
+ expect(described_class.detect_toml_adapter).to eq(:tomlib)
21
+ end
22
+ end
23
+
24
+ context "when TomlRb is available" do
25
+ before do
26
+ stub_const("TomlRb", Module.new)
27
+ end
28
+
29
+ it "returns :toml_rb" do
30
+ expect(described_class.detect_toml_adapter).to eq(:toml_rb)
31
+ end
32
+ end
33
+
34
+ context "when neither adapter is available" do
35
+ it "returns nil" do
36
+ expect(described_class.detect_toml_adapter).to be_nil
37
+ end
38
+ end
39
+
40
+ context "when both adapters are available" do
41
+ before do
42
+ stub_const("Tomlib", Module.new)
43
+ stub_const("TomlRb", Module.new)
44
+ end
45
+
46
+ it "prefers Tomlib" do
47
+ expect(described_class.detect_toml_adapter).to eq(:tomlib)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -56,7 +56,7 @@ module TransformationSpec
56
56
  import: ->(value) { value.to_s.downcase },
57
57
  }
58
58
  attribute :email, :string, transform: {
59
- export: lambda(&:downcase),
59
+ export: lambda(&:upcase),
60
60
  import: lambda(&:downcase),
61
61
  }
62
62
  attribute :tags, :string, collection: true, transform: {
@@ -82,13 +82,36 @@ module TransformationSpec
82
82
  export: ->(value) { "Prof. #{value}" },
83
83
  import: ->(value) { value.gsub("Prof. ", "") },
84
84
  }
85
- map_element "contact-email", to: :email, transform: {
85
+ map_attribute "contact-email", to: :email, transform: {
86
86
  export: ->(value) { "contact+#{value}" },
87
87
  import: ->(value) { value.gsub("contact+", "") },
88
88
  }
89
89
  map_element "skills", to: :tags
90
90
  end
91
91
  end
92
+
93
+ class RoundTripTransformations < Lutaml::Model::Serializable
94
+ attribute :number, :string, transform: {
95
+ import: ->(value) { (value.to_f + 1).to_s },
96
+ export: ->(value) { (value.to_f - 1).to_s },
97
+ }
98
+
99
+ json do
100
+ map "number", to: :number, transform: {
101
+ import: ->(value) { ((value.to_f * 10) + 1).to_s },
102
+ export: ->(value) { ((value.to_f - 1) / 10.0).to_s },
103
+ }
104
+ end
105
+
106
+ xml do
107
+ root "RoundTripTransformations"
108
+
109
+ map_element "number", to: :number, transform: {
110
+ import: ->(value) { ((value.to_f * 10) + 1).to_s },
111
+ export: ->(value) { ((value.to_f - 1) / 10.0).to_s },
112
+ }
113
+ end
114
+ end
92
115
  end
93
116
 
94
117
  RSpec.describe "Value Transformations" do
@@ -147,6 +170,18 @@ RSpec.describe "Value Transformations" do
147
170
  )
148
171
  end
149
172
 
173
+ let(:json) do
174
+ {
175
+ "fullName" => "Dr. bob",
176
+ "emailAddress" => "bobattest.com",
177
+ "labels" => "senior|lead",
178
+ }.to_json
179
+ end
180
+
181
+ let(:parsed) do
182
+ TransformationSpec::MappingTransformPerson.from_json(json)
183
+ end
184
+
150
185
  let(:expected_xml) do
151
186
  <<~XML
152
187
  <person>
@@ -165,16 +200,15 @@ RSpec.describe "Value Transformations" do
165
200
  expect(parsed["labels"]).to eq("developer-|-architect")
166
201
  end
167
202
 
168
- it "applies mapping transformations during JSON deserialization" do
169
- json = {
170
- "fullName" => "Dr. bob",
171
- "emailAddress" => "bobattest.com",
172
- "labels" => "senior|lead",
173
- }.to_json
174
-
175
- parsed = TransformationSpec::MappingTransformPerson.from_json(json)
203
+ it "correctly deserialize name from JSON" do
176
204
  expect(parsed.name).to eq("Dr. bob")
205
+ end
206
+
207
+ it "correctly deserialize email from JSON" do
177
208
  expect(parsed.email).to eq("bob@test.com")
209
+ end
210
+
211
+ it "correctly deserialize tags from JSON" do
178
212
  expect(parsed.tags).to eq(["senior", "lead"])
179
213
  end
180
214
 
@@ -195,9 +229,8 @@ RSpec.describe "Value Transformations" do
195
229
 
196
230
  let(:expected_xml) do
197
231
  <<~XML
198
- <person>
199
- <full-name>Prof. carol</full-name>
200
- <contact-email>contact+CAROL@TEST.COM</contact-email>
232
+ <person contact-email="contact+CAROL@TEST.COM">
233
+ <full-name>Prof. Carol</full-name>
201
234
  <skills>MANAGER-1</skills>
202
235
  <skills>AGILE-1</skills>
203
236
  </person>
@@ -208,7 +241,7 @@ RSpec.describe "Value Transformations" do
208
241
  json = combined_person.to_json
209
242
  parsed = JSON.parse(json)
210
243
 
211
- expect(parsed["fullName"]).to eq("Prof. carol")
244
+ expect(parsed["fullName"]).to eq("Prof. Carol")
212
245
  expect(parsed["contactEmail"]).to eq("contact+CAROL@TEST.COM")
213
246
  end
214
247
 
@@ -217,7 +250,7 @@ RSpec.describe "Value Transformations" do
217
250
  json = combined_person.to_json
218
251
  parsed_json = TransformationSpec::CombinedTransformPerson.from_json(json)
219
252
  expect(parsed_json.name).to eq("carol")
220
- expect(parsed_json.email).to eq("CAROL@TEST.COM")
253
+ expect(parsed_json.email).to eq("carol@test.com")
221
254
  expect(parsed_json.tags).to eq(["MANAGER-1", "AGILE-1"])
222
255
  end
223
256
 
@@ -227,4 +260,28 @@ RSpec.describe "Value Transformations" do
227
260
  expect(parsed_xml.to_xml).to be_equivalent_to(expected_xml)
228
261
  end
229
262
  end
263
+
264
+ describe "Custom Transformations all mappings applied" do
265
+ let(:xml) do
266
+ <<~XML
267
+ <RoundTripTransformations>
268
+ <number>10.0</number>
269
+ </RoundTripTransformations>
270
+ XML
271
+ end
272
+
273
+ let(:json) do
274
+ { number: "10.0" }.to_json
275
+ end
276
+
277
+ it "correctly round trips XML" do
278
+ parsed = TransformationSpec::RoundTripTransformations.from_xml(xml)
279
+ expect(parsed.to_xml).to be_equivalent_to(xml)
280
+ end
281
+
282
+ it "correctly round trips JSON" do
283
+ parsed = TransformationSpec::RoundTripTransformations.from_json(json)
284
+ expect(parsed.to_json).to be_equivalent_to(json)
285
+ end
286
+ end
230
287
  end
@@ -198,7 +198,7 @@ RSpec.describe Lutaml::Model::Type do
198
198
 
199
199
  it "raises TypeNotEnabledError when using Decimal type" do
200
200
  expect do
201
- described_class.lookup(:decimal).cast("123.45")
201
+ decimal_class.cast("123.45")
202
202
  end.to raise_error(Lutaml::Model::TypeNotEnabledError)
203
203
  end
204
204
  end
@@ -342,11 +342,14 @@ RSpec.describe Lutaml::Model::Type do
342
342
  expected_xml = <<~XML
343
343
  <sample custom_type="to_xml_overrided"/>
344
344
  XML
345
- expect(sample_instance_attribute.to_xml).to be_equivalent_to(expected_xml)
345
+ expect(
346
+ sample_instance_attribute.to_xml,
347
+ ).to be_equivalent_to(expected_xml)
346
348
  end
347
349
 
348
350
  it "correctly serializes to JSON" do
349
- expect(sample_instance.to_json).to eq('{"custom_type":"to_json_overrided"}')
351
+ expected_value = '{"custom_type":"to_json_overrided"}'
352
+ expect(sample_instance.to_json).to eq(expected_value)
350
353
  end
351
354
 
352
355
  it "correctly deserializes from XML" do
@@ -354,7 +357,8 @@ RSpec.describe Lutaml::Model::Type do
354
357
  end
355
358
 
356
359
  it "correctly deserializes from JSON" do
357
- json_sample_instance = SampleModel.from_json('{"custom_type":"test_string"}')
360
+ json_input = '{"custom_type":"test_string"}'
361
+ json_sample_instance = SampleModel.from_json(json_input)
358
362
  json_sample_instance.to_json
359
363
  expect(json_sample_instance.custom_type).to eq("from_json_overrided")
360
364
  end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Lutaml::Model::UninitializedClass do
4
+ subject(:uninitialized) { described_class.instance }
5
+
6
+ describe "#to_s" do
7
+ it "returns self" do
8
+ expect(uninitialized.to_s).to eq(uninitialized)
9
+ end
10
+ end
11
+
12
+ describe "#inspect" do
13
+ it "returns 'uninitialized'" do
14
+ expect(uninitialized.inspect).to eq("uninitialized")
15
+ end
16
+ end
17
+
18
+ describe "#uninitialized?" do
19
+ it "returns true" do
20
+ expect(uninitialized.uninitialized?).to be true
21
+ end
22
+ end
23
+
24
+ describe "#match?" do
25
+ it "returns false for any argument" do
26
+ expect(uninitialized).not_to match /pattern/
27
+ expect(uninitialized).not_to match "string"
28
+ expect(uninitialized).not_to match nil
29
+ end
30
+ end
31
+
32
+ describe "#include?" do
33
+ it "returns false for any argument" do
34
+ expect(uninitialized).not_to include "substring"
35
+ expect(uninitialized).not_to include nil
36
+ end
37
+ end
38
+
39
+ describe "#gsub" do
40
+ it "returns self regardless of arguments" do
41
+ expect(uninitialized.gsub("pattern", "replacement")).to eq(uninitialized)
42
+ expect(uninitialized.gsub("pattern", "replacement")).to eq(uninitialized)
43
+ end
44
+ end
45
+
46
+ describe "#to_yaml" do
47
+ it "returns nil" do
48
+ expect(uninitialized.to_yaml).to be_nil
49
+ end
50
+ end
51
+
52
+ describe "#to_f" do
53
+ it "returns self" do
54
+ expect(uninitialized.to_f).to eq(uninitialized)
55
+ end
56
+ end
57
+
58
+ describe "#size" do
59
+ it "returns 0" do
60
+ expect(uninitialized.size).to eq(0)
61
+ end
62
+ end
63
+
64
+ describe "#encoding" do
65
+ it "returns the default string encoding" do
66
+ expect(uninitialized.encoding).to eq("".encoding)
67
+ end
68
+ end
69
+
70
+ describe "method_missing" do
71
+ context "when method ends with '?'" do
72
+ it "returns false" do
73
+ expect(uninitialized).not_to be_empty
74
+ expect(uninitialized).not_to be_nil
75
+ expect(uninitialized).not_to be_blank
76
+ end
77
+ end
78
+
79
+ context "when method doesn't end with '?'" do
80
+ it "raises NoMethodError" do
81
+ expect { uninitialized.unknown_method }.to raise_error(NoMethodError)
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#respond_to_missing?" do
87
+ it "returns true for methods ending with ?" do
88
+ expect(uninitialized.respond_to?(:empty?)).to be true
89
+ expect(uninitialized.respond_to?(:nil?)).to be true
90
+ end
91
+
92
+ it "returns false for methods not ending with ?" do
93
+ expect(uninitialized.respond_to?(:unknown_method)).to be false
94
+ end
95
+ end
96
+ end
@@ -37,9 +37,9 @@ RSpec.describe Lutaml::Model::Utils do
37
37
  "HelloWorld::FooBarBaz" => "hello_world/foo_bar_baz",
38
38
  }
39
39
 
40
- include_examples "string conversion", :camel_case, camel_case_examples
41
- include_examples "string conversion", :classify, classify_examples
42
- include_examples "string conversion", :snake_case, snake_case_examples
40
+ it_behaves_like "string conversion", :camel_case, camel_case_examples
41
+ it_behaves_like "string conversion", :classify, classify_examples
42
+ it_behaves_like "string conversion", :snake_case, snake_case_examples
43
43
 
44
44
  describe ".deep_dup" do
45
45
  let(:original_hash) do
@@ -5,7 +5,7 @@ require "spec_helper"
5
5
  module DerivedAttributesSpecs
6
6
  class Ceramic < Lutaml::Model::Serializable
7
7
  attribute :name, :string
8
- attribute :value, :float
8
+ attribute :value, register(:float)
9
9
  end
10
10
 
11
11
  class CeramicCollection < Lutaml::Model::Serializable
@@ -0,0 +1,57 @@
1
+ require "spec_helper"
2
+
3
+ module NamespaceSpec
4
+ class NestedChild < Lutaml::Model::Serializable
5
+ attribute :name, :string
6
+
7
+ xml do
8
+ root "NestedChild"
9
+
10
+ map_element :name, to: :name
11
+ end
12
+ end
13
+
14
+ class Child < Lutaml::Model::Serializable
15
+ attribute :nested_child, NestedChild
16
+
17
+ xml do
18
+ root "NestedChild"
19
+
20
+ map_element :NestedChild, to: :nested_child
21
+ end
22
+ end
23
+
24
+ class Parent < Lutaml::Model::Serializable
25
+ attribute :child, Child
26
+
27
+ xml do
28
+ root "Parent"
29
+ namespace "https://abc.com"
30
+
31
+ map_element :Child, to: :child
32
+ end
33
+ end
34
+ end
35
+
36
+ RSpec.describe "NamespaceSpec" do
37
+ let(:parsed) { NamespaceSpec::Parent.from_xml(xml) }
38
+ let(:xml) do
39
+ <<~XML
40
+ <Parent xmlns="https://abc.com">
41
+ <Child>
42
+ <NestedChild>
43
+ <name>Rogger moore</name>
44
+ </NestedChild>
45
+ </Child>
46
+ </Parent>
47
+ XML
48
+ end
49
+
50
+ it "parses nested child using root namespace" do
51
+ expect(parsed.child.nested_child.name).to eq("Rogger moore")
52
+ end
53
+
54
+ it "round-trips xml" do
55
+ expect(parsed.to_xml).to be_equivalent_to(xml)
56
+ end
57
+ end
@@ -0,0 +1,217 @@
1
+ require "spec_helper"
2
+
3
+ module NestedChildMappingsSpec
4
+ class ManifestFont < Lutaml::Model::Serializable
5
+ attribute :name, :string
6
+ attribute :styles, :string, collection: true
7
+
8
+ key_value do
9
+ map "name", to: :name
10
+ map "styles", to: :styles
11
+ end
12
+ end
13
+
14
+ class ManifestResponseFontStyle < Lutaml::Model::Serializable
15
+ attribute :full_name, :string
16
+ attribute :type, :string
17
+ attribute :paths, :string, collection: true
18
+
19
+ key_value do
20
+ map "full_name", to: :full_name
21
+ map "type", to: :type
22
+ map "paths", to: :paths
23
+ end
24
+ end
25
+
26
+ class ManifestResponseFont < ManifestFont
27
+ attribute :name, :string
28
+ attribute :styles, ManifestResponseFontStyle, collection: true
29
+
30
+ key_value do
31
+ map "name", to: :name
32
+ map "styles", to: :styles, child_mappings: {
33
+ type: :key,
34
+ full_name: :full_name,
35
+ paths: :paths,
36
+ }
37
+ end
38
+ end
39
+
40
+ class ManifestResponse < Lutaml::Model::Collection
41
+ instances :fonts, ManifestResponseFont
42
+
43
+ key_value do
44
+ map to: :fonts, root_mappings: {
45
+ name: :key,
46
+ styles: :value,
47
+ }
48
+ end
49
+ end
50
+
51
+ class ManifestResponseWrap < ManifestResponse
52
+ # This class confirms the successful inhertiance of the `Collection` attribute.
53
+ end
54
+ end
55
+
56
+ RSpec.describe "NestedChildMappingsSpec" do
57
+ let(:yaml) do
58
+ <<~YAML
59
+ ---
60
+ Yu Gothic:
61
+ Bold:
62
+ full_name: Yu Gothic Bold
63
+ paths:
64
+ - "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc"
65
+ - "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc"
66
+ - "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc"
67
+ - "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc"
68
+ - "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc"
69
+ Regular:
70
+ full_name: Yu Gothic Regular
71
+ paths:
72
+ - "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc"
73
+ - "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc"
74
+ - "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc"
75
+ - "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc"
76
+ - "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc"
77
+ Noto Sans Condensed:
78
+ Regular:
79
+ full_name: Noto Sans Condensed
80
+ paths:
81
+ - "/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"
82
+ Bold:
83
+ full_name: Noto Sans Condensed Bold
84
+ paths:
85
+ - "/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"
86
+ Bold Italic:
87
+ full_name: Noto Sans Condensed Bold Italic
88
+ paths:
89
+ - "/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"
90
+ Italic:
91
+ full_name: Noto Sans Condensed Italic
92
+ paths:
93
+ - "/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"
94
+ YAML
95
+ end
96
+
97
+ let(:wrapped_yaml) do
98
+ <<~YAML
99
+ ---
100
+ Yu Gothic:
101
+ Bold:
102
+ full_name: Yu Gothic Bold
103
+ paths:
104
+ - "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc"
105
+ - "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc"
106
+ - "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc"
107
+ - "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc"
108
+ - "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc"
109
+ Regular:
110
+ full_name: Yu Gothic Regular
111
+ paths:
112
+ - "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc"
113
+ - "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc"
114
+ - "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc"
115
+ - "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc"
116
+ - "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc"
117
+ Noto Sans Condensed:
118
+ Regular:
119
+ full_name: Noto Sans Condensed
120
+ paths:
121
+ - "/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"
122
+ Bold:
123
+ full_name: Noto Sans Condensed Bold
124
+ paths:
125
+ - "/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"
126
+ Bold Italic:
127
+ full_name: Noto Sans Condensed Bold Italic
128
+ paths:
129
+ - "/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"
130
+ Italic:
131
+ full_name: Noto Sans Condensed Italic
132
+ paths:
133
+ - "/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"
134
+ YAML
135
+ end
136
+
137
+ let(:parsed_yaml) do
138
+ NestedChildMappingsSpec::ManifestResponse.from_yaml(yaml)
139
+ end
140
+
141
+ let(:wrap_parsed_yaml) do
142
+ NestedChildMappingsSpec::ManifestResponseWrap.from_yaml(wrapped_yaml)
143
+ end
144
+
145
+ let(:expected_fonts) do
146
+ NestedChildMappingsSpec::ManifestResponse.new(
147
+ [
148
+ NestedChildMappingsSpec::ManifestResponseFont.new(
149
+ name: "Yu Gothic",
150
+ styles: [
151
+ NestedChildMappingsSpec::ManifestResponseFontStyle.new(
152
+ full_name: "Yu Gothic Bold",
153
+ type: "Bold",
154
+ paths: [
155
+ "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc",
156
+ "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc",
157
+ "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc",
158
+ "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc",
159
+ "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc",
160
+ ],
161
+ ),
162
+ NestedChildMappingsSpec::ManifestResponseFontStyle.new(
163
+ full_name: "Yu Gothic Regular",
164
+ type: "Regular",
165
+ paths: [
166
+ "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc",
167
+ "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc",
168
+ "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc",
169
+ "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc",
170
+ "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc",
171
+ ],
172
+ ),
173
+ ],
174
+ ),
175
+ NestedChildMappingsSpec::ManifestResponseFont.new(
176
+ name: "Noto Sans Condensed",
177
+ styles: [
178
+ NestedChildMappingsSpec::ManifestResponseFontStyle.new(
179
+ full_name: "Noto Sans Condensed",
180
+ type: "Regular",
181
+ paths: ["/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"],
182
+ ),
183
+ NestedChildMappingsSpec::ManifestResponseFontStyle.new(
184
+ full_name: "Noto Sans Condensed Bold",
185
+ type: "Bold",
186
+ paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"],
187
+ ),
188
+ NestedChildMappingsSpec::ManifestResponseFontStyle.new(
189
+ full_name: "Noto Sans Condensed Bold Italic",
190
+ type: "Bold Italic",
191
+ paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"],
192
+ ),
193
+ NestedChildMappingsSpec::ManifestResponseFontStyle.new(
194
+ full_name: "Noto Sans Condensed Italic",
195
+ type: "Italic",
196
+ paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"],
197
+ ),
198
+ ],
199
+ ),
200
+ ],
201
+ )
202
+ end
203
+
204
+ it "parses nested child mappings correctly" do
205
+ expect(parsed_yaml).to eq(expected_fonts)
206
+ end
207
+
208
+ it "rounds trip correctly" do
209
+ expected_yaml = parsed_yaml.to_yaml
210
+ expect(expected_yaml).to eq(yaml)
211
+ end
212
+
213
+ it "round trips nested child mappings correctly with Wrap class" do
214
+ expected_yaml = wrap_parsed_yaml.to_yaml
215
+ expect(expected_yaml).to eq(yaml)
216
+ end
217
+ end
@@ -88,6 +88,12 @@ RSpec.describe "XmlElement" do
88
88
  end
89
89
 
90
90
  def create_element(name, attributes: {}, children: [], text: "")
91
- Lutaml::Model::XmlAdapter::XmlElement.new(name, attributes, children, text)
91
+ Lutaml::Model::Xml::XmlElement.new(
92
+ name,
93
+ attributes,
94
+ children,
95
+ text,
96
+ name: name,
97
+ )
92
98
  end
93
99
  end
@@ -1,8 +1,8 @@
1
1
  require "spec_helper"
2
2
  require "nokogiri"
3
- require_relative "../../../../lib/lutaml/model/xml_adapter/nokogiri_adapter"
3
+ require_relative "../../../../lib/lutaml/model/xml/nokogiri_adapter"
4
4
 
5
- RSpec.describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
5
+ RSpec.describe Lutaml::Model::Xml::NokogiriAdapter do
6
6
  let(:xml_string) do
7
7
  <<-XML
8
8
  <root xmlns="http://example.com/default" xmlns:prefix="http://example.com/prefixed">