rdf-serializers 0.0.7 → 0.0.11
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/lib/rdf/serializers/config.rb +24 -0
- data/lib/rdf/serializers/data_type_helper.rb +35 -0
- data/lib/rdf/serializers/hdnjson_parser.rb +70 -0
- data/lib/rdf/serializers/hextuple_serializer.rb +71 -0
- data/lib/rdf/serializers/list_serializer.rb +19 -0
- data/lib/rdf/serializers/lookup_chain.rb +60 -0
- data/lib/rdf/serializers/nil_serializer.rb +13 -0
- data/lib/rdf/serializers/object_serializer.rb +184 -0
- data/lib/rdf/serializers/relationship.rb +67 -0
- data/lib/rdf/serializers/renderers.rb +31 -5
- data/lib/rdf/serializers/scalar.rb +50 -0
- data/lib/rdf/serializers/serialization_core.rb +121 -0
- data/lib/rdf/serializers/version.rb +1 -1
- data/lib/rdf/serializers.rb +92 -2
- metadata +36 -15
- data/lib/active_model_serializers/adapter/rdf/relationship.rb +0 -70
- data/lib/active_model_serializers/adapter/rdf.rb +0 -193
- data/lib/active_model_serializers/serializer.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d375c9e57bfcdfcaf9bee6372abbf54ea974e1991a34936917e59c1ed67469a8
|
4
|
+
data.tar.gz: 0efed8bc77b7d97301d08ddaf5e5194c6234fd2c203bc1244d0269f06c81becf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6edcec3ce7990cb072e6211ce96452d12dd7c245de15177dffafac781b86e48b3f04e60e01af9853e8523a129cc84210c5e9ed3959a1c192c35184f7379a0f5f
|
7
|
+
data.tar.gz: 807766507554892c53f3dd0c9a3199a9d80b59f8b9c4ddb763b609d57727249f050797dfb55591837ce519b4b31fd3635be96fe14aae48e97dca72e01f997604
|
@@ -14,10 +14,34 @@ module RDF
|
|
14
14
|
include ActiveSupport::Configurable
|
15
15
|
config_accessor :always_include_named_graphs
|
16
16
|
config_accessor :default_graph
|
17
|
+
config_accessor :serializer_lookup_chain
|
18
|
+
config_accessor :serializer_lookup_enabled
|
17
19
|
end
|
18
20
|
|
19
21
|
configure do |config|
|
20
22
|
config.always_include_named_graphs = true
|
23
|
+
|
24
|
+
config.serializer_lookup_enabled = true
|
25
|
+
|
26
|
+
# For configuring how serializers are found.
|
27
|
+
# This should be an array of procs.
|
28
|
+
#
|
29
|
+
# The priority of the output is that the first item
|
30
|
+
# in the evaluated result array will take precedence
|
31
|
+
# over other possible serializer paths.
|
32
|
+
#
|
33
|
+
# i.e.: First match wins.
|
34
|
+
#
|
35
|
+
# @example output
|
36
|
+
# => [
|
37
|
+
# "CustomNamespace::ResourceSerializer",
|
38
|
+
# "ParentSerializer::ResourceSerializer",
|
39
|
+
# "ResourceNamespace::ResourceSerializer" ,
|
40
|
+
# "ResourceSerializer"]
|
41
|
+
#
|
42
|
+
# If CustomNamespace::ResourceSerializer exists, it will be used
|
43
|
+
# for serialization
|
44
|
+
config.serializer_lookup_chain = RDF::Serializers::LookupChain::DEFAULT.dup
|
21
45
|
end
|
22
46
|
end
|
23
47
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
module DataTypeHelper
|
6
|
+
def xsd_to_rdf(xsd, value, **opts) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
7
|
+
klass =
|
8
|
+
case xsd
|
9
|
+
when RDF::XSD[:anyURI]
|
10
|
+
RDF::URI
|
11
|
+
when RDF::XSD[:integer]
|
12
|
+
RDF::Literal::Integer
|
13
|
+
when RDF::XSD[:dateTime]
|
14
|
+
RDF::Literal::DateTime
|
15
|
+
when RDF::XSD[:date]
|
16
|
+
RDF::Literal::Date
|
17
|
+
when RDF::XSD[:boolean]
|
18
|
+
RDF::Literal::Boolean
|
19
|
+
when RDF::XSD[:time]
|
20
|
+
RDF::Literal::Time
|
21
|
+
when RDF::XSD[:long], RDF::XSD[:double]
|
22
|
+
RDF::Literal::Double
|
23
|
+
when RDF::XSD[:decimal]
|
24
|
+
RDF::Literal::Decimal
|
25
|
+
when RDF::XSD[:token]
|
26
|
+
RDF::Literal::Token
|
27
|
+
else
|
28
|
+
RDF::Literal
|
29
|
+
end
|
30
|
+
|
31
|
+
klass.new(value, **opts)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
class HndJSONParser
|
6
|
+
include DataTypeHelper
|
7
|
+
|
8
|
+
HEX_SUBJECT = 0
|
9
|
+
HEX_PREDICATE = 1
|
10
|
+
HEX_OBJECT = 2
|
11
|
+
HEX_DATATYPE = 3
|
12
|
+
HEX_LANGUAGE = 4
|
13
|
+
HEX_GRAPH = 5
|
14
|
+
|
15
|
+
def parse_body(body)
|
16
|
+
body.split("\n").map { |line| parse_hex(JSON.parse(line)) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_hex(hex)
|
20
|
+
subject = parse_subject(hex[HEX_SUBJECT])
|
21
|
+
predicate = RDF::URI(hex[HEX_PREDICATE])
|
22
|
+
object = parse_object(hex[HEX_OBJECT], hex[HEX_DATATYPE], hex[HEX_LANGUAGE])
|
23
|
+
graph = hex[HEX_GRAPH].present? ? RDF::URI(hex[HEX_GRAPH]) : RDF::Serializers.config.default_graph
|
24
|
+
|
25
|
+
RDF::Statement.new(
|
26
|
+
subject,
|
27
|
+
predicate,
|
28
|
+
object,
|
29
|
+
graph_name: graph
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def blank_node(id)
|
36
|
+
@blank_nodes ||= {}
|
37
|
+
@blank_nodes[id] ||= RDF::Node(id)
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_object(value, datatype, language)
|
41
|
+
case datatype
|
42
|
+
when 'http://www.w3.org/1999/02/22-rdf-syntax-ns#namedNode'
|
43
|
+
RDF::URI(value)
|
44
|
+
when 'http://www.w3.org/1999/02/22-rdf-syntax-ns#blankNode'
|
45
|
+
blank_node(value.sub('_:', ''))
|
46
|
+
when language
|
47
|
+
RDF::Literal(value, datatype: RDF.langString, language: language)
|
48
|
+
else
|
49
|
+
xsd_to_rdf(datatype, value, language: language.presence)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def parse_subject(subject)
|
54
|
+
if subject.is_a?(RDF::Resource)
|
55
|
+
subject
|
56
|
+
elsif subject.start_with?('_')
|
57
|
+
blank_node(subject.sub('_:', ''))
|
58
|
+
else
|
59
|
+
RDF::URI(subject)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
def parse(body)
|
65
|
+
new.parse_body(body)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
module HextupleSerializer
|
6
|
+
def iri_from_record(record)
|
7
|
+
return record if record.try(:uri?)
|
8
|
+
|
9
|
+
raise FastJsonapi::MandatoryField, 'record has no iri' unless record.respond_to?(:iri)
|
10
|
+
|
11
|
+
record.iri
|
12
|
+
end
|
13
|
+
|
14
|
+
def normalized_object(object)
|
15
|
+
case object
|
16
|
+
when ::RDF::Term
|
17
|
+
object
|
18
|
+
when ActiveSupport::TimeWithZone
|
19
|
+
::RDF::Literal(object.to_datetime)
|
20
|
+
else
|
21
|
+
::RDF::Literal(object)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def object_value(obj)
|
26
|
+
if obj.is_a?(::RDF::URI)
|
27
|
+
obj.value
|
28
|
+
elsif obj.is_a?(::RDF::Node)
|
29
|
+
obj.to_s
|
30
|
+
else
|
31
|
+
obj.value.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def object_datatype(obj)
|
36
|
+
if obj.is_a?(::RDF::URI)
|
37
|
+
'http://www.w3.org/1999/02/22-rdf-syntax-ns#namedNode'
|
38
|
+
elsif obj.is_a?(::RDF::Node)
|
39
|
+
'http://www.w3.org/1999/02/22-rdf-syntax-ns#blankNode'
|
40
|
+
elsif obj.try(:datatype?)
|
41
|
+
obj.datatype
|
42
|
+
else
|
43
|
+
lit = RDF::Literal(obj)
|
44
|
+
lit.datatype.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def value_to_hex(iri, predicate, object, graph = nil, serialization_params = {})
|
49
|
+
return if object.nil?
|
50
|
+
|
51
|
+
obj = normalized_object(object)
|
52
|
+
|
53
|
+
[
|
54
|
+
iri.to_s,
|
55
|
+
predicate.to_s,
|
56
|
+
object_value(obj),
|
57
|
+
object_datatype(obj),
|
58
|
+
obj.try(:language) || '',
|
59
|
+
operation((graph || ::RDF::Serializers.config.default_graph)&.value, serialization_params[:context])
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
def operation(operation, graph_name)
|
64
|
+
return nil if operation.blank?
|
65
|
+
return operation if graph_name.blank?
|
66
|
+
|
67
|
+
"#{operation}?graph=#{WEBrick::HTTPUtils.escape_form(graph_name.to_s)}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
class ListSerializer
|
6
|
+
include RDF::Serializers::ObjectSerializer
|
7
|
+
|
8
|
+
def hextuples_for_collection
|
9
|
+
@resource.map do |resource|
|
10
|
+
RDF::Serializers.serializer_for(resource).record_hextuples(resource, nil, @includes, @params)
|
11
|
+
end.flatten(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def validate_includes!(_includes); end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
# Extracted from active_model_serializers
|
6
|
+
module LookupChain
|
7
|
+
# Standard appending of Serializer to the resource name.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# Author => AuthorSerializer
|
11
|
+
BY_RESOURCE = lambda do |resource_class, _namespace|
|
12
|
+
serializer_from(resource_class)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Uses the namespace of the resource to find the serializer
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# British::Author => British::AuthorSerializer
|
19
|
+
BY_RESOURCE_NAMESPACE = lambda do |resource_class, _namespace|
|
20
|
+
resource_namespace = namespace_for(resource_class)
|
21
|
+
serializer_name = serializer_from(resource_class)
|
22
|
+
|
23
|
+
"#{resource_namespace}::#{serializer_name}" if resource_namespace && serializer_name
|
24
|
+
end
|
25
|
+
|
26
|
+
# Uses the controller namespace of the resource to find the serializer
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
# Api::V3::AuthorsController => Api::V3::AuthorSerializer
|
30
|
+
BY_NAMESPACE = lambda do |resource_class, namespace|
|
31
|
+
(namespace && resource_class.name) ? "#{namespace}::#{resource_class_name(resource_class)}Serializer" : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
DEFAULT = [
|
35
|
+
BY_NAMESPACE,
|
36
|
+
BY_RESOURCE_NAMESPACE,
|
37
|
+
BY_RESOURCE
|
38
|
+
].freeze
|
39
|
+
|
40
|
+
module_function
|
41
|
+
|
42
|
+
def namespace_for(klass)
|
43
|
+
klass.name&.deconstantize
|
44
|
+
end
|
45
|
+
|
46
|
+
def resource_class_name(klass)
|
47
|
+
klass.name&.demodulize
|
48
|
+
end
|
49
|
+
|
50
|
+
def serializer_from_resource_name(name)
|
51
|
+
"#{name}Serializer"
|
52
|
+
end
|
53
|
+
|
54
|
+
def serializer_from(klass)
|
55
|
+
name = resource_class_name(klass)
|
56
|
+
serializer_from_resource_name(name) if name
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
module RDF
|
6
|
+
module Serializers
|
7
|
+
module ObjectSerializer
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
include FastJsonapi::ObjectSerializer
|
10
|
+
include SerializationCore
|
11
|
+
|
12
|
+
included do
|
13
|
+
class_attribute :_statements
|
14
|
+
self._statements ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def dump(*args, **options)
|
18
|
+
case args.first
|
19
|
+
when :hndjson
|
20
|
+
render_hndjson
|
21
|
+
else
|
22
|
+
render_repository(*args, **options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def triples(*args, **options)
|
27
|
+
if include_named_graphs?(*args)
|
28
|
+
repository.triples(*args, **options)
|
29
|
+
else
|
30
|
+
repository.project_graph(nil).triples(*args, **options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def blank_node(id)
|
37
|
+
@blank_nodes ||= {}
|
38
|
+
@blank_nodes[id] ||= RDF::Node(id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def hextuples_for_collection
|
42
|
+
data = []
|
43
|
+
fieldset = @fieldsets[self.class.record_type.to_sym]
|
44
|
+
@resource.each do |record|
|
45
|
+
data.concat self.class.record_hextuples(record, fieldset, @includes, @params)
|
46
|
+
next unless @includes.present?
|
47
|
+
|
48
|
+
data.concat(
|
49
|
+
self.class.get_included_records_hex(record, @includes, @known_included_objects, @fieldsets, @params)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
data
|
54
|
+
end
|
55
|
+
|
56
|
+
def hextuples_for_one_record
|
57
|
+
serializable_hextuples = []
|
58
|
+
|
59
|
+
serializable_hextuples.concat self.class.record_hextuples(
|
60
|
+
@resource,
|
61
|
+
@fieldsets[self.class.record_type.to_sym],
|
62
|
+
@includes,
|
63
|
+
@params
|
64
|
+
)
|
65
|
+
|
66
|
+
if @includes.present?
|
67
|
+
serializable_hextuples.concat self.class.get_included_records_hex(
|
68
|
+
@resource,
|
69
|
+
@includes,
|
70
|
+
@known_included_objects,
|
71
|
+
@fieldsets,
|
72
|
+
@params
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
serializable_hextuples
|
77
|
+
end
|
78
|
+
|
79
|
+
def include_named_graphs?(*args)
|
80
|
+
::RDF::Serializers.config.always_include_named_graphs ||
|
81
|
+
::RDF::Writer.for(*args.presence || :nquads).instance_methods.include?(:write_quad)
|
82
|
+
end
|
83
|
+
|
84
|
+
def meta_hextuples
|
85
|
+
return [] unless @meta.is_a?(Array)
|
86
|
+
|
87
|
+
@meta.map do |statement|
|
88
|
+
if statement.is_a?(Array)
|
89
|
+
value_to_hex(statement[0], statement[1], statement[2], statement[3], @params)
|
90
|
+
else
|
91
|
+
value_to_hex(statement.subject.to_s, statement.predicate, statement.object, statement.graph_name, @params)
|
92
|
+
end
|
93
|
+
end.compact
|
94
|
+
end
|
95
|
+
|
96
|
+
def repository
|
97
|
+
return @repository if @repository.present?
|
98
|
+
|
99
|
+
@repository = ::RDF::Repository.new
|
100
|
+
parser = HndJSONParser.new
|
101
|
+
|
102
|
+
serializable_hextuples.compact.each do |hextuple|
|
103
|
+
@repository << parser.parse_hex(hextuple)
|
104
|
+
end
|
105
|
+
|
106
|
+
@repository
|
107
|
+
end
|
108
|
+
|
109
|
+
def render_hndjson
|
110
|
+
serializable_hextuples
|
111
|
+
.map { |s| Oj.fast_generate(s) }
|
112
|
+
.join("\n")
|
113
|
+
end
|
114
|
+
|
115
|
+
def render_repository(*args, **options)
|
116
|
+
if include_named_graphs?(*args)
|
117
|
+
repository.dump(*args, **options)
|
118
|
+
else
|
119
|
+
repository.project_graph(nil).dump(*args, **options)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def serializable_hextuples
|
124
|
+
if self.class.is_collection?(@resource, @is_collection)
|
125
|
+
hextuples_for_collection + meta_hextuples
|
126
|
+
elsif !@resource
|
127
|
+
[]
|
128
|
+
else
|
129
|
+
hextuples_for_one_record + meta_hextuples
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class_methods do
|
134
|
+
def create_relationship(base_key, relationship_type, options, block)
|
135
|
+
association = options.delete(:association)
|
136
|
+
image = options.delete(:image)
|
137
|
+
predicate = options.delete(:predicate)
|
138
|
+
sequence = options.delete(:sequence)
|
139
|
+
relation = super
|
140
|
+
relation.association = association
|
141
|
+
relation.image = image
|
142
|
+
relation.predicate = predicate
|
143
|
+
relation.sequence = sequence
|
144
|
+
|
145
|
+
relation
|
146
|
+
end
|
147
|
+
|
148
|
+
# Checks for the `class_name` property on the Model's association to
|
149
|
+
# determine a serializer.
|
150
|
+
def association_serializer_for(name)
|
151
|
+
model_class_name = self.name.to_s.demodulize.classify.gsub(/Serializer$/, '')
|
152
|
+
model_class = model_class_name.safe_constantize
|
153
|
+
|
154
|
+
association_class_name = model_class.try(:reflect_on_association, name)&.class_name
|
155
|
+
|
156
|
+
return nil unless association_class_name
|
157
|
+
|
158
|
+
serializer_for(association_class_name)
|
159
|
+
end
|
160
|
+
|
161
|
+
def inherited(base)
|
162
|
+
super
|
163
|
+
base._statements = _statements.dup
|
164
|
+
end
|
165
|
+
|
166
|
+
def serializer_for(name)
|
167
|
+
associatopm_serializer = association_serializer_for(name)
|
168
|
+
return associatopm_serializer if associatopm_serializer
|
169
|
+
|
170
|
+
begin
|
171
|
+
RDF::Serializers.serializer_for(const_get(name.to_s.classify))
|
172
|
+
rescue NameError
|
173
|
+
raise NameError, "#{self.name} cannot resolve a serializer class for '#{name}'. " \
|
174
|
+
'Consider specifying the serializer directly through options[:serializer].'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def statements(attribute)
|
179
|
+
self._statements << attribute
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
module Relationship
|
6
|
+
include HextupleSerializer
|
7
|
+
|
8
|
+
attr_accessor :predicate, :image, :association, :sequence
|
9
|
+
|
10
|
+
def serialize_hex(record, included, serialization_params)
|
11
|
+
return [] unless include_relationship?(record, serialization_params, included) && predicate.present?
|
12
|
+
|
13
|
+
iris = iris_from_record_and_relationship(record, serialization_params)
|
14
|
+
|
15
|
+
sequence ? relationship_sequence(record, iris, serialization_params) : relationship_statements(record, iris, serialization_params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def relationship_sequence(record, iris, serialization_params)
|
19
|
+
return [] if iris.blank?
|
20
|
+
|
21
|
+
sequence = RDF::Node.new
|
22
|
+
|
23
|
+
[
|
24
|
+
value_to_hex(iri_from_record(record).to_s, predicate, sequence, nil, serialization_params),
|
25
|
+
value_to_hex(sequence, RDF.type, RDF.Seq, nil, serialization_params)
|
26
|
+
] + iris.map.with_index do |iri, index|
|
27
|
+
value_to_hex(sequence, RDF["_#{index}"], iri, nil, serialization_params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def relationship_statements(record, iris, serialization_params)
|
32
|
+
iris.map do |related_iri|
|
33
|
+
value_to_hex(
|
34
|
+
iri_from_record(record).to_s,
|
35
|
+
predicate,
|
36
|
+
related_iri,
|
37
|
+
nil,
|
38
|
+
serialization_params
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def include_relationship?(record, serialization_params, included = false)
|
44
|
+
return false if lazy_load_data && !included
|
45
|
+
|
46
|
+
super(record, serialization_params)
|
47
|
+
end
|
48
|
+
|
49
|
+
def iris_from_record_and_relationship(record, params = {})
|
50
|
+
initialize_static_serializer unless @initialized_static_serializer
|
51
|
+
|
52
|
+
associated_object = fetch_associated_object(record, params)
|
53
|
+
return [] unless associated_object
|
54
|
+
|
55
|
+
if associated_object.respond_to? :map
|
56
|
+
return associated_object.compact.map do |object|
|
57
|
+
iri_from_record(object)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
[iri_from_record(associated_object)]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
FastJsonapi::Relationship.prepend(RDF::Serializers::Relationship)
|
@@ -19,16 +19,42 @@ module RDF
|
|
19
19
|
raise "#{symbol} is not a known rdf format" if format.nil?
|
20
20
|
|
21
21
|
Mime::Type.register format.content_type.first, format.file_extension.first
|
22
|
-
add_renderer(format, opts)
|
22
|
+
add_renderer(format.file_extension.first, format.content_type.first, format.symbols.first, opts)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def self.add_renderer(
|
27
|
-
ActionController::Renderers.add
|
28
|
-
self.content_type =
|
29
|
-
|
26
|
+
def self.add_renderer(ext, content_type, symbol, opts = {})
|
27
|
+
ActionController::Renderers.add ext do |resource, options|
|
28
|
+
self.content_type = content_type
|
29
|
+
serializer_opts = RDF::Serializers::Renderers.transform_opts(
|
30
|
+
options,
|
31
|
+
respond_to?(:serializer_params, true) ? serializer_params : {}
|
32
|
+
)
|
33
|
+
RDF::Serializers.serializer_for(resource)&.new(resource, serializer_opts)&.dump(symbol, **opts)
|
30
34
|
end
|
31
35
|
end
|
36
|
+
|
37
|
+
def self.transform_include(include, root = nil)
|
38
|
+
return root if include.blank?
|
39
|
+
return [root, include].compact.join('.') if include.is_a?(Symbol) || include.is_a?(String)
|
40
|
+
|
41
|
+
if include.is_a?(Hash)
|
42
|
+
include.flat_map do |k, v|
|
43
|
+
transform_include(v, [root, k].compact.join('.'))
|
44
|
+
end
|
45
|
+
elsif include.is_a?(Array)
|
46
|
+
include.flat_map do |v|
|
47
|
+
transform_include(v, root)
|
48
|
+
end
|
49
|
+
end.compact
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.transform_opts(options, params)
|
53
|
+
(options || {}).merge(
|
54
|
+
include: transform_include(options[:include]),
|
55
|
+
params: params
|
56
|
+
)
|
57
|
+
end
|
32
58
|
end
|
33
59
|
end
|
34
60
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
module Scalar
|
6
|
+
include HextupleSerializer
|
7
|
+
|
8
|
+
attr_accessor :predicate, :image, :datatype
|
9
|
+
|
10
|
+
def initialize(key:, method:, options: {})
|
11
|
+
super
|
12
|
+
@predicate = options[:predicate]
|
13
|
+
@image = options[:image]
|
14
|
+
@datatype = options[:datatype]
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize_hex(record, serialization_params)
|
18
|
+
return [] unless conditionally_allowed?(record, serialization_params) && predicate.present?
|
19
|
+
|
20
|
+
value = value_from_record(record, method, serialization_params)
|
21
|
+
|
22
|
+
return [] if value.nil?
|
23
|
+
|
24
|
+
if value.is_a?(Array)
|
25
|
+
value.map { |arr_item| value_to_hex(iri_from_record(record).to_s, predicate, arr_item, nil, serialization_params) }
|
26
|
+
elsif value.is_a?(::RDF::List)
|
27
|
+
first = value.statements.first&.subject || RDF.nil
|
28
|
+
value.statements.map do |statement|
|
29
|
+
value_to_hex(statement.subject.to_s, statement.predicate, statement.object, statement.graph_name, serialization_params)
|
30
|
+
end + [
|
31
|
+
value_to_hex(iri_from_record(record).to_s, predicate, first, nil, serialization_params)
|
32
|
+
]
|
33
|
+
else
|
34
|
+
[value_to_hex(iri_from_record(record).to_s, predicate, value, nil, serialization_params)]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_from_record(record, method, serialization_params)
|
39
|
+
if method.is_a?(Proc)
|
40
|
+
FastJsonapi.call_proc(method, record, serialization_params)
|
41
|
+
else
|
42
|
+
v = record.public_send(method)
|
43
|
+
v.is_a?('ActiveRecord'.safe_constantize&.const_get('Relation') || NilClass) ? v.to_a : v
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
FastJsonapi::Scalar.prepend(RDF::Serializers::Scalar)
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
module Serializers
|
5
|
+
module SerializationCore
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include HextupleSerializer
|
10
|
+
extend HextupleSerializer
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
def relationships_hextuples(record, relationships = nil, fieldset = nil, includes_list = [], params = {})
|
15
|
+
relationships = relationships_to_serialize if relationships.nil?
|
16
|
+
relationships = relationships.slice(*fieldset) if fieldset.present?
|
17
|
+
relationships = [] if fieldset == []
|
18
|
+
|
19
|
+
statements = []
|
20
|
+
relationships.each do |key, relationship|
|
21
|
+
included = includes_list.present? && includes_list.include?(key)
|
22
|
+
statements.concat relationship.serialize_hex(record, included, params)
|
23
|
+
end
|
24
|
+
|
25
|
+
statements
|
26
|
+
end
|
27
|
+
|
28
|
+
def attributes_hextuples(record, fieldset = nil, params = {})
|
29
|
+
attributes = attributes_to_serialize
|
30
|
+
attributes = attributes.slice(*fieldset) if fieldset.present?
|
31
|
+
attributes = {} if fieldset == []
|
32
|
+
|
33
|
+
statements = attributes.flat_map do |_k, attr|
|
34
|
+
attr.serialize_hex(record, params)
|
35
|
+
end
|
36
|
+
|
37
|
+
statements.compact
|
38
|
+
end
|
39
|
+
|
40
|
+
def statements_hextuples(record, params = {})
|
41
|
+
statements = []
|
42
|
+
|
43
|
+
_statements&.map do |key|
|
44
|
+
send(key, record, params).each do |statement|
|
45
|
+
statements << if statement.is_a?(Array)
|
46
|
+
value_to_hex(statement[0], statement[1], statement[2], statement[3], params)
|
47
|
+
else
|
48
|
+
value_to_hex(statement.subject.to_s, statement.predicate, statement.object, statement.graph_name, params)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
statements.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
def record_hextuples(record, fieldset, includes_list, params = {})
|
57
|
+
if cache_store_instance
|
58
|
+
record_hex = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
|
59
|
+
temp_hex = []
|
60
|
+
temp_hex.concat attributes_hextuples(record, fieldset, params) if attributes_to_serialize.present?
|
61
|
+
temp_hex.concat statements_hextuples(record, params)
|
62
|
+
if cachable_relationships_to_serialize.present?
|
63
|
+
temp_hex.concat relationships_hextuples(record, cachable_relationships_to_serialize, fieldset, includes_list, params)
|
64
|
+
end
|
65
|
+
temp_hex
|
66
|
+
end
|
67
|
+
if uncachable_relationships_to_serialize.present?
|
68
|
+
record_hex.concat relationships_hextuples(record, uncachable_relationships_to_serialize, fieldset, includes_list, params)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
record_hex = []
|
72
|
+
record_hex.concat attributes_hextuples(record, fieldset, params) if attributes_to_serialize.present?
|
73
|
+
record_hex.concat statements_hextuples(record, params)
|
74
|
+
if relationships_to_serialize.present?
|
75
|
+
record_hex.concat relationships_hextuples(record, nil, fieldset, includes_list, params)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
record_hex
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_included_records_hex(record, includes_list, known_included_objects, fieldsets, params = {})
|
82
|
+
return unless includes_list.present?
|
83
|
+
return [] unless relationships_to_serialize
|
84
|
+
|
85
|
+
includes_list = parse_includes_list(includes_list)
|
86
|
+
|
87
|
+
includes_list.each_with_object([]) do |include_item, included_records|
|
88
|
+
relationship_item = relationships_to_serialize[include_item.first]
|
89
|
+
|
90
|
+
next unless relationship_item&.include_relationship?(record, params)
|
91
|
+
|
92
|
+
included_objects = Array(relationship_item.fetch_associated_object(record, params))
|
93
|
+
next if included_objects.empty?
|
94
|
+
|
95
|
+
static_serializer = relationship_item.static_serializer
|
96
|
+
static_record_type = relationship_item.static_record_type
|
97
|
+
|
98
|
+
included_objects.each do |inc_obj|
|
99
|
+
serializer = static_serializer || relationship_item.serializer_for(inc_obj, params)
|
100
|
+
record_type = static_record_type || serializer.record_type
|
101
|
+
|
102
|
+
if include_item.last.any?
|
103
|
+
serializer_records = serializer.get_included_records_hex(inc_obj, include_item.last, known_included_objects, fieldsets, params)
|
104
|
+
included_records.concat(serializer_records) unless serializer_records.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
code = "#{record_type}_#{serializer.iri_from_record(inc_obj)}"
|
108
|
+
next if known_included_objects.include?(code)
|
109
|
+
|
110
|
+
known_included_objects << code
|
111
|
+
|
112
|
+
included_records.concat(
|
113
|
+
serializer.record_hextuples(inc_obj, fieldsets[record_type], includes_list, params)
|
114
|
+
)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/rdf/serializers.rb
CHANGED
@@ -1,5 +1,95 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
|
3
|
+
require 'fast_jsonapi'
|
4
|
+
|
5
|
+
require 'rdf/serializers/lookup_chain'
|
5
6
|
require 'rdf/serializers/config'
|
7
|
+
require 'rdf/serializers/hextuple_serializer'
|
8
|
+
require 'rdf/serializers/data_type_helper'
|
9
|
+
require 'rdf/serializers/hdnjson_parser'
|
10
|
+
require 'rdf/serializers/serialization_core'
|
11
|
+
require 'rdf/serializers/object_serializer'
|
12
|
+
require 'rdf/serializers/scalar'
|
13
|
+
require 'rdf/serializers/relationship'
|
14
|
+
require 'rdf/serializers/nil_serializer'
|
15
|
+
require 'rdf/serializers/list_serializer'
|
16
|
+
|
17
|
+
module RDF
|
18
|
+
module Serializers
|
19
|
+
class << self
|
20
|
+
# Extracted from active_model_serializers
|
21
|
+
# @param resource [ActiveRecord::Base, ActiveModelSerializers::Model]
|
22
|
+
# @return [ActiveModel::Serializer]
|
23
|
+
# Preferentially returns
|
24
|
+
# 1. resource.serializer_class
|
25
|
+
# 2. ArraySerializer when resource is a collection
|
26
|
+
# 3. options[:serializer]
|
27
|
+
# 4. lookup serializer when resource is a Class
|
28
|
+
def serializer_for(resource_or_class, options = {})
|
29
|
+
if resource_or_class.respond_to?(:serializer_class)
|
30
|
+
resource_or_class.serializer_class
|
31
|
+
elsif resource_or_class.respond_to?(:to_ary)
|
32
|
+
unless resource_or_class.all? { |resource| resource.is_a?(resource_or_class.first.class) }
|
33
|
+
return ListSerializer
|
34
|
+
end
|
35
|
+
|
36
|
+
serializer_for(resource_or_class.first)
|
37
|
+
else
|
38
|
+
resource_class = resource_or_class.class == Class ? resource_or_class : resource_or_class.class
|
39
|
+
options.fetch(:serializer) { get_serializer_for(resource_class, options[:namespace]) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Extracted from active_model_serializers
|
46
|
+
# Find a serializer from a class and caches the lookup.
|
47
|
+
# Preferentially returns:
|
48
|
+
# 1. class name appended with "Serializer"
|
49
|
+
# 2. try again with superclass, if present
|
50
|
+
# 3. nil
|
51
|
+
def get_serializer_for(klass, namespace = nil)
|
52
|
+
return nil unless config.serializer_lookup_enabled
|
53
|
+
|
54
|
+
return NilSerializer if klass == NilClass
|
55
|
+
|
56
|
+
cache_key = ActiveSupport::Cache.expand_cache_key(klass, namespace)
|
57
|
+
serializers_cache.fetch_or_store(cache_key) do
|
58
|
+
# NOTE(beauby): When we drop 1.9.3 support we can lazify the map for perfs.
|
59
|
+
lookup_chain = serializer_lookup_chain_for(klass, namespace)
|
60
|
+
serializer_class = serializer_class_from_chain(lookup_chain)
|
61
|
+
|
62
|
+
if serializer_class
|
63
|
+
serializer_class
|
64
|
+
elsif klass.superclass
|
65
|
+
get_serializer_for(klass.superclass, namespace)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Extracted from active_model_serializers
|
71
|
+
# Used to cache serializer name => serializer class
|
72
|
+
# when looked up by Serializer.get_serializer_for.
|
73
|
+
def serializers_cache
|
74
|
+
@serializers_cache ||= Concurrent::Map.new
|
75
|
+
end
|
76
|
+
|
77
|
+
def serializer_class_from_chain(lookup_chain)
|
78
|
+
lookup_chain.map do |klass|
|
79
|
+
klass&.safe_constantize
|
80
|
+
rescue LoadError
|
81
|
+
nil
|
82
|
+
end.find do |klass|
|
83
|
+
klass&.include?(FastJsonapi::SerializationCore)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def serializer_lookup_chain_for(klass, namespace = nil)
|
88
|
+
lookups = config.serializer_lookup_chain
|
89
|
+
Array[*lookups].flat_map do |lookup|
|
90
|
+
lookup.call(klass, namespace)
|
91
|
+
end.compact
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdf-serializers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arthur Dingemans
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: jsonapi-serializer
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 2.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oj
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: railties
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,24 +72,31 @@ dependencies:
|
|
58
72
|
- - "~>"
|
59
73
|
- !ruby/object:Gem::Version
|
60
74
|
version: '3.0'
|
61
|
-
description:
|
75
|
+
description:
|
62
76
|
email: arthur@argu.co
|
63
77
|
executables: []
|
64
78
|
extensions: []
|
65
79
|
extra_rdoc_files: []
|
66
80
|
files:
|
67
|
-
- lib/active_model_serializers/adapter/rdf.rb
|
68
|
-
- lib/active_model_serializers/adapter/rdf/relationship.rb
|
69
|
-
- lib/active_model_serializers/serializer.rb
|
70
81
|
- lib/rdf/serializers.rb
|
71
82
|
- lib/rdf/serializers/config.rb
|
83
|
+
- lib/rdf/serializers/data_type_helper.rb
|
84
|
+
- lib/rdf/serializers/hdnjson_parser.rb
|
85
|
+
- lib/rdf/serializers/hextuple_serializer.rb
|
86
|
+
- lib/rdf/serializers/list_serializer.rb
|
87
|
+
- lib/rdf/serializers/lookup_chain.rb
|
88
|
+
- lib/rdf/serializers/nil_serializer.rb
|
89
|
+
- lib/rdf/serializers/object_serializer.rb
|
90
|
+
- lib/rdf/serializers/relationship.rb
|
72
91
|
- lib/rdf/serializers/renderers.rb
|
92
|
+
- lib/rdf/serializers/scalar.rb
|
93
|
+
- lib/rdf/serializers/serialization_core.rb
|
73
94
|
- lib/rdf/serializers/version.rb
|
74
95
|
homepage: https://github.com/ontola/rdf-serializers
|
75
96
|
licenses:
|
76
97
|
- MIT
|
77
98
|
metadata: {}
|
78
|
-
post_install_message:
|
99
|
+
post_install_message:
|
79
100
|
rdoc_options: []
|
80
101
|
require_paths:
|
81
102
|
- lib
|
@@ -90,8 +111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
111
|
- !ruby/object:Gem::Version
|
91
112
|
version: '0'
|
92
113
|
requirements: []
|
93
|
-
rubygems_version: 3.
|
94
|
-
signing_key:
|
114
|
+
rubygems_version: 3.2.22
|
115
|
+
signing_key:
|
95
116
|
specification_version: 4
|
96
117
|
summary: Adds RDF serialization, like n-triples or turtle, to active model serializers
|
97
118
|
test_files: []
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveModelSerializers
|
4
|
-
module Adapter
|
5
|
-
class RDF
|
6
|
-
class Relationship < JsonApi::Relationship
|
7
|
-
def triples
|
8
|
-
return [] if no_data?
|
9
|
-
|
10
|
-
data.map do |object|
|
11
|
-
raise "#{object} is not a RDF::Resource but a #{object.class}" unless object.is_a?(::RDF::Resource)
|
12
|
-
|
13
|
-
::RDF::Statement.new(subject, predicate, object, graph_name: graph_name)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def data
|
20
|
-
@data ||=
|
21
|
-
if association.collection?
|
22
|
-
objects_for_many(association).compact
|
23
|
-
else
|
24
|
-
[object_for_one(association)].compact
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def graph_name
|
29
|
-
association.reflection.options[:graph] || ::RDF::Serializers.config.default_graph
|
30
|
-
end
|
31
|
-
|
32
|
-
def no_data?
|
33
|
-
subject.blank? || predicate.blank? || data.empty?
|
34
|
-
end
|
35
|
-
|
36
|
-
def objects_for_many(association)
|
37
|
-
collection_serializer = association.lazy_association.serializer
|
38
|
-
if collection_serializer.respond_to?(:each)
|
39
|
-
collection_serializer.map do |serializer|
|
40
|
-
serializer.read_attribute_for_serialization(:rdf_subject)
|
41
|
-
end
|
42
|
-
else
|
43
|
-
[]
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def object_for_one(association)
|
48
|
-
if belongs_to_id_on_self?(association)
|
49
|
-
parent_serializer.read_attribute_for_serialization(:rdf_subject)
|
50
|
-
else
|
51
|
-
serializer = association.lazy_association.serializer
|
52
|
-
if (virtual_value = association.virtual_value)
|
53
|
-
virtual_value[:id]
|
54
|
-
elsif serializer && association.object
|
55
|
-
serializer.read_attribute_for_serialization(:rdf_subject)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def predicate
|
61
|
-
@predicate ||= association.reflection.options[:predicate]
|
62
|
-
end
|
63
|
-
|
64
|
-
def subject
|
65
|
-
@subject ||= parent_serializer.read_attribute_for_serialization(:rdf_subject)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,193 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# rubocop:disable Metrics/ClassLength
|
4
|
-
module ActiveModelSerializers
|
5
|
-
module Adapter
|
6
|
-
class RDF < Base
|
7
|
-
extend ActiveSupport::Autoload
|
8
|
-
autoload :Relationship
|
9
|
-
|
10
|
-
delegate :object, to: :serializer
|
11
|
-
|
12
|
-
def initialize(serializer, options = {})
|
13
|
-
super
|
14
|
-
@include_directive = JSONAPI::IncludeDirective.new(options[:include], allow_wildcard: true)
|
15
|
-
@fieldset = options[:fieldset] || ActiveModel::Serializer::Fieldset.new(options.delete(:fields))
|
16
|
-
@resource_identifiers = Set.new
|
17
|
-
end
|
18
|
-
|
19
|
-
def dump(*args, **options)
|
20
|
-
if include_named_graphs?(*args)
|
21
|
-
repository.dump(*args, options)
|
22
|
-
else
|
23
|
-
repository.project_graph(nil).dump(*args, options)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def triples(*args, **options)
|
28
|
-
if include_named_graphs?(*args)
|
29
|
-
repository.triples(*args, options)
|
30
|
-
else
|
31
|
-
repository.project_graph(nil).triples(*args, options)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
protected
|
36
|
-
|
37
|
-
attr_reader :fieldset
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def add_attribute(subject, predicate, value, graph)
|
42
|
-
return unless predicate
|
43
|
-
|
44
|
-
normalized = value.is_a?(Array) ? value : [value]
|
45
|
-
normalized.compact.map { |v| add_triple([subject, predicate, v, graph]) }
|
46
|
-
end
|
47
|
-
|
48
|
-
def add_triple(triple)
|
49
|
-
@repository << (triple.is_a?(Array) ? normalized_triple(*triple) : triple)
|
50
|
-
end
|
51
|
-
|
52
|
-
def attributes_for(serializer, fields)
|
53
|
-
serializer.attributes(fields).each do |key, value|
|
54
|
-
add_attribute(
|
55
|
-
serializer.read_attribute_for_serialization(:rdf_subject),
|
56
|
-
serializer.class._attributes_data[key].try(:options).try(:[], :predicate),
|
57
|
-
value,
|
58
|
-
serializer.class._attributes_data[key].try(:options).try(:[], :graph)
|
59
|
-
)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def custom_triples_for(serializer)
|
64
|
-
serializer.class.try(:_triples)&.map do |key|
|
65
|
-
serializer.read_attribute_for_serialization(key).each do |triple|
|
66
|
-
add_triple(triple)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def repository
|
72
|
-
return @repository if @repository.present?
|
73
|
-
|
74
|
-
@repository = ::RDF::Repository.new
|
75
|
-
|
76
|
-
serializers.each { |serializer| process_resource(serializer, @include_directive) }
|
77
|
-
serializers.each { |serializer| process_relationships(serializer, @include_directive) }
|
78
|
-
instance_options[:meta]&.each { |meta| add_triple(meta) }
|
79
|
-
|
80
|
-
raise_missing_nodes if raise_on_missing_nodes?
|
81
|
-
|
82
|
-
@repository
|
83
|
-
end
|
84
|
-
|
85
|
-
def include_named_graphs?(*args)
|
86
|
-
::RDF::Serializers.config.always_include_named_graphs ||
|
87
|
-
::RDF::Writer.for(*args.presence || :nquads).instance_methods.include?(:write_quad)
|
88
|
-
end
|
89
|
-
|
90
|
-
def missing_nodes
|
91
|
-
@missing_nodes ||=
|
92
|
-
@repository
|
93
|
-
.objects
|
94
|
-
.select(&:node?)
|
95
|
-
.reject { |n| @repository.has_subject?(n) }
|
96
|
-
.map { |n| @repository.query([nil, nil, n]).first }
|
97
|
-
end
|
98
|
-
|
99
|
-
def normalized_object(object) # rubocop:disable Metrics/MethodLength
|
100
|
-
case object
|
101
|
-
when ::RDF::Term
|
102
|
-
object
|
103
|
-
when ::RDF::List
|
104
|
-
list = object.statements
|
105
|
-
@repository << object.statements
|
106
|
-
list.first.subject
|
107
|
-
when ActiveSupport::TimeWithZone
|
108
|
-
::RDF::Literal(object.to_datetime)
|
109
|
-
else
|
110
|
-
::RDF::Literal(object)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def normalized_triple(subject, predicate, object, graph = nil)
|
115
|
-
::RDF::Statement.new(
|
116
|
-
subject,
|
117
|
-
::RDF::URI(predicate),
|
118
|
-
normalized_object(object),
|
119
|
-
graph_name: graph || ::RDF::Serializers.config.default_graph
|
120
|
-
)
|
121
|
-
end
|
122
|
-
|
123
|
-
def process_relationship(serializer, include_slice)
|
124
|
-
return serializer.each { |s| process_relationship(s, include_slice) } if serializer.respond_to?(:each)
|
125
|
-
|
126
|
-
return unless serializer&.object && process_resource(serializer, include_slice)
|
127
|
-
|
128
|
-
process_relationships(serializer, include_slice)
|
129
|
-
end
|
130
|
-
|
131
|
-
def process_relationships(serializer, include_slice)
|
132
|
-
return unless serializer.respond_to?(:associations)
|
133
|
-
|
134
|
-
serializer.associations(include_slice).each do |association|
|
135
|
-
process_relationship(association.lazy_association.serializer, include_slice[association.key])
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def process_resource(serializer, include_slice = {})
|
140
|
-
if serializer.is_a?(ActiveModel::Serializer::CollectionSerializer)
|
141
|
-
return serializer.map { |child| process_resource(child, include_slice) }
|
142
|
-
end
|
143
|
-
return unless serializer.respond_to?(:rdf_subject) || serializer.object.respond_to?(:rdf_subject)
|
144
|
-
|
145
|
-
return false unless @resource_identifiers.add?(serializer.read_attribute_for_serialization(:rdf_subject))
|
146
|
-
|
147
|
-
resource_object_for(serializer, include_slice)
|
148
|
-
true
|
149
|
-
end
|
150
|
-
|
151
|
-
def raise_on_missing_nodes?
|
152
|
-
Rails.env.development? || Rails.env.test?
|
153
|
-
end
|
154
|
-
|
155
|
-
def raise_missing_nodes
|
156
|
-
return if missing_nodes.empty?
|
157
|
-
|
158
|
-
raise "The following triples point to nodes that are not included in the graph:\n#{missing_nodes.join("\n")}"
|
159
|
-
end
|
160
|
-
|
161
|
-
def relationships_for(serializer, requested_associations, include_slice)
|
162
|
-
include_directive = JSONAPI::IncludeDirective.new(requested_associations, allow_wildcard: true)
|
163
|
-
serializer.associations(include_directive, include_slice).each do |association|
|
164
|
-
Relationship.new(serializer, instance_options, association).triples.each do |triple|
|
165
|
-
add_triple(triple)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def resource_object_for(serializer, include_slice = {})
|
171
|
-
type = type_for(serializer, instance_options).to_s
|
172
|
-
serializer.fetch(self) do
|
173
|
-
break nil if serializer.read_attribute_for_serialization(:rdf_subject).nil?
|
174
|
-
|
175
|
-
requested_fields = fieldset&.fields_for(type)
|
176
|
-
attributes_for(serializer, requested_fields)
|
177
|
-
custom_triples_for(serializer)
|
178
|
-
end
|
179
|
-
requested_associations = fieldset.fields_for(type) || '*'
|
180
|
-
relationships_for(serializer, requested_associations, include_slice)
|
181
|
-
end
|
182
|
-
|
183
|
-
def serializers
|
184
|
-
serializer.respond_to?(:each) ? serializer : [serializer]
|
185
|
-
end
|
186
|
-
|
187
|
-
def type_for(serializer, instance_options)
|
188
|
-
JsonApi::ResourceIdentifier.new(serializer, instance_options).as_json[:type]
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
# rubocop:enable Metrics/ClassLength
|