lutaml-model 0.8.24 → 0.8.28
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.
- checksums.yaml +4 -4
- data/Gemfile +5 -5
- data/lib/compat/opal/lutaml_model_boot.rb +3 -0
- data/lib/lutaml/json/adapter/yeptris_adapter.rb +1 -1
- data/lib/lutaml/model/compiled_rule.rb +16 -0
- data/lib/lutaml/model/serialize/format_conversion.rb +29 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/xml/declaration_planner.rb +8 -2
- data/lib/lutaml/xml/format.rb +3 -0
- data/lib/lutaml/xml/namespace_collector.rb +4 -3
- data/lib/lutaml/xml/plan_compiler.rb +152 -38
- data/lib/lutaml/xml/plan_hydrator.rb +301 -40
- data/lib/lutaml/xml/plan_order.rb +50 -0
- data/lib/lutaml/xml/plan_serializer.rb +241 -0
- data/lib/lutaml/xml/plan_walk.rb +30 -0
- data/lib/lutaml/yaml/adapter/yeptris_adapter.rb +8 -2
- data/lib/tasks/performance_probe_plan_walk.rb +58 -0
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/xml/plan_fast_path_spec.rb +455 -4
- data/spec/lutaml/xml/plan_walk_spec.rb +102 -0
- metadata +9 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b364f3d15a4c9fc529d85154e1e4789997432f557be698bfef559e3dc1139163
|
|
4
|
+
data.tar.gz: 3fe6af19792fc59b5800d0078e34171dd9b5d4c4009922e43a8eb0050ad8308c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7b3ebf674d7f7d306caea42542b54250dcd7ebbffc23186e2e8aa2b932d040e7dcdf4f74f41b1a9b66f089661eacd3eb8cf0676c8728b471f14e5c403175f2d4
|
|
7
|
+
data.tar.gz: e38d81dc78936d9e24d0f389134bca507c7a77d338196663ef10d1986bc3165da3ae7c88043ef0fe5c04189aace3f846034ed5a03f3463d7dfbf48267ab42fa6
|
data/Gemfile
CHANGED
|
@@ -19,7 +19,6 @@ gem "benchmark-ips"
|
|
|
19
19
|
gem "bigdecimal"
|
|
20
20
|
gem "canon" # , path: "../canon"
|
|
21
21
|
gem "json-ld"
|
|
22
|
-
gem "leptris"
|
|
23
22
|
gem "liquid", "~> 5"
|
|
24
23
|
gem "multi_json"
|
|
25
24
|
gem "nokogiri"
|
|
@@ -27,10 +26,13 @@ gem "oj"
|
|
|
27
26
|
gem "openssl", "~> 3.0"
|
|
28
27
|
gem "ox"
|
|
29
28
|
gem "rake"
|
|
30
|
-
# yeptris ships prebuilt platform gems for linux/darwin only; a source
|
|
31
|
-
# build is not wired for windows runners.
|
|
32
29
|
gem "rdf-turtle"
|
|
33
30
|
gem "rexml"
|
|
31
|
+
# Native engines stay development-only: every adapter detects and
|
|
32
|
+
# degrades gracefully at runtime (safe requires + prefer-if-available).
|
|
33
|
+
# yeptris ships no Windows prebuilt platform gem.
|
|
34
|
+
gem "leptris"
|
|
35
|
+
gem "teptris"
|
|
34
36
|
gem "yeptris" unless Gem.win_platform?
|
|
35
37
|
# TODO: revert rng branch to main when lutaml/rng#32 is merged
|
|
36
38
|
gem "rng", git: "https://github.com/lutaml/rng", branch: "main"
|
|
@@ -39,8 +41,6 @@ gem "rubocop"
|
|
|
39
41
|
gem "rubocop-performance", require: false
|
|
40
42
|
gem "rubocop-rake", require: false
|
|
41
43
|
gem "rubocop-rspec", require: false
|
|
42
|
-
# teptris ships prebuilt gems for every platform (incl. mingw-ucrt)
|
|
43
|
-
gem "teptris"
|
|
44
44
|
gem "tomlib"
|
|
45
45
|
gem "toml-rb"
|
|
46
46
|
|
|
@@ -169,6 +169,9 @@ require "lutaml/xml/parsed_namespace_declaration"
|
|
|
169
169
|
require "lutaml/xml/parsed_namespace_set"
|
|
170
170
|
require "lutaml/xml/plan_compiler"
|
|
171
171
|
require "lutaml/xml/plan_hydrator"
|
|
172
|
+
require "lutaml/xml/plan_order"
|
|
173
|
+
require "lutaml/xml/plan_serializer"
|
|
174
|
+
require "lutaml/xml/plan_walk"
|
|
172
175
|
require "lutaml/xml/polymorphic_value_handler"
|
|
173
176
|
require "lutaml/xml/qualified_inheritance_strategy"
|
|
174
177
|
require "lutaml/xml/rexml"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "yeptris"
|
|
4
3
|
require_relative "standard_adapter"
|
|
5
4
|
|
|
6
5
|
module Lutaml
|
|
@@ -13,6 +12,7 @@ module Lutaml
|
|
|
13
12
|
# load-only), so everything else inherits from the standard adapter.
|
|
14
13
|
class YeptrisAdapter < StandardAdapter
|
|
15
14
|
def self.parse(json, _options = {})
|
|
15
|
+
require "yeptris"
|
|
16
16
|
::Yeptris::JSON.load(json)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -183,6 +183,22 @@ module Lutaml
|
|
|
183
183
|
options.fetch(key, default)
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
+
# Hot rule-shape reads on the serialization path (form/raw/cdata
|
|
187
|
+
# dominate method_missing traffic — one serialization of the
|
|
188
|
+
# 200-item probe made 4,800 misses). Real methods keep the hot
|
|
189
|
+
# path off method_missing; same source (options), same defaults.
|
|
190
|
+
def form
|
|
191
|
+
options[:form]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def raw
|
|
195
|
+
options[:raw]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def cdata
|
|
199
|
+
options[:cdata]
|
|
200
|
+
end
|
|
201
|
+
|
|
186
202
|
# Handle method calls for accessing options dynamically
|
|
187
203
|
#
|
|
188
204
|
# This allows options to be accessed as methods (e.g., rule.cdata, rule.raw, rule.mixed_content)
|
|
@@ -81,6 +81,29 @@ module Lutaml
|
|
|
81
81
|
# No-op by default; XML overrides via prepend
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
# Serialize-side plan fast path: same compiled plan, direct
|
|
85
|
+
# leptris construction. Serialize-shaped plans only — custom
|
|
86
|
+
# methods, polymorphism, unions, and spellings keep the
|
|
87
|
+
# interpretive serializer.
|
|
88
|
+
def xml_plan_fast_serialize(instance, options)
|
|
89
|
+
return nil unless defined?(::Leptris::XML::Document)
|
|
90
|
+
return nil if options.key?(:only) || options.key?(:except) ||
|
|
91
|
+
options.key?(:mappings) || options.key?(:adapter) ||
|
|
92
|
+
options.key?(:_adapter_override) ||
|
|
93
|
+
options.key?(:indent) || options.key?(:xml_declaration) ||
|
|
94
|
+
options.key?(:declaration) || options.key?(:doctype)
|
|
95
|
+
|
|
96
|
+
adapter_name = Lutaml::Model::Config.adapter_for(:xml)
|
|
97
|
+
adapter_name &&= adapter_name.name
|
|
98
|
+
return nil unless adapter_name.to_s.end_with?("LeptrisAdapter")
|
|
99
|
+
|
|
100
|
+
register = Lutaml::Model::Config.default_register
|
|
101
|
+
plan = Lutaml::Xml::PlanCompiler.compile(self, register)
|
|
102
|
+
return nil unless plan && Lutaml::Xml::PlanSerializer.serializable?(plan)
|
|
103
|
+
|
|
104
|
+
Lutaml::Xml::PlanSerializer.call(instance, plan)
|
|
105
|
+
end
|
|
106
|
+
|
|
84
107
|
# Whole-document native materialization (Phase 5): compile the
|
|
85
108
|
# mapping into a Leptris descriptor plan and hydrate from one
|
|
86
109
|
# plan walk. Only when the resolved XML adapter is leptris-
|
|
@@ -106,7 +129,8 @@ module Lutaml
|
|
|
106
129
|
return nil unless root.name == plan[:tree][:name]
|
|
107
130
|
|
|
108
131
|
Lutaml::Xml::PlanHydrator.call(self, plan,
|
|
109
|
-
plan[:descriptor].walk(root)
|
|
132
|
+
plan[:descriptor].walk(root),
|
|
133
|
+
node: root)
|
|
110
134
|
rescue ::Leptris::XML::ParseError => e
|
|
111
135
|
raise Lutaml::Model::InvalidFormatError.new(:xml, e.message)
|
|
112
136
|
end
|
|
@@ -240,6 +264,10 @@ module Lutaml
|
|
|
240
264
|
Instrumentation.instrument(:to, model: name, format: format) do
|
|
241
265
|
adapter_override = options.delete(:adapter)
|
|
242
266
|
options[:_adapter_override] = true if adapter_override
|
|
267
|
+
if format == :xml && Lutaml::Model::Config.instance.xml_plan_fast_path
|
|
268
|
+
fast = xml_plan_fast_serialize(instance, options)
|
|
269
|
+
return fast if fast
|
|
270
|
+
end
|
|
243
271
|
value = public_send(:"as_#{format}", instance, options)
|
|
244
272
|
adapter = resolve_adapter(format, adapter_override)
|
|
245
273
|
|
data/lib/lutaml/model/version.rb
CHANGED
|
@@ -281,12 +281,18 @@ module Lutaml
|
|
|
281
281
|
|
|
282
282
|
attr_reader :register
|
|
283
283
|
|
|
284
|
-
# Resolve the correct register for a child mapper class
|
|
284
|
+
# Resolve the correct register for a child mapper class.
|
|
285
|
+
# Hoisted per (mapper class) on the planner: resolve_for_child is
|
|
286
|
+
# globally cached but each call still allocates its key — the
|
|
287
|
+
# serialization hot path resolves the same handful of child
|
|
288
|
+
# classes per element.
|
|
285
289
|
#
|
|
286
290
|
# @param mapper_class [Class] The child's mapper class
|
|
287
291
|
# @return [Symbol] The resolved register ID
|
|
288
292
|
def register_for(mapper_class)
|
|
289
|
-
|
|
293
|
+
@register_for ||= {}
|
|
294
|
+
@register_for[mapper_class] ||=
|
|
295
|
+
Lutaml::Model::Register.resolve_for_child(mapper_class, @register)
|
|
290
296
|
end
|
|
291
297
|
|
|
292
298
|
# Build individual child plans for collection items
|
data/lib/lutaml/xml/format.rb
CHANGED
|
@@ -23,6 +23,9 @@ module Lutaml
|
|
|
23
23
|
module Xml
|
|
24
24
|
autoload :PlanCompiler, "#{__dir__}/plan_compiler"
|
|
25
25
|
autoload :PlanHydrator, "#{__dir__}/plan_hydrator"
|
|
26
|
+
autoload :PlanOrder, "#{__dir__}/plan_order"
|
|
27
|
+
autoload :PlanSerializer, "#{__dir__}/plan_serializer"
|
|
28
|
+
autoload :PlanWalk, "#{__dir__}/plan_walk"
|
|
26
29
|
# Error module for XML-specific errors
|
|
27
30
|
module Error
|
|
28
31
|
autoload :XmlError, "#{__dir__}/error/xml_error"
|
|
@@ -566,9 +566,10 @@ module Lutaml
|
|
|
566
566
|
# Resolve type using child model's register context
|
|
567
567
|
mapper_class = options[:mapper_class]
|
|
568
568
|
child_register = if mapper_class
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
569
|
+
(@register_for ||= {})[mapper_class] ||=
|
|
570
|
+
Lutaml::Model::Register.resolve_for_child(
|
|
571
|
+
mapper_class, @register
|
|
572
|
+
)
|
|
572
573
|
else
|
|
573
574
|
@register
|
|
574
575
|
end
|
|
@@ -9,13 +9,15 @@ module Lutaml
|
|
|
9
9
|
# walk+hydrate 8.7x faster, 82% fewer allocations than the
|
|
10
10
|
# interpretive path, hydration-equal output.
|
|
11
11
|
#
|
|
12
|
-
# A model compiles
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# -
|
|
16
|
-
# -
|
|
17
|
-
#
|
|
18
|
-
#
|
|
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
|
|
19
21
|
# - attributes not derived/union/polymorphic
|
|
20
22
|
#
|
|
21
23
|
# The fast path is opt-in (Config.xml_plan_fast_path) while the
|
|
@@ -52,48 +54,133 @@ module Lutaml
|
|
|
52
54
|
rows = []
|
|
53
55
|
attr_rows = [] # [[rule, attr]] hydration metadata
|
|
54
56
|
plan_attrs = [] # [{name:, kind:}] rows for the engine plan
|
|
55
|
-
compiled = [] # [rule, attr, kind
|
|
57
|
+
compiled = [] # [rule, attr, kind, spelling, delegate_target]
|
|
58
|
+
cdata = false
|
|
59
|
+
mixed_content = false
|
|
60
|
+
needs_nodes = false
|
|
61
|
+
collection_defaults = [] # collection attrs with element rows
|
|
62
|
+
tag = 100 # type_tag echo space for callback-routed rows
|
|
63
|
+
model_ns = plan_namespace(model_class, mapping, register)
|
|
64
|
+
|
|
65
|
+
# Collection rows are NATIVE since leptris 1.9.178 —
|
|
66
|
+
# collection values echo name and type_tag (the #220 gap is
|
|
67
|
+
# closed), so any number routes without callbacks.
|
|
68
|
+
true
|
|
69
|
+
|
|
56
70
|
mapping.mappings(register).each do |rule|
|
|
57
|
-
|
|
71
|
+
delegate_target = nil
|
|
72
|
+
attr = if rule.delegate
|
|
73
|
+
delegate_target = model_class.attributes(register)[rule.delegate]
|
|
74
|
+
t = delegate_target&.type(register)
|
|
75
|
+
if t.is_a?(Class) && t.include?(::Lutaml::Model::Serialize)
|
|
76
|
+
t.attributes(register)[rule.to]
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
model_class.attributes(register)[rule.to]
|
|
80
|
+
end
|
|
58
81
|
return nil if attr.nil?
|
|
59
|
-
return nil if attr.derived?
|
|
82
|
+
return nil if attr.derived?
|
|
83
|
+
|
|
84
|
+
# Interpretive hydration materializes every mapped
|
|
85
|
+
# collection, present or not; the fast path mirrors with
|
|
86
|
+
# constructor-time empty arrays.
|
|
87
|
+
unless rule.attribute? || !attr.collection?
|
|
88
|
+
collection_defaults << attr.name.to_sym
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Fragment deferrals lose ancestor namespace context —
|
|
92
|
+
# ns-qualified models keep those rules interpretive.
|
|
93
|
+
fragment_needed = rule.has_custom_method_for_deserialization? ||
|
|
94
|
+
rule.polymorphic_mapping? || attr.polymorphic? || attr.union?
|
|
95
|
+
return nil if fragment_needed && model_ns
|
|
60
96
|
|
|
61
97
|
if rule.attribute?
|
|
62
98
|
return nil unless scalar_type?(attr, register)
|
|
63
99
|
|
|
64
100
|
attr_rows << [rule, attr]
|
|
65
101
|
plan_attrs << { name: rule.name.to_s }
|
|
66
|
-
|
|
67
|
-
return nil
|
|
102
|
+
elsif rule.content_mapping?
|
|
103
|
+
return nil if content_rows(rows) >= 1
|
|
104
|
+
|
|
105
|
+
mixed_content = true
|
|
106
|
+
compiled << [rule, attr,
|
|
107
|
+
attr.collection? ? :content : :content_deferred,
|
|
108
|
+
nil, delegate_target]
|
|
109
|
+
rows << { name: "__content__#{compiled.size}", kind: :content }
|
|
110
|
+
elsif rule.raw_mapping? || rule.raw == :element
|
|
111
|
+
compiled << [rule, attr, :raw, nil, delegate_target]
|
|
112
|
+
rows << { name: rule.name.to_s, kind: :raw }
|
|
113
|
+
elsif rule.has_custom_method_for_deserialization?
|
|
114
|
+
needs_nodes = true
|
|
115
|
+
compiled << [rule, attr, :custom_method, nil, delegate_target]
|
|
116
|
+
rows << { name: rule.name.to_s, kind: :raw }
|
|
117
|
+
elsif rule.polymorphic_mapping? || attr.polymorphic? || attr.union?
|
|
118
|
+
type = attr.type(register)
|
|
119
|
+
return nil unless serializable_type?(type) || attr.union?
|
|
68
120
|
|
|
121
|
+
needs_nodes = true
|
|
122
|
+
compiled << [rule, attr, :polymorphic, nil, delegate_target]
|
|
123
|
+
rows << { name: rule.name.to_s, kind: :raw }
|
|
124
|
+
else
|
|
69
125
|
type = attr.type(register)
|
|
70
126
|
if serializable_type?(type)
|
|
71
127
|
child = compile(type, register)
|
|
72
128
|
return nil unless child
|
|
73
129
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
130
|
+
if child[:ordered]
|
|
131
|
+
# Ordered/mixed children need element_order on their
|
|
132
|
+
# instances; the walk hands back plan values, not
|
|
133
|
+
# source nodes, so the subtree defers interpretively
|
|
134
|
+
# (the fragment parse runs the full machinery,
|
|
135
|
+
# order included).
|
|
136
|
+
return nil if model_ns
|
|
137
|
+
|
|
138
|
+
needs_nodes = true
|
|
139
|
+
compiled << [rule, attr, :ordered_deferred, nil,
|
|
140
|
+
delegate_target]
|
|
141
|
+
rows << { name: rule.name.to_s, kind: :raw }
|
|
142
|
+
else
|
|
143
|
+
compiled << [rule, attr, :nested, nil, delegate_target]
|
|
144
|
+
needs_nodes ||= child[:needs_nodes]
|
|
145
|
+
nested_row = { name: rule.name.to_s, kind: :nested,
|
|
146
|
+
plan: child[:tree] }
|
|
147
|
+
nested_row[:ns] = child_ns(rule, model_ns) if rule.namespace_set?
|
|
148
|
+
rows << nested_row
|
|
149
|
+
end
|
|
150
|
+
elsif rule.multiple_mappings?
|
|
151
|
+
rule.name.each do |spelling|
|
|
152
|
+
compiled << [rule, attr, :spelling, spelling.to_s,
|
|
153
|
+
delegate_target]
|
|
154
|
+
rows << { name: spelling.to_s, kind: :callback,
|
|
155
|
+
type_tag: (tag += 1) }
|
|
156
|
+
end
|
|
77
157
|
else
|
|
78
158
|
return nil unless scalar_type?(attr, register)
|
|
79
159
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
160
|
+
row = { name: rule.name.to_s }
|
|
161
|
+
row[:ns] = child_ns(rule, model_ns) if rule.namespace_set?
|
|
162
|
+
|
|
163
|
+
if attr.collection?
|
|
164
|
+
compiled << [rule, attr, :collection_native, nil,
|
|
165
|
+
delegate_target]
|
|
166
|
+
rows << row.merge(kind: :collection)
|
|
167
|
+
else
|
|
168
|
+
cdata ||= rule.cdata
|
|
169
|
+
compiled << [rule, attr, :scalar, nil, delegate_target]
|
|
170
|
+
rows << row.merge(kind: :scalar)
|
|
87
171
|
end
|
|
88
|
-
|
|
89
|
-
compiled << [rule, attr, kind]
|
|
90
|
-
rows << { name: rule.name.to_s, kind: kind }
|
|
91
172
|
end
|
|
92
173
|
end
|
|
93
174
|
end
|
|
94
175
|
|
|
95
|
-
|
|
96
|
-
|
|
176
|
+
flags = []
|
|
177
|
+
flags << :cdata if cdata
|
|
178
|
+
flags << :mixed_content if mixed_content
|
|
179
|
+
flags << :ns_lenient if model_ns
|
|
180
|
+
tree = { name: mapping.root_element.to_s,
|
|
181
|
+
attributes: plan_attrs, children: rows }
|
|
182
|
+
tree[:ns] = model_ns if model_ns
|
|
183
|
+
tree[:flags] = flags unless flags.empty?
|
|
97
184
|
begin
|
|
98
185
|
# Lazy: the Opal boot loads this file, and leptris is a
|
|
99
186
|
# native gem there — the require only belongs on the
|
|
@@ -105,23 +192,50 @@ module Lutaml
|
|
|
105
192
|
end
|
|
106
193
|
|
|
107
194
|
{ descriptor: descriptor, tree: tree, rows: compiled,
|
|
108
|
-
attr_rows: attr_rows, mapping: mapping
|
|
195
|
+
attr_rows: attr_rows, mapping: mapping,
|
|
196
|
+
ordered: mapping.ordered? || mapping.mixed_content?,
|
|
197
|
+
needs_nodes: needs_nodes,
|
|
198
|
+
collection_defaults: collection_defaults }
|
|
109
199
|
end
|
|
110
200
|
|
|
111
201
|
def compilable_mapping?(mapping)
|
|
112
|
-
|
|
202
|
+
mapping.root_element &&
|
|
113
203
|
!(mapping.respond_to?(:root_mappings) && mapping.root_mappings)
|
|
114
204
|
end
|
|
115
205
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
206
|
+
# Attribute for a rule — delegate rules resolve against their
|
|
207
|
+
# target model's attributes.
|
|
208
|
+
def attr_of(model_class, rule, register)
|
|
209
|
+
attr = model_class.attributes(register)[rule.to]
|
|
210
|
+
return attr if attr || !rule.delegate
|
|
211
|
+
|
|
212
|
+
target = model_class.attributes(register)[rule.delegate]
|
|
213
|
+
t = target&.type(register)
|
|
214
|
+
if t.is_a?(Class) && t.include?(::Lutaml::Model::Serialize)
|
|
215
|
+
t.attributes(register)[rule.to]
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Rule-level namespace → ChildPlan ns form (leptris 1.9.178):
|
|
220
|
+
# the child binds by local name under the rule's URI with any
|
|
221
|
+
# prefix. Blank-namespace rules (xmlns="") match :none.
|
|
222
|
+
def child_ns(rule, _model_ns)
|
|
223
|
+
uri = rule.namespace
|
|
224
|
+
return { exact: uri.to_s } if uri && !uri.to_s.empty?
|
|
225
|
+
|
|
226
|
+
:none
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def content_rows(rows)
|
|
230
|
+
rows.count { |r| r[:kind] == :content }
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Model-level namespace: exact URI match with lenient prefixes
|
|
234
|
+
# — children bind by local name under any prefix the document
|
|
235
|
+
# bound to the URI (#754 adoption semantics on the engine).
|
|
236
|
+
def plan_namespace(_model_class, mapping, _register)
|
|
237
|
+
ns_class = mapping.namespace_class if mapping.respond_to?(:namespace_class)
|
|
238
|
+
ns_class&.uri ? { exact: ns_class.uri.to_s } : nil
|
|
125
239
|
end
|
|
126
240
|
|
|
127
241
|
def scalar_type?(attr, register)
|