lutaml-model 0.7.3 → 0.7.5
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/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 +2177 -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 +194 -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 +175 -53
- 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 +60 -50
- data/lib/lutaml/model/transform/xml_transform.rb +46 -57
- data/lib/lutaml/model/transform.rb +22 -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/custom_model_spec.rb +15 -3
- data/spec/lutaml/model/defaults_spec.rb +5 -1
- 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 +187 -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/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 +105 -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
|
module Lutaml
|
2
2
|
module Model
|
3
3
|
class Attribute
|
4
|
-
attr_reader :name, :
|
4
|
+
attr_reader :name, :options
|
5
5
|
|
6
6
|
ALLOWED_OPTIONS = %i[
|
7
7
|
raw
|
@@ -17,23 +17,88 @@ module Lutaml
|
|
17
17
|
polymorphic
|
18
18
|
polymorphic_class
|
19
19
|
initialize_empty
|
20
|
+
validations
|
20
21
|
].freeze
|
21
22
|
|
23
|
+
MODEL_STRINGS = [
|
24
|
+
Lutaml::Model::Type::String,
|
25
|
+
"String",
|
26
|
+
:string,
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
# Safe methods than can be overriden without any crashing
|
30
|
+
ALLOW_OVERRIDING = %i[
|
31
|
+
display
|
32
|
+
validate
|
33
|
+
hash
|
34
|
+
itself
|
35
|
+
taint
|
36
|
+
untaint
|
37
|
+
trust
|
38
|
+
untrust
|
39
|
+
methods
|
40
|
+
instance_variables
|
41
|
+
tap
|
42
|
+
extend
|
43
|
+
freeze
|
44
|
+
encoding
|
45
|
+
method
|
46
|
+
object_id
|
47
|
+
].freeze
|
48
|
+
|
49
|
+
def self.cast_type!(type)
|
50
|
+
case type
|
51
|
+
when Symbol then cast_from_symbol!(type)
|
52
|
+
when String then cast_from_string!(type)
|
53
|
+
when Class then type
|
54
|
+
else
|
55
|
+
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.cast_from_symbol!(type)
|
60
|
+
Type.lookup(type)
|
61
|
+
rescue UnknownTypeError
|
62
|
+
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.cast_from_string!(type)
|
66
|
+
Type.const_get(type)
|
67
|
+
rescue NameError
|
68
|
+
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
69
|
+
end
|
70
|
+
|
22
71
|
def initialize(name, type, options = {})
|
72
|
+
validate_name!(
|
73
|
+
name, reserved_methods: Lutaml::Model::Serializable.instance_methods
|
74
|
+
)
|
75
|
+
|
23
76
|
@name = name
|
24
77
|
@options = options
|
25
78
|
|
26
79
|
validate_presence!(type, options[:method_name])
|
27
|
-
|
80
|
+
@type = type
|
28
81
|
process_options!
|
29
82
|
end
|
30
83
|
|
84
|
+
def type(register_id = nil)
|
85
|
+
return if unresolved_type.nil?
|
86
|
+
|
87
|
+
register_id ||= Lutaml::Model::Config.default_register
|
88
|
+
register = Lutaml::Model::GlobalRegister.lookup(register_id)
|
89
|
+
register.get_class_without_register(unresolved_type)
|
90
|
+
end
|
91
|
+
|
92
|
+
def unresolved_type
|
93
|
+
@type
|
94
|
+
end
|
95
|
+
|
31
96
|
def polymorphic?
|
32
97
|
@options[:polymorphic_class]
|
33
98
|
end
|
34
99
|
|
35
100
|
def derived?
|
36
|
-
|
101
|
+
unresolved_type.nil?
|
37
102
|
end
|
38
103
|
|
39
104
|
def delegate
|
@@ -52,45 +117,61 @@ module Lutaml
|
|
52
117
|
@options[:initialize_empty]
|
53
118
|
end
|
54
119
|
|
120
|
+
def validations
|
121
|
+
@options[:validations]
|
122
|
+
end
|
123
|
+
|
55
124
|
def cast_type!(type)
|
56
|
-
|
57
|
-
when Symbol
|
58
|
-
begin
|
59
|
-
Type.lookup(type)
|
60
|
-
rescue UnknownTypeError
|
61
|
-
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
62
|
-
end
|
63
|
-
when String
|
64
|
-
begin
|
65
|
-
Type.const_get(type)
|
66
|
-
rescue NameError
|
67
|
-
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
68
|
-
end
|
69
|
-
when Class
|
70
|
-
type
|
71
|
-
else
|
72
|
-
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
73
|
-
end
|
125
|
+
self.class.cast_type!(type)
|
74
126
|
end
|
75
127
|
|
76
|
-
def cast_value(value)
|
77
|
-
return
|
128
|
+
def cast_value(value, register)
|
129
|
+
return cast_element(value, register) unless collection_instance?(value)
|
78
130
|
|
79
|
-
value.map { |v|
|
131
|
+
build_collection(value.map { |v| cast_element(v, register) })
|
132
|
+
end
|
133
|
+
|
134
|
+
def cast_element(value, register)
|
135
|
+
resolved_type = type(register)
|
136
|
+
return resolved_type.new(value) if value.is_a?(Hash) && !hash_type?
|
137
|
+
|
138
|
+
resolved_type.cast(value)
|
139
|
+
end
|
140
|
+
|
141
|
+
def hash_type?
|
142
|
+
type == Lutaml::Model::Type::Hash
|
80
143
|
end
|
81
144
|
|
82
145
|
def setter
|
83
146
|
:"#{@name}="
|
84
147
|
end
|
85
148
|
|
149
|
+
def collection
|
150
|
+
@options[:collection]
|
151
|
+
end
|
152
|
+
|
86
153
|
def collection?
|
87
|
-
|
154
|
+
collection || false
|
88
155
|
end
|
89
156
|
|
90
157
|
def singular?
|
91
158
|
!collection?
|
92
159
|
end
|
93
160
|
|
161
|
+
def collection_class
|
162
|
+
return Array unless custom_collection?
|
163
|
+
|
164
|
+
collection
|
165
|
+
end
|
166
|
+
|
167
|
+
def collection_instance?(value)
|
168
|
+
value.is_a?(collection_class)
|
169
|
+
end
|
170
|
+
|
171
|
+
def build_collection(*args)
|
172
|
+
collection_class.new(args.flatten)
|
173
|
+
end
|
174
|
+
|
94
175
|
def raw?
|
95
176
|
@raw
|
96
177
|
end
|
@@ -99,13 +180,13 @@ module Lutaml
|
|
99
180
|
!enum_values.empty?
|
100
181
|
end
|
101
182
|
|
102
|
-
def default
|
103
|
-
cast_value(default_value)
|
183
|
+
def default(register = Lutaml::Model::Config.default_register)
|
184
|
+
cast_value(default_value(register), register)
|
104
185
|
end
|
105
186
|
|
106
|
-
def default_value
|
187
|
+
def default_value(register)
|
107
188
|
if delegate
|
108
|
-
type.attributes[to].default
|
189
|
+
type(register).attributes[to].default(register)
|
109
190
|
elsif options[:default].is_a?(Proc)
|
110
191
|
options[:default].call
|
111
192
|
elsif options.key?(:default)
|
@@ -115,8 +196,8 @@ module Lutaml
|
|
115
196
|
end
|
116
197
|
end
|
117
198
|
|
118
|
-
def default_set?
|
119
|
-
!Utils.uninitialized?(default_value)
|
199
|
+
def default_set?(register)
|
200
|
+
!Utils.uninitialized?(default_value(register))
|
120
201
|
end
|
121
202
|
|
122
203
|
def pattern
|
@@ -153,8 +234,8 @@ module Lutaml
|
|
153
234
|
options[:values].include?(value)
|
154
235
|
end
|
155
236
|
|
156
|
-
def valid_pattern!(value)
|
157
|
-
return true unless
|
237
|
+
def valid_pattern!(value, resolved_type)
|
238
|
+
return true unless resolved_type == Lutaml::Model::Type::String
|
158
239
|
return true unless pattern
|
159
240
|
|
160
241
|
unless pattern.match?(value)
|
@@ -174,32 +255,48 @@ module Lutaml
|
|
174
255
|
# 2. Value count should be between the collection range if defined
|
175
256
|
# e.g if collection: 0..5 is set then the value greater then 5
|
176
257
|
# will raise `Lutaml::Model::CollectionCountOutOfRangeError`
|
177
|
-
def validate_value!(value)
|
258
|
+
def validate_value!(value, register)
|
178
259
|
# Use the default value if the value is nil
|
179
|
-
value = default if value.nil?
|
260
|
+
value = default(register) if value.nil?
|
261
|
+
resolved_type = type(register)
|
180
262
|
|
181
263
|
valid_value!(value) &&
|
182
264
|
valid_collection!(value, self) &&
|
183
|
-
valid_pattern!(value) &&
|
184
|
-
validate_polymorphic!(value)
|
265
|
+
valid_pattern!(value, resolved_type) &&
|
266
|
+
validate_polymorphic!(value, resolved_type) &&
|
267
|
+
execute_validations!(value)
|
268
|
+
end
|
269
|
+
|
270
|
+
# execute custom validations on the attribute value
|
271
|
+
# i.e presence: true, numericality: true, etc
|
272
|
+
def execute_validations!(value)
|
273
|
+
return true if Utils.blank?(value)
|
274
|
+
|
275
|
+
memoization_container = {}
|
276
|
+
errors = Lutaml::Model::Validator.call(value, validations, memoization_container)
|
277
|
+
|
278
|
+
return if errors.empty?
|
279
|
+
|
280
|
+
raise Lutaml::Model::ValidationFailedError.new(errors)
|
185
281
|
end
|
186
282
|
|
187
|
-
def validate_polymorphic(value)
|
188
|
-
return value.all? { |v| validate_polymorphic!(v) } if value.is_a?(Array)
|
283
|
+
def validate_polymorphic(value, resolved_type)
|
284
|
+
return value.all? { |v| validate_polymorphic!(v, resolved_type) } if value.is_a?(Array)
|
189
285
|
return true unless options[:polymorphic]
|
190
286
|
|
191
|
-
valid_polymorphic_type?(value)
|
287
|
+
valid_polymorphic_type?(value, resolved_type)
|
192
288
|
end
|
193
289
|
|
194
|
-
def validate_polymorphic!(value)
|
195
|
-
return true if validate_polymorphic(value)
|
290
|
+
def validate_polymorphic!(value, resolved_type)
|
291
|
+
return true if validate_polymorphic(value, resolved_type)
|
196
292
|
|
197
|
-
raise Lutaml::Model::PolymorphicError.new(value, options,
|
293
|
+
raise Lutaml::Model::PolymorphicError.new(value, options, resolved_type)
|
198
294
|
end
|
199
295
|
|
200
296
|
def validate_collection_range
|
201
297
|
range = @options[:collection]
|
202
298
|
return if range == true
|
299
|
+
return if custom_collection?
|
203
300
|
|
204
301
|
unless range.is_a?(Range)
|
205
302
|
raise ArgumentError, "Invalid collection range: #{range}"
|
@@ -228,14 +325,14 @@ module Lutaml
|
|
228
325
|
end
|
229
326
|
|
230
327
|
def valid_collection!(value, caller)
|
231
|
-
raise Lutaml::Model::CollectionTrueMissingError.new(name, caller) if
|
328
|
+
raise Lutaml::Model::CollectionTrueMissingError.new(name, caller) if collection_instance?(value) && !collection?
|
232
329
|
|
233
330
|
return true unless collection?
|
234
331
|
|
235
332
|
# Allow any value for unbounded collections
|
236
333
|
return true if options[:collection] == true
|
237
334
|
|
238
|
-
unless
|
335
|
+
unless collection_instance?(value)
|
239
336
|
raise Lutaml::Model::CollectionCountOutOfRangeError.new(
|
240
337
|
name,
|
241
338
|
value,
|
@@ -246,7 +343,7 @@ module Lutaml
|
|
246
343
|
range = options[:collection]
|
247
344
|
return true unless range.is_a?(Range)
|
248
345
|
|
249
|
-
if range.end.nil?
|
346
|
+
if range.is_a?(Range) && range.end.nil?
|
250
347
|
if value.size < range.begin
|
251
348
|
raise Lutaml::Model::CollectionCountOutOfRangeError.new(
|
252
349
|
name,
|
@@ -254,7 +351,7 @@ module Lutaml
|
|
254
351
|
range,
|
255
352
|
)
|
256
353
|
end
|
257
|
-
elsif !range.cover?(value.size)
|
354
|
+
elsif range.is_a?(Range) && !range.cover?(value.size)
|
258
355
|
raise Lutaml::Model::CollectionCountOutOfRangeError.new(
|
259
356
|
name,
|
260
357
|
value,
|
@@ -263,43 +360,86 @@ module Lutaml
|
|
263
360
|
end
|
264
361
|
end
|
265
362
|
|
266
|
-
def serialize(value, format, options = {})
|
267
|
-
value ||=
|
363
|
+
def serialize(value, format, register, options = {})
|
364
|
+
value ||= build_collection if collection? && initialize_empty?
|
268
365
|
return value if value.nil? || Utils.uninitialized?(value)
|
269
366
|
return value if derived?
|
270
|
-
return serialize_array(value, format, options) if value.is_a?(Array)
|
271
|
-
return serialize_model(value, format, options) if type <= Serialize
|
272
367
|
|
273
|
-
|
274
|
-
|
368
|
+
resolved_type = options[:resolved_type] || type(register)
|
369
|
+
serialize_options = options.merge(resolved_type: resolved_type)
|
370
|
+
return serialize_array(value, format, register, serialize_options) if collection_instance?(value)
|
371
|
+
return serialize_model(value, format, register, options) if resolved_type <= Serialize
|
275
372
|
|
276
|
-
|
277
|
-
|
278
|
-
return value.map { |v| cast(v, format, options) } if value.is_a?(Array)
|
373
|
+
serialize_value(value, format, resolved_type)
|
374
|
+
end
|
279
375
|
|
280
|
-
|
376
|
+
def cast(value, format, register, options = {})
|
377
|
+
resolved_type = options[:resolved_type] || type(register)
|
378
|
+
return build_collection(value.map { |v| cast(v, format, register, options.merge(resolved_type: resolved_type)) }) if collection_instance?(value) || value.is_a?(Array)
|
281
379
|
|
282
|
-
|
380
|
+
return value if already_serialized?(resolved_type, value)
|
283
381
|
|
382
|
+
klass = resolve_polymorphic_class(resolved_type, value, options)
|
284
383
|
if can_serialize?(klass, value, format)
|
285
|
-
klass.apply_mappings(value, format, options)
|
384
|
+
klass.apply_mappings(value, format, options.merge(register: register))
|
286
385
|
elsif needs_conversion?(klass, value)
|
287
386
|
klass.send(:"from_#{format}", value)
|
288
387
|
else
|
289
|
-
|
388
|
+
# No need to use register#get_class,
|
389
|
+
# can_serialize? method already checks if type is Serializable or not.
|
390
|
+
Type.lookup(klass).cast(value)
|
290
391
|
end
|
291
392
|
end
|
292
393
|
|
293
|
-
def serializable?
|
294
|
-
type <= Serialize
|
394
|
+
def serializable?(register)
|
395
|
+
type(register) <= Serialize
|
396
|
+
end
|
397
|
+
|
398
|
+
def collection_range
|
399
|
+
return unless collection?
|
400
|
+
|
401
|
+
collection.is_a?(Range) ? collection : 0..Float::INFINITY
|
402
|
+
end
|
403
|
+
|
404
|
+
def sequenced_appearance_count(element_order, mapped_name, current_index)
|
405
|
+
elements = element_order[current_index..]
|
406
|
+
element_count = elements.take_while { |element| element == mapped_name }.count
|
407
|
+
return element_count if element_count.between?(*collection_range.minmax)
|
408
|
+
|
409
|
+
raise Lutaml::Model::ElementCountOutOfRangeError.new(
|
410
|
+
mapped_name,
|
411
|
+
element_count,
|
412
|
+
collection_range,
|
413
|
+
)
|
414
|
+
end
|
415
|
+
|
416
|
+
def process_options!
|
417
|
+
validate_options!(@options)
|
418
|
+
@raw = !!@options[:raw]
|
419
|
+
@validations = @options[:validations]
|
420
|
+
set_default_for_collection if collection?
|
295
421
|
end
|
296
422
|
|
297
423
|
def deep_dup
|
298
|
-
self.class.new(name,
|
424
|
+
self.class.new(name, unresolved_type, Utils.deep_dup(options))
|
299
425
|
end
|
300
426
|
|
301
427
|
private
|
302
428
|
|
429
|
+
def validate_name!(name, reserved_methods:)
|
430
|
+
return unless reserved_methods.include?(name.to_sym)
|
431
|
+
|
432
|
+
if ALLOW_OVERRIDING.include?(name.to_sym)
|
433
|
+
warn_name_conflict(name)
|
434
|
+
else
|
435
|
+
raise Lutaml::Model::InvalidAttributeNameError.new(name)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def warn_name_conflict(name)
|
440
|
+
Logger.warn("Attribute name `#{name}` conflicts with a built-in method")
|
441
|
+
end
|
442
|
+
|
303
443
|
def resolve_polymorphic_class(type, value, options)
|
304
444
|
return type unless polymorphic_map_defined?(options, value)
|
305
445
|
|
@@ -320,35 +460,42 @@ module Lutaml
|
|
320
460
|
(format == :xml && value.is_a?(Lutaml::Model::Xml::XmlElement))
|
321
461
|
end
|
322
462
|
|
323
|
-
def castable_serialized_type?(value)
|
324
|
-
type <= Serialize && value.is_a?(type.model)
|
325
|
-
end
|
326
|
-
|
327
463
|
def can_serialize?(klass, value, format)
|
328
464
|
klass <= Serialize && castable?(value, format)
|
329
465
|
end
|
330
466
|
|
467
|
+
def custom_collection?
|
468
|
+
return false if singular?
|
469
|
+
return false if collection == true
|
470
|
+
return false if collection.is_a?(Range)
|
471
|
+
|
472
|
+
collection <= Lutaml::Model::Collection
|
473
|
+
end
|
474
|
+
|
331
475
|
def needs_conversion?(klass, value)
|
332
|
-
!value.nil? && !value.is_a?(klass)
|
476
|
+
!value.nil? && !value.is_a?(klass) && Utils.initialized?(value)
|
333
477
|
end
|
334
478
|
|
335
479
|
def already_serialized?(klass, value)
|
336
480
|
klass <= Serialize && value.is_a?(klass.model)
|
337
481
|
end
|
338
482
|
|
339
|
-
def serialize_array(value, format, options)
|
340
|
-
value.map { |v| serialize(v, format, options) }
|
483
|
+
def serialize_array(value, format, register, options)
|
484
|
+
value.map { |v| serialize(v, format, register, options) }
|
341
485
|
end
|
342
486
|
|
343
|
-
def serialize_model(value, format, options)
|
487
|
+
def serialize_model(value, format, register, options)
|
488
|
+
as_options = options.merge(register: register)
|
344
489
|
return unless Utils.present?(value)
|
345
|
-
return value.class.as(format, value, options) if value.is_a?(type)
|
346
490
|
|
347
|
-
|
491
|
+
resolved_type = as_options.delete(:resolved_type) || type(register)
|
492
|
+
return value.class.as(format, value, as_options) if value.is_a?(resolved_type)
|
493
|
+
|
494
|
+
resolved_type.as(format, value, as_options)
|
348
495
|
end
|
349
496
|
|
350
|
-
def serialize_value(value, format)
|
351
|
-
value =
|
497
|
+
def serialize_value(value, format, resolved_type)
|
498
|
+
value = resolved_type.new(value) unless value.is_a?(Type::Value)
|
352
499
|
value.send(:"to_#{format}")
|
353
500
|
end
|
354
501
|
|
@@ -358,29 +505,19 @@ module Lutaml
|
|
358
505
|
raise ArgumentError, "method or type must be set for an attribute"
|
359
506
|
end
|
360
507
|
|
361
|
-
def process_type!(type)
|
362
|
-
validate_type!(type)
|
363
|
-
@type = cast_type!(type)
|
364
|
-
end
|
365
|
-
|
366
|
-
def process_options!
|
367
|
-
validate_options!(@options)
|
368
|
-
@raw = !!@options[:raw]
|
369
|
-
set_default_for_collection if collection?
|
370
|
-
end
|
371
|
-
|
372
508
|
def set_default_for_collection
|
373
509
|
validate_collection_range
|
374
|
-
@options[:default] ||= -> {
|
510
|
+
@options[:default] ||= -> { build_collection } if initialize_empty?
|
375
511
|
end
|
376
512
|
|
377
513
|
def validate_options!(options)
|
378
514
|
if (invalid_opts = options.keys - ALLOWED_OPTIONS).any?
|
379
|
-
raise
|
380
|
-
"Invalid options given for `#{name}` #{invalid_opts}"
|
515
|
+
raise Lutaml::Model::InvalidAttributeOptionsError.new(name, invalid_opts)
|
381
516
|
end
|
382
517
|
|
383
|
-
|
518
|
+
# No need to change user register#get_class, only checks if type is LutaML-Model string.
|
519
|
+
# Using MODEL_STRINGS since pattern is only supported for String type.
|
520
|
+
if options.key?(:pattern) && !MODEL_STRINGS.include?(type)
|
384
521
|
raise StandardError,
|
385
522
|
"Invalid option `pattern` given for `#{name}`, " \
|
386
523
|
"`pattern` is only allowed for :string type"
|
@@ -394,17 +531,16 @@ module Lutaml
|
|
394
531
|
end
|
395
532
|
|
396
533
|
def validate_type!(type)
|
397
|
-
return true if type.is_a?(Class)
|
398
|
-
return true if [Symbol, String].include?(type.class) && cast_type!(type)
|
534
|
+
return true if [Symbol, String].include?(type.class) || type.is_a?(Class)
|
399
535
|
|
400
536
|
raise ArgumentError,
|
401
537
|
"Invalid type: #{type}, must be a Symbol, String or a Class"
|
402
538
|
end
|
403
539
|
|
404
|
-
def valid_polymorphic_type?(value)
|
540
|
+
def valid_polymorphic_type?(value, resolved_type)
|
405
541
|
return value.is_a?(type) unless has_polymorphic_list?
|
406
542
|
|
407
|
-
options[:polymorphic].include?(value.class) && value.is_a?(
|
543
|
+
options[:polymorphic].include?(value.class) && value.is_a?(resolved_type)
|
408
544
|
end
|
409
545
|
|
410
546
|
def has_polymorphic_list?
|
data/lib/lutaml/model/choice.rb
CHANGED
@@ -41,8 +41,28 @@ module Lutaml
|
|
41
41
|
raise Lutaml::Model::ChoiceLowerBoundError.new(validated_attributes, @min) if valid.count < @min
|
42
42
|
end
|
43
43
|
|
44
|
+
def import_model_attributes(model)
|
45
|
+
return import_model_later(model, :import_model_attributes) if later_importable?(model)
|
46
|
+
|
47
|
+
root_model_error(model)
|
48
|
+
imported_attributes = Utils.deep_dup(model.attributes.values)
|
49
|
+
imported_attributes.each do |attr|
|
50
|
+
attr.options[:choice] = self
|
51
|
+
@model.define_attribute_methods(attr)
|
52
|
+
end
|
53
|
+
@attributes.concat(imported_attributes)
|
54
|
+
attrs_hash = imported_attributes.to_h { |attr| [attr.name, attr] }
|
55
|
+
@model.attributes.merge!(attrs_hash)
|
56
|
+
end
|
57
|
+
|
44
58
|
private
|
45
59
|
|
60
|
+
def root_model_error(model)
|
61
|
+
return unless model.root?
|
62
|
+
|
63
|
+
raise Lutaml::Model::ImportModelWithRootError.new(model)
|
64
|
+
end
|
65
|
+
|
46
66
|
def valid_attributes(object, validated_attributes)
|
47
67
|
@attributes.each do |attribute|
|
48
68
|
if attribute.is_a?(Choice)
|
@@ -58,6 +78,16 @@ module Lutaml
|
|
58
78
|
|
59
79
|
validated_attributes
|
60
80
|
end
|
81
|
+
|
82
|
+
def later_importable?(model)
|
83
|
+
model.is_a?(Symbol) || model.is_a?(String)
|
84
|
+
end
|
85
|
+
|
86
|
+
def import_model_later(model, method)
|
87
|
+
@model.importable_choices[self][method] << model.to_sym
|
88
|
+
@model.instance_variable_set(:@choices_imported, false)
|
89
|
+
@model.setup_trace_point
|
90
|
+
end
|
61
91
|
end
|
62
92
|
end
|
63
93
|
end
|