lutaml-model 0.7.3 → 0.7.6
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/.envrc +1 -0
- data/.github/workflows/dependent-tests.yml +4 -0
- data/.github/workflows/rake.yml +12 -0
- data/.github/workflows/release.yml +3 -0
- data/.gitignore +6 -1
- data/.irbrc +1 -0
- data/.pryrc +1 -0
- data/.rubocop_todo.yml +25 -52
- data/README.adoc +2294 -192
- data/docs/custom_registers.adoc +228 -0
- data/docs/schema_generation.adoc +898 -0
- data/docs/schema_import.adoc +364 -0
- data/flake.lock +114 -0
- data/flake.nix +103 -0
- data/lib/lutaml/model/attribute.rb +230 -94
- data/lib/lutaml/model/choice.rb +30 -0
- data/lib/lutaml/model/collection.rb +195 -0
- data/lib/lutaml/model/comparable_model.rb +3 -3
- data/lib/lutaml/model/config.rb +26 -3
- data/lib/lutaml/model/constants.rb +2 -0
- data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
- data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
- data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
- data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
- data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
- data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
- data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
- data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
- data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
- data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
- data/lib/lutaml/model/error.rb +10 -0
- data/lib/lutaml/model/errors.rb +36 -0
- data/lib/lutaml/model/format_registry.rb +5 -2
- data/lib/lutaml/model/global_register.rb +41 -0
- data/lib/lutaml/model/{hash.rb → hash_adapter.rb} +5 -5
- data/lib/lutaml/model/jsonl/document.rb +14 -0
- data/lib/lutaml/model/jsonl/mapping.rb +19 -0
- data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
- data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
- data/lib/lutaml/model/jsonl/transform.rb +19 -0
- data/lib/lutaml/model/jsonl.rb +21 -0
- data/lib/lutaml/model/key_value_document.rb +3 -2
- data/lib/lutaml/model/mapping/key_value_mapping.rb +64 -4
- data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +4 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +8 -3
- data/lib/lutaml/model/register.rb +105 -0
- data/lib/lutaml/model/registrable.rb +6 -0
- data/lib/lutaml/model/schema/base_schema.rb +64 -0
- data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
- data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
- data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
- data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
- data/lib/lutaml/model/schema/generator/definition.rb +53 -0
- data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
- data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
- data/lib/lutaml/model/schema/generator/property.rb +110 -0
- data/lib/lutaml/model/schema/generator/ref.rb +24 -0
- data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
- data/lib/lutaml/model/schema/json_schema.rb +42 -49
- data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
- data/lib/lutaml/model/schema/renderer.rb +36 -0
- data/lib/lutaml/model/schema/shared_methods.rb +24 -0
- data/lib/lutaml/model/schema/templates/model.erb +9 -0
- data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
- data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
- data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
- data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
- data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
- data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
- data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
- data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
- data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
- data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
- data/lib/lutaml/model/schema.rb +1 -0
- data/lib/lutaml/model/sequence.rb +60 -30
- data/lib/lutaml/model/serialize.rb +177 -54
- data/lib/lutaml/model/services/base.rb +11 -0
- data/lib/lutaml/model/services/logger.rb +2 -2
- data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
- data/lib/lutaml/model/services/type/validator/number.rb +25 -0
- data/lib/lutaml/model/services/type/validator/string.rb +52 -0
- data/lib/lutaml/model/services/type/validator.rb +43 -0
- data/lib/lutaml/model/services/validator.rb +145 -0
- data/lib/lutaml/model/services.rb +3 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +68 -62
- data/lib/lutaml/model/transform/xml_transform.rb +46 -57
- data/lib/lutaml/model/transform.rb +23 -8
- data/lib/lutaml/model/type/boolean.rb +1 -1
- data/lib/lutaml/model/type/date.rb +1 -1
- data/lib/lutaml/model/type/date_time.rb +1 -1
- data/lib/lutaml/model/type/decimal.rb +11 -9
- data/lib/lutaml/model/type/float.rb +2 -1
- data/lib/lutaml/model/type/integer.rb +24 -21
- data/lib/lutaml/model/type/string.rb +4 -2
- data/lib/lutaml/model/type/time.rb +1 -1
- data/lib/lutaml/model/type/time_without_date.rb +1 -1
- data/lib/lutaml/model/type/value.rb +5 -1
- data/lib/lutaml/model/type.rb +5 -2
- data/lib/lutaml/model/utils.rb +30 -8
- data/lib/lutaml/model/validation.rb +6 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml/document.rb +37 -19
- data/lib/lutaml/model/xml/mapping.rb +74 -13
- data/lib/lutaml/model/xml/mapping_rule.rb +10 -2
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +5 -3
- data/lib/lutaml/model/xml/oga/element.rb +4 -1
- data/lib/lutaml/model/xml/oga_adapter.rb +4 -3
- data/lib/lutaml/model/xml/ox_adapter.rb +20 -6
- data/lib/lutaml/model/xml/xml_element.rb +3 -28
- data/lib/lutaml/model/xml_adapter/element.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +1 -1
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -1
- data/lib/lutaml/model/yamls/document.rb +14 -0
- data/lib/lutaml/model/yamls/mapping.rb +19 -0
- data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
- data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
- data/lib/lutaml/model/yamls/transform.rb +19 -0
- data/lib/lutaml/model/yamls.rb +21 -0
- data/lib/lutaml/model.rb +7 -31
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -5
- data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
- data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
- data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
- data/spec/fixtures/xml/product_catalog.xsd +151 -0
- data/spec/fixtures/xml/specifications_schema.xsd +38 -0
- data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
- data/spec/lutaml/model/attribute_spec.rb +41 -44
- data/spec/lutaml/model/choice_spec.rb +44 -0
- data/spec/lutaml/model/custom_collection_spec.rb +830 -0
- data/spec/lutaml/model/global_register_spec.rb +108 -0
- data/spec/lutaml/model/group_spec.rb +9 -3
- data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
- data/spec/lutaml/model/jsonl_spec.rb +229 -0
- data/spec/lutaml/model/multiple_mapping_spec.rb +1 -1
- data/spec/lutaml/model/register/key_value_spec.rb +275 -0
- data/spec/lutaml/model/register/xml_spec.rb +185 -0
- data/spec/lutaml/model/register_spec.rb +147 -0
- data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
- data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
- data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
- data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
- data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
- data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
- data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
- data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
- data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
- data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
- data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
- data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
- data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
- data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
- data/spec/lutaml/model/sequence_spec.rb +36 -0
- data/spec/lutaml/model/serializable_spec.rb +31 -0
- data/spec/lutaml/model/type_spec.rb +8 -4
- data/spec/lutaml/model/utils_spec.rb +3 -3
- data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
- data/spec/lutaml/model/xml/root_mappings/nested_child_mappings_spec.rb +164 -0
- data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
- data/spec/lutaml/model/xml_adapter_spec.rb +24 -0
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +11 -4
- data/spec/lutaml/model/xml_mapping_spec.rb +1 -1
- data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
- data/spec/lutaml/model/yamls_spec.rb +294 -0
- data/spec/spec_helper.rb +1 -0
- metadata +106 -9
- data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
- /data/lib/lutaml/model/{hash → hash_adapter}/document.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/mapping.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/mapping_rule.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/standard_adapter.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/transform.rb +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "lutaml/model/schema"
|
3
3
|
|
4
|
-
module
|
4
|
+
module YamlSchemaSpec
|
5
5
|
class Glaze < Lutaml::Model::Serializable
|
6
6
|
attribute :color, Lutaml::Model::Type::String
|
7
7
|
attribute :finish, Lutaml::Model::Type::String
|
@@ -13,35 +13,258 @@ module SchemaGeneration
|
|
13
13
|
attribute :glaze, Glaze
|
14
14
|
attribute :materials, Lutaml::Model::Type::String, collection: true
|
15
15
|
end
|
16
|
+
|
17
|
+
class ChoiceModel < Lutaml::Model::Serializable
|
18
|
+
attribute :name, Lutaml::Model::Type::String
|
19
|
+
attribute :email, Lutaml::Model::Type::String
|
20
|
+
attribute :phone, Lutaml::Model::Type::String
|
21
|
+
|
22
|
+
choice(min: 1, max: 2) do
|
23
|
+
attribute :email, Lutaml::Model::Type::String
|
24
|
+
attribute :phone, Lutaml::Model::Type::String
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ValidationModel < Lutaml::Model::Serializable
|
29
|
+
attribute :name, Lutaml::Model::Type::String, values: %w[Alice Bob Charlie]
|
30
|
+
attribute :email, Lutaml::Model::Type::String, pattern: /.*?\S+@.+\.\S+/
|
31
|
+
attribute :age, Lutaml::Model::Type::Integer, collection: 1..3
|
32
|
+
attribute :score, Lutaml::Model::Type::Float, default: 0.0
|
33
|
+
end
|
34
|
+
|
35
|
+
class Shape < Lutaml::Model::Serializable
|
36
|
+
attribute :area, :float
|
37
|
+
end
|
38
|
+
|
39
|
+
class Circle < Shape
|
40
|
+
attribute :radius, Lutaml::Model::Type::Float
|
41
|
+
end
|
42
|
+
|
43
|
+
class Square < Shape
|
44
|
+
attribute :side, Lutaml::Model::Type::Float
|
45
|
+
end
|
46
|
+
|
47
|
+
class PolymorphicModel < Lutaml::Model::Serializable
|
48
|
+
attribute :shape, Shape, polymorphic: [Circle, Square]
|
49
|
+
end
|
16
50
|
end
|
17
51
|
|
18
52
|
RSpec.describe Lutaml::Model::Schema::YamlSchema do
|
19
53
|
describe ".generate" do
|
20
|
-
|
21
|
-
schema
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
54
|
+
context "with basic model" do
|
55
|
+
let(:schema) do
|
56
|
+
described_class.generate(
|
57
|
+
YamlSchemaSpec::Vase,
|
58
|
+
id: "http://stsci.edu/schemas/yaml-schema/draft-01",
|
59
|
+
description: "A vase schema",
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
let(:expected_schema) do
|
64
|
+
<<~YAML
|
65
|
+
%YAML 1.1
|
66
|
+
---
|
67
|
+
"$schema": https://json-schema.org/draft/2020-12/schema
|
68
|
+
"$id": http://stsci.edu/schemas/yaml-schema/draft-01
|
69
|
+
description: A vase schema
|
70
|
+
"$ref": "#/$defs/YamlSchemaSpec_Vase"
|
71
|
+
"$defs":
|
72
|
+
YamlSchemaSpec_Vase:
|
73
|
+
type: object
|
74
|
+
additionalProperties: false
|
75
|
+
properties:
|
76
|
+
height:
|
77
|
+
type:
|
78
|
+
- number
|
79
|
+
- 'null'
|
80
|
+
diameter:
|
81
|
+
type:
|
82
|
+
- number
|
83
|
+
- 'null'
|
84
|
+
glaze:
|
85
|
+
"$ref": "#/$defs/YamlSchemaSpec_Glaze"
|
86
|
+
materials:
|
87
|
+
type: array
|
88
|
+
items:
|
89
|
+
type: string
|
90
|
+
YamlSchemaSpec_Glaze:
|
91
|
+
type: object
|
92
|
+
additionalProperties: false
|
93
|
+
properties:
|
94
|
+
color:
|
95
|
+
type:
|
96
|
+
- string
|
97
|
+
- 'null'
|
98
|
+
finish:
|
99
|
+
type:
|
100
|
+
- string
|
101
|
+
- 'null'
|
102
|
+
|
103
|
+
YAML
|
104
|
+
end
|
105
|
+
|
106
|
+
it "generates a YAML schema for nested Serialize objects" do
|
107
|
+
expect(schema).to eq(expected_schema)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "with choice validation" do
|
112
|
+
let(:schema) do
|
113
|
+
described_class.generate(YamlSchemaSpec::ChoiceModel)
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:expected_schema) do
|
117
|
+
<<~YAML
|
118
|
+
%YAML 1.1
|
119
|
+
---
|
120
|
+
"$schema": https://json-schema.org/draft/2020-12/schema
|
121
|
+
"$ref": "#/$defs/YamlSchemaSpec_ChoiceModel"
|
122
|
+
"$defs":
|
123
|
+
YamlSchemaSpec_ChoiceModel:
|
124
|
+
type: object
|
125
|
+
additionalProperties: false
|
126
|
+
properties:
|
127
|
+
name:
|
128
|
+
type:
|
129
|
+
- string
|
130
|
+
- 'null'
|
131
|
+
email:
|
132
|
+
type:
|
133
|
+
- string
|
134
|
+
- 'null'
|
135
|
+
phone:
|
136
|
+
type:
|
137
|
+
- string
|
138
|
+
- 'null'
|
139
|
+
oneOf:
|
140
|
+
- type: object
|
141
|
+
properties:
|
142
|
+
email:
|
143
|
+
type:
|
144
|
+
- string
|
145
|
+
- 'null'
|
146
|
+
phone:
|
147
|
+
type:
|
148
|
+
- string
|
149
|
+
- 'null'
|
150
|
+
YAML
|
151
|
+
end
|
152
|
+
|
153
|
+
it "generates a YAML schema with choice constraints" do
|
154
|
+
expect(schema.strip).to eq(expected_schema.strip)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "with validation constraints" do
|
159
|
+
let(:schema) do
|
160
|
+
described_class.generate(YamlSchemaSpec::ValidationModel)
|
161
|
+
end
|
162
|
+
|
163
|
+
let(:expected_schema) do
|
164
|
+
<<~YAML
|
165
|
+
%YAML 1.1
|
166
|
+
---
|
167
|
+
"$schema": https://json-schema.org/draft/2020-12/schema
|
168
|
+
"$ref": "#/$defs/YamlSchemaSpec_ValidationModel"
|
169
|
+
"$defs":
|
170
|
+
YamlSchemaSpec_ValidationModel:
|
171
|
+
type: object
|
172
|
+
additionalProperties: false
|
173
|
+
properties:
|
174
|
+
name:
|
175
|
+
type:
|
176
|
+
- string
|
177
|
+
- 'null'
|
178
|
+
enum:
|
179
|
+
- Alice
|
180
|
+
- Bob
|
181
|
+
- Charlie
|
182
|
+
email:
|
183
|
+
type:
|
184
|
+
- string
|
185
|
+
- 'null'
|
186
|
+
pattern: ".*?\\\\S+@.+\\\\.\\\\S+"
|
187
|
+
age:
|
188
|
+
type: array
|
189
|
+
items:
|
190
|
+
type: integer
|
191
|
+
minItems: 1
|
192
|
+
maxItems: 3
|
193
|
+
score:
|
194
|
+
type:
|
195
|
+
- number
|
196
|
+
- 'null'
|
197
|
+
default: 0.0
|
198
|
+
YAML
|
199
|
+
end
|
200
|
+
|
201
|
+
it "generates a YAML schema with validation constraints" do
|
202
|
+
expect(schema.strip).to eq(expected_schema.strip)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "with polymorphic types" do
|
207
|
+
let(:schema) do
|
208
|
+
described_class.generate(YamlSchemaSpec::PolymorphicModel)
|
209
|
+
end
|
210
|
+
|
211
|
+
let(:expected_schema) do
|
212
|
+
<<~YAML
|
213
|
+
%YAML 1.1
|
214
|
+
---
|
215
|
+
"$schema": https://json-schema.org/draft/2020-12/schema
|
216
|
+
"$ref": "#/$defs/YamlSchemaSpec_PolymorphicModel"
|
217
|
+
"$defs":
|
218
|
+
YamlSchemaSpec_PolymorphicModel:
|
219
|
+
type: object
|
220
|
+
additionalProperties: false
|
221
|
+
properties:
|
222
|
+
shape:
|
223
|
+
type:
|
224
|
+
- object
|
225
|
+
- 'null'
|
226
|
+
oneOf:
|
227
|
+
- "$ref": "#/$defs/YamlSchemaSpec_Circle"
|
228
|
+
- "$ref": "#/$defs/YamlSchemaSpec_Square"
|
229
|
+
- "$ref": "#/$defs/YamlSchemaSpec_Shape"
|
230
|
+
YamlSchemaSpec_Shape:
|
231
|
+
type: object
|
232
|
+
additionalProperties: false
|
233
|
+
properties:
|
234
|
+
area:
|
235
|
+
type:
|
236
|
+
- number
|
237
|
+
- 'null'
|
238
|
+
YamlSchemaSpec_Circle:
|
239
|
+
type: object
|
240
|
+
additionalProperties: false
|
241
|
+
properties:
|
242
|
+
area:
|
243
|
+
type:
|
244
|
+
- number
|
245
|
+
- 'null'
|
246
|
+
radius:
|
247
|
+
type:
|
248
|
+
- number
|
249
|
+
- 'null'
|
250
|
+
YamlSchemaSpec_Square:
|
251
|
+
type: object
|
252
|
+
additionalProperties: false
|
253
|
+
properties:
|
254
|
+
area:
|
255
|
+
type:
|
256
|
+
- number
|
257
|
+
- 'null'
|
258
|
+
side:
|
259
|
+
type:
|
260
|
+
- number
|
261
|
+
- 'null'
|
262
|
+
YAML
|
263
|
+
end
|
264
|
+
|
265
|
+
it "generates a YAML schema with polymorphic type constraints" do
|
266
|
+
expect(schema.strip).to eq(expected_schema.strip)
|
267
|
+
end
|
45
268
|
end
|
46
269
|
end
|
47
270
|
end
|
@@ -50,6 +50,17 @@ module SequenceSpec
|
|
50
50
|
map_element "ceramic", to: :ceramic
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
class CeramicRestricted < Ceramic
|
55
|
+
restrict :id, collection: 0..1
|
56
|
+
restrict :name, collection: 0..1
|
57
|
+
restrict :type, collection: 0..1
|
58
|
+
restrict :color, collection: 0..1
|
59
|
+
restrict :bold, collection: 0..1
|
60
|
+
restrict :text, collection: 0..1
|
61
|
+
restrict :usage, collection: 0..1
|
62
|
+
restrict :size, collection: 1..2
|
63
|
+
end
|
53
64
|
end
|
54
65
|
|
55
66
|
RSpec.describe "Sequence" do
|
@@ -229,4 +240,29 @@ RSpec.describe "Sequence" do
|
|
229
240
|
end.to raise_error(Lutaml::Model::UnknownSequenceMappingError, "map_attribute is not allowed in sequence")
|
230
241
|
end
|
231
242
|
end
|
243
|
+
|
244
|
+
describe "#validate_content!" do
|
245
|
+
let(:sequence) { SequenceSpec::CeramicRestricted.mappings_for(:xml).element_sequence[0] }
|
246
|
+
let(:klass) { SequenceSpec::CeramicRestricted }
|
247
|
+
|
248
|
+
it "does not raise for correct order" do
|
249
|
+
expect { sequence.validate_content!(["tag", "id", "name", "type", "color", "bold", "text", "usage", "size"], klass) }
|
250
|
+
.not_to raise_error
|
251
|
+
end
|
252
|
+
|
253
|
+
it "raises for incorrect order" do
|
254
|
+
expect { sequence.validate_content!(["tag", "name", "id", "type", "color", "bold", "text", "usage", "size"], klass) }
|
255
|
+
.to raise_error(Lutaml::Model::IncorrectSequenceError)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "raises for unknown tag" do
|
259
|
+
expect { sequence.validate_content!(["tag", "id", "name", "foo", "type", "color", "bold", "text", "usage", "size"], klass) }
|
260
|
+
.to raise_error(Lutaml::Model::IncorrectSequenceError)
|
261
|
+
end
|
262
|
+
|
263
|
+
it "raises error for missing required tag" do
|
264
|
+
expect { sequence.validate_content!(["tag", "id", "name", "type", "color", "bold", "text", "usage"], klass) }
|
265
|
+
.to raise_error(Lutaml::Model::ElementCountOutOfRangeError)
|
266
|
+
end
|
267
|
+
end
|
232
268
|
end
|
@@ -193,6 +193,37 @@ RSpec.describe Lutaml::Model::Serializable do
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
|
196
|
+
describe ".restrict" do
|
197
|
+
before do
|
198
|
+
stub_const("RestrictTestClass", Class.new(described_class))
|
199
|
+
RestrictTestClass.attribute(:foo, :string, collection: 1..3, values: [1, 2, 3])
|
200
|
+
end
|
201
|
+
|
202
|
+
it "merges new options into the attribute's options" do
|
203
|
+
expect { RestrictTestClass.restrict(:foo, collection: 2..4) }
|
204
|
+
.to change { RestrictTestClass.attributes[:foo].options[:collection] }
|
205
|
+
.from(1..3).to(2..4)
|
206
|
+
|
207
|
+
expect(RestrictTestClass.attributes[:foo].options[:values]).to eq([1, 2, 3])
|
208
|
+
end
|
209
|
+
|
210
|
+
it "does not remove existing options not specified in restrict" do
|
211
|
+
RestrictTestClass.restrict(:foo, collection: 5..6, values: [4, 5, 6])
|
212
|
+
expect(RestrictTestClass.attributes[:foo].options[:collection]).to eq(5..6)
|
213
|
+
expect(RestrictTestClass.attributes[:foo].options[:values]).to eq([4, 5, 6])
|
214
|
+
end
|
215
|
+
|
216
|
+
it "raises an error for invalid options" do
|
217
|
+
expect { RestrictTestClass.restrict(:foo, new_option: :bar) }
|
218
|
+
.to raise_error(Lutaml::Model::InvalidAttributeOptionsError, "Invalid options given for `foo` [:new_option]")
|
219
|
+
end
|
220
|
+
|
221
|
+
it "raises an error if the attribute does not exist" do
|
222
|
+
expect { RestrictTestClass.restrict(:bar, collection: 1..2) }
|
223
|
+
.to raise_error(NoMethodError)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
196
227
|
describe ".mappings_for" do
|
197
228
|
context "when mapping is defined" do
|
198
229
|
it "returns the defined mapping" do
|
@@ -198,7 +198,7 @@ RSpec.describe Lutaml::Model::Type do
|
|
198
198
|
|
199
199
|
it "raises TypeNotEnabledError when using Decimal type" do
|
200
200
|
expect do
|
201
|
-
|
201
|
+
decimal_class.cast("123.45")
|
202
202
|
end.to raise_error(Lutaml::Model::TypeNotEnabledError)
|
203
203
|
end
|
204
204
|
end
|
@@ -342,11 +342,14 @@ RSpec.describe Lutaml::Model::Type do
|
|
342
342
|
expected_xml = <<~XML
|
343
343
|
<sample custom_type="to_xml_overrided"/>
|
344
344
|
XML
|
345
|
-
expect(
|
345
|
+
expect(
|
346
|
+
sample_instance_attribute.to_xml,
|
347
|
+
).to be_equivalent_to(expected_xml)
|
346
348
|
end
|
347
349
|
|
348
350
|
it "correctly serializes to JSON" do
|
349
|
-
|
351
|
+
expected_value = '{"custom_type":"to_json_overrided"}'
|
352
|
+
expect(sample_instance.to_json).to eq(expected_value)
|
350
353
|
end
|
351
354
|
|
352
355
|
it "correctly deserializes from XML" do
|
@@ -354,7 +357,8 @@ RSpec.describe Lutaml::Model::Type do
|
|
354
357
|
end
|
355
358
|
|
356
359
|
it "correctly deserializes from JSON" do
|
357
|
-
|
360
|
+
json_input = '{"custom_type":"test_string"}'
|
361
|
+
json_sample_instance = SampleModel.from_json(json_input)
|
358
362
|
json_sample_instance.to_json
|
359
363
|
expect(json_sample_instance.custom_type).to eq("from_json_overrided")
|
360
364
|
end
|
@@ -37,9 +37,9 @@ RSpec.describe Lutaml::Model::Utils do
|
|
37
37
|
"HelloWorld::FooBarBaz" => "hello_world/foo_bar_baz",
|
38
38
|
}
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
it_behaves_like "string conversion", :camel_case, camel_case_examples
|
41
|
+
it_behaves_like "string conversion", :classify, classify_examples
|
42
|
+
it_behaves_like "string conversion", :snake_case, snake_case_examples
|
43
43
|
|
44
44
|
describe ".deep_dup" do
|
45
45
|
let(:original_hash) do
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module NestedChildMappingsSpec
|
4
|
+
class ManifestFont < Lutaml::Model::Serializable
|
5
|
+
attribute :name, :string
|
6
|
+
attribute :styles, :string, collection: true
|
7
|
+
|
8
|
+
key_value do
|
9
|
+
map "name", to: :name
|
10
|
+
map "styles", to: :styles
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ManifestResponseFontStyle < Lutaml::Model::Serializable
|
15
|
+
attribute :full_name, :string
|
16
|
+
attribute :type, :string
|
17
|
+
attribute :paths, :string, collection: true
|
18
|
+
|
19
|
+
key_value do
|
20
|
+
map "full_name", to: :full_name
|
21
|
+
map "type", to: :type
|
22
|
+
map "paths", to: :paths
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ManifestResponseFont < ManifestFont
|
27
|
+
attribute :name, :string
|
28
|
+
attribute :styles, ManifestResponseFontStyle, collection: true
|
29
|
+
|
30
|
+
key_value do
|
31
|
+
map "name", to: :name
|
32
|
+
map "styles", to: :styles, child_mappings: {
|
33
|
+
type: :key,
|
34
|
+
full_name: :full_name,
|
35
|
+
paths: :paths,
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ManifestResponse < Lutaml::Model::Collection
|
41
|
+
instances :fonts, ManifestResponseFont
|
42
|
+
|
43
|
+
key_value do
|
44
|
+
map to: :fonts, root_mappings: {
|
45
|
+
name: :key,
|
46
|
+
styles: :value,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
RSpec.describe "NestedChildMappingsSpec" do
|
53
|
+
let(:yaml) do
|
54
|
+
<<~YAML
|
55
|
+
---
|
56
|
+
Yu Gothic:
|
57
|
+
Bold:
|
58
|
+
full_name: Yu Gothic Bold
|
59
|
+
paths:
|
60
|
+
- "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc"
|
61
|
+
- "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc"
|
62
|
+
- "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc"
|
63
|
+
- "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc"
|
64
|
+
- "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc"
|
65
|
+
Regular:
|
66
|
+
full_name: Yu Gothic Regular
|
67
|
+
paths:
|
68
|
+
- "/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc"
|
69
|
+
- "/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc"
|
70
|
+
- "/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc"
|
71
|
+
- "/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc"
|
72
|
+
- "/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc"
|
73
|
+
Noto Sans Condensed:
|
74
|
+
Regular:
|
75
|
+
full_name: Noto Sans Condensed
|
76
|
+
paths:
|
77
|
+
- "/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"
|
78
|
+
Bold:
|
79
|
+
full_name: Noto Sans Condensed Bold
|
80
|
+
paths:
|
81
|
+
- "/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"
|
82
|
+
Bold Italic:
|
83
|
+
full_name: Noto Sans Condensed Bold Italic
|
84
|
+
paths:
|
85
|
+
- "/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"
|
86
|
+
Italic:
|
87
|
+
full_name: Noto Sans Condensed Italic
|
88
|
+
paths:
|
89
|
+
- "/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"
|
90
|
+
YAML
|
91
|
+
end
|
92
|
+
|
93
|
+
let(:parsed_yaml) do
|
94
|
+
NestedChildMappingsSpec::ManifestResponse.from_yaml(yaml)
|
95
|
+
end
|
96
|
+
|
97
|
+
let(:expected_fonts) do
|
98
|
+
NestedChildMappingsSpec::ManifestResponse.new(
|
99
|
+
[
|
100
|
+
NestedChildMappingsSpec::ManifestResponseFont.new(
|
101
|
+
name: "Yu Gothic",
|
102
|
+
styles: [
|
103
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
104
|
+
full_name: "Yu Gothic Bold",
|
105
|
+
type: "Bold",
|
106
|
+
paths: [
|
107
|
+
"/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothB.ttc",
|
108
|
+
"/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothB.ttc",
|
109
|
+
"/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothB.ttc",
|
110
|
+
"/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothB.ttc",
|
111
|
+
"/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothB.ttc",
|
112
|
+
],
|
113
|
+
),
|
114
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
115
|
+
full_name: "Yu Gothic Regular",
|
116
|
+
type: "Regular",
|
117
|
+
paths: [
|
118
|
+
"/Applications/Microsoft Excel.app/Contents/Resources/DFonts/YuGothR.ttc",
|
119
|
+
"/Applications/Microsoft OneNote.app/Contents/Resources/DFonts/YuGothR.ttc",
|
120
|
+
"/Applications/Microsoft Outlook.app/Contents/Resources/DFonts/YuGothR.ttc",
|
121
|
+
"/Applications/Microsoft PowerPoint.app/Contents/Resources/DFonts/YuGothR.ttc",
|
122
|
+
"/Applications/Microsoft Word.app/Contents/Resources/DFonts/YuGothR.ttc",
|
123
|
+
],
|
124
|
+
),
|
125
|
+
],
|
126
|
+
),
|
127
|
+
NestedChildMappingsSpec::ManifestResponseFont.new(
|
128
|
+
name: "Noto Sans Condensed",
|
129
|
+
styles: [
|
130
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
131
|
+
full_name: "Noto Sans Condensed",
|
132
|
+
type: "Regular",
|
133
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-Condensed.ttf"],
|
134
|
+
),
|
135
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
136
|
+
full_name: "Noto Sans Condensed Bold",
|
137
|
+
type: "Bold",
|
138
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedBold.ttf"],
|
139
|
+
),
|
140
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
141
|
+
full_name: "Noto Sans Condensed Bold Italic",
|
142
|
+
type: "Bold Italic",
|
143
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedBoldItalic.ttf"],
|
144
|
+
),
|
145
|
+
NestedChildMappingsSpec::ManifestResponseFontStyle.new(
|
146
|
+
full_name: "Noto Sans Condensed Italic",
|
147
|
+
type: "Italic",
|
148
|
+
paths: ["/Users/foo/.fontist/fonts/NotoSans-CondensedItalic.ttf"],
|
149
|
+
),
|
150
|
+
],
|
151
|
+
),
|
152
|
+
],
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "parses nested child mappings correctly" do
|
157
|
+
expect(parsed_yaml).to eq(expected_fonts)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "rounds trip correctly" do
|
161
|
+
expected_yaml = parsed_yaml.to_yaml
|
162
|
+
expect(expected_yaml).to eq(yaml)
|
163
|
+
end
|
164
|
+
end
|
@@ -88,6 +88,12 @@ RSpec.describe "XmlElement" do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
def create_element(name, attributes: {}, children: [], text: "")
|
91
|
-
Lutaml::Model::Xml::XmlElement.new(
|
91
|
+
Lutaml::Model::Xml::XmlElement.new(
|
92
|
+
name,
|
93
|
+
attributes,
|
94
|
+
children,
|
95
|
+
text,
|
96
|
+
name: name,
|
97
|
+
)
|
92
98
|
end
|
93
99
|
end
|
@@ -164,15 +164,15 @@ RSpec.describe "XmlNamespace" do
|
|
164
164
|
end
|
165
165
|
|
166
166
|
context "with no prefix" do
|
167
|
-
|
168
|
-
|
169
|
-
|
167
|
+
it_behaves_like "XML serialization with namespace",
|
168
|
+
TestModelNoPrefix,
|
169
|
+
'<test xmlns="http://example.com/test"><name>Test Name</name></test>'
|
170
170
|
end
|
171
171
|
|
172
172
|
context "with prefix" do
|
173
|
-
|
174
|
-
|
175
|
-
|
173
|
+
it_behaves_like "XML serialization with namespace",
|
174
|
+
TestModelWithPrefix,
|
175
|
+
'<test:test xmlns:test="http://example.com/test"><test:name>Test Name</test:name></test:test>'
|
176
176
|
end
|
177
177
|
|
178
178
|
context "with prefixed namespace" do
|
@@ -130,6 +130,30 @@ RSpec.describe "XmlAdapter" do
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
+
context "when xml has `lang` attribute" do
|
134
|
+
before do
|
135
|
+
title_class = Class.new(Lutaml::Model::Serializable) do
|
136
|
+
attribute :lang, :string
|
137
|
+
attribute :content, :string
|
138
|
+
|
139
|
+
xml do
|
140
|
+
root "title"
|
141
|
+
|
142
|
+
map_attribute "lang", to: :lang
|
143
|
+
map_content to: :content
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
stub_const("Title", title_class)
|
148
|
+
end
|
149
|
+
|
150
|
+
let(:xml_input) { "<title lang='en'>Title</title>" }
|
151
|
+
let(:parsed) { Title.from_xml(xml_input) }
|
152
|
+
|
153
|
+
it { expect(parsed.lang).to eq("en") }
|
154
|
+
it { expect(parsed.content).to eq("Title") }
|
155
|
+
end
|
156
|
+
|
133
157
|
it "serializes to XML" do
|
134
158
|
expected_xml = <<~XML
|
135
159
|
<SampleModel>
|