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,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module Lutaml
6
+ module Model
7
+ module Schema
8
+ module Renderers
9
+ # Renders a Definitions::RestrictedType into a Ruby class extending
10
+ # a Lutaml::Model::Type::* with a cast body that mutates options
11
+ # with facet values and delegates to super.
12
+ class RestrictedType < Base
13
+ def render
14
+ Templates::RESTRICTED_SIMPLE_TYPE.result(binding)
15
+ end
16
+
17
+ private
18
+
19
+ def rendered_class_name = @spec.class_name
20
+ def parent_class = @spec.parent_class
21
+
22
+ def xml_namespace_line
23
+ ns = @spec.namespace_class_name
24
+ ns && "namespace #{ns}"
25
+ end
26
+
27
+ def restricted_simple_type_required_files = required_files_block
28
+
29
+ def restricted_simple_type_cast_body
30
+ [
31
+ render_min_max,
32
+ render_pattern,
33
+ render_enumerations,
34
+ render_transform,
35
+ ].compact.join
36
+ end
37
+
38
+ def render_min_max
39
+ f = @spec.facets
40
+ max = f.max_inclusive || f.max_exclusive
41
+ min = f.min_inclusive || f.min_exclusive
42
+ return nil unless max || min
43
+
44
+ out = +""
45
+ out << "#{@extended_indent}options[:max] = #{max}\n" if max
46
+ out << "#{@extended_indent}options[:min] = #{min}\n" if min
47
+ out
48
+ end
49
+
50
+ def render_pattern
51
+ p = @spec.facets.pattern
52
+ p && "#{@extended_indent}options[:pattern] = %r{#{p}}\n"
53
+ end
54
+
55
+ def render_enumerations
56
+ e = @spec.facets.enumerations
57
+ return nil if e.nil? || e.empty?
58
+
59
+ casted = e.map { |v| "super(#{v.inspect})" }.join(", ")
60
+ "#{@extended_indent}options[:values] = [#{casted}]\n"
61
+ end
62
+
63
+ def render_transform
64
+ t = @spec.transform_facet
65
+ t && "#{@extended_indent}value = #{t.expression}\n"
66
+ end
67
+
68
+ def registration_methods
69
+ Registration.methods_block(
70
+ class_name: @spec.class_name,
71
+ module_namespace: @module_namespace,
72
+ indent: @indent,
73
+ lazy: true,
74
+ keep_when_namespaced: @spec.keep_register_when_namespaced,
75
+ )
76
+ end
77
+
78
+ def registration_execution
79
+ Registration.execution_line(
80
+ class_name: @spec.class_name,
81
+ module_namespace: @module_namespace,
82
+ )
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module Lutaml
6
+ module Model
7
+ module Schema
8
+ module Renderers
9
+ # Renders a Definitions::UnionType into a
10
+ # Lutaml::Model::Type::Value subclass with a cast body whose
11
+ # strategy is chosen from the spec.
12
+ class Union < Base
13
+ def render
14
+ Templates::UNION_TYPE.result(binding)
15
+ end
16
+
17
+ private
18
+
19
+ def rendered_class_name = @spec.class_name
20
+
21
+ def union_required_files = required_files_block
22
+
23
+ def union_cast_body
24
+ case @spec.cast_strategy
25
+ when :resolve_type then resolve_type_body
26
+ when :class_refs then class_refs_body
27
+ end
28
+ end
29
+
30
+ def resolve_type_body
31
+ chain = @spec.members.map { |m| resolve_type_call(m) }
32
+ "#{@extended_indent}#{chain.join(" ||\n ")}\n"
33
+ end
34
+
35
+ def resolve_type_call(type_ref)
36
+ "Lutaml::Model::GlobalContext.resolve_type(:#{type_ref.value}, @register).cast(value, options)"
37
+ end
38
+
39
+ def class_refs_body
40
+ classes = @spec.members.map { |m| literal_class(m) }.join(", ")
41
+ sp2 = @extended_indent
42
+ <<~BODY
43
+ #{sp2}[#{classes}].each do |t|
44
+ #{sp2} begin
45
+ #{sp2} casted = t.cast(value, options)
46
+ #{sp2} return casted unless casted.nil?
47
+ #{sp2} rescue StandardError
48
+ #{sp2} next
49
+ #{sp2} end
50
+ #{sp2}end
51
+ #{sp2}value
52
+ BODY
53
+ end
54
+
55
+ def literal_class(type_ref)
56
+ case type_ref.kind
57
+ when :class_ref then type_ref.value
58
+ when :w3c then "::#{type_ref.value}"
59
+ when :symbol then "Lutaml::Model::GlobalContext.resolve_type(:#{type_ref.value})"
60
+ end
61
+ end
62
+
63
+ def registration_methods
64
+ Registration.methods_block(
65
+ class_name: @spec.class_name,
66
+ module_namespace: @module_namespace,
67
+ indent: @indent,
68
+ lazy: @spec.lazy_register,
69
+ keep_when_namespaced: @spec.keep_register_when_namespaced,
70
+ )
71
+ end
72
+
73
+ def registration_execution
74
+ Registration.execution_line(
75
+ class_name: @spec.class_name,
76
+ module_namespace: @module_namespace,
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module Renderers
7
+ autoload :Base, "#{__dir__}/renderers/base"
8
+ autoload :Registration, "#{__dir__}/renderers/registration"
9
+ autoload :RequiredFilesCalculator, "#{__dir__}/renderers/required_files_calculator"
10
+ autoload :MemberDecls, "#{__dir__}/renderers/member_decls"
11
+ autoload :Mappings, "#{__dir__}/renderers/mappings"
12
+ autoload :Model, "#{__dir__}/renderers/model"
13
+ autoload :RestrictedType, "#{__dir__}/renderers/restricted_type"
14
+ autoload :Union, "#{__dir__}/renderers/union"
15
+ autoload :Namespace, "#{__dir__}/renderers/namespace"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module RncCompiler
7
+ # Resolves RNC input text and the base directory used for relative
8
+ # include paths.
9
+ module SourceResolver
10
+ ResolvedSource =
11
+ Struct.new(:text, :base_dir, :path, keyword_init: true)
12
+
13
+ module_function
14
+
15
+ def resolve(input, options)
16
+ location = options[:location].to_s
17
+ return string_source(input) if location.empty?
18
+
19
+ expanded = File.expand_path(location)
20
+ if File.file?(expanded)
21
+ return file_source(expanded) if input.nil? || input.to_s.empty?
22
+
23
+ return string_source(input, base_dir: File.dirname(expanded), path: expanded)
24
+ end
25
+ return string_source(input, base_dir: expanded) if File.directory?(expanded)
26
+
27
+ string_source(input)
28
+ end
29
+
30
+ def string_source(input, base_dir: nil, path: nil)
31
+ ResolvedSource.new(text: input.to_s, base_dir: base_dir, path: path)
32
+ end
33
+
34
+ def file_source(path)
35
+ ResolvedSource.new(
36
+ text: File.read(path),
37
+ base_dir: File.dirname(path),
38
+ path: path,
39
+ )
40
+ end
41
+
42
+ private_class_method :string_source, :file_source
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ # Compiles RELAX NG Compact Syntax (RNC) into Lutaml::Model Ruby source.
7
+ #
8
+ # RNC support is a thin adapter: the rng gem parses the compact syntax
9
+ # into Rng::Grammar — resolving includes, bracket annotations, and
10
+ # non-leading `start` definitions natively — then RngCompiler performs all
11
+ # model generation and output handling.
12
+ module RncCompiler
13
+ extend self
14
+
15
+ autoload :SourceResolver, "#{__dir__}/rnc_compiler/source_resolver"
16
+
17
+ DEFAULT_OUTPUT_DIR_PREFIX = "rnc_models"
18
+
19
+ def to_models(rnc, options = {})
20
+ require_rnc_parser!
21
+
22
+ grammar = parse_grammar(rnc, options)
23
+
24
+ RngCompiler.to_models(
25
+ grammar,
26
+ options.merge(default_output_dir: default_output_dir),
27
+ )
28
+ end
29
+
30
+ def parse_grammar(rnc, options)
31
+ return rnc if rnc.is_a?(::Rng::Grammar)
32
+
33
+ source = SourceResolver.resolve(rnc, options)
34
+ ::Rng.parse_rnc(source.text, location: source.path || source.base_dir)
35
+ end
36
+
37
+ def require_rnc_parser!
38
+ return if rnc_parser_available?
39
+
40
+ raise "RNC schema compilation requires an rng gem whose " \
41
+ "`Rng.parse_rnc` accepts a `location:` option (for native " \
42
+ "include resolution). Point your Gemfile at an rng version " \
43
+ "that provides it."
44
+ end
45
+
46
+ # rng gained the `location:` option (native include resolution) after
47
+ # the 0.3.7 release; the released parse_rnc takes only `(rnc)`.
48
+ def rnc_parser_available?
49
+ defined?(::Rng::Grammar) && ::Rng.respond_to?(:parse_rnc) &&
50
+ ::Rng.method(:parse_rnc).parameters.any? { |_type, name| name == :location }
51
+ end
52
+
53
+ def default_output_dir
54
+ "#{DEFAULT_OUTPUT_DIR_PREFIX}_#{Time.now.to_i}"
55
+ end
56
+
57
+ private :parse_grammar, :require_rnc_parser!, :rnc_parser_available?,
58
+ :default_output_dir
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module RngCompiler
7
+ # Detects the shape of a <define> body and builds the corresponding
8
+ # Definitions::* spec for it.
9
+ #
10
+ # <data type="X"><param ...>...</param></data> -> RestrictedType
11
+ # <choice><value>a</value><value>b</value></choice> -> RestrictedType (enum)
12
+ # <choice><data type="A"/><data type="B"/></choice> -> UnionType
13
+ #
14
+ # Returns nil when the define is structural (contains elements or
15
+ # attributes) — caller falls back to building a Definitions::Model.
16
+ class DefineClassifier
17
+ def self.build(define, class_name)
18
+ new(define, class_name).build
19
+ end
20
+
21
+ def initialize(define, class_name)
22
+ @define = define
23
+ @class_name = class_name
24
+ end
25
+
26
+ def build
27
+ return nil if structural_define?
28
+
29
+ union_type || data_restricted_type || enum_restricted_type
30
+ end
31
+
32
+ private
33
+
34
+ def structural_define?
35
+ @define.element.any? || @define.attribute.any?
36
+ end
37
+
38
+ def union_type
39
+ choice = value_choice
40
+ return nil unless RngHelpers.pure_union_choice?(choice)
41
+
42
+ type_refs = Array(choice.data).map do |d|
43
+ symbol = RngCompiler::DATA_TYPE_MAP.fetch(
44
+ d.type, RngCompiler::DEFAULT_DATA_TYPE
45
+ )
46
+ Definitions::TypeRef.new(
47
+ kind: :class_ref,
48
+ value: RngHelpers.parent_class_for(symbol),
49
+ )
50
+ end
51
+
52
+ Definitions::UnionType.new(
53
+ class_name: @class_name,
54
+ members: type_refs,
55
+ cast_strategy: :class_refs,
56
+ )
57
+ end
58
+
59
+ def data_restricted_type
60
+ data = RngHelpers.single(@define.data)
61
+ return nil unless data
62
+
63
+ base = RngCompiler::DATA_TYPE_MAP.fetch(
64
+ data.type, RngCompiler::DEFAULT_DATA_TYPE
65
+ )
66
+ Definitions::RestrictedType.new(
67
+ class_name: @class_name,
68
+ parent_class: RngHelpers.parent_class_for(base),
69
+ facets: RngHelpers.facet_from_data(data),
70
+ )
71
+ end
72
+
73
+ def enum_restricted_type
74
+ choice = value_choice
75
+ return nil unless RngHelpers.pure_value_choice?(choice)
76
+
77
+ Definitions::RestrictedType.new(
78
+ class_name: @class_name,
79
+ parent_class: RngHelpers.parent_class_for(:string),
80
+ facets: RngHelpers.facet_from_values(choice.value),
81
+ )
82
+ end
83
+
84
+ def value_choice
85
+ RngHelpers.single(@define.choice)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end