lutaml-model 0.8.18 → 0.8.22

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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +1 -1
  3. data/.github/workflows/rake.yml +1 -0
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +133 -72
  6. data/bench/bench_common.rb +8 -5
  7. data/bench/bench_niso.rb +1 -2
  8. data/bench/gate_config.rb +4 -1
  9. data/benchmark/serialization_benchmark.rb +4 -4
  10. data/docs/_guides/builder-dsl.adoc +127 -0
  11. data/docs/_guides/value-transformations.adoc +27 -0
  12. data/lib/compat/opal/generate_boot.rb +3 -1
  13. data/lib/compat/opal/lutaml_model_boot.rb +10 -7
  14. data/lib/compat/opal/moxml_boot.rb +6 -1
  15. data/lib/lutaml/hash_format/format.rb +29 -0
  16. data/lib/lutaml/hash_format.rb +5 -27
  17. data/lib/lutaml/json/format.rb +61 -0
  18. data/lib/lutaml/json.rb +4 -59
  19. data/lib/lutaml/jsonl/format.rb +22 -0
  20. data/lib/lutaml/jsonl.rb +5 -19
  21. data/lib/lutaml/key_value/format.rb +26 -0
  22. data/lib/lutaml/key_value/transform.rb +4 -1
  23. data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
  24. data/lib/lutaml/key_value.rb +4 -21
  25. data/lib/lutaml/model/attribute.rb +38 -4
  26. data/lib/lutaml/model/collection.rb +2 -2
  27. data/lib/lutaml/model/compiled_rule.rb +1 -1
  28. data/lib/lutaml/model/config.rb +2 -2
  29. data/lib/lutaml/model/consolidation_map.rb +2 -2
  30. data/lib/lutaml/model/consolidation_rule/dispatch_block.rb +2 -2
  31. data/lib/lutaml/model/errors.rb +2 -2
  32. data/lib/lutaml/model/global_context.rb +4 -2
  33. data/lib/lutaml/model/mapping/mapping_rule.rb +4 -1
  34. data/lib/lutaml/model/mapping/model_mapping.rb +2 -2
  35. data/lib/lutaml/model/runtime_compatibility.rb +2 -2
  36. data/lib/lutaml/model/schema/class_loader.rb +2 -1
  37. data/lib/lutaml/model/schema/decorators/definition_collection.rb +4 -4
  38. data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
  39. data/lib/lutaml/model/schema/file_writer.rb +5 -2
  40. data/lib/lutaml/model/schema/renderers/base.rb +4 -3
  41. data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
  42. data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
  43. data/lib/lutaml/model/schema/renderers/model.rb +18 -6
  44. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
  45. data/lib/lutaml/model/schema/renderers.rb +2 -1
  46. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
  47. data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
  48. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
  49. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
  50. data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
  51. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
  52. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
  53. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
  54. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
  55. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +2 -2
  56. data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
  57. data/lib/lutaml/model/sequence.rb +4 -4
  58. data/lib/lutaml/model/serialize/attribute_definition.rb +10 -9
  59. data/lib/lutaml/model/serialize/builder.rb +55 -0
  60. data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
  61. data/lib/lutaml/model/serialize/initialization.rb +12 -3
  62. data/lib/lutaml/model/serialize/model_import.rb +1 -0
  63. data/lib/lutaml/model/serialize.rb +4 -3
  64. data/lib/lutaml/model/type/uninitialized_class_guard.rb +1 -1
  65. data/lib/lutaml/model/type.rb +9 -0
  66. data/lib/lutaml/model/type_registry.rb +2 -2
  67. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  68. data/lib/lutaml/model/union.rb +5 -2
  69. data/lib/lutaml/model/utils.rb +2 -2
  70. data/lib/lutaml/model/value_policy.rb +27 -0
  71. data/lib/lutaml/model/version.rb +1 -1
  72. data/lib/lutaml/model.rb +17 -16
  73. data/lib/lutaml/rdf/mapping.rb +2 -2
  74. data/lib/lutaml/rdf/namespace_set.rb +2 -2
  75. data/lib/lutaml/rdf/transform.rb +2 -2
  76. data/lib/lutaml/toml/format.rb +48 -0
  77. data/lib/lutaml/toml.rb +5 -46
  78. data/lib/lutaml/xml/adapter/base_adapter.rb +2 -2
  79. data/lib/lutaml/xml/builder/base.rb +2 -2
  80. data/lib/lutaml/xml/format.rb +331 -0
  81. data/lib/lutaml/xml/mapping.rb +2 -2
  82. data/lib/lutaml/xml/mapping_rule.rb +62 -3
  83. data/lib/lutaml/xml/model_transform.rb +132 -55
  84. data/lib/lutaml/xml/namespace_collector.rb +3 -3
  85. data/lib/lutaml/xml/parse_session.rb +59 -0
  86. data/lib/lutaml/xml/parsed_namespace_set.rb +2 -2
  87. data/lib/lutaml/xml/schema/builder.rb +5 -5
  88. data/lib/lutaml/xml/schema/xsd/schema_location_mapping.rb +2 -2
  89. data/lib/lutaml/xml/schema/xsd.rb +0 -2
  90. data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
  91. data/lib/lutaml/xml/serialization/format_conversion.rb +3 -3
  92. data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
  93. data/lib/lutaml/xml/transformation/ordered_applier.rb +99 -4
  94. data/lib/lutaml/xml/xml_element.rb +2 -0
  95. data/lib/lutaml/xml.rb +4 -328
  96. data/lib/lutaml/yaml/format.rb +30 -0
  97. data/lib/lutaml/yaml.rb +5 -28
  98. data/lib/lutaml/yamls/adapter/mapping.rb +2 -2
  99. data/lib/lutaml/yamls/format.rb +22 -0
  100. data/lib/lutaml/yamls.rb +5 -19
  101. data/lib/tasks/benchmark_runner.rb +2 -2
  102. data/lutaml-model.gemspec +1 -1
  103. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
  104. data/spec/lutaml/model/attribute_spec.rb +8 -3
  105. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +2 -2
  106. data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
  107. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +8 -8
  108. data/spec/lutaml/model/defaults_spec.rb +45 -0
  109. data/spec/lutaml/model/json_spec.rb +1 -2
  110. data/spec/lutaml/model/ordered_content_spec.rb +18 -7
  111. data/spec/lutaml/model/serialize/builder_spec.rb +361 -0
  112. data/spec/lutaml/model/store_spec.rb +3 -1
  113. data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
  114. data/spec/lutaml/model/union_attribute_spec.rb +4 -2
  115. data/spec/lutaml/model/value_policy_spec.rb +82 -0
  116. data/spec/lutaml/require_load_order_spec.rb +25 -0
  117. data/spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb +88 -0
  118. data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
  119. data/spec/lutaml/xml/out_of_namespace_parsing_spec.rb +123 -0
  120. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
  121. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
  122. data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
  123. metadata +22 -4
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ module ValuePolicySpec
6
+ # A value type whose whole purpose is to wrap any literal:
7
+ # scalar, list, or map (issue #752).
8
+ class LiteralValue < Lutaml::Model::Type::Value
9
+ end
10
+
11
+ class LiteralDoc < Lutaml::Model::Serializable
12
+ attribute :v, LiteralValue
13
+ key_value do
14
+ map "v", to: :v
15
+ end
16
+ end
17
+
18
+ class StringsDoc < Lutaml::Model::Serializable
19
+ attribute :v, :string
20
+ key_value do
21
+ map "v", to: :v
22
+ end
23
+ end
24
+
25
+ class CollectionDoc < Lutaml::Model::Serializable
26
+ attribute :v, :string, collection: true
27
+ key_value do
28
+ map "v", to: :v
29
+ end
30
+ end
31
+ end
32
+
33
+ RSpec.describe "ValuePolicy shaping of non-collection attributes" do
34
+ describe "user-defined Type::Value subclass" do
35
+ it "receives the whole array from key-value data" do
36
+ expect(ValuePolicySpec::LiteralDoc.from_yaml("v: [a, b]").v)
37
+ .to eq(["a", "b"])
38
+ end
39
+
40
+ it "receives an empty array from key-value data" do
41
+ expect(ValuePolicySpec::LiteralDoc.from_yaml("v: []").v).to eq([])
42
+ end
43
+
44
+ it "receives the whole hash from key-value data" do
45
+ v = ValuePolicySpec::LiteralDoc.from_yaml("v: {a: 1}").v
46
+ expect(v.value).to eq("a" => 1)
47
+ end
48
+
49
+ it "keeps scalar behavior" do
50
+ doc = ValuePolicySpec::LiteralDoc.from_yaml(<<~YAML)
51
+ v: hello
52
+ YAML
53
+ expect(doc.v).to eq("hello")
54
+ end
55
+
56
+ it "round-trips an array value through YAML" do
57
+ doc = ValuePolicySpec::LiteralDoc.from_yaml("v: [a, b]")
58
+ expect(ValuePolicySpec::LiteralDoc.from_yaml(doc.to_yaml).v)
59
+ .to eq(["a", "b"])
60
+ end
61
+
62
+ it "accepts an array assigned after construction" do
63
+ doc = ValuePolicySpec::LiteralDoc.new(v: "seed")
64
+ doc.v = ["a", "b"]
65
+ expect(doc.v).to eq(["a", "b"])
66
+ end
67
+ end
68
+
69
+ describe "built-in scalar types" do
70
+ it "keep the collection: true guidance error" do
71
+ expect { ValuePolicySpec::StringsDoc.from_yaml("v: [a]") }
72
+ .to raise_error(Lutaml::Model::CollectionTrueMissingError)
73
+ end
74
+ end
75
+
76
+ describe "collection attributes" do
77
+ it "keep element-wise casting" do
78
+ expect(ValuePolicySpec::CollectionDoc.from_yaml("v: [a, b]").v)
79
+ .to eq(["a", "b"])
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string: true
2
+
3
+ require "spec_helper"
4
+
5
+ # lutaml-model: every public format entrypoint and `require "lutaml/model"`
6
+ # itself must not produce "circular require considered harmful" warnings
7
+ # under ruby -w (regression guard after the public-wrapper/internal-format
8
+ # restructuring).
9
+ RSpec.describe "require load order" do
10
+ it "loads lutaml/model with no circular-require warnings under -w" do
11
+ output = `bundle exec ruby -w -e 'require "lutaml/model"' 2>&1`
12
+ circular = output.lines.count { |l| l.include?("circular require considered harmful") }
13
+ expect(circular).to eq(0),
14
+ "Expected no circular-require warnings, got #{circular}:\n#{output}"
15
+ end
16
+
17
+ %w[json key_value yaml toml hash_format jsonl yamls xml].each do |format|
18
+ it "loads lutaml/#{format} standalone without circular-require warnings" do
19
+ output = `bundle exec ruby -w -e 'require "lutaml/#{format}"' 2>&1`
20
+ circular = output.lines.count { |l| l.include?("circular require considered harmful") }
21
+ expect(circular).to eq(0),
22
+ "Expected no circular-require warnings loading lutaml/#{format}, got #{circular}:\n#{output}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string: true
2
+
3
+ require "spec_helper"
4
+ require_relative "../../../lib/lutaml/model"
5
+
6
+ # lutaml-model#758: a plain (unnamespaced) map_attribute rule on an
7
+ # element-namespaced class must not absorb a namespaced sibling attribute
8
+ # (and vice versa), and a type-level `namespace :blank` must not crash.
9
+ module ElementNsDisjointSpec
10
+ class XmiNs < Lutaml::Xml::Namespace
11
+ uri "http://www.example.com/xmi"
12
+ prefix_default "xmi"
13
+ end
14
+
15
+ class ElemNs < Lutaml::Xml::Namespace
16
+ uri "http://www.example.com/elem"
17
+ prefix_default "e"
18
+ end
19
+
20
+ class XmiTypeAttr < Lutaml::Model::Type::String
21
+ xml do
22
+ namespace XmiNs
23
+ end
24
+ end
25
+
26
+ class DocBase < Lutaml::Model::Serializable
27
+ attribute :xmi_type, XmiTypeAttr
28
+
29
+ xml do
30
+ namespace ElemNs
31
+ map_attribute "type", to: :xmi_type
32
+ end
33
+ end
34
+
35
+ class Doc < DocBase
36
+ attribute :plain_type, Lutaml::Model::Type::String
37
+
38
+ xml do
39
+ root "doc"
40
+ map_attribute "type", to: :plain_type
41
+ end
42
+ end
43
+
44
+ class BlankType < Lutaml::Model::Type::String
45
+ xml do
46
+ namespace :blank
47
+ end
48
+ end
49
+
50
+ class BlankDoc < Lutaml::Model::Serializable
51
+ attribute :v, BlankType
52
+ xml do
53
+ root "bd"
54
+ map_attribute "v", to: :v
55
+ end
56
+ end
57
+ end
58
+
59
+ RSpec.describe "attribute namespace-disjointness on element-namespaced classes (#758)" do
60
+ it "routes xmi:type to the namespaced slot and leaves plain_type nil" do
61
+ doc = ElementNsDisjointSpec::Doc.from_xml(
62
+ %(<doc xmlns:xmi="http://www.example.com/xmi" xmi:type="uml:Parameter"/>),
63
+ )
64
+ expect(doc.xmi_type).to eq("uml:Parameter")
65
+ expect(doc.plain_type).to be_nil
66
+ end
67
+
68
+ it "routes an unprefixed type to plain_type, leaves xmi_type nil" do
69
+ doc = ElementNsDisjointSpec::Doc.from_xml(%(<doc type="EAnone_void"/>))
70
+ expect(doc.plain_type).to eq("EAnone_void")
71
+ expect(doc.xmi_type).to be_nil
72
+ end
73
+
74
+ it "round-trips both spellings without emitting a spurious duplicate" do
75
+ doc = ElementNsDisjointSpec::Doc.from_xml(
76
+ %(<doc xmlns:xmi="http://www.example.com/xmi" xmi:type="uml:Parameter"/>),
77
+ )
78
+ out = doc.to_xml.strip
79
+ expect(out).to include('xmi:type="uml:Parameter"')
80
+ expect(out).not_to match(/\stype="uml:Parameter"/)
81
+ end
82
+
83
+ it "survives type-level namespace :blank without crashing" do
84
+ expect do
85
+ ElementNsDisjointSpec::BlankDoc.from_xml(%(<bd v="x"/>))
86
+ end.not_to raise_error
87
+ end
88
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Two map_attribute rules that share a local name but are distinguished by
6
+ # their type classes' bound namespaces must parse disjointly, mirroring
7
+ # serialization (lutaml-model#744).
8
+ #
9
+ # The coexisting-spelling cases under nokogiri/rexml additionally require the
10
+ # moxml adapter fixes (lutaml/moxml#94, lutaml/moxml#95); oga and ox already
11
+ # preserve same-local-name attributes across namespaces.
12
+ module AttributeNamespaceDisjointnessSpec
13
+ class XmiNs < Lutaml::Xml::Namespace
14
+ uri "http://www.example.com/xmi"
15
+ prefix_default "xmi"
16
+ end
17
+
18
+ class XmiNsType < Lutaml::Model::Type::String
19
+ xml do
20
+ namespace XmiNs
21
+ end
22
+ end
23
+
24
+ class Probe < Lutaml::Model::Serializable
25
+ attribute :xmi_type, XmiNsType
26
+ attribute :plain_type, Lutaml::Model::Type::String
27
+
28
+ xml do
29
+ element "probe"
30
+ map_attribute "type", to: :xmi_type
31
+ map_attribute "type", to: :plain_type
32
+ end
33
+ end
34
+
35
+ def self.parse(xml, adapter)
36
+ Lutaml::Model::Config.with_adapter(xml: adapter) { Probe.from_xml(xml) }
37
+ end
38
+ end
39
+
40
+ RSpec.describe "Attribute namespace-disjoint parsing" do
41
+ let(:namespaced_xmi) do
42
+ %(<probe xmlns:xmi="http://www.example.com/xmi" xmi:type="uml:Parameter"/>)
43
+ end
44
+
45
+ let(:namespaced_foo) do
46
+ %(<probe xmlns:foo="http://www.example.com/xmi" foo:type="uml:Parameter"/>)
47
+ end
48
+
49
+ let(:plain) { %(<probe type="EAnone_void"/>) }
50
+
51
+ let(:both_spellings) do
52
+ %(<probe xmlns:xmi="http://www.example.com/xmi" xmi:type="uml:Parameter" type="EAnone_void"/>)
53
+ end
54
+
55
+ let(:both_spellings_plain_first) do
56
+ %(<probe type="EAnone_void" xmlns:xmi="http://www.example.com/xmi" xmi:type="uml:Parameter"/>)
57
+ end
58
+
59
+ %i[oga ox nokogiri].each do |adapter|
60
+ context "with the #{adapter} adapter" do
61
+ it "routes a namespaced rule to the attribute bound to its URI" do
62
+ probe = AttributeNamespaceDisjointnessSpec.parse(namespaced_xmi,
63
+ adapter)
64
+
65
+ expect(probe.xmi_type).to eq("uml:Parameter")
66
+ expect(probe.plain_type).to be_nil
67
+ end
68
+
69
+ it "accepts any prefix bound to the namespace URI" do
70
+ probe = AttributeNamespaceDisjointnessSpec.parse(namespaced_foo,
71
+ adapter)
72
+
73
+ expect(probe.xmi_type).to eq("uml:Parameter")
74
+ expect(probe.plain_type).to be_nil
75
+ end
76
+
77
+ it "does not let a namespace-less attribute satisfy the namespaced rule" do
78
+ probe = AttributeNamespaceDisjointnessSpec.parse(plain, adapter)
79
+
80
+ expect(probe.xmi_type).to be_nil
81
+ expect(probe.plain_type).to eq("EAnone_void")
82
+ end
83
+ end
84
+ end
85
+
86
+ # Same-local-name attributes coexisting on one element require the moxml
87
+ # fixes (lutaml/moxml#94 drops one under nokogiri; lutaml/moxml#95 crashes
88
+ # under rexml), so these cases run on the adapters that preserve both.
89
+ %i[oga ox].each do |adapter|
90
+ context "with the #{adapter} adapter" do
91
+ it "keeps both spellings disjoint regardless of document order" do
92
+ [both_spellings, both_spellings_plain_first].each do |xml|
93
+ probe = AttributeNamespaceDisjointnessSpec.parse(xml, adapter)
94
+
95
+ expect(probe.xmi_type).to eq("uml:Parameter")
96
+ expect(probe.plain_type).to eq("EAnone_void")
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ describe "serialization" do
103
+ it "writes each rule in its own namespace" do
104
+ probe = AttributeNamespaceDisjointnessSpec::Probe.new(
105
+ xmi_type: "uml:Parameter",
106
+ plain_type: "EAnone_void",
107
+ )
108
+
109
+ expect(probe.to_xml.strip).to be_xml_equivalent_to(
110
+ %(<probe xmlns:xmi="http://www.example.com/xmi" xmi:type="uml:Parameter" type="EAnone_void"/>),
111
+ )
112
+ end
113
+
114
+ it "round-trips both spellings through parsing" do
115
+ probe = AttributeNamespaceDisjointnessSpec::Probe.new(
116
+ xmi_type: "uml:Parameter",
117
+ plain_type: "EAnone_void",
118
+ )
119
+ reparsed = AttributeNamespaceDisjointnessSpec.parse(probe.to_xml, :oga)
120
+
121
+ expect(reparsed.xmi_type).to eq("uml:Parameter")
122
+ expect(reparsed.plain_type).to eq("EAnone_void")
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # lutaml-model#754: parsing a document whose elements live in a namespace
6
+ # different from the model's bound namespace must not silently drop
7
+ # element content. The document's own namespace is adopted as an implicit
8
+ # alias of the model's namespace for the duration of the parse, so
9
+ # lenient (out-of-namespace) parsing is lossless for element content the
10
+ # way in-namespace parsing is.
11
+ module OutOfNamespaceParsingSpec
12
+ class Gml32 < Lutaml::Xml::Namespace
13
+ uri "http://www.opengis.net/gml/3.2"
14
+ prefix_default "gml"
15
+ end
16
+
17
+ class Code < Lutaml::Model::Serializable
18
+ attribute :value, :string
19
+
20
+ xml do
21
+ root "name"
22
+ namespace Gml32
23
+ map_content to: :value
24
+ end
25
+ end
26
+
27
+ class EntryDefinition < Lutaml::Model::Serializable
28
+ attribute :description, :string
29
+ attribute :name, Code, collection: true
30
+
31
+ xml do
32
+ element "Definition"
33
+ namespace Gml32
34
+ map_element "description", to: :description
35
+ map_element "name", to: :name
36
+ end
37
+ end
38
+
39
+ class DictionaryEntry < Lutaml::Model::Serializable
40
+ attribute :definition, EntryDefinition
41
+
42
+ xml do
43
+ element "dictionaryEntry"
44
+ namespace Gml32
45
+ map_element "Definition", to: :definition
46
+ end
47
+ end
48
+
49
+ class Dictionary < Lutaml::Model::Serializable
50
+ attribute :name, Code, collection: true
51
+ attribute :dictionary_entry, DictionaryEntry, collection: true
52
+
53
+ xml do
54
+ element "Dictionary"
55
+ namespace Gml32
56
+ map_element "name", to: :name
57
+ map_element "dictionaryEntry", to: :dictionary_entry
58
+ end
59
+ end
60
+
61
+ def self.gml_doc(uri)
62
+ <<~XML
63
+ <?xml version="1.0" encoding="UTF-8"?>
64
+ <gml:Dictionary xmlns:gml="#{uri}">
65
+ <gml:name>codes</gml:name>
66
+ <gml:dictionaryEntry>
67
+ <gml:Definition>
68
+ <gml:description>first</gml:description>
69
+ <gml:name>ONE</gml:name>
70
+ </gml:Definition>
71
+ </gml:dictionaryEntry>
72
+ </gml:Dictionary>
73
+ XML
74
+ end
75
+ end
76
+
77
+ RSpec.describe "out-of-namespace document parsing" do
78
+ let(:in_namespace_xml) { OutOfNamespaceParsingSpec.gml_doc("http://www.opengis.net/gml/3.2") }
79
+ let(:out_of_namespace_xml) { OutOfNamespaceParsingSpec.gml_doc("http://www.opengis.net/gml") }
80
+
81
+ it "parses simple-type element content from a document in the bound namespace" do
82
+ definition = OutOfNamespaceParsingSpec::Dictionary
83
+ .from_xml(in_namespace_xml).dictionary_entry.first.definition
84
+ expect(definition.description).to eq("first")
85
+ end
86
+
87
+ it "keeps simple-type element content when the document namespace differs (#754)" do
88
+ definition = OutOfNamespaceParsingSpec::Dictionary
89
+ .from_xml(out_of_namespace_xml).dictionary_entry.first.definition
90
+ expect(definition.description).to eq("first")
91
+ end
92
+
93
+ it "keeps serializable-typed element content when the document namespace differs" do
94
+ definition = OutOfNamespaceParsingSpec::Dictionary
95
+ .from_xml(out_of_namespace_xml).dictionary_entry.first.definition
96
+ expect(definition.name.map(&:value)).to eq(["ONE"])
97
+ end
98
+
99
+ it "keeps simple-type content at the document root level" do
100
+ dictionary = OutOfNamespaceParsingSpec::Dictionary.from_xml(out_of_namespace_xml)
101
+ expect(dictionary.name.map(&:value)).to eq(["codes"])
102
+ end
103
+
104
+ it "does not adopt an unrelated child namespace" do
105
+ xml = <<~XML
106
+ <?xml version="1.0" encoding="UTF-8"?>
107
+ <gml:Dictionary xmlns:gml="http://www.opengis.net/gml"
108
+ xmlns:city="http://www.opengis.net/citygml/2.0">
109
+ <city:name>not mine</city:name>
110
+ </gml:Dictionary>
111
+ XML
112
+ dictionary = OutOfNamespaceParsingSpec::Dictionary.from_xml(xml)
113
+ expect(dictionary.name).to be_empty
114
+ end
115
+
116
+ it "round-trips out-of-namespace content without losing the description" do
117
+ dictionary = OutOfNamespaceParsingSpec::Dictionary.from_xml(out_of_namespace_xml)
118
+ output = dictionary.to_xml(prefix: true)
119
+ expect(output).to include("first")
120
+ reparsed = OutOfNamespaceParsingSpec::Dictionary.from_xml(output)
121
+ expect(reparsed.dictionary_entry.first.definition.description).to eq("first")
122
+ end
123
+ end
@@ -14,7 +14,9 @@ RSpec.describe Lutaml::Model::Schema::RncCompiler do
14
14
 
