lutaml-model 0.8.17 → 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/.gitmodules +6 -0
- data/.rubocop_todo.yml +8 -0
- 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-mapping.adoc +21 -0
- data/docs/_pages/validation.adoc +65 -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/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/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 +18 -0
- data/lib/lutaml/toml.rb +1 -1
- data/lib/lutaml/xml/adapter.rb +1 -1
- 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 +21 -8
- 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/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/model/attribute_apply_value_map_spec.rb +134 -0
- data/spec/lutaml/model/attribute_spec.rb +169 -12
- data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
- data/spec/lutaml/model/collection_spec.rb +18 -0
- data/spec/lutaml/model/mixed_content_spec.rb +171 -0
- data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
- data/spec/lutaml/model/ordered_content_spec.rb +30 -0
- 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/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/xml/opal_xml_spec.rb +22 -20
- 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/validate_xml_with_constructs_spec.rb +222 -0
- data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
- data/spec/spec_helper.rb +28 -11
- data/spec/support/opal.rb +5 -2
- metadata +97 -31
- 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
|
@@ -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
|