lutaml-model 0.8.56 → 0.8.106

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/.github/scripts/test_installed_gem.rb +58 -0
  3. data/.github/workflows/downstream-performance.yml +28 -3
  4. data/.github/workflows/js-pr-check.yml +4 -0
  5. data/.github/workflows/js-sync.yml +4 -0
  6. data/.github/workflows/opal.yml +9 -0
  7. data/.github/workflows/rake.yml +6 -41
  8. data/.github/workflows/release.yml +41 -0
  9. data/Gemfile +17 -3
  10. data/bench/bench_alloc_audit.rb +56 -0
  11. data/bench/bench_common.rb +11 -0
  12. data/bench/bench_merge_attempts.rb +49 -0
  13. data/docs/_guides/gem-integration.adoc +85 -0
  14. data/docs/_guides/grammar-backed-formats.adoc +69 -0
  15. data/docs/_guides/index.adoc +8 -1
  16. data/docs/_guides/native-engines.adoc +23 -5
  17. data/docs/_guides/schema-generation.adoc +25 -0
  18. data/docs/_guides/type-registers-contexts.adoc +126 -0
  19. data/lib/compat/opal/lutaml_model_boot.rb +7 -5
  20. data/lib/lutaml/json/adapter/yeptris_adapter.rb +24 -1
  21. data/lib/lutaml/key_value/data_model/element.rb +5 -1
  22. data/lib/lutaml/key_value/mapping_rule.rb +7 -0
  23. data/lib/lutaml/key_value/transform.rb +29 -24
  24. data/lib/lutaml/key_value/transformation/collection_serializer.rb +12 -29
  25. data/lib/lutaml/key_value/transformation/value_serializer.rb +29 -6
  26. data/lib/lutaml/key_value/transformation.rb +64 -116
  27. data/lib/lutaml/model/adapter_resolver.rb +7 -3
  28. data/lib/lutaml/model/attribute.rb +74 -15
  29. data/lib/lutaml/model/cached_type_resolver.rb +13 -6
  30. data/lib/lutaml/model/configuration.rb +13 -0
  31. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  32. data/lib/lutaml/model/mapping/mapping_rule.rb +6 -1
  33. data/lib/lutaml/model/render_policy.rb +26 -8
  34. data/lib/lutaml/model/schema/base_schema.rb +15 -0
  35. data/lib/lutaml/model/schema/generator/definition.rb +52 -8
  36. data/lib/lutaml/model/schema/generator/definitions_collection.rb +35 -14
  37. data/lib/lutaml/model/schema.rb +4 -2
  38. data/lib/lutaml/model/serializable.rb +33 -0
  39. data/lib/lutaml/model/serialize/attribute_definition.rb +196 -2
  40. data/lib/lutaml/model/serialize/builder.rb +22 -0
  41. data/lib/lutaml/model/serialize/conversion_caching.rb +5 -0
  42. data/lib/lutaml/model/serialize/format_conversion.rb +49 -41
  43. data/lib/lutaml/model/serialize/initialization.rb +54 -7
  44. data/lib/lutaml/model/serialize/model_import.rb +60 -0
  45. data/lib/lutaml/model/serialize.rb +14 -6
  46. data/lib/lutaml/model/type/time.rb +6 -0
  47. data/lib/lutaml/model/type/value.rb +9 -7
  48. data/lib/lutaml/model/type_resolver.rb +7 -5
  49. data/lib/lutaml/model/utils.rb +18 -5
  50. data/lib/lutaml/model/version.rb +1 -1
  51. data/lib/lutaml/model.rb +7 -0
  52. data/lib/lutaml/xml/adapter/xml_parser.rb +5 -2
  53. data/lib/lutaml/xml/adapter_loader.rb +5 -2
  54. data/lib/lutaml/xml/format.rb +18 -16
  55. data/lib/lutaml/xml/leptris/native_emission.rb +187 -0
  56. data/lib/lutaml/xml/leptris/plan_compiler.rb +650 -0
  57. data/lib/lutaml/xml/leptris/plan_hydrator.rb +748 -0
  58. data/lib/lutaml/xml/leptris/plan_order.rb +55 -0
  59. data/lib/lutaml/xml/leptris/plan_serializer.rb +702 -0
  60. data/lib/lutaml/xml/leptris/plan_walk.rb +36 -0
  61. data/lib/lutaml/xml/leptris.rb +46 -0
  62. data/lib/lutaml/xml/mapping.rb +71 -18
  63. data/lib/lutaml/xml/model_transform.rb +231 -8
  64. data/lib/lutaml/xml/namespace.rb +18 -8
  65. data/lib/lutaml/xml/namespace_collector.rb +17 -1
  66. data/lib/lutaml/xml/namespace_needs.rb +77 -49
  67. data/lib/lutaml/xml/parse_session.rb +7 -19
  68. data/lib/lutaml/xml/schema/xsd/schema.rb +2 -1
  69. data/lib/lutaml/xml/serialization/format_conversion.rb +5 -1
  70. data/lib/lutaml/xml/transformation/ordered_applier.rb +21 -15
  71. data/lib/lutaml/xml/transformation.rb +15 -1
  72. data/lib/lutaml/xml/type/serializers.rb +8 -1
  73. data/lib/lutaml/yaml/adapter/yeptris_adapter.rb +34 -2
  74. data/lib/tasks/performance_probe_plan_walk.rb +1 -1
  75. data/lutaml-model.gemspec +7 -4
  76. data/spec/lutaml/json/yeptris_adapter_spec.rb +7 -2
  77. data/spec/lutaml/key_value/transformation/value_serializer_spec.rb +9 -5
  78. data/spec/lutaml/key_value/transformation_spec.rb +87 -21
  79. data/spec/lutaml/model/any_element_mapping_spec.rb +41 -0
  80. data/spec/lutaml/model/collection_validation_spec.rb +265 -0
  81. data/spec/lutaml/model/desc_spec.rb +50 -0
  82. data/spec/lutaml/model/late_attribute_declaration_spec.rb +35 -0
  83. data/spec/lutaml/model/model_import_reentry_spec.rb +46 -0
  84. data/spec/lutaml/model/opal_smoke_spec.rb +17 -0
  85. data/spec/lutaml/model/schema/generate_many_spec.rb +61 -0
  86. data/spec/lutaml/model/schema/generation_regressions_spec.rb +139 -0
  87. data/spec/lutaml/model/schema/json_schema_spec.rb +1 -13
  88. data/spec/lutaml/model/schema/yaml_schema_spec.rb +0 -11
  89. data/spec/lutaml/model/serialize/attribute_definition_block_form_spec.rb +244 -0
  90. data/spec/lutaml/model/store_spec.rb +6 -2
  91. data/spec/lutaml/model/unregistered_context_resolution_spec.rb +34 -0
  92. data/spec/lutaml/model/writer_assigned_attribute_spec.rb +62 -0
  93. data/spec/lutaml/model/yeptris_error_parity_spec.rb +95 -0
  94. data/spec/lutaml/xml/adapter/empty_document_diagnostics_spec.rb +50 -0
  95. data/spec/lutaml/xml/adapter/over_cap_parse_spec.rb +105 -0
  96. data/spec/lutaml/xml/adapter_loader_spec.rb +14 -0
  97. data/spec/lutaml/xml/adapter_metadata_default_spec.rb +16 -0
  98. data/spec/lutaml/xml/attribute_ambiguity_surface_spec.rb +5 -0
  99. data/spec/lutaml/xml/clear_parse_state_spec.rb +6 -1
  100. data/spec/lutaml/xml/compiled_rule_namespace_spec.rb +5 -0
  101. data/spec/lutaml/xml/cross_register_plan_spec.rb +75 -0
  102. data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +16 -8
  103. data/spec/lutaml/xml/element_order_tracking_spec.rb +37 -0
  104. data/spec/lutaml/xml/leptris_string_encoding_spec.rb +65 -0
  105. data/spec/lutaml/xml/opal_xml_spec.rb +47 -0
  106. data/spec/lutaml/xml/parse_dom_retention_spec.rb +34 -0
  107. data/spec/lutaml/xml/plan_fast_path_spec.rb +122 -14
  108. data/spec/lutaml/xml/plan_namespaced_serialize_spec.rb +157 -0
  109. data/spec/lutaml/xml/plan_ns_less_document_spec.rb +86 -0
  110. data/spec/lutaml/xml/plan_partition_spec.rb +1 -1
  111. data/spec/lutaml/xml/plan_type_ns_attribute_spec.rb +131 -0
  112. data/spec/lutaml/xml/plan_uri_exact_dispatch_spec.rb +287 -0
  113. data/spec/lutaml/xml/plan_walk_spec.rb +6 -6
  114. data/spec/lutaml/xml/plan_wildcard_any_spec.rb +96 -0
  115. data/spec/lutaml/yaml/yeptris_adapter_spec.rb +29 -0
  116. data/spec/person_spec.rb +8 -0
  117. metadata +48 -17
  118. data/lib/lutaml/xml/plan_compiler.rb +0 -332
  119. data/lib/lutaml/xml/plan_hydrator.rb +0 -367
  120. data/lib/lutaml/xml/plan_order.rb +0 -53
  121. data/lib/lutaml/xml/plan_serializer.rb +0 -252
  122. data/lib/lutaml/xml/plan_walk.rb +0 -30
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.56
4
+ version: 0.8.106
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-09-23 00:00:00.000000000 Z
11
+ date: 2026-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.1.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.1.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bigdecimal
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -92,14 +92,14 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: 0.5.56
95
+ version: 0.5.84
96
96
  type: :runtime
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: 0.5.56
102
+ version: 0.5.84
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: ostruct
105
105
  requirement: !ruby/object:Gem::Requirement