15
15
  describe ".to_models" do
16
16
  context "with an RNC schema using named patterns and start |=" do
17
- let(:schema) { File.read("spec/fixtures/xml/schema/rnc/address_book.rnc") }
17
+ let(:schema) do
18
+ File.read("spec/fixtures/xml/schema/rnc/address_book.rnc")
19
+ end
18
20
  let(:sources) { described_class.to_models(schema) }
19
21
 
20
22
  it "compiles RNC through the RNG compiler" do
@@ -65,7 +67,9 @@ RSpec.describe Lutaml::Model::Schema::RncCompiler do
65
67
 
66
68
  context "with create_files output" do
67
69
  let(:dir) { Dir.mktmpdir }
68
- let(:schema) { File.read("spec/fixtures/xml/schema/rnc/address_book.rnc") }
70
+ let(:schema) do
71
+ File.read("spec/fixtures/xml/schema/rnc/address_book.rnc")
72
+ end
69
73
 
70
74
  before do
71
75
  stub_const("RncAddressBookSpec", Module.new)
@@ -206,7 +210,8 @@ RSpec.describe Lutaml::Model::Schema::RncCompiler do
206
210
  "spec/fixtures/xml/schema/rnc/includes/circular_a.rnc"
207
211
 
208
212
  expect do
