lutaml-model 0.8.3 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +3 -1
  3. data/.rubocop.yml +18 -0
  4. data/.rubocop_todo.yml +16 -22
  5. data/Gemfile +2 -0
  6. data/README.adoc +327 -3
  7. data/docs/_guides/document-validation.adoc +303 -0
  8. data/docs/_guides/index.adoc +19 -0
  9. data/docs/_guides/jsonld-serialization.adoc +217 -0
  10. data/docs/_guides/rdf-serialization.adoc +344 -0
  11. data/docs/_guides/turtle-serialization.adoc +224 -0
  12. data/docs/_guides/xml-mapping.adoc +9 -1
  13. data/docs/_guides/xml_mappings/07_best_practices.adoc +36 -0
  14. data/docs/_guides/xml_mappings/08_troubleshooting.adoc +89 -0
  15. data/docs/_pages/serialization_adapters.adoc +31 -0
  16. data/docs/_references/index.adoc +1 -0
  17. data/docs/_references/rdf-namespaces.adoc +243 -0
  18. data/docs/_tutorials/lutaml-xml-architecture.adoc +6 -1
  19. data/docs/index.adoc +3 -2
  20. data/lib/lutaml/jsonld/adapter.rb +23 -0
  21. data/lib/lutaml/jsonld/context.rb +69 -0
  22. data/lib/lutaml/jsonld/term_definition.rb +39 -0
  23. data/lib/lutaml/jsonld/transform.rb +174 -0
  24. data/lib/lutaml/jsonld.rb +23 -0
  25. data/lib/lutaml/model/attribute.rb +19 -1
  26. data/lib/lutaml/model/error/liquid_drop_already_registered_error.rb +11 -0
  27. data/lib/lutaml/model/error/ordered_content_mapping_error.rb +17 -0
  28. data/lib/lutaml/model/format_registry.rb +10 -1
  29. data/lib/lutaml/model/global_context.rb +1 -0
  30. data/lib/lutaml/model/liquefiable.rb +12 -15
  31. data/lib/lutaml/model/mapping/mapping_rule.rb +10 -2
  32. data/lib/lutaml/model/mapping_hash.rb +1 -1
  33. data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
  34. data/lib/lutaml/model/services/transformer.rb +67 -32
  35. data/lib/lutaml/model/transform.rb +41 -4
  36. data/lib/lutaml/model/uninitialized_class.rb +11 -5
  37. data/lib/lutaml/model/validation/concerns/has_issues.rb +27 -0
  38. data/lib/lutaml/model/validation/context.rb +36 -0
  39. data/lib/lutaml/model/validation/issue.rb +62 -0
  40. data/lib/lutaml/model/validation/layer_result.rb +34 -0
  41. data/lib/lutaml/model/validation/profile.rb +66 -0
  42. data/lib/lutaml/model/validation/registry.rb +60 -0
  43. data/lib/lutaml/model/validation/remediation.rb +33 -0
  44. data/lib/lutaml/model/validation/remediation_result.rb +20 -0
  45. data/lib/lutaml/model/validation/report.rb +39 -0
  46. data/lib/lutaml/model/validation/rule.rb +59 -0
  47. data/lib/lutaml/model/validation.rb +2 -1
  48. data/lib/lutaml/model/validation_framework.rb +77 -0
  49. data/lib/lutaml/model/version.rb +1 -1
  50. data/lib/lutaml/model.rb +10 -0
  51. data/lib/lutaml/rdf/error.rb +7 -0
  52. data/lib/lutaml/rdf/iri.rb +44 -0
  53. data/lib/lutaml/rdf/language_tagged.rb +11 -0
  54. data/lib/lutaml/rdf/literal.rb +62 -0
  55. data/lib/lutaml/rdf/mapping.rb +71 -0
  56. data/lib/lutaml/rdf/mapping_rule.rb +35 -0
  57. data/lib/lutaml/rdf/member_rule.rb +13 -0
  58. data/lib/lutaml/rdf/namespace.rb +58 -0
  59. data/lib/lutaml/rdf/namespace_set.rb +69 -0
  60. data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
  61. data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
  62. data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
  63. data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
  64. data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
  65. data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
  66. data/lib/lutaml/rdf/namespaces.rb +14 -0
  67. data/lib/lutaml/rdf/transform.rb +36 -0
  68. data/lib/lutaml/rdf.rb +19 -0
  69. data/lib/lutaml/turtle/adapter.rb +35 -0
  70. data/lib/lutaml/turtle/mapping.rb +7 -0
  71. data/lib/lutaml/turtle/transform.rb +158 -0
  72. data/lib/lutaml/turtle.rb +22 -0
  73. data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +9 -2
  74. data/lib/lutaml/xml/adapter/oga_adapter.rb +11 -3
  75. data/lib/lutaml/xml/adapter/ox_adapter.rb +5 -2
  76. data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -3
  77. data/lib/lutaml/xml/adapter_element.rb +26 -2
  78. data/lib/lutaml/xml/data_model.rb +14 -0
  79. data/lib/lutaml/xml/document.rb +3 -0
  80. data/lib/lutaml/xml/element.rb +8 -2
  81. data/lib/lutaml/xml/mapping.rb +9 -0
  82. data/lib/lutaml/xml/model_transform.rb +42 -0
  83. data/lib/lutaml/xml/schema/xsd/base.rb +4 -1
  84. data/lib/lutaml/xml/serialization/instance_methods.rb +3 -1
  85. data/lib/lutaml/xml/transformation/ordered_applier.rb +46 -2
  86. data/lib/lutaml/xml/transformation.rb +40 -1
  87. data/lib/lutaml/xml/xml_element.rb +8 -7
  88. data/lutaml-model.gemspec +1 -1
  89. data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
  90. data/spec/lutaml/integration/multi_format_spec.rb +106 -0
  91. data/spec/lutaml/integration/round_trip_spec.rb +170 -0
  92. data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
  93. data/spec/lutaml/jsonld/context_spec.rb +114 -0
  94. data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
  95. data/spec/lutaml/jsonld/transform_spec.rb +211 -0
  96. data/spec/lutaml/model/attribute_default_cache_spec.rb +58 -0
  97. data/spec/lutaml/model/liquefiable_spec.rb +22 -6
  98. data/spec/lutaml/model/liquid_compatibility_spec.rb +442 -0
  99. data/spec/lutaml/model/ordered_content_spec.rb +5 -5
  100. data/spec/lutaml/model/services/transformer_spec.rb +43 -0
  101. data/spec/lutaml/model/transform_cache_spec.rb +62 -0
  102. data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +41 -0
  103. data/spec/lutaml/model/uninitialized_class_deep_dup_spec.rb +39 -0
  104. data/spec/lutaml/model/uninitialized_class_spec.rb +14 -2
  105. data/spec/lutaml/model/validation/concerns/has_issues_spec.rb +76 -0
  106. data/spec/lutaml/model/validation/context_spec.rb +60 -0
  107. data/spec/lutaml/model/validation/issue_spec.rb +77 -0
  108. data/spec/lutaml/model/validation/layer_result_spec.rb +66 -0
  109. data/spec/lutaml/model/validation/profile_spec.rb +134 -0
  110. data/spec/lutaml/model/validation/registry_spec.rb +94 -0
  111. data/spec/lutaml/model/validation/remediation_result_spec.rb +23 -0
  112. data/spec/lutaml/model/validation/remediation_spec.rb +72 -0
  113. data/spec/lutaml/model/validation/report_spec.rb +58 -0
  114. data/spec/lutaml/model/validation/rule_spec.rb +134 -0
  115. data/spec/lutaml/model/validation/uninitialized_class_validate_spec.rb +29 -0
  116. data/spec/lutaml/model/validation/validation_error_spec.rb +29 -0
  117. data/spec/lutaml/model/validation/validation_framework_spec.rb +110 -0
  118. data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
  119. data/spec/lutaml/rdf/iri_spec.rb +73 -0
  120. data/spec/lutaml/rdf/literal_spec.rb +98 -0
  121. data/spec/lutaml/rdf/mapping_spec.rb +164 -0
  122. data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
  123. data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
  124. data/spec/lutaml/rdf/namespace_spec.rb +241 -0
  125. data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
  126. data/spec/lutaml/turtle/adapter_spec.rb +47 -0
  127. data/spec/lutaml/turtle/mapping_spec.rb +123 -0
  128. data/spec/lutaml/turtle/transform_spec.rb +273 -0
  129. data/spec/lutaml/xml/content_model_validation_spec.rb +157 -0
  130. data/spec/lutaml/xml/mapping_spec.rb +12 -7
  131. metadata +95 -7
  132. data/spec/fixtures/liquid_templates/_ceramics.liquid +0 -3
