lutaml-model 0.8.4 → 0.8.6
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-tests.yml +5 -0
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +91 -22
- data/Gemfile +2 -0
- data/README.adoc +114 -2
- data/docs/_guides/index.adoc +18 -0
- data/docs/_guides/jsonld-serialization.adoc +217 -0
- data/docs/_guides/rdf-serialization.adoc +344 -0
- data/docs/_guides/turtle-serialization.adoc +224 -0
- data/docs/_migrations/0-8-0-namespace-restructuring.adoc +90 -0
- data/docs/_pages/serialization_adapters.adoc +31 -0
- data/docs/_references/index.adoc +1 -0
- data/docs/_references/rdf-namespaces.adoc +243 -0
- data/docs/index.adoc +3 -2
- data/lib/lutaml/jsonld/adapter.rb +23 -0
- data/lib/lutaml/jsonld/context.rb +69 -0
- data/lib/lutaml/jsonld/term_definition.rb +39 -0
- data/lib/lutaml/jsonld/transform.rb +174 -0
- data/lib/lutaml/jsonld.rb +23 -0
- data/lib/lutaml/model/format_registry.rb +10 -1
- data/lib/lutaml/model/serialize/format_conversion.rb +17 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +6 -0
- data/lib/lutaml/rdf/error.rb +7 -0
- data/lib/lutaml/rdf/iri.rb +44 -0
- data/lib/lutaml/rdf/language_tagged.rb +11 -0
- data/lib/lutaml/rdf/literal.rb +62 -0
- data/lib/lutaml/rdf/mapping.rb +71 -0
- data/lib/lutaml/rdf/mapping_rule.rb +35 -0
- data/lib/lutaml/rdf/member_rule.rb +13 -0
- data/lib/lutaml/rdf/namespace.rb +58 -0
- data/lib/lutaml/rdf/namespace_set.rb +69 -0
- data/lib/lutaml/rdf/namespaces/dcterms_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/owl_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/rdf_namespace.rb +14 -0
- data/lib/lutaml/rdf/namespaces/rdfs_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/skos_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces/xsd_namespace.rb +12 -0
- data/lib/lutaml/rdf/namespaces.rb +14 -0
- data/lib/lutaml/rdf/transform.rb +36 -0
- data/lib/lutaml/rdf.rb +19 -0
- data/lib/lutaml/turtle/adapter.rb +35 -0
- data/lib/lutaml/turtle/mapping.rb +7 -0
- data/lib/lutaml/turtle/transform.rb +158 -0
- data/lib/lutaml/turtle.rb +22 -0
- data/lib/lutaml/xml/adapter/adapter_helpers.rb +1 -42
- data/lib/lutaml/xml/adapter/base_adapter.rb +48 -458
- data/lib/lutaml/xml/adapter/namespace_data.rb +0 -17
- data/lib/lutaml/xml/adapter/namespace_uri_collector.rb +71 -0
- data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +5 -1110
- data/lib/lutaml/xml/adapter/oga_adapter.rb +6 -846
- data/lib/lutaml/xml/adapter/ox_adapter.rb +7 -884
- data/lib/lutaml/xml/adapter/plan_based_builder.rb +929 -0
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +10 -864
- data/lib/lutaml/xml/adapter/xml_parser.rb +86 -0
- data/lib/lutaml/xml/adapter/xml_serializer.rb +291 -0
- data/lib/lutaml/xml/adapter.rb +0 -1
- data/lib/lutaml/xml/adapter_element.rb +7 -1
- data/lib/lutaml/xml/builder/base.rb +0 -1
- data/lib/lutaml/xml/data_model.rb +9 -1
- data/lib/lutaml/xml/document.rb +3 -1
- data/lib/lutaml/xml/element.rb +13 -10
- data/lib/lutaml/xml/serialization/format_conversion.rb +19 -42
- data/lib/lutaml/xml/serialization/instance_methods.rb +26 -35
- data/lib/lutaml/xml/transformation/custom_method_wrapper.rb +34 -55
- data/lib/lutaml/xml/transformation/rule_applier.rb +1 -1
- data/lib/lutaml/xml/xml_element.rb +24 -20
- data/spec/lutaml/integration/edge_cases_spec.rb +109 -0
- data/spec/lutaml/integration/multi_format_spec.rb +106 -0
- data/spec/lutaml/integration/round_trip_spec.rb +170 -0
- data/spec/lutaml/jsonld/adapter_spec.rb +46 -0
- data/spec/lutaml/jsonld/context_spec.rb +114 -0
- data/spec/lutaml/jsonld/term_definition_spec.rb +55 -0
- data/spec/lutaml/jsonld/transform_spec.rb +211 -0
- data/spec/lutaml/rdf/graph_serialization_spec.rb +137 -0
- data/spec/lutaml/rdf/iri_spec.rb +73 -0
- data/spec/lutaml/rdf/literal_spec.rb +98 -0
- data/spec/lutaml/rdf/mapping_spec.rb +164 -0
- data/spec/lutaml/rdf/member_rule_spec.rb +17 -0
- data/spec/lutaml/rdf/namespace_set_spec.rb +115 -0
- data/spec/lutaml/rdf/namespace_spec.rb +241 -0
- data/spec/lutaml/rdf/rdf_transform_spec.rb +82 -0
- data/spec/lutaml/turtle/adapter_spec.rb +47 -0
- data/spec/lutaml/turtle/mapping_spec.rb +123 -0
- data/spec/lutaml/turtle/transform_spec.rb +273 -0
- data/spec/lutaml/xml/adapter/base_adapter_regression_spec.rb +151 -0
- data/spec/lutaml/xml/adapter/order_spec.rb +150 -0
- data/spec/lutaml/xml/clear_parse_state_spec.rb +139 -0
- data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +0 -2
- data/spec/lutaml/xml/schema/compiler_spec.rb +75 -69
- data/spec/lutaml/xml/transformation/custom_method_wrapper_spec.rb +213 -14
- metadata +58 -3
- data/lib/lutaml/xml/adapter/xml_serialization.rb +0 -145
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Rdf
|
|
5
|
+
class Namespace
|
|
6
|
+
class << self
|
|
7
|
+
def uri(value = nil)
|
|
8
|
+
return @uri if value.nil?
|
|
9
|
+
if @uri
|
|
10
|
+
raise FrozenError,
|
|
11
|
+
"#{name}.uri is already set to #{@uri.inspect}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@uri = value.freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def prefix(value = nil)
|
|
18
|
+
return @prefix if value.nil?
|
|
19
|
+
if @prefix
|
|
20
|
+
raise FrozenError,
|
|
21
|
+
"#{name}.prefix is already set to #{@prefix.inspect}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@prefix = value.to_s.freeze
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def [](local_name)
|
|
28
|
+
"#{uri}#{local_name}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def prefixed(local_name)
|
|
32
|
+
"#{prefix}:#{local_name}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def resolve_compact_iri(compact_iri, namespaces)
|
|
36
|
+
return compact_iri unless compact_iri.include?(":")
|
|
37
|
+
|
|
38
|
+
pfx, local = compact_iri.split(":", 2)
|
|
39
|
+
ns = namespaces.find { |n| n.prefix == pfx }
|
|
40
|
+
ns ? ns[local] : compact_iri
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def ==(other)
|
|
44
|
+
other.is_a?(Class) && other < Namespace &&
|
|
45
|
+
other.uri == uri && other.prefix == prefix
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def hash
|
|
49
|
+
[uri, prefix].hash
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_s
|
|
53
|
+
"#{name}(prefix: #{prefix.inspect}, uri: #{uri.inspect})"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Rdf
|
|
5
|
+
class NamespaceSet
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
def initialize(*namespace_classes)
|
|
9
|
+
@by_prefix = {}
|
|
10
|
+
namespace_classes.each { |ns| add(ns) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add(namespace_class)
|
|
14
|
+
pfx = namespace_class.prefix
|
|
15
|
+
if @by_prefix.key?(pfx) && @by_prefix[pfx] != namespace_class
|
|
16
|
+
raise ArgumentError,
|
|
17
|
+
"Prefix '#{pfx}' conflicts: #{@by_prefix[pfx].name} vs #{namespace_class.name}"
|
|
18
|
+
end
|
|
19
|
+
@by_prefix[pfx] = namespace_class
|
|
20
|
+
self
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def [](prefix)
|
|
24
|
+
@by_prefix[prefix]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def resolve_compact_iri(compact_iri)
|
|
28
|
+
Namespace.resolve_compact_iri(compact_iri, to_a)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def compact(full_uri)
|
|
32
|
+
each do |ns|
|
|
33
|
+
next unless full_uri.start_with?(ns.uri)
|
|
34
|
+
|
|
35
|
+
local = full_uri.delete_prefix(ns.uri)
|
|
36
|
+
return ns.prefixed(local)
|
|
37
|
+
end
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def each(&)
|
|
42
|
+
@by_prefix.each_value(&)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def size
|
|
46
|
+
@by_prefix.size
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def empty?
|
|
50
|
+
@by_prefix.empty?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_a
|
|
54
|
+
@by_prefix.values
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_hash
|
|
58
|
+
@by_prefix.transform_values(&:uri)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def merge(other_set)
|
|
62
|
+
return self if equal?(other_set)
|
|
63
|
+
|
|
64
|
+
other_set.each { |ns| add(ns) }
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Rdf
|
|
5
|
+
module Namespaces
|
|
6
|
+
class RdfSyntaxNamespace < Lutaml::Rdf::Namespace
|
|
7
|
+
uri "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
8
|
+
prefix "rdf"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
RdfNamespace = RdfSyntaxNamespace
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Rdf
|
|
5
|
+
module Namespaces
|
|
6
|
+
autoload :SkosNamespace, "#{__dir__}/namespaces/skos_namespace"
|
|
7
|
+
autoload :DctermsNamespace, "#{__dir__}/namespaces/dcterms_namespace"
|
|
8
|
+
autoload :RdfNamespace, "#{__dir__}/namespaces/rdf_namespace"
|
|
9
|
+
autoload :RdfsNamespace, "#{__dir__}/namespaces/rdfs_namespace"
|
|
10
|
+
autoload :OwlNamespace, "#{__dir__}/namespaces/owl_namespace"
|
|
11
|
+
autoload :XsdNamespace, "#{__dir__}/namespaces/xsd_namespace"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Rdf
|
|
5
|
+
class Transform < Lutaml::Model::Transform
|
|
6
|
+
protected
|
|
7
|
+
|
|
8
|
+
def resolve_subject_uri(mapping, instance)
|
|
9
|
+
mapping.rdf_subject&.call(instance)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def resolve_type_uri(mapping)
|
|
13
|
+
return unless mapping.rdf_type
|
|
14
|
+
|
|
15
|
+
mapping.namespace_set.resolve_compact_iri(mapping.rdf_type)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def resolve_type_compact(mapping)
|
|
19
|
+
mapping.rdf_type
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def build_instance(attrs, options)
|
|
23
|
+
child_register = Lutaml::Model::Register.resolve_for_child(
|
|
24
|
+
model_class, lutaml_register
|
|
25
|
+
)
|
|
26
|
+
instance = model_class.new(attrs.merge(lutaml_register: child_register))
|
|
27
|
+
root_and_parent_assignment(instance, options)
|
|
28
|
+
instance
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def extract_language(value)
|
|
32
|
+
value.language_tag if value.is_a?(Lutaml::Rdf::LanguageTagged)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/lutaml/rdf.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "model"
|
|
4
|
+
|
|
5
|
+
module Lutaml
|
|
6
|
+
module Rdf
|
|
7
|
+
autoload :Error, "#{__dir__}/rdf/error"
|
|
8
|
+
autoload :Iri, "#{__dir__}/rdf/iri"
|
|
9
|
+
autoload :LanguageTagged, "#{__dir__}/rdf/language_tagged"
|
|
10
|
+
autoload :Literal, "#{__dir__}/rdf/literal"
|
|
11
|
+
autoload :Namespace, "#{__dir__}/rdf/namespace"
|
|
12
|
+
autoload :NamespaceSet, "#{__dir__}/rdf/namespace_set"
|
|
13
|
+
autoload :Mapping, "#{__dir__}/rdf/mapping"
|
|
14
|
+
autoload :MappingRule, "#{__dir__}/rdf/mapping_rule"
|
|
15
|
+
autoload :MemberRule, "#{__dir__}/rdf/member_rule"
|
|
16
|
+
autoload :Namespaces, "#{__dir__}/rdf/namespaces"
|
|
17
|
+
autoload :Transform, "#{__dir__}/rdf/transform"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Turtle
|
|
5
|
+
class Adapter
|
|
6
|
+
attr_reader :data, :register
|
|
7
|
+
|
|
8
|
+
def initialize(data, register: nil)
|
|
9
|
+
@data = data
|
|
10
|
+
@register = register || Lutaml::Model::Config.default_register
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.parse(turtle_string, _options = {})
|
|
14
|
+
require "rdf/turtle"
|
|
15
|
+
graph = RDF::Graph.new
|
|
16
|
+
RDF::Turtle::Reader.new(turtle_string).each_statement do |stmt|
|
|
17
|
+
graph << stmt
|
|
18
|
+
end
|
|
19
|
+
graph
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def to_turtle(_options = {})
|
|
23
|
+
require "rdf/turtle"
|
|
24
|
+
case data
|
|
25
|
+
when String
|
|
26
|
+
data
|
|
27
|
+
when RDF::Enumerable
|
|
28
|
+
RDF::Turtle::Writer.buffer { |w| data.each_statement { |s| w << s } }
|
|
29
|
+
else
|
|
30
|
+
data.to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lutaml
|
|
4
|
+
module Turtle
|
|
5
|
+
class MissingSubjectError < Lutaml::Rdf::Error; end
|
|
6
|
+
|
|
7
|
+
class Transform < Lutaml::Rdf::Transform
|
|
8
|
+
def model_to_data(instance, _format, options = {})
|
|
9
|
+
require "rdf/turtle"
|
|
10
|
+
mapping = extract_turtle_mapping(options)
|
|
11
|
+
return "" unless mapping
|
|
12
|
+
|
|
13
|
+
if !mapping.rdf_subject && mapping.rdf_predicates.any? && mapping.rdf_members.empty?
|
|
14
|
+
raise MissingSubjectError,
|
|
15
|
+
"Turtle mapping requires a subject block"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
graph = build_graph(mapping, instance)
|
|
19
|
+
return "" if graph.empty?
|
|
20
|
+
|
|
21
|
+
prefixes = build_prefixes(mapping, instance)
|
|
22
|
+
RDF::Turtle::Writer.buffer(prefixes: prefixes) do |writer|
|
|
23
|
+
graph.each_statement { |stmt| writer << stmt }
|
|
24
|
+
end.strip
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def data_to_model(data, _format, options = {})
|
|
28
|
+
require "rdf/turtle"
|
|
29
|
+
mapping = extract_turtle_mapping(options)
|
|
30
|
+
unless mapping&.rdf_subject
|
|
31
|
+
raise MissingSubjectError,
|
|
32
|
+
"Turtle mapping requires a subject block"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
graph = data.is_a?(RDF::Graph) ? data : Lutaml::Turtle::Adapter.parse(data)
|
|
36
|
+
attrs = extract_attributes(graph, mapping)
|
|
37
|
+
build_instance(attrs, options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def extract_turtle_mapping(options)
|
|
43
|
+
options[:mappings] || mappings_for(:turtle, lutaml_register)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def build_graph(mapping, instance)
|
|
47
|
+
graph = RDF::Graph.new
|
|
48
|
+
|
|
49
|
+
has_predicates_or_type = mapping.rdf_type || mapping.rdf_predicates.any?
|
|
50
|
+
|
|
51
|
+
if has_predicates_or_type
|
|
52
|
+
subject_uri = if mapping.rdf_subject
|
|
53
|
+
RDF::URI(resolve_subject_uri(mapping, instance))
|
|
54
|
+
else
|
|
55
|
+
RDF::Node.new
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if mapping.rdf_type
|
|
59
|
+
type_uri = RDF::URI(resolve_type_uri(mapping))
|
|
60
|
+
graph << RDF::Statement.new(subject_uri, RDF.type, type_uri)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
mapping.rdf_predicates.each do |rule|
|
|
64
|
+
value = instance.public_send(rule.to)
|
|
65
|
+
next if value.nil?
|
|
66
|
+
|
|
67
|
+
Array(value).each do |v|
|
|
68
|
+
object = build_rdf_object(v, rule)
|
|
69
|
+
graph << RDF::Statement.new(subject_uri, RDF::URI(rule.uri),
|
|
70
|
+
object)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
mapping.rdf_members.each do |member_rule|
|
|
76
|
+
collection = Array(instance.public_send(member_rule.attr_name))
|
|
77
|
+
collection.each do |member|
|
|
78
|
+
member_mapping = member.class.mappings[:turtle]
|
|
79
|
+
next unless member_mapping
|
|
80
|
+
|
|
81
|
+
graph << build_graph(member_mapping, member)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
graph
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def build_rdf_object(value, rule)
|
|
89
|
+
if rule.lang_tagged
|
|
90
|
+
lang = extract_language(value)
|
|
91
|
+
RDF::Literal.new(value.to_s, language: lang)
|
|
92
|
+
else
|
|
93
|
+
case value
|
|
94
|
+
when Integer then RDF::Literal.new(value, datatype: RDF::XSD.integer)
|
|
95
|
+
when Float then RDF::Literal.new(value, datatype: RDF::XSD.double)
|
|
96
|
+
when TrueClass, FalseClass then RDF::Literal.new(value, datatype: RDF::XSD.boolean)
|
|
97
|
+
else RDF::Literal.new(value.to_s)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def build_prefixes(mapping, instance)
|
|
103
|
+
ns_set = mapping.namespace_set
|
|
104
|
+
|
|
105
|
+
mapping.rdf_members.each do |member_rule|
|
|
106
|
+
collection = Array(instance.public_send(member_rule.attr_name))
|
|
107
|
+
next if collection.empty?
|
|
108
|
+
|
|
109
|
+
member_mapping = collection.first.class.mappings[:turtle]
|
|
110
|
+
next unless member_mapping
|
|
111
|
+
|
|
112
|
+
ns_set = ns_set.merge(member_mapping.namespace_set)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
ns_set.each.with_object({}) do |ns, h|
|
|
116
|
+
h[ns.prefix.to_sym] = ns.uri if ns.prefix && ns.uri
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def extract_attributes(graph, mapping)
|
|
121
|
+
attrs = {}
|
|
122
|
+
type_uri = resolve_type_uri(mapping)
|
|
123
|
+
|
|
124
|
+
matching_subjects = find_subjects_by_type(graph, type_uri)
|
|
125
|
+
|
|
126
|
+
matching_subjects.each do |subject|
|
|
127
|
+
mapping.rdf_predicates.each do |rule|
|
|
128
|
+
stmts = graph.query([subject, RDF::URI(rule.uri), nil])
|
|
129
|
+
next if stmts.empty?
|
|
130
|
+
|
|
131
|
+
values = stmts.map { |s| literal_to_ruby(s.object) }
|
|
132
|
+
attrs[rule.to] = values.length == 1 ? values.first : values
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
attrs
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def find_subjects_by_type(graph, type_uri)
|
|
140
|
+
graph.query([nil, RDF.type, RDF::URI(type_uri)]).map(&:subject).uniq
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def literal_to_ruby(rdf_object)
|
|
144
|
+
case rdf_object
|
|
145
|
+
when RDF::Literal
|
|
146
|
+
case rdf_object.datatype
|
|
147
|
+
when RDF::XSD.integer then rdf_object.value.to_i
|
|
148
|
+
when RDF::XSD.double, RDF::XSD.decimal, RDF::XSD.float then rdf_object.value.to_f
|
|
149
|
+
when RDF::XSD.boolean then rdf_object.value == "true"
|
|
150
|
+
else rdf_object.value
|
|
151
|
+
end
|
|
152
|
+
else
|
|
153
|
+
rdf_object.to_s
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "model"
|
|
4
|
+
require_relative "rdf"
|
|
5
|
+
|
|
6
|
+
module Lutaml
|
|
7
|
+
module Turtle
|
|
8
|
+
autoload :Mapping, "#{__dir__}/turtle/mapping"
|
|
9
|
+
autoload :Transform, "#{__dir__}/turtle/transform"
|
|
10
|
+
autoload :Adapter, "#{__dir__}/turtle/adapter"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Lutaml::Model::FormatRegistry.register(
|
|
15
|
+
:turtle,
|
|
16
|
+
mapping_class: Lutaml::Turtle::Mapping,
|
|
17
|
+
adapter_class: Lutaml::Turtle::Adapter,
|
|
18
|
+
transformer: Lutaml::Turtle::Transform,
|
|
19
|
+
key_value: false,
|
|
20
|
+
rdf: true,
|
|
21
|
+
error_types: ["RDF::ReaderError"],
|
|
22
|
+
)
|
|
@@ -29,7 +29,7 @@ module Lutaml
|
|
|
29
29
|
when Moxml::Cdata
|
|
30
30
|
"#cdata-section"
|
|
31
31
|
when Moxml::ProcessingInstruction
|
|
32
|
-
|
|
32
|
+
element.target
|
|
33
33
|
when Moxml::Comment
|
|
34
34
|
"comment"
|
|
35
35
|
else
|
|
@@ -102,47 +102,6 @@ module Lutaml
|
|
|
102
102
|
def text_node?(node)
|
|
103
103
|
TEXT_CLASSES.include?(node.class)
|
|
104
104
|
end
|
|
105
|
-
|
|
106
|
-
# Build Element object from child node
|
|
107
|
-
#
|
|
108
|
-
# @param child [Moxml::Node] The child node
|
|
109
|
-
# @return [Element] The Element object
|
|
110
|
-
def build_element_from_child(child)
|
|
111
|
-
if text_node?(child)
|
|
112
|
-
Element.new("Text", "text", node_type: :text)
|
|
113
|
-
else
|
|
114
|
-
Element.new("Element", name_of(child), node_type: :element)
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Get the prefix for a namespace URI from hoisted declarations
|
|
119
|
-
#
|
|
120
|
-
# This checks if a namespace URI is already declared in hoisted_declarations
|
|
121
|
-
# and returns the prefix that should be used. This prevents duplicate xmlns
|
|
122
|
-
# declarations when both hoisted_declarations and attribute code try to add
|
|
123
|
-
# the same namespace.
|
|
124
|
-
#
|
|
125
|
-
# @param namespace_uri [String] The namespace URI to look up
|
|
126
|
-
# @param hoisted_declarations [Hash{String, nil => String}] The hoisted declarations
|
|
127
|
-
# (keys are prefixes or nil for default, values are URIs)
|
|
128
|
-
# @return [String, nil] The prefix if found, nil if not hoisted
|
|
129
|
-
def prefix_for_namespace_uri(namespace_uri, hoisted_declarations)
|
|
130
|
-
return nil unless hoisted_declarations
|
|
131
|
-
|
|
132
|
-
hoisted_declarations.each do |prefix, uri|
|
|
133
|
-
return prefix if uri == namespace_uri
|
|
134
|
-
end
|
|
135
|
-
nil
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Check if a namespace URI is already hoisted
|
|
139
|
-
#
|
|
140
|
-
# @param namespace_uri [String] The namespace URI to check
|
|
141
|
-
# @param hoisted_declarations [Hash{String, nil => String}] The hoisted declarations
|
|
142
|
-
# @return [Boolean] true if the namespace is already hoisted
|
|
143
|
-
def namespace_uri_hoisted?(namespace_uri, hoisted_declarations)
|
|
144
|
-
prefix_for_namespace_uri(namespace_uri, hoisted_declarations) != nil
|
|
145
|
-
end
|
|
146
105
|
end
|
|
147
106
|
end
|
|
148
107
|
end
|