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
|
@@ -73,9 +73,18 @@ Member models (serialized via a container's `members` declaration) can omit the
|
|
|
73
73
|
|
|
74
74
|
=== type
|
|
75
75
|
|
|
76
|
-
Sets the RDF type (`rdf:type`) triple.
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
Sets the RDF type (`rdf:type`) triple. Accepts a single compact IRI or an array:
|
|
77
|
+
|
|
78
|
+
[source,ruby]
|
|
79
|
+
----
|
|
80
|
+
type "skos:Concept"
|
|
81
|
+
# or multiple types:
|
|
82
|
+
type ["skos:Concept", "dcterms:Agent"]
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
Compact IRIs are resolved to full URIs via the declared namespaces. Full URIs
|
|
86
|
+
(e.g., `"http://example.org/MyType"`) are used as-is. Each type produces a
|
|
87
|
+
separate `a` triple in the output.
|
|
79
88
|
|
|
80
89
|
=== predicate
|
|
81
90
|
|
|
@@ -85,7 +94,11 @@ Each `predicate` creates a `Lutaml::Rdf::MappingRule` value object:
|
|
|
85
94
|
* `namespace:` — the `Lutaml::Rdf::Namespace` subclass (required, validated)
|
|
86
95
|
* `to:` — the model attribute to read/write (required)
|
|
87
96
|
* `lang_tagged:` (default: `false`) — if true, appends `@lang` suffix from the
|
|
88
|
-
value's `language_code` or `language` method
|
|
97
|
+
value's `language_code` or `language` method. Mutually exclusive with
|
|
98
|
+
`uri_reference`.
|
|
99
|
+
* `uri_reference:` (default: `false`) — if true, serializes values as URI
|
|
100
|
+
objects rather than string literals. Compact IRIs are resolved via the
|
|
101
|
+
declared namespaces. Mutually exclusive with `lang_tagged`.
|
|
89
102
|
|
|
90
103
|
Validation errors:
|
|
91
104
|
|
|
@@ -723,7 +723,8 @@ end
|
|
|
723
723
|
|
|
724
724
|
* **Ox**: Fastest, use for performance-critical applications
|
|
725
725
|
* **Nokogiri**: Most compatible, good balance
|
|
726
|
-
* **Oga**: Pure Ruby,
|
|
726
|
+
* **Oga**: Pure Ruby, use when no native extensions allowed
|
|
727
|
+
* **REXML**: Pure Ruby (bundled with Ruby), Opal-compatible
|
|
727
728
|
|
|
728
729
|
**Configure once:**
|
|
729
730
|
|
|
@@ -74,7 +74,7 @@ When an adapter is needed for a format, `AdapterResolver` follows this chain:
|
|
|
74
74
|
| Pure Ruby. No compilation needed. Opal-compatible.
|
|
75
75
|
|
|
76
76
|
| `:rexml`
|
|
77
|
-
| Pure Ruby. Bundled with Ruby (default gem).
|
|
77
|
+
| Pure Ruby. Bundled with Ruby (default gem). Opal-compatible.
|
|
78
78
|
|===
|
|
79
79
|
|
|
80
80
|
=== JSON adapters
|
|
@@ -261,8 +261,8 @@ end
|
|
|
261
261
|
|
|
262
262
|
* **Nokogiri**: Most projects (default, best compatibility)
|
|
263
263
|
* **Ox**: Performance-critical applications
|
|
264
|
-
* **Oga**: Pure Ruby environments
|
|
265
|
-
* **REXML**: No extra gems, pure Ruby (bundled with Ruby)
|
|
264
|
+
* **Oga**: Pure Ruby environments
|
|
265
|
+
* **REXML**: No extra gems, pure Ruby (bundled with Ruby). Also used under Opal.
|
|
266
266
|
|
|
267
267
|
=== Choose JSON adapter based on
|
|
268
268
|
|
|
@@ -279,12 +279,14 @@ end
|
|
|
279
279
|
|
|
280
280
|
Lutaml::Model supports running under https://opalrb.com[Opal] (Ruby compiled to JavaScript) with some limitations. The library detects the runtime automatically via `Lutaml::Model::RuntimeCompatibility` and adapts its behavior accordingly.
|
|
281
281
|
|
|
282
|
+
The XML parsing layer is provided by the https://github.com/lutaml/moxml[moxml] gem, which has been updated to support Opal. Under Opal, moxml uses the REXML adapter because Opal reimplements `strscan` and `stringio` in its stdlib, enabling REXML (pure Ruby) to compile cleanly to JavaScript.
|
|
283
|
+
|
|
282
284
|
=== Adapter defaults on Opal
|
|
283
285
|
|
|
284
286
|
[cols="1,2",options="header"]
|
|
285
287
|
|===
|
|
286
288
|
| Format | Behavior
|
|
287
|
-
| XML | Only `:
|
|
289
|
+
| XML | Only `:rexml` is available (auto-selected). Opal reimplements strscan/stringio in its stdlib, enabling REXML to compile to JavaScript.
|
|
288
290
|
| JSON | Only `:standard` is available. Oj and MultiJson require native extensions.
|
|
289
291
|
| YAML | `:standard` (Psych ships with Opal's stdlib).
|
|
290
292
|
| TOML | **Not available.** Both tomlib and toml-rb depend on native extensions.
|
|
@@ -299,6 +301,9 @@ The following features raise `NotImplementedError` when called under Opal:
|
|
|
299
301
|
* `Lutaml::Model::Schema.to_relaxng` -- RELAX NG generation requires Nokogiri
|
|
300
302
|
* `Lutaml::Model::Schema.from_xml` -- XML schema compilation is not supported
|
|
301
303
|
* `Lutaml::Xml::Schema::Xsd::Base#to_formatted_xml` -- XSD formatted output requires the Canon gem
|
|
304
|
+
* XPath queries -- REXML XPath requires features not yet supported by Opal's stdlib
|
|
305
|
+
|
|
306
|
+
See the link:../guides/opal[Opal Usage Guide] for complete setup instructions and limitations.
|
|
302
307
|
|
|
303
308
|
== See also
|
|
304
309
|
|
data/docs/_pages/index.adoc
CHANGED
|
@@ -22,6 +22,7 @@ Fundamental concepts, configuration, and essential features of Lutaml::Model.
|
|
|
22
22
|
* link:breaking-changes[Breaking Changes] - Version compatibility
|
|
23
23
|
* link:comparison-with-shale[Comparison with Shale] - Migration guide
|
|
24
24
|
* link:xml-conformance[XML Standards Conformance] - W3C spec compliance status
|
|
25
|
+
* link:../guides/opal[Opal Usage] - Run in the browser via Opal
|
|
25
26
|
* link:troubleshooting[Troubleshooting] - Common issues and solutions
|
|
26
27
|
|
|
27
28
|
== Getting started
|
|
@@ -192,8 +192,7 @@ Requires the `nokogiri` gem.
|
|
|
192
192
|
Oga::
|
|
193
193
|
(optional)
|
|
194
194
|
Pure Ruby XML parser.
|
|
195
|
-
Does not require native extensions
|
|
196
|
-
https://opalrb.com[Opal] (Ruby on JavaScript).
|
|
195
|
+
Does not require native extensions.
|
|
197
196
|
Requires the `oga` gem.
|
|
198
197
|
|
|
199
198
|
Ox::
|
|
@@ -206,6 +205,8 @@ REXML::
|
|
|
206
205
|
(optional)
|
|
207
206
|
Pure Ruby XML parser, bundled as a default gem with Ruby.
|
|
208
207
|
Moved from standard library to a default gem in Ruby 3.0.
|
|
208
|
+
Opal-compatible: Opal reimplements `strscan` and `stringio` in its stdlib,
|
|
209
|
+
enabling REXML to compile cleanly to JavaScript.
|
|
209
210
|
Requires the `rexml` gem (bundled with Ruby by default).
|
|
210
211
|
|
|
211
212
|
|
data/docs/index.adoc
CHANGED
|
@@ -19,6 +19,7 @@ Lutaml::Model is a Ruby library for creating information models with attributes
|
|
|
19
19
|
* **Multi-format serialization** - XML, JSON, YAML, TOML, JSON-LD, Turtle, Hash, YAML Stream
|
|
20
20
|
* **XML namespace support** - Full W3C namespace implementation
|
|
21
21
|
* **Linked Data support** - RDF namespaces, JSON-LD, and Turtle serialization
|
|
22
|
+
* **Opal support** - Run in the browser via Opal (Ruby to JavaScript) with REXML adapter
|
|
22
23
|
* **Validation** - Built-in validation with custom rules
|
|
23
24
|
* **Schema generation** - Generate XSD, JSON Schema, YAML Schema
|
|
24
25
|
* **Polymorphism** - Handle multiple types elegantly
|
|
@@ -35,7 +35,7 @@ module Lutaml
|
|
|
35
35
|
value = hash[rule.predicate_name]
|
|
36
36
|
next if value.nil?
|
|
37
37
|
|
|
38
|
-
attrs[rule.to] = if rule.lang_tagged && value.is_a?(Hash)
|
|
38
|
+
attrs[rule.to] = if rule.kind == :lang_tagged && value.is_a?(Hash)
|
|
39
39
|
flatten_language_map(value)
|
|
40
40
|
else
|
|
41
41
|
value
|
|
@@ -61,9 +61,8 @@ module Lutaml
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
mapping.rdf_members.each do |member_rule|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
member_mapping = member.class.mappings[:jsonld]
|
|
64
|
+
each_member(instance, member_rule) do |member|
|
|
65
|
+
member_mapping = member_mapping_for(member, :jsonld)
|
|
67
66
|
next unless member_mapping
|
|
68
67
|
|
|
69
68
|
resource = build_resource_data(member_mapping, member)
|
|
@@ -78,13 +77,12 @@ module Lutaml
|
|
|
78
77
|
context_hash = build_context_from_mapping(mapping).to_hash
|
|
79
78
|
|
|
80
79
|
mapping.rdf_members.each do |member_rule|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
member_mapping = collection.first.class.mappings[:jsonld]
|
|
85
|
-
next unless member_mapping
|
|
80
|
+
each_member(instance, member_rule) do |member|
|
|
81
|
+
member_mapping = member_mapping_for(member, :jsonld)
|
|
82
|
+
next unless member_mapping
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
context_hash.merge!(build_context_from_mapping(member_mapping).to_hash)
|
|
85
|
+
end
|
|
88
86
|
end
|
|
89
87
|
|
|
90
88
|
context_hash
|
|
@@ -94,17 +92,30 @@ module Lutaml
|
|
|
94
92
|
context = Context.new
|
|
95
93
|
mapping.namespace_set.each { |ns| context.prefix(ns) }
|
|
96
94
|
mapping.rdf_predicates.each do |pred|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
container: :language)
|
|
101
|
-
else
|
|
102
|
-
context.term(pred.predicate_name, id: pred.uri)
|
|
103
|
-
end
|
|
95
|
+
options = { id: pred.uri }
|
|
96
|
+
term_options = context_term_options(pred)
|
|
97
|
+
context.term(pred.predicate_name, **options, **term_options)
|
|
104
98
|
end
|
|
99
|
+
|
|
100
|
+
mapping.rdf_members.each do |member_rule|
|
|
101
|
+
next unless member_rule.linked?
|
|
102
|
+
|
|
103
|
+
context.term(member_rule.predicate_name.to_s,
|
|
104
|
+
id: member_rule.linked_predicate_uri,
|
|
105
|
+
type: "@id")
|
|
106
|
+
end
|
|
107
|
+
|
|
105
108
|
context
|
|
106
109
|
end
|
|
107
110
|
|
|
111
|
+
def context_term_options(rule)
|
|
112
|
+
case rule.kind
|
|
113
|
+
when :uri_reference then { type: "@id" }
|
|
114
|
+
when :lang_tagged then { container: :language }
|
|
115
|
+
else {}
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
108
119
|
def build_resource_object(mapping, instance)
|
|
109
120
|
context = build_context_from_mapping(mapping).to_hash
|
|
110
121
|
data = build_resource_data(mapping, instance)
|
|
@@ -114,8 +125,12 @@ module Lutaml
|
|
|
114
125
|
def build_resource_data(mapping, instance)
|
|
115
126
|
result = {}
|
|
116
127
|
|
|
117
|
-
if mapping.rdf_type
|
|
118
|
-
result["@type"] =
|
|
128
|
+
if mapping.rdf_type.any?
|
|
129
|
+
result["@type"] = if mapping.rdf_type.length == 1
|
|
130
|
+
mapping.rdf_type.first
|
|
131
|
+
else
|
|
132
|
+
mapping.rdf_type
|
|
133
|
+
end
|
|
119
134
|
end
|
|
120
135
|
|
|
121
136
|
if mapping.rdf_subject
|
|
@@ -127,16 +142,47 @@ module Lutaml
|
|
|
127
142
|
next if value.nil?
|
|
128
143
|
next if value.is_a?(String) && value.empty?
|
|
129
144
|
|
|
130
|
-
result[rule.predicate_name] =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
145
|
+
result[rule.predicate_name] = serialize_value(value, rule)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
mapping.rdf_members.each do |member_rule|
|
|
149
|
+
next unless member_rule.linked?
|
|
150
|
+
|
|
151
|
+
member_refs = collect_member_references(instance, member_rule)
|
|
152
|
+
next if member_refs.empty?
|
|
153
|
+
|
|
154
|
+
result[member_rule.predicate_name.to_s] = member_refs
|
|
135
155
|
end
|
|
136
156
|
|
|
137
157
|
result
|
|
138
158
|
end
|
|
139
159
|
|
|
160
|
+
def collect_member_references(instance, member_rule)
|
|
161
|
+
refs = []
|
|
162
|
+
each_member(instance, member_rule) do |member|
|
|
163
|
+
member_mapping = member_mapping_for(member, :jsonld)
|
|
164
|
+
next unless member_mapping
|
|
165
|
+
|
|
166
|
+
refs << { "@id" => resolve_subject_uri(member_mapping, member) }
|
|
167
|
+
end
|
|
168
|
+
refs
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def serialize_value(value, rule)
|
|
172
|
+
case rule.kind
|
|
173
|
+
when :uri_reference then serialize_uri_reference(value)
|
|
174
|
+
when :lang_tagged then build_language_map(value)
|
|
175
|
+
else serialize_rdf_value(value)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def serialize_uri_reference(value)
|
|
180
|
+
case value
|
|
181
|
+
when Array then value.map { |v| { "@id" => v.to_s } }
|
|
182
|
+
else { "@id" => value.to_s }
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
140
186
|
def build_language_map(values)
|
|
141
187
|
case values
|
|
142
188
|
when Array
|
|
@@ -218,17 +218,17 @@ module Lutaml
|
|
|
218
218
|
end
|
|
219
219
|
|
|
220
220
|
# Writers for deep_dup in subclasses
|
|
221
|
-
attr_writer :
|
|
221
|
+
attr_writer :register_mappings
|
|
222
222
|
|
|
223
223
|
def deep_dup
|
|
224
|
-
|
|
224
|
+
dup_instance.tap do |new_mapping|
|
|
225
225
|
new_mapping.mappings = duplicate_mappings
|
|
226
226
|
new_mapping.register_mappings = Lutaml::Model::Utils.deep_dup(@register_mappings)
|
|
227
227
|
end
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
-
def
|
|
231
|
-
|
|
230
|
+
def dup_instance
|
|
231
|
+
self.class.new(@format)
|
|
232
232
|
end
|
|
233
233
|
|
|
234
234
|
def find_by_to(to)
|
|
@@ -370,15 +370,12 @@ module Lutaml
|
|
|
370
370
|
|
|
371
371
|
# Detect available XML adapter.
|
|
372
372
|
#
|
|
373
|
-
#
|
|
373
|
+
# Delegates to moxml which is the authority on XML adapter
|
|
374
|
+
# availability and platform constraints (Opal, MRI, etc.).
|
|
375
|
+
#
|
|
376
|
+
# @return [Symbol] adapter type name
|
|
374
377
|
def detect_xml_adapter
|
|
375
|
-
|
|
376
|
-
return :nokogiri if Utils.safe_load("nokogiri", :Nokogiri)
|
|
377
|
-
return :ox if Utils.safe_load("ox", :Ox)
|
|
378
|
-
return :oga if Utils.safe_load("oga", :Oga)
|
|
379
|
-
return :rexml if Utils.safe_load("rexml", :REXML)
|
|
380
|
-
|
|
381
|
-
nil
|
|
378
|
+
Moxml::Config.runtime_default_adapter
|
|
382
379
|
end
|
|
383
380
|
|
|
384
381
|
# Detect available TOML adapter.
|
|
@@ -3,6 +3,8 @@ module Lutaml
|
|
|
3
3
|
class Mapping
|
|
4
4
|
include DeepDupable
|
|
5
5
|
|
|
6
|
+
attr_writer :mappings
|
|
7
|
+
|
|
6
8
|
def initialize
|
|
7
9
|
@mappings = []
|
|
8
10
|
@listeners = {} # target => [Listener, ...]
|
|
@@ -11,6 +13,16 @@ module Lutaml
|
|
|
11
13
|
@mappings_imported = ::Hash.new { |h, k| h[k] = false }
|
|
12
14
|
end
|
|
13
15
|
|
|
16
|
+
def deep_dup
|
|
17
|
+
duped = self.class.new
|
|
18
|
+
duped.mappings = duplicate_mappings
|
|
19
|
+
duped
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def duplicate_mappings
|
|
23
|
+
Lutaml::Model::Utils.deep_dup(@mappings)
|
|
24
|
+
end
|
|
25
|
+
|
|
14
26
|
# Get listeners for a specific target (element name/key).
|
|
15
27
|
#
|
|
16
28
|
# @param target [String, Symbol] The element name or key
|
data/lib/lutaml/model/store.rb
CHANGED
|
@@ -5,9 +5,15 @@ require "weakref"
|
|
|
5
5
|
module Lutaml
|
|
6
6
|
module Model
|
|
7
7
|
class Store
|
|
8
|
-
# Compact dead WeakRef shells
|
|
8
|
+
# Compact dead WeakRef shells once a class bucket grows past this size.
|
|
9
9
|
COMPACTION_THRESHOLD = 1000
|
|
10
10
|
|
|
11
|
+
# Once the threshold is exceeded, only compact every Nth subsequent
|
|
12
|
+
# register call. Amortises the O(N) reject! over N inserts so
|
|
13
|
+
# register stays O(1) per call rather than O(N) per call (O(N^2)
|
|
14
|
+
# cumulatively for the class).
|
|
15
|
+
COMPACTION_INTERVAL = 1000
|
|
16
|
+
|
|
11
17
|
class << self
|
|
12
18
|
def instance
|
|
13
19
|
@instance ||= new
|
|
@@ -39,14 +45,17 @@ module Lutaml
|
|
|
39
45
|
# Nested index: { model_key => { reference_key => { value => WeakRef(object) } } }
|
|
40
46
|
# Grouped by model_key so register only iterates this class's own indices.
|
|
41
47
|
@index = {}
|
|
48
|
+
@inserts_since_compaction = ::Hash.new(0)
|
|
49
|
+
@compaction_count = 0
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
def register(object)
|
|
45
53
|
model_key = object.class.to_s
|
|
46
54
|
refs = @store[model_key]
|
|
47
55
|
refs << WeakRef.new(object)
|
|
56
|
+
@inserts_since_compaction[model_key] += 1
|
|
48
57
|
|
|
49
|
-
compact_if_needed(refs)
|
|
58
|
+
compact_if_needed(refs, model_key)
|
|
50
59
|
|
|
51
60
|
update_existing_indices(object, model_key)
|
|
52
61
|
end
|
|
@@ -63,12 +72,16 @@ module Lutaml
|
|
|
63
72
|
entry = model_indices[reference_key][reference_value]
|
|
64
73
|
return nil unless entry
|
|
65
74
|
|
|
66
|
-
dereference(entry)
|
|
75
|
+
obj = dereference(entry)
|
|
76
|
+
model_indices[reference_key].delete(reference_value) unless obj
|
|
77
|
+
obj
|
|
67
78
|
end
|
|
68
79
|
|
|
69
80
|
def clear
|
|
70
81
|
@store = ::Hash.new { |hash, key| hash[key] = [] }
|
|
71
82
|
@index = {}
|
|
83
|
+
@inserts_since_compaction = ::Hash.new(0)
|
|
84
|
+
@compaction_count = 0
|
|
72
85
|
end
|
|
73
86
|
|
|
74
87
|
def store
|
|
@@ -81,6 +94,22 @@ module Lutaml
|
|
|
81
94
|
end
|
|
82
95
|
end
|
|
83
96
|
|
|
97
|
+
def refs_for(model_key)
|
|
98
|
+
@store[model_key]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def inserts_since_compaction
|
|
102
|
+
@inserts_since_compaction
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def compaction_count
|
|
106
|
+
@compaction_count
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def index_entry_count(model_key)
|
|
110
|
+
@index[model_key]&.sum { |_reference_key, entries| entries.size } || 0
|
|
111
|
+
end
|
|
112
|
+
|
|
84
113
|
private
|
|
85
114
|
|
|
86
115
|
def ensure_model_index(model_key)
|
|
@@ -120,14 +149,32 @@ module Lutaml
|
|
|
120
149
|
nil
|
|
121
150
|
end
|
|
122
151
|
|
|
123
|
-
def compact_if_needed(refs)
|
|
152
|
+
def compact_if_needed(refs, model_key)
|
|
124
153
|
return unless refs.size > COMPACTION_THRESHOLD
|
|
154
|
+
return unless @inserts_since_compaction[model_key] >= COMPACTION_INTERVAL
|
|
125
155
|
|
|
156
|
+
@inserts_since_compaction[model_key] = 0
|
|
157
|
+
@compaction_count += 1
|
|
126
158
|
refs.reject! do |ref|
|
|
127
159
|
!ref.weakref_alive?
|
|
128
160
|
rescue WeakRef::RefError
|
|
129
161
|
true
|
|
130
162
|
end
|
|
163
|
+
prune_index(model_key)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def prune_index(model_key)
|
|
167
|
+
model_indices = @index[model_key]
|
|
168
|
+
return unless model_indices
|
|
169
|
+
|
|
170
|
+
model_indices.delete_if do |_reference_key, entries|
|
|
171
|
+
entries.delete_if do |_value, ref|
|
|
172
|
+
!ref.weakref_alive?
|
|
173
|
+
rescue WeakRef::RefError
|
|
174
|
+
true
|
|
175
|
+
end
|
|
176
|
+
entries.empty?
|
|
177
|
+
end
|
|
131
178
|
end
|
|
132
179
|
end
|
|
133
180
|
end
|
data/lib/lutaml/model/version.rb
CHANGED
data/lib/lutaml/rdf/mapping.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Lutaml
|
|
|
10
10
|
super
|
|
11
11
|
@namespace_set = Lutaml::Rdf::NamespaceSet.new
|
|
12
12
|
@rdf_subject = nil
|
|
13
|
-
@rdf_type =
|
|
13
|
+
@rdf_type = []
|
|
14
14
|
@rdf_predicates = []
|
|
15
15
|
@rdf_members = []
|
|
16
16
|
end
|
|
@@ -24,20 +24,26 @@ module Lutaml
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def type(value)
|
|
27
|
-
@rdf_type = value
|
|
27
|
+
@rdf_type = Array(value)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def predicate(name, namespace:, to:, lang_tagged: false
|
|
30
|
+
def predicate(name, namespace:, to:, lang_tagged: false,
|
|
31
|
+
uri_reference: false)
|
|
31
32
|
@rdf_predicates << Lutaml::Rdf::MappingRule.new(
|
|
32
33
|
name,
|
|
33
34
|
namespace: namespace,
|
|
34
35
|
to: to,
|
|
35
36
|
lang_tagged: lang_tagged,
|
|
37
|
+
uri_reference: uri_reference,
|
|
36
38
|
)
|
|
37
39
|
end
|
|
38
40
|
|
|
39
|
-
def members(attr_name)
|
|
40
|
-
@rdf_members << Lutaml::Rdf::MemberRule.new(
|
|
41
|
+
def members(attr_name, predicate_name: nil, namespace: nil)
|
|
42
|
+
@rdf_members << Lutaml::Rdf::MemberRule.new(
|
|
43
|
+
attr_name,
|
|
44
|
+
predicate_name: predicate_name,
|
|
45
|
+
namespace: namespace,
|
|
46
|
+
)
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
def mappings(_register_id = nil)
|
|
@@ -57,14 +63,14 @@ module Lutaml
|
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
def deep_dup
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
dup
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def initialize_copy(source)
|
|
70
|
+
super
|
|
71
|
+
@rdf_type = source.rdf_type.dup
|
|
72
|
+
@rdf_predicates = source.rdf_predicates.dup
|
|
73
|
+
@rdf_members = source.rdf_members.dup
|
|
68
74
|
end
|
|
69
75
|
end
|
|
70
76
|
end
|