lutaml-model 0.8.10 → 0.8.12
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/.github/workflows/opal.yml +31 -0
- data/.rspec-opal +5 -0
- data/.rubocop_todo.yml +68 -7
- data/README.adoc +53 -1
- data/docs/_guides/index.adoc +4 -0
- data/docs/_guides/jsonld-serialization.adoc +3 -1
- data/docs/_guides/opal.adoc +221 -0
- data/docs/_guides/rdf-serialization.adoc +94 -8
- data/docs/_guides/turtle-serialization.adoc +17 -4
- data/docs/_guides/xml_mappings/07_best_practices.adoc +2 -1
- data/docs/_pages/configuration.adoc +9 -4
- data/docs/_pages/index.adoc +1 -0
- data/docs/_pages/serialization_adapters.adoc +3 -2
- data/docs/index.adoc +1 -0
- data/lib/lutaml/hash_format/adapter/mapping.rb +2 -4
- data/lib/lutaml/json/adapter/mapping.rb +2 -4
- data/lib/lutaml/jsonl/adapter/mapping.rb +2 -4
- data/lib/lutaml/jsonld/transform.rb +70 -24
- data/lib/lutaml/key_value/adapter/hash/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/json/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/jsonl/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/toml/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/yaml/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/yamls/mapping.rb +2 -4
- data/lib/lutaml/key_value/mapping.rb +4 -4
- data/lib/lutaml/model/adapter_resolver.rb +5 -8
- data/lib/lutaml/model/mapping/mapping.rb +12 -0
- data/lib/lutaml/model/store.rb +51 -4
- 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/toml/adapter/mapping.rb +2 -4
- data/lib/lutaml/turtle/transform.rb +125 -53
- data/lib/lutaml/xml/schema/xsd.rb +5 -4
- data/lib/lutaml/xml/schema.rb +8 -5
- data/lib/lutaml/xml/xml_orderable.rb +17 -0
- data/lib/lutaml/xml.rb +8 -11
- data/lib/lutaml/yaml/adapter/mapping.rb +2 -4
- data/lib/lutaml/yamls/adapter/mapping.rb +7 -3
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/jsonld/transform_spec.rb +239 -0
- data/spec/lutaml/model/opal_smoke_spec.rb +117 -0
- data/spec/lutaml/model/store_spec.rb +156 -2
- 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/opal_xml_spec.rb +145 -0
- data/spec/lutaml/xml/xml_spec.rb +64 -13
- data/spec/support/opal.rb +6 -0
- metadata +12 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 86f1ceda6e01c3d47e303f092a24062a0e051faaf8a859bb47d2723670088d61
|
|
4
|
+
data.tar.gz: 92b43d69b6d49244c4187859e497fdbbce11d913f11425e8dfdfed1493f97e5a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 33612bb8b97cc3667b88a5638288155319281de507ca4b8ca6386ea6b9e3a79b747d3b0441d92b4083441310a7962449c511e1445eb3779c2bf872b1d2ebdca1
|
|
7
|
+
data.tar.gz: f51445ae0f41d649c89e21c43f8cb1738a146457a35fac1fb5f927f6829d7360e3d115b55d7ea24549c33b03074c381028b89fc10e4ef87e703cd7f51bd36bf6
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: opal
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
submodules: "recursive"
|
|
15
|
+
|
|
16
|
+
- name: Set up Ruby
|
|
17
|
+
uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: "3.3"
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Set up Node.js
|
|
23
|
+
uses: actions/setup-node@v4
|
|
24
|
+
with:
|
|
25
|
+
node-version: "18"
|
|
26
|
+
|
|
27
|
+
- name: Run Opal tests
|
|
28
|
+
# TODO: Remove continue-on-error once REXML adapter is verified
|
|
29
|
+
# under actual Opal runtime (REXML compiles to JS via Opal's stdlib)
|
|
30
|
+
run: bundle exec rake spec:opal
|
|
31
|
+
continue-on-error: true
|
data/.rspec-opal
ADDED
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2026-05-
|
|
3
|
+
# on 2026-05-27 05:02:01 UTC using RuboCop version 1.86.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -11,13 +11,60 @@ Gemspec/RequiredRubyVersion:
|
|
|
11
11
|
Exclude:
|
|
12
12
|
- 'lutaml-model.gemspec'
|
|
13
13
|
|
|
14
|
-
# Offense count:
|
|
14
|
+
# Offense count: 2
|
|
15
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
16
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
17
|
+
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
18
|
+
Layout/ArgumentAlignment:
|
|
19
|
+
Exclude:
|
|
20
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
|
21
|
+
|
|
22
|
+
# Offense count: 1
|
|
23
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
24
|
+
Layout/ClosingParenthesisIndentation:
|
|
25
|
+
Exclude:
|
|
26
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
|
27
|
+
|
|
28
|
+
# Offense count: 1
|
|
29
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
30
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
31
|
+
# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses
|
|
32
|
+
Layout/FirstArgumentIndentation:
|
|
33
|
+
Exclude:
|
|
34
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
|
35
|
+
|
|
36
|
+
# Offense count: 2
|
|
37
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
38
|
+
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
|
39
|
+
# SupportedHashRocketStyles: key, separator, table
|
|
40
|
+
# SupportedColonStyles: key, separator, table
|
|
41
|
+
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
|
42
|
+
Layout/HashAlignment:
|
|
43
|
+
Exclude:
|
|
44
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
|
45
|
+
|
|
46
|
+
# Offense count: 3022
|
|
15
47
|
# This cop supports safe autocorrection (--autocorrect).
|
|
16
48
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
17
49
|
# URISchemes: http, https
|
|
18
50
|
Layout/LineLength:
|
|
19
51
|
Enabled: false
|
|
20
52
|
|
|
53
|
+
# Offense count: 1
|
|
54
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
55
|
+
# Configuration parameters: EnforcedStyle.
|
|
56
|
+
# SupportedStyles: symmetrical, new_line, same_line
|
|
57
|
+
Layout/MultilineMethodCallBraceLayout:
|
|
58
|
+
Exclude:
|
|
59
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
|
60
|
+
|
|
61
|
+
# Offense count: 1
|
|
62
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
63
|
+
# Configuration parameters: AllowInHeredoc.
|
|
64
|
+
Layout/TrailingWhitespace:
|
|
65
|
+
Exclude:
|
|
66
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
|
67
|
+
|
|
21
68
|
# Offense count: 21
|
|
22
69
|
# Configuration parameters: AllowedMethods.
|
|
23
70
|
# AllowedMethods: enums
|
|
@@ -109,6 +156,12 @@ Lint/UnusedMethodArgument:
|
|
|
109
156
|
- 'lib/lutaml/xml/unqualified_inheritance_strategy.rb'
|
|
110
157
|
- 'spec/support/xml/xsd/code_example_validator.rb'
|
|
111
158
|
|
|
159
|
+
# Offense count: 1
|
|
160
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
161
|
+
Lint/UselessAssignment:
|
|
162
|
+
Exclude:
|
|
163
|
+
- 'spec/lutaml/xml/opal_xml_spec.rb'
|
|
164
|
+
|
|
112
165
|
# Offense count: 1
|
|
113
166
|
Lint/UselessConstantScoping:
|
|
114
167
|
Exclude:
|
|
@@ -130,12 +183,12 @@ Metrics/BlockLength:
|
|
|
130
183
|
Metrics/BlockNesting:
|
|
131
184
|
Max: 6
|
|
132
185
|
|
|
133
|
-
# Offense count:
|
|
186
|
+
# Offense count: 308
|
|
134
187
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
135
188
|
Metrics/CyclomaticComplexity:
|
|
136
189
|
Enabled: false
|
|
137
190
|
|
|
138
|
-
# Offense count:
|
|
191
|
+
# Offense count: 553
|
|
139
192
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
140
193
|
Metrics/MethodLength:
|
|
141
194
|
Max: 514
|
|
@@ -238,7 +291,7 @@ RSpec/BeforeAfterAll:
|
|
|
238
291
|
RSpec/ContextWording:
|
|
239
292
|
Enabled: false
|
|
240
293
|
|
|
241
|
-
# Offense count:
|
|
294
|
+
# Offense count: 98
|
|
242
295
|
# Configuration parameters: IgnoredMetadata.
|
|
243
296
|
RSpec/DescribeClass:
|
|
244
297
|
Enabled: false
|
|
@@ -249,7 +302,7 @@ RSpec/DescribeMethod:
|
|
|
249
302
|
- 'spec/lutaml/xml/schema/xsd/schema_helper_methods_spec.rb'
|
|
250
303
|
- 'spec/lutaml/xml/serializable_namespace_spec.rb'
|
|
251
304
|
|
|
252
|
-
# Offense count:
|
|
305
|
+
# Offense count: 1301
|
|
253
306
|
# Configuration parameters: CountAsOne.
|
|
254
307
|
RSpec/ExampleLength:
|
|
255
308
|
Max: 68
|
|
@@ -324,7 +377,7 @@ RSpec/MultipleDescribes:
|
|
|
324
377
|
- 'spec/lutaml/xml/namespace_resolution_strategy_spec.rb'
|
|
325
378
|
- 'spec/lutaml/xml/xml_space_type_spec.rb'
|
|
326
379
|
|
|
327
|
-
# Offense count:
|
|
380
|
+
# Offense count: 1515
|
|
328
381
|
RSpec/MultipleExpectations:
|
|
329
382
|
Max: 21
|
|
330
383
|
|
|
@@ -490,3 +543,11 @@ Style/StringConcatenation:
|
|
|
490
543
|
- 'lib/lutaml/model/schema/xml_compiler/complex_type.rb'
|
|
491
544
|
- 'lib/lutaml/model/schema/xml_compiler/simple_type.rb'
|
|
492
545
|
- 'lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb'
|
|
546
|
+
|
|
547
|
+
# Offense count: 1
|
|
548
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
549
|
+
# Configuration parameters: EnforcedStyleForMultiline.
|
|
550
|
+
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
|
|
551
|
+
Style/TrailingCommaInArguments:
|
|
552
|
+
Exclude:
|
|
553
|
+
- 'spec/lutaml/model/opal_smoke_spec.rb'
|
data/README.adoc
CHANGED
|
@@ -5507,6 +5507,49 @@ vocab.to_turtle
|
|
|
5507
5507
|
vocab.to_jsonld
|
|
5508
5508
|
----
|
|
5509
5509
|
|
|
5510
|
+
==== Multiple RDF types
|
|
5511
|
+
|
|
5512
|
+
`type` accepts a single compact IRI or an array:
|
|
5513
|
+
|
|
5514
|
+
[source,ruby]
|
|
5515
|
+
----
|
|
5516
|
+
type "skos:Concept"
|
|
5517
|
+
# or multiple types:
|
|
5518
|
+
type ["skos:Concept", "dcterms:Agent"]
|
|
5519
|
+
----
|
|
5520
|
+
|
|
5521
|
+
In Turtle, each type produces a separate `a` triple. In JSON-LD, a single type
|
|
5522
|
+
produces `"@type": "skos:Concept"` while multiple types produce an array.
|
|
5523
|
+
|
|
5524
|
+
==== URI reference predicates
|
|
5525
|
+
|
|
5526
|
+
Predicates declared with `uri_reference: true` serialize values as URI objects
|
|
5527
|
+
rather than string literals. In Turtle, values are emitted without quotes. In
|
|
5528
|
+
JSON-LD, values are wrapped in `{"@id": ...}` and the context term includes
|
|
5529
|
+
`"@type": "@id"`.
|
|
5530
|
+
|
|
5531
|
+
[source,ruby]
|
|
5532
|
+
----
|
|
5533
|
+
predicate :related, namespace: SkosNamespace, to: :related, uri_reference: true
|
|
5534
|
+
----
|
|
5535
|
+
|
|
5536
|
+
Round-trip fidelity is preserved: compact IRI forms (e.g. `"skos:other"`) are
|
|
5537
|
+
maintained through serialization and deserialization.
|
|
5538
|
+
|
|
5539
|
+
==== Linked member predicates
|
|
5540
|
+
|
|
5541
|
+
`members` accepts `predicate_name:` and `namespace:` to generate linking triples
|
|
5542
|
+
from the container to each member:
|
|
5543
|
+
|
|
5544
|
+
[source,ruby]
|
|
5545
|
+
----
|
|
5546
|
+
members :children, predicate_name: :member, namespace: SkosNamespace
|
|
5547
|
+
----
|
|
5548
|
+
|
|
5549
|
+
In Turtle, this produces `skos:member <child-uri>` triples on the container
|
|
5550
|
+
subject. In JSON-LD, the context includes a `member` term with `"@type": "@id"`
|
|
5551
|
+
and the container resource contains `{"@id": "..."}` references.
|
|
5552
|
+
|
|
5510
5553
|
See link:docs/_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
|
|
5511
5554
|
complete guide including language-tagged values, graph serialization, and
|
|
5512
5555
|
architecture details.
|
|
@@ -16152,11 +16195,20 @@ Lutaml::Model supports running under https://opalrb.com[Opal] (Ruby compiled to
|
|
|
16152
16195
|
JavaScript). The runtime is detected automatically and adapter selection is
|
|
16153
16196
|
adjusted accordingly.
|
|
16154
16197
|
|
|
16155
|
-
* *XML*: Only the `:
|
|
16198
|
+
* *XML*: Only the `:rexml` adapter is available (auto-selected). Opal
|
|
16199
|
+
reimplements `strscan` and `stringio` in its stdlib, enabling REXML (pure
|
|
16200
|
+
Ruby) to compile cleanly to JavaScript.
|
|
16156
16201
|
* *JSON*: Only the `:standard` adapter is available.
|
|
16202
|
+
* *YAML*: Only the `:standard` adapter is available.
|
|
16157
16203
|
* *TOML*: Not available on Opal.
|
|
16158
16204
|
* *Schema generation*: `to_xsd`, `to_relaxng`, and `from_xml` (XML schema
|
|
16159
16205
|
compilation) raise `NotImplementedError` on Opal.
|
|
16206
|
+
* *Liquid templating*: Not available on Opal (Phase 1 — skipped).
|
|
16207
|
+
* *XPath*: Not available on Opal (REXML XPath requires features not yet
|
|
16208
|
+
supported by Opal's stdlib).
|
|
16209
|
+
|
|
16210
|
+
See the link:docs/_guides/opal.adoc[Opal Usage Guide] for setup instructions
|
|
16211
|
+
and limitations.
|
|
16160
16212
|
|
|
16161
16213
|
|
|
16162
16214
|
=== Error handling
|
data/docs/_guides/index.adoc
CHANGED
|
@@ -46,6 +46,10 @@ See link:../references/rdf-namespaces[RDF Namespaces] for namespace classes,
|
|
|
46
46
|
* link:../consolidation-mapping[Consolidation Mapping] - Group sibling elements into structured models
|
|
47
47
|
* link:../document-validation[Document Validation] - Document-level validation with rules, profiles, and remediation
|
|
48
48
|
|
|
49
|
+
== Runtime environments
|
|
50
|
+
|
|
51
|
+
* link:../opal[Opal Usage Guide] - Run Lutaml::Model in the browser via Opal (Ruby to JavaScript)
|
|
52
|
+
|
|
49
53
|
== By task
|
|
50
54
|
|
|
51
55
|
=== I want to serialize to XML
|
|
@@ -160,7 +160,9 @@ end
|
|
|
160
160
|
----
|
|
161
161
|
|
|
162
162
|
See link:../_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
|
|
163
|
-
complete guide including graph-level serialization with `members
|
|
163
|
+
complete guide including graph-level serialization with `members`, URI reference
|
|
164
|
+
predicates (`uri_reference: true`), multiple RDF types, and linking predicates
|
|
165
|
+
on member collections.
|
|
164
166
|
|
|
165
167
|
== Multi-format models
|
|
166
168
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Opal Usage Guide
|
|
3
|
+
parent: Guides
|
|
4
|
+
nav_order: 99
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
= Opal Usage Guide
|
|
8
|
+
|
|
9
|
+
:toc:
|
|
10
|
+
:toclevels: 3
|
|
11
|
+
|
|
12
|
+
== Overview
|
|
13
|
+
|
|
14
|
+
https://opalrb.com[Opal] is a Ruby-to-JavaScript compiler that allows Ruby code to run in the browser. Lutaml::Model supports running under Opal, enabling XML/JSON/YAML serialization in client-side applications.
|
|
15
|
+
|
|
16
|
+
The XML parsing layer is provided by the https://github.com/lutaml/moxml[moxml] gem (v0.2+), which detects the Opal runtime and uses the REXML adapter automatically.
|
|
17
|
+
|
|
18
|
+
== How it works
|
|
19
|
+
|
|
20
|
+
Opal compiles Ruby source code to JavaScript. Under Opal:
|
|
21
|
+
|
|
22
|
+
. `RUBY_ENGINE` equals `"opal"`
|
|
23
|
+
. `Lutaml::Model::RuntimeCompatibility.opal?` returns `true`
|
|
24
|
+
. Adapter selection is adjusted automatically
|
|
25
|
+
. Gems with C extensions (Nokogiri, Ox, Oj, tomlib) are not available
|
|
26
|
+
. REXML is used for XML because Opal reimplements `strscan` and `stringio` in its stdlib, enabling REXML (pure Ruby) to compile cleanly to JavaScript
|
|
27
|
+
|
|
28
|
+
No configuration is needed -- the runtime is detected and adapters are selected automatically.
|
|
29
|
+
|
|
30
|
+
== Supported features
|
|
31
|
+
|
|
32
|
+
=== Fully supported
|
|
33
|
+
|
|
34
|
+
* Model definition with `attribute`, types, collections, defaults
|
|
35
|
+
* XML serialization/deserialization (via REXML adapter)
|
|
36
|
+
** Element and attribute mapping
|
|
37
|
+
** Nested elements and collections
|
|
38
|
+
** Mixed content
|
|
39
|
+
** Namespaces (parsing and serialization)
|
|
40
|
+
** CDATA sections
|
|
41
|
+
** Processing instructions
|
|
42
|
+
** Comments
|
|
43
|
+
** Entity references
|
|
44
|
+
** XML declarations and doctypes
|
|
45
|
+
* JSON serialization/deserialization (via standard adapter)
|
|
46
|
+
* YAML serialization/deserialization (via standard adapter)
|
|
47
|
+
* Hash transformation
|
|
48
|
+
* Custom types
|
|
49
|
+
* Model import (`import_model`)
|
|
50
|
+
* Value transformations
|
|
51
|
+
|
|
52
|
+
=== Not available
|
|
53
|
+
|
|
54
|
+
[cols="1,3",options="header"]
|
|
55
|
+
|===
|
|
56
|
+
| Feature | Reason
|
|
57
|
+
| XSD schema generation | Requires Nokogiri
|
|
58
|
+
| RELAX NG generation | Requires Nokogiri
|
|
59
|
+
| XML schema compilation | Requires Nokogiri + native parsing
|
|
60
|
+
| TOML serialization | Both tomlib and toml-rb require native extensions
|
|
61
|
+
| Oj / MultiJson adapters | Require native extensions
|
|
62
|
+
| Ox / Nokogiri / Oga adapters | Require native extensions or C extensions
|
|
63
|
+
| XPath queries | REXML XPath requires features not yet in Opal's stdlib
|
|
64
|
+
| Liquid templating | Uses file-system-based template loading (Phase 1: skipped)
|
|
65
|
+
| Canon XML equivalence | Uses Nokogiri for XML parsing
|
|
66
|
+
|===
|
|
67
|
+
|
|
68
|
+
== Setup
|
|
69
|
+
|
|
70
|
+
=== Gemfile
|
|
71
|
+
|
|
72
|
+
Add Opal gems to your Gemfile:
|
|
73
|
+
|
|
74
|
+
[source,ruby]
|
|
75
|
+
----
|
|
76
|
+
gem "lutaml-model"
|
|
77
|
+
|
|
78
|
+
group :opal do
|
|
79
|
+
gem "opal", "~> 1.8"
|
|
80
|
+
gem "opal-rspec", "~> 1.0"
|
|
81
|
+
gem "opal-sprockets"
|
|
82
|
+
end
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
=== Rake task
|
|
86
|
+
|
|
87
|
+
Add an Opal RSpec task to your Rakefile:
|
|
88
|
+
|
|
89
|
+
[source,ruby]
|
|
90
|
+
----
|
|
91
|
+
begin
|
|
92
|
+
require "opal/rspec/rake_task"
|
|
93
|
+
rescue LoadError
|
|
94
|
+
# Opal not available
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
namespace :spec do
|
|
98
|
+
if defined?(Opal::RSpec::RakeTask)
|
|
99
|
+
desc "Run Opal (JavaScript) tests"
|
|
100
|
+
Opal::RSpec::RakeTask.new(:opal) do |server, runner|
|
|
101
|
+
server.append_path "lib"
|
|
102
|
+
runner.default_path = "spec"
|
|
103
|
+
runner.pattern = "spec/**/*_spec.{rb,opal}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
----
|
|
108
|
+
|
|
109
|
+
=== Test configuration
|
|
110
|
+
|
|
111
|
+
Create `spec/support/opal.rb` for Opal-specific test patches:
|
|
112
|
+
|
|
113
|
+
[source,ruby]
|
|
114
|
+
----
|
|
115
|
+
# frozen_string_literal: true
|
|
116
|
+
|
|
117
|
+
if RUBY_ENGINE == "opal"
|
|
118
|
+
Lutaml::Model::Config.xml_adapter_type = :rexml
|
|
119
|
+
end
|
|
120
|
+
----
|
|
121
|
+
|
|
122
|
+
Create `.rspec-opal`:
|
|
123
|
+
|
|
124
|
+
----
|
|
125
|
+
--default-path=spec
|
|
126
|
+
--pattern='spec/**/*_spec.{rb,opal}'
|
|
127
|
+
-I lib
|
|
128
|
+
--opal-opt=-g,lutaml-model
|
|
129
|
+
-I spec
|
|
130
|
+
--require=spec_helper
|
|
131
|
+
--require=support/opal
|
|
132
|
+
----
|
|
133
|
+
|
|
134
|
+
=== CI workflow
|
|
135
|
+
|
|
136
|
+
Add `.github/workflows/opal.yml`:
|
|
137
|
+
|
|
138
|
+
[source,yaml]
|
|
139
|
+
----
|
|
140
|
+
name: opal
|
|
141
|
+
on:
|
|
142
|
+
push:
|
|
143
|
+
branches: [main]
|
|
144
|
+
pull_request:
|
|
145
|
+
|
|
146
|
+
jobs:
|
|
147
|
+
test:
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
steps:
|
|
150
|
+
- uses: actions/checkout@v4
|
|
151
|
+
with:
|
|
152
|
+
submodules: "recursive"
|
|
153
|
+
- uses: ruby/setup-ruby@v1
|
|
154
|
+
with:
|
|
155
|
+
ruby-version: "3.3"
|
|
156
|
+
bundler-cache: true
|
|
157
|
+
- uses: actions/setup-node@v4
|
|
158
|
+
with:
|
|
159
|
+
node-version: "18"
|
|
160
|
+
- name: Run Opal tests
|
|
161
|
+
run: bundle exec rake spec:opal
|
|
162
|
+
----
|
|
163
|
+
|
|
164
|
+
== Example: XML round-trip in the browser
|
|
165
|
+
|
|
166
|
+
[source,ruby]
|
|
167
|
+
----
|
|
168
|
+
class Person
|
|
169
|
+
include Lutaml::Model::Serialize
|
|
170
|
+
|
|
171
|
+
attribute :name, :string
|
|
172
|
+
attribute :age, :integer
|
|
173
|
+
|
|
174
|
+
xml do
|
|
175
|
+
root "person"
|
|
176
|
+
map_element "name", to: :name
|
|
177
|
+
map_element "age", to: :age
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Parse XML
|
|
182
|
+
person = Person.from_xml('<person><name>Alice</name><age>30</age></person>')
|
|
183
|
+
person.name # => "Alice"
|
|
184
|
+
person.age # => 30
|
|
185
|
+
|
|
186
|
+
# Serialize to XML
|
|
187
|
+
person.to_xml # => "<person><name>Alice</name><age>30</age></person>"
|
|
188
|
+
|
|
189
|
+
# JSON and YAML also work
|
|
190
|
+
person.to_json # => '{"name":"Alice","age":30}'
|
|
191
|
+
person.to_yaml # => "---\nname: Alice\nage: 30\n"
|
|
192
|
+
----
|
|
193
|
+
|
|
194
|
+
== Architecture
|
|
195
|
+
|
|
196
|
+
Under Opal, the library uses a JRuby-like pattern for dependency management:
|
|
197
|
+
|
|
198
|
+
. *Dependencies stay in the gemspec.* Gems like Nokogiri, Ox, and Oga remain listed as dependencies -- they are simply not loadable under Opal.
|
|
199
|
+
. *Requires are guarded with `RUBY_ENGINE`.* Code that depends on native gems uses `RUBY_ENGINE == "opal"` checks instead of silent `rescue LoadError`.
|
|
200
|
+
. *No gem splitting.* The same gem works on both MRI and Opal.
|
|
201
|
+
|
|
202
|
+
Key components:
|
|
203
|
+
* `Lutaml::Model::RuntimeCompatibility` -- detects the runtime (opal, windows, native)
|
|
204
|
+
* `Lutaml::Model::AdapterResolver` -- selects adapters based on runtime capabilities
|
|
205
|
+
* `Moxml::Config::OPAL_DEFAULT_ADAPTER` -- set to `:rexml`
|
|
206
|
+
* `Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS` -- set to `%i[rexml]`
|
|
207
|
+
|
|
208
|
+
== Dependencies
|
|
209
|
+
|
|
210
|
+
The following gems in the lutaml ecosystem support Opal:
|
|
211
|
+
|
|
212
|
+
* **lutaml-model** -- Core model library (this gem)
|
|
213
|
+
* **moxml** (v0.2+) -- XML parsing abstraction with REXML adapter for Opal
|
|
214
|
+
* **canon** -- XML comparison (uses moxml under Opal; comparison features work but Nokogiri-specific features do not)
|
|
215
|
+
|
|
216
|
+
== Limitations
|
|
217
|
+
|
|
218
|
+
* **No XPath** -- REXML's XPath module has dependencies not yet reimplemented in Opal's stdlib.
|
|
219
|
+
* **No schema generation** -- XSD, RELAX NG generation, and schema compilation require Nokogiri.
|
|
220
|
+
* **No TOML** -- Both TOML adapters (tomlib, toml-rb) require native extensions.
|
|
221
|
+
* **No Liquid** -- Template rendering via Liquid is skipped in Opal (file-system dependency). May be addressed in a future phase using liquidjs via Opal's JavaScript bridge.
|
|
@@ -94,15 +94,22 @@ subject { |m| "http://example.org/concept/#{m.code}" }
|
|
|
94
94
|
|
|
95
95
|
==== type
|
|
96
96
|
|
|
97
|
-
Sets the RDF type (`rdf:type`).
|
|
98
|
-
namespaces:
|
|
97
|
+
Sets the RDF type (`rdf:type`). Accepts a single compact IRI or an array of
|
|
98
|
+
compact IRIs. Compact IRIs are resolved via declared namespaces:
|
|
99
99
|
|
|
100
100
|
[source,ruby]
|
|
101
101
|
----
|
|
102
102
|
type "skos:Concept"
|
|
103
103
|
# resolves to <http://www.w3.org/2004/02/skos/core#Concept>
|
|
104
|
+
|
|
105
|
+
# multiple types:
|
|
106
|
+
type ["skos:Concept", "dcterms:Agent"]
|
|
104
107
|
----
|
|
105
108
|
|
|
109
|
+
In Turtle, each type produces a separate `a` triple. In JSON-LD, a single type
|
|
110
|
+
produces `"@type": "skos:Concept"` while multiple types produce an array
|
|
111
|
+
`"@type": ["skos:Concept", "dcterms:Agent"]`.
|
|
112
|
+
|
|
106
113
|
==== predicate
|
|
107
114
|
|
|
108
115
|
Each `predicate` creates a mapping between an RDF predicate and a model
|
|
@@ -119,13 +126,38 @@ Parameters:
|
|
|
119
126
|
* `namespace:` — the `Lutaml::Rdf::Namespace` subclass (required)
|
|
120
127
|
* `to:` — the model attribute to read (required)
|
|
121
128
|
* `lang_tagged:` (default: `false`) — if true, values are serialized with
|
|
122
|
-
language tags (see <<language-tagged-values>>)
|
|
129
|
+
language tags (see <<language-tagged-values>>). Mutually exclusive with
|
|
130
|
+
`uri_reference`.
|
|
131
|
+
* `uri_reference:` (default: `false`) — if true, values are serialized as URI
|
|
132
|
+
references rather than string literals (see <<uri-reference-predicates>>).
|
|
133
|
+
Mutually exclusive with `lang_tagged`.
|
|
123
134
|
|
|
124
135
|
==== members
|
|
125
136
|
|
|
126
137
|
Declares that a container model contains member resources that should be
|
|
127
138
|
serialized as separate subjects in the output graph (see <<graph-serialization>>).
|
|
128
139
|
|
|
140
|
+
Optional linking predicate parameters generate relationship triples from the
|
|
141
|
+
container to each member:
|
|
142
|
+
|
|
143
|
+
[source,ruby]
|
|
144
|
+
----
|
|
145
|
+
members :concepts, predicate_name: :member, namespace: SkosNamespace
|
|
146
|
+
----
|
|
147
|
+
|
|
148
|
+
When `predicate_name` and `namespace` are provided:
|
|
149
|
+
|
|
150
|
+
* **Turtle**: produces `skos:member <child-uri>` triples on the container subject
|
|
151
|
+
* **JSON-LD**: adds a `member` term to `@context` with `"@type": "@id"` and
|
|
152
|
+
`{"@id": "..."}` references in the container resource
|
|
153
|
+
|
|
154
|
+
Parameters:
|
|
155
|
+
|
|
156
|
+
* `attr_name` — the model attribute holding the member collection (required)
|
|
157
|
+
* `predicate_name:` — the local name for the linking predicate (optional)
|
|
158
|
+
* `namespace:` — the `Lutaml::Rdf::Namespace` subclass for the linking predicate
|
|
159
|
+
(required when `predicate_name` is given)
|
|
160
|
+
|
|
129
161
|
== Serialization
|
|
130
162
|
|
|
131
163
|
=== Turtle
|
|
@@ -207,6 +239,48 @@ class LocalizedLiteral < Lutaml::Model::Serializable
|
|
|
207
239
|
end
|
|
208
240
|
----
|
|
209
241
|
|
|
242
|
+
== [[uri-reference-predicates]]URI Reference Predicates
|
|
243
|
+
|
|
244
|
+
Predicates declared with `uri_reference: true` serialize attribute values as
|
|
245
|
+
URI references rather than string literals:
|
|
246
|
+
|
|
247
|
+
[source,ruby]
|
|
248
|
+
----
|
|
249
|
+
predicate :related, namespace: SkosNamespace, to: :related, uri_reference: true
|
|
250
|
+
----
|
|
251
|
+
|
|
252
|
+
**Turtle:**
|
|
253
|
+
|
|
254
|
+
Values are emitted as URI objects without quotes. Compact IRIs (e.g.
|
|
255
|
+
`"skos:other"`) are resolved to full URIs using the declared namespaces. Full
|
|
256
|
+
URIs (e.g. `"http://example.org/foo"`) are used as-is.
|
|
257
|
+
|
|
258
|
+
[source,turtle]
|
|
259
|
+
----
|
|
260
|
+
<http://example.org/concept/1> skos:related skos:other .
|
|
261
|
+
----
|
|
262
|
+
|
|
263
|
+
**JSON-LD:**
|
|
264
|
+
|
|
265
|
+
Values are wrapped in `{"@id": ...}` objects. The auto-generated `@context`
|
|
266
|
+
includes a term definition with `"@type": "@id"`:
|
|
267
|
+
|
|
268
|
+
[source,json]
|
|
269
|
+
----
|
|
270
|
+
{
|
|
271
|
+
"@context": {
|
|
272
|
+
"related": { "@id": "http://www.w3.org/2004/02/skos/core#related", "@type": "@id" }
|
|
273
|
+
},
|
|
274
|
+
"related": [ { "@id": "skos:other" } ]
|
|
275
|
+
}
|
|
276
|
+
----
|
|
277
|
+
|
|
278
|
+
Round-trip fidelity: compact IRI forms are preserved through serialization and
|
|
279
|
+
deserialization. Values round-trip as `Concept.from_turtle(concept.to_turtle)`
|
|
280
|
+
preserving the original `"skos:other"` compact form.
|
|
281
|
+
|
|
282
|
+
The `uri_reference` option is mutually exclusive with `lang_tagged`.
|
|
283
|
+
|
|
210
284
|
== [[graph-serialization]]Graph Serialization
|
|
211
285
|
|
|
212
286
|
When a container model holds a collection of member resources, use `members` to
|
|
@@ -227,11 +301,16 @@ class Vocabulary < Lutaml::Model::Serializable
|
|
|
227
301
|
type "skos:ConceptScheme"
|
|
228
302
|
predicate :prefLabel, namespace: SkosNamespace, to: :id
|
|
229
303
|
|
|
230
|
-
members :concepts
|
|
304
|
+
members :concepts,
|
|
305
|
+
predicate_name: :member,
|
|
306
|
+
namespace: SkosNamespace
|
|
231
307
|
end
|
|
232
308
|
end
|
|
233
309
|
----
|
|
234
310
|
|
|
311
|
+
The `predicate_name` and `namespace` parameters on `members` generate linking
|
|
312
|
+
triples from the container to each member.
|
|
313
|
+
|
|
235
314
|
=== Turtle Output with Members
|
|
236
315
|
|
|
237
316
|
[source,ruby]
|
|
@@ -247,7 +326,9 @@ Produces a single Turtle document with the container and all member triples:
|
|
|
247
326
|
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
248
327
|
|
|
249
328
|
<http://example.org/vocab/iso1087> a skos:ConceptScheme;
|
|
250
|
-
skos:prefLabel "iso1087"
|
|
329
|
+
skos:prefLabel "iso1087";
|
|
330
|
+
skos:member <http://example.org/concept/2119>;
|
|
331
|
+
skos:member <http://example.org/concept/2120> .
|
|
251
332
|
|
|
252
333
|
<http://example.org/concept/2119> a skos:Concept;
|
|
253
334
|
skos:notation "2119";
|
|
@@ -272,14 +353,19 @@ Produces a JSON-LD document with `@graph` containing all resources:
|
|
|
272
353
|
{
|
|
273
354
|
"@context": {
|
|
274
355
|
"skos": "http://www.w3.org/2004/02/skos/core#",
|
|
275
|
-
"prefLabel": "skos:prefLabel",
|
|
276
|
-
"notation": "skos:notation"
|
|
356
|
+
"prefLabel": { "@id": "skos:prefLabel" },
|
|
357
|
+
"notation": { "@id": "skos:notation" },
|
|
358
|
+
"member": { "@id": "http://www.w3.org/2004/02/skos/core#member", "@type": "@id" }
|
|
277
359
|
},
|
|
278
360
|
"@graph": [
|
|
279
361
|
{
|
|
280
362
|
"@id": "http://example.org/vocab/iso1087",
|
|
281
363
|
"@type": "skos:ConceptScheme",
|
|
282
|
-
"prefLabel": "iso1087"
|
|
364
|
+
"prefLabel": "iso1087",
|
|
365
|
+
"member": [
|
|
366
|
+
{ "@id": "http://example.org/concept/2119" },
|
|
367
|
+
{ "@id": "http://example.org/concept/2120" }
|
|
368
|
+
]
|
|
283
369
|
},
|
|
284
370
|
{
|
|
285
371
|
"@id": "http://example.org/concept/2119",
|