@@ -120,28 +120,28 @@ dependencies:
120
120
  requirements:
121
121
  - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: '3.4'
123
+ version: '3.7'
124
124
  type: :runtime
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: '3.4'
130
+ version: '3.7'
131
131
  - !ruby/object:Gem::Dependency
132
132
  name: thor
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
- - - ">="
135
+ - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: '0'
137
+ version: '1.4'
138
138
  type: :runtime
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
- - - ">="
142
+ - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: '0'
144
+ version: '1.4'
145
145
  description: 'LutaML creating data models in Ruby
146
146
 
147
147
  '
@@ -153,6 +153,7 @@ extensions: []
153
153
  extra_rdoc_files: []
154
154
  files:
155
155
  - ".envrc"
156
+ - ".github/scripts/test_installed_gem.rb"
156
157
  - ".github/workflows/dependent-repos-todo.json"
157
158
  - ".github/workflows/dependent-repos.json"
158
159
  - ".github/workflows/dependent-tests.yml"
@@ -180,8 +181,10 @@ files:
180
181
  - Rakefile
181
182
  - bench/baselines/.gitkeep
182
183
  - bench/bench_all.rb
184
+ - bench/bench_alloc_audit.rb
183
185
  - bench/bench_common.rb
184
186
  - bench/bench_compare.rb
