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
@@ -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,18 @@ module Lutaml
|
|
2
2
|
module Model
|
3
3
|
class KeyValueTransform < Lutaml::Model::Transform
|
4
4
|
def data_to_model(data, format, options = {})
|
5
|
-
|
5
|
+
if model_class.include?(Lutaml::Model::Serialize)
|
6
|
+
instance = model_class.new(__register: __register)
|
7
|
+
else
|
8
|
+
instance = model_class.new
|
9
|
+
register_accessor_methods_for(instance, __register)
|
10
|
+
end
|
6
11
|
mappings = extract_mappings(options, format)
|
7
12
|
|
8
|
-
|
13
|
+
Utils.add_if_present(options, :key_mappings, mappings.key_mappings)
|
14
|
+
Utils.add_if_present(options, :value_mappings, mappings.value_mappings)
|
15
|
+
|
16
|
+
mappings.mappings.each do |rule|
|
9
17
|
process_mapping_rule(data, instance, format, rule, options)
|
10
18
|
end
|
11
19
|
|
@@ -13,18 +21,30 @@ module Lutaml
|
|
13
21
|
end
|
14
22
|
|
15
23
|
def model_to_data(instance, format, options = {})
|
16
|
-
mappings =
|
24
|
+
mappings = extract_mappings(options, format)
|
17
25
|
|
18
|
-
|
26
|
+
hash = {}
|
27
|
+
mappings.mappings.each do |rule|
|
19
28
|
next unless valid_mapping?(rule, options)
|
20
|
-
next handle_delegate(instance, rule, hash, format) if rule.delegate
|
21
29
|
|
22
|
-
|
30
|
+
process_rule!(instance, rule, hash, format, mappings, options)
|
23
31
|
end
|
32
|
+
|
33
|
+
hash.keys == [""] ? hash[""] : hash
|
24
34
|
end
|
25
35
|
|
26
36
|
private
|
27
37
|
|
38
|
+
def process_rule!(instance, rule, hash, format, mappings, options)
|
39
|
+
return handle_delegate(instance, rule, hash, format) if rule.delegate
|
40
|
+
|
41
|
+
value = process_mapping_for_instance(instance, hash, format, rule, options)
|
42
|
+
|
43
|
+
return unless mappings.key_value_mappings? && value
|
44
|
+
|
45
|
+
hash[rule_from_name(rule)] = handle_key_value_mappings(value, mappings)
|
46
|
+
end
|
47
|
+
|
28
48
|
def process_mapping_for_instance(instance, hash, format, rule, options)
|
29
49
|
if rule.custom_methods[:to]
|
30
50
|
return instance.send(rule.custom_methods[:to], instance, hash)
|
@@ -74,9 +94,11 @@ module Lutaml
|
|
74
94
|
end
|
75
95
|
|
76
96
|
def serialize_value(value, rule, attr, format, options)
|
77
|
-
return attr.serialize(value, format, options) unless rule.child_mappings
|
97
|
+
return attr.serialize(value, format, __register, options) unless rule.child_mappings
|
78
98
|
|
79
|
-
generate_hash_from_child_mappings(
|
99
|
+
generate_hash_from_child_mappings(
|
100
|
+
attr, value, format, rule.child_mappings
|
101
|
+
)
|
80
102
|
end
|
81
103
|
|
82
104
|
def rule_from_name(rule)
|
@@ -90,11 +112,12 @@ module Lutaml
|
|
90
112
|
|
91
113
|
generate_remaining_mappings_for_value(child_mappings, value, format)
|
92
114
|
|
115
|
+
attr_type = attr.type(__register)
|
93
116
|
value.each do |child_obj|
|
94
|
-
rules =
|
117
|
+
rules = attr_type.mappings_for(format)
|
95
118
|
|
96
119
|
hash.merge!(
|
97
|
-
extract_hash_for_child_mapping(child_mappings, child_obj, rules),
|
120
|
+
extract_hash_for_child_mapping(child_mappings, child_obj, rules, format),
|
98
121
|
)
|
99
122
|
end
|
100
123
|
|
@@ -119,17 +142,16 @@ module Lutaml
|
|
119
142
|
end
|
120
143
|
|
121
144
|
def child_mapping_for(name, mappings)
|
122
|
-
mappings.find_by_to(name)&.name.to_s
|
145
|
+
mappings.find_by_to(name)&.name.to_s
|
123
146
|
end
|
124
147
|
|
125
|
-
def extract_hash_for_child_mapping(child_mappings, child_obj, rules)
|
148
|
+
def extract_hash_for_child_mapping(child_mappings, child_obj, rules, format)
|
126
149
|
key = nil
|
127
150
|
value = {}
|
128
151
|
|
129
152
|
child_mappings.each do |attr_name, path|
|
130
153
|
rule = rules.find_by_to(attr_name)
|
131
|
-
|
132
|
-
attr_value = normalize_attribute_value(child_obj.send(attr_name))
|
154
|
+
attr_value = normalize_attribute_value(child_obj, rule.from, format)
|
133
155
|
|
134
156
|
next unless rule&.render?(attr_value, nil)
|
135
157
|
next key = attr_value if path == :key
|
@@ -141,14 +163,10 @@ module Lutaml
|
|
141
163
|
{ key => value }
|
142
164
|
end
|
143
165
|
|
144
|
-
def normalize_attribute_value(value)
|
145
|
-
|
146
|
-
value.
|
147
|
-
|
148
|
-
value.map(&:to_hash)
|
149
|
-
else
|
150
|
-
value
|
151
|
-
end
|
166
|
+
def normalize_attribute_value(value, attr_name, format)
|
167
|
+
Lutaml::Model::Config.adapter_for(format).parse(
|
168
|
+
value.public_send(:"to_#{format}"),
|
169
|
+
)[attr_name.to_s]
|
152
170
|
end
|
153
171
|
|
154
172
|
def extract_hash_value_for_child_mapping(path, value, map_value)
|
@@ -167,7 +185,7 @@ module Lutaml
|
|
167
185
|
return if value.nil? && !rule.render_nil
|
168
186
|
|
169
187
|
attribute = instance.send(rule.delegate).class.attributes[rule.to]
|
170
|
-
hash[rule_from_name(rule)] = attribute.serialize(value, format)
|
188
|
+
hash[rule_from_name(rule)] = attribute.serialize(value, format, __register)
|
171
189
|
end
|
172
190
|
|
173
191
|
def extract_value_for_delegate(instance, rule)
|
@@ -175,16 +193,16 @@ module Lutaml
|
|
175
193
|
end
|
176
194
|
|
177
195
|
def extract_mappings(options, format)
|
178
|
-
options[:mappings] || mappings_for(format)
|
196
|
+
options[:mappings] || mappings_for(format)
|
179
197
|
end
|
180
198
|
|
181
199
|
def process_mapping_rule(doc, instance, format, rule, options = {})
|
182
|
-
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
183
|
-
|
184
200
|
attr = attribute_for_rule(rule)
|
185
201
|
return if attr&.derived?
|
186
202
|
|
187
|
-
|
203
|
+
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule, attr)
|
204
|
+
|
205
|
+
value = rule_value_extractor_class.call(rule, doc, format, attr, __register, options)
|
188
206
|
value = apply_value_map(value, rule.value_map(:from, options), attr)
|
189
207
|
|
190
208
|
return process_custom_method(rule, instance, value) if rule.has_custom_method_for_deserialization?
|
@@ -196,37 +214,6 @@ module Lutaml
|
|
196
214
|
rule.deserialize(instance, value, attributes, self)
|
197
215
|
end
|
198
216
|
|
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
217
|
def process_custom_method(rule, instance, value)
|
231
218
|
return unless Utils.present?(value)
|
232
219
|
|
@@ -235,7 +222,7 @@ module Lutaml
|
|
235
222
|
|
236
223
|
def cast_value(value, attr, format, rule)
|
237
224
|
cast_options = rule.polymorphic ? { polymorphic: rule.polymorphic } : {}
|
238
|
-
attr.cast(value, format, cast_options)
|
225
|
+
attr.cast(value, format, __register, cast_options)
|
239
226
|
end
|
240
227
|
|
241
228
|
def translate_mappings(hash, child_mappings, attr, format)
|
@@ -257,9 +244,10 @@ module Lutaml
|
|
257
244
|
end
|
258
245
|
|
259
246
|
def build_child_hash(key, value, child_mappings, attr, format)
|
247
|
+
attr_type = attr.type(__register)
|
260
248
|
child_mappings.to_h do |attr_name, path|
|
261
249
|
attr_value = extract_attr_value(path, key, value)
|
262
|
-
attr_rule =
|
250
|
+
attr_rule = attr_type.mappings_for(format).find_by_to(attr_name)
|
263
251
|
[attr_rule.from.to_s, attr_value]
|
264
252
|
end
|
265
253
|
end
|
@@ -279,13 +267,31 @@ module Lutaml
|
|
279
267
|
end
|
280
268
|
|
281
269
|
def map_child_data(child_hash, attr, format)
|
270
|
+
attr_type = attr.type(__register)
|
282
271
|
self.class.data_to_model(
|
283
|
-
|
272
|
+
attr_type,
|
284
273
|
child_hash,
|
285
274
|
format,
|
286
|
-
{ mappings:
|
275
|
+
{ mappings: attr_type.mappings_for(format) },
|
287
276
|
)
|
288
277
|
end
|
278
|
+
|
279
|
+
def handle_key_value_mappings(value, mappings)
|
280
|
+
value.to_h do |v|
|
281
|
+
[
|
282
|
+
v[mappings.key_mappings.to_instance.to_s],
|
283
|
+
if mappings.value_mappings
|
284
|
+
v[mappings.value_mappings.as_attribute.to_s]
|
285
|
+
else
|
286
|
+
v.except(mappings.key_mappings.to_instance.to_s)
|
287
|
+
end,
|
288
|
+
]
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def rule_value_extractor_class
|
293
|
+
Lutaml::Model::RuleValueExtractor
|
294
|
+
end
|
289
295
|
end
|
290
296
|
end
|
291
297
|
end
|