lutaml-model 0.8.17 → 0.8.19

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 (195) 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/builder-dsl.adoc +127 -0
  14. data/docs/_guides/schema-import.adoc +42 -0
  15. data/docs/_guides/xml-mapping.adoc +21 -0
  16. data/docs/_pages/validation.adoc +65 -0
  17. data/lib/compat/opal/generate_boot.rb +123 -0
  18. data/lib/compat/opal/js_bundle_entry.rb +42 -0
  19. data/lib/compat/opal/lutaml_model_boot.rb +497 -0
  20. data/lib/compat/opal/moxml_boot.rb +68 -0
  21. data/lib/compat/opal/yaml_compat.rb +32 -0
  22. data/lib/lutaml/json.rb +1 -1
  23. data/lib/lutaml/key_value/transform.rb +3 -8
  24. data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
  25. data/lib/lutaml/key_value/transformation.rb +17 -5
  26. data/lib/lutaml/model/adapter_resolver.rb +2 -2
  27. data/lib/lutaml/model/attribute.rb +88 -11
  28. data/lib/lutaml/model/cached_type_resolver.rb +1 -1
  29. data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
  30. data/lib/lutaml/model/global_context.rb +11 -8
  31. data/lib/lutaml/model/instrumentation.rb +2 -2
  32. data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
  33. data/lib/lutaml/model/register.rb +2 -2
  34. data/lib/lutaml/model/runtime_compatibility.rb +50 -0
  35. data/lib/lutaml/model/schema/class_loader.rb +58 -0
  36. data/lib/lutaml/model/schema/compiled_output.rb +83 -0
  37. data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
  38. data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
  39. data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
  40. data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
  41. data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
  42. data/lib/lutaml/model/schema/definitions/model.rb +40 -0
  43. data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
  44. data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
  45. data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
  46. data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
  47. data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
  48. data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
  49. data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
  50. data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
  51. data/lib/lutaml/model/schema/definitions.rb +24 -0
  52. data/lib/lutaml/model/schema/file_writer.rb +70 -0
  53. data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
  54. data/lib/lutaml/model/schema/generator/property.rb +21 -4
  55. data/lib/lutaml/model/schema/module_nesting.rb +31 -0
  56. data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
  57. data/lib/lutaml/model/schema/registry_generator.rb +115 -0
  58. data/lib/lutaml/model/schema/renderers/base.rb +40 -0
  59. data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
  60. data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
  61. data/lib/lutaml/model/schema/renderers/model.rb +121 -0
  62. data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
  63. data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
  64. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
  65. data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
  66. data/lib/lutaml/model/schema/renderers/union.rb +83 -0
  67. data/lib/lutaml/model/schema/renderers.rb +19 -0
  68. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
  69. data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
  70. data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
  71. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
  72. data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
  73. data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
  74. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
  75. data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
  76. data/lib/lutaml/model/schema/templates.rb +162 -0
  77. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
  78. data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
  79. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
  80. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
  81. data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
  82. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
  83. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
  84. data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
  85. data/lib/lutaml/model/schema.rb +19 -0
  86. data/lib/lutaml/model/serialize/attribute_definition.rb +23 -11
  87. data/lib/lutaml/model/serialize/builder.rb +70 -2
  88. data/lib/lutaml/model/serialize/initialization.rb +3 -2
  89. data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
  90. data/lib/lutaml/model/serialize.rb +6 -9
  91. data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
  92. data/lib/lutaml/model/services.rb +0 -2
  93. data/lib/lutaml/model/store.rb +2 -1
  94. data/lib/lutaml/model/toml.rb +1 -1
  95. data/lib/lutaml/model/transform.rb +17 -39
  96. data/lib/lutaml/model/transformation_registry.rb +1 -1
  97. data/lib/lutaml/model/type.rb +1 -0
  98. data/lib/lutaml/model/union.rb +320 -0
  99. data/lib/lutaml/model/utils.rb +6 -0
  100. data/lib/lutaml/model/validation.rb +1 -4
  101. data/lib/lutaml/model/version.rb +1 -1
  102. data/lib/lutaml/model.rb +18 -0
  103. data/lib/lutaml/toml.rb +1 -1
  104. data/lib/lutaml/xml/adapter.rb +1 -1
  105. data/lib/lutaml/xml/adapter_loader.rb +1 -1
  106. data/lib/lutaml/xml/builder/base.rb +1 -1
  107. data/lib/lutaml/xml/builder.rb +1 -1
  108. data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
  109. data/lib/lutaml/xml/model_transform.rb +5 -1
  110. data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
  111. data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
  112. data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
  113. data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
  114. data/lib/lutaml/xml/transformation/element_builder.rb +21 -8
  115. data/lib/lutaml/xml/transformation/ordered_applier.rb +104 -4
  116. data/lib/lutaml/xml/xsd_validator.rb +66 -0
  117. data/lib/lutaml/xml.rb +53 -12
  118. data/lutaml-model.gemspec +4 -2
  119. data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
  120. data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
  121. data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
  122. data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
  123. data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
  124. data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
  125. data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
  126. data/spec/fixtures/xml/schema/rng/book.rng +27 -0
  127. data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
  128. data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
  129. data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
  130. data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
  131. data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
  132. data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
  133. data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
  134. data/spec/fixtures/xml/schema/rng/person.rng +23 -0
  135. data/spec/fixtures/xml/schema/rng/union.rng +14 -0
  136. data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
  137. data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
  138. data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
  139. data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
  140. data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
  141. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
  142. data/spec/lutaml/model/attribute_spec.rb +169 -12
  143. data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
  144. data/spec/lutaml/model/collection_spec.rb +18 -0
  145. data/spec/lutaml/model/mixed_content_spec.rb +171 -0
  146. data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
  147. data/spec/lutaml/model/ordered_content_spec.rb +41 -0
  148. data/spec/lutaml/model/raw_element_spec.rb +125 -0
  149. data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
  150. data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
  151. data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
  152. data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
  153. data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
  154. data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
  155. data/spec/lutaml/model/serialize/builder_spec.rb +358 -0
  156. data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
  157. data/spec/lutaml/model/union_attribute_spec.rb +658 -0
  158. data/spec/lutaml/model/union_spec.rb +257 -0
  159. data/spec/lutaml/model/utils_spec.rb +14 -0
  160. data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
  161. data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
  162. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
  163. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
  164. data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
  165. data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
  166. data/spec/spec_helper.rb +28 -11
  167. data/spec/support/opal.rb +5 -2
  168. metadata +99 -31
  169. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
  170. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
  171. data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
  172. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
  173. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
  174. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
  175. data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
  176. data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
  177. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
  178. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
  179. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
  180. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
  181. data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
  182. data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
  183. data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
  184. data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
  185. data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
  186. data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
  187. data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
  188. data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
  189. data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
  190. data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
  191. data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
  192. data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
  193. data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
  194. data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
  195. data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