209
- described_class.to_models(File.read(circular_path), location: circular_path)
213
+ described_class.to_models(File.read(circular_path),
214
+ location: circular_path)
210
215
  end.to raise_error(/circular_a\.rnc/)
211
216
  end
212
217
  end
@@ -308,7 +313,9 @@ RSpec.describe Lutaml::Model::Schema::RncCompiler do
308
313
  # The inline restricted type is subclassed (not mutated) so a fresh
309
314
  # namespaced type inherits its constraints and carries the namespace.
310
315
  expect(sources["CodeType"]).not_to match(/namespace \w+Namespace/)
311
- subclass = sources.keys.find { |k| sources[k].match?(/class \w+ < CodeType/) }
316
+ subclass = sources.keys.find do |k|
317
+ sources[k].match?(/class \w+ < CodeType/)
318
+ end
312
319
  expect(subclass).not_to be_nil
313
320
  expect(sources[subclass]).to match(/namespace \w+Namespace/)
314
321
  end
@@ -323,7 +330,9 @@ RSpec.describe Lutaml::Model::Schema::RncCompiler do
323
330
  namespaces = sources.select { |_, v| v.include?("Lutaml::Xml::W3c::XmlNamespace") }
324
331
  expect(namespaces.size).to eq(2)
325
332
  uris = namespaces.values.map { |v| v[/uri "([^"]+)"/, 1] }
