lutaml-model 0.8.16 → 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 (230) 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/.github/workflows/release.yml +0 -3
  9. data/.gitmodules +6 -0
  10. data/.rubocop_todo.yml +76 -12
  11. data/Gemfile +16 -1
  12. data/README.adoc +267 -0
  13. data/Rakefile +166 -4
  14. data/docs/_guides/schema-import.adoc +42 -0
  15. data/docs/_guides/xml/namespace-semantics.adoc +2 -0
  16. data/docs/_guides/xml-mapping.adoc +21 -0
  17. data/docs/_guides/xml-namespace-qualification.adoc +142 -0
  18. data/docs/_pages/validation.adoc +65 -0
  19. data/docs/_tutorials/xml-element-attribute-namespace-guide.adoc +2 -0
  20. data/docs/_tutorials/xml-schema-primer-style-guide.adoc +2 -0
  21. data/docs/namespace-management.adoc +2 -0
  22. data/docs/xml-schema-qualification.md +6 -0
  23. data/lib/compat/opal/generate_boot.rb +123 -0
  24. data/lib/compat/opal/js_bundle_entry.rb +42 -0
  25. data/lib/compat/opal/lutaml_model_boot.rb +497 -0
  26. data/lib/compat/opal/moxml_boot.rb +68 -0
  27. data/lib/compat/opal/yaml_compat.rb +32 -0
  28. data/lib/lutaml/json.rb +1 -1
  29. data/lib/lutaml/jsonld.rb +1 -4
  30. data/lib/lutaml/key_value/transform.rb +3 -8
  31. data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
  32. data/lib/lutaml/key_value/transformation.rb +17 -5
  33. data/lib/lutaml/model/adapter_resolver.rb +2 -2
  34. data/lib/lutaml/model/attribute.rb +88 -11
  35. data/lib/lutaml/model/cached_type_resolver.rb +1 -1
  36. data/lib/lutaml/model/choice.rb +34 -0
  37. data/lib/lutaml/model/compiled_rule.rb +7 -0
  38. data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
  39. data/lib/lutaml/model/global_context.rb +11 -8
  40. data/lib/lutaml/model/instrumentation.rb +2 -2
  41. data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
  42. data/lib/lutaml/model/register.rb +2 -2
  43. data/lib/lutaml/model/runtime_compatibility.rb +50 -0
  44. data/lib/lutaml/model/schema/class_loader.rb +58 -0
  45. data/lib/lutaml/model/schema/compiled_output.rb +83 -0
  46. data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
  47. data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
  48. data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
  49. data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
  50. data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
  51. data/lib/lutaml/model/schema/definitions/model.rb +40 -0
  52. data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
  53. data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
  54. data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
  55. data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
  56. data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
  57. data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
  58. data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
  59. data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
  60. data/lib/lutaml/model/schema/definitions.rb +24 -0
  61. data/lib/lutaml/model/schema/file_writer.rb +70 -0
  62. data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
  63. data/lib/lutaml/model/schema/generator/property.rb +21 -4
  64. data/lib/lutaml/model/schema/module_nesting.rb +31 -0
  65. data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
  66. data/lib/lutaml/model/schema/registry_generator.rb +115 -0
  67. data/lib/lutaml/model/schema/renderers/base.rb +40 -0
  68. data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
  69. data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
  70. data/lib/lutaml/model/schema/renderers/model.rb +121 -0
  71. data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
  72. data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
  73. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
  74. data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
  75. data/lib/lutaml/model/schema/renderers/union.rb +83 -0
  76. data/lib/lutaml/model/schema/renderers.rb +19 -0
  77. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
  78. data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
  79. data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
  80. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
  81. data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
  82. data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
  83. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
  84. data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
  85. data/lib/lutaml/model/schema/templates.rb +162 -0
  86. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
  87. data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
  88. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
  89. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
  90. data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
  91. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
  92. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
  93. data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
  94. data/lib/lutaml/model/schema.rb +19 -0
  95. data/lib/lutaml/model/serialize/attribute_definition.rb +14 -2
  96. data/lib/lutaml/model/serialize/builder.rb +15 -2
  97. data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
  98. data/lib/lutaml/model/serialize.rb +6 -9
  99. data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
  100. data/lib/lutaml/model/services.rb +0 -2
  101. data/lib/lutaml/model/store.rb +2 -1
  102. data/lib/lutaml/model/toml.rb +1 -1
  103. data/lib/lutaml/model/transform.rb +17 -39
  104. data/lib/lutaml/model/transformation_registry.rb +1 -1
  105. data/lib/lutaml/model/type.rb +1 -0
  106. data/lib/lutaml/model/union.rb +320 -0
  107. data/lib/lutaml/model/utils.rb +6 -0
  108. data/lib/lutaml/model/validation.rb +1 -4
  109. data/lib/lutaml/model/version.rb +1 -1
  110. data/lib/lutaml/model.rb +19 -0
  111. data/lib/lutaml/{jsonld → rdf}/context.rb +1 -1
  112. data/lib/lutaml/{jsonld/transform.rb → rdf/linked_data_transform.rb} +33 -35
  113. data/lib/lutaml/{jsonld → rdf}/term_definition.rb +1 -1
  114. data/lib/lutaml/rdf.rb +3 -0
  115. data/lib/lutaml/toml.rb +1 -1
  116. data/lib/lutaml/xml/adapter.rb +1 -1
  117. data/lib/lutaml/xml/adapter_element.rb +2 -2
  118. data/lib/lutaml/xml/adapter_loader.rb +1 -1
  119. data/lib/lutaml/xml/builder/base.rb +1 -1
  120. data/lib/lutaml/xml/builder.rb +1 -1
  121. data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
  122. data/lib/lutaml/xml/model_transform.rb +5 -1
  123. data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
  124. data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
  125. data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
  126. data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
  127. data/lib/lutaml/xml/transformation/element_builder.rb +59 -31
  128. data/lib/lutaml/xml/transformation/ordered_applier.rb +6 -0
  129. data/lib/lutaml/xml/xsd_validator.rb +66 -0
  130. data/lib/lutaml/xml.rb +53 -12
  131. data/lib/lutaml/yamlld/adapter.rb +25 -0
  132. data/lib/lutaml/yamlld.rb +25 -0
  133. data/lutaml-model.gemspec +4 -2
  134. data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
  135. data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
  136. data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
  137. data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
  138. data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
  139. data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
  140. data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
  141. data/spec/fixtures/xml/schema/rng/book.rng +27 -0
  142. data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
  143. data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
  144. data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
  145. data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
  146. data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
  147. data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
  148. data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
  149. data/spec/fixtures/xml/schema/rng/person.rng +23 -0
  150. data/spec/fixtures/xml/schema/rng/union.rng +14 -0
  151. data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
  152. data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
  153. data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
  154. data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
  155. data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
  156. data/spec/lutaml/integration/multi_format_spec.rb +23 -0
  157. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
  158. data/spec/lutaml/model/attribute_spec.rb +177 -13
  159. data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
  160. data/spec/lutaml/model/choice_restrict_spec.rb +225 -0
  161. data/spec/lutaml/model/collection_spec.rb +18 -0
  162. data/spec/lutaml/model/custom_model_spec.rb +4 -4
  163. data/spec/lutaml/model/mixed_content_spec.rb +173 -2
  164. data/spec/lutaml/model/multiple_mapping_spec.rb +4 -4
  165. data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
  166. data/spec/lutaml/model/ordered_content_spec.rb +33 -3
  167. data/spec/lutaml/model/raw_element_spec.rb +125 -0
  168. data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
  169. data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
  170. data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
  171. data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
  172. data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
  173. data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
  174. data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
  175. data/spec/lutaml/model/uninitialized_class_spec.rb +1 -1
  176. data/spec/lutaml/model/union_attribute_spec.rb +658 -0
  177. data/spec/lutaml/model/union_spec.rb +257 -0
  178. data/spec/lutaml/model/utils_spec.rb +14 -0
  179. data/spec/lutaml/model/xsd_form_default_patterns_spec.rb +2 -2
  180. data/spec/lutaml/model/xsd_patterns_spec.rb +4 -4
  181. data/spec/lutaml/{jsonld → rdf}/context_spec.rb +2 -2
  182. data/spec/lutaml/{jsonld/transform_spec.rb → rdf/linked_data_transform_spec.rb} +15 -1
  183. data/spec/lutaml/{jsonld → rdf}/term_definition_spec.rb +2 -2
  184. data/spec/lutaml/turtle/transform_spec.rb +2 -2
  185. data/spec/lutaml/xml/enhanced_mapping_spec.rb +230 -1
  186. data/spec/lutaml/xml/mapping_spec.rb +4 -4
  187. data/spec/lutaml/xml/namespace_placement_spec.rb +11 -8
  188. data/spec/lutaml/xml/namespace_three_phase_spec.rb +1 -1
  189. data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
  190. data/spec/lutaml/xml/prefix_control_spec.rb +4 -4
  191. data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
  192. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
  193. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
  194. data/spec/lutaml/xml/serializable_namespace_spec.rb +6 -6
  195. data/spec/lutaml/xml/type_namespace_examples_spec.rb +1 -1
  196. data/spec/lutaml/xml/type_namespace_integration_spec.rb +3 -3
  197. data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
  198. data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
  199. data/spec/lutaml/yamlld/adapter_spec.rb +56 -0
  200. data/spec/lutaml/yamlld/registration_spec.rb +33 -0
  201. data/spec/spec_helper.rb +28 -11
  202. data/spec/support/opal.rb +5 -2
  203. metadata +108 -37
  204. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
  205. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
  206. data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
  207. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
  208. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
  209. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
  210. data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
  211. data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
  212. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
  213. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
  214. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
  215. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
  216. data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
  217. data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
  218. data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
  219. data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
  220. data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
  221. data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
  222. data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
  223. data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
  224. data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
  225. data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
  226. data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
  227. data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
  228. data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
  229. data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
  230. data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent" unless RUBY_ENGINE == "opal"
