lutaml-model 0.8.16 → 0.8.18
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/.github/workflows/downstream-performance.yml +27 -1
- data/.github/workflows/js-pr-check.yml +102 -0
- data/.github/workflows/js-sync.yml +51 -0
- data/.github/workflows/opal.yml +26 -6
- data/.github/workflows/performance.yml +32 -7
- data/.github/workflows/rake.yml +78 -32
- data/.github/workflows/release.yml +0 -3
- data/.gitmodules +6 -0
- data/.rubocop_todo.yml +76 -12
- data/Gemfile +16 -1
- data/README.adoc +267 -0
- data/Rakefile +166 -4
- data/docs/_guides/schema-import.adoc +42 -0
- data/docs/_guides/xml/namespace-semantics.adoc +2 -0
- data/docs/_guides/xml-mapping.adoc +21 -0
- data/docs/_guides/xml-namespace-qualification.adoc +142 -0
- data/docs/_pages/validation.adoc +65 -0
- data/docs/_tutorials/xml-element-attribute-namespace-guide.adoc +2 -0
- data/docs/_tutorials/xml-schema-primer-style-guide.adoc +2 -0
- data/docs/namespace-management.adoc +2 -0
- data/docs/xml-schema-qualification.md +6 -0
- data/lib/compat/opal/generate_boot.rb +123 -0
- data/lib/compat/opal/js_bundle_entry.rb +42 -0
- data/lib/compat/opal/lutaml_model_boot.rb +497 -0
- data/lib/compat/opal/moxml_boot.rb +68 -0
- data/lib/compat/opal/yaml_compat.rb +32 -0
- data/lib/lutaml/json.rb +1 -1
- data/lib/lutaml/jsonld.rb +1 -4
- data/lib/lutaml/key_value/transform.rb +3 -8
- data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
- data/lib/lutaml/key_value/transformation.rb +17 -5
- data/lib/lutaml/model/adapter_resolver.rb +2 -2
- data/lib/lutaml/model/attribute.rb +88 -11
- data/lib/lutaml/model/cached_type_resolver.rb +1 -1
- data/lib/lutaml/model/choice.rb +34 -0
- data/lib/lutaml/model/compiled_rule.rb +7 -0
- data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
- data/lib/lutaml/model/global_context.rb +11 -8
- data/lib/lutaml/model/instrumentation.rb +2 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
- data/lib/lutaml/model/register.rb +2 -2
- data/lib/lutaml/model/runtime_compatibility.rb +50 -0
- data/lib/lutaml/model/schema/class_loader.rb +58 -0
- data/lib/lutaml/model/schema/compiled_output.rb +83 -0
- data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
- data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
- data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
- data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
- data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
- data/lib/lutaml/model/schema/definitions/model.rb +40 -0
- data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
- data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
- data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
- data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
- data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
- data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
- data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
- data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
- data/lib/lutaml/model/schema/definitions.rb +24 -0
- data/lib/lutaml/model/schema/file_writer.rb +70 -0
- data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
- data/lib/lutaml/model/schema/generator/property.rb +21 -4
- data/lib/lutaml/model/schema/module_nesting.rb +31 -0
- data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
- data/lib/lutaml/model/schema/registry_generator.rb +115 -0
- data/lib/lutaml/model/schema/renderers/base.rb +40 -0
- data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
- data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
- data/lib/lutaml/model/schema/renderers/model.rb +121 -0
- data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
- data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
- data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
- data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
- data/lib/lutaml/model/schema/renderers/union.rb +83 -0
- data/lib/lutaml/model/schema/renderers.rb +19 -0
- data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
- data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
- data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
- data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
- data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
- data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
- data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
- data/lib/lutaml/model/schema/templates.rb +162 -0
- data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
- data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
- data/lib/lutaml/model/schema.rb +19 -0
- data/lib/lutaml/model/serialize/attribute_definition.rb +14 -2
- data/lib/lutaml/model/serialize/builder.rb +15 -2
- data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
- data/lib/lutaml/model/serialize.rb +6 -9
- data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
- data/lib/lutaml/model/services.rb +0 -2
- data/lib/lutaml/model/store.rb +2 -1
- data/lib/lutaml/model/toml.rb +1 -1
- data/lib/lutaml/model/transform.rb +17 -39
- data/lib/lutaml/model/transformation_registry.rb +1 -1
- data/lib/lutaml/model/type.rb +1 -0
- data/lib/lutaml/model/union.rb +320 -0
- data/lib/lutaml/model/utils.rb +6 -0
- data/lib/lutaml/model/validation.rb +1 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +19 -0
- data/lib/lutaml/{jsonld → rdf}/context.rb +1 -1
- data/lib/lutaml/{jsonld/transform.rb → rdf/linked_data_transform.rb} +33 -35
- data/lib/lutaml/{jsonld → rdf}/term_definition.rb +1 -1
- data/lib/lutaml/rdf.rb +3 -0
- data/lib/lutaml/toml.rb +1 -1
- data/lib/lutaml/xml/adapter.rb +1 -1
- data/lib/lutaml/xml/adapter_element.rb +2 -2
- data/lib/lutaml/xml/adapter_loader.rb +1 -1
- data/lib/lutaml/xml/builder/base.rb +1 -1
- data/lib/lutaml/xml/builder.rb +1 -1
- data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
- data/lib/lutaml/xml/model_transform.rb +5 -1
- data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
- data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
- data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
- data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
- data/lib/lutaml/xml/transformation/element_builder.rb +59 -31
- data/lib/lutaml/xml/transformation/ordered_applier.rb +6 -0
- data/lib/lutaml/xml/xsd_validator.rb +66 -0
- data/lib/lutaml/xml.rb +53 -12
- data/lib/lutaml/yamlld/adapter.rb +25 -0
- data/lib/lutaml/yamlld.rb +25 -0
- data/lutaml-model.gemspec +4 -2
- data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
- data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
- data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
- data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
- data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
- data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
- data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
- data/spec/fixtures/xml/schema/rng/book.rng +27 -0
- data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
- data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
- data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
- data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
- data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
- data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
- data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
- data/spec/fixtures/xml/schema/rng/person.rng +23 -0
- data/spec/fixtures/xml/schema/rng/union.rng +14 -0
- data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
- data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
- data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
- data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
- data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
- data/spec/lutaml/integration/multi_format_spec.rb +23 -0
- data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
- data/spec/lutaml/model/attribute_spec.rb +177 -13
- data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
- data/spec/lutaml/model/choice_restrict_spec.rb +225 -0
- data/spec/lutaml/model/collection_spec.rb +18 -0
- data/spec/lutaml/model/custom_model_spec.rb +4 -4
- data/spec/lutaml/model/mixed_content_spec.rb +173 -2
- data/spec/lutaml/model/multiple_mapping_spec.rb +4 -4
- data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
- data/spec/lutaml/model/ordered_content_spec.rb +33 -3
- data/spec/lutaml/model/raw_element_spec.rb +125 -0
- data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
- data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
- data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
- data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
- data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
- data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
- data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
- data/spec/lutaml/model/uninitialized_class_spec.rb +1 -1
- data/spec/lutaml/model/union_attribute_spec.rb +658 -0
- data/spec/lutaml/model/union_spec.rb +257 -0
- data/spec/lutaml/model/utils_spec.rb +14 -0
- data/spec/lutaml/model/xsd_form_default_patterns_spec.rb +2 -2
- data/spec/lutaml/model/xsd_patterns_spec.rb +4 -4
- data/spec/lutaml/{jsonld → rdf}/context_spec.rb +2 -2
- data/spec/lutaml/{jsonld/transform_spec.rb → rdf/linked_data_transform_spec.rb} +15 -1
- data/spec/lutaml/{jsonld → rdf}/term_definition_spec.rb +2 -2
- data/spec/lutaml/turtle/transform_spec.rb +2 -2
- data/spec/lutaml/xml/enhanced_mapping_spec.rb +230 -1
- data/spec/lutaml/xml/mapping_spec.rb +4 -4
- data/spec/lutaml/xml/namespace_placement_spec.rb +11 -8
- data/spec/lutaml/xml/namespace_three_phase_spec.rb +1 -1
- data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
- data/spec/lutaml/xml/prefix_control_spec.rb +4 -4
- data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
- data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
- data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
- data/spec/lutaml/xml/serializable_namespace_spec.rb +6 -6
- data/spec/lutaml/xml/type_namespace_examples_spec.rb +1 -1
- data/spec/lutaml/xml/type_namespace_integration_spec.rb +3 -3
- data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
- data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
- data/spec/lutaml/yamlld/adapter_spec.rb +56 -0
- data/spec/lutaml/yamlld/registration_spec.rb +33 -0
- data/spec/spec_helper.rb +28 -11
- data/spec/support/opal.rb +5 -2
- metadata +108 -37
- data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
- data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
- data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
- data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
- data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
- data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
- data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
- data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
- data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
- data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
- data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
- data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
- data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
- data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
- data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
- data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
- data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
- data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
- data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
- data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
- data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
- data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
- data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
- data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
- data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
- data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
|
@@ -87,6 +87,50 @@ module RawElementSpec
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
+
# mixed_content parent with a collection raw: :element mapping.
|
|
91
|
+
# Round-trip dispatches through OrderedApplier#apply_element_rule_single
|
|
92
|
+
# (see issue #727 — previously double-wrapped each fragment).
|
|
93
|
+
class MixedContentRawCollectionContainer < Lutaml::Model::Serializable
|
|
94
|
+
attribute :name, :string
|
|
95
|
+
attribute :fragments, :string, collection: true
|
|
96
|
+
|
|
97
|
+
xml do
|
|
98
|
+
element "container"
|
|
99
|
+
mixed_content
|
|
100
|
+
map_element "name", to: :name
|
|
101
|
+
map_element "fragment", to: :fragments, raw: :element
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# mixed_content parent with a single (non-collection) raw: :element mapping.
|
|
106
|
+
# Dispatches through OrderedApplier -> :apply_rule -> RuleApplier#apply_rule,
|
|
107
|
+
# which already had the short-circuit. Kept as a regression guard.
|
|
108
|
+
class MixedContentRawSingleContainer < Lutaml::Model::Serializable
|
|
109
|
+
attribute :name, :string
|
|
110
|
+
attribute :embedded, :string
|
|
111
|
+
|
|
112
|
+
xml do
|
|
113
|
+
element "container"
|
|
114
|
+
mixed_content
|
|
115
|
+
map_element "name", to: :name
|
|
116
|
+
map_element "foreign", to: :embedded, raw: :element
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# ordered (no mixed_content) parent with a collection raw: :element mapping.
|
|
121
|
+
# Same dispatch path as mixed_content; same bug class.
|
|
122
|
+
class OrderedRawCollectionContainer < Lutaml::Model::Serializable
|
|
123
|
+
attribute :name, :string
|
|
124
|
+
attribute :fragments, :string, collection: true
|
|
125
|
+
|
|
126
|
+
xml do
|
|
127
|
+
element "container"
|
|
128
|
+
ordered
|
|
129
|
+
map_element "name", to: :name
|
|
130
|
+
map_element "fragment", to: :fragments, raw: :element
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
90
134
|
RSpec.describe "map_element raw: :element" do
|
|
91
135
|
describe "deserialization" do
|
|
92
136
|
describe "foreign namespace element capture" do
|
|
@@ -378,6 +422,87 @@ module RawElementSpec
|
|
|
378
422
|
end
|
|
379
423
|
end
|
|
380
424
|
|
|
425
|
+
# Regression for issue #727: OrderedApplier#apply_element_rule_single
|
|
426
|
+
# was missing the raw: :element short-circuit, so each captured fragment
|
|
427
|
+
# string (which already contains its own <fragment> wrapper) got wrapped
|
|
428
|
+
# again, producing <fragment><fragment>...</fragment></fragment>.
|
|
429
|
+
describe "round-trip with mixed_content + raw: :element collection" do
|
|
430
|
+
let(:mixed_collection_xml) do
|
|
431
|
+
<<~XML
|
|
432
|
+
<container>
|
|
433
|
+
before
|
|
434
|
+
<name>multi</name>
|
|
435
|
+
<fragment id="1">first</fragment>
|
|
436
|
+
<fragment id="2"><nested>second</nested></fragment>
|
|
437
|
+
after
|
|
438
|
+
</container>
|
|
439
|
+
XML
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
it "does not double-wrap fragments on round-trip" do
|
|
443
|
+
doc = MixedContentRawCollectionContainer.from_xml(mixed_collection_xml)
|
|
444
|
+
output = doc.to_xml
|
|
445
|
+
|
|
446
|
+
expect(output).not_to include("<fragment><fragment")
|
|
447
|
+
expect(output).to include('id="1"')
|
|
448
|
+
expect(output).to include("first")
|
|
449
|
+
expect(output).to include('id="2"')
|
|
450
|
+
expect(output).to include("<nested>second</nested>")
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
it "is stable across repeated round-trips" do
|
|
454
|
+
doc = MixedContentRawCollectionContainer.from_xml(mixed_collection_xml)
|
|
455
|
+
once = MixedContentRawCollectionContainer.from_xml(doc.to_xml)
|
|
456
|
+
twice = MixedContentRawCollectionContainer.from_xml(once.to_xml)
|
|
457
|
+
|
|
458
|
+
expect(twice.fragments.length).to eq(2)
|
|
459
|
+
expect(twice.fragments[0]).to include("first")
|
|
460
|
+
expect(twice.fragments[1]).to include("<nested>second</nested>")
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Regression guard: the non-collection path under mixed_content dispatches
|
|
465
|
+
# through :apply_rule -> RuleApplier#apply_rule, which already had the
|
|
466
|
+
# short-circuit. Lock in the working behavior.
|
|
467
|
+
describe "round-trip with mixed_content + raw: :element single" do
|
|
468
|
+
let(:mixed_single_xml) do
|
|
469
|
+
'<container>before<name>x</name><foreign attr="v"><child>c</child></foreign>after</container>'
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
it "emits the foreign element verbatim without wrapping" do
|
|
473
|
+
doc = MixedContentRawSingleContainer.from_xml(mixed_single_xml)
|
|
474
|
+
output = doc.to_xml
|
|
475
|
+
|
|
476
|
+
expect(output).not_to include("<foreign><foreign")
|
|
477
|
+
expect(output).to include('attr="v"')
|
|
478
|
+
expect(output).to include("<child>c</child>")
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Same dispatch path (OrderedApplier) as mixed_content; same bug class.
|
|
483
|
+
describe "round-trip with ordered + raw: :element collection" do
|
|
484
|
+
let(:ordered_collection_xml) do
|
|
485
|
+
<<~XML
|
|
486
|
+
<container>
|
|
487
|
+
<name>multi</name>
|
|
488
|
+
<fragment id="1">first</fragment>
|
|
489
|
+
<fragment id="2">second</fragment>
|
|
490
|
+
</container>
|
|
491
|
+
XML
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
it "does not double-wrap fragments on round-trip" do
|
|
495
|
+
doc = OrderedRawCollectionContainer.from_xml(ordered_collection_xml)
|
|
496
|
+
output = doc.to_xml
|
|
497
|
+
|
|
498
|
+
expect(output).not_to include("<fragment><fragment")
|
|
499
|
+
expect(output).to include('id="1"')
|
|
500
|
+
expect(output).to include("first")
|
|
501
|
+
expect(output).to include('id="2"')
|
|
502
|
+
expect(output).to include("second")
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
|
|
381
506
|
describe "nil and empty values" do
|
|
382
507
|
it "omits raw element when value is nil" do
|
|
383
508
|
model = SvgContainer.new(svg_data: nil)
|
|
@@ -9,19 +9,10 @@ RSpec.describe Lutaml::Model::RuleValueExtractor do
|
|
|
9
9
|
let(:rule) { instance_double(Lutaml::Model::Jsonl::MappingRule) }
|
|
10
10
|
let(:doc) { { "name" => "Test", "value" => 123 } }
|
|
11
11
|
let(:format) { :json }
|
|
12
|
-
let(:attr) {
|
|
13
|
-
let(:register) {
|
|
12
|
+
let(:attr) { Lutaml::Model::Attribute.new("name", :string) }
|
|
13
|
+
let(:register) { Lutaml::Model::Config.default_register }
|
|
14
14
|
let(:options) { {} }
|
|
15
15
|
|
|
16
|
-
def mock_resolver(default_set_value, default_value_data = nil)
|
|
17
|
-
resolver_double = instance_double(Lutaml::Model::Services::DefaultValueResolver,
|
|
18
|
-
default_set?: default_set_value,
|
|
19
|
-
default_value: default_value_data)
|
|
20
|
-
allow(Lutaml::Model::Services::DefaultValueResolver).to receive(:new)
|
|
21
|
-
.with(attr, register, instance)
|
|
22
|
-
.and_return(resolver_double)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
16
|
describe "#call" do
|
|
26
17
|
context "when rule has single mapping" do
|
|
27
18
|
before do
|
|
@@ -82,7 +73,7 @@ RSpec.describe Lutaml::Model::RuleValueExtractor do
|
|
|
82
73
|
end
|
|
83
74
|
end
|
|
84
75
|
|
|
85
|
-
context "when value is not found" do
|
|
76
|
+
context "when value is not found and attribute has no default" do
|
|
86
77
|
before do
|
|
87
78
|
allow(rule).to receive_messages(
|
|
88
79
|
multiple_mappings?: false,
|
|
@@ -90,7 +81,6 @@ RSpec.describe Lutaml::Model::RuleValueExtractor do
|
|
|
90
81
|
root_mapping?: false,
|
|
91
82
|
raw_mapping?: false,
|
|
92
83
|
)
|
|
93
|
-
mock_resolver(false)
|
|
94
84
|
end
|
|
95
85
|
|
|
96
86
|
it "returns uninitialized value" do
|
|
@@ -99,6 +89,10 @@ RSpec.describe Lutaml::Model::RuleValueExtractor do
|
|
|
99
89
|
end
|
|
100
90
|
|
|
101
91
|
context "when attribute has default value" do
|
|
92
|
+
let(:attr) do
|
|
93
|
+
Lutaml::Model::Attribute.new("name", :string, default: "default_value")
|
|
94
|
+
end
|
|
95
|
+
|
|
102
96
|
before do
|
|
103
97
|
allow(rule).to receive_messages(
|
|
104
98
|
multiple_mappings?: false,
|
|
@@ -106,7 +100,6 @@ RSpec.describe Lutaml::Model::RuleValueExtractor do
|
|
|
106
100
|
root_mapping?: false,
|
|
107
101
|
raw_mapping?: false,
|
|
108
102
|
)
|
|
109
|
-
mock_resolver(true, "default_value")
|
|
110
103
|
end
|
|
111
104
|
|
|
112
105
|
it "returns default value" do
|
|
@@ -33,4 +33,29 @@ RSpec.describe Lutaml::Model::RuntimeCompatibility do
|
|
|
33
33
|
.to eq(error_class)
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
# lib/lutaml/model.rb and lib/lutaml/xml.rb re-evaluate their top-level
|
|
38
|
+
# prepend calls when Opal's eager loader processes them more than once.
|
|
39
|
+
# Opal's Module#prepend raises "Prepending a module multiple times is
|
|
40
|
+
# not supported" in that case; runtime_compatibility.rb aligns Opal
|
|
41
|
+
# with MRI's idempotent behavior. MRI has been idempotent since 2.0,
|
|
42
|
+
# so the spec applies to both runtimes.
|
|
43
|
+
describe "Module#prepend idempotency" do
|
|
44
|
+
it "is a no-op when the module is already in the ancestor chain" do
|
|
45
|
+
mod = Module.new
|
|
46
|
+
klass = Class.new
|
|
47
|
+
klass.prepend(mod)
|
|
48
|
+
|
|
49
|
+
expect { klass.prepend(mod) }.not_to raise_error
|
|
50
|
+
expect(klass.ancestors.count(mod)).to eq(1)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "still prepends when the module is not yet present" do
|
|
54
|
+
mod = Module.new
|
|
55
|
+
klass = Class.new
|
|
56
|
+
|
|
57
|
+
expect { klass.prepend(mod) }.not_to raise_error
|
|
58
|
+
expect(klass.ancestors).to include(mod)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
36
61
|
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "lutaml/model/schema/definitions"
|
|
5
|
+
require "lutaml/model/schema/renderers/model"
|
|
6
|
+
|
|
7
|
+
RSpec.describe Lutaml::Model::Schema::Renderers::Model do
|
|
8
|
+
let(:defs) { Lutaml::Model::Schema::Definitions }
|
|
9
|
+
|
|
10
|
+
it "renders a rooted element with one element attribute" do
|
|
11
|
+
spec = defs::Model.new(
|
|
12
|
+
class_name: "Person",
|
|
13
|
+
xml_root: defs::XmlRoot.new(kind: :element, name: "Person"),
|
|
14
|
+
members: [
|
|
15
|
+
defs::Attribute.new(
|
|
16
|
+
name: "name",
|
|
17
|
+
type: defs::TypeRef.new(kind: :symbol, value: "string"),
|
|
18
|
+
xml_name: "name",
|
|
19
|
+
kind: :element,
|
|
20
|
+
),
|
|
21
|
+
],
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
output = described_class.render(spec)
|
|
25
|
+
|
|
26
|
+
expect(output).to include("class Person < Lutaml::Model::Serializable")
|
|
27
|
+
expect(output).to include("attribute :name, :string")
|
|
28
|
+
expect(output).to include("xml do")
|
|
29
|
+
expect(output).to include('element "Person"')
|
|
30
|
+
expect(output).to include('map_element "name", to: :name')
|
|
31
|
+
expect(output).to include("def self.register")
|
|
32
|
+
expect(output).to include("Person.register_class_with_id")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "renders a fragment (no element / type_name directive)" do
|
|
36
|
+
spec = defs::Model.new(
|
|
37
|
+
class_name: "Inner",
|
|
38
|
+
xml_root: defs::XmlRoot.new(kind: :fragment, name: nil),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
output = described_class.render(spec)
|
|
42
|
+
|
|
43
|
+
expect(output).not_to include("element ")
|
|
44
|
+
expect(output).not_to include("type_name ")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "renders an attribute mapping with map_attribute" do
|
|
48
|
+
spec = defs::Model.new(
|
|
49
|
+
class_name: "Item",
|
|
50
|
+
xml_root: defs::XmlRoot.new(kind: :element, name: "Item"),
|
|
51
|
+
members: [
|
|
52
|
+
defs::Attribute.new(
|
|
53
|
+
name: "id",
|
|
54
|
+
type: defs::TypeRef.new(kind: :symbol, value: "string"),
|
|
55
|
+
xml_name: "id",
|
|
56
|
+
kind: :attribute,
|
|
57
|
+
),
|
|
58
|
+
],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
output = described_class.render(spec)
|
|
62
|
+
|
|
63
|
+
expect(output).to include('map_attribute "id", to: :id')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "renders a class reference type with no leading colon" do
|
|
67
|
+
spec = defs::Model.new(
|
|
68
|
+
class_name: "Owner",
|
|
69
|
+
xml_root: defs::XmlRoot.new(kind: :element, name: "Owner"),
|
|
70
|
+
members: [
|
|
71
|
+
defs::Attribute.new(
|
|
72
|
+
name: "address",
|
|
73
|
+
type: defs::TypeRef.new(kind: :class_ref, value: "Address"),
|
|
74
|
+
xml_name: "address",
|
|
75
|
+
kind: :element,
|
|
76
|
+
),
|
|
77
|
+
],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
output = described_class.render(spec)
|
|
81
|
+
|
|
82
|
+
expect(output).to include("attribute :address, Address")
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "renders a w3c type with the ::-prefixed reference" do
|
|
86
|
+
spec = defs::Model.new(
|
|
87
|
+
class_name: "Para",
|
|
88
|
+
xml_root: defs::XmlRoot.new(kind: :element, name: "Para"),
|
|
89
|
+
members: [
|
|
90
|
+
defs::Attribute.new(
|
|
91
|
+
name: "lang",
|
|
92
|
+
type: defs::TypeRef.new(kind: :w3c, value: "Lutaml::Xml::W3c::Lang"),
|
|
93
|
+
xml_name: "lang",
|
|
94
|
+
kind: :attribute,
|
|
95
|
+
),
|
|
96
|
+
],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
output = described_class.render(spec)
|
|
100
|
+
|
|
101
|
+
expect(output).to include("attribute :lang, ::Lutaml::Xml::W3c::Lang")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "skips register methods inside a module_namespace" do
|
|
105
|
+
spec = defs::Model.new(
|
|
106
|
+
class_name: "Inside",
|
|
107
|
+
xml_root: defs::XmlRoot.new(kind: :element, name: "Inside"),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
output = described_class.render(spec, module_namespace: "Mod::Sub")
|
|
111
|
+
|
|
112
|
+
expect(output).to include("module Mod")
|
|
113
|
+
expect(output).to include("module Sub")
|
|
114
|
+
expect(output).not_to include("def self.register")
|
|
115
|
+
expect(output).not_to include("register_class_with_id")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "renders a choice block with attributes inside" do
|
|
119
|
+
spec = defs::Model.new(
|
|
120
|
+
class_name: "Either",
|
|
121
|
+
xml_root: defs::XmlRoot.new(kind: :element, name: "Either"),
|
|
122
|
+
members: [
|
|
123
|
+
defs::Choice.new(
|
|
124
|
+
header: "choice",
|
|
125
|
+
alternatives: [
|
|
126
|
+
defs::Attribute.new(
|
|
127
|
+
name: "alpha",
|
|
128
|
+
type: defs::TypeRef.new(kind: :symbol, value: "string"),
|
|
129
|
+
xml_name: "alpha",
|
|
130
|
+
kind: :element,
|
|
131
|
+
),
|
|
132
|
+
defs::Attribute.new(
|
|
133
|
+
name: "beta",
|
|
134
|
+
type: defs::TypeRef.new(kind: :symbol, value: "string"),
|
|
135
|
+
xml_name: "beta",
|
|
136
|
+
kind: :element,
|
|
137
|
+
),
|
|
138
|
+
],
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
output = described_class.render(spec)
|
|
144
|
+
|
|
145
|
+
expect(output).to include("choice do")
|
|
146
|
+
expect(output).to include("attribute :alpha, :string")
|
|
147
|
+
expect(output).to include("attribute :beta, :string")
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "lutaml/model/schema/definitions"
|
|
5
|
+
require "lutaml/model/schema/renderers/namespace"
|
|
6
|
+
|
|
7
|
+
RSpec.describe Lutaml::Model::Schema::Renderers::Namespace do
|
|
8
|
+
let(:defs) { Lutaml::Model::Schema::Definitions }
|
|
9
|
+
|
|
10
|
+
it "renders a namespace class with uri and prefix_default" do
|
|
11
|
+
spec = defs::Namespace.new(
|
|
12
|
+
class_name: "ExampleNs",
|
|
13
|
+
uri: "http://example.com/ns",
|
|
14
|
+
prefix_default: "ex",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
output = described_class.render(spec)
|
|
18
|
+
|
|
19
|
+
expect(output).to include("class ExampleNs < Lutaml::Xml::W3c::XmlNamespace")
|
|
20
|
+
expect(output).to include(%(uri "http://example.com/ns"))
|
|
21
|
+
expect(output).to include(%(prefix_default "ex"))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "wraps in module when module_namespace is supplied" do
|
|
25
|
+
spec = defs::Namespace.new(
|
|
26
|
+
class_name: "Inner",
|
|
27
|
+
uri: "http://example.com/inner",
|
|
28
|
+
prefix_default: nil,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
output = described_class.render(spec, module_namespace: "Mod::Sub")
|
|
32
|
+
|
|
33
|
+
expect(output).to include("module Mod")
|
|
34
|
+
expect(output).to include("module Sub")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "lutaml/model/schema/definitions"
|
|
5
|
+
require "lutaml/model/schema/renderers/restricted_type"
|
|
6
|
+
|
|
7
|
+
RSpec.describe Lutaml::Model::Schema::Renderers::RestrictedType do
|
|
8
|
+
let(:defs) { Lutaml::Model::Schema::Definitions }
|
|
9
|
+
|
|
10
|
+
it "renders min/max facets in the cast body" do
|
|
11
|
+
spec = defs::RestrictedType.new(
|
|
12
|
+
class_name: "PositiveInt",
|
|
13
|
+
parent_class: "Lutaml::Model::Type::Integer",
|
|
14
|
+
facets: defs::Facet.new(min_inclusive: 1, max_inclusive: 100),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
output = described_class.render(spec)
|
|
18
|
+
|
|
19
|
+
expect(output).to include("class PositiveInt < Lutaml::Model::Type::Integer")
|
|
20
|
+
expect(output).to include("def self.cast(value, options = {})")
|
|
21
|
+
expect(output).to include("options[:min] = 1")
|
|
22
|
+
expect(output).to include("options[:max] = 100")
|
|
23
|
+
expect(output).to include("value = super(value, options)")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "renders enumerations facet" do
|
|
27
|
+
spec = defs::RestrictedType.new(
|
|
28
|
+
class_name: "Color",
|
|
29
|
+
parent_class: "Lutaml::Model::Type::String",
|
|
30
|
+
facets: defs::Facet.new(enumerations: %w[red green blue]),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
output = described_class.render(spec)
|
|
34
|
+
|
|
35
|
+
expect(output).to include('options[:values] = [super("red"), super("green"), super("blue")]')
|
|
36
|
+
expect(output).not_to include("options[:min]")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "renders pattern facet as a regexp literal" do
|
|
40
|
+
spec = defs::RestrictedType.new(
|
|
41
|
+
class_name: "Slug",
|
|
42
|
+
parent_class: "Lutaml::Model::Type::String",
|
|
43
|
+
facets: defs::Facet.new(pattern: "[a-z0-9-]+"),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
output = described_class.render(spec)
|
|
47
|
+
|
|
48
|
+
expect(output).to include("options[:pattern] = %r{[a-z0-9-]+}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "renders transform facet" do
|
|
52
|
+
spec = defs::RestrictedType.new(
|
|
53
|
+
class_name: "Upper",
|
|
54
|
+
parent_class: "Lutaml::Model::Type::String",
|
|
55
|
+
facets: defs::Facet.new,
|
|
56
|
+
transform_facet: defs::TransformFacet.new(expression: "value.upcase"),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
output = described_class.render(spec)
|
|
60
|
+
|
|
61
|
+
expect(output).to include("value = value.upcase")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "uses lazy register memoisation" do
|
|
65
|
+
spec = defs::RestrictedType.new(
|
|
66
|
+
class_name: "Tiny",
|
|
67
|
+
parent_class: "Lutaml::Model::Type::Integer",
|
|
68
|
+
facets: defs::Facet.new(max_inclusive: 5),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
output = described_class.render(spec)
|
|
72
|
+
|
|
73
|
+
expect(output).to include("@register ||= Lutaml::Model::Config.default_register")
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "lutaml/model/schema/definitions"
|
|
5
|
+
require "lutaml/model/schema/renderers/union"
|
|
6
|
+
|
|
7
|
+
RSpec.describe Lutaml::Model::Schema::Renderers::Union do
|
|
8
|
+
let(:defs) { Lutaml::Model::Schema::Definitions }
|
|
9
|
+
|
|
10
|
+
it "renders the :class_refs strategy as an each/begin/rescue loop" do
|
|
11
|
+
spec = defs::UnionType.new(
|
|
12
|
+
class_name: "NumberOrString",
|
|
13
|
+
members: [
|
|
14
|
+
defs::TypeRef.new(kind: :class_ref, value: "Integer"),
|
|
15
|
+
defs::TypeRef.new(kind: :class_ref, value: "String"),
|
|
16
|
+
],
|
|
17
|
+
cast_strategy: :class_refs,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
output = described_class.render(spec)
|
|
21
|
+
|
|
22
|
+
expect(output).to include("class NumberOrString < Lutaml::Model::Type::Value")
|
|
23
|
+
expect(output).to include("[Integer, String].each do |t|")
|
|
24
|
+
expect(output).to include("casted = t.cast(value, options)")
|
|
25
|
+
expect(output).to include("return casted unless casted.nil?")
|
|
26
|
+
expect(output).to include("rescue StandardError")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "renders the :resolve_type strategy as a ||-chained resolve_type call" do
|
|
30
|
+
spec = defs::UnionType.new(
|
|
31
|
+
class_name: "IntegerOrToken",
|
|
32
|
+
members: [
|
|
33
|
+
defs::TypeRef.new(kind: :symbol, value: "integer"),
|
|
34
|
+
defs::TypeRef.new(kind: :symbol, value: "token"),
|
|
35
|
+
],
|
|
36
|
+
cast_strategy: :resolve_type,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
output = described_class.render(spec)
|
|
40
|
+
|
|
41
|
+
expect(output).to include("Lutaml::Model::GlobalContext.resolve_type(:integer, @register).cast(value, options)")
|
|
42
|
+
expect(output).to include("Lutaml::Model::GlobalContext.resolve_type(:token, @register).cast(value, options)")
|
|
43
|
+
expect(output).to include(" ||\n")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "emits required_files lines above the module wrap" do
|
|
47
|
+
spec = defs::UnionType.new(
|
|
48
|
+
class_name: "AnyOf",
|
|
49
|
+
members: [defs::TypeRef.new(kind: :class_ref, value: "Integer")],
|
|
50
|
+
cast_strategy: :class_refs,
|
|
51
|
+
required_files: ['require_relative "integer"'],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
output = described_class.render(spec)
|
|
55
|
+
|
|
56
|
+
expect(output).to include('require_relative "integer"')
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
# Regression: Transform#apply_value_map must handle the nil-attr case used
|
|
4
|
+
# by custom-method-only key-value rules. Pre-consolidation behavior:
|
|
5
|
+
# the default { omitted: :nil, nil: :nil, empty: :empty } map converted
|
|
6
|
+
# UninitializedClass.instance to nil so the downstream Utils.present? guard
|
|
7
|
+
# in KeyValue::Transform#process_mapping_rule would skip the custom method
|
|
8
|
+
# for absent input keys.
|
|
9
|
+
RSpec.describe Lutaml::Model::Transform, "#apply_value_map (nil attr)" do
|
|
10
|
+
let(:transform_subclass) do
|
|
11
|
+
Class.new(described_class) do
|
|
12
|
+
# Expose the protected delegator for direct testing.
|
|
13
|
+
public :apply_value_map
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:context) { Class.new(Lutaml::Model::Serializable) }
|
|
18
|
+
let(:register) { Lutaml::Model::Config.default_register }
|
|
19
|
+
let(:transform) { transform_subclass.new(context, register) }
|
|
20
|
+
|
|
21
|
+
let(:uninit) { Lutaml::Model::UninitializedClass.instance }
|
|
22
|
+
let(:default_vmap) { { omitted: :nil, nil: :nil, empty: :empty } }
|
|
23
|
+
|
|
24
|
+
describe "default mapping with nil attr" do
|
|
25
|
+
it "converts UninitializedClass to nil via the :omitted => :nil mapping" do
|
|
26
|
+
# Before the fix: returned uninit unchanged, causing Utils.present? to
|
|
27
|
+
# be true and the custom method to run for absent fields.
|
|
28
|
+
expect(transform.apply_value_map(uninit, default_vmap, nil)).to be_nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "converts nil to nil via the :nil => :nil mapping" do
|
|
32
|
+
expect(transform.apply_value_map(nil, default_vmap, nil)).to be_nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "passes initialized values through unchanged" do
|
|
36
|
+
expect(transform.apply_value_map("hello", default_vmap, nil)).to eq("hello")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "explicit :omitted => :omitted with nil attr" do
|
|
41
|
+
let(:vmap) { { omitted: :omitted, nil: :nil, empty: :empty } }
|
|
42
|
+
|
|
43
|
+
it "preserves UninitializedClass for explicit omitted: :omitted" do
|
|
44
|
+
expect(transform.apply_value_map(uninit, vmap, nil)).to eq(uninit)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Behavior-preservation: the pre-consolidation attribute-less path mapped the
|
|
49
|
+
# :omitted option to UninitializedClass.instance (present? => true, so the
|
|
50
|
+
# custom method runs), and resolved :nil/:empty consistently for empty inputs.
|
|
51
|
+
describe "non-:nil options with nil attr" do
|
|
52
|
+
it "maps a nil value to uninitialized via nil: :omitted" do
|
|
53
|
+
expect(transform.apply_value_map(nil, { nil: :omitted }, nil)).to eq(uninit)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "maps an empty value to uninitialized via empty: :omitted" do
|
|
57
|
+
expect(transform.apply_value_map("", { empty: :omitted }, nil)).to eq(uninit)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "maps an empty value to nil via empty: :nil" do
|
|
61
|
+
expect(transform.apply_value_map("", { empty: :nil }, nil)).to be_nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -23,7 +23,7 @@ RSpec.describe Lutaml::Model::UninitializedClass do
|
|
|
23
23
|
|
|
24
24
|
describe "#match?" do
|
|
25
25
|
it "returns false for any argument" do
|
|
26
|
-
expect(uninitialized).not_to
|
|
26
|
+
expect(uninitialized).not_to include("pattern")
|
|
27
27
|
expect(uninitialized).not_to match "string"
|
|
28
28
|
expect(uninitialized).not_to match nil
|
|
29
29
|
end
|