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
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "../mapping/key_value_mapping"
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module Jsonl
|
6
|
+
class Mapping < Lutaml::Model::KeyValueMapping
|
7
|
+
def initialize
|
8
|
+
super(:jsonl)
|
9
|
+
end
|
10
|
+
|
11
|
+
def deep_dup
|
12
|
+
self.class.new.tap do |new_mapping|
|
13
|
+
new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "json"
|
2
|
+
require_relative "document"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module Jsonl
|
7
|
+
class StandardAdapter < Document
|
8
|
+
FORMAT_SYMBOL = :jsonl
|
9
|
+
|
10
|
+
def self.parse(jsonl, _options = {})
|
11
|
+
results = []
|
12
|
+
jsonl.split("\n").each do |line|
|
13
|
+
next if line.strip.empty?
|
14
|
+
|
15
|
+
begin
|
16
|
+
results << JSON.parse(line, create_additions: false)
|
17
|
+
rescue JSON::ParserError => e
|
18
|
+
warn "Skipping invalid line: #{e.message}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
results
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_jsonl(*_args)
|
26
|
+
@jsons.map do |json|
|
27
|
+
JSON.generate(json)
|
28
|
+
end.join("\n")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
module Jsonl
|
4
|
+
class Transform < Lutaml::Model::KeyValueTransform
|
5
|
+
def data_to_model(data, format, options = {})
|
6
|
+
mappings = defined_mappings_for(:jsonl) || mappings_for(:json)
|
7
|
+
|
8
|
+
super(data, format, options.merge(mappings: mappings))
|
9
|
+
end
|
10
|
+
|
11
|
+
def model_to_data(instance, format, options = {})
|
12
|
+
mappings = defined_mappings_for(:jsonl) || mappings_for(:json)
|
13
|
+
|
14
|
+
super(instance, format, options.merge(mappings: mappings))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module Jsonl
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative "jsonl/standard_adapter"
|
11
|
+
require_relative "jsonl/document"
|
12
|
+
require_relative "jsonl/mapping"
|
13
|
+
require_relative "jsonl/mapping_rule"
|
14
|
+
require_relative "jsonl/transform"
|
15
|
+
|
16
|
+
Lutaml::Model::FormatRegistry.register(
|
17
|
+
:jsonl,
|
18
|
+
mapping_class: Lutaml::Model::Jsonl::Mapping,
|
19
|
+
adapter_class: Lutaml::Model::Jsonl::StandardAdapter,
|
20
|
+
transformer: Lutaml::Model::Jsonl::Transform,
|
21
|
+
)
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Lutaml
|
2
2
|
module Model
|
3
3
|
class KeyValueDocument
|
4
|
-
attr_reader :attributes
|
4
|
+
attr_reader :attributes, :register
|
5
5
|
|
6
|
-
def initialize(attributes = {})
|
6
|
+
def initialize(attributes = {}, register: nil)
|
7
7
|
@attributes = attributes
|
8
|
+
@register = register || Lutaml::Model::Config.default_register
|
8
9
|
end
|
9
10
|
|
10
11
|
def [](key)
|
@@ -4,7 +4,7 @@ require_relative "key_value_mapping_rule"
|
|
4
4
|
module Lutaml
|
5
5
|
module Model
|
6
6
|
class KeyValueMapping < Mapping
|
7
|
-
attr_reader :mappings, :format
|
7
|
+
attr_reader :mappings, :format, :key_mappings, :value_mappings
|
8
8
|
|
9
9
|
def initialize(format = nil)
|
10
10
|
super()
|
@@ -12,6 +12,22 @@ module Lutaml
|
|
12
12
|
@format = format
|
13
13
|
end
|
14
14
|
|
15
|
+
def root(name = nil)
|
16
|
+
@root = name
|
17
|
+
end
|
18
|
+
|
19
|
+
def no_root
|
20
|
+
@root = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def no_root?
|
24
|
+
@root.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def root_name
|
28
|
+
@root
|
29
|
+
end
|
30
|
+
|
15
31
|
def map(
|
16
32
|
name = nil,
|
17
33
|
to: nil,
|
@@ -76,6 +92,39 @@ module Lutaml
|
|
76
92
|
|
77
93
|
alias map_all_content map_all
|
78
94
|
|
95
|
+
def map_instances(to:)
|
96
|
+
map(root_name || to, to: to)
|
97
|
+
end
|
98
|
+
|
99
|
+
def map_key(to_instance: nil, as_attribute: nil)
|
100
|
+
@key_mappings = KeyValueMappingRule.new(
|
101
|
+
Constants::KEY_MAPPING_KEY,
|
102
|
+
to: nil,
|
103
|
+
to_instance: to_instance,
|
104
|
+
as_attribute: as_attribute,
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
def map_value(to_instance: nil, as_attribute: nil)
|
109
|
+
@value_mappings = KeyValueMappingRule.new(
|
110
|
+
Constants::VALUE_MAPPING_KEY,
|
111
|
+
to: nil,
|
112
|
+
to_instance: to_instance,
|
113
|
+
as_attribute: as_attribute,
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
def key_value_mappings
|
118
|
+
{
|
119
|
+
key: @key_mappings,
|
120
|
+
value: @value_mappings,
|
121
|
+
}.compact
|
122
|
+
end
|
123
|
+
|
124
|
+
def key_value_mappings?
|
125
|
+
Utils.present?(@key_mappings) || Utils.present?(@value_mappings)
|
126
|
+
end
|
127
|
+
|
79
128
|
def name_for_mapping(root_mappings, name)
|
80
129
|
return "root_mapping" if root_mappings
|
81
130
|
|
@@ -112,8 +161,10 @@ module Lutaml
|
|
112
161
|
end
|
113
162
|
|
114
163
|
def validate_root_mappings!(name)
|
115
|
-
if
|
116
|
-
raise MultipleMappingsError.new(
|
164
|
+
if root_mapping || (name == "root_mapping" && @mappings.any?)
|
165
|
+
raise MultipleMappingsError.new(
|
166
|
+
"root_mappings cannot be used with other mappings",
|
167
|
+
)
|
117
168
|
end
|
118
169
|
end
|
119
170
|
|
@@ -126,7 +177,8 @@ module Lutaml
|
|
126
177
|
end
|
127
178
|
|
128
179
|
def validate_mappings!(_type)
|
129
|
-
if (@raw_mapping && Utils.present?(@mappings)) ||
|
180
|
+
if (@raw_mapping && Utils.present?(@mappings)) ||
|
181
|
+
(!@raw_mapping && @mappings.any?(&:raw_mapping?))
|
130
182
|
raise StandardError, "map_all is not allowed with other mappings"
|
131
183
|
end
|
132
184
|
end
|
@@ -145,10 +197,18 @@ module Lutaml
|
|
145
197
|
@mappings.find { |m| m.to.to_s == to.to_s }
|
146
198
|
end
|
147
199
|
|
200
|
+
def find_by_name(name)
|
201
|
+
@mappings.find { |m| m.name.to_s == name.to_s }
|
202
|
+
end
|
203
|
+
|
148
204
|
def polymorphic_mapping
|
149
205
|
@mappings.find(&:polymorphic_mapping?)
|
150
206
|
end
|
151
207
|
|
208
|
+
def root_mapping
|
209
|
+
@mappings.find(&:root_mapping?)
|
210
|
+
end
|
211
|
+
|
152
212
|
Lutaml::Model::Config::KEY_VALUE_FORMATS.each do |format|
|
153
213
|
define_method(:"format_#{format}?") do
|
154
214
|
@format == format
|
@@ -9,6 +9,8 @@ module Lutaml
|
|
9
9
|
def initialize(
|
10
10
|
name,
|
11
11
|
to:,
|
12
|
+
to_instance: nil,
|
13
|
+
as_attribute: nil,
|
12
14
|
render_nil: false,
|
13
15
|
render_default: false,
|
14
16
|
render_empty: false,
|
@@ -27,6 +29,8 @@ module Lutaml
|
|
27
29
|
super(
|
28
30
|
name,
|
29
31
|
to: to,
|
32
|
+
to_instance: to_instance,
|
33
|
+
as_attribute: as_attribute,
|
30
34
|
render_nil: render_nil,
|
31
35
|
render_default: render_default,
|
32
36
|
render_empty: render_empty,
|
@@ -3,6 +3,8 @@ module Lutaml
|
|
3
3
|
class MappingRule
|
4
4
|
attr_reader :name,
|
5
5
|
:to,
|
6
|
+
:to_instance,
|
7
|
+
:as_attribute,
|
6
8
|
:render_nil,
|
7
9
|
:render_default,
|
8
10
|
:render_empty,
|
@@ -44,6 +46,8 @@ module Lutaml
|
|
44
46
|
def initialize(
|
45
47
|
name,
|
46
48
|
to:,
|
49
|
+
to_instance: nil,
|
50
|
+
as_attribute: nil,
|
47
51
|
render_nil: false,
|
48
52
|
render_default: false,
|
49
53
|
render_empty: false,
|
@@ -61,6 +65,8 @@ module Lutaml
|
|
61
65
|
)
|
62
66
|
@name = name
|
63
67
|
@to = to
|
68
|
+
@to_instance = to_instance
|
69
|
+
@as_attribute = as_attribute
|
64
70
|
@render_nil = render_nil
|
65
71
|
@render_default = render_default
|
66
72
|
@render_empty = render_empty
|
@@ -212,8 +218,7 @@ module Lutaml
|
|
212
218
|
def deserialize(model, value, attributes, mapper_class = nil)
|
213
219
|
handle_custom_method(model, value, mapper_class) ||
|
214
220
|
handle_delegate(model, value, attributes) ||
|
215
|
-
handle_transform_method(model, value, attributes)
|
216
|
-
assign_value(model, value)
|
221
|
+
handle_transform_method(model, value, attributes)
|
217
222
|
end
|
218
223
|
|
219
224
|
def has_custom_method_for_serialization?
|
@@ -284,7 +289,7 @@ module Lutaml
|
|
284
289
|
delegate_value = model.public_send(delegate)
|
285
290
|
return if Utils.initialized?(delegate_value) && !delegate_value.nil?
|
286
291
|
|
287
|
-
model.public_send(:"#{delegate}=", attributes[delegate].type.new)
|
292
|
+
model.public_send(:"#{delegate}=", attributes[delegate].type(model.register).new)
|
288
293
|
end
|
289
294
|
|
290
295
|
def handle_transform_method(model, value, attributes)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
class Register
|
6
|
+
attr_reader :id, :models
|
7
|
+
|
8
|
+
def initialize(id)
|
9
|
+
@id = id
|
10
|
+
@models = {}
|
11
|
+
@global_substitutions = {}
|
12
|
+
@type_class_cache = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_model(klass, id: nil)
|
16
|
+
id ||= Utils.base_class_snake_case(klass).to_sym
|
17
|
+
return Lutaml::Model::Type.register(id, klass) if klass <= Lutaml::Model::Type::Value
|
18
|
+
raise NotRegistrableClassError.new(klass) unless klass.include?(Lutaml::Model::Registrable)
|
19
|
+
|
20
|
+
@models[id.to_sym] = klass
|
21
|
+
@models[klass.to_s] = klass
|
22
|
+
end
|
23
|
+
|
24
|
+
def resolve(klass_str)
|
25
|
+
@models[klass_str.to_s]
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_class(klass_name)
|
29
|
+
expected_class = get_class_without_register(klass_name)
|
30
|
+
if !(expected_class < Lutaml::Model::Type::Value)
|
31
|
+
expected_class.class_variable_set(:@@register, id)
|
32
|
+
end
|
33
|
+
expected_class
|
34
|
+
end
|
35
|
+
|
36
|
+
def register_model_tree(klass)
|
37
|
+
register_model(klass)
|
38
|
+
if klass.include?(Lutaml::Model::Serialize)
|
39
|
+
register_attributes(klass.attributes)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def register_global_type_substitution(from_type:, to_type:)
|
44
|
+
@global_substitutions[from_type] = to_type
|
45
|
+
end
|
46
|
+
|
47
|
+
def register_attributes(attributes)
|
48
|
+
attributes.each_value do |attribute|
|
49
|
+
next unless attribute.unresolved_type.is_a?(Class)
|
50
|
+
next if built_in_type?(attribute.unresolved_type) || attribute.unresolved_type.nil?
|
51
|
+
|
52
|
+
register_model_tree(attribute.unresolved_type)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def substitutable?(klass)
|
57
|
+
@global_substitutions.key?(klass)
|
58
|
+
end
|
59
|
+
|
60
|
+
def substitute(klass)
|
61
|
+
@global_substitutions[klass]
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_class_without_register(klass_name)
|
65
|
+
klass = extract_class_from(klass_name)
|
66
|
+
raise Lutaml::Model::UnknownTypeError.new(klass_name) unless klass
|
67
|
+
|
68
|
+
substitute(klass) || substitute(klass_name) || klass
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def get_type_class(klass_name)
|
74
|
+
if klass_name.is_a?(String)
|
75
|
+
Lutaml::Model::Type.const_get(klass_name)
|
76
|
+
elsif klass_name.is_a?(Symbol)
|
77
|
+
Lutaml::Model::Type.lookup(klass_name)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def built_in_type?(type)
|
82
|
+
Lutaml::Model::Type::TYPE_CODES.value?(type.inspect) ||
|
83
|
+
Lutaml::Model::Type::TYPE_CODES.key?(type.to_s.to_sym)
|
84
|
+
end
|
85
|
+
|
86
|
+
def extract_class_from(klass)
|
87
|
+
return klass if klass.is_a?(Class)
|
88
|
+
return @models[klass] if @models.key?(klass)
|
89
|
+
|
90
|
+
# Check cache first
|
91
|
+
return @type_class_cache[klass] if @type_class_cache.key?(klass)
|
92
|
+
|
93
|
+
# If not in cache, try to get type class and cache it
|
94
|
+
if type_klass = get_type_class(klass)
|
95
|
+
@type_class_cache[klass] = type_klass
|
96
|
+
type_klass
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def clear_type_class_cache
|
101
|
+
@type_class_cache.clear
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative "generator/definitions_collection"
|
2
|
+
# require_relative "generator/collection"
|
3
|
+
require_relative "generator/ref"
|
4
|
+
|
5
|
+
module Lutaml
|
6
|
+
module Model
|
7
|
+
module Schema
|
8
|
+
class BaseSchema
|
9
|
+
class << self
|
10
|
+
def generate(klass, options = {})
|
11
|
+
schema = new(
|
12
|
+
klass,
|
13
|
+
schema: options[:schema],
|
14
|
+
id: options[:id],
|
15
|
+
title: options[:title],
|
16
|
+
description: options[:description],
|
17
|
+
).generate_schema_hash
|
18
|
+
|
19
|
+
format_schema(schema, options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :schema, :klass
|
24
|
+
attr_accessor :id, :title, :description
|
25
|
+
|
26
|
+
def initialize(klass, schema:, id: nil, title: nil, description: nil)
|
27
|
+
@schema = schema
|
28
|
+
@klass = klass
|
29
|
+
@id = id
|
30
|
+
@title = title
|
31
|
+
@description = description
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate_schema_hash
|
35
|
+
{
|
36
|
+
"$schema" => schema,
|
37
|
+
"$id" => id,
|
38
|
+
"description" => description,
|
39
|
+
"$ref" => "#/$defs/#{klass.name.gsub('::', '_')}",
|
40
|
+
"$defs" => generate_definitions(klass),
|
41
|
+
}.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def polymorphic?(attr)
|
47
|
+
Utils.present?(attr.options[:polymorphic])
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_definitions(klass)
|
51
|
+
Generator::DefinitionsCollection.from_class(klass).to_schema
|
52
|
+
end
|
53
|
+
|
54
|
+
def generate_polymorphic_definitions(attr)
|
55
|
+
return {} unless polymorphic?(attr)
|
56
|
+
|
57
|
+
attr.options[:polymorphic].each_with_object({}) do |child, result|
|
58
|
+
result.merge!(generate_definitions(child))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module Schema
|
6
|
+
module Decorators
|
7
|
+
class Attribute
|
8
|
+
ATTRIBUTE_TYPES = {
|
9
|
+
"string" => ":string",
|
10
|
+
"integer" => ":integer",
|
11
|
+
"boolean" => ":boolean",
|
12
|
+
"number" => ":float",
|
13
|
+
"object" => ":hash",
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
attr_reader :name, :options, :polymorphic
|
17
|
+
attr_accessor :type, :base_class
|
18
|
+
|
19
|
+
# @param attribute [Hash] The JSON schema attribute to be decorated.
|
20
|
+
def initialize(name, options)
|
21
|
+
@name = name
|
22
|
+
@options = options
|
23
|
+
@type = extract_type(options)
|
24
|
+
@polymorphic = options["oneOf"]&.map { |choice| choice["$ref"].split("/").last }
|
25
|
+
end
|
26
|
+
|
27
|
+
def default
|
28
|
+
@options["default"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def polymorphic?
|
32
|
+
!!@polymorphic
|
33
|
+
end
|
34
|
+
|
35
|
+
def polymorphic_classes
|
36
|
+
return [] unless polymorphic?
|
37
|
+
|
38
|
+
@base_class.sub_classes.map do |klass|
|
39
|
+
klass.namespaced_name.gsub("_", "::")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def collection?
|
44
|
+
@options["type"] == "array"
|
45
|
+
end
|
46
|
+
|
47
|
+
def collection
|
48
|
+
return unless collection?
|
49
|
+
|
50
|
+
if @options["minItems"] && @options["maxItems"]
|
51
|
+
@options["minItems"]..@options["maxItems"]
|
52
|
+
elsif @options["minItems"]
|
53
|
+
@options["minItems"]..Float::INFINITY
|
54
|
+
elsif @options["maxItems"]
|
55
|
+
0..@options["maxItems"]
|
56
|
+
else
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def choice?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def lutaml_type_symbol(type)
|
68
|
+
return ":string" if type.nil?
|
69
|
+
|
70
|
+
type = type.first if type.is_a?(Array)
|
71
|
+
|
72
|
+
return array_type(options) if type == "array"
|
73
|
+
return object_type(options) if type == "object"
|
74
|
+
|
75
|
+
ATTRIBUTE_TYPES[type] || ":string"
|
76
|
+
end
|
77
|
+
|
78
|
+
def array_type(options)
|
79
|
+
lutaml_type_symbol(options["items"]["type"])
|
80
|
+
end
|
81
|
+
|
82
|
+
def extract_type(options)
|
83
|
+
return extract_type(options["items"]) if options["type"] == "array"
|
84
|
+
|
85
|
+
if options["type"]
|
86
|
+
lutaml_type_symbol(options["type"])
|
87
|
+
elsif options["$ref"]
|
88
|
+
options["$ref"].split("/").last.gsub("_", "::")
|
89
|
+
else
|
90
|
+
":string" # Default to string if type is not recognized
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def object_type(options)
|
95
|
+
if options["oneOf"]
|
96
|
+
{
|
97
|
+
"polymorphic" => options["oneOf"].map do |choice|
|
98
|
+
choice["$ref"].split("/").last.gsub("_", "::")
|
99
|
+
end,
|
100
|
+
}
|
101
|
+
|
102
|
+
elsif options["properties"]
|
103
|
+
options["properties"].map do |name, attr|
|
104
|
+
Attribute.new(name, attr)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
":hash" # Default to hash if no specific type is defined
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "attribute"
|
4
|
+
|
5
|
+
module Lutaml
|
6
|
+
module Model
|
7
|
+
module Schema
|
8
|
+
module Decorators
|
9
|
+
class Choices
|
10
|
+
attr_reader :attributes
|
11
|
+
|
12
|
+
# Decorates a collection of choice attributes.
|
13
|
+
# This class is used to handle attributes that are part of a choice
|
14
|
+
# constraint in a JSON schema. It provides a way to access the choice
|
15
|
+
# attributes in a structured manner.
|
16
|
+
def initialize(attributes)
|
17
|
+
@attributes = attributes.values
|
18
|
+
end
|
19
|
+
|
20
|
+
def choice?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def polymorphic?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|