4
+
5
+ module Lutaml
6
+ module Xml
7
+ # Validates a serialized XML string against an XSD schema file.
8
+ #
9
+ # Backs the `validate_xml_with` class macro (issue #264). Nokogiri is
10
+ # required lazily so models that do not use XSD validation keep working
11
+ # on platforms/adapters without it (Opal, ox-only installs).
12
+ #
13
+ # Compiled schemas are memoized by absolute path: the XSD files are
14
+ # constant for the life of the process, so each is read and compiled once
15
+ # and reused across every validate / validate_xml call.
16
+ #
17
+ # Not to be confused with Schema::Xsd::SchemaValidator, which checks
18
+ # that an XSD document itself is well-formed XSD.
19
+ module XsdValidator
20
+ # Concurrent (and a working Nokogiri) are unavailable under Opal, so
21
+ # the memoization store is only built off-Opal. validate raises a clear
22
+ # NotImplementedError under Opal before this store is ever consulted.
23
+ @schemas = ::Concurrent::Map.new unless RUBY_ENGINE == "opal"
24
+
25
+ # @param xml [String] an XML document
26
+ # @param schema_paths [Array<String>] paths to XSD schema files
27
+ # @return [Array<Error::SchemaValidationError>] one error per violation
28
+ def self.validate(xml, schema_paths)
29
+ paths = Array(schema_paths)
30
+ return [] if paths.empty?
31
+
32
+ if RUBY_ENGINE == "opal"
33
+ raise NotImplementedError,
34
+ "XSD schema validation (validate_xml_with) requires the " \
35
+ "nokogiri gem, which is not available under Opal."
36
+ end
37
+
38
+ ensure_nokogiri!
39
+ # Strict parsing: libxml2 recovery would otherwise repair malformed
40
+ # input and report it as schema-valid. NONET remains on by default.
41
+ document = ::Nokogiri::XML(xml, &:strict)
42
+ paths.flat_map do |path|
43
+ schema_for(path)
44
+ .validate(document)
45
+ .map { |error| Error::SchemaValidationError.new(error.message, path) }
46
+ end
47
+ end
48
+
49
+ def self.schema_for(path)
50
+ @schemas.compute_if_absent(path) do
51
+ ::Nokogiri::XML::Schema(File.read(path))
52
+ end
53
+ end
54
+
55
+ def self.ensure_nokogiri!
56
+ require "nokogiri"
57
+ rescue LoadError
58
+ raise Error::XmlConfigurationError,
59
+ "XSD schema validation (validate_xml_with) requires the " \
60
+ "nokogiri gem; add `gem \"nokogiri\"` to your Gemfile"
61
+ end
62
+
63
+ private_class_method :ensure_nokogiri!, :schema_for
64
+ end
65
+ end
66
+ end
data/lib/lutaml/xml.rb CHANGED
@@ -11,6 +11,15 @@ require_relative "model"
11
11
  # XML requires moxml for parsing
