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
@@ -11,7 +11,7 @@ module Lutaml
|
|
11
11
|
# Outputs a warning message that
|
12
12
|
# Usage of `old` name is deprecated will be removed in the next major
|
13
13
|
# release. Please use the `replacement`` instead.
|
14
|
-
def self.
|
14
|
+
def self.warn_future_deprecation(old:, replacement:)
|
15
15
|
warn("Usage of `#{old}` is deprecated and will be removed in the next major release. Please use `#{replacement}` instead.")
|
16
16
|
end
|
17
17
|
|
@@ -23,7 +23,7 @@ module Lutaml
|
|
23
23
|
# `<name>` is handled by default. No need to explicitly
|
24
24
|
# define at `<caller_file>:<caller_line>`.
|
25
25
|
def self.warn_auto_handling(name:, caller_file:, caller_line:)
|
26
|
-
warn("`#{name}` is handled by default. No need to
|
26
|
+
warn("`#{name}` is handled by default. No need to explicitly define at `#{caller_file}:#{caller_line}`")
|
27
27
|
end
|
28
28
|
|
29
29
|
def call(message, type)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
class RuleValueExtractor < Services::Base
|
6
|
+
def initialize(rule, doc, format, attr, register, options)
|
7
|
+
super()
|
8
|
+
|
9
|
+
@rule = rule
|
10
|
+
@doc = doc
|
11
|
+
@format = format
|
12
|
+
@attr = attr
|
13
|
+
@register = register
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
rule_names.each do |rule_name|
|
19
|
+
value = rule_value_for(rule_name)
|
20
|
+
value = transform_mapped_value(value) if should_transform_value?(value)
|
21
|
+
return value if Utils.initialized?(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
uninitialized_value
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :rule, :doc, :format, :attr, :register, :options
|
30
|
+
|
31
|
+
def rule_names
|
32
|
+
rule.multiple_mappings? ? rule.name : [rule.name]
|
33
|
+
end
|
34
|
+
|
35
|
+
def rule_value_for(name)
|
36
|
+
return doc if root_or_nil?(name)
|
37
|
+
return convert_to_format(doc, format) if rule.raw_mapping?
|
38
|
+
return fetch_value(name) if Utils.string_or_symbol_key?(doc, name)
|
39
|
+
return attr.default(register) if attr&.default_set?(register)
|
40
|
+
|
41
|
+
uninitialized_value
|
42
|
+
end
|
43
|
+
|
44
|
+
def root_or_nil?(name)
|
45
|
+
name.nil? || rule.root_mapping?
|
46
|
+
end
|
47
|
+
|
48
|
+
def fetch_value(name)
|
49
|
+
Utils.fetch_str_or_sym(doc, name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def transform_mapped_value(value)
|
53
|
+
value.map do |k, v|
|
54
|
+
if v.is_a?(Hash)
|
55
|
+
transform_hash_value(v, k, options)
|
56
|
+
else
|
57
|
+
transform_simple_value(k, v, options)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def transform_hash_value(hash_value, key, options)
|
63
|
+
hash_value.merge(
|
64
|
+
{
|
65
|
+
options[:key_mappings].to_instance.to_s => key,
|
66
|
+
},
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def transform_simple_value(key, value, options)
|
71
|
+
{
|
72
|
+
options[:key_mappings].to_instance.to_s => key,
|
73
|
+
options[:value_mappings].as_attribute.to_s => value,
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def should_transform_value?(value)
|
78
|
+
(options[:key_mappings] || options[:value_mappings]) &&
|
79
|
+
value.is_a?(Hash)
|
80
|
+
end
|
81
|
+
|
82
|
+
def convert_to_format(doc, format)
|
83
|
+
adapter = Lutaml::Model::Config.adapter_for(format)
|
84
|
+
adapter.new(doc).public_send(:"to_#{format}")
|
85
|
+
end
|
86
|
+
|
87
|
+
def uninitialized_value
|
88
|
+
Lutaml::Model::UninitializedClass.instance
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module Services
|
6
|
+
module Type
|
7
|
+
class Validator
|
8
|
+
class Number < Validator
|
9
|
+
module ClassMethods
|
10
|
+
def validate!(value, options)
|
11
|
+
return if Utils.blank?(options)
|
12
|
+
|
13
|
+
validate_values!(value, options[:values])
|
14
|
+
validate_min_max_bounds!(value, options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
extend ClassMethods
|
19
|
+
include ClassMethods
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
module Services
|
4
|
+
module Type
|
5
|
+
class Validator
|
6
|
+
class String < Validator
|
7
|
+
module ClassMethods
|
8
|
+
def validate!(value, options)
|
9
|
+
return if Utils.blank?(value)
|
10
|
+
|
11
|
+
validate_values!(value, options[:values])
|
12
|
+
validate_length!(value, options)
|
13
|
+
validate_pattern!(value, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_length!(value, options)
|
17
|
+
min, max = options&.values_at(:min, :max)
|
18
|
+
return if min.nil? || max.nil?
|
19
|
+
|
20
|
+
validate_min_length!(value, min) if min
|
21
|
+
validate_max_length!(value, max) if max
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_pattern!(value, options)
|
25
|
+
pattern = options[:pattern]
|
26
|
+
return if Utils.blank?(pattern)
|
27
|
+
return if value.match?(pattern)
|
28
|
+
|
29
|
+
raise Lutaml::Model::Type::PatternNotMatchedError.new(value, pattern)
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_min_length!(value, min)
|
33
|
+
return if value.length >= min
|
34
|
+
|
35
|
+
raise Lutaml::Model::Type::MinLengthError.new(value, min)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_max_length!(value, max)
|
39
|
+
return if value.length <= max
|
40
|
+
|
41
|
+
raise Lutaml::Model::Type::MaxLengthError.new(value, max)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
extend ClassMethods
|
46
|
+
include ClassMethods
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative "validator/string"
|
2
|
+
require_relative "validator/number"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module Services
|
7
|
+
module Type
|
8
|
+
class Validator
|
9
|
+
module ClassMethods
|
10
|
+
def validate_values!(value, values)
|
11
|
+
return if Utils.blank?(values) || values.include?(value)
|
12
|
+
|
13
|
+
raise Lutaml::Model::Type::InvalidValueError.new(value, values)
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_min_max_bounds!(value, options)
|
17
|
+
min, max = options&.values_at(:min, :max)
|
18
|
+
return if min.nil? && max.nil?
|
19
|
+
|
20
|
+
validate_min_bound!(value, min) if min
|
21
|
+
validate_max_bound!(value, max) if max
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_min_bound!(value, min)
|
25
|
+
return if value >= min
|
26
|
+
|
27
|
+
raise Lutaml::Model::Type::MinBoundError.new(value, min)
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_max_bound!(value, max)
|
31
|
+
return if value <= max
|
32
|
+
|
33
|
+
raise Lutaml::Model::Type::MaxBoundError.new(value, max)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
extend ClassMethods
|
38
|
+
include ClassMethods
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
class ValidationRule
|
6
|
+
attr_reader :attribute, :options, :custom_method
|
7
|
+
|
8
|
+
def initialize(attribute: nil, custom_method: nil, options: {})
|
9
|
+
if Utils.blank?(attribute) && Utils.blank?(custom_method)
|
10
|
+
raise ArgumentError, "Missing attribute or custom method"
|
11
|
+
end
|
12
|
+
|
13
|
+
@attribute = attribute
|
14
|
+
@options = options
|
15
|
+
@custom_method = custom_method
|
16
|
+
end
|
17
|
+
|
18
|
+
def custom?
|
19
|
+
Utils.present?(custom_method)
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_options?
|
23
|
+
options.is_a?(Hash)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Validator < Services::Base
|
28
|
+
attr_reader :value, :validations, :errors, :memoization_container
|
29
|
+
|
30
|
+
def initialize(value, validations, memoization_container)
|
31
|
+
super()
|
32
|
+
|
33
|
+
@errors = Errors.new
|
34
|
+
@value = value
|
35
|
+
@memoization_container = memoization_container
|
36
|
+
|
37
|
+
resolve_validations(validations)
|
38
|
+
end
|
39
|
+
|
40
|
+
def call
|
41
|
+
return [] if Utils.blank?(validations)
|
42
|
+
|
43
|
+
@validations.each do |validation|
|
44
|
+
if validation.custom?
|
45
|
+
public_send(validation.custom_method, value)
|
46
|
+
else
|
47
|
+
validation.options.each do |key, rule|
|
48
|
+
send(:"validate_#{key}", value, validation.attribute, rule)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
errors.messages
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def resolve_validations(validations_block)
|
59
|
+
@validations ||= []
|
60
|
+
return unless validations_block
|
61
|
+
|
62
|
+
instance_eval(&validations_block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate(method_name)
|
66
|
+
@validations << ValidationRule.new(custom_method: method_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def validates(attribute, options)
|
70
|
+
@validations <<
|
71
|
+
ValidationRule.new(attribute: attribute, options: options)
|
72
|
+
end
|
73
|
+
|
74
|
+
def validate_presence(model, attr, rule)
|
75
|
+
return if rule.nil?
|
76
|
+
|
77
|
+
if model.is_a?(Array)
|
78
|
+
model.each { |v| validate_presence(v, attr, rule) }
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
value = model.public_send(attr)
|
83
|
+
return if Utils.present?(value)
|
84
|
+
|
85
|
+
errors.add(attr, "`#{attr}` is required")
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate_numericality(model, attr, rule)
|
89
|
+
return if rule.nil?
|
90
|
+
|
91
|
+
if model.is_a?(Array)
|
92
|
+
model.each { |v| validate_numericality(v, attr, rule) }
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
value = model.public_send(attr)
|
97
|
+
return unless validate_integer(attr, value)
|
98
|
+
|
99
|
+
validate_comparison_rules(attr, value, rule)
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_integer(attr, value)
|
103
|
+
return true if value.is_a?(Integer)
|
104
|
+
|
105
|
+
errors.add(
|
106
|
+
attr,
|
107
|
+
"`#{attr}` value is `#{value.class}`, but expected integer",
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def validate_comparison_rules(attr, value, options)
|
112
|
+
validate_less_than(attr, value, options[:less_than])
|
113
|
+
validate_greater_than(attr, value, options[:greater_than])
|
114
|
+
validate_equal_to(attr, value, options[:equal_to])
|
115
|
+
end
|
116
|
+
|
117
|
+
def validate_less_than(attr, value, limit)
|
118
|
+
return if !limit || value < limit
|
119
|
+
|
120
|
+
errors.add(
|
121
|
+
attr,
|
122
|
+
"#{attr} value is `#{value}`, which is not less than #{limit}",
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def validate_greater_than(attr, value, limit)
|
127
|
+
return if !limit || value > limit
|
128
|
+
|
129
|
+
errors.add(
|
130
|
+
attr,
|
131
|
+
"#{attr} value is `#{value}`, which is not greater than #{limit}",
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
def validate_equal_to(attr, value, target)
|
136
|
+
return if !target || value == target
|
137
|
+
|
138
|
+
errors.add(
|
139
|
+
attr,
|
140
|
+
"#{attr} value is `#{value}`, which is not equal to #{target}",
|
141
|
+
)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -2,10 +2,17 @@ module Lutaml
|
|
2
2
|
module Model
|
3
3
|
class KeyValueTransform < Lutaml::Model::Transform
|
4
4
|
def data_to_model(data, format, options = {})
|
5
|
-
instance = model_class.
|
5
|
+
instance = if model_class.include?(Lutaml::Model::Serialize)
|
6
|
+
model_class.new({}, register: register)
|
7
|
+
else
|
8
|
+
model_class.new(register)
|
9
|
+
end
|
6
10
|
mappings = extract_mappings(options, format)
|
7
11
|
|
8
|
-
|
12
|
+
Utils.add_if_present(options, :key_mappings, mappings.key_mappings)
|
13
|
+
Utils.add_if_present(options, :value_mappings, mappings.value_mappings)
|
14
|
+
|
15
|
+
mappings.mappings.each do |rule|
|
9
16
|
process_mapping_rule(data, instance, format, rule, options)
|
10
17
|
end
|
11
18
|
|
@@ -13,18 +20,30 @@ module Lutaml
|
|
13
20
|
end
|
14
21
|
|
15
22
|
def model_to_data(instance, format, options = {})
|
16
|
-
mappings =
|
23
|
+
mappings = extract_mappings(options, format)
|
17
24
|
|
18
|
-
|
25
|
+
hash = {}
|
26
|
+
mappings.mappings.each do |rule|
|
19
27
|
next unless valid_mapping?(rule, options)
|
20
|
-
next handle_delegate(instance, rule, hash, format) if rule.delegate
|
21
28
|
|
22
|
-
|
29
|
+
process_rule!(instance, rule, hash, format, mappings, options)
|
23
30
|
end
|
31
|
+
|
32
|
+
hash.keys == [""] ? hash[""] : hash
|
24
33
|
end
|
25
34
|
|
26
35
|
private
|
27
36
|
|
37
|
+
def process_rule!(instance, rule, hash, format, mappings, options)
|
38
|
+
return handle_delegate(instance, rule, hash, format) if rule.delegate
|
39
|
+
|
40
|
+
value = process_mapping_for_instance(instance, hash, format, rule, options)
|
41
|
+
|
42
|
+
return unless mappings.key_value_mappings? && value
|
43
|
+
|
44
|
+
hash[rule_from_name(rule)] = handle_key_value_mappings(value, mappings)
|
45
|
+
end
|
46
|
+
|
28
47
|
def process_mapping_for_instance(instance, hash, format, rule, options)
|
29
48
|
if rule.custom_methods[:to]
|
30
49
|
return instance.send(rule.custom_methods[:to], instance, hash)
|
@@ -74,9 +93,11 @@ module Lutaml
|
|
74
93
|
end
|
75
94
|
|
76
95
|
def serialize_value(value, rule, attr, format, options)
|
77
|
-
return attr.serialize(value, format, options) unless rule.child_mappings
|
96
|
+
return attr.serialize(value, format, register, options) unless rule.child_mappings
|
78
97
|
|
79
|
-
generate_hash_from_child_mappings(
|
98
|
+
generate_hash_from_child_mappings(
|
99
|
+
attr, value, format, rule.child_mappings
|
100
|
+
)
|
80
101
|
end
|
81
102
|
|
82
103
|
def rule_from_name(rule)
|
@@ -90,8 +111,9 @@ module Lutaml
|
|
90
111
|
|
91
112
|
generate_remaining_mappings_for_value(child_mappings, value, format)
|
92
113
|
|
114
|
+
attr_type = attr.type(register)
|
93
115
|
value.each do |child_obj|
|
94
|
-
rules =
|
116
|
+
rules = attr_type.mappings_for(format)
|
95
117
|
|
96
118
|
hash.merge!(
|
97
119
|
extract_hash_for_child_mapping(child_mappings, child_obj, rules),
|
@@ -119,7 +141,7 @@ module Lutaml
|
|
119
141
|
end
|
120
142
|
|
121
143
|
def child_mapping_for(name, mappings)
|
122
|
-
mappings.find_by_to(name)&.name.to_s
|
144
|
+
mappings.find_by_to(name)&.name.to_s
|
123
145
|
end
|
124
146
|
|
125
147
|
def extract_hash_for_child_mapping(child_mappings, child_obj, rules)
|
@@ -167,7 +189,7 @@ module Lutaml
|
|
167
189
|
return if value.nil? && !rule.render_nil
|
168
190
|
|
169
191
|
attribute = instance.send(rule.delegate).class.attributes[rule.to]
|
170
|
-
hash[rule_from_name(rule)] = attribute.serialize(value, format)
|
192
|
+
hash[rule_from_name(rule)] = attribute.serialize(value, format, register)
|
171
193
|
end
|
172
194
|
|
173
195
|
def extract_value_for_delegate(instance, rule)
|
@@ -175,16 +197,16 @@ module Lutaml
|
|
175
197
|
end
|
176
198
|
|
177
199
|
def extract_mappings(options, format)
|
178
|
-
options[:mappings] || mappings_for(format)
|
200
|
+
options[:mappings] || mappings_for(format)
|
179
201
|
end
|
180
202
|
|
181
203
|
def process_mapping_rule(doc, instance, format, rule, options = {})
|
182
|
-
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
183
|
-
|
184
204
|
attr = attribute_for_rule(rule)
|
185
205
|
return if attr&.derived?
|
186
206
|
|
187
|
-
|
207
|
+
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule, attr)
|
208
|
+
|
209
|
+
value = rule_value_extractor_class.call(rule, doc, format, attr, register, options)
|
188
210
|
value = apply_value_map(value, rule.value_map(:from, options), attr)
|
189
211
|
|
190
212
|
return process_custom_method(rule, instance, value) if rule.has_custom_method_for_deserialization?
|
@@ -196,37 +218,6 @@ module Lutaml
|
|
196
218
|
rule.deserialize(instance, value, attributes, self)
|
197
219
|
end
|
198
220
|
|
199
|
-
def extract_rule_value(doc, rule, format, attr)
|
200
|
-
rule_names = rule.multiple_mappings? ? rule.name : [rule.name]
|
201
|
-
|
202
|
-
rule_names.each do |rule_name|
|
203
|
-
value = rule_value_for(rule_name, doc, rule, format, attr)
|
204
|
-
|
205
|
-
return value if Utils.initialized?(value)
|
206
|
-
end
|
207
|
-
|
208
|
-
Lutaml::Model::UninitializedClass.instance
|
209
|
-
end
|
210
|
-
|
211
|
-
def rule_value_for(name, doc, rule, format, attr)
|
212
|
-
if rule.root_mapping?
|
213
|
-
doc
|
214
|
-
elsif rule.raw_mapping?
|
215
|
-
convert_to_format(doc, format)
|
216
|
-
elsif Utils.string_or_symbol_key?(doc, name)
|
217
|
-
Utils.fetch_with_string_or_symbol_key(doc, name)
|
218
|
-
elsif attr&.default_set?
|
219
|
-
attr.default
|
220
|
-
else
|
221
|
-
Lutaml::Model::UninitializedClass.instance
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
def convert_to_format(doc, format)
|
226
|
-
adapter = Lutaml::Model::Config.adapter_for(format)
|
227
|
-
adapter.new(doc).public_send(:"to_#{format}")
|
228
|
-
end
|
229
|
-
|
230
221
|
def process_custom_method(rule, instance, value)
|
231
222
|
return unless Utils.present?(value)
|
232
223
|
|
@@ -235,7 +226,7 @@ module Lutaml
|
|
235
226
|
|
236
227
|
def cast_value(value, attr, format, rule)
|
237
228
|
cast_options = rule.polymorphic ? { polymorphic: rule.polymorphic } : {}
|
238
|
-
attr.cast(value, format, cast_options)
|
229
|
+
attr.cast(value, format, register, cast_options)
|
239
230
|
end
|
240
231
|
|
241
232
|
def translate_mappings(hash, child_mappings, attr, format)
|
@@ -257,9 +248,10 @@ module Lutaml
|
|
257
248
|
end
|
258
249
|
|
259
250
|
def build_child_hash(key, value, child_mappings, attr, format)
|
251
|
+
attr_type = attr.type(register)
|
260
252
|
child_mappings.to_h do |attr_name, path|
|
261
253
|
attr_value = extract_attr_value(path, key, value)
|
262
|
-
attr_rule =
|
254
|
+
attr_rule = attr_type.mappings_for(format).find_by_to(attr_name)
|
263
255
|
[attr_rule.from.to_s, attr_value]
|
264
256
|
end
|
265
257
|
end
|
@@ -279,13 +271,31 @@ module Lutaml
|
|
279
271
|
end
|
280
272
|
|
281
273
|
def map_child_data(child_hash, attr, format)
|
274
|
+
attr_type = attr.type(register)
|
282
275
|
self.class.data_to_model(
|
283
|
-
|
276
|
+
attr_type,
|
284
277
|
child_hash,
|
285
278
|
format,
|
286
|
-
{ mappings:
|
279
|
+
{ mappings: attr_type.mappings_for(format) },
|
287
280
|
)
|
288
281
|
end
|
282
|
+
|
283
|
+
def handle_key_value_mappings(value, mappings)
|
284
|
+
value.to_h do |v|
|
285
|
+
[
|
286
|
+
v[mappings.key_mappings.to_instance.to_s],
|
287
|
+
if mappings.value_mappings
|
288
|
+
v[mappings.value_mappings.as_attribute.to_s]
|
289
|
+
else
|
290
|
+
v.except(mappings.key_mappings.to_instance.to_s)
|
291
|
+
end,
|
292
|
+
]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def rule_value_extractor_class
|
297
|
+
Lutaml::Model::RuleValueExtractor
|
298
|
+
end
|
289
299
|
end
|
290
300
|
end
|
291
301
|
end
|