326
- prefixes = namespaces.values.map { |v| v[/prefix_default "([^"]+)"/, 1] }
333
+ prefixes = namespaces.values.map do |v|
334
+ v[/prefix_default "([^"]+)"/, 1]
335
+ end
327
336
  expect(uris).to contain_exactly("urn:a", "urn:b")
328
337
  expect(prefixes.uniq.size).to eq(2)
329
338
  end
@@ -341,7 +350,9 @@ RSpec.describe Lutaml::Model::Schema::RncCompiler do
341
350
  }
342
351
  RNC
343
352
 
344
- wrapper = sources.keys.find { |k| sources[k].match?(/namespace \w+Namespace/) && !k.include?("Namespace") }
353
+ wrapper = sources.keys.find do |k|
354
+ sources[k].match?(/namespace \w+Namespace/) && !k.include?("Namespace")
355
+ end
345
356
  expect(sources[wrapper]).to include("< Lutaml::Model::Type::DateTime")
346
357
  end
347
358
  end
@@ -33,7 +33,9 @@ RSpec.describe Lutaml::Model::Schema::RngCompiler do
33
33
  end
34
34
 
35
35
  let(:dir) { Dir.mktmpdir }
36
- let(:schema) { File.read("spec/fixtures/xml/schema/rng/address_book.rng") }
36
+ let(:schema) do
37
+ File.read("spec/fixtures/xml/schema/rng/address_book.rng")
38
+ end
37
39
 