@@ -72,6 +72,71 @@ The `required: true` option validates that an attribute is present.
72
72
  attribute :name, :string, required: true
73
73
  ----
74
74
 
75
+ === XML schema validation
76
+
77
+ The `validate_xml_with` class macro validates the model's generated XML
78
+ against one or more XML Schema (XSD) files during `validate` / `validate!`.
79
+
80
+ [source,ruby]
81
+ ----
82
+ class Person < Lutaml::Model::Serializable
83
+ attribute :name, :string
84
+
85
+ xml do
86
+ root "person"
87
+ map_element "name", to: :name
88
+ end
89
+
90
+ validate_xml_with "schemas/person.xsd"
91
+ end
92
+
93
+ person = Person.new(name: "Alice")
94
+ person.validate # => collects Lutaml::Xml::Error::SchemaValidationError
95
+ # instances when the generated XML does not conform
96
+ person.validate! # => raises Lutaml::Model::ValidationError on nonconformance
97
+ ----
98
+
99
+ The macro accepts one or more schema paths, and repeated calls append to the
100
+ configured list. Subclasses inherit the parent's schema paths parent-first;
101
+ paths added by a subclass do not affect the parent.
102
+
103
+ [source,ruby]
104
+ ----
105
+ validate_xml_with "schemas/base.xsd", "schemas/extensions.xsd"
106
+ ----
107
+
108
+ Raw XML strings can be checked against the same schemas with the explicit
109
+ class-level helpers, for example to reject nonconforming input before
110
+ `from_xml`:
111
+
112
+ [source,ruby]
113
+ ----
114
+ Person.validate_xml(xml_string) # => array of schema validation errors
115
+ Person.validate_xml!(xml_string) # => raises Lutaml::Model::ValidationError
116
+ ----
117
+
118
+ NOTE: `to_xml` and `from_xml` never validate implicitly. Call `validate!`
119
+ before serializing, or `validate_xml!` on incoming XML, when enforcement is
120
+ needed — the same explicit contract as every other validation type.
121
+
122
+ XSD validation uses Nokogiri, which is required lazily on first validation.
123
+ The schemas must be local files declared explicitly; `xsi:schemaLocation`
124
+ in documents is not read and remote schemas are not fetched. Relative paths
125
+ resolve against the file that declares `validate_xml_with`, so a model works
126
+ regardless of the process working directory.
127
+
128
+ Misconfiguration raises instead of being collected as validation errors:
129
+
130
+ * a missing schema file raises `Errno::ENOENT`;
131
+ * a malformed XSD raises `Nokogiri::XML::SyntaxError`;
132
+ * a malformed XML string passed to `validate_xml` / `validate_xml!` raises
133
+ `Nokogiri::XML::SyntaxError` — parsing is strict, so malformed input is
134
+ never silently repaired and reported as conforming;
135
+ * a missing nokogiri gem raises `Lutaml::Xml::Error::XmlConfigurationError`;
136
+ * a class using the macro without an XML root mapping raises
137
+ `Lutaml::Model::TypeOnlyMappingError` from `validate`, since validation
138
+ must serialize the model to check it.
139
+
75
140
 