12
12
  require "moxml"
13
13
 
14
+ # Under Opal, moxml's autoloads don't fire (Opal ignores autoload).
15
+ # Pull in the eager-load boot that ships at lib/compat/opal/moxml_boot.rb
16
+ # so Moxml::Adapter::{Oga,Rexml} etc. are actually defined. Without
17
+ # this, `Config.adapter = :oga` later fails with
18
+ # `NameError: uninitialized constant Moxml::Adapter::Oga`.
19
+ if Lutaml::Model.opal?
20
+ require "moxml_boot"
21
+ end
22
+
14
23
  module Lutaml
15
24
  module Xml
16
25
  # Error module for XML-specific errors
@@ -24,6 +33,8 @@ module Lutaml
24
33
  "#{__dir__}/xml/error/xml_configuration_error"
25
34
  autoload :NamespaceMismatchError,
26
35
  "#{__dir__}/xml/error/namespace_mismatch_error"
36
+ autoload :SchemaValidationError,
37
+ "#{__dir__}/xml/error/schema_validation_error"
27
38
  end
28
39
 
29
40
  # XML Configuration modules
@@ -86,6 +97,7 @@ module Lutaml
86
97
  autoload :CustomMethodWrapper,
87
98
  "#{__dir__}/xml/transformation/custom_method_wrapper"
