lutaml-model 0.8.9 → 0.8.11
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/dependent-repos.json +1 -0
- data/.rubocop_todo.yml +52 -22
- data/README.adoc +43 -0
- data/docs/_guides/jsonld-serialization.adoc +3 -1
- data/docs/_guides/rdf-serialization.adoc +94 -8
- data/docs/_guides/turtle-serialization.adoc +17 -4
- data/lib/lutaml/jsonld/transform.rb +70 -24
- data/lib/lutaml/key_value/transform.rb +5 -5
- data/lib/lutaml/key_value/transformation/collection_serializer.rb +25 -11
- data/lib/lutaml/key_value/transformation/value_serializer.rb +7 -7
- data/lib/lutaml/key_value/transformation.rb +27 -17
- data/lib/lutaml/model/adapter_resolver.rb +4 -6
- data/lib/lutaml/model/attribute.rb +26 -23
- data/lib/lutaml/model/cached_type_resolver.rb +10 -9
- data/lib/lutaml/model/cli.rb +1 -1
- data/lib/lutaml/model/collection.rb +4 -4
- data/lib/lutaml/model/comparable_model.rb +11 -11
- data/lib/lutaml/model/config.rb +1 -1
- data/lib/lutaml/model/consolidation/dispatcher.rb +1 -1
- data/lib/lutaml/model/consolidation/pattern_chunker.rb +3 -3
- data/lib/lutaml/model/format_registry.rb +6 -4
- data/lib/lutaml/model/global_context.rb +2 -2
- data/lib/lutaml/model/global_register.rb +1 -1
- data/lib/lutaml/model/instrumentation.rb +1 -1
- data/lib/lutaml/model/mapping/mapping_rule.rb +3 -3
- data/lib/lutaml/model/mapping/model_mapping.rb +1 -1
- data/lib/lutaml/model/mapping/model_mapping_rule.rb +1 -1
- data/lib/lutaml/model/register.rb +3 -3
- data/lib/lutaml/model/render_policy.rb +11 -17
- data/lib/lutaml/model/runtime_compatibility.rb +0 -1
- data/lib/lutaml/model/schema/xml_compiler/group.rb +1 -1
- data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +1 -1
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +14 -14
- data/lib/lutaml/model/serialize/attribute_definition.rb +1 -1
- data/lib/lutaml/model/serialize/deserialization_context.rb +50 -0
- data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
- data/lib/lutaml/model/serialize/initialization.rb +44 -7
- data/lib/lutaml/model/serialize/model_import.rb +1 -1
- data/lib/lutaml/model/serialize.rb +8 -1
- data/lib/lutaml/model/services/rule_value_extractor.rb +2 -1
- data/lib/lutaml/model/store.rb +77 -24
- data/lib/lutaml/model/transformation_registry.rb +1 -1
- data/lib/lutaml/model/type_context.rb +7 -1
- data/lib/lutaml/model/type_resolver.rb +1 -6
- data/lib/lutaml/model/utils.rb +19 -6
- data/lib/lutaml/model/validation_framework.rb +1 -1
- data/lib/lutaml/model/value_transformer.rb +2 -2
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/rdf/mapping.rb +19 -13
- data/lib/lutaml/rdf/mapping_rule.rb +19 -2
- data/lib/lutaml/rdf/member_rule.rb +19 -2
- data/lib/lutaml/rdf/transform.rb +20 -11
- data/lib/lutaml/turtle/transform.rb +125 -53
- data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -1
- data/lib/lutaml/xml/adapter/base_adapter.rb +10 -14
- data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +3 -3
- data/lib/lutaml/xml/adapter/plan_based_builder.rb +14 -14
- data/lib/lutaml/xml/adapter/xml_serializer.rb +3 -3
- data/lib/lutaml/xml/configurable.rb +2 -1
- data/lib/lutaml/xml/data_model.rb +2 -2
- data/lib/lutaml/xml/decisions/decision_context.rb +3 -3
- data/lib/lutaml/xml/decisions/rules/element_form_default_unqualified_rule.rb +1 -1
- data/lib/lutaml/xml/decisions/rules/element_form_option_rule.rb +1 -1
- data/lib/lutaml/xml/decisions/rules/used_prefix_rule.rb +1 -1
- data/lib/lutaml/xml/declaration_plan.rb +2 -2
- data/lib/lutaml/xml/declaration_planner.rb +12 -13
- data/lib/lutaml/xml/document.rb +13 -13
- data/lib/lutaml/xml/format_chooser.rb +3 -3
- data/lib/lutaml/xml/hoisting_algorithm.rb +1 -1
- data/lib/lutaml/xml/mapping.rb +2 -2
- data/lib/lutaml/xml/mapping_rule.rb +16 -3
- data/lib/lutaml/xml/model_transform.rb +17 -19
- data/lib/lutaml/xml/namespace_collector.rb +10 -10
- data/lib/lutaml/xml/namespace_declaration.rb +2 -2
- data/lib/lutaml/xml/namespace_declaration_data.rb +5 -8
- data/lib/lutaml/xml/namespace_scope_config.rb +3 -2
- data/lib/lutaml/xml/namespace_type_resolver.rb +4 -4
- data/lib/lutaml/xml/nokogiri/element.rb +2 -2
- data/lib/lutaml/xml/polymorphic_value_handler.rb +1 -1
- data/lib/lutaml/xml/schema/xsd/base.rb +7 -7
- data/lib/lutaml/xml/schema/xsd/choice.rb +2 -2
- data/lib/lutaml/xml/schema/xsd/complex_type.rb +5 -5
- data/lib/lutaml/xml/schema/xsd/errors/message_builder.rb +3 -3
- data/lib/lutaml/xml/schema/xsd/group.rb +2 -2
- data/lib/lutaml/xml/schema/xsd/sequence.rb +2 -2
- data/lib/lutaml/xml/schema/xsd_schema.rb +5 -5
- data/lib/lutaml/xml/serialization/format_conversion.rb +4 -3
- data/lib/lutaml/xml/transformation/element_builder.rb +4 -2
- data/lib/lutaml/xml/transformation/rule_applier.rb +2 -2
- data/lib/lutaml/xml/transformation/value_serializer.rb +4 -6
- data/lib/lutaml/xml/transformation.rb +4 -4
- data/lib/lutaml/xml/type/configurable.rb +0 -4
- data/lib/lutaml/xml/xml_element.rb +21 -13
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/jsonld/transform_spec.rb +239 -0
- data/spec/lutaml/model/cached_type_resolver_spec.rb +3 -3
- data/spec/lutaml/model/optimization_spec.rb +228 -0
- data/spec/lutaml/model/store_spec.rb +195 -0
- data/spec/lutaml/rdf/mapping_rule_spec.rb +97 -0
- data/spec/lutaml/rdf/mapping_spec.rb +74 -4
- data/spec/lutaml/rdf/member_rule_spec.rb +41 -0
- data/spec/lutaml/rdf/rdf_transform_spec.rb +95 -29
- data/spec/lutaml/turtle/mapping_spec.rb +2 -2
- data/spec/lutaml/turtle/transform_spec.rb +315 -0
- data/spec/lutaml/xml/data_model_spec.rb +10 -28
- metadata +7 -4
|
@@ -4,79 +4,145 @@ require "spec_helper"
|
|
|
4
4
|
require "lutaml/rdf"
|
|
5
5
|
|
|
6
6
|
RSpec.describe Lutaml::Rdf::Transform do
|
|
7
|
-
let(:transform) { described_class.new(nil) }
|
|
8
|
-
|
|
9
7
|
describe "#resolve_subject_uri" do
|
|
10
8
|
it "returns nil when mapping has no subject" do
|
|
11
9
|
mapping = Lutaml::Rdf::Mapping.new
|
|
12
|
-
|
|
10
|
+
transform = described_class.new(nil)
|
|
11
|
+
expect(transform.resolve_subject_uri(mapping, double)).to be_nil
|
|
13
12
|
end
|
|
14
13
|
|
|
15
14
|
it "calls subject proc with instance" do
|
|
16
15
|
mapping = Lutaml::Rdf::Mapping.new
|
|
17
16
|
mapping.subject { |i| "http://example.org/#{i}" }
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
transform = described_class.new(nil)
|
|
19
|
+
result = transform.resolve_subject_uri(mapping, "test")
|
|
20
20
|
expect(result).to eq("http://example.org/test")
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
describe "#
|
|
25
|
-
it "returns nil when mapping has no type" do
|
|
26
|
-
mapping = Lutaml::Rdf::Mapping.new
|
|
27
|
-
expect(transform.send(:resolve_type_uri, mapping)).to be_nil
|
|
28
|
-
end
|
|
29
|
-
|
|
24
|
+
describe "#resolve_single_type_uri" do
|
|
30
25
|
it "resolves compact IRI to full URI" do
|
|
31
|
-
mapping = Lutaml::Rdf::Mapping.new
|
|
32
26
|
stub_const("TestNs", Class.new(Lutaml::Rdf::Namespace) do
|
|
33
27
|
uri "http://example.org/"
|
|
34
28
|
prefix "ex"
|
|
35
29
|
end)
|
|
30
|
+
mapping = Lutaml::Rdf::Mapping.new
|
|
36
31
|
mapping.namespace(TestNs)
|
|
37
32
|
mapping.type "ex:Thing"
|
|
38
33
|
|
|
39
|
-
|
|
34
|
+
transform = described_class.new(nil)
|
|
35
|
+
result = transform.resolve_single_type_uri(mapping, "ex:Thing")
|
|
40
36
|
expect(result).to eq("http://example.org/Thing")
|
|
41
37
|
end
|
|
42
38
|
end
|
|
43
39
|
|
|
44
|
-
describe "#
|
|
45
|
-
it "returns
|
|
40
|
+
describe "#resolve_type_uris" do
|
|
41
|
+
it "returns empty array when mapping has no types" do
|
|
42
|
+
mapping = Lutaml::Rdf::Mapping.new
|
|
43
|
+
transform = described_class.new(nil)
|
|
44
|
+
expect(transform.resolve_type_uris(mapping)).to eq([])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "resolves all type compact IRIs to full URIs" do
|
|
48
|
+
stub_const("MultiNs", Class.new(Lutaml::Rdf::Namespace) do
|
|
49
|
+
uri "http://example.org/"
|
|
50
|
+
prefix "ex"
|
|
51
|
+
end)
|
|
46
52
|
mapping = Lutaml::Rdf::Mapping.new
|
|
47
|
-
mapping.
|
|
53
|
+
mapping.namespace(MultiNs)
|
|
54
|
+
mapping.type ["ex:Thing", "ex:Other"]
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
transform = described_class.new(nil)
|
|
57
|
+
result = transform.resolve_type_uris(mapping)
|
|
58
|
+
expect(result).to eq(["http://example.org/Thing", "http://example.org/Other"])
|
|
51
59
|
end
|
|
52
60
|
end
|
|
53
61
|
|
|
54
62
|
describe "#extract_language" do
|
|
55
63
|
it "extracts language from LanguageTagged objects" do
|
|
56
64
|
literal = Lutaml::Rdf::Literal.new("hello", language: "eng")
|
|
57
|
-
|
|
65
|
+
transform = described_class.new(nil)
|
|
66
|
+
expect(transform.extract_language(literal)).to eq("eng")
|
|
58
67
|
end
|
|
59
68
|
|
|
60
69
|
it "returns nil for plain strings" do
|
|
61
|
-
|
|
70
|
+
transform = described_class.new(nil)
|
|
71
|
+
expect(transform.extract_language("hello")).to be_nil
|
|
62
72
|
end
|
|
63
73
|
|
|
64
74
|
it "returns nil for non-LanguageTagged objects" do
|
|
65
|
-
|
|
75
|
+
transform = described_class.new(nil)
|
|
76
|
+
expect(transform.extract_language(42)).to be_nil
|
|
66
77
|
end
|
|
67
78
|
end
|
|
68
79
|
|
|
69
|
-
describe "#
|
|
70
|
-
it "
|
|
71
|
-
stub_const("
|
|
72
|
-
|
|
80
|
+
describe "#each_member" do
|
|
81
|
+
it "iterates over collection attribute values" do
|
|
82
|
+
stub_const("MemberItem", Class.new do
|
|
83
|
+
attr_reader :label
|
|
84
|
+
|
|
85
|
+
def initialize(label)
|
|
86
|
+
@label = label
|
|
87
|
+
end
|
|
88
|
+
end)
|
|
89
|
+
stub_const("MemberParent", Class.new(Lutaml::Model::Serializable) do
|
|
90
|
+
attribute :items, :string, collection: true
|
|
91
|
+
end)
|
|
92
|
+
|
|
93
|
+
instance = MemberParent.new(items: ["a", "b"])
|
|
94
|
+
member_rule = Lutaml::Rdf::MemberRule.new(:items)
|
|
95
|
+
transform = described_class.new(nil)
|
|
96
|
+
|
|
97
|
+
collected = []
|
|
98
|
+
transform.each_member(instance, member_rule) { |m| collected << m }
|
|
99
|
+
expect(collected).to eq(["a", "b"])
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "handles nil collection as empty" do
|
|
103
|
+
stub_const("NilParent", Class.new(Lutaml::Model::Serializable) do
|
|
104
|
+
attribute :items, :string, collection: true
|
|
105
|
+
end)
|
|
106
|
+
|
|
107
|
+
instance = NilParent.new
|
|
108
|
+
member_rule = Lutaml::Rdf::MemberRule.new(:items)
|
|
109
|
+
transform = described_class.new(nil)
|
|
110
|
+
|
|
111
|
+
collected = []
|
|
112
|
+
transform.each_member(instance, member_rule) { |m| collected << m }
|
|
113
|
+
expect(collected).to eq([])
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "#member_mapping_for" do
|
|
118
|
+
it "returns the mapping for the given format" do
|
|
119
|
+
stub_const("MappedChild", Class.new(Lutaml::Model::Serializable) do
|
|
120
|
+
attribute :label, :string
|
|
121
|
+
|
|
122
|
+
turtle do
|
|
123
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
124
|
+
subject { |m| "http://example.org/#{m.label}" } # rubocop:disable RSpec/NamedSubject
|
|
125
|
+
|
|
126
|
+
predicate :prefLabel,
|
|
127
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
128
|
+
to: :label
|
|
129
|
+
end
|
|
130
|
+
end)
|
|
131
|
+
|
|
132
|
+
member = MappedChild.new(label: "test")
|
|
133
|
+
transform = described_class.new(nil)
|
|
134
|
+
mapping = transform.member_mapping_for(member, :turtle)
|
|
135
|
+
expect(mapping).to be_a(Lutaml::Rdf::Mapping)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "returns nil when no mapping for format" do
|
|
139
|
+
stub_const("UnmappedChild", Class.new(Lutaml::Model::Serializable) do
|
|
140
|
+
attribute :label, :string
|
|
73
141
|
end)
|
|
74
142
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
expect(instance).to be_a(BuildTestModel)
|
|
79
|
-
expect(instance.name).to eq("test")
|
|
143
|
+
member = UnmappedChild.new(label: "test")
|
|
144
|
+
transform = described_class.new(nil)
|
|
145
|
+
expect(transform.member_mapping_for(member, :turtle)).to be_nil
|
|
80
146
|
end
|
|
81
147
|
end
|
|
82
148
|
end
|
|
@@ -27,7 +27,7 @@ RSpec.describe Lutaml::Turtle::Mapping do
|
|
|
27
27
|
describe "#type" do
|
|
28
28
|
it "sets RDF type" do
|
|
29
29
|
mapping.type("skos:Concept")
|
|
30
|
-
expect(mapping.rdf_type).to eq("skos:Concept")
|
|
30
|
+
expect(mapping.rdf_type).to eq(["skos:Concept"])
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -108,7 +108,7 @@ RSpec.describe Lutaml::Turtle::Mapping do
|
|
|
108
108
|
duped = mapping.deep_dup
|
|
109
109
|
expect(duped.namespace_set.size).to eq(2)
|
|
110
110
|
expect(duped.rdf_subject).to be_a(Proc)
|
|
111
|
-
expect(duped.rdf_type).to eq("skos:Concept")
|
|
111
|
+
expect(duped.rdf_type).to eq(["skos:Concept"])
|
|
112
112
|
expect(duped.rdf_predicates.length).to eq(1)
|
|
113
113
|
end
|
|
114
114
|
|
|
@@ -260,6 +260,13 @@ RSpec.describe Lutaml::Turtle::Transform do
|
|
|
260
260
|
model = TurtleTestModel.from_turtle(turtle_input)
|
|
261
261
|
expect(model.name).to eq("test concept")
|
|
262
262
|
end
|
|
263
|
+
|
|
264
|
+
it "round-trips deserialized data back to Turtle" do
|
|
265
|
+
model = TurtleTestModel.from_turtle(turtle_input)
|
|
266
|
+
turtle_out = model.to_turtle
|
|
267
|
+
expect(turtle_out).to include("skos:Concept")
|
|
268
|
+
expect(turtle_out).to include("skos:notation \"2119\"")
|
|
269
|
+
end
|
|
263
270
|
end
|
|
264
271
|
|
|
265
272
|
describe "round-trip" do
|
|
@@ -270,4 +277,312 @@ RSpec.describe Lutaml::Turtle::Transform do
|
|
|
270
277
|
expect(restored.description).to eq("A test description")
|
|
271
278
|
end
|
|
272
279
|
end
|
|
280
|
+
|
|
281
|
+
describe "multiple types" do
|
|
282
|
+
before do
|
|
283
|
+
stub_const("DualTypeModel", Class.new(Lutaml::Model::Serializable) do
|
|
284
|
+
attribute :name, :string
|
|
285
|
+
|
|
286
|
+
turtle do
|
|
287
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
288
|
+
Lutaml::Rdf::Namespaces::DctermsNamespace
|
|
289
|
+
|
|
290
|
+
subject { |m| "http://example.org/#{m.name}" }
|
|
291
|
+
|
|
292
|
+
type ["skos:Concept", "dcterms:Agent"]
|
|
293
|
+
|
|
294
|
+
predicate :prefLabel,
|
|
295
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
296
|
+
to: :name
|
|
297
|
+
end
|
|
298
|
+
end)
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
it "generates multiple rdf:type triples" do
|
|
302
|
+
instance = DualTypeModel.new(name: "test")
|
|
303
|
+
result = instance.to_turtle
|
|
304
|
+
expect(result).to include("a skos:Concept")
|
|
305
|
+
expect(result).to include("dcterms:Agent")
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
it "round-trips multiple types" do
|
|
309
|
+
instance = DualTypeModel.new(name: "test")
|
|
310
|
+
turtle = instance.to_turtle
|
|
311
|
+
restored = DualTypeModel.from_turtle(turtle)
|
|
312
|
+
expect(restored.name).to eq("test")
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
describe "empty type array" do
|
|
317
|
+
before do
|
|
318
|
+
stub_const("NoTypeModel", Class.new(Lutaml::Model::Serializable) do
|
|
319
|
+
attribute :name, :string
|
|
320
|
+
|
|
321
|
+
turtle do
|
|
322
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
323
|
+
|
|
324
|
+
subject { |m| "http://example.org/#{m.name}" }
|
|
325
|
+
|
|
326
|
+
predicate :prefLabel,
|
|
327
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
328
|
+
to: :name
|
|
329
|
+
end
|
|
330
|
+
end)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it "omits rdf:type when no types declared" do
|
|
334
|
+
instance = NoTypeModel.new(name: "test")
|
|
335
|
+
result = instance.to_turtle
|
|
336
|
+
expect(result).not_to include(" a ")
|
|
337
|
+
expect(result).to include("skos:prefLabel")
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
describe "URI reference predicates" do
|
|
342
|
+
before do
|
|
343
|
+
stub_const("UriRefModel", Class.new(Lutaml::Model::Serializable) do
|
|
344
|
+
attribute :name, :string
|
|
345
|
+
attribute :related, :string, collection: true
|
|
346
|
+
|
|
347
|
+
turtle do
|
|
348
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
349
|
+
Lutaml::Rdf::Namespaces::DctermsNamespace
|
|
350
|
+
|
|
351
|
+
subject { |m| "http://example.org/#{m.name}" }
|
|
352
|
+
|
|
353
|
+
type "skos:Concept"
|
|
354
|
+
|
|
355
|
+
predicate :prefLabel,
|
|
356
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
357
|
+
to: :name
|
|
358
|
+
|
|
359
|
+
predicate :related,
|
|
360
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
361
|
+
to: :related,
|
|
362
|
+
uri_reference: true
|
|
363
|
+
end
|
|
364
|
+
end)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
it "emits URI objects instead of literals" do
|
|
368
|
+
instance = UriRefModel.new(name: "test", related: ["skos:other"])
|
|
369
|
+
result = instance.to_turtle
|
|
370
|
+
expect(result).to include("skos:related skos:other")
|
|
371
|
+
expect(result).not_to include('"skos:other"')
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
it "round-trips URI references preserving compact form" do
|
|
375
|
+
instance = UriRefModel.new(name: "test", related: ["skos:other"])
|
|
376
|
+
turtle = instance.to_turtle
|
|
377
|
+
restored = UriRefModel.from_turtle(turtle)
|
|
378
|
+
expect(restored.related).to eq("skos:other")
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it "handles full URI values without prefix" do
|
|
382
|
+
instance = UriRefModel.new(name: "test",
|
|
383
|
+
related: ["http://example.org/foo"])
|
|
384
|
+
result = instance.to_turtle
|
|
385
|
+
expect(result).to include("skos:related <http://example.org/foo>")
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
it "round-trips full URI values as-is" do
|
|
389
|
+
instance = UriRefModel.new(name: "test",
|
|
390
|
+
related: ["http://example.org/foo"])
|
|
391
|
+
turtle = instance.to_turtle
|
|
392
|
+
restored = UriRefModel.from_turtle(turtle)
|
|
393
|
+
expect(restored.related).to eq("http://example.org/foo")
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
describe "member linking predicates" do
|
|
398
|
+
before do
|
|
399
|
+
stub_const("ChildModel", Class.new(Lutaml::Model::Serializable) do
|
|
400
|
+
attribute :label, :string
|
|
401
|
+
attribute :cid, :string
|
|
402
|
+
|
|
403
|
+
turtle do
|
|
404
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
405
|
+
|
|
406
|
+
subject { |m| "http://example.org/child/#{m.cid}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
407
|
+
|
|
408
|
+
type "skos:Concept"
|
|
409
|
+
|
|
410
|
+
predicate :prefLabel,
|
|
411
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
412
|
+
to: :label
|
|
413
|
+
end
|
|
414
|
+
end)
|
|
415
|
+
|
|
416
|
+
stub_const("ParentModel", Class.new(Lutaml::Model::Serializable) do
|
|
417
|
+
attribute :name, :string
|
|
418
|
+
attribute :children, ChildModel, collection: true
|
|
419
|
+
|
|
420
|
+
turtle do
|
|
421
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
422
|
+
|
|
423
|
+
subject { |m| "http://example.org/parent/#{m.name}" }
|
|
424
|
+
|
|
425
|
+
type "skos:Collection"
|
|
426
|
+
|
|
427
|
+
predicate :prefLabel,
|
|
428
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
429
|
+
to: :name
|
|
430
|
+
|
|
431
|
+
members :children,
|
|
432
|
+
predicate_name: :member,
|
|
433
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace
|
|
434
|
+
end
|
|
435
|
+
end)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
it "generates linking triples from parent to members" do
|
|
439
|
+
parent = ParentModel.new(
|
|
440
|
+
name: "parent1",
|
|
441
|
+
children: [
|
|
442
|
+
ChildModel.new(label: "child1", cid: "c1"),
|
|
443
|
+
ChildModel.new(label: "child2", cid: "c2"),
|
|
444
|
+
],
|
|
445
|
+
)
|
|
446
|
+
result = parent.to_turtle
|
|
447
|
+
expect(result).to include("skos:member <http://example.org/child/c1>")
|
|
448
|
+
expect(result).to include("<http://example.org/child/c2>")
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
it "still generates member graph nodes" do
|
|
452
|
+
parent = ParentModel.new(
|
|
453
|
+
name: "parent1",
|
|
454
|
+
children: [
|
|
455
|
+
ChildModel.new(label: "child1", cid: "c1"),
|
|
456
|
+
],
|
|
457
|
+
)
|
|
458
|
+
result = parent.to_turtle
|
|
459
|
+
expect(result).to include("skos:prefLabel \"child1\"")
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
describe "linked-only model (no type, no predicates)" do
|
|
464
|
+
before do
|
|
465
|
+
stub_const("LinkedOnlyChild", Class.new(Lutaml::Model::Serializable) do
|
|
466
|
+
attribute :cid, :string
|
|
467
|
+
attribute :label, :string
|
|
468
|
+
|
|
469
|
+
turtle do
|
|
470
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
471
|
+
|
|
472
|
+
subject { |m| "http://example.org/item/#{m.cid}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
473
|
+
|
|
474
|
+
type "skos:Concept"
|
|
475
|
+
|
|
476
|
+
predicate :prefLabel,
|
|
477
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
478
|
+
to: :label
|
|
479
|
+
end
|
|
480
|
+
end)
|
|
481
|
+
|
|
482
|
+
stub_const("LinkedOnlyParent", Class.new(Lutaml::Model::Serializable) do
|
|
483
|
+
attribute :pid, :string
|
|
484
|
+
attribute :items, LinkedOnlyChild, collection: true
|
|
485
|
+
|
|
486
|
+
turtle do
|
|
487
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
488
|
+
|
|
489
|
+
subject { |m| "http://example.org/group/#{m.pid}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
490
|
+
|
|
491
|
+
members :items,
|
|
492
|
+
predicate_name: :member,
|
|
493
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace
|
|
494
|
+
end
|
|
495
|
+
end)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
it "generates linking triples even without type or predicates" do
|
|
499
|
+
parent = LinkedOnlyParent.new(
|
|
500
|
+
pid: "g1",
|
|
501
|
+
items: [
|
|
502
|
+
LinkedOnlyChild.new(cid: "a", label: "Alpha"),
|
|
503
|
+
LinkedOnlyChild.new(cid: "b", label: "Beta"),
|
|
504
|
+
],
|
|
505
|
+
)
|
|
506
|
+
result = parent.to_turtle
|
|
507
|
+
expect(result).to include("skos:member")
|
|
508
|
+
expect(result).to include("<http://example.org/item/a>")
|
|
509
|
+
expect(result).to include("<http://example.org/item/b>")
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
it "includes member subgraph data" do
|
|
513
|
+
parent = LinkedOnlyParent.new(
|
|
514
|
+
pid: "g1",
|
|
515
|
+
items: [LinkedOnlyChild.new(cid: "a", label: "Alpha")],
|
|
516
|
+
)
|
|
517
|
+
result = parent.to_turtle
|
|
518
|
+
expect(result).to include("skos:prefLabel \"Alpha\"")
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
it "does not generate rdf:type for parent" do
|
|
522
|
+
parent = LinkedOnlyParent.new(
|
|
523
|
+
pid: "g1",
|
|
524
|
+
items: [LinkedOnlyChild.new(cid: "a", label: "Alpha")],
|
|
525
|
+
)
|
|
526
|
+
result = parent.to_turtle
|
|
527
|
+
parent_line = result.lines.find { |l| l.include?("<http://example.org/group/g1>") }
|
|
528
|
+
expect(parent_line).not_to include(" a ")
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
describe "heterogeneous member collection" do
|
|
533
|
+
before do
|
|
534
|
+
stub_const("HeteroChildA", Class.new(Lutaml::Model::Serializable) do
|
|
535
|
+
attribute :label, :string
|
|
536
|
+
|
|
537
|
+
turtle do
|
|
538
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
539
|
+
|
|
540
|
+
subject { |m| "http://example.org/a/#{m.label}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
541
|
+
|
|
542
|
+
type "skos:Concept"
|
|
543
|
+
|
|
544
|
+
predicate :prefLabel,
|
|
545
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
546
|
+
to: :label
|
|
547
|
+
end
|
|
548
|
+
end)
|
|
549
|
+
|
|
550
|
+
stub_const("HeteroChildB", Class.new(Lutaml::Model::Serializable) do
|
|
551
|
+
attribute :title, :string
|
|
552
|
+
|
|
553
|
+
turtle do
|
|
554
|
+
namespace Lutaml::Rdf::Namespaces::DctermsNamespace
|
|
555
|
+
|
|
556
|
+
subject { |m| "http://example.org/b/#{m.title}" } # rubocop:disable RSpec/NamedSubject, RSpec/MultipleSubjects
|
|
557
|
+
|
|
558
|
+
type "dcterms:Agent"
|
|
559
|
+
|
|
560
|
+
predicate :title,
|
|
561
|
+
namespace: Lutaml::Rdf::Namespaces::DctermsNamespace,
|
|
562
|
+
to: :title
|
|
563
|
+
end
|
|
564
|
+
end)
|
|
565
|
+
|
|
566
|
+
stub_const("HeteroParent", Class.new(Lutaml::Model::Serializable) do
|
|
567
|
+
attribute :name, :string
|
|
568
|
+
attribute :items, :string, collection: true
|
|
569
|
+
|
|
570
|
+
turtle do
|
|
571
|
+
namespace Lutaml::Rdf::Namespaces::SkosNamespace
|
|
572
|
+
|
|
573
|
+
subject { |m| "http://example.org/parent/#{m.name}" }
|
|
574
|
+
|
|
575
|
+
type "skos:Collection"
|
|
576
|
+
|
|
577
|
+
predicate :prefLabel,
|
|
578
|
+
namespace: Lutaml::Rdf::Namespaces::SkosNamespace,
|
|
579
|
+
to: :name
|
|
580
|
+
end
|
|
581
|
+
end)
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
it "includes prefixes from all member types" do
|
|
585
|
+
skip "Heterogeneous collection requires union-typed attribute (not yet supported)"
|
|
586
|
+
end
|
|
587
|
+
end
|
|
273
588
|
end
|
|
@@ -6,15 +6,10 @@ RSpec.describe Lutaml::Xml::DataModel do
|
|
|
6
6
|
describe Lutaml::Xml::DataModel::XmlElement do
|
|
7
7
|
let(:element_name) { "test-element" }
|
|
8
8
|
let(:namespace_class) do
|
|
9
|
-
Class.new
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def self.uri
|
|
15
|
-
"http://example.com/test"
|
|
16
|
-
end
|
|
17
|
-
end
|
|
9
|
+
ns = Class.new(Lutaml::Xml::Namespace)
|
|
10
|
+
ns.prefix_default "test"
|
|
11
|
+
ns.uri "http://example.com/test"
|
|
12
|
+
ns
|
|
18
13
|
end
|
|
19
14
|
|
|
20
15
|
describe "#initialize" do
|
|
@@ -146,11 +141,7 @@ RSpec.describe Lutaml::Xml::DataModel do
|
|
|
146
141
|
end
|
|
147
142
|
|
|
148
143
|
it "returns unprefixed when namespace has no prefix" do
|
|
149
|
-
ns_no_prefix = Class.new
|
|
150
|
-
def self.prefix_default
|
|
151
|
-
nil
|
|
152
|
-
end
|
|
153
|
-
end
|
|
144
|
+
ns_no_prefix = Class.new(Lutaml::Xml::Namespace)
|
|
154
145
|
element = described_class.new("element", ns_no_prefix)
|
|
155
146
|
|
|
156
147
|
expect(element.qualified_name).to eq("element")
|
|
@@ -208,15 +199,10 @@ RSpec.describe Lutaml::Xml::DataModel do
|
|
|
208
199
|
let(:attr_name) { "test-attr" }
|
|
209
200
|
let(:attr_value) { "test-value" }
|
|
210
201
|
let(:namespace_class) do
|
|
211
|
-
Class.new
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def self.uri
|
|
217
|
-
"http://example.com/test"
|
|
218
|
-
end
|
|
219
|
-
end
|
|
202
|
+
ns = Class.new(Lutaml::Xml::Namespace)
|
|
203
|
+
ns.prefix_default "test"
|
|
204
|
+
ns.uri "http://example.com/test"
|
|
205
|
+
ns
|
|
220
206
|
end
|
|
221
207
|
|
|
222
208
|
describe "#initialize" do
|
|
@@ -257,11 +243,7 @@ RSpec.describe Lutaml::Xml::DataModel do
|
|
|
257
243
|
end
|
|
258
244
|
|
|
259
245
|
it "returns unprefixed when namespace has no prefix" do
|
|
260
|
-
ns_no_prefix = Class.new
|
|
261
|
-
def self.prefix_default
|
|
262
|
-
nil
|
|
263
|
-
end
|
|
264
|
-
end
|
|
246
|
+
ns_no_prefix = Class.new(Lutaml::Xml::Namespace)
|
|
265
247
|
attribute = described_class.new(attr_name, attr_value, ns_no_prefix)
|
|
266
248
|
|
|
267
249
|
expect(attribute.qualified_name).to eq(attr_name)
|
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.
|
|
4
|
+
version: 0.8.11
|
|
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-05-
|
|
11
|
+
date: 2026-05-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -92,14 +92,14 @@ dependencies:
|
|
|
92
92
|
requirements:
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: 0.1.
|
|
95
|
+
version: 0.1.20
|
|
96
96
|
type: :runtime
|
|
97
97
|
prerelease: false
|
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - ">="
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: 0.1.
|
|
102
|
+
version: 0.1.20
|
|
103
103
|
- !ruby/object:Gem::Dependency
|
|
104
104
|
name: ostruct
|
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -532,6 +532,7 @@ files:
|
|
|
532
532
|
- lib/lutaml/model/serialize.rb
|
|
533
533
|
- lib/lutaml/model/serialize/attribute_definition.rb
|
|
534
534
|
- lib/lutaml/model/serialize/builder.rb
|
|
535
|
+
- lib/lutaml/model/serialize/deserialization_context.rb
|
|
535
536
|
- lib/lutaml/model/serialize/enum_handling.rb
|
|
536
537
|
- lib/lutaml/model/serialize/format_conversion.rb
|
|
537
538
|
- lib/lutaml/model/serialize/initialization.rb
|
|
@@ -1614,6 +1615,7 @@ files:
|
|
|
1614
1615
|
- spec/lutaml/model/multiple_mapping_spec.rb
|
|
1615
1616
|
- spec/lutaml/model/namespace_versioning_spec.rb
|
|
1616
1617
|
- spec/lutaml/model/one_entry_cache_spec.rb
|
|
1618
|
+
- spec/lutaml/model/optimization_spec.rb
|
|
1617
1619
|
- spec/lutaml/model/ordered_content_spec.rb
|
|
1618
1620
|
- spec/lutaml/model/polymorphic_spec.rb
|
|
1619
1621
|
- spec/lutaml/model/processing_instruction_spec.rb
|
|
@@ -1705,6 +1707,7 @@ files:
|
|
|
1705
1707
|
- spec/lutaml/rdf/graph_serialization_spec.rb
|
|
1706
1708
|
- spec/lutaml/rdf/iri_spec.rb
|
|
1707
1709
|
- spec/lutaml/rdf/literal_spec.rb
|
|
1710
|
+
- spec/lutaml/rdf/mapping_rule_spec.rb
|
|
1708
1711
|
- spec/lutaml/rdf/mapping_spec.rb
|
|
1709
1712
|
- spec/lutaml/rdf/member_rule_spec.rb
|
|
1710
1713
|
- spec/lutaml/rdf/namespace_set_spec.rb
|