lutaml-model 0.8.67 → 0.8.69
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/lib/lutaml/key_value/mapping_rule.rb +7 -0
- data/lib/lutaml/key_value/transform.rb +9 -5
- data/lib/lutaml/key_value/transformation/value_serializer.rb +26 -1
- data/lib/lutaml/model/attribute.rb +13 -3
- data/lib/lutaml/model/render_policy.rb +26 -8
- data/lib/lutaml/model/type/value.rb +9 -7
- data/lib/lutaml/model/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f8ea7edf1c5b0a9d5e208fb6a83df7f40eba0a83e9ac78797130269e1a3cba7c
|
|
4
|
+
data.tar.gz: 7f2d97eac672eae74a1a192db0196305d62c45d3bd1f34855fb30a79475d6f88
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e202fb8a65018521903d42867ecd434df1f84c8568eab99f0f4e737651b6ecb51e91fc60c9b188aecc8e65f645bb10f9103a73b70617356a05c9d7ba566c8c8a
|
|
7
|
+
data.tar.gz: cabf97f1c9aaf7432a4831d3c5b5fec4d54306a073f9deb4e1e2cb4bf7ad97b608b52775cfef0b73294c51fd794368531c521386bb55a84ac2c1c23ad4004e56
|
|
@@ -83,6 +83,13 @@ module Lutaml
|
|
|
83
83
|
def root_mapping?
|
|
84
84
|
name == "root_mapping"
|
|
85
85
|
end
|
|
86
|
+
|
|
87
|
+
# The writer symbol for this rule's target, interned once per
|
|
88
|
+
# rule: the interpolation allocated a String and interned it per
|
|
89
|
+
# row per document in the transform's hot loop.
|
|
90
|
+
def kv_setter
|
|
91
|
+
@kv_setter ||= :"#{to}="
|
|
92
|
+
end
|
|
86
93
|
end
|
|
87
94
|
end
|
|
88
95
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module Lutaml
|
|
2
2
|
module KeyValue
|
|
3
|
+
MISSING = Object.new.freeze
|
|
3
4
|
class Transform < Lutaml::Model::Transform
|
|
4
5
|
def data_to_model(data, format, options = {})
|
|
5
6
|
# Use child's own default register if it has one
|
|
@@ -208,14 +209,17 @@ module Lutaml
|
|
|
208
209
|
children = []
|
|
209
210
|
absent = []
|
|
210
211
|
rows.each do |rule, attr, kind, type, child_rows|
|
|
211
|
-
|
|
212
|
+
# One hash pass per row: fetch with a sentinel distinguishes
|
|
213
|
+
# absent keys from present-nil values (both read as nil), so
|
|
214
|
+
# the separate string_or_symbol_key? probe goes away.
|
|
215
|
+
v = Lutaml::Model::Utils.fetch_str_or_sym(doc, rule.name, MISSING)
|
|
216
|
+
if v.equal?(MISSING)
|
|
212
217
|
absent << rule
|
|
213
218
|
next
|
|
214
219
|
end
|
|
215
|
-
v = Lutaml::Model::Utils.fetch_str_or_sym(doc, rule.name)
|
|
216
220
|
|
|
217
221
|
if kind == :scalar
|
|
218
|
-
setters << [
|
|
222
|
+
setters << [rule.kv_setter, v]
|
|
219
223
|
next
|
|
220
224
|
end
|
|
221
225
|
|
|
@@ -239,7 +243,7 @@ module Lutaml
|
|
|
239
243
|
|
|
240
244
|
built << child
|
|
241
245
|
end
|
|
242
|
-
setters << [
|
|
246
|
+
setters << [rule.kv_setter, built]
|
|
243
247
|
children.concat(built)
|
|
244
248
|
else
|
|
245
249
|
return nil unless v.is_a?(::Hash)
|
|
@@ -249,7 +253,7 @@ module Lutaml
|
|
|
249
253
|
options)
|
|
250
254
|
return nil if child.nil?
|
|
251
255
|
|
|
252
|
-
setters << [
|
|
256
|
+
setters << [rule.kv_setter, child]
|
|
253
257
|
children << child
|
|
254
258
|
end
|
|
255
259
|
end
|
|
@@ -151,8 +151,19 @@ model_class: nil)
|
|
|
151
151
|
return value.public_send(:"to_#{format}")
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
# Wrap value in type and call to_#{format}
|
|
155
154
|
if rule.attribute_type.is_a?(Class) && rule.attribute_type < Lutaml::Model::Type::Value
|
|
155
|
+
plan = serialize_plan(rule)
|
|
156
|
+
# Identity only under identity_fast_type's exact condition
|
|
157
|
+
# (no custom to AND no custom from): a custom from means the
|
|
158
|
+
# wrapper constructor normalizes the value, so the raw value
|
|
159
|
+
# must still pass through it.
|
|
160
|
+
if plan[:identity_type] && value.instance_of?(plan[:identity_type])
|
|
161
|
+
return value
|
|
162
|
+
end
|
|
163
|
+
if (to_fn = plan[:to_fn])
|
|
164
|
+
return to_fn.call(rule.attribute_type.new(value))
|
|
165
|
+
end
|
|
166
|
+
|
|
156
167
|
wrapped_value = rule.attribute_type.new(value)
|
|
157
168
|
wrapped_value.public_send(:"to_#{format}")
|
|
158
169
|
else
|
|
@@ -187,9 +198,23 @@ model_class: nil)
|
|
|
187
198
|
reference: reference_type?(rule),
|
|
188
199
|
union: ::Lutaml::Model::Type::Union.rule?(rule),
|
|
189
200
|
nested: nested_model?(rule),
|
|
201
|
+
# The custom to_<format> serializer, resolved once per rule:
|
|
202
|
+
# the default path is pure identity (to_<format> returns the
|
|
203
|
+
# wrapped .value), so values without a custom serializer skip
|
|
204
|
+
# the wrap-allocate-cast ceremony entirely, and custom ones
|
|
205
|
+
# skip the per-value registry lookup.
|
|
206
|
+
to_fn: custom_to_fn(rule),
|
|
190
207
|
}
|
|
191
208
|
end
|
|
192
209
|
|
|
210
|
+
def custom_to_fn(rule)
|
|
211
|
+
type = rule.attribute_type
|
|
212
|
+
return nil unless type.is_a?(::Class) && type < ::Lutaml::Model::Type::Value
|
|
213
|
+
|
|
214
|
+
::Lutaml::Model::Type::Value
|
|
215
|
+
.format_type_serializer_for(@format, type)&.fetch(:to, nil)
|
|
216
|
+
end
|
|
217
|
+
|
|
193
218
|
# Builtin scalars whose type registers no custom to_<format>
|
|
194
219
|
# serializer serialize as the value itself — Type::Value#to_<format>'s
|
|
195
220
|
# default — without the wrap-allocate-cast ceremony per field.
|
|
@@ -316,6 +316,16 @@ module Lutaml
|
|
|
316
316
|
@unresolved_type || @type
|
|
317
317
|
end
|
|
318
318
|
|
|
319
|
+
def reference_type?
|
|
320
|
+
(@reference_type ||= unresolved_type == Lutaml::Model::Type::Reference)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def value_map_boolean?
|
|
324
|
+
(@value_map_boolean ||=
|
|
325
|
+
type == Lutaml::Model::Type::Boolean ||
|
|
326
|
+
unresolved_type == Lutaml::Model::Type::Boolean)
|
|
327
|
+
end
|
|
328
|
+
|
|
319
329
|
def polymorphic?
|
|
320
330
|
@options[:polymorphic_class]
|
|
321
331
|
end
|
|
@@ -438,8 +448,7 @@ module Lutaml
|
|
|
438
448
|
|
|
439
449
|
option = value_map[key]
|
|
440
450
|
if (option.is_a?(TrueClass) || option.is_a?(FalseClass)) &&
|
|
441
|
-
|
|
442
|
-
unresolved_type == Lutaml::Model::Type::Boolean)
|
|
451
|
+
value_map_boolean?
|
|
443
452
|
return option
|
|
444
453
|
end
|
|
445
454
|
|
|
@@ -507,7 +516,8 @@ module Lutaml
|
|
|
507
516
|
return resolved_type.new(value) if value.is_a?(::Hash) && !hash_type?
|
|
508
517
|
|
|
509
518
|
# Special handling for Reference types - pass the metadata
|
|
510
|
-
|
|
519
|
+
# (the comparison is attribute-constant; memoize the verdict)
|
|
520
|
+
if reference_type?
|
|
511
521
|
return resolved_type.cast_with_metadata(value,
|
|
512
522
|
@options[:ref_model_class], @options[:ref_key_attribute])
|
|
513
523
|
end
|
|
@@ -12,6 +12,14 @@ module Lutaml
|
|
|
12
12
|
# options are normalized into value_map entries at MappingRule construction
|
|
13
13
|
# time, so downstream code never needs to re-interpret them.
|
|
14
14
|
module RenderPolicy
|
|
15
|
+
# Isolated holder: host transformations freeze after compile; an
|
|
16
|
+
# ivar cache on them raises FrozenError (learned the hard way).
|
|
17
|
+
SKIP_PLANS = ::Class.new do
|
|
18
|
+
def self.cache
|
|
19
|
+
@cache ||= {}.compare_by_identity
|
|
20
|
+
end
|
|
21
|
+
end.cache
|
|
22
|
+
|
|
15
23
|
def self.derived_attribute_for?(context_obj, attr_name)
|
|
16
24
|
return false unless context_obj.is_a?(Lutaml::Model::Serialize) &&
|
|
17
25
|
context_obj.class.is_a?(Class) &&
|
|
@@ -52,16 +60,26 @@ module Lutaml
|
|
|
52
60
|
# @param context_obj [Object] The context object (model_instance or delegate_obj)
|
|
53
61
|
# @return [Boolean] true if should skip
|
|
54
62
|
def check_skip_logic?(value, rule, context_obj)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
# Rule-constant parts (attribute name, :to value map) are frozen
|
|
64
|
+
# per rule; recomputing them per value showed in the to_yaml
|
|
65
|
+
# wall profile. The host transformation freezes itself after
|
|
66
|
+
# compile, so the cache lives in an isolated holder (TypeProbeCache
|
|
67
|
+
# precedent): host object -> identity-keyed rule plans.
|
|
68
|
+
per_host = (SKIP_PLANS[self] ||= {}.compare_by_identity)
|
|
69
|
+
skip_plan = (per_host[rule] ||= [extract_attribute_name(rule), to_value_map(rule)])
|
|
70
|
+
attr_name, to_map = skip_plan
|
|
71
|
+
|
|
72
|
+
# Plain conditionals, not `when ->(v)` literals: each lambda
|
|
73
|
+
# literal in a case/when allocates on every call — two per
|
|
74
|
+
# value here, 16% of to_yaml's allocations in the object-mode
|
|
75
|
+
# profile.
|
|
76
|
+
if value.nil?
|
|
61
77
|
return to_map[:nil] == :omitted
|
|
62
|
-
|
|
78
|
+
end
|
|
79
|
+
if Lutaml::Model::Utils.empty?(value)
|
|
63
80
|
return to_map[:empty] == :omitted
|
|
64
|
-
|
|
81
|
+
end
|
|
82
|
+
if Lutaml::Model::Utils.uninitialized?(value)
|
|
65
83
|
return to_map[:omitted] == :omitted || to_map[:omitted].nil?
|
|
66
84
|
end
|
|
67
85
|
|
|
@@ -67,12 +67,14 @@ from: nil)
|
|
|
67
67
|
# @param type_class [Class] the type class to look up
|
|
68
68
|
# @return [Hash, nil] { to: Proc, from: Proc } or nil
|
|
69
69
|
def format_type_serializer_for(format, type_class)
|
|
70
|
-
# Hot path:
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
# Hot path: memoized per format -> class with NO key
|
|
71
|
+
# allocation — the previous [format, class] array key
|
|
72
|
+
# allocated on every lookup (2k arrays per 200 serializes in
|
|
73
|
+
# the object-mode profile). Identity-keyed nested maps; the
|
|
74
|
+
# hierarchy walk below only runs on a miss, and format
|
|
75
|
+
# serializers register at load time, so a hit is stable.
|
|
76
|
+
per_format = (SERIALIZER_LOOKUP_CACHE[format] ||= {}.compare_by_identity)
|
|
77
|
+
return per_format[type_class] if per_format.key?(type_class)
|
|
76
78
|
|
|
77
79
|
klass = type_class
|
|
78
80
|
found = nil
|
|
@@ -85,7 +87,7 @@ from: nil)
|
|
|
85
87
|
|
|
86
88
|
klass = klass.superclass
|
|
87
89
|
end
|
|
88
|
-
|
|
90
|
+
per_format[type_class] = found
|
|
89
91
|
found
|
|
90
92
|
end
|
|
91
93
|
|
data/lib/lutaml/model/version.rb
CHANGED