88
99
  autoload :Transform, "#{__dir__}/xml/transform"
100
+ autoload :XsdValidator, "#{__dir__}/xml/xsd_validator"
89
101
  autoload :Adapter, "#{__dir__}/xml/adapter"
90
102
  autoload :XmlElement, "#{__dir__}/xml/xml_element"
91
103
  autoload :AdapterElement, "#{__dir__}/xml/adapter_element"
@@ -143,12 +155,12 @@ module Lutaml
143
155
  autoload :SharedDsl, "#{__dir__}/xml/shared_dsl"
144
156
 
145
157
  autoload :Oga, "#{__dir__}/xml/oga"
158
+ autoload :Rexml, "#{__dir__}/xml/rexml"
146
159
  Lutaml::Model::RuntimeCompatibility.autoload_native(
147
160
  self,
148
161
  NokogiriElement: "#{__dir__}/xml/nokogiri/element",
149
162
  Nokogiri: "#{__dir__}/xml/nokogiri",
150
163
  Ox: "#{__dir__}/xml/ox",
151
- Rexml: "#{__dir__}/xml/rexml",
152
164
  )
153
165
  end
154
166
  end
@@ -168,10 +180,15 @@ Lutaml::Model::FormatRegistry.register(
168
180
  Ox::ParseError
169
181
  REXML::ParseException
170
182
  ],
171
- adapter_options: if Lutaml::Model::RuntimeCompatibility.opal?
183
+ adapter_options: if Lutaml::Model.opal?
184
+ # Both Oga (vendored opal-oga fork, pure-Ruby lexer)
185
+ # and REXML (bundled stdlib gem, pure Ruby) work
186
+ # under Opal. Oga is the default because moxml's CI
187
+ # verifies it most thoroughly; REXML is selectable
188
+ # for callers that prefer a stdlib-only stack.
172
189
  {
173
- available: %i[rexml],
174
- default: :rexml,
190
+ available: %i[oga rexml],
191
+ default: :oga,
175
192
  }
176
193
  else