38
40
  let(:valid_xml) do
39
41
  <<~XML
@@ -220,10 +222,13 @@ RSpec.describe Lutaml::Model::Schema::RngCompiler do
220
222
  end
221
223
 
222
224
  let(:dir) { Dir.mktmpdir }
223
- let(:schema) { File.read("spec/fixtures/xml/schema/rng/integer_range.rng") }
225
+ let(:schema) do
226
+ File.read("spec/fixtures/xml/schema/rng/integer_range.rng")
227
+ end
224
228
 
225
229
  it "generates a SimpleType subclass for <data> with restrictions" do
226
- src = File.read(File.join(dir, "rngrestrictionspec", "st_integer_range.rb"))
230
+ src = File.read(File.join(dir, "rngrestrictionspec",
231
+ "st_integer_range.rb"))
227
232
  expect(src).to include("class StIntegerRange < Lutaml::Model::Type::Integer")
228
233
  expect(src).to include("options[:min] = 1")
229
234
  expect(src).to include("options[:max] = 255")
@@ -507,7 +512,9 @@ RSpec.describe Lutaml::Model::Schema::RngCompiler do
507
512
  expect(classes_hash.keys).to contain_exactly(
508
513
  "AddressBook", "Card", "CardContent"
509
514
  )
510
- classes_hash.each_value { |src| expect(src).to start_with("# frozen_string_literal: true") }
515
+ classes_hash.each_value do |src|
516
+ expect(src).to start_with("# frozen_string_literal: true")
517
+ end
511
518
  end