187
+ - bench/bench_merge_attempts.rb
185
188
  - bench/bench_mml.rb
186
189
  - bench/bench_niso.rb
187
190
  - bench/bench_sts.rb
@@ -204,6 +207,8 @@ files:
204
207
  - docs/_guides/creating-xsd.adoc
205
208
  - docs/_guides/custom-methods.adoc
206
209
  - docs/_guides/document-validation.adoc
210
+ - docs/_guides/gem-integration.adoc
211
+ - docs/_guides/grammar-backed-formats.adoc
207
212
  - docs/_guides/index.adoc
208
213
  - docs/_guides/jsonld-serialization.adoc
209
214
  - docs/_guides/keyvalue-serialization.adoc
@@ -216,6 +221,7 @@ files:
216
221
  - docs/_guides/schema-generation.adoc
217
222
  - docs/_guides/schema-import.adoc
218
223
  - docs/_guides/turtle-serialization.adoc
224
+ - docs/_guides/type-registers-contexts.adoc
219
225
  - docs/_guides/value-transformations.adoc
220
226
  - docs/_guides/xml-mapping.adoc
221
227
  - docs/_guides/xml-mappings-guide.adoc
@@ -779,7 +785,14 @@ files:
779
785
  - lib/lutaml/xml/format_chooser.rb
780
786
  - lib/lutaml/xml/hoisting_algorithm.rb
781
787
  - lib/lutaml/xml/html_entities.rb
788
+ - lib/lutaml/xml/leptris.rb
782
789
  - lib/lutaml/xml/leptris/element.rb
790
+ - lib/lutaml/xml/leptris/native_emission.rb
791
+ - lib/lutaml/xml/leptris/plan_compiler.rb
792
+ - lib/lutaml/xml/leptris/plan_hydrator.rb
793
+ - lib/lutaml/xml/leptris/plan_order.rb
794
+ - lib/lutaml/xml/leptris/plan_serializer.rb
795
+ - lib/lutaml/xml/leptris/plan_walk.rb
783
796
  - lib/lutaml/xml/listener.rb
784
797
  - lib/lutaml/xml/mapping.rb
785
798
  - lib/lutaml/xml/mapping_rule.rb
@@ -808,11 +821,6 @@ files:
808
821
  - lib/lutaml/xml/parse_session.rb
809
822
  - lib/lutaml/xml/parsed_namespace_declaration.rb
810
823
  - lib/lutaml/xml/parsed_namespace_set.rb
811
- - lib/lutaml/xml/plan_compiler.rb
812
- - lib/lutaml/xml/plan_hydrator.rb
813
- - lib/lutaml/xml/plan_order.rb
814
- - lib/lutaml/xml/plan_serializer.rb
815
- - lib/lutaml/xml/plan_walk.rb
816
824
  - lib/lutaml/xml/polymorphic_value_handler.rb
817
825
  - lib/lutaml/xml/qualified_inheritance_strategy.rb
818
826
  - lib/lutaml/xml/rexml.rb
@@ -1694,6 +1702,7 @@ files:
1694
1702
  - spec/lutaml/key_value/when_attribute_spec.rb
1695
1703
  - spec/lutaml/model/absent_value_contracts_spec.rb
1696
1704
  - spec/lutaml/model/adapter_resolver_configured_spec.rb
