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,257 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module UnionSpec
|
|
6
|
+
class WithUnit < Lutaml::Model::Serializable
|
|
7
|
+
attribute :number, :float
|
|
8
|
+
attribute :unit, :string
|
|
9
|
+
|
|
10
|
+
key_value do
|
|
11
|
+
map "number", to: :number
|
|
12
|
+
map "unit", to: :unit
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Celsius < Lutaml::Model::Serializable
|
|
17
|
+
attribute :celsius, :float
|
|
18
|
+
|
|
19
|
+
key_value do
|
|
20
|
+
map "celsius", to: :celsius
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Member declaring an aliased key (two names for one attribute).
|
|
25
|
+
class AliasedName < Lutaml::Model::Serializable
|
|
26
|
+
attribute :name, :string
|
|
27
|
+
|
|
28
|
+
key_value do
|
|
29
|
+
map %w[name product_name], to: :name
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
RSpec.describe Lutaml::Model::Type::Union do
|
|
35
|
+
let(:int_str) do
|
|
36
|
+
[Lutaml::Model::Type::Integer, Lutaml::Model::Type::String]
|
|
37
|
+
end
|
|
38
|
+
let(:int_float) do
|
|
39
|
+
[Lutaml::Model::Type::Integer, Lutaml::Model::Type::Float]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def member_for(value, members, format: nil)
|
|
43
|
+
result = described_class.conforming_member(value, members,
|
|
44
|
+
format: format, register: nil)
|
|
45
|
+
result&.first
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe ".conforming_member (scalars)" do
|
|
49
|
+
it "matches Integer for an integer-lexical string" do
|
|
50
|
+
expect(member_for("42", int_str)).to eq(Lutaml::Model::Type::Integer)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "falls through to String when Integer rejects" do
|
|
54
|
+
expect(member_for("hello", int_str)).to eq(Lutaml::Model::Type::String)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "rejects a float-string for Integer (sound predicate, not Integer('3.7'))" do
|
|
58
|
+
expect(member_for("3.7", int_float)).to eq(Lutaml::Model::Type::Float)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "accepts a numeric integer string for the Integer member first" do
|
|
62
|
+
expect(member_for("1300", int_float)).to eq(Lutaml::Model::Type::Integer)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "matches a native Integer value" do
|
|
66
|
+
expect(member_for(42, int_str)).to eq(Lutaml::Model::Type::Integer)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "matches a native Float value to the Float member" do
|
|
70
|
+
expect(member_for(3.7, int_float)).to eq(Lutaml::Model::Type::Float)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "returns nil when no scalar member conforms" do
|
|
74
|
+
expect(member_for("abc", int_float)).to be_nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "rejects an Integer that Float cannot represent exactly" do
|
|
78
|
+
expect(member_for((2**60) + 1, [Lutaml::Model::Type::Float])).to be_nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "widens a Float-representable Integer into Float" do
|
|
82
|
+
member, casted = described_class.conforming_member(
|
|
83
|
+
2**60, [Lutaml::Model::Type::Float], format: nil, register: nil
|
|
84
|
+
)
|
|
85
|
+
expect([member, casted]).to eq([Lutaml::Model::Type::Float, (2**60).to_f])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "casts the value to the matched member" do
|
|
89
|
+
member, casted = described_class.conforming_member(
|
|
90
|
+
"42", int_str, format: nil, register: nil
|
|
91
|
+
)
|
|
92
|
+
expect([member, casted]).to eq([Lutaml::Model::Type::Integer, 42])
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe ".conforming_member (boolean lexical space)" do
|
|
97
|
+
let(:bool_str) do
|
|
98
|
+
[Lutaml::Model::Type::Boolean, Lutaml::Model::Type::String]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "matches a true/false word to Boolean" do
|
|
102
|
+
expect(member_for("true", bool_str))
|
|
103
|
+
.to eq(Lutaml::Model::Type::Boolean)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "falls through to String for a non-boolean word" do
|
|
107
|
+
expect(member_for("maybe", bool_str))
|
|
108
|
+
.to eq(Lutaml::Model::Type::String)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe ".conforming_member (models by key-coverage)" do
|
|
113
|
+
let(:members) { [UnionSpec::WithUnit, UnionSpec::Celsius] }
|
|
114
|
+
|
|
115
|
+
it "selects the model whose fields cover the input keys" do
|
|
116
|
+
member, = described_class.conforming_member(
|
|
117
|
+
{ "number" => 1300.0, "unit" => "C" }, members,
|
|
118
|
+
format: :yaml, register: nil
|
|
119
|
+
)
|
|
120
|
+
expect(member).to eq(UnionSpec::WithUnit)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "selects the disjoint-key model" do
|
|
124
|
+
member, = described_class.conforming_member(
|
|
125
|
+
{ "celsius" => 1200.0 }, members, format: :yaml, register: nil
|
|
126
|
+
)
|
|
127
|
+
expect(member).to eq(UnionSpec::Celsius)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it "deserializes the selected model member" do
|
|
131
|
+
_, value = described_class.conforming_member(
|
|
132
|
+
{ "celsius" => 1200.0 }, members, format: :yaml, register: nil
|
|
133
|
+
)
|
|
134
|
+
expect(value).to be_a(UnionSpec::Celsius)
|
|
135
|
+
expect(value.celsius).to eq(1200.0)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "returns nil when no model covers the keys" do
|
|
139
|
+
result = described_class.conforming_member(
|
|
140
|
+
{ "unknown" => 1 }, members, format: :yaml, register: nil
|
|
141
|
+
)
|
|
142
|
+
expect(result).to be_nil
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "selects a member matched through an aliased key" do
|
|
146
|
+
member, value = described_class.conforming_member(
|
|
147
|
+
{ "product_name" => "Vase" }, [UnionSpec::AliasedName],
|
|
148
|
+
format: :yaml, register: nil
|
|
149
|
+
)
|
|
150
|
+
expect(member).to eq(UnionSpec::AliasedName)
|
|
151
|
+
expect(value.name).to eq("Vase")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "returns an existing member instance unchanged (idempotent)" do
|
|
155
|
+
instance = UnionSpec::Celsius.new(celsius: 1200.0)
|
|
156
|
+
member, value = described_class.conforming_member(
|
|
157
|
+
instance, members, format: :yaml, register: nil
|
|
158
|
+
)
|
|
159
|
+
expect(member).to eq(UnionSpec::Celsius)
|
|
160
|
+
expect(value).to be(instance)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe "nil passthrough" do
|
|
165
|
+
it "returns nil for nil input" do
|
|
166
|
+
expect(member_for(nil, int_str)).to be_nil
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe ".validate_members!" do
|
|
171
|
+
it "raises on an empty member list" do
|
|
172
|
+
expect { described_class.validate_members!([]) }
|
|
173
|
+
.to raise_error(ArgumentError)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "raises when a member is not a valid type" do
|
|
177
|
+
expect { described_class.validate_members!([Object]) }
|
|
178
|
+
.to raise_error(ArgumentError)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it "raises when a catch-all is not last" do
|
|
182
|
+
expect do
|
|
183
|
+
described_class.validate_members!(
|
|
184
|
+
[Lutaml::Model::Type::String, Lutaml::Model::Type::Integer],
|
|
185
|
+
)
|
|
186
|
+
end.to raise_error(ArgumentError)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "accepts a valid ordered member list" do
|
|
190
|
+
expect do
|
|
191
|
+
described_class.validate_members!(
|
|
192
|
+
[Lutaml::Model::Type::Integer, Lutaml::Model::Type::String],
|
|
193
|
+
)
|
|
194
|
+
end.not_to raise_error
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
describe ".validate_combo!" do
|
|
199
|
+
it "raises when combined with polymorphic" do
|
|
200
|
+
expect { described_class.validate_combo!(polymorphic: {}) }
|
|
201
|
+
.to raise_error(ArgumentError)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "raises when combined with raw" do
|
|
205
|
+
expect { described_class.validate_combo!(raw: true) }
|
|
206
|
+
.to raise_error(ArgumentError)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
it "accepts a plain options hash" do
|
|
210
|
+
expect { described_class.validate_combo!(collection: true) }
|
|
211
|
+
.not_to raise_error
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
describe "schema export" do
|
|
216
|
+
let(:klass) do
|
|
217
|
+
Class.new(Lutaml::Model::Serializable) do
|
|
218
|
+
attribute :v,
|
|
219
|
+
[Lutaml::Model::Type::Integer, Lutaml::Model::Type::String]
|
|
220
|
+
xml do
|
|
221
|
+
element "union_schema_sample"
|
|
222
|
+
map_element "v", to: :v
|
|
223
|
+
end
|
|
224
|
+
key_value { map "v", to: :v }
|
|
225
|
+
|
|
226
|
+
def self.name
|
|
227
|
+
"UnionSchemaSample"
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "emits anyOf for JSON Schema (valid if at least one member matches)" do
|
|
233
|
+
schema = JSON.parse(Lutaml::Model::Schema::JsonSchema.generate(klass))
|
|
234
|
+
defs = schema["$defs"] || schema["definitions"] || {}
|
|
235
|
+
property = defs.dig("UnionSchemaSample", "properties", "v") ||
|
|
236
|
+
schema.dig("properties", "v")
|
|
237
|
+
|
|
238
|
+
expect(property).to eq(
|
|
239
|
+
"anyOf" => [
|
|
240
|
+
{ "type" => "integer" },
|
|
241
|
+
{ "type" => "string" },
|
|
242
|
+
{ "type" => "null" },
|
|
243
|
+
],
|
|
244
|
+
)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it "raises UnionSchemaUnsupportedError for XSD" do
|
|
248
|
+
expect { Lutaml::Xml::Schema::XsdSchema.generate(klass) }
|
|
249
|
+
.to raise_error(Lutaml::Model::UnionSchemaUnsupportedError)
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "raises UnionSchemaUnsupportedError for RelaxNG" do
|
|
253
|
+
expect { Lutaml::Xml::Schema::RelaxngSchema.generate(klass) }
|
|
254
|
+
.to raise_error(Lutaml::Model::UnionSchemaUnsupportedError)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
@@ -56,6 +56,20 @@ RSpec.describe Lutaml::Model::Utils do
|
|
|
56
56
|
it_behaves_like "string conversion", :snake_case, SNAKE_CASE_EXAMPLES
|
|
57
57
|
it_behaves_like "string conversion", :pluralize, PLURALIZE_EXAMPLES
|
|
58
58
|
|
|
59
|
+
describe ".last_of_split" do
|
|
60
|
+
it "returns the segment after the colon" do
|
|
61
|
+
expect(utils.last_of_split("foo:Bar")).to eq("Bar")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "returns the input unchanged when there is no colon" do
|
|
65
|
+
expect(utils.last_of_split("Bar")).to eq("Bar")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "returns nil for nil input" do
|
|
69
|
+
expect(utils.last_of_split(nil)).to be_nil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
59
73
|
describe ".deep_dup" do
|
|
60
74
|
let(:original_hash) do
|
|
61
75
|
{
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require "spec_helper"
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Adapter-agnostic XML round-trip scenarios, shared between Oga and
|
|
6
|
+
# REXML smoke runs. Both adapters are pure Ruby and work under Opal;
|
|
7
|
+
# this shared example is the contract that any future Opal-capable
|
|
8
|
+
# adapter would also need to satisfy.
|
|
9
|
+
RSpec.shared_examples "opal XML adapter round-trip" do |adapter_name|
|
|
6
10
|
before do
|
|
7
|
-
Lutaml::Model::Config.xml_adapter_type =
|
|
11
|
+
Lutaml::Model::Config.xml_adapter_type = adapter_name
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
it "round-trips XML through parse and serialize" do
|
|
@@ -118,28 +122,26 @@ RSpec.describe "XML with REXML under Opal", if: RUBY_ENGINE == "opal" do
|
|
|
118
122
|
expect(xml).to include("<title>Widget</title>")
|
|
119
123
|
expect(xml).to include("<count>5</count>")
|
|
120
124
|
end
|
|
125
|
+
end
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
# Pure-Ruby XML adapters that ship in the Opal bundle. Each nested
|
|
128
|
+
# group runs the shared contract for its adapter; the top-level guard
|
|
129
|
+
# gates the whole file under Opal.
|
|
130
|
+
RSpec.describe "XML adapters under Opal", if: RUBY_ENGINE == "opal" do
|
|
131
|
+
describe "with Oga (default)" do
|
|
132
|
+
it_behaves_like "opal XML adapter round-trip", :oga
|
|
126
133
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const_set(:MODEL_CLASS, plain_model)
|
|
131
|
-
model plain_model
|
|
132
|
-
attribute :data, :string
|
|
133
|
-
xml do
|
|
134
|
-
element "data"
|
|
135
|
-
map_element "data", to: :data
|
|
136
|
-
end
|
|
134
|
+
it "uses Oga adapter by default under Opal" do
|
|
135
|
+
expect(Lutaml::Model::Config.xml_adapter_type).to eq(:oga)
|
|
137
136
|
end
|
|
138
|
-
|
|
139
|
-
expect(plain_model.ancestors).to include(Lutaml::Xml::XmlOrderable)
|
|
140
137
|
end
|
|
141
138
|
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
describe "with REXML" do
|
|
140
|
+
it_behaves_like "opal XML adapter round-trip", :rexml
|
|
141
|
+
|
|
142
|
+
it "selects REXML when explicitly configured" do
|
|
143
|
+
Lutaml::Model::Config.xml_adapter_type = :rexml
|
|
144
|
+
expect(Lutaml::Model::Config.xml_adapter_type).to eq(:rexml)
|
|
145
|
+
end
|
|
144
146
|
end
|
|
145
147
|
end
|
|
@@ -41,8 +41,9 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
|
|
|
41
41
|
it "keeps built-in W3C XML attribute types when processing xml.xsd" do
|
|
42
42
|
described_class.as_models(xml_namespace_schema)
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
type_ref = described_class.builder.attributes["id"].type
|
|
45
|
+
expect(type_ref.kind).to eq(:w3c)
|
|
46
|
+
expect(type_ref.value).to eq("Lutaml::Xml::W3c::XmlIdType")
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
|
|
@@ -227,8 +228,7 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
|
|
|
227
228
|
end
|
|
228
229
|
|
|
229
230
|
let(:expected_classes) do
|
|
230
|
-
|
|
231
|
-
classes = types.filter_map do |name, value|
|
|
231
|
+
classes = described_class::SupportedDataTypes::TABLE.filter_map do |name, value|
|
|
232
232
|
name.to_s unless value[:skippable]
|
|
233
233
|
end
|
|
234
234
|
classes << "User"
|
|
@@ -751,4 +751,21 @@ RSpec.describe Lutaml::Model::Schema::XmlCompiler do
|
|
|
751
751
|
end
|
|
752
752
|
end
|
|
753
753
|
end
|
|
754
|
+
|
|
755
|
+
describe "SupportedDataTypes.skippable?" do
|
|
756
|
+
# Attribute TypeRefs store the snake_case rendering, simple_content
|
|
757
|
+
# keeps the XSD spelling; skippable? must accept both for dateTime,
|
|
758
|
+
# the one skippable name that changes under snake_case.
|
|
759
|
+
it "matches both the XSD and snake_case spelling of skippable types" do
|
|
760
|
+
sdt = described_class::SupportedDataTypes
|
|
761
|
+
expect(sdt.skippable?("dateTime")).to be true
|
|
762
|
+
expect(sdt.skippable?("date_time")).to be true
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
it "still rejects non-skippable types in both spellings" do
|
|
766
|
+
sdt = described_class::SupportedDataTypes
|
|
767
|
+
expect(sdt.skippable?("nonNegativeInteger")).to be false
|
|
768
|
+
expect(sdt.skippable?("non_negative_integer")).to be false
|
|
769
|
+
end
|
|
770
|
+
end
|
|
754
771
|
end
|