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
@@ -4,40 +4,44 @@ module Lutaml
|
|
4
4
|
module Model
|
5
5
|
module Schema
|
6
6
|
class XsdSchema
|
7
|
+
extend SharedMethods
|
8
|
+
|
7
9
|
def self.generate(klass, options = {})
|
10
|
+
register = extract_register_from(klass)
|
8
11
|
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
9
12
|
xml.schema(xmlns: "http://www.w3.org/2001/XMLSchema") do
|
10
|
-
generate_complex_type(xml, klass)
|
13
|
+
generate_complex_type(xml, klass, nil, register)
|
11
14
|
end
|
12
15
|
end
|
13
16
|
|
14
17
|
options[:pretty] ? builder.to_xml(indent: 2) : builder.to_xml
|
15
18
|
end
|
16
19
|
|
17
|
-
def self.generate_complex_type(xml, klass, element_name = nil)
|
20
|
+
def self.generate_complex_type(xml, klass, element_name = nil, register)
|
18
21
|
xml.element(name: element_name || klass.name) do
|
19
22
|
xml.complexType do
|
20
23
|
xml.sequence do
|
21
|
-
generate_elements(xml, klass)
|
24
|
+
generate_elements(xml, klass, register)
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
|
-
def self.generate_elements(xml, klass)
|
30
|
+
def self.generate_elements(xml, klass, register)
|
28
31
|
klass.attributes.each do |name, attr|
|
29
|
-
|
30
|
-
|
32
|
+
attr_type = attr.type(register)
|
33
|
+
if attr_type <= Lutaml::Model::Serialize
|
34
|
+
generate_complex_type(xml, attr_type, name, register)
|
31
35
|
elsif attr.collection?
|
32
36
|
xml.element(name: name, minOccurs: "0", maxOccurs: "unbounded") do
|
33
37
|
xml.complexType do
|
34
38
|
xml.sequence do
|
35
|
-
xml.element(name: "item", type: get_xsd_type(
|
39
|
+
xml.element(name: "item", type: get_xsd_type(attr_type))
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
else
|
40
|
-
xml.element(name: name, type: get_xsd_type(
|
44
|
+
xml.element(name: name, type: get_xsd_type(attr_type))
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
@@ -1,49 +1,55 @@
|
|
1
|
+
require_relative "base_schema"
|
1
2
|
require "yaml"
|
2
3
|
|
3
4
|
module Lutaml
|
4
5
|
module Model
|
5
6
|
module Schema
|
6
|
-
class YamlSchema
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class YamlSchema < BaseSchema
|
8
|
+
class << self
|
9
|
+
def generate(
|
10
|
+
klass,
|
11
|
+
id: nil,
|
12
|
+
title: nil,
|
13
|
+
description: nil,
|
14
|
+
pretty: false
|
15
|
+
)
|
16
|
+
options = {
|
17
|
+
schema: "https://json-schema.org/draft/2020-12/schema",
|
18
|
+
id: id,
|
19
|
+
title: title,
|
20
|
+
description: description,
|
21
|
+
pretty: pretty,
|
22
|
+
}
|
11
23
|
|
12
|
-
|
13
|
-
|
14
|
-
"type" => "map",
|
15
|
-
"mapping" => generate_mapping(klass),
|
16
|
-
}
|
17
|
-
end
|
24
|
+
super(klass, options)
|
25
|
+
end
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
|
27
|
+
def format_schema(schema, options)
|
28
|
+
<<~SCHEMA
|
29
|
+
%YAML 1.1
|
30
|
+
#{options[:pretty] ? schema.to_yaml : YAML.dump(schema)}
|
31
|
+
SCHEMA
|
22
32
|
end
|
23
|
-
end
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
"sequence" => [{ "type" => get_yaml_type(attr.type) }],
|
32
|
-
}
|
33
|
-
else
|
34
|
-
{ "type" => get_yaml_type(attr.type) }
|
34
|
+
def generate_model_classes(schema)
|
35
|
+
definitions = definition_collection_class.new(schema["$defs"])
|
36
|
+
|
37
|
+
definitions.transform_values do |definition|
|
38
|
+
generate_model_class(definition)
|
39
|
+
end
|
35
40
|
end
|
36
|
-
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
Lutaml::Model::
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
private
|
43
|
+
|
44
|
+
def generate_model_class(schema)
|
45
|
+
template = File.join(__dir__, "templates", "model.erb")
|
46
|
+
|
47
|
+
Lutaml::Model::Schema::Renderer.render(template, schema: schema)
|
48
|
+
end
|
49
|
+
|
50
|
+
def definition_collection_class
|
51
|
+
Lutaml::Model::Schema::Decorators::DefinitionCollection
|
52
|
+
end
|
47
53
|
end
|
48
54
|
end
|
49
55
|
end
|
data/lib/lutaml/model/schema.rb
CHANGED
@@ -18,30 +18,16 @@ module Lutaml
|
|
18
18
|
instance_eval(&block)
|
19
19
|
end
|
20
20
|
|
21
|
-
def map_element(
|
22
|
-
name,
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
transform: {}
|
32
|
-
)
|
33
|
-
@attributes << @model.map_element(
|
34
|
-
name,
|
35
|
-
to: to,
|
36
|
-
render_nil: render_nil,
|
37
|
-
render_default: render_default,
|
38
|
-
with: with,
|
39
|
-
delegate: delegate,
|
40
|
-
cdata: cdata,
|
41
|
-
namespace: namespace,
|
42
|
-
prefix: prefix,
|
43
|
-
transform: transform,
|
44
|
-
)
|
21
|
+
def map_element(name, **kwargs)
|
22
|
+
@attributes << @model.map_element(name, **kwargs)
|
23
|
+
end
|
24
|
+
|
25
|
+
def import_model_mappings(model)
|
26
|
+
return import_mappings_later(model) if later_importable?(model)
|
27
|
+
raise Lutaml::Model::ImportModelWithRootError.new(model) if model.root?
|
28
|
+
|
29
|
+
@model.import_model_mappings(model)
|
30
|
+
@attributes.concat(Utils.deep_dup(model.mappings_for(:xml).elements))
|
45
31
|
end
|
46
32
|
|
47
33
|
def map_attribute(*)
|
@@ -56,16 +42,60 @@ module Lutaml
|
|
56
42
|
raise Lutaml::Model::UnknownSequenceMappingError.new("map_all")
|
57
43
|
end
|
58
44
|
|
59
|
-
def validate_content!(element_order)
|
60
|
-
|
61
|
-
|
45
|
+
def validate_content!(element_order, model_class)
|
46
|
+
validate_sequence!(
|
47
|
+
extract_defined_order(model_class.attributes),
|
48
|
+
element_order,
|
49
|
+
)
|
50
|
+
end
|
62
51
|
|
63
|
-
|
64
|
-
|
65
|
-
|
52
|
+
private
|
53
|
+
|
54
|
+
def validate_sequence!(defined_order, element_order)
|
55
|
+
eo_index = element_order_index(element_order, defined_order)
|
56
|
+
|
57
|
+
defined_order.each do |element, klass_attr|
|
58
|
+
if klass_attr.collection?
|
59
|
+
if add_missing_element?(element_order, eo_index, element, klass_attr)
|
60
|
+
element_order.insert(eo_index, element)
|
61
|
+
else
|
62
|
+
occurrences = klass_attr.sequenced_appearance_count(element_order, element, eo_index)
|
63
|
+
next eo_index += occurrences if occurrences.positive?
|
64
|
+
end
|
66
65
|
end
|
66
|
+
|
67
|
+
next eo_index += 1 if element_order[eo_index] == element
|
68
|
+
|
69
|
+
raise Lutaml::Model::IncorrectSequenceError.new(element, element_order[eo_index])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_missing_element?(element_order, eo_index, element, klass_attr)
|
74
|
+
return false unless klass_attr.collection_range.min.zero?
|
75
|
+
|
76
|
+
element_order[eo_index] != element && !element_order.include?(element)
|
77
|
+
end
|
78
|
+
|
79
|
+
def element_order_index(element_order, defined_order)
|
80
|
+
element_order.find_index { |d| defined_order.key?(d) } || 0
|
81
|
+
end
|
82
|
+
|
83
|
+
def extract_defined_order(model_attrs)
|
84
|
+
@attributes.each_with_object({}) do |rule, acc|
|
85
|
+
acc[rule.name.to_s] = model_attrs[rule.to.to_s] ||
|
86
|
+
model_attrs[rule.to.to_sym]
|
67
87
|
end
|
68
88
|
end
|
89
|
+
|
90
|
+
def later_importable?(model)
|
91
|
+
model.is_a?(Symbol) ||
|
92
|
+
model.is_a?(String)
|
93
|
+
end
|
94
|
+
|
95
|
+
def import_mappings_later(model)
|
96
|
+
@model.sequence_importable_mappings[self] << model.to_sym
|
97
|
+
@model.set_mappings_imported(false)
|
98
|
+
end
|
69
99
|
end
|
70
100
|
end
|
71
101
|
end
|
@@ -3,7 +3,6 @@ require_relative "config"
|
|
3
3
|
require_relative "type"
|
4
4
|
require_relative "attribute"
|
5
5
|
require_relative "mapping_hash"
|
6
|
-
# require_relative "mapping"
|
7
6
|
require_relative "json_adapter"
|
8
7
|
require_relative "comparable_model"
|
9
8
|
require_relative "schema_location"
|
@@ -13,6 +12,7 @@ require_relative "choice"
|
|
13
12
|
require_relative "sequence"
|
14
13
|
require_relative "liquefiable"
|
15
14
|
require_relative "transform"
|
15
|
+
require_relative "registrable"
|
16
16
|
|
17
17
|
module Lutaml
|
18
18
|
module Model
|
@@ -20,6 +20,7 @@ module Lutaml
|
|
20
20
|
include ComparableModel
|
21
21
|
include Validation
|
22
22
|
include Lutaml::Model::Liquefiable
|
23
|
+
include Lutaml::Model::Registrable
|
23
24
|
|
24
25
|
def self.included(base)
|
25
26
|
base.extend(ClassMethods)
|
@@ -29,7 +30,7 @@ module Lutaml
|
|
29
30
|
module ClassMethods
|
30
31
|
include Lutaml::Model::Liquefiable::ClassMethods
|
31
32
|
|
32
|
-
attr_accessor :
|
33
|
+
attr_accessor :choice_attributes, :mappings
|
33
34
|
|
34
35
|
def inherited(subclass)
|
35
36
|
super
|
@@ -48,6 +49,16 @@ module Lutaml
|
|
48
49
|
instance_variable_set(:@model, self)
|
49
50
|
end
|
50
51
|
|
52
|
+
def attributes(register = nil)
|
53
|
+
ensure_imports!(register) if finalized?
|
54
|
+
@attributes
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_imports!(register = nil)
|
58
|
+
ensure_model_imports!(register)
|
59
|
+
ensure_choice_imports!(register)
|
60
|
+
end
|
61
|
+
|
51
62
|
def model(klass = nil)
|
52
63
|
if klass
|
53
64
|
@model = klass
|
@@ -112,7 +123,7 @@ module Lutaml
|
|
112
123
|
end
|
113
124
|
define_method(:"#{name}=") do |value|
|
114
125
|
value_set_for(name)
|
115
|
-
instance_variable_set(:"@#{name}", attr.cast_value(value))
|
126
|
+
instance_variable_set(:"@#{name}", attr.cast_value(value, register))
|
116
127
|
end
|
117
128
|
end
|
118
129
|
end
|
@@ -131,6 +142,25 @@ module Lutaml
|
|
131
142
|
attr
|
132
143
|
end
|
133
144
|
|
145
|
+
def restrict(name, options = {})
|
146
|
+
validate_attribute_options!(name, options)
|
147
|
+
attr = attributes[name]
|
148
|
+
attr.options.merge!(options)
|
149
|
+
attr.process_options!
|
150
|
+
name
|
151
|
+
end
|
152
|
+
|
153
|
+
def validate_attribute_options!(name, options)
|
154
|
+
invalid_opts = options.keys - Attribute::ALLOWED_OPTIONS
|
155
|
+
return if invalid_opts.empty?
|
156
|
+
|
157
|
+
raise Lutaml::Model::InvalidAttributeOptionsError.new(name, invalid_opts)
|
158
|
+
end
|
159
|
+
|
160
|
+
def register(name)
|
161
|
+
name&.to_sym
|
162
|
+
end
|
163
|
+
|
134
164
|
def root?
|
135
165
|
mappings_for(:xml)&.root?
|
136
166
|
end
|
@@ -142,6 +172,14 @@ module Lutaml
|
|
142
172
|
end
|
143
173
|
|
144
174
|
def import_model_attributes(model)
|
175
|
+
if model.is_a?(Symbol) || model.is_a?(String)
|
176
|
+
importable_models[:import_model_attributes] << model.to_sym
|
177
|
+
@models_imported = false
|
178
|
+
@choices_imported = false
|
179
|
+
setup_trace_point
|
180
|
+
return
|
181
|
+
end
|
182
|
+
|
145
183
|
model.attributes.each_value do |attr|
|
146
184
|
define_attribute_methods(attr)
|
147
185
|
end
|
@@ -151,8 +189,14 @@ module Lutaml
|
|
151
189
|
end
|
152
190
|
|
153
191
|
def import_model_mappings(model)
|
154
|
-
|
192
|
+
if model.is_a?(Symbol) || model.is_a?(String)
|
193
|
+
importable_models[:import_model_mappings] << model.to_sym
|
194
|
+
@models_imported = false
|
195
|
+
setup_trace_point
|
196
|
+
return
|
197
|
+
end
|
155
198
|
|
199
|
+
import_model_with_root_error(model)
|
156
200
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
157
201
|
next unless model.mappings.key?(format)
|
158
202
|
|
@@ -178,11 +222,27 @@ module Lutaml
|
|
178
222
|
end
|
179
223
|
|
180
224
|
def import_model(model)
|
225
|
+
if model.is_a?(Symbol) || model.is_a?(String)
|
226
|
+
importable_models[:import_model] << model.to_sym
|
227
|
+
@models_imported = false
|
228
|
+
@choices_imported = false
|
229
|
+
setup_trace_point
|
230
|
+
return
|
231
|
+
end
|
232
|
+
|
181
233
|
import_model_with_root_error(model)
|
182
234
|
import_model_attributes(model)
|
183
235
|
import_model_mappings(model)
|
184
236
|
end
|
185
237
|
|
238
|
+
def importable_models
|
239
|
+
@importable_models ||= MappingHash.new { |h, k| h[k] = [] }
|
240
|
+
end
|
241
|
+
|
242
|
+
def importable_choices
|
243
|
+
@importable_choices ||= MappingHash.new { |h, k| h[k] = MappingHash.new { |h1, k1| h1[k1] = [] } }
|
244
|
+
end
|
245
|
+
|
186
246
|
def add_enum_methods_to_model(klass, enum_name, values, collection: false)
|
187
247
|
add_enum_getter_if_not_defined(klass, enum_name, collection)
|
188
248
|
add_enum_setter_if_not_defined(klass, enum_name, values, collection)
|
@@ -264,15 +324,8 @@ module Lutaml
|
|
264
324
|
end
|
265
325
|
|
266
326
|
def process_mapping(format, &block)
|
267
|
-
# klass = Lutaml::Model.const_get("#{format.to_s.capitalize}Mapping")
|
268
|
-
# mappings[format] ||= klass.new
|
269
|
-
# mappings[format].instance_eval(&block)
|
270
|
-
|
271
|
-
# handle_root_assignment(mappings, format)
|
272
|
-
|
273
327
|
klass = ::Lutaml::Model::Config.mappings_class_for(format)
|
274
328
|
mappings[format] ||= klass.new
|
275
|
-
|
276
329
|
mappings[format].instance_eval(&block)
|
277
330
|
|
278
331
|
if mappings[format].respond_to?(:finalize)
|
@@ -281,24 +334,24 @@ module Lutaml
|
|
281
334
|
end
|
282
335
|
|
283
336
|
def from(format, data, options = {})
|
284
|
-
return data if Utils.uninitialized?(data)
|
285
|
-
|
286
337
|
adapter = Lutaml::Model::Config.adapter_for(format)
|
287
338
|
|
288
339
|
doc = adapter.parse(data, options)
|
289
|
-
|
340
|
+
send("of_#{format}", doc, options)
|
290
341
|
end
|
291
342
|
|
292
343
|
def of(format, doc, options = {})
|
293
|
-
if doc.is_a?(Array)
|
344
|
+
if doc.is_a?(Array) && format != :jsonl
|
294
345
|
return doc.map { |item| send(:"of_#{format}", item) }
|
295
346
|
end
|
296
347
|
|
297
348
|
if format == :xml
|
298
|
-
|
349
|
+
valid = root? || options[:from_collection]
|
350
|
+
raise Lutaml::Model::NoRootMappingError.new(self) unless valid
|
299
351
|
|
300
352
|
options[:encoding] = doc.encoding
|
301
353
|
end
|
354
|
+
options[:register] = extract_register_id(options[:register])
|
302
355
|
|
303
356
|
transformer = Lutaml::Model::Config.transformer_for(format)
|
304
357
|
transformer.data_to_model(self, doc, format, options)
|
@@ -309,7 +362,6 @@ module Lutaml
|
|
309
362
|
adapter = Lutaml::Model::Config.adapter_for(format)
|
310
363
|
|
311
364
|
options[:mapper_class] = self if format == :xml
|
312
|
-
|
313
365
|
adapter.new(value).public_send(:"to_#{format}", options)
|
314
366
|
end
|
315
367
|
|
@@ -335,6 +387,7 @@ module Lutaml
|
|
335
387
|
end
|
336
388
|
|
337
389
|
def mappings_for(format)
|
390
|
+
@mappings[:xml]&.ensure_mappings_imported! if @mappings&.dig(:xml)&.finalized?
|
338
391
|
mappings[format] || default_mappings(format)
|
339
392
|
end
|
340
393
|
|
@@ -350,12 +403,21 @@ module Lutaml
|
|
350
403
|
)
|
351
404
|
end
|
352
405
|
|
353
|
-
mapping.root(
|
406
|
+
mapping.root(Utils.base_class_name(self)) if format == :xml
|
354
407
|
end
|
355
408
|
end
|
356
409
|
|
357
410
|
def apply_mappings(doc, format, options = {})
|
358
|
-
|
411
|
+
register = options[:register] || Lutaml::Model::Config.default_register
|
412
|
+
instance = if options.key?(:instance)
|
413
|
+
options[:instance]
|
414
|
+
elsif model.include?(Lutaml::Model::Serialize)
|
415
|
+
model.new({}, register: register)
|
416
|
+
else
|
417
|
+
object = model.new
|
418
|
+
register_accessor_methods_for(object, register)
|
419
|
+
object
|
420
|
+
end
|
359
421
|
return instance if Utils.blank?(doc)
|
360
422
|
|
361
423
|
mappings = mappings_for(format)
|
@@ -364,7 +426,6 @@ module Lutaml
|
|
364
426
|
return resolve_polymorphic(doc, format, mappings, instance, options)
|
365
427
|
end
|
366
428
|
|
367
|
-
# options[:mappings] = mappings.mappings
|
368
429
|
transformer = Lutaml::Model::Config.transformer_for(format)
|
369
430
|
transformer.data_to_model(self, doc, format, options)
|
370
431
|
end
|
@@ -377,7 +438,7 @@ module Lutaml
|
|
377
438
|
klass_name = polymorphic_mapping.polymorphic_map[klass_key]
|
378
439
|
klass = Object.const_get(klass_name)
|
379
440
|
|
380
|
-
klass.apply_mappings(doc, format, options)
|
441
|
+
klass.apply_mappings(doc, format, options.merge(register: instance.register))
|
381
442
|
end
|
382
443
|
|
383
444
|
def apply_value_map(value, value_map, attr)
|
@@ -400,7 +461,7 @@ module Lutaml
|
|
400
461
|
end
|
401
462
|
|
402
463
|
def empty_object(attr)
|
403
|
-
return
|
464
|
+
return attr.build_collection if attr.collection?
|
404
465
|
|
405
466
|
""
|
406
467
|
end
|
@@ -422,6 +483,72 @@ module Lutaml
|
|
422
483
|
value
|
423
484
|
end
|
424
485
|
end
|
486
|
+
|
487
|
+
def register_accessor_methods_for(object, register)
|
488
|
+
Utils.add_singleton_method_if_not_defined(object, :register) do
|
489
|
+
@register
|
490
|
+
end
|
491
|
+
Utils.add_singleton_method_if_not_defined(object, :register=) do |value|
|
492
|
+
@register = value
|
493
|
+
end
|
494
|
+
object.register = register
|
495
|
+
end
|
496
|
+
|
497
|
+
def extract_register_id(register)
|
498
|
+
if register
|
499
|
+
register.is_a?(Lutaml::Model::Register) ? register.id : register
|
500
|
+
elsif class_variable_defined?(:@@register)
|
501
|
+
class_variable_get(:@@register)
|
502
|
+
else
|
503
|
+
Lutaml::Model::Config.default_register
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def ensure_model_imports!(register_id = nil)
|
508
|
+
return if @models_imported
|
509
|
+
|
510
|
+
register_id ||= Lutaml::Model::Config.default_register
|
511
|
+
register = Lutaml::Model::GlobalRegister.lookup(register_id)
|
512
|
+
importable_models.each do |method, models|
|
513
|
+
models.uniq.each do |model|
|
514
|
+
model_class = register.get_class_without_register(model)
|
515
|
+
import_model_with_root_error(model_class)
|
516
|
+
|
517
|
+
@model.public_send(method, model_class)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
@models_imported = true
|
522
|
+
end
|
523
|
+
|
524
|
+
def ensure_choice_imports!(register_id = nil)
|
525
|
+
return if @choices_imported
|
526
|
+
|
527
|
+
register_id ||= Lutaml::Model::Config.default_register
|
528
|
+
register = Lutaml::Model::GlobalRegister.lookup(register_id)
|
529
|
+
importable_choices.each do |choice, choice_imports|
|
530
|
+
choice_imports.each do |method, models|
|
531
|
+
models.uniq!
|
532
|
+
choice.public_send(method, register.get_class_without_register(models.shift)) until models.empty?
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
@choices_imported = true
|
537
|
+
end
|
538
|
+
|
539
|
+
def setup_trace_point
|
540
|
+
@trace ||= TracePoint.new(:end) do |_tp|
|
541
|
+
if include?(Lutaml::Model::Serialize)
|
542
|
+
@finalized = true
|
543
|
+
@trace.disable
|
544
|
+
end
|
545
|
+
end
|
546
|
+
@trace.enable unless @trace.enabled?
|
547
|
+
end
|
548
|
+
|
549
|
+
def finalized?
|
550
|
+
@finalized
|
551
|
+
end
|
425
552
|
end
|
426
553
|
|
427
554
|
def self.register_format_mapping_method(format)
|
@@ -452,25 +579,27 @@ module Lutaml
|
|
452
579
|
end
|
453
580
|
|
454
581
|
define_method(:"to_#{format}") do |options = {}|
|
455
|
-
|
456
|
-
|
457
|
-
options[:parse_encoding] = encoding if encoding
|
458
|
-
self.class.to(format, self, options)
|
582
|
+
to_format(format, options)
|
459
583
|
end
|
460
584
|
end
|
461
585
|
|
462
|
-
attr_accessor :element_order, :schema_location, :encoding
|
586
|
+
attr_accessor :element_order, :schema_location, :encoding, :register
|
463
587
|
attr_writer :ordered, :mixed
|
464
588
|
|
465
589
|
def initialize(attrs = {}, options = {})
|
466
590
|
@using_default = {}
|
467
591
|
return unless self.class.attributes
|
468
592
|
|
593
|
+
@register = extract_register_id(options[:register])
|
469
594
|
set_ordering(attrs)
|
470
595
|
set_schema_location(attrs)
|
471
596
|
initialize_attributes(attrs, options)
|
472
597
|
end
|
473
598
|
|
599
|
+
def extract_register_id(register)
|
600
|
+
self.class.extract_register_id(register)
|
601
|
+
end
|
602
|
+
|
474
603
|
def value_map(options)
|
475
604
|
{
|
476
605
|
omitted: options[:omitted] || :nil,
|
@@ -479,30 +608,9 @@ module Lutaml
|
|
479
608
|
}
|
480
609
|
end
|
481
610
|
|
482
|
-
def attr_value(attrs, name,
|
483
|
-
value =
|
484
|
-
|
485
|
-
elsif attrs.key?(name.to_s)
|
486
|
-
attrs[name.to_s]
|
487
|
-
else
|
488
|
-
attr_rule.default
|
489
|
-
end
|
490
|
-
|
491
|
-
if attr_rule.collection? || value.is_a?(Array)
|
492
|
-
value&.map do |v|
|
493
|
-
if v.is_a?(Hash)
|
494
|
-
attr_rule.type.new(v)
|
495
|
-
else
|
496
|
-
# TODO: This code is problematic because Type.cast does not know
|
497
|
-
# about all the types.
|
498
|
-
Lutaml::Model::Type.cast(v, attr_rule.type)
|
499
|
-
end
|
500
|
-
end
|
501
|
-
else
|
502
|
-
# TODO: This code is problematic because Type.cast does not know
|
503
|
-
# about all the types.
|
504
|
-
Lutaml::Model::Type.cast(value, attr_rule.type)
|
505
|
-
end
|
611
|
+
def attr_value(attrs, name, attribute)
|
612
|
+
value = Utils.fetch_str_or_sym(attrs, name, attribute.default(register))
|
613
|
+
attribute.cast_value(value, register)
|
506
614
|
end
|
507
615
|
|
508
616
|
def using_default_for(attribute_name)
|
@@ -569,8 +677,22 @@ module Lutaml
|
|
569
677
|
self.class.as_yaml(self)
|
570
678
|
end
|
571
679
|
|
680
|
+
def to_format(format, options = {})
|
681
|
+
validate_root_mapping!(format, options)
|
682
|
+
|
683
|
+
options[:parse_encoding] = encoding if encoding
|
684
|
+
self.class.to(format, self, options)
|
685
|
+
end
|
686
|
+
|
572
687
|
private
|
573
688
|
|
689
|
+
def validate_root_mapping!(format, options)
|
690
|
+
return if format != :xml
|
691
|
+
return if options[:collection] || self.class.root?
|
692
|
+
|
693
|
+
raise Lutaml::Model::NoRootMappingError.new(self.class)
|
694
|
+
end
|
695
|
+
|
574
696
|
def set_ordering(attrs)
|
575
697
|
return unless attrs.respond_to?(:ordered?)
|
576
698
|
|
@@ -600,9 +722,9 @@ module Lutaml
|
|
600
722
|
def determine_value(attrs, name, attr)
|
601
723
|
if attrs.key?(name) || attrs.key?(name.to_s)
|
602
724
|
attr_value(attrs, name, attr)
|
603
|
-
elsif attr.default_set?
|
725
|
+
elsif attr.default_set?(register)
|
604
726
|
using_default_for(name)
|
605
|
-
attr.default
|
727
|
+
attr.default(register)
|
606
728
|
else
|
607
729
|
Lutaml::Model::UninitializedClass.instance
|
608
730
|
end
|