1705
+ - spec/lutaml/model/any_element_mapping_spec.rb
1697
1706
  - spec/lutaml/model/attribute_apply_value_map_spec.rb
1698
1707
  - spec/lutaml/model/attribute_collection_spec.rb
1699
1708
  - spec/lutaml/model/attribute_default_cache_spec.rb
@@ -1734,6 +1743,7 @@ files:
1734
1743
  - spec/lutaml/model/deferred_type_no_materialize_spec.rb
1735
1744
  - spec/lutaml/model/delegation_spec.rb
1736
1745
  - spec/lutaml/model/derived_attribute_serialization_spec.rb
1746
+ - spec/lutaml/model/desc_spec.rb
1737
1747
  - spec/lutaml/model/dynamic_attribute_spec.rb
1738
1748
  - spec/lutaml/model/encoding_spec.rb
1739
1749
  - spec/lutaml/model/enum_spec.rb
@@ -1755,6 +1765,7 @@ files:
1755
1765
  - spec/lutaml/model/jsonl_spec.rb
1756
1766
  - spec/lutaml/model/key_value_data_model/key_value_element_spec.rb
1757
1767
  - spec/lutaml/model/key_value_mapping_spec.rb
1768
+ - spec/lutaml/model/late_attribute_declaration_spec.rb
1758
1769
  - spec/lutaml/model/lazy_collection_spec.rb
1759
1770
  - spec/lutaml/model/liquefiable_lazy_spec.rb
1760
1771
  - spec/lutaml/model/liquefiable_spec.rb
@@ -1766,6 +1777,7 @@ files:
1766
1777
  - spec/lutaml/model/mapping_rule_perf_guard_spec.rb
1767
1778
  - spec/lutaml/model/mapping_type_validation_spec.rb
1768
1779
  - spec/lutaml/model/mixed_content_spec.rb
1780
+ - spec/lutaml/model/model_import_reentry_spec.rb
1769
1781
  - spec/lutaml/model/model_transformer_spec.rb
1770
1782
  - spec/lutaml/model/multiple_mapping_spec.rb
1771
1783
  - spec/lutaml/model/namespace_versioning_spec.rb
@@ -1788,6 +1800,8 @@ files:
1788
1800
  - spec/lutaml/model/root_mappings_spec.rb
1789
1801
  - spec/lutaml/model/rule_value_extractor_spec.rb
1790
1802
  - spec/lutaml/model/runtime_compatibility_spec.rb
1803
+ - spec/lutaml/model/schema/generate_many_spec.rb
1804
+ - spec/lutaml/model/schema/generation_regressions_spec.rb
1791
1805
  - spec/lutaml/model/schema/generator/definitions_collection_spec.rb
1792
1806
  - spec/lutaml/model/schema/json_schema_spec.rb
1793
1807
  - spec/lutaml/model/schema/json_schema_to_models_spec.rb
@@ -1804,6 +1818,7 @@ files:
1804
1818
  - spec/lutaml/model/serializable_instantiate_spec.rb
1805
1819
  - spec/lutaml/model/serializable_spec.rb
1806
1820
  - spec/lutaml/model/serializable_validation_spec.rb
1821
+ - spec/lutaml/model/serialize/attribute_definition_block_form_spec.rb
1807
1822
  - spec/lutaml/model/serialize/builder_spec.rb
1808
1823
  - spec/lutaml/model/serialize_perf_guard_spec.rb
1809
1824
  - spec/lutaml/model/services/transformer_spec.rb
@@ -1848,6 +1863,7 @@ files:
1848
1863
  - spec/lutaml/model/uninitialized_class_spec.rb
1849
1864
  - spec/lutaml/model/union_attribute_spec.rb
1850
1865
  - spec/lutaml/model/union_spec.rb
1866
+ - spec/lutaml/model/unregistered_context_resolution_spec.rb
1851
1867
  - spec/lutaml/model/utils_spec.rb
1852
1868
  - spec/lutaml/model/validation/concerns/has_issues_spec.rb
1853
1869
  - spec/lutaml/model/validation/context_spec.rb
@@ -1867,6 +1883,7 @@ files:
1867
1883
  - spec/lutaml/model/value_policy_spec.rb
1868
1884
  - spec/lutaml/model/value_transformation_comprehensive_spec.rb
1869
1885
  - spec/lutaml/model/with_child_mapping_spec.rb
1886
+ - spec/lutaml/model/writer_assigned_attribute_spec.rb
1870
1887
  - spec/lutaml/model/xml_decoupling_spec.rb
1871
1888
  - spec/lutaml/model/xsd_form_default_patterns_spec.rb
1872
1889
  - spec/lutaml/model/xsd_patterns_spec.rb
@@ -1878,6 +1895,7 @@ files:
1878
1895
  - spec/lutaml/model/yamls_range_spec.rb
