lutaml-model 0.8.19 → 0.9.1

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.

Potentially problematic release.


This version of lutaml-model might be problematic. Click here for more details.

Files changed (56) 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_todo.yml +133 -72
  5. data/bench/bench_common.rb +6 -3
  6. data/bench/bench_niso.rb +1 -2
  7. data/lib/compat/opal/generate_boot.rb +3 -1
  8. data/lib/lutaml/key_value/transform.rb +4 -1
  9. data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
  10. data/lib/lutaml/model/attribute.rb +23 -2
  11. data/lib/lutaml/model/global_context.rb +4 -2
  12. data/lib/lutaml/model/schema/class_loader.rb +2 -1
  13. data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
  14. data/lib/lutaml/model/schema/file_writer.rb +5 -2
  15. data/lib/lutaml/model/schema/renderers/base.rb +2 -1
  16. data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
  17. data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
  18. data/lib/lutaml/model/schema/renderers/model.rb +16 -4
  19. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
  20. data/lib/lutaml/model/schema/renderers.rb +2 -1
  21. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
  22. data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
  23. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
  24. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
  25. data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
  26. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
  27. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
  28. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
  29. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
  30. data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
  31. data/lib/lutaml/model/serialize/attribute_definition.rb +1 -0
  32. data/lib/lutaml/model/serialize/initialization.rb +9 -1
  33. data/lib/lutaml/model/serialize/model_import.rb +1 -0
  34. data/lib/lutaml/model/serialize.rb +5 -4
  35. data/lib/lutaml/model/union.rb +5 -2
  36. data/lib/lutaml/model/version.rb +1 -1
  37. data/lib/lutaml/xml/mapping_rule.rb +55 -1
  38. data/lib/lutaml/xml/model_transform.rb +53 -5
  39. data/lib/lutaml/xml/schema/xsd.rb +0 -2
  40. data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
  41. data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
  42. data/lib/lutaml/xml/transformation/ordered_applier.rb +2 -1
  43. data/lutaml-model.gemspec +4 -1
  44. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
  45. data/spec/lutaml/model/attribute_spec.rb +8 -3
  46. data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
  47. data/spec/lutaml/model/defaults_spec.rb +45 -0
  48. data/spec/lutaml/model/json_spec.rb +1 -2
  49. data/spec/lutaml/model/serialize/builder_spec.rb +6 -3
  50. data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
  51. data/spec/lutaml/model/union_attribute_spec.rb +4 -2
  52. data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
  53. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
  54. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
  55. data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
  56. metadata +5 -3
@@ -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
@@ -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'. " \
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.19
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-27 00:00:00.000000000 Z
11
+ date: 2026-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -1654,6 +1654,7 @@ files:
1654
1654
  - spec/lutaml/model/context_registry_spec.rb
1655
1655
  - spec/lutaml/model/custom_bibtex_adapter_spec.rb
1656
1656
  - spec/lutaml/model/custom_collection_spec.rb
1657
+ - spec/lutaml/model/custom_from_empty_values_spec.rb
1657
1658
  - spec/lutaml/model/custom_model_spec.rb
1658
1659
  - spec/lutaml/model/custom_serialization_spec.rb
1659
1660
  - spec/lutaml/model/custom_vobject_adapter_spec.rb
@@ -1812,6 +1813,7 @@ files:
1812
1813
  - spec/lutaml/xml/adapter/ox_adapter_spec.rb
1813
1814
  - spec/lutaml/xml/adapter/rexml_adapter_spec.rb
1814
1815
  - spec/lutaml/xml/adapter/xml_namespace_spec.rb
1816
+ - spec/lutaml/xml/attribute_namespace_disjointness_spec.rb
1815
1817
  - spec/lutaml/xml/builder/builder_spec.rb
1816
1818
  - spec/lutaml/xml/clear_parse_state_spec.rb
1817
1819
  - spec/lutaml/xml/compiled_rule_namespace_spec.rb
@@ -1946,7 +1948,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
1946
1948
  requirements:
1947
1949
  - - ">="
1948
1950
  - !ruby/object:Gem::Version
1949
- version: 3.0.0
1951
+ version: 3.2.0
1950
1952
  required_rubygems_version: !ruby/object:Gem::Requirement
1951
1953
  requirements:
1952
1954
  - - ">="