177
194
  {
@@ -210,18 +227,24 @@ Lutaml::Model::Serialize.prepend(
210
227
  # to classes that included it before the prepend. Serializable includes
211
228
  # Serialize during model boot, so make the XML instance API available on the
212
229
  # concrete base class as well.
213
- if Lutaml::Model::RuntimeCompatibility.opal?
230
+ #
231
+ # Anonymous model classes (Class.new { include Serialize }) extend
232
+ # Serialize::ClassMethods rather than inheriting from Serializable, so the
233
+ # ModelImportExt override (root?) must land on ClassMethods too. The other
234
+ # two (FormatConversion, InstanceMethods) are already prepended unconditionally
235
+ # above, so Opal's no-double-prepend rule means we don't repeat them here.
236
+ if Lutaml::Model.opal?
214
237
  Lutaml::Model::Serializable.singleton_class.prepend(
215
238
  Lutaml::Xml::Serialization::ModelImportExt,
216
239
  )
217
240
 
218
- Lutaml::Model::Serializable.singleton_class.prepend(
219
- Lutaml::Xml::Serialization::FormatConversion,
220
- )
221
-
222
241
  Lutaml::Model::Serializable.prepend(
223
242
  Lutaml::Xml::Serialization::InstanceMethods,
224
243
  )
244
+
245
+ Lutaml::Model::Serialize::ClassMethods.prepend(
246
+ Lutaml::Xml::Serialization::ModelImportExt,
247
+ )
225
248
  end
226
249
 
227
250
  # Register XML-specific attribute override warning names
@@ -240,7 +263,7 @@ Lutaml::Xml::Type::Serializers.register_all!
240
263
 
241
264
  # Register XML schema methods
242
265
  Lutaml::Model::Schema.register_method(:to_xsd) do |klass, options = {}|
243
- if Lutaml::Model::RuntimeCompatibility.opal?
266
+ if Lutaml::Model.opal?
244
267
  raise NotImplementedError,
245
268
  "XSD schema generation is not available under Opal."
246
269
  end
@@ -249,7 +272,7 @@ Lutaml::Model::Schema.register_method(:to_xsd) do |klass, options = {}|
249
272
  end
250
273
 
251
274
  Lutaml::Model::Schema.register_method(:to_relaxng) do |klass, options = {}|
252
- if Lutaml::Model::RuntimeCompatibility.opal?
275
+ if Lutaml::Model.opal?
253
276
  raise NotImplementedError,
254
277
  "RELAX NG schema generation requires Nokogiri, " \
255
278
  "which is not available under Opal."
@@ -259,7 +282,7 @@ Lutaml::Model::Schema.register_method(:to_relaxng) do |klass, options = {}|
259
282
  end
260
283
 
261
284
  Lutaml::Model::Schema.register_method(:from_xml) do |xml, options = {}|
262
- if Lutaml::Model::RuntimeCompatibility.opal?
285
+ if Lutaml::Model.opal?
263
286
  raise NotImplementedError,
264
287
  "XML schema compilation is not available under Opal."
265
288
  end
@@ -267,6 +290,24 @@ Lutaml::Model::Schema.register_method(:from_xml) do |xml, options = {}|
267
290
  Lutaml::Model::Schema::XmlCompiler.to_models(xml, options)
268
291
  end
269
292
 
293
+ Lutaml::Model::Schema.register_method(:from_relaxng) do |rng, options = {}|
294
+ if Lutaml::Model::RuntimeCompatibility.opal?
295
+ raise NotImplementedError,
296
+ "RELAX NG schema compilation is not available under Opal."
297
+ end
298
+
299
+ Lutaml::Model::Schema::RngCompiler.to_models(rng, options)
300
+ end
301
+
302
+ Lutaml::Model::Schema.register_method(:from_rnc) do |rnc, options = {}|
303
+ if Lutaml::Model::RuntimeCompatibility.opal?
304
+ raise NotImplementedError,
305
+ "RNC schema compilation is not available under Opal."
306
+ end
307
+
308
+ Lutaml::Model::Schema::RncCompiler.to_models(rnc, options)
309
+ end
310
+
270
311
  # Register XML namespace registry with GlobalContext
271
312
  Lutaml::Model::GlobalContext.register_format_registry(
272
313
  :xml, Lutaml::Xml::NamespaceClassRegistry.new
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Lutaml
6
+ module YamlLd
7
+ class Adapter < Lutaml::KeyValue::Document
8
+ PERMITTED_CLASSES = Lutaml::Yaml::Adapter::StandardAdapter::PERMITTED_CLASSES
9
+
10
+ def self.parse(yaml_string, _options = {})
11
+ YAML.safe_load(yaml_string, permitted_classes: PERMITTED_CLASSES)
12
+ end
13
+
14
+ def to_yamlld(options = {})
15
+ attributes_to_serialize =
16
+ if @attributes.is_a?(Lutaml::KeyValue::DataModel::Element)
17
+ @attributes.to_hash["__root__"]
18
+ else
19
+ @attributes
20
+ end
21
+ YAML.dump(attributes_to_serialize, options)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "model"
4
+ require_relative "rdf"
5
+ require_relative "yaml"
6
+
7
+ module Lutaml
8
+ module YamlLd
9
+ autoload :Adapter, "#{__dir__}/yamlld/adapter"
10
+ end
11
+ end
12
+
13
+ # Known limitations (v1):
14
+ # - Single YAML document only; multi-document streams not interpreted as @graph.
15
+ # - YAML anchors/aliases not used to deduplicate @id references.
16
+ # - External @context loading not performed (matches :jsonld behavior).
17
+ Lutaml::Model::FormatRegistry.register(
18
+ :yamlld,
19
+ mapping_class: Lutaml::Rdf::Mapping,
20
+ adapter_class: Lutaml::YamlLd::Adapter,
21
+ transformer: Lutaml::Rdf::LinkedDataTransform,
22
+ key_value: false,
23
+ rdf: true,
24
+ error_types: ["Psych::SyntaxError"],
25
+ )
data/lutaml-model.gemspec CHANGED
@@ -23,9 +23,11 @@ Gem::Specification.new do |spec|
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the
25
25
  # RubyGem that have been added into git.
26
+ # vendor/ is excluded so the opal-oga / opal-ruby-ll submodule checkouts
27
+ # are not shipped in the published gem.
26
28
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
29
  `git ls-files -z`.split("\x0").reject do |f|
28
- f.match(%r{^(test|features)/})
30
+ f.match(%r{^(test|features|vendor/)/})
29
31
  end
30
32
  end
31
33
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -36,7 +38,7 @@ Gem::Specification.new do |spec|
36
38
  spec.add_dependency "canon"
37
39
  spec.add_dependency "concurrent-ruby"
38
40
  spec.add_dependency "liquid", ">= 4.0", "< 6.0"
39
- spec.add_dependency "moxml", "~> 0.1.23"
41
+ spec.add_dependency "moxml", "~> 0.1.25"
40
42
  spec.add_dependency "ostruct"
41
43
  spec.add_dependency "rubyzip", "~> 2.3"
42
44
  spec.add_dependency "thor"
@@ -0,0 +1,15 @@
1
+ addressBook =
2
+ element addressBook {
3
+ card*
4
+ }
5
+
6
+ card =
7
+ element card {
8
+ name,
9
+ email
10
+ }
11
+
12
+ name = element name { text }
13
+ email = element email { text }
14
+
15
+ start |= addressBook
@@ -0,0 +1,18 @@
1
+ namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
2
+
3
+ book =
4
+ element book {
5
+ attribute xml:lang { text }?,
6
+ [ a:defaultValue = "paperback" ]
7
+ attribute format { "hardcover" | "paperback" }?,
8
+ attribute anchor { xsd:ID }?,
9
+ attribute indent { text | "adaptive" }?,
10
+ title,
11
+ \list
12
+ }
13
+
14
+ title = element title { text }
15
+ \list = element list { item+ }
16
+ item = element item { text }
17
+
18
+ start |= book
@@ -0,0 +1,6 @@
1
+ book =
2
+ element book {
3
+ title
4
+ }
5
+
6
+ title = element title { text }
@@ -0,0 +1,8 @@
1
+ include "circular_b.rnc"
2
+
3
+ circular_a =
4
+ element circularA {
5
+ empty
6
+ }
7
+
8
+ start |= circular_a
@@ -0,0 +1,8 @@
1
+ include "circular_a.rnc"
2
+
3
+ circular_b =
4
+ element circularB {
5
+ empty
6
+ }
7
+
8
+ start |= circular_b
@@ -0,0 +1,8 @@
1
+ include "book.rnc"
2
+
3
+ library =
4
+ element library {
5
+ book+
6
+ }
7
+
8
+ start |= library
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
3
+ <start>
4
+ <element name="addressBook">
5
+ <zeroOrMore>
6
+ <element name="card">
7
+ <ref name="cardContent"/>
8
+ </element>
9
+ </zeroOrMore>
10
+ </element>
11
+ </start>
12
+ <define name="cardContent">
13
+ <element name="name"><text/></element>
14
+ <element name="email"><text/></element>
15
+ </define>
16
+ </grammar>
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Exercises: <attribute>, <optional>, <oneOrMore>, <group>, <choice>,
4
+ <empty>, <data type="..."/>
5
+ -->
6
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
7
+ <start>
8
+ <element name="book">
9
+ <attribute name="isbn"><data type="string"/></attribute>
10
+ <element name="title"><text/></element>
11
+ <optional>
12
+ <element name="subtitle"><text/></element>
13
+ </optional>
14
+ <oneOrMore>
15
+ <element name="author"><text/></element>
16
+ </oneOrMore>
17
+ <group>
18
+ <element name="publisher"><text/></element>
19
+ <element name="year"><data type="integer"/></element>
20
+ </group>
21
+ <choice>
22
+ <element name="hardcover"><empty/></element>
23
+ <element name="paperback"><empty/></element>
24
+ </choice>
25
+ </element>
26
+ </start>
27
+ </grammar>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
3
+ <start>
4
+ <element name="thing">
5
+ <attribute name="version">
6
+ <value>1.0</value>
7
+ </attribute>
8
+ <element name="name"><text/></element>
9
+ </element>
10
+ </start>
11
+ </grammar>
@@ -0,0 +1,30 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Regression fixture for the handle_group / handle_choice import-drop bug.
4
+
5
+ `Container` is a rooted element whose body is a `<choice>` containing a
6
+ `<ref>` to `FragmentThing`. `FragmentThing` is a *fragment* define (no
7
+ outer <element>), so a ref to it must surface as an `import_model` line
8
+ (and a `require_relative` in the generated container source) rather than
9
+ as a child attribute.
10
+
11
+ Before the fix, `handle_choice` discarded `collector.imports` and the
12
+ generated `Container` had no reference to `FragmentThing`.
13
+ -->
14
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
15
+ <start><ref name="Container"/></start>
16
+
17
+ <define name="Container">
18
+ <element name="container">
19
+ <choice>
20
+ <ref name="FragmentThing"/>
21
+ <element name="other"><text/></element>
22
+ </choice>
23
+ </element>
24
+ </define>
25
+
26
+ <define name="FragmentThing">
27
+ <attribute name="x"><text/></attribute>
28
+ <attribute name="y"><text/></attribute>
29
+ </define>
30
+ </grammar>
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ FullNameType example from https://github.com/lutaml/lutaml-model/issues/9
4
+ Regression fixture: tests that document order is preserved in the generated
5
+ XML mappings, mirroring XmlCompiler's resolved_element_order behavior.
6
+ -->
7
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
8
+ <start>
9
+ <ref name="FullNameType"/>
10
+ </start>
11
+ <define name="FullNameType">
12
+ <element name="fullname">
13
+ <optional>
14
+ <ref name="name_abbreviation"/>
15
+ </optional>
16
+ <choice>
17
+ <group>
18
+ <zeroOrMore><ref name="prefix"/></zeroOrMore>
19
+ <zeroOrMore><ref name="forename"/></zeroOrMore>
20
+ <optional><ref name="formatted_initials"/></optional>
21
+ <ref name="surname"/>
22
+ <zeroOrMore><ref name="addition"/></zeroOrMore>
23
+ </group>
24
+ <ref name="completeName"/>
25
+ </choice>
26
+ <zeroOrMore><ref name="biblionote"/></zeroOrMore>
27
+ <zeroOrMore><ref name="variantname"/></zeroOrMore>
28
+ </element>
29
+ </define>
30
+ <define name="name_abbreviation"><element name="abbrev"><text/></element></define>
31
+ <define name="prefix"><element name="prefix"><text/></element></define>
32
+ <define name="forename"><element name="forename"><text/></element></define>
33
+ <define name="formatted_initials"><element name="initials"><text/></element></define>
34
+ <define name="surname"><element name="surname"><text/></element></define>
35
+ <define name="addition"><element name="addition"><text/></element></define>
36
+ <define name="completeName"><element name="completeName"><text/></element></define>
37
+ <define name="biblionote"><element name="biblionote"><text/></element></define>
38
+ <define name="variantname"><element name="variantname"><text/></element></define>
39
+ </grammar>
@@ -0,0 +1,32 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Exercises slice 4: restrictions via <data><param>, enumerations via
4
+ <choice><value>, define-wrapped simple types -> SimpleTypeClass output.
5
+ -->
6
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
7
+ <start>
8
+ <element name="thing">
9
+ <attribute name="val">
10
+ <ref name="StIntegerRange"/>
11
+ </attribute>
12
+ <element name="color">
13
+ <ref name="StColor"/>
14
+ </element>
15
+ </element>
16
+ </start>
17
+
18
+ <define name="StIntegerRange">
19
+ <data type="integer">
20
+ <param name="minInclusive">1</param>
21
+ <param name="maxInclusive">255</param>
22
+ </data>
23
+ </define>
24
+
25
+ <define name="StColor">
26
+ <choice>
27
+ <value>red</value>
28
+ <value>green</value>
29
+ <value>blue</value>
30
+ </choice>
31
+ </define>
32
+ </grammar>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
3
+ <start>
4
+ <element name="tags">
5
+ <list>
6
+ <oneOrMore>
7
+ <element name="t"><text/></element>
8
+ </oneOrMore>
9
+ </list>
10
+ </element>
11
+ </start>
12
+ </grammar>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="http://example.com/books" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
3
+ <start>
4
+ <element name="book">
5
+ <attribute name="isbn"><data type="string"/></attribute>
6
+ <element name="title"><text/></element>
7
+ </element>
8
+ </start>
9
+ </grammar>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Exercises <mixed> -->
3
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0">
4
+ <start>
5
+ <element name="para">
6
+ <mixed>
7
+ <zeroOrMore>
8
+ <element name="emph"><text/></element>
9
+ </zeroOrMore>
10
+ </mixed>
11
+ </element>
12
+ </start>
13
+ </grammar>
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Multi-define grammar with cross-references. Exercises the "one class per
4
+ define" path with a ref-from-start and nested element references.
5
+ -->
6
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
7
+ <start>
8
+ <ref name="Person"/>
9
+ </start>
10
+ <define name="Person">
11
+ <element name="Person">
12
+ <element name="name"><data type="string"/></element>
13
+ <element name="age"><data type="integer"/></element>
14
+ <ref name="Address"/>
15
+ </element>
16
+ </define>
17
+ <define name="Address">
18
+ <element name="Address">
19
+ <element name="street"><data type="string"/></element>
20
+ <element name="city"><data type="string"/></element>
21
+ </element>
22
+ </define>
23
+ </grammar>
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
3
+ <start>
4
+ <element name="thing">
5
+ <element name="count"><ref name="IntOrString"/></element>
6
+ </element>
7
+ </start>
8
+ <define name="IntOrString">
9
+ <choice>
10
+ <data type="integer"/>
11
+ <data type="string"/>
12
+ </choice>
13
+ </define>
14
+ </grammar>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
3
+ xmlns="http://example.com/address"
4
+ targetNamespace="http://example.com/address"
5
+ elementFormDefault="qualified">
6
+ <xs:element name="address">
7
+ <xs:complexType>
8
+ <xs:sequence>
9
+ <xs:element name="city" type="xs:string"/>
10
+ </xs:sequence>
11
+ </xs:complexType>
12
+ </xs:element>
13
+ </xs:schema>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
+ <xs:element name="contact">
4
+ <xs:complexType>
5
+ <xs:choice>
6
+ <xs:element name="email" type="xs:string"/>
7
+ <xs:element name="phone" type="xs:string"/>
8
+ </xs:choice>
9
+ </xs:complexType>
10
+ </xs:element>
11
+ </xs:schema>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
+ <xs:element name="person">
4
+ <xs:complexType>
5
+ <xs:sequence>
6
+ <xs:element name="name" type="xs:string"/>
7
+ <xs:element name="age" type="xs:integer"/>
8
+ </xs:sequence>
9
+ </xs:complexType>
10
+ </xs:element>
11
+ </xs:schema>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
+ <xs:element name="person">
4
+ <xs:complexType>
5
+ <xs:sequence>
6
+ <xs:element name="name" type="xs:string"/>
7
+ <xs:element name="age">
8
+ <xs:simpleType>
9
+ <xs:restriction base="xs:integer">
10
+ <xs:maxInclusive value="150"/>
11
+ </xs:restriction>
12
+ </xs:simpleType>
13
+ </xs:element>
14
+ </xs:sequence>
15
+ </xs:complexType>
16
+ </xs:element>
17
+ </xs:schema>