512
519
  end
513
520
 
@@ -821,7 +828,8 @@ RSpec.describe Lutaml::Model::Schema::RngCompiler do
821
828
 
822
829
  it "returns generated source per class" do
823
830
  expect(result).to be_a(Hash)
824
- expect(result.keys).to contain_exactly("AddressBook", "Card", "CardContent")
831
+ expect(result.keys).to contain_exactly("AddressBook", "Card",
832
+ "CardContent")
825
833
  end
826
834
 
827
835
  it "loads the namespaced module and registers classes" do
@@ -886,7 +894,8 @@ RSpec.describe Lutaml::Model::Schema::RngCompiler do
886
894
  File.read("spec/fixtures/xml/schema/rng/address_book.rng"),
887
895
  )
888
896
  expect(result).to be_a(Hash)
889
- expect(result.keys).to contain_exactly("AddressBook", "Card", "CardContent")
897
+ expect(result.keys).to contain_exactly("AddressBook", "Card",
898
+ "CardContent")
890
899
  end
891
900
  end
892
901
  end
@@ -307,9 +307,8 @@ RSpec.describe Lutaml::Xml::W3c do
307
307
  RUBY
308
308
  temp_script.close
309
309
 
310
- # rubocop:disable Style/CommandLiteral
310
+ # rubocop:disable-next Style/CommandLiteral
311
311
  result = `#{RbConfig.ruby} -I#{lib_path} #{temp_script.path} 2>&1`