@@ -8,6 +8,95 @@ parent: XML Mappings Guide
8
8
 
9
9
  == Common errors
10
10
 
11
+ === OrderedContentMappingError
12
+
13
+ **Error message:**
14
+
15
+ ----
16
+ Lutaml::Model::OrderedContentMappingError: Element-only content model
17
+ (`ordered`) does not support `map_content` in ModelName. Use `mixed_content`
18
+ instead of `ordered` when you need to capture text content between elements.
19
+ ----
20
+
21
+ **Cause:**
22
+
23
+ Using `map_content` (text content mapping) together with `ordered`
24
+ (element-only content model). Element-only models only contain child elements,
25
+ not text nodes.
26
+
27
+ **Solution:**
28
+
29
+ If you need text content between elements, use `mixed_content` instead of
30
+ `ordered`:
31
+
32
+ [source,ruby]
33
+ ----
34
+ # WRONG -- ordered + map_content is invalid
35
+ class Paragraph < Lutaml::Model::Serializable
36
+ xml do
37
+ element 'p'
38
+ ordered # Element-only, no text allowed
39
+ map_content to: :text # ERROR!
40
+ end
41
+ end
42
+
43
+ # CORRECT -- use mixed_content for text + elements
44
+ class Paragraph < Lutaml::Model::Serializable
45
+ attribute :text, :string, collection: true
46
+
47
+ xml do
48
+ element 'p'
49
+ mixed_content # Allows text + elements
50
+ map_content to: :text # OK!
51
+ end
52
+ end
53
+ ----
54
+
55
+ === MixedContentCollectionError
56
+
57
+ **Error message:**
58
+
59
+ ----
60
+ Lutaml::Model::MixedContentCollectionError: Mixed content requires `text`
61
+ to be a string collection in ModelName. Use `attribute :text, :string,
62
+ collection: true` when `mixed_content` is enabled.
63
+ ----
64
+
65
+ **Cause:**
66
+
67
+ Using `mixed_content` with `map_content` mapped to a non-collection attribute.
68
+ Mixed content produces multiple text segments (one between each pair of child
69
+ elements), so the target attribute must be a collection.
70
+
71
+ **Solution:**
72
+
73
+ Declare the attribute with `collection: true`:
74
+
75
+ [source,ruby]
76
+ ----
77
+ # WRONG -- non-collection attribute with mixed content
78
+ class Paragraph < Lutaml::Model::Serializable
79
+ attribute :text, :string # Missing collection: true
80
+
81
+ xml do
82
+ element 'p'
83
+ mixed_content
84
+ map_content to: :text # ERROR!
85
+ end
86
+ end
87
+
88
+ # CORRECT -- collection attribute for mixed content
89
+ class Paragraph < Lutaml::Model::Serializable
90
+ attribute :text, :string, collection: true
91
+
92
+ xml do
93
+ element 'p'
94
+ mixed_content
95
+ map_content to: :text # OK!
96
+ end
97
+ end
98
+ ----
99
+
11
100
  === NoRootMappingError
