lutaml-model 0.8.14 → 0.8.16
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/.rubocop_todo.yml +14 -73
- data/Gemfile +3 -5
- data/README.adoc +188 -25
- data/docs/_guides/xml-mapping.adoc +178 -24
- data/docs/_pages/importable_models.adoc +7 -1
- data/lib/lutaml/jsonld/transform.rb +44 -13
- data/lib/lutaml/model/attribute.rb +4 -0
- data/lib/lutaml/model/liquefiable.rb +9 -0
- data/lib/lutaml/model/liquid/indexed_access.rb +33 -0
- data/lib/lutaml/model/liquid.rb +1 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +2 -1
- data/lib/lutaml/rdf/mapping.rb +10 -1
- data/lib/lutaml/rdf/member_rule.rb +29 -4
- data/lib/lutaml/turtle/transform.rb +55 -35
- data/lib/lutaml/xml/adapter/plan_based_builder.rb +3 -1
- data/lib/lutaml/xml/adapter/xml_serializer.rb +15 -5
- data/lib/lutaml/xml/adapter.rb +2 -1
- data/lib/lutaml/xml/builder/base.rb +2 -1
- data/lib/lutaml/xml/data_model.rb +19 -3
- data/lib/lutaml/xml/mapping.rb +3 -1
- data/lib/lutaml/xml/mapping_rule.rb +28 -2
- data/lib/lutaml/xml/model_transform.rb +9 -1
- data/lib/lutaml/xml/serialization/instance_methods.rb +16 -9
- data/lib/lutaml/xml/transformation/element_builder.rb +1 -3
- data/lib/lutaml/xml/transformation/rule_applier.rb +21 -0
- data/lib/lutaml/xml/transformation/rule_compiler.rb +12 -3
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/jsonld/transform_spec.rb +149 -0
- data/spec/lutaml/model/liquid/indexed_access_spec.rb +135 -0
- data/spec/lutaml/model/mixed_content_spec.rb +48 -7
- data/spec/lutaml/model/raw_element_spec.rb +533 -0
- data/spec/lutaml/rdf/mapping_spec.rb +71 -6
- data/spec/lutaml/rdf/member_rule_spec.rb +103 -1
- data/spec/lutaml/turtle/transform_spec.rb +144 -0
- metadata +9 -6
|
@@ -1097,42 +1097,184 @@ end
|
|
|
1097
1097
|
====
|
|
1098
1098
|
|
|
1099
1099
|
|
|
1100
|
-
[[xml-map-all]]
|
|
1101
|
-
==== Mapping entire XML element into an attribute
|
|
1102
1100
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1101
|
+
[[xml-raw-element]]
|
|
1102
|
+
==== Capturing raw XML content
|
|
1105
1103
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1104
|
+
Lutaml::Model provides several mechanisms for capturing XML content as raw strings.
|
|
1105
|
+
Use these when you need to embed foreign XML vocabularies (SVG, MathML, XSL-FO) or
|
|
1106
|
+
preserve markup that your model doesn't need to parse.
|
|
1108
1107
|
|
|
1109
|
-
|
|
1108
|
+
===== `raw: :element` on `map_element` -- full element capture (recommended)
|
|
1110
1109
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1110
|
+
The `raw: :element` option captures a specific mapped element as a complete XML
|
|
1111
|
+
string, including its opening tag, attributes, children, and closing tag.
|
|
1112
|
+
This provides **lossless round-trip** fidelity -- the captured string is
|
|
1113
|
+
self-contained and serialized verbatim.
|
|
1114
1114
|
|
|
1115
|
-
|
|
1116
|
-
|
|
1115
|
+
.Syntax
|
|
1116
|
+
[source,ruby]
|
|
1117
|
+
----
|
|
1118
|
+
xml do
|
|
1119
|
+
map_element "svg", to: :svg_data, raw: :element
|
|
1120
|
+
end
|
|
1121
|
+
----
|
|
1117
1122
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1123
|
+
.Capturing an SVG element as raw XML
|
|
1124
|
+
[example]
|
|
1125
|
+
====
|
|
1126
|
+
[source,ruby]
|
|
1127
|
+
----
|
|
1128
|
+
class SvgContainer < Lutaml::Model::Serializable
|
|
1129
|
+
attribute :name, :string
|
|
1130
|
+
attribute :svg_data, :string
|
|
1120
1131
|
|
|
1121
|
-
|
|
1132
|
+
xml do
|
|
1133
|
+
element "container"
|
|
1134
|
+
map_element "name", to: :name
|
|
1135
|
+
map_element "svg", to: :svg_data, raw: :element
|
|
1136
|
+
end
|
|
1137
|
+
end
|
|
1138
|
+
----
|
|
1122
1139
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1140
|
+
[source,xml]
|
|
1141
|
+
----
|
|
1142
|
+
<container>
|
|
1143
|
+
<name>diagram</name>
|
|
1144
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
1145
|
+
<rect x="0" y="0" width="100" height="100" fill="red"/>
|
|
1146
|
+
</svg>
|
|
1147
|
+
</container>
|
|
1148
|
+
----
|
|
1126
1149
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1150
|
+
[source,ruby]
|
|
1151
|
+
----
|
|
1152
|
+
> doc = SvgContainer.from_xml(xml)
|
|
1153
|
+
> doc.name
|
|
1154
|
+
# => "diagram"
|
|
1155
|
+
> doc.svg_data
|
|
1156
|
+
# => "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <rect x=\"0\" y=\"0\" width=\"100\" height=\"100\" fill=\"red\"/>\n</svg>"
|
|
1157
|
+
----
|
|
1130
1158
|
|
|
1131
|
-
|
|
1132
|
-
the
|
|
1159
|
+
The captured string round-trips correctly -- serializing the model back to XML
|
|
1160
|
+
outputs the raw element verbatim without escaping or wrapping.
|
|
1133
1161
|
|
|
1134
|
-
|
|
1162
|
+
`raw: :element` also works with collection attributes:
|
|
1135
1163
|
|
|
1164
|
+
[source,ruby]
|
|
1165
|
+
----
|
|
1166
|
+
class MultiFragment < Lutaml::Model::Serializable
|
|
1167
|
+
attribute :fragments, :string, collection: true
|
|
1168
|
+
|
|
1169
|
+
xml do
|
|
1170
|
+
element "container"
|
|
1171
|
+
map_element "fragment", to: :fragments, raw: :element
|
|
1172
|
+
end
|
|
1173
|
+
end
|
|
1174
|
+
----
|
|
1175
|
+
====
|
|
1176
|
+
|
|
1177
|
+
===== `raw: :content` on `map_element` -- inner content capture
|
|
1178
|
+
|
|
1179
|
+
The `raw: :content` option captures only the *inner XML* of the matched element,
|
|
1180
|
+
without the wrapper tags. The wrapper element is reconstructed from the mapping rule
|
|
1181
|
+
during serialization.
|
|
1182
|
+
|
|
1183
|
+
.Syntax
|
|
1184
|
+
[source,ruby]
|
|
1185
|
+
----
|
|
1186
|
+
xml do
|
|
1187
|
+
map_element "street", to: :street, raw: :content
|
|
1188
|
+
end
|
|
1189
|
+
----
|
|
1190
|
+
|
|
1191
|
+
.Capturing inner XML content
|
|
1192
|
+
[example]
|
|
1193
|
+
====
|
|
1194
|
+
[source,ruby]
|
|
1195
|
+
----
|
|
1196
|
+
class Address < Lutaml::Model::Serializable
|
|
1197
|
+
attribute :street, :string
|
|
1198
|
+
|
|
1199
|
+
xml do
|
|
1200
|
+
element "address"
|
|
1201
|
+
map_element "street", to: :street, raw: :content
|
|
1202
|
+
end
|
|
1203
|
+
end
|
|
1204
|
+
----
|
|
1205
|
+
|
|
1206
|
+
[source,xml]
|
|
1207
|
+
----
|
|
1208
|
+
<address><street><b>123</b> Main St</street></address>
|
|
1209
|
+
----
|
|
1210
|
+
|
|
1211
|
+
[source,ruby]
|
|
1212
|
+
----
|
|
1213
|
+
> doc = Address.from_xml(xml)
|
|
1214
|
+
> doc.street
|
|
1215
|
+
# => "<b>123</b> Main St"
|
|
1216
|
+
----
|
|
1217
|
+
====
|
|
1218
|
+
|
|
1219
|
+
NOTE: `raw: :content` is **lossy** -- namespace prefixes declared on the wrapper
|
|
1220
|
+
element are not captured. For lossless capture, use `raw: :element` instead.
|
|
1221
|
+
|
|
1222
|
+
===== Namespace behavior
|
|
1223
|
+
|
|
1224
|
+
Both `raw: :element` and `raw: :content` match elements by local name regardless
|
|
1225
|
+
of namespace or prefix:
|
|
1226
|
+
|
|
1227
|
+
* Elements with no namespace (`<svg>`)
|
|
1228
|
+
* Elements with an `xmlns` declaration on themselves (`<svg xmlns="...">`)
|
|
1229
|
+
* Elements inheriting a default namespace from a parent
|
|
1230
|
+
* Elements with an explicit namespace prefix (`<ns:svg>`)
|
|
1231
|
+
|
|
1232
|
+
This is intentional -- raw capture is designed to handle foreign XML vocabularies
|
|
1233
|
+
without needing model classes for them.
|
|
1234
|
+
|
|
1235
|
+
===== Round-trip fidelity comparison
|
|
1236
|
+
|
|
1237
|
+
| Aspect | `raw: :element` | `raw: :content` |
|
|
1238
|
+
|--------|----------------|-----------------|
|
|
1239
|
+
| Element name | Preserved (in string) | Reconstructed from rule |
|
|
1240
|
+
| Element attributes | Preserved (in string) | Lost unless separately mapped |
|
|
1241
|
+
| Inner content | Preserved (in string) | Preserved (in string) |
|
|
1242
|
+
| Namespace prefixes | Safe (declared in string) | May break if declared on wrapper |
|
|
1243
|
+
| Fidelity | **Lossless** | **Lossy** |
|
|
1244
|
+
|
|
1245
|
+
===== Wrapper model pattern for attribute extraction
|
|
1246
|
+
|
|
1247
|
+
Raw capture stores the element as a string. If you need to inspect or modify
|
|
1248
|
+
attributes on the captured element, use a wrapper model with `map_all`:
|
|
1249
|
+
|
|
1250
|
+
[source,ruby]
|
|
1251
|
+
----
|
|
1252
|
+
class SvgInner < Lutaml::Model::Serializable
|
|
1253
|
+
attribute :raw, :string
|
|
1254
|
+
|
|
1255
|
+
xml do
|
|
1256
|
+
element "svg"
|
|
1257
|
+
map_all to: :raw
|
|
1258
|
+
end
|
|
1259
|
+
end
|
|
1260
|
+
|
|
1261
|
+
class Container < Lutaml::Model::Serializable
|
|
1262
|
+
attribute :svg_data, SvgInner
|
|
1263
|
+
|
|
1264
|
+
xml do
|
|
1265
|
+
element "container"
|
|
1266
|
+
map_element "svg", to: :svg_data
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1269
|
+
----
|
|
1270
|
+
|
|
1271
|
+
===== `map_all` -- root inner XML capture
|
|
1272
|
+
|
|
1273
|
+
The `map_all` directive captures the entire inner XML of the root element into
|
|
1274
|
+
a single attribute. It is **exclusive** -- it cannot be combined with other
|
|
1275
|
+
`map_element` or `map_content` mappings (only `map_attribute` is allowed).
|
|
1276
|
+
|
|
1277
|
+
.Syntax
|
|
1136
1278
|
[source,ruby]
|
|
1137
1279
|
----
|
|
1138
1280
|
xml do
|
|
@@ -1167,6 +1309,18 @@ end
|
|
|
1167
1309
|
----
|
|
1168
1310
|
====
|
|
1169
1311
|
|
|
1312
|
+
===== Deprecated: `attribute :x, :string, raw: true`
|
|
1313
|
+
|
|
1314
|
+
[WARNING]
|
|
1315
|
+
====
|
|
1316
|
+
`attribute :name, :string, raw: true` is deprecated. Use
|
|
1317
|
+
`map_element "name", to: :name, raw: :content` instead.
|
|
1318
|
+
====
|
|
1319
|
+
|
|
1320
|
+
This legacy syntax captures the inner XML of the matched element.
|
|
1321
|
+
It is equivalent to `raw: :content` on the mapping. Existing code continues
|
|
1322
|
+
to work but emits a deprecation warning.
|
|
1323
|
+
|
|
1170
1324
|
|
|
1171
1325
|
==== Mapping CDATA nodes
|
|
1172
1326
|
|
|
@@ -546,7 +546,13 @@ the current value is the same as the default value.
|
|
|
546
546
|
|
|
547
547
|
|
|
548
548
|
|
|
549
|
-
=== Attribute as raw string
|
|
549
|
+
=== Attribute as raw string (deprecated)
|
|
550
|
+
|
|
551
|
+
[WARNING]
|
|
552
|
+
====
|
|
553
|
+
`attribute :name, :string, raw: true` is deprecated.
|
|
554
|
+
Use `map_element "name", to: :name, raw: :content` instead.
|
|
555
|
+
====
|
|
550
556
|
|
|
551
557
|
An attribute can be set to read the value as raw string for XML, by using the `raw: true` option.
|
|
552
558
|
|
|
@@ -45,6 +45,12 @@ module Lutaml
|
|
|
45
45
|
build_instance(attrs, options)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
protected
|
|
49
|
+
|
|
50
|
+
def additional_resource_data(_instance, _mapping)
|
|
51
|
+
{}
|
|
52
|
+
end
|
|
53
|
+
|
|
48
54
|
private
|
|
49
55
|
|
|
50
56
|
def extract_mapping(options)
|
|
@@ -52,28 +58,32 @@ module Lutaml
|
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
def build_graph_document(mapping, instance)
|
|
55
|
-
context =
|
|
61
|
+
context = build_merged_context_recursive(mapping, instance)
|
|
62
|
+
graph = collect_resources(mapping, instance)
|
|
63
|
+
|
|
64
|
+
{ "@context" => context, "@graph" => graph }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def collect_resources(mapping, instance)
|
|
56
68
|
graph = []
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
graph << resource unless resource.empty?
|
|
61
|
-
end
|
|
70
|
+
resource = build_resource_data(mapping, instance)
|
|
71
|
+
graph << resource unless resource.empty?
|
|
62
72
|
|
|
63
73
|
mapping.rdf_members.each do |member_rule|
|
|
64
74
|
each_member(instance, member_rule) do |member|
|
|
65
75
|
member_mapping = member_mapping_for(member, :jsonld)
|
|
66
76
|
next unless member_mapping
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
graph
|
|
78
|
+
child_resources = collect_resources(member_mapping, member)
|
|
79
|
+
graph.concat(child_resources)
|
|
70
80
|
end
|
|
71
81
|
end
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
graph
|
|
74
84
|
end
|
|
75
85
|
|
|
76
|
-
def
|
|
86
|
+
def build_merged_context_recursive(mapping, instance)
|
|
77
87
|
context_hash = build_context_from_mapping(mapping).to_hash
|
|
78
88
|
|
|
79
89
|
mapping.rdf_members.each do |member_rule|
|
|
@@ -82,6 +92,9 @@ module Lutaml
|
|
|
82
92
|
next unless member_mapping
|
|
83
93
|
|
|
84
94
|
context_hash.merge!(build_context_from_mapping(member_mapping).to_hash)
|
|
95
|
+
|
|
96
|
+
child_ctx = build_merged_context_recursive(member_mapping, member)
|
|
97
|
+
context_hash.merge!(child_ctx)
|
|
85
98
|
end
|
|
86
99
|
end
|
|
87
100
|
|
|
@@ -100,9 +113,14 @@ module Lutaml
|
|
|
100
113
|
mapping.rdf_members.each do |member_rule|
|
|
101
114
|
next unless member_rule.linked?
|
|
102
115
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
predicate_uri = if member_rule.predicate_name
|
|
117
|
+
member_rule.linked_predicate_uri
|
|
118
|
+
end
|
|
119
|
+
if predicate_uri
|
|
120
|
+
context.term(member_rule.predicate_name.to_s,
|
|
121
|
+
id: predicate_uri,
|
|
122
|
+
type: "@id")
|
|
123
|
+
end
|
|
106
124
|
end
|
|
107
125
|
|
|
108
126
|
context
|
|
@@ -151,9 +169,12 @@ module Lutaml
|
|
|
151
169
|
member_refs = collect_member_references(instance, member_rule)
|
|
152
170
|
next if member_refs.empty?
|
|
153
171
|
|
|
154
|
-
|
|
172
|
+
key = jsonld_member_key(member_rule)
|
|
173
|
+
result[key] = member_refs
|
|
155
174
|
end
|
|
156
175
|
|
|
176
|
+
result.merge!(additional_resource_data(instance, mapping))
|
|
177
|
+
|
|
157
178
|
result
|
|
158
179
|
end
|
|
159
180
|
|
|
@@ -168,6 +189,16 @@ module Lutaml
|
|
|
168
189
|
refs
|
|
169
190
|
end
|
|
170
191
|
|
|
192
|
+
def jsonld_member_key(member_rule)
|
|
193
|
+
if member_rule.predicate_name
|
|
194
|
+
member_rule.predicate_name.to_s
|
|
195
|
+
elsif member_rule.link.is_a?(String)
|
|
196
|
+
member_rule.link.split(":").last
|
|
197
|
+
else
|
|
198
|
+
member_rule.attr_name.to_s
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
171
202
|
def serialize_value(value, rule)
|
|
172
203
|
case rule.kind
|
|
173
204
|
when :uri_reference then serialize_uri_reference(value)
|
|
@@ -665,6 +665,10 @@ instance_object = nil)
|
|
|
665
665
|
def process_options!
|
|
666
666
|
validate_options!(@options)
|
|
667
667
|
@raw = !!@options[:raw]
|
|
668
|
+
if @raw
|
|
669
|
+
warn "[DEPRECATED] attribute :#{name}, :string, raw: true is deprecated. " \
|
|
670
|
+
"Use map_element \"name\", to: :#{name}, raw: :content instead."
|
|
671
|
+
end
|
|
668
672
|
@validations = @options[:validations]
|
|
669
673
|
set_default_for_collection if collection?
|
|
670
674
|
end
|
|
@@ -54,6 +54,15 @@ module Lutaml
|
|
|
54
54
|
value.to_liquid
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
|
+
|
|
58
|
+
def liquid_method_missing(method)
|
|
59
|
+
if @object.is_a?(::Lutaml::Model::Liquid::IndexedAccess)
|
|
60
|
+
result = @object.liquid_fetch(method)
|
|
61
|
+
result.nil? ? super : liquefy_value(result)
|
|
62
|
+
else
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
end
|
|
57
66
|
end)
|
|
58
67
|
end
|
|
59
68
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Model
|
|
5
|
+
module Liquid
|
|
6
|
+
# Module for Lutaml::Model objects that support bracket-based
|
|
7
|
+
# lookup in Liquid templates (e.g., collections with index or key access).
|
|
8
|
+
#
|
|
9
|
+
# Include this module in any Serializable subclass that supports
|
|
10
|
+
# +self[key]+ so that its auto-generated Liquid drop can delegate
|
|
11
|
+
# +drop[key]+ through to the underlying object.
|
|
12
|
+
#
|
|
13
|
+
# Example:
|
|
14
|
+
# class Glossarist::Collections::LocalizationCollection
|
|
15
|
+
# include Lutaml::Model::Liquid::IndexedAccess
|
|
16
|
+
# # ...
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# drop['eng'] #=> calls drop.liquid_method_missing('eng')
|
|
20
|
+
# #=> calls @object.liquid_fetch('eng')
|
|
21
|
+
# #=> calls @object['eng']
|
|
22
|
+
# #=> returns the localized concept drop
|
|
23
|
+
module IndexedAccess
|
|
24
|
+
# Called by the auto-generated Liquid drop via +liquid_method_missing+.
|
|
25
|
+
# Delegates to +self[key]+ by default. Override in specific classes
|
|
26
|
+
# for custom lookup behavior.
|
|
27
|
+
def liquid_fetch(key)
|
|
28
|
+
self[key]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/lutaml/model/liquid.rb
CHANGED
data/lib/lutaml/model/version.rb
CHANGED
data/lib/lutaml/model.rb
CHANGED
|
@@ -149,7 +149,8 @@ module Lutaml
|
|
|
149
149
|
"#{__dir__}/model/error/incorrect_sequence_error"
|
|
150
150
|
autoload :ChoiceUpperBoundError,
|
|
151
151
|
"#{__dir__}/model/error/choice_upper_bound_error"
|
|
152
|
-
autoload :TypeOnlyMappingError,
|
|
152
|
+
autoload :TypeOnlyMappingError,
|
|
153
|
+
"#{__dir__}/model/error/type_only_mapping_error"
|
|
153
154
|
autoload :NoRootMappingError, "#{__dir__}/model/error/no_root_mapping_error"
|
|
154
155
|
autoload :ImportModelWithRootError,
|
|
155
156
|
"#{__dir__}/model/error/import_model_with_root_error"
|
data/lib/lutaml/rdf/mapping.rb
CHANGED
|
@@ -27,6 +27,14 @@ module Lutaml
|
|
|
27
27
|
@rdf_type = Array(value)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def types(*values)
|
|
31
|
+
@rdf_type = values.flatten
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def has_types_or_predicates?
|
|
35
|
+
@rdf_type.any? || @rdf_predicates.any?
|
|
36
|
+
end
|
|
37
|
+
|
|
30
38
|
def predicate(name, namespace:, to:, lang_tagged: false,
|
|
31
39
|
uri_reference: false)
|
|
32
40
|
@rdf_predicates << Lutaml::Rdf::MappingRule.new(
|
|
@@ -38,11 +46,12 @@ module Lutaml
|
|
|
38
46
|
)
|
|
39
47
|
end
|
|
40
48
|
|
|
41
|
-
def members(attr_name, predicate_name: nil, namespace: nil)
|
|
49
|
+
def members(attr_name, predicate_name: nil, namespace: nil, link: nil)
|
|
42
50
|
@rdf_members << Lutaml::Rdf::MemberRule.new(
|
|
43
51
|
attr_name,
|
|
44
52
|
predicate_name: predicate_name,
|
|
45
53
|
namespace: namespace,
|
|
54
|
+
link: link,
|
|
46
55
|
)
|
|
47
56
|
end
|
|
48
57
|
|
|
@@ -3,28 +3,53 @@
|
|
|
3
3
|
module Lutaml
|
|
4
4
|
module Rdf
|
|
5
5
|
class MemberRule
|
|
6
|
-
attr_reader :attr_name, :predicate_name, :namespace
|
|
6
|
+
attr_reader :attr_name, :predicate_name, :namespace, :link
|
|
7
7
|
|
|
8
|
-
def initialize(attr_name, predicate_name: nil, namespace: nil)
|
|
8
|
+
def initialize(attr_name, predicate_name: nil, namespace: nil, link: nil)
|
|
9
9
|
if predicate_name && !namespace
|
|
10
10
|
raise ArgumentError,
|
|
11
11
|
"namespace is required when predicate_name is provided"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
if predicate_name && link
|
|
15
|
+
raise ArgumentError,
|
|
16
|
+
"predicate_name and link are mutually exclusive"
|
|
17
|
+
end
|
|
18
|
+
|
|
14
19
|
@attr_name = attr_name.to_sym
|
|
15
20
|
@predicate_name = predicate_name
|
|
16
21
|
@namespace = namespace
|
|
22
|
+
@link = link
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
def linked?
|
|
20
|
-
|
|
26
|
+
!!(@predicate_name || @link)
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
def linked_predicate_uri
|
|
24
|
-
return nil unless
|
|
30
|
+
return nil unless @predicate_name
|
|
25
31
|
|
|
26
32
|
@namespace[@predicate_name]
|
|
27
33
|
end
|
|
34
|
+
|
|
35
|
+
def link_predicate_for(member, resolver)
|
|
36
|
+
return nil unless @link
|
|
37
|
+
|
|
38
|
+
case @link
|
|
39
|
+
when String
|
|
40
|
+
resolver.call(@link)
|
|
41
|
+
when Proc
|
|
42
|
+
resolver.call(@link.call(member))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def resolve_link_uri(member, resolver)
|
|
47
|
+
if @predicate_name
|
|
48
|
+
linked_predicate_uri
|
|
49
|
+
elsif @link
|
|
50
|
+
link_predicate_for(member, resolver)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
28
53
|
end
|
|
29
54
|
end
|
|
30
55
|
end
|