lutaml-model 0.8.17 → 0.8.18
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/.github/workflows/downstream-performance.yml +27 -1
- data/.github/workflows/js-pr-check.yml +102 -0
- data/.github/workflows/js-sync.yml +51 -0
- data/.github/workflows/opal.yml +26 -6
- data/.github/workflows/performance.yml +32 -7
- data/.github/workflows/rake.yml +78 -32
- data/.gitmodules +6 -0
- data/.rubocop_todo.yml +8 -0
- data/Gemfile +16 -1
- data/README.adoc +267 -0
- data/Rakefile +166 -4
- data/docs/_guides/schema-import.adoc +42 -0
- data/docs/_guides/xml-mapping.adoc +21 -0
- data/docs/_pages/validation.adoc +65 -0
- data/lib/compat/opal/generate_boot.rb +123 -0
- data/lib/compat/opal/js_bundle_entry.rb +42 -0
- data/lib/compat/opal/lutaml_model_boot.rb +497 -0
- data/lib/compat/opal/moxml_boot.rb +68 -0
- data/lib/compat/opal/yaml_compat.rb +32 -0
- data/lib/lutaml/json.rb +1 -1
- data/lib/lutaml/key_value/transform.rb +3 -8
- data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
- data/lib/lutaml/key_value/transformation.rb +17 -5
- data/lib/lutaml/model/adapter_resolver.rb +2 -2
- data/lib/lutaml/model/attribute.rb +88 -11
- data/lib/lutaml/model/cached_type_resolver.rb +1 -1
- data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
- data/lib/lutaml/model/global_context.rb +11 -8
- data/lib/lutaml/model/instrumentation.rb +2 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
- data/lib/lutaml/model/register.rb +2 -2
- data/lib/lutaml/model/runtime_compatibility.rb +50 -0
- data/lib/lutaml/model/schema/class_loader.rb +58 -0
- data/lib/lutaml/model/schema/compiled_output.rb +83 -0
- data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
- data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
- data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
- data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
- data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
- data/lib/lutaml/model/schema/definitions/model.rb +40 -0
- data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
- data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
- data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
- data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
- data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
- data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
- data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
- data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
- data/lib/lutaml/model/schema/definitions.rb +24 -0
- data/lib/lutaml/model/schema/file_writer.rb +70 -0
- data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
- data/lib/lutaml/model/schema/generator/property.rb +21 -4
- data/lib/lutaml/model/schema/module_nesting.rb +31 -0
- data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
- data/lib/lutaml/model/schema/registry_generator.rb +115 -0
- data/lib/lutaml/model/schema/renderers/base.rb +40 -0
- data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
- data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
- data/lib/lutaml/model/schema/renderers/model.rb +121 -0
- data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
- data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
- data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
- data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
- data/lib/lutaml/model/schema/renderers/union.rb +83 -0
- data/lib/lutaml/model/schema/renderers.rb +19 -0
- data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
- data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
- data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
- data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
- data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
- data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
- data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
- data/lib/lutaml/model/schema/templates.rb +162 -0
- data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
- data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
- data/lib/lutaml/model/schema.rb +19 -0
- data/lib/lutaml/model/serialize/attribute_definition.rb +14 -2
- data/lib/lutaml/model/serialize/builder.rb +15 -2
- data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
- data/lib/lutaml/model/serialize.rb +6 -9
- data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
- data/lib/lutaml/model/services.rb +0 -2
- data/lib/lutaml/model/store.rb +2 -1
- data/lib/lutaml/model/toml.rb +1 -1
- data/lib/lutaml/model/transform.rb +17 -39
- data/lib/lutaml/model/transformation_registry.rb +1 -1
- data/lib/lutaml/model/type.rb +1 -0
- data/lib/lutaml/model/union.rb +320 -0
- data/lib/lutaml/model/utils.rb +6 -0
- data/lib/lutaml/model/validation.rb +1 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +18 -0
- data/lib/lutaml/toml.rb +1 -1
- data/lib/lutaml/xml/adapter.rb +1 -1
- data/lib/lutaml/xml/adapter_loader.rb +1 -1
- data/lib/lutaml/xml/builder/base.rb +1 -1
- data/lib/lutaml/xml/builder.rb +1 -1
- data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
- data/lib/lutaml/xml/model_transform.rb +5 -1
- data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
- data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
- data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
- data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
- data/lib/lutaml/xml/transformation/element_builder.rb +21 -8
- data/lib/lutaml/xml/transformation/ordered_applier.rb +6 -0
- data/lib/lutaml/xml/xsd_validator.rb +66 -0
- data/lib/lutaml/xml.rb +53 -12
- data/lutaml-model.gemspec +4 -2
- data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
- data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
- data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
- data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
- data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
- data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
- data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
- data/spec/fixtures/xml/schema/rng/book.rng +27 -0
- data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
- data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
- data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
- data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
- data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
- data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
- data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
- data/spec/fixtures/xml/schema/rng/person.rng +23 -0
- data/spec/fixtures/xml/schema/rng/union.rng +14 -0
- data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
- data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
- data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
- data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
- data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
- data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
- data/spec/lutaml/model/attribute_spec.rb +169 -12
- data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
- data/spec/lutaml/model/collection_spec.rb +18 -0
- data/spec/lutaml/model/mixed_content_spec.rb +171 -0
- data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
- data/spec/lutaml/model/ordered_content_spec.rb +30 -0
- data/spec/lutaml/model/raw_element_spec.rb +125 -0
- data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
- data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
- data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
- data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
- data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
- data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
- data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
- data/spec/lutaml/model/union_attribute_spec.rb +658 -0
- data/spec/lutaml/model/union_spec.rb +257 -0
- data/spec/lutaml/model/utils_spec.rb +14 -0
- data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
- data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
- data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
- data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
- data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
- data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
- data/spec/spec_helper.rb +28 -11
- data/spec/support/opal.rb +5 -2
- metadata +97 -31
- data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
- data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
- data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
- data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
- data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
- data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
- data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
- data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
- data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
- data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
- data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
- data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
- data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
- data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
- data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
- data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
- data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
- data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
- data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
- data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
- data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
- data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
- data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
- data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
- data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
- data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Lutaml
|
|
4
|
-
module Model
|
|
5
|
-
module Schema
|
|
6
|
-
module XmlCompiler
|
|
7
|
-
class SimpleType
|
|
8
|
-
attr_accessor :class_name, :base_class, :instance, :unions
|
|
9
|
-
|
|
10
|
-
LUTAML_VALUE_CLASS_NAME = "Lutaml::Model::Type::Value"
|
|
11
|
-
|
|
12
|
-
SUPPORTED_DATA_TYPES = {
|
|
13
|
-
nonNegativeInteger: { skippable: false,
|
|
14
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: /\+?[0-9]+/ } },
|
|
15
|
-
normalizedString: { skippable: false,
|
|
16
|
-
class_name: "Lutaml::Model::Type::String", validations: { transform: "value.gsub(/[\\r\\n\\t]/, ' ')" } },
|
|
17
|
-
positiveInteger: { skippable: false,
|
|
18
|
-
class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0 } },
|
|
19
|
-
unsignedShort: { skippable: false,
|
|
20
|
-
class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 65535 } },
|
|
21
|
-
base64Binary: { skippable: false,
|
|
22
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A([A-Za-z0-9+\/]+={0,2}|\s)*\z/ } },
|
|
23
|
-
unsignedLong: { skippable: false,
|
|
24
|
-
class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 18446744073709551615 } },
|
|
25
|
-
unsignedByte: { skippable: false,
|
|
26
|
-
class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 255 } },
|
|
27
|
-
unsignedInt: { skippable: false,
|
|
28
|
-
class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: 0, max_inclusive: 4294967295 } },
|
|
29
|
-
hexBinary: { skippable: false,
|
|
30
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: /([0-9a-fA-F]{2})*/ } },
|
|
31
|
-
language: { skippable: false,
|
|
32
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\z/ } },
|
|
33
|
-
dateTime: { skippable: true,
|
|
34
|
-
class_name: "Lutaml::Model::Type::DateTime" },
|
|
35
|
-
boolean: { skippable: true,
|
|
36
|
-
class_name: "Lutaml::Model::Type::Boolean" },
|
|
37
|
-
integer: { skippable: true,
|
|
38
|
-
class_name: "Lutaml::Model::Type::Integer" },
|
|
39
|
-
decimal: { skippable: true,
|
|
40
|
-
class_name: "Lutaml::Model::Type::Decimal" },
|
|
41
|
-
string: { skippable: true,
|
|
42
|
-
class_name: "Lutaml::Model::Type::String" },
|
|
43
|
-
double: { skippable: true,
|
|
44
|
-
class_name: "Lutaml::Model::Type::Float" },
|
|
45
|
-
NCName: { skippable: false,
|
|
46
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A[a-zA-Z_][\w.-]*\z/ } },
|
|
47
|
-
anyURI: { skippable: false,
|
|
48
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: "\\A\#{URI::DEFAULT_PARSER.make_regexp(%w[http https ftp])}\\z" } },
|
|
49
|
-
token: { skippable: false,
|
|
50
|
-
class_name: "Lutaml::Model::Type::String", validations: { pattern: /\A[^\t\n\f\r ]+(?: [^\t\n\f\r ]+)*\z/ } },
|
|
51
|
-
byte: { skippable: false,
|
|
52
|
-
class_name: "Lutaml::Model::Type::Integer", validations: { min_inclusive: -128, max_inclusive: 127 } },
|
|
53
|
-
long: { skippable: false,
|
|
54
|
-
class_name: "Lutaml::Model::Type::Decimal" },
|
|
55
|
-
int: { skippable: true,
|
|
56
|
-
class_name: "Lutaml::Model::Type::Integer" },
|
|
57
|
-
id: { skippable: false, class_name: "Lutaml::Model::Type::String",
|
|
58
|
-
validations: { pattern: /\A[a-zA-Z_][\w.-]*\z/ } },
|
|
59
|
-
}.freeze
|
|
60
|
-
|
|
61
|
-
INSTANCE_MODEL_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
|
|
62
|
-
# frozen_string_literal: true
|
|
63
|
-
require "lutaml/model"
|
|
64
|
-
|
|
65
|
-
<%= "\#{required_files}\n" -%>
|
|
66
|
-
<%= module_opening -%>
|
|
67
|
-
class <%= klass_name %><%= " < \#{parent_class}" if parent_class %>
|
|
68
|
-
<%= @indent %>def self.cast(value, options = {})
|
|
69
|
-
<%= extended_indent %>return if value.nil?
|
|
70
|
-
|
|
71
|
-
<%= instance&.to_method_body(extended_indent) %>
|
|
72
|
-
value = super(value, options)
|
|
73
|
-
value
|
|
74
|
-
end
|
|
75
|
-
<%= registration_methods -%>
|
|
76
|
-
end
|
|
77
|
-
<%= module_closing -%>
|
|
78
|
-
<%= registration_execution -%>
|
|
79
|
-
TEMPLATE
|
|
80
|
-
|
|
81
|
-
UNION_MODEL_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
|
|
82
|
-
# frozen_string_literal: true
|
|
83
|
-
require "lutaml/model"
|
|
84
|
-
<%= union_required_files %>
|
|
85
|
-
|
|
86
|
-
<%= module_opening -%>
|
|
87
|
-
class <%= klass_name %> < <%= LUTAML_VALUE_CLASS_NAME %>
|
|
88
|
-
<%= @indent %>def self.cast(value, options = {})
|
|
89
|
-
<%= extended_indent %>return if value.nil?
|
|
90
|
-
|
|
91
|
-
<%= union_class_method_body %>
|
|
92
|
-
end
|
|
93
|
-
<%= registration_methods -%>
|
|
94
|
-
end
|
|
95
|
-
<%= module_closing -%>
|
|
96
|
-
<%= registration_execution -%>
|
|
97
|
-
TEMPLATE
|
|
98
|
-
|
|
99
|
-
def initialize(name, unions = [])
|
|
100
|
-
@class_name = name
|
|
101
|
-
@unions = unions
|
|
102
|
-
@module_namespace = nil
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def to_class(options: {})
|
|
106
|
-
setup_options(options)
|
|
107
|
-
template = unions&.any? ? UNION_MODEL_TEMPLATE : INSTANCE_MODEL_TEMPLATE
|
|
108
|
-
template.result(binding)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def required_files
|
|
112
|
-
files = Array(instance&.required_files)
|
|
113
|
-
# Don't add requires for parent classes if using module namespace
|
|
114
|
-
# They're handled via central autoload registry
|
|
115
|
-
if !@module_namespace && require_parent?
|
|
116
|
-
files << "require_relative \"#{Utils.snake_case(parent_class)}\""
|
|
117
|
-
end
|
|
118
|
-
# Filter out require_relative when using autoload, keep external requires
|
|
119
|
-
if @module_namespace
|
|
120
|
-
files = files.select { |f| f.start_with?("require \"") }
|
|
121
|
-
end
|
|
122
|
-
files.join("\n")
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
private
|
|
126
|
-
|
|
127
|
-
def setup_options(options)
|
|
128
|
-
@indent = " " * options&.fetch(:indent, 2)
|
|
129
|
-
@extended_indent = @indent * 2
|
|
130
|
-
@module_namespace = options[:module_namespace]
|
|
131
|
-
@modules = @module_namespace&.split("::") || []
|
|
132
|
-
@register_id = options[:register_id]
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def module_opening
|
|
136
|
-
return "" if @modules.empty?
|
|
137
|
-
|
|
138
|
-
@modules.map.with_index do |mod, i|
|
|
139
|
-
"#{' ' * i}module #{mod}"
|
|
140
|
-
end.join("\n") + "\n"
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def module_closing
|
|
144
|
-
return "" if @modules.empty?
|
|
145
|
-
|
|
146
|
-
@modules.reverse.map.with_index do |_mod, i|
|
|
147
|
-
"#{' ' * (@modules.size - i - 1)}end"
|
|
148
|
-
end.join("\n")
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def registration_methods
|
|
152
|
-
if @module_namespace
|
|
153
|
-
# Still need register method for union types that call register.get_class
|
|
154
|
-
<<~REGISTRATION
|
|
155
|
-
|
|
156
|
-
#{@indent}def self.register
|
|
157
|
-
#{extended_indent}Lutaml::Model::Config.default_register
|
|
158
|
-
#{@indent}end
|
|
159
|
-
REGISTRATION
|
|
160
|
-
else
|
|
161
|
-
<<~REGISTRATION
|
|
162
|
-
|
|
163
|
-
#{@indent}def self.register
|
|
164
|
-
#{extended_indent}@register ||= Lutaml::Model::Config.default_register
|
|
165
|
-
#{@indent}end
|
|
166
|
-
|
|
167
|
-
#{@indent}def self.register_class_with_id
|
|
168
|
-
#{extended_indent}context = Lutaml::Model::GlobalContext.context(Lutaml::Model::Config.default_register)
|
|
169
|
-
#{extended_indent}context.registry.register(:#{Utils.snake_case(class_name)}, self)
|
|
170
|
-
#{@indent}end
|
|
171
|
-
REGISTRATION
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def registration_execution
|
|
176
|
-
return "" if @module_namespace
|
|
177
|
-
|
|
178
|
-
"\n#{klass_name}.register_class_with_id"
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def klass_name
|
|
182
|
-
@klass_name ||= Utils.camel_case(class_name)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def require_parent?
|
|
186
|
-
return false if Utils.blank?(base_class)
|
|
187
|
-
|
|
188
|
-
!!!SUPPORTED_DATA_TYPES[base_class&.to_sym]&.dig(:skippable)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def parent_class
|
|
192
|
-
type_info = SUPPORTED_DATA_TYPES[base_class&.to_sym]
|
|
193
|
-
return type_info[:class_name] if type_info&.dig(:skippable)
|
|
194
|
-
return Utils.camel_case(base_class.to_s) if !!!type_info&.dig(:skippable) && Utils.present?(base_class)
|
|
195
|
-
|
|
196
|
-
LUTAML_VALUE_CLASS_NAME
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def union_class_method_body
|
|
200
|
-
unions.map do |union|
|
|
201
|
-
"#{extended_indent}Lutaml::Model::GlobalContext.resolve_type(:#{down_union_class_name(union)}, @register).cast(value, options)"
|
|
202
|
-
end.join(" ||\n ")
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
def union_required_files
|
|
206
|
-
# Don't add requires for union classes if using module namespace
|
|
207
|
-
# They're handled via central autoload registry
|
|
208
|
-
return "" if @module_namespace
|
|
209
|
-
|
|
210
|
-
unions.filter_map do |union|
|
|
211
|
-
next if SUPPORTED_DATA_TYPES.dig(last_of_split(union).to_sym,
|
|
212
|
-
:skippable)
|
|
213
|
-
|
|
214
|
-
"require_relative \"#{down_union_class_name(union)}\""
|
|
215
|
-
end.join("\n")
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def down_union_class_name(union)
|
|
219
|
-
Utils.snake_case(last_of_split(union))
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
def last_of_split(field)
|
|
223
|
-
field&.split(":")&.last
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def extended_indent
|
|
227
|
-
@extended_indent || " "
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
class << self
|
|
231
|
-
def setup_supported_types
|
|
232
|
-
SUPPORTED_DATA_TYPES.reject do |_, simple_type|
|
|
233
|
-
simple_type[:skippable]
|
|
234
|
-
end.each_with_object({}) do |(name, simple_type), hash|
|
|
235
|
-
str_name = name.to_s
|
|
236
|
-
new(str_name).tap do |instance|
|
|
237
|
-
instance.base_class = Utils.base_class_snake_case(simple_type[:class_name])
|
|
238
|
-
instance.instance = setup_restriction(instance.base_class,
|
|
239
|
-
simple_type[:validations])
|
|
240
|
-
hash[str_name] = instance
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def setup_restriction(base_class, validations)
|
|
246
|
-
return unless validations
|
|
247
|
-
|
|
248
|
-
Restriction.new.tap do |restriction|
|
|
249
|
-
restriction.base_class = base_class
|
|
250
|
-
restriction.min_inclusive = validations[:min_inclusive]
|
|
251
|
-
restriction.max_inclusive = validations[:max_inclusive]
|
|
252
|
-
restriction.pattern = validations[:pattern]
|
|
253
|
-
restriction.transform = validations[:transform]
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def skippable?(type)
|
|
258
|
-
SUPPORTED_DATA_TYPES.dig(type&.to_sym, :skippable)
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
end
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Lutaml
|
|
4
|
-
module Model
|
|
5
|
-
module Schema
|
|
6
|
-
module XmlCompiler
|
|
7
|
-
# Generates XmlNamespace class definitions from XSD namespace URIs
|
|
8
|
-
class XmlNamespaceClass
|
|
9
|
-
attr_accessor :uri, :prefix_default, :class_name
|
|
10
|
-
|
|
11
|
-
TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
|
|
12
|
-
# frozen_string_literal: true
|
|
13
|
-
|
|
14
|
-
require "lutaml/model"
|
|
15
|
-
|
|
16
|
-
<%= module_opening -%>
|
|
17
|
-
# Namespace: <%= uri %>
|
|
18
|
-
class <%= class_name %> < Lutaml::Xml::W3c::XmlNamespace
|
|
19
|
-
uri <%= uri.inspect %>
|
|
20
|
-
prefix_default <%= prefix_default.inspect %>
|
|
21
|
-
end
|
|
22
|
-
<%= module_closing -%>
|
|
23
|
-
TEMPLATE
|
|
24
|
-
|
|
25
|
-
def initialize(uri:, prefix: nil, class_name: nil)
|
|
26
|
-
@uri = uri
|
|
27
|
-
@prefix_default = prefix || derive_prefix_from_uri(uri)
|
|
28
|
-
@class_name = class_name || derive_class_name_from_uri(uri)
|
|
29
|
-
@module_namespace = nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def to_class(options: {})
|
|
33
|
-
@module_namespace = options[:module_namespace]
|
|
34
|
-
@modules = @module_namespace&.split("::") || []
|
|
35
|
-
TEMPLATE.result(binding)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def required_file
|
|
39
|
-
"require_relative \"#{Utils.snake_case(class_name)}\""
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def class_reference
|
|
43
|
-
class_name
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def module_opening
|
|
49
|
-
return "" if @modules.empty?
|
|
50
|
-
|
|
51
|
-
@modules.map.with_index do |mod, i|
|
|
52
|
-
"#{' ' * i}module #{mod}"
|
|
53
|
-
end.join("\n") + "\n"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def module_closing
|
|
57
|
-
return "" if @modules.empty?
|
|
58
|
-
|
|
59
|
-
@modules.reverse.map.with_index do |_mod, i|
|
|
60
|
-
"#{' ' * (@modules.size - i - 1)}end"
|
|
61
|
-
end.join("\n")
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def derive_prefix_from_uri(uri)
|
|
65
|
-
# Extract meaningful part from URI
|
|
66
|
-
# http://schemas.openxmlformats.org/officeDocument/2006/math -> "m"
|
|
67
|
-
# http://www.w3.org/2001/XMLSchema -> "xs"
|
|
68
|
-
case uri
|
|
69
|
-
when %r{/math$}
|
|
70
|
-
"m"
|
|
71
|
-
when %r{XMLSchema}
|
|
72
|
-
"xs"
|
|
73
|
-
when %r{/(\w+)$}
|
|
74
|
-
$1[0..2] # First 3 chars
|
|
75
|
-
else
|
|
76
|
-
"ns" # Generic prefix
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def derive_class_name_from_uri(uri)
|
|
81
|
-
# Extract domain and path components
|
|
82
|
-
# http://schemas.openxmlformats.org/officeDocument/2006/math
|
|
83
|
-
# -> OoxmlMathNamespace
|
|
84
|
-
return "XmlSchemaNamespace" if uri.include?("XMLSchema")
|
|
85
|
-
|
|
86
|
-
parsed_uri = URI.parse(uri)
|
|
87
|
-
parts = parsed_uri.host&.split(".")&.reject(&:empty?) || []
|
|
88
|
-
path_parts = parsed_uri.path&.split("/")&.reject(&:empty?) || []
|
|
89
|
-
|
|
90
|
-
# Use reverse domain notation + meaningful path parts
|
|
91
|
-
# Filter out version numbers and empty parts
|
|
92
|
-
name_parts = parts.reverse.take(2) + path_parts.last(2)
|
|
93
|
-
name_parts = name_parts.map do |p|
|
|
94
|
-
Utils.camel_case(p.gsub(/\d+/, "").gsub(/[^a-zA-Z]/, ""))
|
|
95
|
-
end
|
|
96
|
-
.reject(&:empty?)
|
|
97
|
-
|
|
98
|
-
# Fallback if no valid parts
|
|
99
|
-
return "DefaultNamespace" if name_parts.empty?
|
|
100
|
-
|
|
101
|
-
"#{name_parts.join}Namespace"
|
|
102
|
-
rescue URI::InvalidURIError, StandardError
|
|
103
|
-
# Fallback for invalid URIs or any other errors
|
|
104
|
-
"DefaultNamespace"
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
require_relative "base"
|
|
2
|
-
|
|
3
|
-
module Lutaml
|
|
4
|
-
module Model
|
|
5
|
-
module Services
|
|
6
|
-
class DefaultValueResolver < Base
|
|
7
|
-
def initialize(attribute, register, instance_object)
|
|
8
|
-
super()
|
|
9
|
-
|
|
10
|
-
@attribute = attribute
|
|
11
|
-
@register = register
|
|
12
|
-
@instance_object = instance_object
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Get the default value with proper instance context and casting
|
|
16
|
-
# Returns the evaluated and casted default value
|
|
17
|
-
def default
|
|
18
|
-
attribute.cast_value(default_value, register)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Get the default value with proper instance context
|
|
22
|
-
# Returns the evaluated default value (procs are executed)
|
|
23
|
-
def default_value
|
|
24
|
-
raw_value = raw_default_value
|
|
25
|
-
return raw_value unless raw_value.is_a?(Proc)
|
|
26
|
-
|
|
27
|
-
# Execute proc in instance context if available, otherwise call it directly
|
|
28
|
-
if instance_object
|
|
29
|
-
instance_object.instance_exec(&raw_value)
|
|
30
|
-
else
|
|
31
|
-
raw_value.call
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Get the raw default value without executing procs
|
|
36
|
-
def raw_default_value
|
|
37
|
-
if attribute.delegate
|
|
38
|
-
# For delegated attributes, recursively resolve through the service
|
|
39
|
-
delegated_attr = attribute.type(register).attributes[attribute.to]
|
|
40
|
-
self.class.new(delegated_attr, register,
|
|
41
|
-
instance_object).raw_default_value
|
|
42
|
-
elsif attribute.options.key?(:default)
|
|
43
|
-
attribute.options[:default]
|
|
44
|
-
else
|
|
45
|
-
Lutaml::Model::UninitializedClass.instance
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Check if a default value is set (not uninitialized)
|
|
50
|
-
def default_set?
|
|
51
|
-
!Utils.uninitialized?(raw_default_value)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
private
|
|
55
|
-
|
|
56
|
-
attr_reader :attribute, :register, :instance_object
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
|
|
3
|
-
class TestModel < Lutaml::Model::Serializable
|
|
4
|
-
attribute :_class, :string, default: -> { self.class.name }
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
RSpec.describe Lutaml::Model::Services::DefaultValueResolver do
|
|
8
|
-
let(:register) { Lutaml::Model::Config.default_register }
|
|
9
|
-
let(:instance) { nil }
|
|
10
|
-
|
|
11
|
-
describe "#default_value" do
|
|
12
|
-
context "when default is a static value" do
|
|
13
|
-
let(:attribute) do
|
|
14
|
-
Lutaml::Model::Attribute.new("name", :string, default: "John")
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
it "returns the static value" do
|
|
18
|
-
resolver = described_class.new(attribute, register, instance)
|
|
19
|
-
expect(resolver.default_value).to eq("John")
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
context "when default is a proc without instance context" do
|
|
24
|
-
let(:attribute) do
|
|
25
|
-
Lutaml::Model::Attribute.new("count", :integer, default: -> { 42 })
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "executes the proc" do
|
|
29
|
-
resolver = described_class.new(attribute, register, instance)
|
|
30
|
-
expect(resolver.default_value).to eq(42)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
context "when default is a proc with instance context" do
|
|
35
|
-
let(:model) { TestModel.new }
|
|
36
|
-
let(:attribute) { TestModel.attributes[:_class] }
|
|
37
|
-
|
|
38
|
-
it "executes the proc in the instance context" do
|
|
39
|
-
resolver = described_class.new(attribute, register, model)
|
|
40
|
-
expect(resolver.default_value).to eq("TestModel")
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
describe "#default" do
|
|
46
|
-
context "when default is a static value" do
|
|
47
|
-
let(:attribute) do
|
|
48
|
-
Lutaml::Model::Attribute.new("count", :integer, default: "42")
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "returns the casted value" do
|
|
52
|
-
resolver = described_class.new(attribute, register, instance)
|
|
53
|
-
expect(resolver.default).to eq(42)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
context "when default is a proc" do
|
|
58
|
-
let(:attribute) do
|
|
59
|
-
Lutaml::Model::Attribute.new("file", :string, default: -> {
|
|
60
|
-
Pathname.new("avatar.png")
|
|
61
|
-
})
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
it "returns the casted proc result" do
|
|
65
|
-
resolver = described_class.new(attribute, register, instance)
|
|
66
|
-
expect(resolver.default).to eq("avatar.png")
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
context "when default is a proc with instance context" do
|
|
71
|
-
let(:model) { TestModel.new }
|
|
72
|
-
let(:attribute) { TestModel.attributes[:_class] }
|
|
73
|
-
|
|
74
|
-
it "executes and casts the proc in the instance context" do
|
|
75
|
-
resolver = described_class.new(attribute, register, model)
|
|
76
|
-
expect(resolver.default).to eq("TestModel")
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
describe "#raw_default_value" do
|
|
82
|
-
context "when default is not set" do
|
|
83
|
-
let(:attribute) do
|
|
84
|
-
Lutaml::Model::Attribute.new("name", :string)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it "returns uninitialized" do
|
|
88
|
-
resolver = described_class.new(attribute, register, instance)
|
|
89
|
-
expect(resolver.raw_default_value).to be(Lutaml::Model::UninitializedClass.instance)
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
context "when default is a static value" do
|
|
94
|
-
let(:attribute) do
|
|
95
|
-
Lutaml::Model::Attribute.new("name", :string, default: "John")
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it "returns the static value without execution" do
|
|
99
|
-
resolver = described_class.new(attribute, register, instance)
|
|
100
|
-
expect(resolver.raw_default_value).to eq("John")
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
context "when default is a proc" do
|
|
105
|
-
let(:proc_default) { -> { "John" } }
|
|
106
|
-
let(:attribute) do
|
|
107
|
-
Lutaml::Model::Attribute.new("name", :string, default: proc_default)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it "returns the proc without execution" do
|
|
111
|
-
resolver = described_class.new(attribute, register, instance)
|
|
112
|
-
expect(resolver.raw_default_value).to eq(proc_default)
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
context "when default is nil" do
|
|
117
|
-
let(:attribute) do
|
|
118
|
-
Lutaml::Model::Attribute.new("value", :string, default: nil)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it "returns nil" do
|
|
122
|
-
resolver = described_class.new(attribute, register, instance)
|
|
123
|
-
expect(resolver.raw_default_value).to be_nil
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
describe "#default_set?" do
|
|
129
|
-
context "when default is not set" do
|
|
130
|
-
let(:attribute) do
|
|
131
|
-
Lutaml::Model::Attribute.new("name", :string)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
it "returns false" do
|
|
135
|
-
resolver = described_class.new(attribute, register, instance)
|
|
136
|
-
expect(resolver.default_set?).to be(false)
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
context "when default is a static value" do
|
|
141
|
-
let(:attribute) do
|
|
142
|
-
Lutaml::Model::Attribute.new("name", :string, default: "John")
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it "returns true" do
|
|
146
|
-
resolver = described_class.new(attribute, register, instance)
|
|
147
|
-
expect(resolver.default_set?).to be(true)
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
context "when default is a proc" do
|
|
152
|
-
let(:attribute) do
|
|
153
|
-
Lutaml::Model::Attribute.new("name", :string, default: -> { "John" })
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
it "returns true" do
|
|
157
|
-
resolver = described_class.new(attribute, register, instance)
|
|
158
|
-
expect(resolver.default_set?).to be(true)
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
require "support/xml/schema_helper"
|
|
3
|
-
|
|
4
|
-
RSpec.describe Lutaml::Model::Schema::XmlCompiler::AttributeGroup do
|
|
5
|
-
let(:attribute_group) { described_class.new(name: "foo", ref: nil) }
|
|
6
|
-
let(:dummy_class) do
|
|
7
|
-
Class.new do
|
|
8
|
-
def to_attributes(indent); end
|
|
9
|
-
def to_xml_mapping(indent); end
|
|
10
|
-
def required_files; end
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
describe "#initialize" do
|
|
15
|
-
it "sets name, ref, and empty instances" do
|
|
16
|
-
ag = described_class.new(name: "bar", ref: "baz")
|
|
17
|
-
expect(ag.name).to eq("bar")
|
|
18
|
-
expect(ag.ref).to eq("baz")
|
|
19
|
-
expect(ag.instances).to eq([])
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
describe "#<<" do
|
|
24
|
-
it "adds instances to the list" do
|
|
25
|
-
instance = instance_double(dummy_class, to_attributes: "attr",
|
|
26
|
-
to_xml_mapping: "xml", required_files: "files")
|
|
27
|
-
expect { attribute_group << instance }.to change {
|
|
28
|
-
attribute_group.instances.size
|
|
29
|
-
}.by(1)
|
|
30
|
-
expect(attribute_group.instances).to include(instance)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "ignores nil instances" do
|
|
34
|
-
expect { attribute_group << nil }.not_to(change do
|
|
35
|
-
attribute_group.instances.size
|
|
36
|
-
end)
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
describe "#to_attributes" do
|
|
41
|
-
it "returns attributes from resolved_instances" do
|
|
42
|
-
instance = instance_double(dummy_class, to_attributes: "attr")
|
|
43
|
-
attribute_group.instances = [instance]
|
|
44
|
-
expect(attribute_group.to_attributes(" ").join).to include("attr")
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
describe "#to_xml_mapping" do
|
|
49
|
-
it "returns xml mappings from resolved_instances" do
|
|
50
|
-
instance = instance_double(dummy_class, to_xml_mapping: "xml")
|
|
51
|
-
attribute_group.instances = [instance]
|
|
52
|
-
expect(attribute_group.to_xml_mapping(" ").join).to include("xml")
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
describe "#required_files" do
|
|
57
|
-
it "collects required_files from resolved_instances" do
|
|
58
|
-
instance = instance_double(dummy_class, required_files: "files")
|
|
59
|
-
attribute_group.instances = [instance]
|
|
60
|
-
expect(attribute_group.required_files.join).to include("files")
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
describe "private methods" do
|
|
65
|
-
it "resolved_instances returns @instances if ref is not present" do
|
|
66
|
-
attribute_group.instances = [1, 2]
|
|
67
|
-
attribute_group.ref = nil
|
|
68
|
-
expect(attribute_group.send(:resolved_instances)).to eq([1, 2])
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|