12
101
 
13
102
  **Error message:**
@@ -25,6 +25,8 @@ LutaML, out of the box, supports the following serialization formats:
25
25
  * YAML (https://yaml.org/[YAML version 1.2])
26
26
  * JSON (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/[ECMA-404 The JSON Data Interchange Standard], unofficial link: https://www.json.org[JSON])
27
27
  * TOML (https://toml.io/en[TOML version 1.0])
28
+ * JSON-LD (https://www.w3.org/TR/json-ld11/[W3C JSON-LD 1.1])
29
+ * Turtle (https://www.w3.org/TR/turtle/[W3C RDF 1.1 Turtle])
28
30
 
29
31
  The adapter interface is also used to support certain transformation of models
30
32
  into an "end format", which is not a serialization format. For example, the
@@ -309,6 +311,35 @@ end
309
311
  ----
310
312
 
311
313
 
314
+ === JSON-LD
315
+
316
+ Lutaml::Model supports serialization to and from JSON-LD (W3C JSON-LD 1.1).
317
+ JSON-LD is a key-value format that extends the built-in JSON serialization with
318
+ JSON-LD-specific constructs: `@context`, `@type`, and `@id`.
319
+
320
+ The adapter uses the standard Ruby `JSON` library internally — no additional gem
321
+ is required beyond `lutaml-model`.
322
+
323
+ See the link:../guides/jsonld-serialization[JSON-LD Serialization guide] for
324
+ mapping DSL details and usage examples.
325
+
326
+
327
+ === Turtle
328
+
329
+ Lutaml::Model supports serialization to and from W3C RDF Turtle format.
330
+ Turtle is a non-key-value format based on RDF triples with its own mapping DSL.
331
+
332
+ Requires the `rdf-turtle` gem:
333
+
334
+ [source,ruby]
335
+ ----
336
+ gem "rdf-turtle", "~> 3.3"
337
+ ----
338
+
339
+ See the link:../guides/turtle-serialization[Turtle Serialization guide] for
340
+ mapping DSL details and usage examples.
341
+
342
+
312
343
  === Error handling
313
344
 
314
345
  When parsing invalid serialization format data, an `InvalidFormatError` is
@@ -39,6 +39,7 @@ Deep dives into specific features:
39
39
 
40
40
  * link:../format-independent-features[Format-Independent Features] - Cross-format mechanisms
41
41
  * link:../parent-root-context[Parent and Root Context] - Model hierarchy navigation
42
+ * link:../rdf-namespaces[RDF Namespaces] - Namespace, NamespaceSet, Iri, Literal
42
43
 
43
44
  == See also
44
45
 
@@ -0,0 +1,243 @@
1
+ ---
2
+ title: RDF Namespaces
3
+ nav_order: 12
4
+ ---
5
+
6
+ = RDF Namespaces
7
+
8
+ Lutaml::Model provides a comprehensive RDF infrastructure with `Namespace`,
9
+ `NamespaceSet`, `Iri`, and `Literal` classes. These are shared by the JSON-LD
10
+ and Turtle format adapters.
11
+
12
+ == Namespace
13
+
14
+ `Lutaml::Rdf::Namespace` is the abstract base class for RDF namespace
15
+ vocabularies. Subclasses define `uri` and `prefix` at the class level:
16
+
17
+ [source,ruby]
18
+ ----
19
+ ns = Class.new(Lutaml::Rdf::Namespace)
20
+ ns.uri "http://example.org/ns/"
21
+ ns.prefix "ex"
22
+
23
+ ns["someName"] # => "http://example.org/ns/someName"
24
+ ns.prefixed("Name") # => "ex:Name"
25
+ ----
26
+
27
+ === Immutability
28
+
29
+ Once set, `uri` and `prefix` are frozen and cannot be changed:
30
+
31
+ [source,ruby]
32
+ ----
33
+ ns = Class.new(Lutaml::Rdf::Namespace)
34
+ ns.uri "http://example.org/ns/"
35
+ ns.uri "http://other.org/" # => raises FrozenError
36
+ ----
37
+
38
+ Each subclass has independent state — setting `uri` on one does not affect
39
+ another.
40
+
41
+ === Equality
42
+
43
+ Namespace classes implement equality based on `uri` and `prefix`:
44
+
45
+ [source,ruby]
46
+ ----
47
+ ns1 = Class.new(Lutaml::Rdf::Namespace)
48
+ ns1.uri "http://example.org/"
49
+ ns1.prefix "ex"
50
+
51
+ ns2 = Class.new(Lutaml::Rdf::Namespace)
52
+ ns2.uri "http://example.org/"
53
+ ns2.prefix "ex"
54
+
55
+ ns1 == ns2 # => true
56
+ ----
57
+
58
+ === Compact IRI resolution
59
+
60
+ [source,ruby]
61
+ ----
62
+ Lutaml::Rdf::Namespace.resolve_compact_iri("skos:Concept", [SkosNamespace])
63
+ # => "http://www.w3.org/2004/02/skos/core#Concept"
64
+
65
+ Lutaml::Rdf::Namespace.resolve_compact_iri("plain_name", [SkosNamespace])
66
+ # => "plain_name" (returned as-is when no colon present)
67
+ ----
68
+
69
+ == NamespaceSet
70
+
71
+ `Lutaml::Rdf::NamespaceSet` is an ordered, enumerable collection of namespace
72
+ classes with O(1) prefix lookup:
73
+
74
+ [source,ruby]
75
+ ----
76
+ set = Lutaml::Rdf::NamespaceSet.new(
77
+ Lutaml::Rdf::Namespaces::SkosNamespace,
78
+ Lutaml::Rdf::Namespaces::DctermsNamespace
79
+ )
80
+
81
+ set["skos"] # => SkosNamespace (O(1) lookup)
82
+ set.size # => 2
83
+ set.resolve_compact_iri("skos:Concept")
84
+ # => "http://www.w3.org/2004/02/skos/core#Concept"
85
+ set.compact("http://www.w3.org/2004/02/skos/core#Concept")
86
+ # => "skos:Concept"
87
+ set.to_hash
88
+ # => { "skos" => "http://www.w3.org/2004/02/skos/core#",
89
+ # "dcterms" => "http://purl.org/dc/terms/" }
90
+ ----
91
+
92
+ === Collision detection
93
+
94
+ Adding two different namespace classes with the same prefix raises an error:
95
+
96
+ [source,ruby]
97
+ ----
98
+ set = Lutaml::Rdf::NamespaceSet.new(SkosNamespace)
99
+ ns = Class.new(Lutaml::Rdf::Namespace)
100
+ ns.uri "http://other.org/"
101
+ ns.prefix "skos"
102
+ set.add(ns) # => raises ArgumentError: Prefix 'skos' conflicts
103
+ ----
104
+
105
+ Adding the same class twice is allowed (idempotent).
106
+
107
+ == Iri
108
+
109
+ `Lutaml::Rdf::Iri` is an immutable value object for IRIs:
110
+
111
+ [source,ruby]
112
+ ----
113
+ iri = Lutaml::Rdf::Iri.new("http://example.org/concept/1")
114
+ iri.to_s # => "http://example.org/concept/1"
115
+ iri.value # => "http://example.org/concept/1" (frozen)
116
+
117
+ # Expand compact → full
118
+ set = Lutaml::Rdf::NamespaceSet.new(SkosNamespace)
119
+ compact_iri = Lutaml::Rdf::Iri.new("skos:Concept")
120
+ compact_iri.expand(set) # => Iri("http://www.w3.org/2004/02/skos/core#Concept")
121
+
122
+ # Compact full → compact
123
+ full_iri = Lutaml::Rdf::Iri.new("http://www.w3.org/2004/02/skos/core#Concept")
124
+ full_iri.compact(set) # => "skos:Concept"
125
+ ----
126
+
127
+ Implements `Comparable` for sorting.
128
+
129
+ == Literal
130
+
131
+ `Lutaml::Rdf::Literal` is a value object for RDF literals with optional
132
+ datatype and language tag:
133
+
134
+ [source,ruby]
135
+ ----
136
+ # Plain literal
137
+ lit = Lutaml::Rdf::Literal.new("hello")
138
+ lit.to_turtle # => '"hello"'
139
+ lit.to_jsonld_term # => "hello"
140
+
141
+ # Language-tagged literal
142
+ lit = Lutaml::Rdf::Literal.new("hello", language: "en")
143
+ lit.to_turtle # => '"hello"@en'
144
+ lit.to_jsonld_term # => { "@value" => "hello", "@language" => "en" }
145
+
146
+ # Typed literal
147
+ lit = Lutaml::Rdf::Literal.new("42", datatype: "http://www.w3.org/2001/XMLSchema#integer")
148
+ lit.to_turtle # => '"42"^^<http://www.w3.org/2001/XMLSchema#integer>'
149
+ lit.to_jsonld_term # => { "@value" => "42", "@type" => "xsd:integer" }
150
+ ----
151
+
152
+ === Special character escaping
153
+
154
+ Quotes, newlines, tabs, and backslashes are automatically escaped in Turtle
155
+ and JSON-LD output.
156
+
157
+ == Standard W3C Namespaces
158
+
159
+ Pre-defined namespace classes are in `Lutaml::Rdf::Namespaces`:
160
+
161
+ [cols="1,1,1"]
162
+ |===
163
+ | Class | Prefix | URI
164
+
165
+ | `SkosNamespace` | `skos` | `http://www.w3.org/2004/02/skos/core#`
166
+ | `DctermsNamespace` | `dcterms` | `http://purl.org/dc/terms/`
167
+ | `RdfSyntaxNamespace` | `rdf` | `http://www.w3.org/1999/02/22-rdf-syntax-ns#`
168
+ | `RdfsNamespace` | `rdfs` | `http://www.w3.org/2000/01/rdf-schema#`
169
+ | `OwlNamespace` | `owl` | `http://www.w3.org/2002/07/owl#`
170
+ | `XsdNamespace` | `xsd` | `http://www.w3.org/2001/XMLSchema#`
171
+ |===
172
+
173
+ NOTE: `RdfNamespace` is a backward-compatible alias for `RdfSyntaxNamespace`.
174
+
175
+ == Custom Namespaces
176
+
177
+ Define your own by subclassing:
178
+
179
+ [source,ruby]
180
+ ----
181
+ class MyNamespace < Lutaml::Rdf::Namespace
182
+ uri "http://example.org/vocab/"
183
+ prefix "my"
184
+ end
185
+
186
+ MyNamespace["term"] # => "http://example.org/vocab/term"
187
+ ----
188
+
189
+ == Error hierarchy
190
+
191
+ [cols="1,1"]
192
+ |===
193
+ | Class | Description
194
+
195
+ | `Lutaml::Rdf::Error` | Base error for all RDF errors
196
+ | `Lutaml::Turtle::MissingSubjectError` | Raised when Turtle mapping has no `subject` block
197
+ |===
198
+
199
+ == Unified RDF Mapping
200
+
201
+ `Lutaml::Rdf::Mapping` is the unified mapping base class shared by both Turtle
202
+ and JSON-LD format adapters. It provides the `rdf` DSL for defining
203
+ predicate-based mappings once and using them for both output formats.
204
+
205
+ See link:../_guides/rdf-serialization.adoc[Unified RDF Serialization] for the
206
+ complete guide.
207
+
208
+ === MappingRule
209
+
210
+ `Lutaml::Rdf::MappingRule` is a value object for predicate-to-attribute
211
+ mappings:
212
+
213
+ [source,ruby]
214
+ ----
215
+ # Created by the `predicate` DSL method
216
+ predicate :prefLabel, namespace: SkosNamespace, to: :name, lang_tagged: true
217
+ ----
218
+
219
+ Each rule stores: `predicate_name`, `namespace`, `to` (attribute symbol), and
220
+ `lang_tagged` flag.
221
+
222
+ === MemberRule
223
+
224
+ `Lutaml::Rdf::MemberRule` is a value object for the `members` declaration in
225
+ graph-level RDF mapping:
226
+
227
+ [source,ruby]
228
+ ----
229
+ # In a container model's rdf block
230
+ members :concepts
231
+ ----
232
+
233
+ Stores the attribute name referencing the collection of member models.
234
+
235
+ === Transform
236
+
237
+ `Lutaml::Rdf::Transform` is the base transform class for RDF serialization,
238
+ shared by both `Turtle::Transform` and `JsonLd::Transform`. It provides:
239
+
240
+ * `resolve_subject_uri(mapping, instance)` — evaluates the subject block
241
+ * `resolve_type_uri(mapping)` — resolves compact IRI to full URI
242
+ * `resolve_type_compact(mapping)` — returns the compact IRI form
243
+ * `extract_language(value)` — extracts language code from `LanguageTagged` values
@@ -690,7 +690,8 @@ XmlElement = {
690
690
  attributes: { orderDate: "1999-10-20" },
691
691
  elements: [
692
692
  XmlElement(name: "shipTo", ...),
693
- XmlText("Text content")
693
+ XmlText("Text content"),
694
+ XmlComment(" comment text ")
694
695
  ],
695
696
  content: "Text content", # For mixed content
696
697
  }
@@ -705,6 +706,10 @@ XmlText = {
705
706
  content: "Text content",
706
707
  namespace: nil, # or NamespaceClass
707
708
  }
709
+
710
+ XmlComment = {
711
+ content: " comment text ", # Content between <!-- and -->
712
+ }
708
713
  ```
709
714
 
710
715
  **Key Properties**:
data/docs/index.adoc CHANGED
@@ -11,13 +11,14 @@ image:https://img.shields.io/github/license/lutaml/lutaml-model.svg[License]
11
11
  image:https://github.com/lutaml/lutaml-model/actions/workflows/rake.yml/badge.svg["Build", link="https://github.com/lutaml/lutaml-model/actions/workflows/rake.yml"]
12
12
  image:https://github.com/lutaml/lutaml-model/actions/workflows/dependent-tests.yml/badge.svg["Dependent tests", link="https://github.com/lutaml/lutaml-model/actions/workflows/dependent-tests.yml"]
13
13
 
14
- Lutaml::Model is a Ruby library for creating information models with attributes and types, providing flexible and comprehensive mechanisms for serializing to and from multiple formats including XML, JSON, YAML, TOML, and Hash.
14
+ Lutaml::Model is a Ruby library for creating information models with attributes and types, providing flexible and comprehensive mechanisms for serializing to and from multiple formats including XML, JSON, YAML, TOML, JSON-LD, Turtle, and Hash.
15
15
 
16
16
  == Key features
17
17
 
18
18
  * **Model-driven design** - Define models with attributes and types
19
- * **Multi-format serialization** - XML, JSON, YAML, TOML, Hash, YAML Stream
19
+ * **Multi-format serialization** - XML, JSON, YAML, TOML, JSON-LD, Turtle, Hash, YAML Stream
20
20
  * **XML namespace support** - Full W3C namespace implementation
21
+ * **Linked Data support** - RDF namespaces, JSON-LD, and Turtle serialization
21
22
  * **Validation** - Built-in validation with custom rules
22
23
  * **Schema generation** - Generate XSD, JSON Schema, YAML Schema
23
24
  * **Polymorphism** - Handle multiple types elegantly
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Lutaml
6
+ module JsonLd
7
+ class Adapter < Lutaml::KeyValue::Document
8
+ def self.parse(jsonld_string, _options = {})
9
+ JSON.parse(jsonld_string, create_additions: false)
10
+ end
11
+
12
+ def to_jsonld(*args)
13
+ options = args.first || {}
14
+ data = @attributes
15
+ if options[:pretty]
16
+ JSON.pretty_generate(data, *args)
17
+ else
18
+ JSON.generate(data, *args)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module JsonLd
5
+ class Context
6
+ attr_reader :prefixes, :terms
7
+
8
+ def initialize
9
+ @prefixes = {}
10
+ @terms = {}
11
+ @vocab = nil
12
+ @language = nil
13
+ @base = nil
14
+ end
15
+
16
+ def prefix(namespace_class)
17
+ @prefixes[namespace_class.prefix] = namespace_class.uri
18
+ end
19
+
20
+ def vocab(uri = nil)
21
+ @vocab = uri if uri
22
+ @vocab
23
+ end
24
+
25
+ def language(lang = nil)
26
+ @language = lang if lang
27
+ @language
28
+ end
29
+
30
+ def base(uri = nil)
31
+ @base = uri if uri
32
+ @base
33
+ end
34
+
35
+ def term(name, id: nil, type: nil, container: nil, language: nil,
36
+ reverse: false)
37
+ @terms[name] = TermDefinition.new(
38
+ name: name,
39
+ id: id,
40
+ type: type,
41
+ container: container,
42
+ language: language,
43
+ reverse: reverse,
44
+ )
45
+ end
46
+
47
+ def to_hash
48
+ ctx = {}
49
+ ctx["@vocab"] = @vocab if @vocab
50
+ ctx["@language"] = @language if @language
51
+ ctx["@base"] = @base if @base
52
+ @prefixes.each { |pfx, uri| ctx[pfx] = uri }
53
+ @terms.each_value { |td| ctx.merge!(td.to_context_hash) }
54
+ ctx
55
+ end
56
+
57
+ def resolve(term_name)
58
+ if term_name.include?(":")
59
+ pfx, local = term_name.split(":", 2)
60
+ "#{@prefixes[pfx]}#{local}" if @prefixes.key?(pfx)
61
+ elsif @terms.key?(term_name)
62
+ @terms[term_name].id
63
+ elsif @vocab
64
+ "#{@vocab}#{term_name}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module JsonLd
5
+ class TermDefinition
6
+ attr_reader :name, :id, :type, :container, :language, :reverse
7
+
8
+ def initialize(name:, id: nil, type: nil, container: nil, language: nil,
9
+ reverse: false)
10
+ @name = name
11
+ @id = id
12
+ @type = type
13
+ @container = container
14
+ @language = language
15
+ @reverse = reverse
16
+ end
17
+
18
+ def to_context_hash
19
+ if simple_mapping?
20
+ { @name => @id }
21
+ else
22
+ defn = {}
23
+ defn["@id"] = @id if @id
24
+ defn["@type"] = @type if @type
25
+ defn["@container"] = "@#{@container}" if @container
26
+ defn["@language"] = @language if @language
27
+ defn["@reverse"] = @reverse if @reverse
28
+ { @name => defn }
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def simple_mapping?
35
+ @id && @type.nil? && @container.nil? && @language.nil? && !@reverse
36
+ end
37
+ end
38
+ end
39
+ end