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
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module Renderers
|
|
7
|
+
# Computes the `require_relative` / `require` lines that a
|
|
8
|
+
# generated Definitions::Model needs at the top of its file.
|
|
9
|
+
#
|
|
10
|
+
# XSD and RNG have different strategies because their type
|
|
11
|
+
# references mean different things:
|
|
12
|
+
#
|
|
13
|
+
# - XSD attributes carry TypeRef(:w3c | :symbol | :class_ref):
|
|
14
|
+
# :w3c -> require "lutaml/xml/w3c"
|
|
15
|
+
# :symbol -> require_relative "<value>"
|
|
16
|
+
# (unless the value is a skippable built-in)
|
|
17
|
+
# decimal is special: require "bigdecimal"
|
|
18
|
+
# :class_ref -> no require (class is autoloaded by registry)
|
|
19
|
+
# Plus any required_files surfaced by simple_content + the
|
|
20
|
+
# namespace class.
|
|
21
|
+
#
|
|
22
|
+
# - RNG attributes only ever reference other generated classes
|
|
23
|
+
# via :class_ref, so the calculator only collects class names
|
|
24
|
+
# (imports + class_ref types + namespace) and the caller
|
|
25
|
+
# formats them as `require_relative "snake_case_name"`.
|
|
26
|
+
module RequiredFilesCalculator
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
# XSD strategy. `skippable_type?` is a one-arg predicate the
|
|
30
|
+
# caller supplies so the calculator doesn't have to know the
|
|
31
|
+
# XSD built-in type table.
|
|
32
|
+
def for_xml(model, skippable_type:)
|
|
33
|
+
deps = []
|
|
34
|
+
Definitions::MemberWalk.each_attribute(model.members) do |attr|
|
|
35
|
+
deps.concat(xml_attribute_requires(attr, skippable_type))
|
|
36
|
+
end
|
|
37
|
+
deps.concat(simple_content_requires(model.simple_content, skippable_type))
|
|
38
|
+
deps << namespace_require(model.namespace_class_name) if model.namespace_class_name
|
|
39
|
+
deps.uniq
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# RNG strategy. Returns the de-duplicated list of generated-class
|
|
43
|
+
# names the model depends on; caller wraps as `require_relative`.
|
|
44
|
+
def class_names_for_rng(model)
|
|
45
|
+
deps = model.imports.dup
|
|
46
|
+
Definitions::MemberWalk.each_attribute(model.members) do |attr|
|
|
47
|
+
deps << attr.type.value if attr.type.kind == :class_ref
|
|
48
|
+
end
|
|
49
|
+
deps << model.namespace_class_name if model.namespace_class_name
|
|
50
|
+
deps.uniq
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def namespace_require(class_name)
|
|
54
|
+
%(require_relative "#{Utils.snake_case(class_name)}")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def xml_attribute_requires(attr, skippable_type)
|
|
58
|
+
ref = attr.type
|
|
59
|
+
return [] unless ref
|
|
60
|
+
|
|
61
|
+
case ref.kind
|
|
62
|
+
when :w3c then ['require "lutaml/xml/w3c"']
|
|
63
|
+
when :symbol then xml_symbol_requires(ref.value, skippable_type)
|
|
64
|
+
when :class_ref then []
|
|
65
|
+
else []
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def xml_symbol_requires(value, skippable_type)
|
|
70
|
+
return [%(require "bigdecimal")] if value == "decimal"
|
|
71
|
+
return [] if skippable_type.call(value)
|
|
72
|
+
|
|
73
|
+
[%(require_relative "#{value}")]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# base_class arrives as a raw XSD name (e.g. "xs:dateTime"). The
|
|
77
|
+
# built-in skippable check keys on the XSD spelling (`:dateTime`),
|
|
78
|
+
# not the snake_case file form, so test against `local` directly
|
|
79
|
+
# before emitting the require_relative.
|
|
80
|
+
def simple_content_requires(simple_content, skippable_type)
|
|
81
|
+
return [] if simple_content.nil? || simple_content.base_class.nil?
|
|
82
|
+
|
|
83
|
+
local = Utils.last_of_split(simple_content.base_class)
|
|
84
|
+
return [%(require "bigdecimal")] if local == "decimal"
|
|
85
|
+
return [] if skippable_type.call(local)
|
|
86
|
+
|
|
87
|
+
[%(require_relative "#{Utils.snake_case(local)}")]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Model
|
|
7
|
+
module Schema
|
|
8
|
+
module Renderers
|
|
9
|
+
# Renders a Definitions::RestrictedType into a Ruby class extending
|
|
10
|
+
# a Lutaml::Model::Type::* with a cast body that mutates options
|
|
11
|
+
# with facet values and delegates to super.
|
|
12
|
+
class RestrictedType < Base
|
|
13
|
+
def render
|
|
14
|
+
Templates::RESTRICTED_SIMPLE_TYPE.result(binding)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def rendered_class_name = @spec.class_name
|
|
20
|
+
def parent_class = @spec.parent_class
|
|
21
|
+
|
|
22
|
+
def xml_namespace_line
|
|
23
|
+
ns = @spec.namespace_class_name
|
|
24
|
+
ns && "namespace #{ns}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def restricted_simple_type_required_files = required_files_block
|
|
28
|
+
|
|
29
|
+
def restricted_simple_type_cast_body
|
|
30
|
+
[
|
|
31
|
+
render_min_max,
|
|
32
|
+
render_pattern,
|
|
33
|
+
render_enumerations,
|
|
34
|
+
render_transform,
|
|
35
|
+
].compact.join
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render_min_max
|
|
39
|
+
f = @spec.facets
|
|
40
|
+
max = f.max_inclusive || f.max_exclusive
|
|
41
|
+
min = f.min_inclusive || f.min_exclusive
|
|
42
|
+
return nil unless max || min
|
|
43
|
+
|
|
44
|
+
out = +""
|
|
45
|
+
out << "#{@extended_indent}options[:max] = #{max}\n" if max
|
|
46
|
+
out << "#{@extended_indent}options[:min] = #{min}\n" if min
|
|
47
|
+
out
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def render_pattern
|
|
51
|
+
p = @spec.facets.pattern
|
|
52
|
+
p && "#{@extended_indent}options[:pattern] = %r{#{p}}\n"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def render_enumerations
|
|
56
|
+
e = @spec.facets.enumerations
|
|
57
|
+
return nil if e.nil? || e.empty?
|
|
58
|
+
|
|
59
|
+
casted = e.map { |v| "super(#{v.inspect})" }.join(", ")
|
|
60
|
+
"#{@extended_indent}options[:values] = [#{casted}]\n"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_transform
|
|
64
|
+
t = @spec.transform_facet
|
|
65
|
+
t && "#{@extended_indent}value = #{t.expression}\n"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def registration_methods
|
|
69
|
+
Registration.methods_block(
|
|
70
|
+
class_name: @spec.class_name,
|
|
71
|
+
module_namespace: @module_namespace,
|
|
72
|
+
indent: @indent,
|
|
73
|
+
lazy: true,
|
|
74
|
+
keep_when_namespaced: @spec.keep_register_when_namespaced,
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def registration_execution
|
|
79
|
+
Registration.execution_line(
|
|
80
|
+
class_name: @spec.class_name,
|
|
81
|
+
module_namespace: @module_namespace,
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Model
|
|
7
|
+
module Schema
|
|
8
|
+
module Renderers
|
|
9
|
+
# Renders a Definitions::UnionType into a
|
|
10
|
+
# Lutaml::Model::Type::Value subclass with a cast body whose
|
|
11
|
+
# strategy is chosen from the spec.
|
|
12
|
+
class Union < Base
|
|
13
|
+
def render
|
|
14
|
+
Templates::UNION_TYPE.result(binding)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def rendered_class_name = @spec.class_name
|
|
20
|
+
|
|
21
|
+
def union_required_files = required_files_block
|
|
22
|
+
|
|
23
|
+
def union_cast_body
|
|
24
|
+
case @spec.cast_strategy
|
|
25
|
+
when :resolve_type then resolve_type_body
|
|
26
|
+
when :class_refs then class_refs_body
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def resolve_type_body
|
|
31
|
+
chain = @spec.members.map { |m| resolve_type_call(m) }
|
|
32
|
+
"#{@extended_indent}#{chain.join(" ||\n ")}\n"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def resolve_type_call(type_ref)
|
|
36
|
+
"Lutaml::Model::GlobalContext.resolve_type(:#{type_ref.value}, @register).cast(value, options)"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def class_refs_body
|
|
40
|
+
classes = @spec.members.map { |m| literal_class(m) }.join(", ")
|
|
41
|
+
sp2 = @extended_indent
|
|
42
|
+
<<~BODY
|
|
43
|
+
#{sp2}[#{classes}].each do |t|
|
|
44
|
+
#{sp2} begin
|
|
45
|
+
#{sp2} casted = t.cast(value, options)
|
|
46
|
+
#{sp2} return casted unless casted.nil?
|
|
47
|
+
#{sp2} rescue StandardError
|
|
48
|
+
#{sp2} next
|
|
49
|
+
#{sp2} end
|
|
50
|
+
#{sp2}end
|
|
51
|
+
#{sp2}value
|
|
52
|
+
BODY
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def literal_class(type_ref)
|
|
56
|
+
case type_ref.kind
|
|
57
|
+
when :class_ref then type_ref.value
|
|
58
|
+
when :w3c then "::#{type_ref.value}"
|
|
59
|
+
when :symbol then "Lutaml::Model::GlobalContext.resolve_type(:#{type_ref.value})"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def registration_methods
|
|
64
|
+
Registration.methods_block(
|
|
65
|
+
class_name: @spec.class_name,
|
|
66
|
+
module_namespace: @module_namespace,
|
|
67
|
+
indent: @indent,
|
|
68
|
+
lazy: @spec.lazy_register,
|
|
69
|
+
keep_when_namespaced: @spec.keep_register_when_namespaced,
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def registration_execution
|
|
74
|
+
Registration.execution_line(
|
|
75
|
+
class_name: @spec.class_name,
|
|
76
|
+
module_namespace: @module_namespace,
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module Renderers
|
|
7
|
+
autoload :Base, "#{__dir__}/renderers/base"
|
|
8
|
+
autoload :Registration, "#{__dir__}/renderers/registration"
|
|
9
|
+
autoload :RequiredFilesCalculator, "#{__dir__}/renderers/required_files_calculator"
|
|
10
|
+
autoload :MemberDecls, "#{__dir__}/renderers/member_decls"
|
|
11
|
+
autoload :Mappings, "#{__dir__}/renderers/mappings"
|
|
12
|
+
autoload :Model, "#{__dir__}/renderers/model"
|
|
13
|
+
autoload :RestrictedType, "#{__dir__}/renderers/restricted_type"
|
|
14
|
+
autoload :Union, "#{__dir__}/renderers/union"
|
|
15
|
+
autoload :Namespace, "#{__dir__}/renderers/namespace"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module RncCompiler
|
|
7
|
+
# Resolves RNC input text and the base directory used for relative
|
|
8
|
+
# include paths.
|
|
9
|
+
module SourceResolver
|
|
10
|
+
ResolvedSource =
|
|
11
|
+
Struct.new(:text, :base_dir, :path, keyword_init: true)
|
|
12
|
+
|
|
13
|
+
module_function
|
|
14
|
+
|
|
15
|
+
def resolve(input, options)
|
|
16
|
+
location = options[:location].to_s
|
|
17
|
+
return string_source(input) if location.empty?
|
|
18
|
+
|
|
19
|
+
expanded = File.expand_path(location)
|
|
20
|
+
if File.file?(expanded)
|
|
21
|
+
return file_source(expanded) if input.nil? || input.to_s.empty?
|
|
22
|
+
|
|
23
|
+
return string_source(input, base_dir: File.dirname(expanded), path: expanded)
|
|
24
|
+
end
|
|
25
|
+
return string_source(input, base_dir: expanded) if File.directory?(expanded)
|
|
26
|
+
|
|
27
|
+
string_source(input)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def string_source(input, base_dir: nil, path: nil)
|
|
31
|
+
ResolvedSource.new(text: input.to_s, base_dir: base_dir, path: path)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def file_source(path)
|
|
35
|
+
ResolvedSource.new(
|
|
36
|
+
text: File.read(path),
|
|
37
|
+
base_dir: File.dirname(path),
|
|
38
|
+
path: path,
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private_class_method :string_source, :file_source
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
# Compiles RELAX NG Compact Syntax (RNC) into Lutaml::Model Ruby source.
|
|
7
|
+
#
|
|
8
|
+
# RNC support is a thin adapter: the rng gem parses the compact syntax
|
|
9
|
+
# into Rng::Grammar — resolving includes, bracket annotations, and
|
|
10
|
+
# non-leading `start` definitions natively — then RngCompiler performs all
|
|
11
|
+
# model generation and output handling.
|
|
12
|
+
module RncCompiler
|
|
13
|
+
extend self
|
|
14
|
+
|
|
15
|
+
autoload :SourceResolver, "#{__dir__}/rnc_compiler/source_resolver"
|
|
16
|
+
|
|
17
|
+
DEFAULT_OUTPUT_DIR_PREFIX = "rnc_models"
|
|
18
|
+
|
|
19
|
+
def to_models(rnc, options = {})
|
|
20
|
+
require_rnc_parser!
|
|
21
|
+
|
|
22
|
+
grammar = parse_grammar(rnc, options)
|
|
23
|
+
|
|
24
|
+
RngCompiler.to_models(
|
|
25
|
+
grammar,
|
|
26
|
+
options.merge(default_output_dir: default_output_dir),
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def parse_grammar(rnc, options)
|
|
31
|
+
return rnc if rnc.is_a?(::Rng::Grammar)
|
|
32
|
+
|
|
33
|
+
source = SourceResolver.resolve(rnc, options)
|
|
34
|
+
::Rng.parse_rnc(source.text, location: source.path || source.base_dir)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def require_rnc_parser!
|
|
38
|
+
return if rnc_parser_available?
|
|
39
|
+
|
|
40
|
+
raise "RNC schema compilation requires an rng gem whose " \
|
|
41
|
+
"`Rng.parse_rnc` accepts a `location:` option (for native " \
|
|
42
|
+
"include resolution). Point your Gemfile at an rng version " \
|
|
43
|
+
"that provides it."
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# rng gained the `location:` option (native include resolution) after
|
|
47
|
+
# the 0.3.7 release; the released parse_rnc takes only `(rnc)`.
|
|
48
|
+
def rnc_parser_available?
|
|
49
|
+
defined?(::Rng::Grammar) && ::Rng.respond_to?(:parse_rnc) &&
|
|
50
|
+
::Rng.method(:parse_rnc).parameters.any? { |_type, name| name == :location }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def default_output_dir
|
|
54
|
+
"#{DEFAULT_OUTPUT_DIR_PREFIX}_#{Time.now.to_i}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private :parse_grammar, :require_rnc_parser!, :rnc_parser_available?,
|
|
58
|
+
:default_output_dir
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module RngCompiler
|
|
7
|
+
# Detects the shape of a <define> body and builds the corresponding
|
|
8
|
+
# Definitions::* spec for it.
|
|
9
|
+
#
|
|
10
|
+
# <data type="X"><param ...>...</param></data> -> RestrictedType
|
|
11
|
+
# <choice><value>a</value><value>b</value></choice> -> RestrictedType (enum)
|
|
12
|
+
# <choice><data type="A"/><data type="B"/></choice> -> UnionType
|
|
13
|
+
#
|
|
14
|
+
# Returns nil when the define is structural (contains elements or
|
|
15
|
+
# attributes) — caller falls back to building a Definitions::Model.
|
|
16
|
+
class DefineClassifier
|
|
17
|
+
def self.build(define, class_name)
|
|
18
|
+
new(define, class_name).build
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(define, class_name)
|
|
22
|
+
@define = define
|
|
23
|
+
@class_name = class_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build
|
|
27
|
+
return nil if structural_define?
|
|
28
|
+
|
|
29
|
+
union_type || data_restricted_type || enum_restricted_type
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def structural_define?
|
|
35
|
+
@define.element.any? || @define.attribute.any?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def union_type
|
|
39
|
+
choice = value_choice
|
|
40
|
+
return nil unless RngHelpers.pure_union_choice?(choice)
|
|
41
|
+
|
|
42
|
+
type_refs = Array(choice.data).map do |d|
|
|
43
|
+
symbol = RngCompiler::DATA_TYPE_MAP.fetch(
|
|
44
|
+
d.type, RngCompiler::DEFAULT_DATA_TYPE
|
|
45
|
+
)
|
|
46
|
+
Definitions::TypeRef.new(
|
|
47
|
+
kind: :class_ref,
|
|
48
|
+
value: RngHelpers.parent_class_for(symbol),
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
Definitions::UnionType.new(
|
|
53
|
+
class_name: @class_name,
|
|
54
|
+
members: type_refs,
|
|
55
|
+
cast_strategy: :class_refs,
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def data_restricted_type
|
|
60
|
+
data = RngHelpers.single(@define.data)
|
|
61
|
+
return nil unless data
|
|
62
|
+
|
|
63
|
+
base = RngCompiler::DATA_TYPE_MAP.fetch(
|
|
64
|
+
data.type, RngCompiler::DEFAULT_DATA_TYPE
|
|
65
|
+
)
|
|
66
|
+
Definitions::RestrictedType.new(
|
|
67
|
+
class_name: @class_name,
|
|
68
|
+
parent_class: RngHelpers.parent_class_for(base),
|
|
69
|
+
facets: RngHelpers.facet_from_data(data),
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def enum_restricted_type
|
|
74
|
+
choice = value_choice
|
|
75
|
+
return nil unless RngHelpers.pure_value_choice?(choice)
|
|
76
|
+
|
|
77
|
+
Definitions::RestrictedType.new(
|
|
78
|
+
class_name: @class_name,
|
|
79
|
+
parent_class: RngHelpers.parent_class_for(:string),
|
|
80
|
+
facets: RngHelpers.facet_from_values(choice.value),
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def value_choice
|
|
85
|
+
RngHelpers.single(@define.choice)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|