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,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Model
|
|
7
|
+
module Schema
|
|
8
|
+
# Derives the class name and default prefix for a generated
|
|
9
|
+
# XmlNamespace subclass from a namespace URI.
|
|
10
|
+
#
|
|
11
|
+
# Used by both XSD and RNG compilers so the same URI always
|
|
12
|
+
# produces the same class name across formats.
|
|
13
|
+
module NamespaceNaming
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def prefix_for(uri)
|
|
17
|
+
case uri
|
|
18
|
+
when %r{/math$} then "m"
|
|
19
|
+
when %r{XMLSchema} then "xs"
|
|
20
|
+
when %r{/(\w+)\z} then ::Regexp.last_match(1)[0..2]
|
|
21
|
+
else "ns"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def class_name_for(uri)
|
|
26
|
+
return "XmlSchemaNamespace" if uri.include?("XMLSchema")
|
|
27
|
+
|
|
28
|
+
parsed = URI.parse(uri)
|
|
29
|
+
host_parts = (parsed.host&.split(".") || []).reject(&:empty?)
|
|
30
|
+
path_parts = (parsed.path&.split("/") || []).reject(&:empty?)
|
|
31
|
+
|
|
32
|
+
name_parts = host_parts.reverse.take(2) + path_parts.last(2)
|
|
33
|
+
name_parts = name_parts.map do |p|
|
|
34
|
+
Utils.camel_case(p.gsub(/\d+/, "").gsub(/[^a-zA-Z]/, ""))
|
|
35
|
+
end.reject(&:empty?)
|
|
36
|
+
|
|
37
|
+
return "DefaultNamespace" if name_parts.empty?
|
|
38
|
+
|
|
39
|
+
"#{name_parts.join}Namespace"
|
|
40
|
+
rescue URI::InvalidURIError
|
|
41
|
+
"DefaultNamespace"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Model
|
|
7
|
+
module Schema
|
|
8
|
+
# Base class for per-format registry generators (one Ruby file that
|
|
9
|
+
# declares autoloads + a `self.register_all` that registers every
|
|
10
|
+
# generated class with the model registry).
|
|
11
|
+
#
|
|
12
|
+
# Subclasses customize the ERB skeleton (e.g., XSD adds import
|
|
13
|
+
# resolution phases) but inherit the shared module-wrap and
|
|
14
|
+
# autoload/registration emit methods.
|
|
15
|
+
class RegistryGenerator
|
|
16
|
+
DEFAULT_TEMPLATE = ERB.new(<<~TMPL, trim_mode: "-")
|
|
17
|
+
# frozen_string_literal: true
|
|
18
|
+
# Auto-generated central registry for <%= @module_namespace %>
|
|
19
|
+
|
|
20
|
+
<%= module_opening -%>
|
|
21
|
+
<%= autoload_declarations %>
|
|
22
|
+
|
|
23
|
+
def self.register_all
|
|
24
|
+
context = Lutaml::Model::GlobalContext.context(:<%= @register_id %>) ||
|
|
25
|
+
Lutaml::Model::GlobalContext.create_context(id: :<%= @register_id %>)
|
|
26
|
+
|
|
27
|
+
<%= registration_body %>
|
|
28
|
+
end
|
|
29
|
+
<%= module_closing -%>
|
|
30
|
+
TMPL
|
|
31
|
+
|
|
32
|
+
def self.generate(model_entries, options = {})
|
|
33
|
+
new(model_entries, options).generate
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# `model_entries` is an Array of CompiledOutput::Entry with kind
|
|
37
|
+
# `:model`. `options[:namespaces]` is an Array of `:namespace`
|
|
38
|
+
# entries, autoloaded but never registered.
|
|
39
|
+
def initialize(model_entries, options)
|
|
40
|
+
@classes = model_entries
|
|
41
|
+
@namespaces = options[:namespaces] || []
|
|
42
|
+
@module_namespace = options[:module_namespace]
|
|
43
|
+
@register_id = options[:register_id] || :default
|
|
44
|
+
@modules = @module_namespace&.split("::") || []
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def generate
|
|
48
|
+
return nil unless @module_namespace
|
|
49
|
+
|
|
50
|
+
template.result(binding)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
# Subclasses can override to use a different ERB skeleton.
|
|
56
|
+
def template
|
|
57
|
+
self.class::DEFAULT_TEMPLATE
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The body of `def self.register_all`. Default is just the
|
|
61
|
+
# registration block; subclasses can override to add extra phases
|
|
62
|
+
# (e.g., XSD's ensure_imports! / ensure_mappings_imported! passes).
|
|
63
|
+
def registration_body
|
|
64
|
+
registration_calls
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Inside-`register_all` indent, used by phase-comment overrides.
|
|
68
|
+
def body_indent
|
|
69
|
+
" " * (module_depth + 1)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Autoload every generated constant: model classes AND namespace
|
|
73
|
+
# classes. Models are then registered with `register_all`;
|
|
74
|
+
# namespaces are only autoloaded (model classes reference them
|
|
75
|
+
# in their `namespace ...` mapping declarations).
|
|
76
|
+
def autoload_declarations
|
|
77
|
+
indent = " " * module_depth
|
|
78
|
+
module_subdir = @module_namespace.split("::").last.downcase
|
|
79
|
+
(@classes + @namespaces).map do |entry|
|
|
80
|
+
class_name = Utils.camel_case(entry.name)
|
|
81
|
+
file_name = Utils.snake_case(entry.name)
|
|
82
|
+
"#{indent}autoload :#{class_name}, " \
|
|
83
|
+
"File.join(__dir__, \"#{module_subdir}\", \"#{file_name}\")"
|
|
84
|
+
end.join("\n")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def registration_calls
|
|
88
|
+
indent = " " * (module_depth + 1)
|
|
89
|
+
@classes.map do |entry|
|
|
90
|
+
class_name = Utils.camel_case(entry.name)
|
|
91
|
+
id = Utils.snake_case(entry.name).to_sym
|
|
92
|
+
"#{indent}#{class_name} # Trigger autoload\n" \
|
|
93
|
+
"#{indent}context.registry.register(:#{id}, #{class_name})\n" \
|
|
94
|
+
"#{indent}#{class_name}.instance_variable_set(:@register, :#{@register_id})"
|
|
95
|
+
end.join("\n")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Thin forwarders so the ERB template using `<%= module_opening %>`
|
|
99
|
+
# / `<%= module_closing %>` keeps working. Logic lives in
|
|
100
|
+
# `Lutaml::Model::Schema::ModuleNesting`.
|
|
101
|
+
def module_opening
|
|
102
|
+
ModuleNesting.opening(Array(@modules))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def module_closing
|
|
106
|
+
ModuleNesting.closing(Array(@modules))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def module_depth
|
|
110
|
+
@modules.size
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Model
|
|
7
|
+
module Schema
|
|
8
|
+
module Renderers
|
|
9
|
+
# Shared constructor and helpers for the value/namespace renderers
|
|
10
|
+
# (Union, RestrictedType, Namespace). Model has its own constructor
|
|
11
|
+
# because it computes an opt-out (module-wrappable) namespace.
|
|
12
|
+
class Base
|
|
13
|
+
def self.render(spec, **)
|
|
14
|
+
new(spec, **).render
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(spec, indent: 2, module_namespace: nil, register_id: :default)
|
|
18
|
+
@spec = spec
|
|
19
|
+
@indent = indent.is_a?(Integer) ? " " * indent : indent
|
|
20
|
+
@extended_indent = @indent * 2
|
|
21
|
+
@module_namespace = module_namespace
|
|
22
|
+
@modules = module_namespace&.split("::") || []
|
|
23
|
+
@register_id = register_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def required_files_block
|
|
29
|
+
files = @spec.required_files
|
|
30
|
+
files.empty? ? "" : "#{files.uniq.join("\n")}\n"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def module_opening = ModuleNesting.opening(@modules)
|
|
34
|
+
def module_closing = ModuleNesting.closing(@modules)
|
|
35
|
+
def boilerplate_indent_str = @indent
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module Renderers
|
|
7
|
+
# Renders the `map_element` / `map_attribute` / `sequence do` lines
|
|
8
|
+
# inside the `xml do ... end` block of a generated class. Used by
|
|
9
|
+
# Renderers::Model.
|
|
10
|
+
class Mappings
|
|
11
|
+
def self.render(members, indent:, base_indent:, simple_content: nil)
|
|
12
|
+
new(indent: indent, base_indent: base_indent,
|
|
13
|
+
simple_content: simple_content).render(members)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(indent:, base_indent:, simple_content:)
|
|
17
|
+
@indent = indent
|
|
18
|
+
@base_indent = base_indent
|
|
19
|
+
@simple_content = simple_content
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render(members)
|
|
23
|
+
members.map { |m| render_one(m, @indent) }.join +
|
|
24
|
+
simple_content_attribute_mappings
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def render_one(member, indent)
|
|
30
|
+
case member
|
|
31
|
+
when Definitions::Sequence then render_sequence(member, indent)
|
|
32
|
+
when Definitions::Choice then member.alternatives.map { |a| render_one(a, indent) }.join
|
|
33
|
+
when Definitions::Attribute then render_attribute(member, indent)
|
|
34
|
+
when Definitions::GroupImport then "#{indent}import_model_mappings :#{member.name}\n"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render_sequence(sequence, indent)
|
|
39
|
+
inner_indent = indent + @base_indent
|
|
40
|
+
inner = sequence.members.map { |m| render_one(m, inner_indent) }.join
|
|
41
|
+
"#{indent}sequence do\n#{inner}#{indent}end\n"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_attribute(attr, indent)
|
|
45
|
+
case attr.kind
|
|
46
|
+
when :element then map_member("map_element", attr, indent)
|
|
47
|
+
when :attribute then map_member("map_attribute", attr, indent)
|
|
48
|
+
else ""
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def map_member(verb, attr, indent)
|
|
53
|
+
%(#{indent}#{verb} "#{attr.xml_name}", to: :#{attr.name}#{render_options(attr)}\n)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def render_options(attr)
|
|
57
|
+
opts = []
|
|
58
|
+
opts << "render_default: true" if attr.render_default
|
|
59
|
+
opts << "render_empty: true" if attr.render_empty
|
|
60
|
+
opts.empty? ? "" : ", #{opts.join(', ')}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def simple_content_attribute_mappings
|
|
64
|
+
return "" unless @simple_content
|
|
65
|
+
|
|
66
|
+
@simple_content.additional_attributes.map { |a| render_one(a, @indent) }.join
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module Renderers
|
|
7
|
+
# Renders the attribute-declaration block of a generated class:
|
|
8
|
+
# the lines between `class X < Parent` and `xml do`. Used by
|
|
9
|
+
# Renderers::Model.
|
|
10
|
+
class MemberDecls
|
|
11
|
+
def self.render(members, indent:, base_indent:, mixed: false,
|
|
12
|
+
text_content: false, simple_content: nil)
|
|
13
|
+
new(indent: indent, base_indent: base_indent,
|
|
14
|
+
mixed: mixed,
|
|
15
|
+
text_content: text_content,
|
|
16
|
+
simple_content: simple_content).render(members)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(indent:, base_indent:, mixed:, text_content:,
|
|
20
|
+
simple_content:)
|
|
21
|
+
@indent = indent
|
|
22
|
+
@base_indent = base_indent
|
|
23
|
+
@mixed = mixed
|
|
24
|
+
@text_content = text_content
|
|
25
|
+
@simple_content = simple_content
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def render(members)
|
|
29
|
+
lines = members.map { |m| render_one(m, @indent) }.join
|
|
30
|
+
lines + content_attribute_line + simple_content_extras
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def render_one(member, indent)
|
|
36
|
+
case member
|
|
37
|
+
when Definitions::Choice then render_choice(member, indent)
|
|
38
|
+
when Definitions::Sequence then member.members.map { |m| render_one(m, indent) }.join
|
|
39
|
+
when Definitions::Attribute then render_attribute(member, indent)
|
|
40
|
+
when Definitions::GroupImport then "#{indent}import_model_attributes :#{member.name}\n"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def render_choice(choice, indent)
|
|
45
|
+
inner_indent = indent + @base_indent
|
|
46
|
+
inner = choice.alternatives.map { |alt| render_one(alt, inner_indent) }.join
|
|
47
|
+
"#{indent}#{choice.header} do\n#{inner}#{indent}end\n"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def render_attribute(attr, indent)
|
|
51
|
+
doc = format_doc(attr.documentation, indent)
|
|
52
|
+
"#{doc}#{indent}attribute :#{attr.name}, #{type_literal(attr.type)}#{options_suffix(attr)}\n"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def format_doc(doc, indent)
|
|
56
|
+
return "" if Utils.blank?(doc&.to_s&.strip)
|
|
57
|
+
|
|
58
|
+
doc.to_s.lines.map { |line| "#{indent}# #{line.strip}\n" }.join
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def type_literal(type_ref)
|
|
62
|
+
case type_ref.kind
|
|
63
|
+
when :symbol then ":#{type_ref.value}"
|
|
64
|
+
when :class_ref then type_ref.value
|
|
65
|
+
when :w3c then "::#{type_ref.value}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def options_suffix(attr)
|
|
70
|
+
opts = []
|
|
71
|
+
opts << collection_option(attr.collection) if attr.collection
|
|
72
|
+
opts << "default: -> { #{attr.default.inspect} }" if attr.default
|
|
73
|
+
opts << "initialize_empty: true" if attr.initialize_empty
|
|
74
|
+
opts.empty? ? "" : ", #{opts.join(', ')}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def collection_option(coll)
|
|
78
|
+
case coll
|
|
79
|
+
when true then "collection: true"
|
|
80
|
+
when Range then range_option(coll)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def range_option(range)
|
|
85
|
+
ending = if range.end.respond_to?(:infinite?) && range.end.infinite?
|
|
86
|
+
"Float::INFINITY"
|
|
87
|
+
else
|
|
88
|
+
range.end
|
|
89
|
+
end
|
|
90
|
+
"collection: #{range.begin}..#{ending}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def content_attribute_line
|
|
94
|
+
return simple_content_attribute if @simple_content
|
|
95
|
+
return "#{@indent}attribute :content, :string, collection: true\n" if @mixed
|
|
96
|
+
return "#{@indent}attribute :content, :string\n" if @text_content
|
|
97
|
+
|
|
98
|
+
""
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def simple_content_attribute
|
|
102
|
+
type_sym = Utils.snake_case(Utils.last_of_split(@simple_content.base_class))
|
|
103
|
+
"#{@indent}attribute :content, :#{type_sym}\n"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def simple_content_extras
|
|
107
|
+
return "" unless @simple_content
|
|
108
|
+
|
|
109
|
+
@simple_content.additional_attributes.map { |a| render_attribute(a, @indent) }.join
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
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::Model into a Lutaml::Model::Serializable
|
|
10
|
+
# subclass as Ruby source. Member-declaration and xml-mapping
|
|
11
|
+
# rendering is delegated to MemberDecls and Mappings.
|
|
12
|
+
class Model
|
|
13
|
+
def self.render(spec, **)
|
|
14
|
+
new(spec, **).render
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(spec, indent: 2, module_namespace: nil, register_id: :default)
|
|
18
|
+
@spec = spec
|
|
19
|
+
@indent = indent.is_a?(Integer) ? " " * indent : indent
|
|
20
|
+
@extended_indent = @indent * 2
|
|
21
|
+
@raw_module_namespace = module_namespace
|
|
22
|
+
@register_id = register_id
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def render
|
|
26
|
+
Templates::SERIALIZABLE_CLASS.result(binding)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Effective namespace (nil when spec opts out of module wrapping).
|
|
32
|
+
def module_namespace
|
|
33
|
+
@spec.module_wrappable ? @raw_module_namespace : nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def modules
|
|
37
|
+
module_namespace&.split("::") || []
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def rendered_class_name = @spec.class_name
|
|
41
|
+
def serializable_class_parent = @spec.parent_class
|
|
42
|
+
def serializable_class_required_files = format_files(@spec.required_files)
|
|
43
|
+
def serializable_class_documentation = format_doc_block(@spec.documentation)
|
|
44
|
+
def serializable_class_imports = render_imports
|
|
45
|
+
|
|
46
|
+
def serializable_class_attributes
|
|
47
|
+
MemberDecls.render(
|
|
48
|
+
@spec.members,
|
|
49
|
+
indent: @indent,
|
|
50
|
+
base_indent: @indent,
|
|
51
|
+
mixed: @spec.mixed,
|
|
52
|
+
text_content: @spec.text_content,
|
|
53
|
+
simple_content: @spec.simple_content,
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def xml_attribute_mappings
|
|
58
|
+
Mappings.render(
|
|
59
|
+
@spec.members,
|
|
60
|
+
indent: @extended_indent,
|
|
61
|
+
base_indent: @indent,
|
|
62
|
+
simple_content: @spec.simple_content,
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def xml_root_directive_line
|
|
67
|
+
case @spec.xml_root.kind
|
|
68
|
+
when :element then %(element "#{@spec.xml_root.name}")
|
|
69
|
+
when :type_name then %(type_name "#{@spec.xml_root.name}")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def xml_namespace_line
|
|
74
|
+
ns = @spec.namespace_class_name
|
|
75
|
+
ns && "namespace #{ns}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def xml_mixed_content? = @spec.mixed
|
|
79
|
+
def xml_text_content? = @spec.text_content || !!@spec.simple_content || @spec.mixed
|
|
80
|
+
def xml_extra_mappings = ""
|
|
81
|
+
|
|
82
|
+
def module_opening = ModuleNesting.opening(modules)
|
|
83
|
+
def module_closing = ModuleNesting.closing(modules)
|
|
84
|
+
def boilerplate_indent_str = @indent
|
|
85
|
+
|
|
86
|
+
def registration_methods
|
|
87
|
+
Registration.methods_block(
|
|
88
|
+
class_name: @spec.class_name,
|
|
89
|
+
module_namespace: module_namespace,
|
|
90
|
+
indent: @indent,
|
|
91
|
+
lazy: @spec.lazy_register,
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def registration_execution
|
|
96
|
+
Registration.execution_line(
|
|
97
|
+
class_name: @spec.class_name,
|
|
98
|
+
module_namespace: module_namespace,
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def format_files(files)
|
|
103
|
+
return "" if files.empty? || module_namespace
|
|
104
|
+
|
|
105
|
+
"#{files.uniq.join("\n")}\n"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def format_doc_block(doc)
|
|
109
|
+
return "" if Utils.blank?(doc&.to_s&.strip)
|
|
110
|
+
|
|
111
|
+
doc.to_s.lines.map { |line| "# #{line.strip}\n" }.join
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def render_imports
|
|
115
|
+
@spec.imports.map { |name| "#{@indent}import_model #{name}\n" }.join
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
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::Namespace into a
|
|
10
|
+
# Lutaml::Xml::W3c::XmlNamespace subclass.
|
|
11
|
+
class Namespace < Base
|
|
12
|
+
def render
|
|
13
|
+
Templates::XML_NAMESPACE.result(binding)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def class_name = @spec.class_name
|
|
19
|
+
def uri = @spec.uri
|
|
20
|
+
def prefix_default = @spec.prefix_default
|
|
21
|
+
|
|
22
|
+
def element_form_default_line
|
|
23
|
+
efd = @spec.element_form_default
|
|
24
|
+
efd && "element_form_default #{efd.inspect}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Schema
|
|
6
|
+
module Renderers
|
|
7
|
+
# Stateless helper for emitting the `self.register` and
|
|
8
|
+
# `self.register_class_with_id` method block plus the trailing
|
|
9
|
+
# execution line, shared by the Model, RestrictedType, and Union
|
|
10
|
+
# renderers.
|
|
11
|
+
#
|
|
12
|
+
# Cases:
|
|
13
|
+
# module_namespace = nil -> emit both methods + execution
|
|
14
|
+
# namespace + keep_when_namespaced -> emit only `self.register`
|
|
15
|
+
# namespace + !keep -> emit nothing
|
|
16
|
+
module Registration
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
def methods_block(class_name:, module_namespace:, indent:,
|
|
20
|
+
lazy: false, keep_when_namespaced: false)
|
|
21
|
+
return "" if module_namespace && !keep_when_namespaced
|
|
22
|
+
return register_only_block(indent, lazy) if module_namespace
|
|
23
|
+
|
|
24
|
+
full_block(class_name, indent, lazy)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def execution_line(class_name:, module_namespace:)
|
|
28
|
+
return "" if module_namespace
|
|
29
|
+
|
|
30
|
+
"\n#{class_name}.register_class_with_id\n"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_only_block(indent, lazy)
|
|
34
|
+
<<~REG.gsub(/^/, indent)
|
|
35
|
+
|
|
36
|
+
def self.register
|
|
37
|
+
#{indent}#{register_body(lazy)}
|
|
38
|
+
end
|
|
39
|
+
REG
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def full_block(class_name, indent, lazy)
|
|
43
|
+
target = Utils.snake_case(class_name).to_sym
|
|
44
|
+
<<~REG.gsub(/^/, indent)
|
|
45
|
+
|
|
46
|
+
def self.register
|
|
47
|
+
#{indent}#{register_body(lazy)}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.register_class_with_id
|
|
51
|
+
#{indent}context = Lutaml::Model::GlobalContext.context(Lutaml::Model::Config.default_register)
|
|
52
|
+
#{indent}context.registry.register(:#{target}, self)
|
|
53
|
+
end
|
|
54
|
+
REG
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def register_body(lazy)
|
|
58
|
+
lazy ? "@register ||= Lutaml::Model::Config.default_register" : "Lutaml::Model::Config.default_register"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|