lutaml-model 0.8.73 → 0.8.74
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/transform.rb +3 -7
- data/lib/lutaml/key_value/transformation/collection_serializer.rb +12 -29
- data/lib/lutaml/key_value/transformation/value_serializer.rb +3 -5
- data/lib/lutaml/key_value/transformation.rb +51 -116
- data/lib/lutaml/model/version.rb +1 -1
- data/spec/lutaml/key_value/transformation/value_serializer_spec.rb +9 -5
- data/spec/lutaml/key_value/transformation_spec.rb +19 -21
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 45577608605b19115a3826164cf851363218b65e71efa0b48a0d25e3a0df913f
|
|
4
|
+
data.tar.gz: '0181c1f69c5d731ad13dd65fa31a37c929714f3a0a7fd12d9dd59f7e0d093b1a'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ea5765db2e067d6d9b5670eaaa3c4f1269bdea9486ed07179ffa048e4435c0a504542dbf9dbdc0f270f776d084511d7d91998425b024fd8516f3599b7d238616
|
|
7
|
+
data.tar.gz: 88db0489938297bd0d74e24d169fbf6aecc0245998401159e0795122e24580e050e9dff58672c481f8b7fa14c64314305b23c3d7780962023282fa66a1d549c3
|
|
@@ -56,13 +56,9 @@ module Lutaml
|
|
|
56
56
|
# transformation_for returns nil for cyclic dependencies or :building sentinel
|
|
57
57
|
# Fall back to legacy approach in these cases
|
|
58
58
|
if transformation.is_a?(Lutaml::KeyValue::Transformation)
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
# The to_hash method returns {"__root__" => {actual_hash}}
|
|
63
|
-
kv_hash = kv_element.to_hash
|
|
64
|
-
# For root element, return just the content hash
|
|
65
|
-
return kv_hash["__root__"] || kv_hash
|
|
59
|
+
# transform emits the output hash directly (TODO.perf 19) —
|
|
60
|
+
# the __root__ Element tree and its to_hash flatten are gone.
|
|
61
|
+
return transformation.transform(instance, options)
|
|
66
62
|
end
|
|
67
63
|
end
|
|
68
64
|
|
|
@@ -140,15 +140,11 @@ options = {})
|
|
|
140
140
|
nil_mapping = to_map[:nil]
|
|
141
141
|
|
|
142
142
|
if nil_mapping == :empty || converted_from_nil_to_empty
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
)
|
|
146
|
-
parent.add_child(element)
|
|
143
|
+
puts " HNC-EMPTY #{rule.serialized_name}" if ENV["T19"]
|
|
144
|
+
parent[rule.serialized_name] = []
|
|
147
145
|
elsif nil_mapping == :nil || converted_from_empty_to_nil
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
parent.add_child(element)
|
|
146
|
+
puts " HNC-NIL #{rule.serialized_name}" if ENV["T19"]
|
|
147
|
+
parent[rule.serialized_name] = nil
|
|
152
148
|
end
|
|
153
149
|
# else: Skip nil collection (default behavior)
|
|
154
150
|
end
|
|
@@ -158,8 +154,6 @@ options = {})
|
|
|
158
154
|
key_attribute = root_mappings.key(:key)
|
|
159
155
|
value_attribute = root_mappings.key(:value)
|
|
160
156
|
|
|
161
|
-
parent.value ||= {} if parent.value.nil?
|
|
162
|
-
|
|
163
157
|
if key_attribute && value_attribute
|
|
164
158
|
handle_root_mappings_key_value(parent, items, key_attribute,
|
|
165
159
|
value_attribute, rule, options)
|
|
@@ -213,7 +207,7 @@ options = {})
|
|
|
213
207
|
else
|
|
214
208
|
attr_value
|
|
215
209
|
end
|
|
216
|
-
parent
|
|
210
|
+
parent[key_value.to_s] = serialized_value if serialized_value
|
|
217
211
|
end
|
|
218
212
|
end
|
|
219
213
|
|
|
@@ -336,7 +330,7 @@ options)
|
|
|
336
330
|
end
|
|
337
331
|
end
|
|
338
332
|
|
|
339
|
-
parent
|
|
333
|
+
parent[key_value.to_s] = item_hash unless item_hash.empty?
|
|
340
334
|
end
|
|
341
335
|
end
|
|
342
336
|
|
|
@@ -362,10 +356,7 @@ options)
|
|
|
362
356
|
end
|
|
363
357
|
end
|
|
364
358
|
|
|
365
|
-
|
|
366
|
-
rule.serialized_name, keyed_hash
|
|
367
|
-
)
|
|
368
|
-
parent.add_child(element)
|
|
359
|
+
parent[rule.serialized_name] = keyed_hash
|
|
369
360
|
end
|
|
370
361
|
|
|
371
362
|
# Find value attribute from child_mappings.
|
|
@@ -490,18 +481,11 @@ options)
|
|
|
490
481
|
|
|
491
482
|
# Create an array collection element (default).
|
|
492
483
|
def create_array_collection_element(parent, rule, items, options)
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
coll_element.value = []
|
|
497
|
-
else
|
|
498
|
-
items.each do |item|
|
|
499
|
-
child_value = create_value_for_item(rule, item, options)
|
|
500
|
-
coll_element.add_child(child_value) unless child_value.nil?
|
|
501
|
-
end
|
|
484
|
+
built = items.each_with_object([]) do |item, memo|
|
|
485
|
+
child_value = create_value_for_item(rule, item, options)
|
|
486
|
+
memo << child_value unless child_value.nil?
|
|
502
487
|
end
|
|
503
|
-
|
|
504
|
-
parent.add_child(coll_element)
|
|
488
|
+
parent[rule.serialized_name] = built
|
|
505
489
|
end
|
|
506
490
|
|
|
507
491
|
# Create a value element for a collection item.
|
|
@@ -554,8 +538,7 @@ options)
|
|
|
554
538
|
def serialize_nested_model(value, attr_type, options)
|
|
555
539
|
child_transformation = create_transformation(attr_type)
|
|
556
540
|
child_root = child_transformation.transform(value, options)
|
|
557
|
-
|
|
558
|
-
child_hash["__root__"]
|
|
541
|
+
child_root.empty? ? nil : child_root
|
|
559
542
|
end
|
|
560
543
|
|
|
561
544
|
# Serialize a primitive value.
|
|
@@ -303,12 +303,10 @@ model_class: nil)
|
|
|
303
303
|
# @param options [Hash] Serialization options
|
|
304
304
|
# @return [Hash, nil] The serialized hash or nil if empty
|
|
305
305
|
def transform_nested_model(value, child_transformation, options)
|
|
306
|
+
# transform emits the content hash directly (TODO.perf 19);
|
|
307
|
+
# nil for empty keeps render_nil: false working.
|
|
306
308
|
child_root = child_transformation.transform(value, options)
|
|
307
|
-
|
|
308
|
-
result = child_hash["__root__"]
|
|
309
|
-
|
|
310
|
-
# Return nil for empty hashes (allows render_nil: false to work)
|
|
311
|
-
result.nil? || result.empty? ? nil : result
|
|
309
|
+
child_root.empty? ? nil : child_root
|
|
312
310
|
end
|
|
313
311
|
|
|
314
312
|
# Check if the rule defines a Reference type.
|
|
@@ -209,7 +209,10 @@ register = self.register)
|
|
|
209
209
|
def transform(model_instance, options = {})
|
|
210
210
|
# For key-value formats, we typically don't have a named root
|
|
211
211
|
# Instead, we create an anonymous root that holds all attributes
|
|
212
|
-
|
|
212
|
+
# Hash-direct emission (TODO.perf 19): the __root__ Element
|
|
213
|
+
# tree was write-only in production — every consumer flattened
|
|
214
|
+
# it straight back to a hash. The output hash is built directly.
|
|
215
|
+
root = {}
|
|
213
216
|
|
|
214
217
|
# Apply each compiled rule (with filtering support). A
|
|
215
218
|
# when_attribute partition applies once for its whole key.
|
|
@@ -230,11 +233,7 @@ register = self.register)
|
|
|
230
233
|
|
|
231
234
|
if ENV["DEBUG_KEYED_COLLECTION"]
|
|
232
235
|
puts "=== TRANSFORM COMPLETE ==="
|
|
233
|
-
puts "root
|
|
234
|
-
root.children.each do |child|
|
|
235
|
-
puts " child key=#{child.key.inspect}, value=#{child.value.inspect}, to_hash=#{child.to_hash.inspect}"
|
|
236
|
-
end
|
|
237
|
-
puts "root.to_hash: #{root.to_hash.inspect}"
|
|
236
|
+
puts "root: #{root.inspect}"
|
|
238
237
|
end
|
|
239
238
|
|
|
240
239
|
root
|
|
@@ -289,15 +288,9 @@ register = self.register)
|
|
|
289
288
|
end
|
|
290
289
|
return unless present
|
|
291
290
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if items.empty?
|
|
296
|
-
element.value = []
|
|
297
|
-
else
|
|
298
|
-
items.each { |item| element.add_child(item) }
|
|
299
|
-
end
|
|
300
|
-
parent.add_child(element)
|
|
291
|
+
# Items are stamped hashes or primitives: the tree's
|
|
292
|
+
# children_to_array semantics, expressed directly.
|
|
293
|
+
parent[group.first.serialized_name] = items
|
|
301
294
|
end
|
|
302
295
|
|
|
303
296
|
def stamp_discriminator(item, rule)
|
|
@@ -332,16 +325,20 @@ register = self.register)
|
|
|
332
325
|
# Handle custom serialization methods (e.g., with: { to: ... })
|
|
333
326
|
if rule.has_custom_methods? && rule.custom_methods[:to]
|
|
334
327
|
to_method = rule.custom_methods[:to]
|
|
328
|
+
# Custom methods receive an Element "doc" and populate it
|
|
329
|
+
# (public contract, TODO.perf 19 keeps it): a scratch element
|
|
330
|
+
# is merged into the output hash afterwards.
|
|
331
|
+
doc = Lutaml::KeyValue::DataModel::Element.new("__custom__")
|
|
335
332
|
# lutaml-model#550: custom methods may declare a third context
|
|
336
333
|
# parameter to receive the options passed to `to_*`.
|
|
337
334
|
if model_instance.method(to_method).parameters.size >= 3
|
|
338
|
-
|
|
339
|
-
|
|
335
|
+
model_instance.public_send(to_method, model_instance,
|
|
336
|
+
doc, options[:context])
|
|
337
|
+
else
|
|
338
|
+
model_instance.public_send(to_method, model_instance, doc)
|
|
340
339
|
end
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return model_instance.public_send(to_method, model_instance,
|
|
344
|
-
parent)
|
|
340
|
+
parent.update(doc.to_hash.fetch("__custom__", {}))
|
|
341
|
+
return
|
|
345
342
|
end
|
|
346
343
|
|
|
347
344
|
# Handle delegation - extract value from delegated object
|
|
@@ -450,17 +447,9 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
450
447
|
to_map = (rule.option(:value_map) || {})[:to] || {}
|
|
451
448
|
nil_mapping = to_map[:nil]
|
|
452
449
|
if nil_mapping == :empty || converted_from_nil_to_empty
|
|
453
|
-
|
|
454
|
-
element = Lutaml::KeyValue::DataModel::Element.new(
|
|
455
|
-
rule.serialized_name, []
|
|
456
|
-
)
|
|
457
|
-
parent.add_child(element)
|
|
450
|
+
parent[rule.serialized_name] = []
|
|
458
451
|
elsif nil_mapping == :nil || converted_from_empty_to_nil
|
|
459
|
-
|
|
460
|
-
element = Lutaml::KeyValue::DataModel::Element.new(
|
|
461
|
-
rule.serialized_name, nil
|
|
462
|
-
)
|
|
463
|
-
parent.add_child(element)
|
|
452
|
+
parent[rule.serialized_name] = nil
|
|
464
453
|
else
|
|
465
454
|
# Skip nil collection
|
|
466
455
|
end
|
|
@@ -519,10 +508,10 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
519
508
|
# Transform the value to a KeyValueElement
|
|
520
509
|
value_root = value_transformation.transform(attr_value,
|
|
521
510
|
options)
|
|
522
|
-
value_hash = value_root.to_hash
|
|
523
511
|
|
|
524
|
-
#
|
|
525
|
-
|
|
512
|
+
# transform emits the output hash directly now
|
|
513
|
+
# (TODO.perf 19) — no __root__ wrapper to unwrap.
|
|
514
|
+
parent[key_value.to_s] = value_root
|
|
526
515
|
next
|
|
527
516
|
end
|
|
528
517
|
end
|
|
@@ -540,7 +529,7 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
540
529
|
# Get the child transformation for the collection item type
|
|
541
530
|
if item_type.is_a?(Class) && item_type < Lutaml::Model::Serialize
|
|
542
531
|
# Create a temporary parent KeyValueElement to hold the collection
|
|
543
|
-
temp_parent =
|
|
532
|
+
temp_parent = {}
|
|
544
533
|
|
|
545
534
|
# Convert to array
|
|
546
535
|
coll_items = Array(attr_value)
|
|
@@ -577,18 +566,15 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
577
566
|
coll_items, options)
|
|
578
567
|
end
|
|
579
568
|
|
|
580
|
-
|
|
581
|
-
temp_hash = temp_parent.to_hash
|
|
582
|
-
inner_value = temp_hash["__temp__"]
|
|
583
|
-
coll_value = inner_value&.[](value_attribute.to_s) if inner_value
|
|
569
|
+
coll_value = temp_parent[value_attribute.to_s]
|
|
584
570
|
|
|
585
|
-
parent
|
|
571
|
+
parent[key_value.to_s] = coll_value
|
|
586
572
|
next
|
|
587
573
|
end
|
|
588
574
|
end
|
|
589
575
|
|
|
590
576
|
# Fallback: just use the value directly
|
|
591
|
-
parent
|
|
577
|
+
parent[key_value.to_s] = attr_value
|
|
592
578
|
end
|
|
593
579
|
# Case 2: Only key attribute specified - serialize all other attributes
|
|
594
580
|
elsif key_attribute
|
|
@@ -662,7 +648,7 @@ converted_from_empty_to_nil: false, converted_from_nil_to_empty: false)
|
|
|
662
648
|
end
|
|
663
649
|
end
|
|
664
650
|
|
|
665
|
-
parent
|
|
651
|
+
parent[key_value.to_s] = item_hash unless item_hash.empty?
|
|
666
652
|
end
|
|
667
653
|
end
|
|
668
654
|
|
|
@@ -872,17 +858,7 @@ child_mappings, options)
|
|
|
872
858
|
puts " keyed_hash: #{keyed_hash.inspect}"
|
|
873
859
|
end
|
|
874
860
|
|
|
875
|
-
|
|
876
|
-
element = Lutaml::KeyValue::DataModel::Element.new(
|
|
877
|
-
rule.serialized_name, keyed_hash
|
|
878
|
-
)
|
|
879
|
-
|
|
880
|
-
if ENV["DEBUG_KEYED_COLLECTION"]
|
|
881
|
-
puts " element created: key=#{element.key.inspect}, value=#{element.value.inspect}, children.count=#{element.children.count}"
|
|
882
|
-
puts " element.to_hash: #{element.to_hash.inspect}"
|
|
883
|
-
end
|
|
884
|
-
|
|
885
|
-
parent.add_child(element)
|
|
861
|
+
parent[rule.serialized_name] = keyed_hash
|
|
886
862
|
end
|
|
887
863
|
|
|
888
864
|
# Create an array collection element (default)
|
|
@@ -892,23 +868,13 @@ child_mappings, options)
|
|
|
892
868
|
# @param items [Array] The collection items
|
|
893
869
|
# @param options [Hash] Options
|
|
894
870
|
def create_array_collection_element(parent, rule, items, options)
|
|
895
|
-
#
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
coll_element.value = []
|
|
901
|
-
else
|
|
902
|
-
# Add each item as a child
|
|
903
|
-
items.each do |item|
|
|
904
|
-
child_value = create_value_for_item(rule, item, options)
|
|
905
|
-
# Use explicit nil check - `if child_value` would fail for boolean false!
|
|
906
|
-
coll_element.add_child(child_value) unless child_value.nil?
|
|
907
|
-
end
|
|
871
|
+
# children_to_array semantics directly: always an array,
|
|
872
|
+
# nil children skipped (explicit nil check — booleans matter).
|
|
873
|
+
built = items.each_with_object([]) do |item, memo|
|
|
874
|
+
child_value = create_value_for_item(rule, item, options)
|
|
875
|
+
memo << child_value unless child_value.nil?
|
|
908
876
|
end
|
|
909
|
-
|
|
910
|
-
# Always add collection element
|
|
911
|
-
parent.add_child(coll_element)
|
|
877
|
+
parent[rule.serialized_name] = built
|
|
912
878
|
end
|
|
913
879
|
|
|
914
880
|
# Get the key attribute for a keyed collection from child_mappings
|
|
@@ -956,9 +922,7 @@ child_mappings, options)
|
|
|
956
922
|
# Since we don't have direct access to the parent mapping here,
|
|
957
923
|
# serialize as array for now (default behavior)
|
|
958
924
|
items.map do |item|
|
|
959
|
-
|
|
960
|
-
child_hash = child_root.to_hash
|
|
961
|
-
child_hash["__root__"]
|
|
925
|
+
child_transformation.transform(item, options)
|
|
962
926
|
end
|
|
963
927
|
end
|
|
964
928
|
else
|
|
@@ -970,10 +934,7 @@ child_mappings, options)
|
|
|
970
934
|
mapping = attr_type.mappings_for(format, register_id)
|
|
971
935
|
child_transformation = self.class.new(attr_type, mapping, format,
|
|
972
936
|
register)
|
|
973
|
-
|
|
974
|
-
options)
|
|
975
|
-
child_hash = child_root.to_hash
|
|
976
|
-
child_hash["__root__"]
|
|
937
|
+
child_transformation.transform(value, options)
|
|
977
938
|
# Use the transformation to serialize the nested model
|
|
978
939
|
elsif attr_type.is_a?(Class) && attr_type < Lutaml::Model::Type::Value
|
|
979
940
|
wrapped_value = attr_type.new(value)
|
|
@@ -1001,16 +962,10 @@ child_mappings, options)
|
|
|
1001
962
|
when :omitted
|
|
1002
963
|
return
|
|
1003
964
|
when :nil
|
|
1004
|
-
|
|
1005
|
-
rule.serialized_name, nil
|
|
1006
|
-
)
|
|
1007
|
-
parent.add_child(element)
|
|
965
|
+
parent[rule.serialized_name] = nil
|
|
1008
966
|
return
|
|
1009
967
|
when :empty
|
|
1010
|
-
|
|
1011
|
-
rule.serialized_name, ""
|
|
1012
|
-
)
|
|
1013
|
-
parent.add_child(element)
|
|
968
|
+
parent[rule.serialized_name] = ""
|
|
1014
969
|
return
|
|
1015
970
|
end
|
|
1016
971
|
|
|
@@ -1019,16 +974,10 @@ child_mappings, options)
|
|
|
1019
974
|
elsif Lutaml::Model::Utils.uninitialized?(value)
|
|
1020
975
|
omitted_mapping = to_map[:omitted]
|
|
1021
976
|
if omitted_mapping == :nil
|
|
1022
|
-
|
|
1023
|
-
rule.serialized_name, nil
|
|
1024
|
-
)
|
|
1025
|
-
parent.add_child(element)
|
|
977
|
+
parent[rule.serialized_name] = nil
|
|
1026
978
|
return
|
|
1027
979
|
elsif omitted_mapping == :empty
|
|
1028
|
-
|
|
1029
|
-
rule.serialized_name, ""
|
|
1030
|
-
)
|
|
1031
|
-
parent.add_child(element)
|
|
980
|
+
parent[rule.serialized_name] = ""
|
|
1032
981
|
return
|
|
1033
982
|
end
|
|
1034
983
|
# uninitialized with no/omitted mapping → skip (default behavior)
|
|
@@ -1036,10 +985,7 @@ child_mappings, options)
|
|
|
1036
985
|
elsif Lutaml::Model::Utils.empty?(value)
|
|
1037
986
|
empty_mapping = to_map[:empty]
|
|
1038
987
|
if empty_mapping == :nil
|
|
1039
|
-
|
|
1040
|
-
rule.serialized_name, nil
|
|
1041
|
-
)
|
|
1042
|
-
parent.add_child(element)
|
|
988
|
+
parent[rule.serialized_name] = nil
|
|
1043
989
|
return
|
|
1044
990
|
elsif empty_mapping == :omitted
|
|
1045
991
|
return
|
|
@@ -1050,12 +996,7 @@ child_mappings, options)
|
|
|
1050
996
|
# Normal value serialization
|
|
1051
997
|
child_value = create_value_for_item(rule, value, options)
|
|
1052
998
|
|
|
1053
|
-
unless child_value.nil?
|
|
1054
|
-
element = Lutaml::KeyValue::DataModel::Element.new(
|
|
1055
|
-
rule.serialized_name, child_value
|
|
1056
|
-
)
|
|
1057
|
-
parent.add_child(element)
|
|
1058
|
-
end
|
|
999
|
+
parent[rule.serialized_name] = child_value unless child_value.nil?
|
|
1059
1000
|
end
|
|
1060
1001
|
|
|
1061
1002
|
# Create value for an item (handles nested models and primitives)
|
|
@@ -1125,13 +1066,10 @@ child_mappings, options)
|
|
|
1125
1066
|
|
|
1126
1067
|
if child_transformation
|
|
1127
1068
|
child_root = child_transformation.transform(value, options)
|
|
1128
|
-
#
|
|
1129
|
-
#
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
# If the nested model serialized to empty hash, return nil
|
|
1133
|
-
# This allows render_nil: false to work correctly for nested models
|
|
1134
|
-
result.nil? || result.empty? ? nil : result
|
|
1069
|
+
# transform emits the content hash directly (TODO.perf 19);
|
|
1070
|
+
# an empty serialization still returns nil so render_nil:
|
|
1071
|
+
# false keeps working for nested models.
|
|
1072
|
+
child_root.empty? ? nil : child_root
|
|
1135
1073
|
else
|
|
1136
1074
|
# Fallback: serialize as primitive
|
|
1137
1075
|
serialize_value(value, rule)
|
|
@@ -1167,15 +1105,12 @@ child_mappings, options)
|
|
|
1167
1105
|
# For each key-value pair in the parsed content, add as a child element
|
|
1168
1106
|
if parsed_content.is_a?(::Hash)
|
|
1169
1107
|
parsed_content.each do |key, val|
|
|
1170
|
-
|
|
1171
|
-
parent.add_child(element)
|
|
1108
|
+
parent[key.to_s] = val
|
|
1172
1109
|
end
|
|
1173
1110
|
elsif parsed_content.is_a?(::Array)
|
|
1174
|
-
#
|
|
1175
|
-
#
|
|
1176
|
-
|
|
1177
|
-
parsed_content)
|
|
1178
|
-
parent.add_child(element)
|
|
1111
|
+
# Graceful handling of unusual map_all content (parity with
|
|
1112
|
+
# the tree's __root__ wrapper)
|
|
1113
|
+
parent["__root__"] = parsed_content
|
|
1179
1114
|
end
|
|
1180
1115
|
end
|
|
1181
1116
|
|
data/lib/lutaml/model/version.rb
CHANGED
|
@@ -5,11 +5,15 @@ require "spec_helper"
|
|
|
5
5
|
RSpec.describe Lutaml::KeyValue::Transformation::ValueSerializer do
|
|
6
6
|
let(:format) { :json }
|
|
7
7
|
let(:register_id) { :default }
|
|
8
|
+
# No doubles (house rule): a real transformation factory. Under
|
|
9
|
+
# hash-direct emission (TODO.perf 19) transform returns the content
|
|
10
|
+
# hash itself.
|
|
8
11
|
let(:transformation_factory) do
|
|
9
|
-
->(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
->(type_class) do
|
|
13
|
+
Lutaml::KeyValue::Transformation.new(
|
|
14
|
+
type_class, type_class.mappings_for(format, register_id),
|
|
15
|
+
format, register_id
|
|
16
|
+
)
|
|
13
17
|
end
|
|
14
18
|
end
|
|
15
19
|
|
|
@@ -106,7 +110,7 @@ RSpec.describe Lutaml::KeyValue::Transformation::ValueSerializer do
|
|
|
106
110
|
end
|
|
107
111
|
|
|
108
112
|
it "validates type mismatch" do
|
|
109
|
-
wrong_value =
|
|
113
|
+
wrong_value = Object.new
|
|
110
114
|
expect do
|
|
111
115
|
serializer.serialize_item(wrong_value, rule)
|
|
112
116
|
end.to raise_error(Lutaml::Model::IncorrectModelError, /but should be/)
|
|
@@ -60,9 +60,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
60
60
|
|
|
61
61
|
result = transformation.transform(model)
|
|
62
62
|
|
|
63
|
-
expect(result).to be_a(
|
|
64
|
-
expect(result.key).to eq("__root__")
|
|
65
|
-
expect(result.children.length).to eq(3)
|
|
63
|
+
expect(result).to be_a(Hash)
|
|
66
64
|
end
|
|
67
65
|
|
|
68
66
|
it "creates proper key-value structure" do
|
|
@@ -71,7 +69,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
71
69
|
transformation = described_class.new(KVSimpleModel, mapping, :json, nil)
|
|
72
70
|
|
|
73
71
|
result = transformation.transform(model)
|
|
74
|
-
hash = result
|
|
72
|
+
hash = { "__root__" => result }
|
|
75
73
|
|
|
76
74
|
# Remove __root__ wrapper
|
|
77
75
|
expect(hash["__root__"]).to eq({
|
|
@@ -87,7 +85,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
87
85
|
transformation = described_class.new(KVSimpleModel, mapping, :json, nil)
|
|
88
86
|
|
|
89
87
|
result = transformation.transform(model)
|
|
90
|
-
hash = result
|
|
88
|
+
hash = { "__root__" => result }
|
|
91
89
|
|
|
92
90
|
# nil values should be rendered by default
|
|
93
91
|
expect(hash["__root__"]["age"]).to be_nil
|
|
@@ -103,7 +101,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
103
101
|
transformation = described_class.new(KVPerson, mapping, :json, nil)
|
|
104
102
|
|
|
105
103
|
result = transformation.transform(person)
|
|
106
|
-
hash = result
|
|
104
|
+
hash = { "__root__" => result }
|
|
107
105
|
|
|
108
106
|
expect(hash["__root__"]).to eq({
|
|
109
107
|
"name" => "John",
|
|
@@ -121,7 +119,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
121
119
|
transformation = described_class.new(KVPerson, mapping, :json, nil)
|
|
122
120
|
|
|
123
121
|
result = transformation.transform(person)
|
|
124
|
-
hash = result
|
|
122
|
+
hash = { "__root__" => result }
|
|
125
123
|
|
|
126
124
|
# nil nested model should be in output (based on default render_nil)
|
|
127
125
|
expect(hash["__root__"]["address"]).to be_nil
|
|
@@ -137,7 +135,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
137
135
|
transformation = described_class.new(KVTeam, mapping, :json, nil)
|
|
138
136
|
|
|
139
137
|
result = transformation.transform(team)
|
|
140
|
-
hash = result
|
|
138
|
+
hash = { "__root__" => result }
|
|
141
139
|
|
|
142
140
|
expect(hash["__root__"]).to eq({
|
|
143
141
|
"name" => "Dev Team",
|
|
@@ -152,7 +150,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
152
150
|
transformation = described_class.new(KVTeam, mapping, :json, nil)
|
|
153
151
|
|
|
154
152
|
result = transformation.transform(team)
|
|
155
|
-
hash = result
|
|
153
|
+
hash = { "__root__" => result }
|
|
156
154
|
|
|
157
155
|
# Empty collections are not rendered by default (render_empty defaults to false)
|
|
158
156
|
expect(hash["__root__"]["members"]).to be_nil
|
|
@@ -164,7 +162,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
164
162
|
transformation = described_class.new(KVTeam, mapping, :json, nil)
|
|
165
163
|
|
|
166
164
|
result = transformation.transform(team)
|
|
167
|
-
hash = result
|
|
165
|
+
hash = { "__root__" => result }
|
|
168
166
|
|
|
169
167
|
# nil collection should be nil by default
|
|
170
168
|
expect(hash["__root__"]["members"]).to be_nil
|
|
@@ -190,7 +188,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
190
188
|
nil)
|
|
191
189
|
|
|
192
190
|
result = transformation.transform(model)
|
|
193
|
-
hash = result
|
|
191
|
+
hash = { "__root__" => result }
|
|
194
192
|
|
|
195
193
|
expect(hash["__root__"]["name"]).to eq("JOHN")
|
|
196
194
|
end
|
|
@@ -213,7 +211,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
213
211
|
transformation = described_class.new(KVRenderModel, mapping, :json, nil)
|
|
214
212
|
|
|
215
213
|
result = transformation.transform(model)
|
|
216
|
-
hash = result
|
|
214
|
+
hash = { "__root__" => result }
|
|
217
215
|
|
|
218
216
|
# optional should be omitted
|
|
219
217
|
expect(hash["__root__"].keys).not_to include("optional")
|
|
@@ -225,7 +223,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
225
223
|
transformation = described_class.new(KVRenderModel, mapping, :json, nil)
|
|
226
224
|
|
|
227
225
|
result = transformation.transform(model)
|
|
228
|
-
hash = result
|
|
226
|
+
hash = { "__root__" => result }
|
|
229
227
|
|
|
230
228
|
# defaulted should be omitted
|
|
231
229
|
expect(hash["__root__"].keys).not_to include("defaulted")
|
|
@@ -234,22 +232,22 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
234
232
|
end
|
|
235
233
|
|
|
236
234
|
describe "architecture compliance" do
|
|
237
|
-
it "
|
|
235
|
+
it "emits the presentation hash directly (TODO.perf 19)" do
|
|
238
236
|
model = KVSimpleModel.new(name: "John", age: 30, active: true)
|
|
239
237
|
mapping = KVSimpleModel.mappings_for(:json)
|
|
240
238
|
transformation = described_class.new(KVSimpleModel, mapping, :json, nil)
|
|
241
239
|
|
|
242
240
|
result = transformation.transform(model)
|
|
243
241
|
|
|
244
|
-
#
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
expect(result.children).to all(be_a(Lutaml::KeyValue::DataModel::Element))
|
|
242
|
+
# Hash-direct emission: the __root__ Element tree (write-only in
|
|
243
|
+
# production, immediately flattened by every consumer) is gone;
|
|
244
|
+
# transform produces the output hash itself.
|
|
245
|
+
expect(result).to be_a(Hash)
|
|
246
|
+
expect(result).to eq("name" => "John", "age" => 30, "active" => true)
|
|
250
247
|
end
|
|
251
248
|
|
|
252
249
|
it "separates content (KeyValueElement) from presentation (Hash)" do
|
|
250
|
+
skip "superseded by hash-direct emission (TODO.perf 19)"
|
|
253
251
|
model = KVSimpleModel.new(name: "John", age: 30, active: true)
|
|
254
252
|
mapping = KVSimpleModel.mappings_for(:json)
|
|
255
253
|
transformation = described_class.new(KVSimpleModel, mapping, :json, nil)
|
|
@@ -259,7 +257,7 @@ RSpec.describe Lutaml::KeyValue::Transformation do
|
|
|
259
257
|
expect(result).to be_a(Lutaml::KeyValue::DataModel::Element)
|
|
260
258
|
|
|
261
259
|
# Presentation happens via to_hash, not during transformation
|
|
262
|
-
hash = result
|
|
260
|
+
hash = { "__root__" => result }
|
|
263
261
|
expect(hash).to be_a(Hash)
|
|
264
262
|
end
|
|
265
263
|
end
|
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.
|
|
4
|
+
version: 0.8.74
|
|
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-
|
|
11
|
+
date: 2026-09-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|