1879
1896
  - spec/lutaml/model/yamls_sequence_spec.rb
1880
1897
  - spec/lutaml/model/yamls_spec.rb
1898
+ - spec/lutaml/model/yeptris_error_parity_spec.rb
1881
1899
  - spec/lutaml/model_spec.rb
1882
1900
  - spec/lutaml/rdf/context_spec.rb
1883
1901
  - spec/lutaml/rdf/graph_serialization_spec.rb
@@ -1896,12 +1914,16 @@ files:
1896
1914
  - spec/lutaml/turtle/mapping_spec.rb
1897
1915
  - spec/lutaml/turtle/transform_spec.rb
1898
1916
  - spec/lutaml/xml/adapter/base_adapter_regression_spec.rb
1917
+ - spec/lutaml/xml/adapter/empty_document_diagnostics_spec.rb
1899
1918
  - spec/lutaml/xml/adapter/nokogiri_adapter_spec.rb
1900
1919
  - spec/lutaml/xml/adapter/oga_adapter_spec.rb
1901
1920
  - spec/lutaml/xml/adapter/order_spec.rb
1921
+ - spec/lutaml/xml/adapter/over_cap_parse_spec.rb
1902
1922
  - spec/lutaml/xml/adapter/ox_adapter_spec.rb
1903
1923
  - spec/lutaml/xml/adapter/rexml_adapter_spec.rb
1904
1924
  - spec/lutaml/xml/adapter/xml_namespace_spec.rb
1925
+ - spec/lutaml/xml/adapter_loader_spec.rb
1926
+ - spec/lutaml/xml/adapter_metadata_default_spec.rb
1905
1927
  - spec/lutaml/xml/attribute_ambiguity_surface_spec.rb
1906
1928
  - spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb
1907
1929
  - spec/lutaml/xml/attribute_namespace_disjointness_spec.rb
@@ -1912,6 +1934,7 @@ files:
1912
1934
  - spec/lutaml/xml/conformance/xml_schema_instance_spec.rb
1913
1935
  - spec/lutaml/xml/content_model_validation_spec.rb
1914
1936
  - spec/lutaml/xml/cross_class_dispatch_spec.rb
1937
+ - spec/lutaml/xml/cross_register_plan_spec.rb
1915
1938
  - spec/lutaml/xml/data_model_spec.rb
1916
1939
  - spec/lutaml/xml/decisions/decision_engine_spec.rb
1917
1940
  - spec/lutaml/xml/decisions/decision_spec.rb
@@ -1922,6 +1945,7 @@ files:
1922
1945
  - spec/lutaml/xml/doctype_preservation_spec.rb
1923
1946
  - spec/lutaml/xml/doubly_defined_namespace_spec.rb
1924
1947
  - spec/lutaml/xml/dual_namespace_element_spec.rb
1948
+ - spec/lutaml/xml/element_order_tracking_spec.rb
1925
1949
  - spec/lutaml/xml/element_spec.rb
1926
1950
  - spec/lutaml/xml/encoding_normalizer_spec.rb
1927
1951
  - spec/lutaml/xml/enhanced_mapping_spec.rb
@@ -1931,6 +1955,7 @@ files:
1931
1955
  - spec/lutaml/xml/general_namespace_spec.rb
1932
1956
  - spec/lutaml/xml/html_entities_spec.rb
1933
1957
  - spec/lutaml/xml/indent_spec.rb
1958
+ - spec/lutaml/xml/leptris_string_encoding_spec.rb
1934
1959
  - spec/lutaml/xml/line_ending_spec.rb
1935
1960
  - spec/lutaml/xml/mapping_finalization_guard_spec.rb
1936
1961
  - spec/lutaml/xml/mapping_rule_spec.rb
@@ -1967,11 +1992,17 @@ files:
1967
1992
  - spec/lutaml/xml/opal_xml_spec.rb
1968
1993
  - spec/lutaml/xml/ordered_method_spec.rb
1969
1994
  - spec/lutaml/xml/out_of_namespace_parsing_spec.rb
1995
+ - spec/lutaml/xml/parse_dom_retention_spec.rb
1970
1996
  - spec/lutaml/xml/pipeline_integration_spec.rb
1971
1997
  - spec/lutaml/xml/plain_model_accessors_spec.rb
1972
1998
  - spec/lutaml/xml/plan_fast_path_spec.rb
1999
+ - spec/lutaml/xml/plan_namespaced_serialize_spec.rb
2000
+ - spec/lutaml/xml/plan_ns_less_document_spec.rb
1973
2001
  - spec/lutaml/xml/plan_partition_spec.rb
2002
+ - spec/lutaml/xml/plan_type_ns_attribute_spec.rb
2003
+ - spec/lutaml/xml/plan_uri_exact_dispatch_spec.rb
1974
2004
  - spec/lutaml/xml/plan_walk_spec.rb
