lutaml-model 0.8.17 → 0.8.18

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/downstream-performance.yml +27 -1
  3. data/.github/workflows/js-pr-check.yml +102 -0
  4. data/.github/workflows/js-sync.yml +51 -0
  5. data/.github/workflows/opal.yml +26 -6
  6. data/.github/workflows/performance.yml +32 -7
  7. data/.github/workflows/rake.yml +78 -32
  8. data/.gitmodules +6 -0
  9. data/.rubocop_todo.yml +8 -0
  10. data/Gemfile +16 -1
  11. data/README.adoc +267 -0
  12. data/Rakefile +166 -4
  13. data/docs/_guides/schema-import.adoc +42 -0
  14. data/docs/_guides/xml-mapping.adoc +21 -0
  15. data/docs/_pages/validation.adoc +65 -0
  16. data/lib/compat/opal/generate_boot.rb +123 -0
  17. data/lib/compat/opal/js_bundle_entry.rb +42 -0
  18. data/lib/compat/opal/lutaml_model_boot.rb +497 -0
  19. data/lib/compat/opal/moxml_boot.rb +68 -0
  20. data/lib/compat/opal/yaml_compat.rb +32 -0
  21. data/lib/lutaml/json.rb +1 -1
  22. data/lib/lutaml/key_value/transform.rb +3 -8
  23. data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
  24. data/lib/lutaml/key_value/transformation.rb +17 -5
  25. data/lib/lutaml/model/adapter_resolver.rb +2 -2
  26. data/lib/lutaml/model/attribute.rb +88 -11
  27. data/lib/lutaml/model/cached_type_resolver.rb +1 -1
  28. data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
  29. data/lib/lutaml/model/global_context.rb +11 -8
  30. data/lib/lutaml/model/instrumentation.rb +2 -2
  31. data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
  32. data/lib/lutaml/model/register.rb +2 -2
  33. data/lib/lutaml/model/runtime_compatibility.rb +50 -0
  34. data/lib/lutaml/model/schema/class_loader.rb +58 -0
  35. data/lib/lutaml/model/schema/compiled_output.rb +83 -0
  36. data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
  37. data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
  38. data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
  39. data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
  40. data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
  41. data/lib/lutaml/model/schema/definitions/model.rb +40 -0
  42. data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
  43. data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
  44. data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
  45. data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
  46. data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
  47. data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
  48. data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
  49. data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
  50. data/lib/lutaml/model/schema/definitions.rb +24 -0
  51. data/lib/lutaml/model/schema/file_writer.rb +70 -0
  52. data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
  53. data/lib/lutaml/model/schema/generator/property.rb +21 -4
  54. data/lib/lutaml/model/schema/module_nesting.rb +31 -0
  55. data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
  56. data/lib/lutaml/model/schema/registry_generator.rb +115 -0
  57. data/lib/lutaml/model/schema/renderers/base.rb +40 -0
  58. data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
  59. data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
  60. data/lib/lutaml/model/schema/renderers/model.rb +121 -0
  61. data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
  62. data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
  63. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
  64. data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
  65. data/lib/lutaml/model/schema/renderers/union.rb +83 -0
  66. data/lib/lutaml/model/schema/renderers.rb +19 -0
  67. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
  68. data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
  69. data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
  70. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
  71. data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
  72. data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
  73. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
  74. data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
  75. data/lib/lutaml/model/schema/templates.rb +162 -0
  76. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
  77. data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
  78. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
  79. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
  80. data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
  81. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
  82. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
  83. data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
  84. data/lib/lutaml/model/schema.rb +19 -0
  85. data/lib/lutaml/model/serialize/attribute_definition.rb +14 -2
  86. data/lib/lutaml/model/serialize/builder.rb +15 -2
  87. data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
  88. data/lib/lutaml/model/serialize.rb +6 -9
  89. data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
  90. data/lib/lutaml/model/services.rb +0 -2
  91. data/lib/lutaml/model/store.rb +2 -1
  92. data/lib/lutaml/model/toml.rb +1 -1
  93. data/lib/lutaml/model/transform.rb +17 -39
  94. data/lib/lutaml/model/transformation_registry.rb +1 -1
  95. data/lib/lutaml/model/type.rb +1 -0
  96. data/lib/lutaml/model/union.rb +320 -0
  97. data/lib/lutaml/model/utils.rb +6 -0
  98. data/lib/lutaml/model/validation.rb +1 -4
  99. data/lib/lutaml/model/version.rb +1 -1
  100. data/lib/lutaml/model.rb +18 -0
  101. data/lib/lutaml/toml.rb +1 -1
  102. data/lib/lutaml/xml/adapter.rb +1 -1
  103. data/lib/lutaml/xml/adapter_loader.rb +1 -1
  104. data/lib/lutaml/xml/builder/base.rb +1 -1
  105. data/lib/lutaml/xml/builder.rb +1 -1
  106. data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
  107. data/lib/lutaml/xml/model_transform.rb +5 -1
  108. data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
  109. data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
  110. data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
  111. data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
  112. data/lib/lutaml/xml/transformation/element_builder.rb +21 -8
  113. data/lib/lutaml/xml/transformation/ordered_applier.rb +6 -0
  114. data/lib/lutaml/xml/xsd_validator.rb +66 -0
  115. data/lib/lutaml/xml.rb +53 -12
  116. data/lutaml-model.gemspec +4 -2
  117. data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
  118. data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
  119. data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
  120. data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
  121. data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
  122. data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
  123. data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
  124. data/spec/fixtures/xml/schema/rng/book.rng +27 -0
  125. data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
  126. data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
  127. data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
  128. data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
  129. data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
  130. data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
  131. data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
  132. data/spec/fixtures/xml/schema/rng/person.rng +23 -0
  133. data/spec/fixtures/xml/schema/rng/union.rng +14 -0
  134. data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
  135. data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
  136. data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
  137. data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
  138. data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
  139. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
  140. data/spec/lutaml/model/attribute_spec.rb +169 -12
  141. data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
  142. data/spec/lutaml/model/collection_spec.rb +18 -0
  143. data/spec/lutaml/model/mixed_content_spec.rb +171 -0
  144. data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
  145. data/spec/lutaml/model/ordered_content_spec.rb +30 -0
  146. data/spec/lutaml/model/raw_element_spec.rb +125 -0
  147. data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
  148. data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
  149. data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
  150. data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
  151. data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
  152. data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
  153. data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
  154. data/spec/lutaml/model/union_attribute_spec.rb +658 -0
  155. data/spec/lutaml/model/union_spec.rb +257 -0
  156. data/spec/lutaml/model/utils_spec.rb +14 -0
  157. data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
  158. data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
  159. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
  160. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
  161. data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
  162. data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
  163. data/spec/spec_helper.rb +28 -11
  164. data/spec/support/opal.rb +5 -2
  165. metadata +97 -31
  166. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
  167. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
  168. data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
  169. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
  170. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
  171. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
  172. data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
  173. data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
  174. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
  175. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
  176. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
  177. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
  178. data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
  179. data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
  180. data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
  181. data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
  182. data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
  183. data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
  184. data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
  185. data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
  186. data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
  187. data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
  188. data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
  189. data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
  190. data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
  191. data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
  192. data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