312
- # rubocop:enable Style/CommandLiteral
313
312
 
314
313
  expect($?.success?).to be(true),
315
314
  "W3C types not registered after require 'lutaml/xml'. " \
@@ -335,9 +334,8 @@ RSpec.describe Lutaml::Xml::W3c do
335
334
  RUBY
336
335
  temp_script.close
337
336
 
338
- # rubocop:disable Style/CommandLiteral
337
+ # rubocop:disable-next Style/CommandLiteral
339
338
  result = `#{RbConfig.ruby} -I#{lib_path} #{temp_script.path} 2>&1`
340
- # rubocop:enable Style/CommandLiteral
341
339
 
342
340
  expect($?.success?).to be(true),
343
341
  "Type.lookup failed for W3C symbols after require 'lutaml/xml'. " \
@@ -361,9 +359,8 @@ RSpec.describe Lutaml::Xml::W3c do
361
359
  RUBY
362
360
  temp_script.close
363
361
 
364
- # rubocop:disable Style/CommandLiteral
362
+ # rubocop:disable-next Style/CommandLiteral
365
363
  result = `#{RbConfig.ruby} -I#{lib_path} #{temp_script.path} 2>&1`
366
- # rubocop:enable Style/CommandLiteral
367
364
 
368
365
  expect($?.success?).to be(true),
369
366
  "Symbol-based attribute definition failed after require 'lutaml/xml'. " \