2005
+ - spec/lutaml/xml/plan_wildcard_any_spec.rb
1975
2006
  - spec/lutaml/xml/prefix_control_spec.rb
1976
2007
  - spec/lutaml/xml/register_spec.rb
1977
2008
  - spec/lutaml/xml/reusable_mapping_spec.rb
@@ -1,332 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lutaml
4
- module Xml
5
- # Phase 5 slice: compile a model's XML mapping into a
6
- # Leptris::XML::Descriptor plan and materialize whole documents in
7
- # one native pass (leptris_plan_walk) — no moxml wrapper tree, no
8
- # per-element Ruby dispatch. Measured on the 200-item probe:
9
- # walk+hydrate 8.7x faster, 82% fewer allocations than the
10
- # interpretive path, hydration-equal output.
11
- #
12
- # A model compiles when EVERY rule is plan-shaped; richer rows
13
- # defer their subtree verbatim and interpret post-walk rather
14
- # than opting the whole model out:
15
- # - map_attribute / map_element / content / raw rows
16
- # - Value-scalar types or nested Serializables
17
- # - custom methods, polymorphism, unions, and ordered/mixed
18
- # children capture :raw and interpret post-walk
19
- # - ordered/mixed root mappings compile; the entry points
20
- # reconstruct element_order from the node surface
21
- # - attributes not derived/union/polymorphic
22
- #
23
- # The fast path is opt-in (Config.xml_plan_fast_path) while the
24
- # full semantics audit (parent links, consolidation, ordering
25
- # metadata) completes.
26
- module PlanCompiler
27
- # Isolated holder: suites freeze model classes; a cache on a
28
- # frozen constant would be immutable (TypeProbeCache precedent).
29
- PLAN_CACHE = ::Class.new do
30
- class << self
31
- def cache
32
- @cache ||= {}
33
- end
34
- end
35
- end.cache
36
-
37
- class << self
38
- def compile(model_class, register)
39
- key = [model_class, register]
40
- return PLAN_CACHE[key] if PLAN_CACHE.key?(key)
41
-
42
- # Cycle guard: a self-referential model (JATS sec-in-sec) must
43
- # resolve to nil — the interpretive pipeline owns it. The guard
44
- # is THREAD-LOCAL (a shared in-progress set races: a concurrent
45
- # same-key compile would cache false permanently — the #828
46
- # lesson); the nil at the cycle point is NOT cached — the
47
- # outermost build completes and caches the real verdict.
48
- stack = (Thread.current[:plan_compiler_stack] ||= [])
49
- return nil if stack.include?(key)
50
-
51
- stack.push(key)
52
- begin
53
- PLAN_CACHE[key] = build(model_class, register)
54
- ensure
55
- stack.pop
56
- end
57
- end
58
-
59
- private
60
-
61
- def build(model_class, register)
62
- return nil unless model_class.is_a?(Class) &&
63
- model_class.include?(::Lutaml::Model::Serialize)
64
-
65
- mapping = model_class.mappings_for(:xml, register)
66
- return nil unless compilable_mapping?(mapping)
67
-
68
- rows = []
69
- attr_rows = [] # [[rule, attr]] hydration metadata
70
- plan_attrs = [] # [{name:, kind:}] rows for the engine plan
71
- compiled = [] # [rule, attr, kind, spelling, delegate_target]
72
- cdata = false
73
- mixed_content = false
74
- needs_nodes = false
75
- collection_defaults = [] # collection attrs with element rows
76
- tag = 100 # type_tag echo space for callback-routed rows
77
- model_ns = plan_namespace(model_class, mapping, register)
78
-
79
- # Collection rows are NATIVE since leptris 1.9.178 —
80
- # collection values echo name and type_tag (the #220 gap is
81
- # closed), so any number routes without callbacks.
82
- true
83
-
84
- mapping.mappings(register).each do |rule|
85
- delegate_target = nil
86
- attr = if rule.delegate
87
- delegate_target = model_class.attributes(register)[rule.delegate]
88
- t = delegate_target&.type(register)
89
- if t.is_a?(Class) && t.include?(::Lutaml::Model::Serialize)
90
- t.attributes(register)[rule.to]
91
- end
92
- else
93
- model_class.attributes(register)[rule.to]
94
- end
95
- return nil if attr.nil?
96
- return nil if attr.derived?
97
-
98
- # Partition rows (TODO 34 step 2) ride native predicates
99
- # only in the plain-capture shape; any other when_attribute
100
- # shape stays interpretive (the suite pins nested-target
101
- # partitions to the interpretive path).
102
- unless rule.when_attribute.empty?
103
- t = attr.type(register)
104
- plain_partition = !rule.delegate &&
105
- !rule.has_custom_method_for_deserialization? &&
106
- !rule.polymorphic_mapping? &&
107
- !attr.polymorphic? && !attr.union? &&
108
- !(t.is_a?(Class) &&
109
- t.include?(::Lutaml::Model::Serialize))
110
- return nil unless plain_partition
111
- end
112
-
113
- # Interpretive hydration materializes every mapped
114
- # collection, present or not; the fast path mirrors with
115
- # constructor-time empty arrays.
116
- unless rule.attribute? || !attr.collection?
117
- collection_defaults << attr.name.to_sym
118
- end
119
-
120
- # Fragment deferrals lose ancestor namespace context —
121
- # ns-qualified models keep those rules interpretive.
122
- fragment_needed = rule.has_custom_method_for_deserialization? ||
123
- rule.polymorphic_mapping? || attr.polymorphic? || attr.union?
124
- return nil if fragment_needed && model_ns
125
-
126
- if rule.attribute?
127
- return nil unless scalar_type?(attr, register)
128
- # Attribute plan rows are local-name keyed; a type-level
129
- # namespace makes the attribute (URI, local)-identified
130
- # (lutaml-model#744) — the interpretive matcher owns it
131
- # until plan rows carry namespace identity.
132
- return nil if attr.type_namespace_class(register)
133
-
134
- attr_rows << [rule, attr]
135
- plan_attrs << { name: rule.name.to_s }
136
- elsif rule.content_mapping?
137
- return nil if content_rows(rows) >= 1
138
-
139
- mixed_content = true
140
- compiled << [rule, attr,
141
- attr.collection? ? :content : :content_deferred,
142
- nil, delegate_target]
143
- rows << { name: "__content__#{compiled.size}", kind: :content }
144
- elsif rule.raw_mapping? || rule.raw == :element
145
- compiled << [rule, attr, :raw, nil, delegate_target]
146
- rows << { name: rule.name.to_s, kind: :raw }
147
- elsif rule.has_custom_method_for_deserialization?
148
- needs_nodes = true
149
- compiled << [rule, attr, :custom_method, nil, delegate_target]
150
- rows << { name: rule.name.to_s, kind: :raw }
151
- elsif rule.polymorphic_mapping? || attr.polymorphic? || attr.union?
152
- type = attr.type(register)
153
- return nil unless serializable_type?(type) || attr.union?
154
-
155
- needs_nodes = true
156
- compiled << [rule, attr, :polymorphic, nil, delegate_target]
157
- rows << { name: rule.name.to_s, kind: :raw }
158
- else
159
- type = attr.type(register)
160
- if serializable_type?(type)
161
- child = compile(type, register)
162
- return nil unless child
163
-
164
- if child[:ordered]
165
- # Ordered/mixed children need element_order on their
166
- # instances; the walk hands back plan values, not
167
- # source nodes, so the subtree defers interpretively
168
- # (the fragment parse runs the full machinery,
169
- # order included).
170
- return nil if model_ns
171
-
172
- needs_nodes = true
173
- compiled << [rule, attr, :ordered_deferred, nil,
174
- delegate_target]
175
- rows << { name: rule.name.to_s, kind: :raw }
176
- else
177
- compiled << [rule, attr, :nested, nil, delegate_target]
178
- needs_nodes ||= child[:needs_nodes]
179
- nested_row = { name: rule.name.to_s, kind: :nested,
180
- plan: child[:tree] }
181
- nested_row[:ns] = child_ns(rule, model_ns) if rule.namespace_set?
182
- rows << nested_row
183
- end
184
- elsif rule.multiple_mappings?
185
- rule.name.each do |spelling|
186
- compiled << [rule, attr, :spelling, spelling.to_s,
187
- delegate_target]
188
- rows << { name: spelling.to_s, kind: :callback,
189
- type_tag: (tag += 1) }
190
- end
191
- else
192
- return nil unless scalar_type?(attr, register)
193
-
194
- row = { name: rule.name.to_s }
195
- row[:ns] = child_ns(rule, model_ns) if rule.namespace_set?
196
- # lutaml-model#88: same-name rows partitioned by the
197
- # rule's discriminator ride the engine's exclusive
198
- # predicate match (leptris 1.9.221+, #1272).
199
- row[:when] = rule.when_attribute unless rule.when_attribute.empty?
200
-
201
- if attr.collection?
202
- compiled << [rule, attr, :collection_native, nil,
203
- delegate_target]
204
- rows << row.merge(kind: :collection)
205
- else
206
- cdata ||= rule.cdata
207
- compiled << [rule, attr, :scalar, nil, delegate_target]
208
- rows << row.merge(kind: :scalar)
209
- end
210
- end
211
- end
212
- end
213
-
214
- row_tags = partition_row_tags!(compiled, rows, tag)
215
-
216
- flags = []
217
- flags << :cdata if cdata
218
- flags << :mixed_content if mixed_content
219
- flags << :ns_lenient if model_ns
220
- tree = { name: mapping.root_element.to_s,
221
- attributes: plan_attrs, children: rows }
222
- tree[:ns] = model_ns if model_ns
223
- tree[:flags] = flags unless flags.empty?
224
- begin
225
- # Lazy: the Opal boot loads this file, and leptris is a
226
- # native gem there — the require only belongs on the
227
- # engines-enabled path.
228
- require "leptris/xml/descriptor"
229
- descriptor = ::Leptris::XML::Descriptor.build(**tree)
230
- rescue StandardError
231
- return nil
232
- end
233
-
234
- { descriptor: descriptor, tree: tree, rows: compiled,
235
- attr_rows: attr_rows, mapping: mapping,
236
- row_tags: row_tags,
237
- ordered: mapping.ordered? || mapping.mixed_content?,
238
- needs_nodes: needs_nodes,
239
- collection_defaults: collection_defaults }
240
- end
241
-
242
- def compilable_mapping?(mapping)
243
- mapping.root_element &&
244
- !(mapping.respond_to?(:root_mappings) && mapping.root_mappings)
245
- end
246
-
247
- # Partition bookkeeping for when_attribute rows (#88, TODO
248
- # 34 step 2): the engine matches same-name rows exclusively —
249
- # first matching row wins — so predicate rows must PRECEDE any
250
- # plain sibling on the same name (a plain-first order
251
- # double-captures: the plain row takes everything and the
252
- # predicates still claim their matches). Every row of a
253
- # partitioned name gets a distinct type_tag; plan values echo
254
- # it back, giving the hydrator row-exact routing with no
255
- # per-occurrence re-derivation. Returns {compiled_index =>
256
- # tag} (nil when the model has no partitions). compiled and
257
- # rows are parallel and stay in lockstep through the reorder.
258
- def partition_row_tags!(compiled, rows, tag)
259
- by_name = rows.each_with_index
260
- .group_by { |(row, _)| row[:name] }
261
- .select { |_name, pairs| pairs.any? { |(row, _)| row[:when] } }
262
- return nil if by_name.empty?
263
-
264
- permutation = by_name.each_value.flat_map do |pairs|
265
- pairs.sort_by { |(row, i)| [row[:when] ? 0 : 1, i] }
266
- .map(&:last)
267
- end
268
- remaining = (0...rows.length).to_a - permutation
269
- permutation.concat(remaining)
270
-
271
- row_tags = {}
272
- permutation.each_with_index do |old_index, new_index|
273
- row = rows[old_index]
274
- next unless by_name.key?(row[:name])
275
-
276
- tag += 1
277
- row[:type_tag] = tag
278
- row_tags[new_index] = tag
279
- end
280
- rows.replace(permutation.map { |i| rows[i] })
281
- compiled.replace(permutation.map { |i| compiled[i] })
282
- row_tags
283
- end
284
-
285
- # Attribute for a rule — delegate rules resolve against their
286
- # target model's attributes.
287
- def attr_of(model_class, rule, register)
288
- attr = model_class.attributes(register)[rule.to]
289
- return attr if attr || !rule.delegate
290
-
291
- target = model_class.attributes(register)[rule.delegate]
292
- t = target&.type(register)
293
- if t.is_a?(Class) && t.include?(::Lutaml::Model::Serialize)
294
- t.attributes(register)[rule.to]
295
- end
296
- end
297
-
298
- # Rule-level namespace → ChildPlan ns form (leptris 1.9.178):
299
- # the child binds by local name under the rule's URI with any
300
- # prefix. Blank-namespace rules (xmlns="") match :none.
301
- def child_ns(rule, _model_ns)
302
- uri = rule.namespace
303
- return { exact: uri.to_s } if uri && !uri.to_s.empty?
304
-
305
- :none
306
- end
307
-
308
- def content_rows(rows)
309
- rows.count { |r| r[:kind] == :content }
310
- end
311
-
312
- # Model-level namespace: exact URI match with lenient prefixes
313
- # — children bind by local name under any prefix the document
314
- # bound to the URI (#754 adoption semantics on the engine).
315
- def plan_namespace(_model_class, mapping, _register)
316
- ns_class = mapping.namespace_class if mapping.respond_to?(:namespace_class)
317
- ns_class&.uri ? { exact: ns_class.uri.to_s } : nil
318
- end
319
-
320
- def scalar_type?(attr, register)
321
- type = attr.type(register)
322
- type.is_a?(Class) && type < ::Lutaml::Model::Type::Value &&
323
- !attr.custom_collection?
324
- end
325
-
326
- def serializable_type?(type)
327
- type.is_a?(Class) && type.include?(::Lutaml::Model::Serialize)
328
- end
329
- end
330
- end
331
- end
332
- end