@@ -1,265 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lutaml
4
- module Model
5
- module Schema
6
- module XmlCompiler
7
- class SimpleType
8
- attr_accessor :class_name, :base_class, :instance, :unions
9
-
10
- LUTAML_VALUE_CLASS_NAME = "Lutaml::Model::Type::Value"
11
-
12
- SUPPORTED_DATA_TYPES = {
13
- nonNegativeInteger: { skippable: false,
14
- class_name: "Lutaml::Model::Type::String", validations: { pattern: /\+?[0-9]+/ } },
15
- normalizedString: { skippable: false,
16
- class_name: "Lutaml::Model::Type::String", validations: { transform: "value.gsub(/[\\r\\n\\t]/, ' ')" } },
17
- positiveInteger: { skippable: false,
18
- class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0 } },
19
- unsignedShort: { skippable: false,
20
- class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 65535 } },
21
- base64Binary: { skippable: false,
22
- class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A([A-Za-z0-9+\/]+={0,2}|\s)*\z/ } },
23
- unsignedLong: { skippable: false,
24
- class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 18446744073709551615 } },
25
- unsignedByte: { skippable: false,
26
- class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 255 } },
27
- unsignedInt: { skippable: false,
28
- class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 4294967295 } },
29
- hexBinary: { skippable: false,
30
- class_name: "Lutaml::Model::Type::String", validations: { pattern: /([0-9a-fA-F]{2})*/ } },
31
- language: { skippable: false,
32
- class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\z/ } },
33
- dateTime: { skippable: true,
34
- class_name: "Lutaml::Model::Type::DateTime" },
35
- boolean: { skippable: true,
36
- class_name: "Lutaml::Model::Type::Boolean" },
37
- integer: { skippable: true,
38
- class_name: "Lutaml::Model::Type::Integer" },
39
- decimal: { skippable: true,
40
- class_name: "Lutaml::Model::Type::Decimal" },
41
- string: { skippable: true,
42
- class_name: "Lutaml::Model::Type::String" },
43
- double: { skippable: true,
44
- class_name: "Lutaml::Model::Type::Float" },
45
- NCName: { skippable: false,
46
- class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A[a-zA-Z_][\w.-]*\z/ } },
47
- anyURI: { skippable: false,
48
- class_name: "Lutaml::Model::Type::String", validations: { pattern: "\\A\#{URI::DEFAULT_PARSER.make_regexp(%w[http https ftp])}\\z" } },
49
- token: { skippable: false,
50
- class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A[^\t\n\f\r ]+(?: [^\t\n\f\r ]+)*\z/ } },
51
- byte: { skippable: false,
52
- class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: -128, max_inclusive: 127 } },
53
- long: { skippable: false,
54
- class_name: "Lutaml::Model::Type::Decimal" },
55
- int: { skippable: true,
56
- class_name: "Lutaml::Model::Type::Integer" },
57
- id: { skippable: false, class_name: "Lutaml::Model::Type::String",
58
- validations: { pattern: /\A[a-zA-Z_][\w.-]*\z/ } },
59
- }.freeze
60
-
61
- INSTANCE_MODEL_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
62
- # frozen_string_literal: true
63
- require "lutaml/model"
64
-
65
- <%= "\#{required_files}\n" -%>
66
- <%= module_opening -%>
67
- class <%= klass_name %><%= " < \#{parent_class}" if parent_class %>
68
- <%= @indent %>def self.cast(value, options = {})
69
- <%= extended_indent %>return if value.nil?
70
-
71
- <%= instance&.to_method_body(extended_indent) %>
72
- value = super(value, options)
73
- value
74
- end
75
- <%= registration_methods -%>
76
- end
77
- <%= module_closing -%>
78
- <%= registration_execution -%>
79
- TEMPLATE
80
-
81
- UNION_MODEL_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
82
- # frozen_string_literal: true
83
- require "lutaml/model"
84
- <%= union_required_files %>
85
-
86
- <%= module_opening -%>
87
- class <%= klass_name %> < <%= LUTAML_VALUE_CLASS_NAME %>
88
- <%= @indent %>def self.cast(value, options = {})
89
- <%= extended_indent %>return if value.nil?
90
-
91
- <%= union_class_method_body %>
92
- end
93
- <%= registration_methods -%>
94
- end
95
- <%= module_closing -%>
96
- <%= registration_execution -%>
97
- TEMPLATE
98
-
99
- def initialize(name, unions = [])
100
- @class_name = name
101
- @unions = unions
102
- @module_namespace = nil
103
- end
104
-
105
- def to_class(options: {})
106
- setup_options(options)
107
- template = unions&.any? ? UNION_MODEL_TEMPLATE : INSTANCE_MODEL_TEMPLATE
108
- template.result(binding)
109
- end
110
-
111
- def required_files
112
- files = Array(instance&.required_files)
113
- # Don't add requires for parent classes if using module namespace
114
- # They're handled via central autoload registry
115
- if !@module_namespace && require_parent?
116
- files << "require_relative \"#{Utils.snake_case(parent_class)}\""
117
- end
118
- # Filter out require_relative when using autoload, keep external requires
119
- if @module_namespace
120
- files = files.select { |f| f.start_with?("require \"") }
121
- end
122
- files.join("\n")
123
- end
124
-
125
- private
126
-
127
- def setup_options(options)
128
- @indent = " " * options&.fetch(:indent, 2)
129
- @extended_indent = @indent * 2
130
- @module_namespace = options[:module_namespace]
131
- @modules = @module_namespace&.split("::") || []
132
- @register_id = options[:register_id]
133
- end
134
-
135
- def module_opening
136
- return "" if @modules.empty?
137
-
138
- @modules.map.with_index do |mod, i|
139
- "#{' ' * i}module #{mod}"
140
- end.join("\n") + "\n"
141
- end
142
-
143
- def module_closing
144
- return "" if @modules.empty?
145
-
146
- @modules.reverse.map.with_index do |_mod, i|
147
- "#{' ' * (@modules.size - i - 1)}end"
148
- end.join("\n")
149
- end
150
-
151
- def registration_methods
152
- if @module_namespace
153
- # Still need register method for union types that call register.get_class
154
- <<~REGISTRATION
155
-
156
- #{@indent}def self.register
157
- #{extended_indent}Lutaml::Model::Config.default_register
158
- #{@indent}end
159
- REGISTRATION
160
- else
161
- <<~REGISTRATION
162
-
163
- #{@indent}def self.register
164
- #{extended_indent}@register ||= Lutaml::Model::Config.default_register
165
- #{@indent}end
166
-
167
- #{@indent}def self.register_class_with_id
168
- #{extended_indent}context = Lutaml::Model::GlobalContext.context(Lutaml::Model::Config.default_register)
169
- #{extended_indent}context.registry.register(:#{Utils.snake_case(class_name)}, self)
170
- #{@indent}end
171
- REGISTRATION
172
- end
173
- end
174
-
175
- def registration_execution
176
- return "" if @module_namespace
177
-
178
- "\n#{klass_name}.register_class_with_id"
179
- end
180
-
181
- def klass_name
182
- @klass_name ||= Utils.camel_case(class_name)
183
- end
184
-
185
- def require_parent?
186
- return false if Utils.blank?(base_class)
187
-
188
- !!!SUPPORTED_DATA_TYPES[base_class&.to_sym]&.dig(:skippable)
189
- end
190
-
191
- def parent_class
192
- type_info = SUPPORTED_DATA_TYPES[base_class&.to_sym]
193
- return type_info[:class_name] if type_info&.dig(:skippable)
194
- return Utils.camel_case(base_class.to_s) if !!!type_info&.dig(:skippable) && Utils.present?(base_class)
195
-
196
- LUTAML_VALUE_CLASS_NAME
197
- end
198
-
199
- def union_class_method_body
200
- unions.map do |union|
201
- "#{extended_indent}Lutaml::Model::GlobalContext.resolve_type(:#{down_union_class_name(union)}, @register).cast(value, options)"
202
- end.join(" ||\n ")
203
- end
204
-
205
- def union_required_files
206
- # Don't add requires for union classes if using module namespace
207
- # They're handled via central autoload registry
208
- return "" if @module_namespace
209
-
210
- unions.filter_map do |union|
211
- next if SUPPORTED_DATA_TYPES.dig(last_of_split(union).to_sym,
212
- :skippable)
213
-
214
- "require_relative \"#{down_union_class_name(union)}\""
215
- end.join("\n")
216
- end
217
-
218
- def down_union_class_name(union)
219
- Utils.snake_case(last_of_split(union))
220
- end
221
-
222
- def last_of_split(field)
223
- field&.split(":")&.last
224
- end
225
-
226
- def extended_indent
227
- @extended_indent || " "
228
- end
229
-
230
- class << self
231
- def setup_supported_types
232
- SUPPORTED_DATA_TYPES.reject do |_, simple_type|
233
- simple_type[:skippable]
234
- end.each_with_object({}) do |(name, simple_type), hash|
235
- str_name = name.to_s
236
- new(str_name).tap do |instance|
237
- instance.base_class = Utils.base_class_snake_case(simple_type[:class_name])
238
- instance.instance = setup_restriction(instance.base_class,
239
- simple_type[:validations])
240
- hash[str_name] = instance
241
- end
242
- end
243
- end
244
-
245
- def setup_restriction(base_class, validations)
246
- return unless validations
247
-
248
- Restriction.new.tap do |restriction|
249
- restriction.base_class = base_class
250
- restriction.min_inclusive = validations[:min_inclusive]
251
- restriction.max_inclusive = validations[:max_inclusive]
252
- restriction.pattern = validations[:pattern]
253
- restriction.transform = validations[:transform]
254
- end
255
- end
256
-
257
- def skippable?(type)
258
- SUPPORTED_DATA_TYPES.dig(type&.to_sym, :skippable)
259
- end
260
- end
261
- end
262
- end
263
- end
264
- end
265
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lutaml
4
- module Model
5
- module Schema
6
- module XmlCompiler
7
- # Generates XmlNamespace class definitions from XSD namespace URIs
8
- class XmlNamespaceClass
9
- attr_accessor :uri, :prefix_default, :class_name
10
-
11
- TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
12
- # frozen_string_literal: true
13
-
14
- require "lutaml/model"
15
-
16
- <%= module_opening -%>
17
- # Namespace: <%= uri %>
18
- class <%= class_name %> < Lutaml::Xml::W3c::XmlNamespace
19
- uri <%= uri.inspect %>
20
- prefix_default <%= prefix_default.inspect %>
21
- end
22
- <%= module_closing -%>
23
- TEMPLATE
24
-
25
- def initialize(uri:, prefix: nil, class_name: nil)
26
- @uri = uri
27
- @prefix_default = prefix || derive_prefix_from_uri(uri)
28
- @class_name = class_name || derive_class_name_from_uri(uri)
29
- @module_namespace = nil
30
- end
31
-
32
- def to_class(options: {})
33
- @module_namespace = options[:module_namespace]
34
- @modules = @module_namespace&.split("::") || []
35
- TEMPLATE.result(binding)
36
- end
37
-
38
- def required_file
39
- "require_relative \"#{Utils.snake_case(class_name)}\""
40
- end
41
-
42
- def class_reference
43
- class_name
44
- end
45
-
46
- private
47
-
48
- def module_opening
49
- return "" if @modules.empty?
50
-
51
- @modules.map.with_index do |mod, i|
52
- "#{' ' * i}module #{mod}"
53
- end.join("\n") + "\n"
54
- end
55
-
56
- def module_closing
57
- return "" if @modules.empty?
58
-
59
- @modules.reverse.map.with_index do |_mod, i|
60
- "#{' ' * (@modules.size - i - 1)}end"
61
- end.join("\n")
62
- end
63
-
64
- def derive_prefix_from_uri(uri)
65
- # Extract meaningful part from URI
66
- # http://schemas.openxmlformats.org/officeDocument/2006/math -> "m"
67
- # http://www.w3.org/2001/XMLSchema -> "xs"
68
- case uri
69
- when %r{/math$}
70
- "m"
71
- when %r{XMLSchema}
72
- "xs"
73
- when %r{/(\w+)$}
74
- $1[0..2] # First 3 chars
75
- else
76
- "ns" # Generic prefix
77
- end
78
- end
79
-
80
- def derive_class_name_from_uri(uri)
81
- # Extract domain and path components
82
- # http://schemas.openxmlformats.org/officeDocument/2006/math
83
- # -> OoxmlMathNamespace
84
- return "XmlSchemaNamespace" if uri.include?("XMLSchema")
85
-
86
- parsed_uri = URI.parse(uri)
87
- parts = parsed_uri.host&.split(".")&.reject(&:empty?) || []
88
- path_parts = parsed_uri.path&.split("/")&.reject(&:empty?) || []
89
-
90
- # Use reverse domain notation + meaningful path parts
91
- # Filter out version numbers and empty parts
92
- name_parts = parts.reverse.take(2) + path_parts.last(2)
93
- name_parts = name_parts.map do |p|
94
- Utils.camel_case(p.gsub(/\d+/, "").gsub(/[^a-zA-Z]/, ""))
95
- end
96
- .reject(&:empty?)
97
-
98
- # Fallback if no valid parts
99
- return "DefaultNamespace" if name_parts.empty?
100
-
101
- "#{name_parts.join}Namespace"
102
- rescue URI::InvalidURIError, StandardError
103
- # Fallback for invalid URIs or any other errors
104
- "DefaultNamespace"
105
- end
106
- end
107
- end
108
- end
109
- end
110
- end
@@ -1,60 +0,0 @@
1
- require_relative "base"
2
-
3
- module Lutaml
4
- module Model
5
- module Services
6
- class DefaultValueResolver < Base
7
- def initialize(attribute, register, instance_object)
8
- super()
9
-
10
- @attribute = attribute
11
- @register = register
12
- @instance_object = instance_object
13
- end
14
-
15
- # Get the default value with proper instance context and casting
16
- # Returns the evaluated and casted default value
17
- def default
18
- attribute.cast_value(default_value, register)
19
- end
20
-
21
- # Get the default value with proper instance context
22
- # Returns the evaluated default value (procs are executed)
23
- def default_value
24
- raw_value = raw_default_value
25
- return raw_value unless raw_value.is_a?(Proc)
26
-
27
- # Execute proc in instance context if available, otherwise call it directly
28
- if instance_object
29
- instance_object.instance_exec(&raw_value)
30
- else
31
- raw_value.call
32
- end
33
- end
34
-
35
- # Get the raw default value without executing procs
36
- def raw_default_value
37
- if attribute.delegate
38
- # For delegated attributes, recursively resolve through the service
39
- delegated_attr = attribute.type(register).attributes[attribute.to]
40
- self.class.new(delegated_attr, register,
41
- instance_object).raw_default_value
42
- elsif attribute.options.key?(:default)
43
- attribute.options[:default]
44
- else
45
- Lutaml::Model::UninitializedClass.instance
46
- end
47
- end
48
-
49
- # Check if a default value is set (not uninitialized)
50
- def default_set?
51
- !Utils.uninitialized?(raw_default_value)
52
- end
53
-
54
- private
55
-
56
- attr_reader :attribute, :register, :instance_object
57
- end
58
- end
59
- end
60
- end
@@ -1,162 +0,0 @@
1
- require "spec_helper"
2
-
3
- class TestModel < Lutaml::Model::Serializable
4
- attribute :_class, :string, default: -> { self.class.name }
5
- end
6
-
7
- RSpec.describe Lutaml::Model::Services::DefaultValueResolver do
8
- let(:register) { Lutaml::Model::Config.default_register }
9
- let(:instance) { nil }
10
-
11
- describe "#default_value" do
12
- context "when default is a static value" do
13
- let(:attribute) do
14
- Lutaml::Model::Attribute.new("name", :string, default: "John")
15
- end
16
-
17
- it "returns the static value" do
18
- resolver = described_class.new(attribute, register, instance)
19
- expect(resolver.default_value).to eq("John")
20
- end
21
- end
22
-
23
- context "when default is a proc without instance context" do
24
- let(:attribute) do
25
- Lutaml::Model::Attribute.new("count", :integer, default: -> { 42 })
26
- end
27
-
28
- it "executes the proc" do
29
- resolver = described_class.new(attribute, register, instance)
30
- expect(resolver.default_value).to eq(42)
31
- end
32
- end
33
-
34
- context "when default is a proc with instance context" do
35
- let(:model) { TestModel.new }
36
- let(:attribute) { TestModel.attributes[:_class] }
37
-
38
- it "executes the proc in the instance context" do
39
- resolver = described_class.new(attribute, register, model)
40
- expect(resolver.default_value).to eq("TestModel")
41
- end
42
- end
43
- end
44
-
45
- describe "#default" do
46
- context "when default is a static value" do
47
- let(:attribute) do
48
- Lutaml::Model::Attribute.new("count", :integer, default: "42")
49
- end
50
-
51
- it "returns the casted value" do
52
- resolver = described_class.new(attribute, register, instance)
53
- expect(resolver.default).to eq(42)
54
- end
55
- end
56
-
57
- context "when default is a proc" do
58
- let(:attribute) do
59
- Lutaml::Model::Attribute.new("file", :string, default: -> {
60
- Pathname.new("avatar.png")
61
- })
62
- end
63
-
64
- it "returns the casted proc result" do
65
- resolver = described_class.new(attribute, register, instance)
66
- expect(resolver.default).to eq("avatar.png")
67
- end
68
- end
69
-
70
- context "when default is a proc with instance context" do
71
- let(:model) { TestModel.new }
72
- let(:attribute) { TestModel.attributes[:_class] }
73
-
74
- it "executes and casts the proc in the instance context" do
75
- resolver = described_class.new(attribute, register, model)
76
- expect(resolver.default).to eq("TestModel")
77
- end
78
- end
79
- end
80
-
81
- describe "#raw_default_value" do
82
- context "when default is not set" do
83
- let(:attribute) do
84
- Lutaml::Model::Attribute.new("name", :string)
85
- end
86
-
87
- it "returns uninitialized" do
88
- resolver = described_class.new(attribute, register, instance)
89
- expect(resolver.raw_default_value).to be(Lutaml::Model::UninitializedClass.instance)
90
- end
91
- end
92
-
93
- context "when default is a static value" do
94
- let(:attribute) do
95
- Lutaml::Model::Attribute.new("name", :string, default: "John")
96
- end
97
-
98
- it "returns the static value without execution" do
99
- resolver = described_class.new(attribute, register, instance)
100
- expect(resolver.raw_default_value).to eq("John")
101
- end
102
- end
103
-
104
- context "when default is a proc" do
105
- let(:proc_default) { -> { "John" } }
106
- let(:attribute) do
107
- Lutaml::Model::Attribute.new("name", :string, default: proc_default)
108
- end
109
-
110
- it "returns the proc without execution" do
111
- resolver = described_class.new(attribute, register, instance)
112
- expect(resolver.raw_default_value).to eq(proc_default)
113
- end
114
- end
115
-
116
- context "when default is nil" do
117
- let(:attribute) do
118
- Lutaml::Model::Attribute.new("value", :string, default: nil)
119
- end
120
-
121
- it "returns nil" do
122
- resolver = described_class.new(attribute, register, instance)
123
- expect(resolver.raw_default_value).to be_nil
124
- end
125
- end
126
- end
127
-
128
- describe "#default_set?" do
129
- context "when default is not set" do
130
- let(:attribute) do
131
- Lutaml::Model::Attribute.new("name", :string)
132
- end
133
-
134
- it "returns false" do
135
- resolver = described_class.new(attribute, register, instance)
136
- expect(resolver.default_set?).to be(false)
137
- end
138
- end
139
-
140
- context "when default is a static value" do
141
- let(:attribute) do
142
- Lutaml::Model::Attribute.new("name", :string, default: "John")
143
- end
144
-
145
- it "returns true" do
146
- resolver = described_class.new(attribute, register, instance)
147
- expect(resolver.default_set?).to be(true)
148
- end
149
- end
150
-
151
- context "when default is a proc" do
152
- let(:attribute) do
153
- Lutaml::Model::Attribute.new("name", :string, default: -> { "John" })
154
- end
155
-
156
- it "returns true" do
157
- resolver = described_class.new(attribute, register, instance)
158
- expect(resolver.default_set?).to be(true)
159
- end
160
- end
161
- end
162
- end
@@ -1,71 +0,0 @@
1
- require "spec_helper"
2
- require "support/xml/schema_helper"
3
-
4
- RSpec.describe Lutaml::Model::Schema::XmlCompiler::AttributeGroup do
5
- let(:attribute_group) { described_class.new(name: "foo", ref: nil) }
6
- let(:dummy_class) do
7
- Class.new do
8
- def to_attributes(indent); end
9
- def to_xml_mapping(indent); end
10
- def required_files; end
11
- end
12
- end
13
-
14
- describe "#initialize" do
15
- it "sets name, ref, and empty instances" do
16
- ag = described_class.new(name: "bar", ref: "baz")
17
- expect(ag.name).to eq("bar")
18
- expect(ag.ref).to eq("baz")
19
- expect(ag.instances).to eq([])
20
- end
21
- end
22
-
23
- describe "#<<" do
24
- it "adds instances to the list" do
25
- instance = instance_double(dummy_class, to_attributes: "attr",
26
- to_xml_mapping: "xml", required_files: "files")
27
- expect { attribute_group << instance }.to change {
28
- attribute_group.instances.size
29
- }.by(1)
30
- expect(attribute_group.instances).to include(instance)
31
- end
32
-
33
- it "ignores nil instances" do
34
- expect { attribute_group << nil }.not_to(change do
35
- attribute_group.instances.size
36
- end)
37
- end
38
- end
39
-
40
- describe "#to_attributes" do
41
- it "returns attributes from resolved_instances" do
42
- instance = instance_double(dummy_class, to_attributes: "attr")
43
- attribute_group.instances = [instance]
44
- expect(attribute_group.to_attributes(" ").join).to include("attr")
45
- end
46
- end
47
-
48
- describe "#to_xml_mapping" do
49
- it "returns xml mappings from resolved_instances" do
50
- instance = instance_double(dummy_class, to_xml_mapping: "xml")
51
- attribute_group.instances = [instance]
52
- expect(attribute_group.to_xml_mapping(" ").join).to include("xml")
53
- end
54
- end
55
-
56
- describe "#required_files" do
57
- it "collects required_files from resolved_instances" do
58
- instance = instance_double(dummy_class, required_files: "files")
59
- attribute_group.instances = [instance]
60
- expect(attribute_group.required_files.join).to include("files")
61
- end
62
- end
63
-
64
- describe "private methods" do
65
- it "resolved_instances returns @instances if ref is not present" do
66
- attribute_group.instances = [1, 2]
67
- attribute_group.ref = nil
68
- expect(attribute_group.send(:resolved_instances)).to eq([1, 2])
69
- end
70
- end
71
- end