76
141
  == Validation examples
77
142
 
@@ -0,0 +1,123 @@
1
+ # frozen_string: true
2
+
3
+ # One-shot generator for lib/compat/opal/lutaml_model_boot.rb.
4
+ #
5
+ # Walks lib/lutaml/**/*.rb and extracts every `autoload :Name, "path"`
6
+ # declaration (single-line or multi-line), resolving the standard
7
+ # `"#{__dir__}/..."` and `"#{File.dirname(__FILE__)}/..."` interpolation
8
+ # patterns relative to the declaring file's directory.
9
+ #
10
+ # Opal treats autoload as a no-op, so every autoload target must appear
11
+ # here as an explicit `require` for nested constants to resolve at runtime.
12
+ #
13
+ # Run after adding/removing any autoload in lib/lutaml/:
14
+ # ruby lib/compat/opal/generate_boot.rb
15
+
16
+ require "pathname"
17
+
18
+ REPO_ROOT = Pathname.new(File.expand_path("../../..", __dir__))
19
+ LIB = REPO_ROOT.join("lib")
20
+
21
+ # Patterns excluded under Opal because they pull in native-only deps
22
+ # (Nokogiri/Ox C extensions, Thor CLI, etc.). REXML is pure Ruby and
23
+ # ships in the bundled gem + moxml's lib/compat/opal/rexml/* shadows,
24
+ # so it is NOT excluded — it is a fully working second Opal adapter.
25
+ NATIVE_ONLY_PATTERNS = [
26
+ %r{(nokogiri|ox)_adapter\z},
27
+ %r{/schema/(relaxng|xsd)\b},
28
+ %r{/schema_builder/(nokogiri|oga)\b},
29
+ %r{/schema/builder/(nokogiri|oga)\b},
30
+ %r{\Alutaml/model/cli\z},
31
+ ].freeze
32
+
33
+ # Match `autoload :Name, "...anything..."` where the string argument may
34
+ # span multiple physical lines (whitespace after the comma).
35
+ DECL_RE = /autoload\s+:[A-Za-z0-9_]+\s*,\s*"([^"]+)"/
36
+
37
+ # Stitches multi-line autoload declarations into a single logical line so
38
+ # DECL_RE can match. We only merge when the line ends right after the
39
+ # comma that follows `autoload :Name`.
40
+ def join_multiline_autoloads(src)
41
+ src.gsub(/(autoload\s+:[A-Za-z0-9_]+\s*,)\s*\n\s*/) { "#{Regexp.last_match(1)} " }
42
+ end
43
+
44
+ # Resolve an autoload's raw string argument to a require path relative to
45
+ # LIB. Both common interpolation shapes resolve relative to the declaring
46
+ # file's directory, which is what MRI does for them.
47
+ def resolve_path(raw, declaring_file)
48
+ declaring_dir = Pathname.new(declaring_file).dirname
49
+
50
+ if raw.start_with?("\#{__dir__}/")
51
+ subdir = raw.sub("\#{__dir__}/", "").delete_suffix(".rb")
52
+ (declaring_dir + subdir).relative_path_from(LIB).to_s.delete_suffix(".rb")
53
+ elsif raw.start_with?("\#{File.dirname(__FILE__)}/")
54
+ subdir = raw.sub("\#{File.dirname(__FILE__)}/", "").delete_suffix(".rb")
55
+ (declaring_dir + subdir).relative_path_from(LIB).to_s.delete_suffix(".rb")
56
+ elsif raw.start_with?("lutaml/")
57
+ raw.delete_suffix(".rb")
58
+ else
59
+ raw.delete_suffix(".rb")
60
+ end
61
+ end
62
+
63
+ paths = []
64
+ Dir.glob("#{LIB}/lutaml/**/*.rb").each do |file|
65
+ src = File.read(file)
66
+ # Strip full-line and trailing comments so commented-out autoload
67
+ # examples (e.g. in type_registry.rb) don't end up in the boot file.
68
+ src = src.lines.reject { |line| line.lstrip.start_with?("#") }.join
69
+ join_multiline_autoloads(src).scan(DECL_RE).each do |(raw)|
70
+ paths << resolve_path(raw, file)
71
+ end
72
+ end
73
+
74
+ paths = paths.uniq.sort
75
+ native_only = paths.select { |p| NATIVE_ONLY_PATTERNS.any? { |re| p =~ re } }
76
+ opal_paths = paths - native_only
77
+
78
+ # Order matters: namespace-defining entry files must load before any of
79
+ # their nested files. lutaml/model.rb declares the Lutaml::Model module
80
+ # (referenced by every other file); lutaml/xml.rb declares Lutaml::Xml.
81
+ # After the entry files, load by depth-then-name so a parent file (e.g.
82
+ # lutaml/hash_format.rb) is required before its children (e.g.
83
+ # lutaml/hash_format/adapter/document.rb).
84
+ #
85
+ # ENTRY_FILES are added unconditionally: they are top-level entry points
86
+ # that nothing else autoloads (because they ARE the entry points users
87
+ # require directly), so they would otherwise be missing from the list.
88
+ ENTRY_FILES = %w[lutaml/model lutaml/xml].freeze
89
+ remaining = (opal_paths - ENTRY_FILES).sort_by { |p| [p.count("/"), p] }
90
+ ordered = ENTRY_FILES + remaining
91
+
92
+ out = []
93
+ out << "# frozen_string_literal: true"
94
+ out << ""
95
+ out << "# DO NOT EDIT — regenerate with:"
96
+ out << "# ruby lib/compat/opal/generate_boot.rb"
97
+ out << "#"
98
+ out << "# Opal does not support Ruby autoload (it is a no-op at parse time)."
99
+ out << "# This file explicitly requires every autoload target in lib/lutaml/**"
100
+ out << "# so that nested constants resolve at runtime under Opal."
101
+ out << "#"
102
+ out << "# Native-only paths excluded under Opal:"
103
+ out << "# - nokogiri/ox/rexml adapters (C extensions unavailable)"
104
+ out << "# - XSD / RELAX NG schema generators (require Nokogiri)"
105
+ out << "# - lutaml/model/cli (thor-based, runtime-irrelevant under Opal)"
106
+ out << "#"
107
+ out << "# Ordering: lutaml/model and lutaml/xml define the Lutaml::Model and"
108
+ out << "# Lutaml::Xml namespaces, so they load first. Remaining paths are"
109
+ out << "# ordered by depth (shallower first) so parent files declare their"
110
+ out << "# child namespaces before children load."
111
+ out << "#"
112
+ out << "# Loaded by the Opal Rakefile before spec_helper."
113
+ out << ""
114
+ ordered.each { |p| out << %(require "#{p}") }
115
+ out << ""
116
+
117
+ # OPAL_BOOT_OUTPUT lets the in-sync guard spec regenerate to a temp path
118
+ # without clobbering the committed manifest; unset, it writes canonically.
119
+ target = ENV.fetch("OPAL_BOOT_OUTPUT") do
120
+ File.expand_path("lutaml_model_boot.rb", __dir__)
121
+ end
122
+ File.write(target, out.join("\n"))
123
+ warn "Generated #{ordered.size} requires (excluded #{native_only.size} native-only paths) -> #{target}"
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # JS bundle entry point. Pulls in the Opal-only compat shims and boot
4
+ # files BEFORE lutaml/model so that moxml's adapter constants, the
5
+ # String/Encoding/YAML/Array#pack patches, and the eager-loaded
6
+ # Moxml::* / Lutaml::* constants are all in place by the time
7
+ # lib/lutaml/model.rb runs.
8
+ #
9
+ # Used by scripts/build.rb as the bundle ENTRY (instead of
10
+ # "lutaml/model" directly) so the JS bundle mirrors the load order
11
+ # that lutaml-model's Rakefile applies for `rake spec:opal`.
12
+
13
+ if RUBY_ENGINE == "opal"
14
+ # 0. runtime_compatibility first: it stubs Mutex/ConditionVariable/
15
+ # Thread/WeakRef and patches Module#prepend for Opal. Oga's LRU
16
+ # and other gems reference Mutex at file-load time, so this must
17
+ # run before any gem that touches thread-safety primitives.
18
+ require "lutaml/model/runtime_compatibility"
19
+
20
+ # 0a. Opal's stdlib StringIO < IO loads here so moxml's
21
+ # `class StringIO` (bare, no parent) in rexml_compat.rb re-opens
22
+ # the existing class instead of conflicting with `< ::IO`.
23
+ # Without this, the bare-class definition runs first and Opal's
24
+ # `< IO` definition later raises "superclass mismatch".
25
+ require "stringio"
26
+
27
+ # 1. stdlib shims: String/Encoding/StringIO + Array#pack + nodejs/yaml
28
+ require "rexml_compat"
29
+ require "yaml_compat"
30
+
31
+ # 2. forks' Opal-aware conditionals select the pure-Ruby lexer
32
+ require "oga"
33
+ require "ll/setup"
34
+
35
+ # 3. eager-load boots (Opal ignores autoload)
36
+ require "moxml_boot"
37
+ require "lutaml_model_boot"
38
+ end
39
+
40
+ # 4. the actual entry point
41
+ require "lutaml/model"
42
+ require "lutaml/xml"