json-ld 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/json/ld.rb +50 -0
- data/lib/json/ld/extensions.rb +34 -0
- data/lib/json/ld/format.rb +16 -0
- data/lib/json/ld/reader.rb +44 -62
- data/lib/json/ld/writer.rb +652 -0
- metadata +60 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/lib/json/ld.rb
CHANGED
@@ -21,11 +21,61 @@ module JSON
|
|
21
21
|
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
22
22
|
module LD
|
23
23
|
require 'json'
|
24
|
+
require 'json/ld/extensions'
|
24
25
|
require 'json/ld/format'
|
25
26
|
autoload :Reader, 'json/ld/reader'
|
26
27
|
autoload :VERSION, 'json/ld/version'
|
27
28
|
autoload :Writer, 'json/ld/writer'
|
28
29
|
|
30
|
+
# Keywords
|
31
|
+
BASE = '@base'.freeze
|
32
|
+
COERCE = '@coerce'.freeze
|
33
|
+
CONTEXT = '@context'.freeze
|
34
|
+
DATATYPE = '@datatype'.freeze
|
35
|
+
IRI = '@iri'.freeze
|
36
|
+
LANGUAGE = '@language'.freeze
|
37
|
+
LITERAL = '@literal'.freeze
|
38
|
+
SUBJECT = '@subject'.freeze
|
39
|
+
TYPE = '@type'.freeze
|
40
|
+
VOCAB = '@vocab'.freeze
|
41
|
+
|
42
|
+
# Default context
|
43
|
+
# @see http://json-ld.org/spec/ED/20110507/#the-default-context
|
44
|
+
DEFAULT_CONTEXT = {
|
45
|
+
'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
46
|
+
'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
|
47
|
+
'owl' => 'http://www.w3.org/2002/07/owl#',
|
48
|
+
'xsd' => 'http://www.w3.org/2001/XMLSchema#',
|
49
|
+
'dcterms' => 'http://purl.org/dc/terms/',
|
50
|
+
'foaf' => 'http://xmlns.com/foaf/0.1/',
|
51
|
+
'cal' => 'http://www.w3.org/2002/12/cal/ical#',
|
52
|
+
'vcard' => 'http://www.w3.org/2006/vcard/ns# ',
|
53
|
+
'geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#',
|
54
|
+
'cc' => 'http://creativecommons.org/ns#',
|
55
|
+
'sioc' => 'http://rdfs.org/sioc/ns#',
|
56
|
+
'doap' => 'http://usefulinc.com/ns/doap#',
|
57
|
+
'com' => 'http://purl.org/commerce#',
|
58
|
+
'ps' => 'http://purl.org/payswarm#',
|
59
|
+
'gr' => 'http://purl.org/goodrelations/v1#',
|
60
|
+
'sig' => 'http://purl.org/signature#',
|
61
|
+
'ccard' => 'http://purl.org/commerce/creditcard#',
|
62
|
+
'@coerce' => {
|
63
|
+
# Note: rdf:type is not in the document, but necessary for this implementation
|
64
|
+
'xsd:anyURI' => ['rdf:type', 'rdf:rest', 'foaf:homepage', 'foaf:member'],
|
65
|
+
'xsd:integer' => 'foaf:age',
|
66
|
+
}
|
67
|
+
}.freeze
|
68
|
+
|
69
|
+
# Default type coercion, in property => datatype order
|
70
|
+
DEFAULT_COERCE = {
|
71
|
+
RDF.type => RDF::XSD.anyURI,
|
72
|
+
RDF.first => false, # Make sure @coerce isn't generated for this
|
73
|
+
RDF.rest => RDF::XSD.anyURI,
|
74
|
+
RDF::FOAF.homepage => RDF::XSD.anyURI,
|
75
|
+
RDF::FOAF.member => RDF::XSD.anyURI,
|
76
|
+
RDF::FOAF.age => RDF::XSD.integer,
|
77
|
+
}.freeze
|
78
|
+
|
29
79
|
def self.debug?; @debug; end
|
30
80
|
def self.debug=(value); @debug = value; end
|
31
81
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module RDF
|
2
|
+
class Graph
|
3
|
+
# Resource properties
|
4
|
+
#
|
5
|
+
# Properties arranged as a hash with the predicate Term as index to an array of resources or literals
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# graph.load(':foo a :bar; rdfs:label "An example" .', "http://example.com/")
|
9
|
+
# graph.resources(URI.new("http://example.com/subject")) =>
|
10
|
+
# {
|
11
|
+
# "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" => [<http://example.com/#bar>],
|
12
|
+
# "http://example.com/#label" => ["An example"]
|
13
|
+
# }
|
14
|
+
def properties(subject, recalc = false)
|
15
|
+
@properties ||= {}
|
16
|
+
@properties.delete(subject.to_s) if recalc
|
17
|
+
@properties[subject.to_s] ||= begin
|
18
|
+
hash = Hash.new
|
19
|
+
self.query(:subject => subject) do |statement|
|
20
|
+
pred = statement.predicate.to_s
|
21
|
+
|
22
|
+
hash[pred] ||= []
|
23
|
+
hash[pred] << statement.object
|
24
|
+
end
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get type(s) of subject, returns a list of symbols
|
30
|
+
def type_of(subject)
|
31
|
+
query(:subject => subject, :predicate => RDF.type).map {|st| st.object}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/json/ld/format.rb
CHANGED
@@ -28,4 +28,20 @@ module JSON::LD
|
|
28
28
|
reader { JSON::LD::Reader }
|
29
29
|
writer { JSON::LD::Writer }
|
30
30
|
end
|
31
|
+
|
32
|
+
# Alias for JSON-LD format
|
33
|
+
#
|
34
|
+
# This allows the following:
|
35
|
+
#
|
36
|
+
# @example Obtaining an Notation3 format class
|
37
|
+
# RDF::Format.for(:jsonld) #=> JSON::LD::JSONLD
|
38
|
+
# RDF::Format.for(:jsonld).reader #=> JSON::LD::Reader
|
39
|
+
# RDF::Format.for(:jsonld).writer #=> JSON::LD::Writer
|
40
|
+
class JSONLD < RDF::Format
|
41
|
+
content_type 'application/json', :extension => :jsonld
|
42
|
+
content_encoding 'utf-8'
|
43
|
+
|
44
|
+
reader { JSON::LD::Reader }
|
45
|
+
writer { JSON::LD::Writer }
|
46
|
+
end
|
31
47
|
end
|
data/lib/json/ld/reader.rb
CHANGED
@@ -7,33 +7,6 @@ module JSON::LD
|
|
7
7
|
class Reader < RDF::Reader
|
8
8
|
format Format
|
9
9
|
|
10
|
-
# Default context
|
11
|
-
# @see http://json-ld.org/spec/ED/20110507/#the-default-context
|
12
|
-
DEFAULT_CONTEXT = {
|
13
|
-
"rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
14
|
-
"rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
|
15
|
-
"owl" => "http://www.w3.org/2002/07/owl#",
|
16
|
-
"xsd" => "http://www.w3.org/2001/XMLSchema#",
|
17
|
-
"dcterms" => "http://purl.org/dc/terms/",
|
18
|
-
"foaf" => "http://xmlns.com/foaf/0.1/",
|
19
|
-
"cal" => "http://www.w3.org/2002/12/cal/ical#",
|
20
|
-
"vcard" => "http://www.w3.org/2006/vcard/ns# ",
|
21
|
-
"geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
|
22
|
-
"cc" => "http://creativecommons.org/ns#",
|
23
|
-
"sioc" => "http://rdfs.org/sioc/ns#",
|
24
|
-
"doap" => "http://usefulinc.com/ns/doap#",
|
25
|
-
"com" => "http://purl.org/commerce#",
|
26
|
-
"ps" => "http://purl.org/payswarm#",
|
27
|
-
"gr" => "http://purl.org/goodrelations/v1#",
|
28
|
-
"sig" => "http://purl.org/signature#",
|
29
|
-
"ccard" => "http://purl.org/commerce/creditcard#",
|
30
|
-
"@coerce" => {
|
31
|
-
# Note: rdf:type is not in the document, but necessary for this implementation
|
32
|
-
"xsd:anyURI" => ["rdf:type", "foaf:homepage", "foaf:member", "rdf:type"],
|
33
|
-
"xsd:integer" => "foaf:age",
|
34
|
-
}
|
35
|
-
}.freeze
|
36
|
-
|
37
10
|
##
|
38
11
|
# The graph constructed when parsing.
|
39
12
|
#
|
@@ -181,44 +154,49 @@ module JSON::LD
|
|
181
154
|
|
182
155
|
# 2.1) If a @context keyword is found, the processor merges each key-value pair in
|
183
156
|
# the local context into the active context ...
|
184
|
-
if element[
|
157
|
+
if element[CONTEXT]
|
185
158
|
# Merge context
|
186
|
-
ec = parse_context(ec.dup, element[
|
159
|
+
ec = parse_context(ec.dup, element[CONTEXT])
|
187
160
|
prefixes.merge!(ec.mappings) # Update parsed prefixes
|
188
161
|
end
|
189
162
|
|
190
163
|
# Other shortcuts to allow use of this method for terminal associative arrays
|
191
|
-
if element[
|
164
|
+
if element[IRI].is_a?(String)
|
192
165
|
# Return the IRI found from the value
|
193
|
-
object = expand_term(element[
|
166
|
+
object = expand_term(element[IRI], ec.base, ec)
|
194
167
|
add_triple(path, subject, property, object) if subject && property
|
195
|
-
|
168
|
+
return
|
169
|
+
elsif element[LITERAL]
|
196
170
|
literal_opts = {}
|
197
|
-
literal_opts[:datatype] = expand_term(element[
|
198
|
-
literal_opts[:language] = element[
|
199
|
-
object = RDF::Literal.new(element[
|
171
|
+
literal_opts[:datatype] = expand_term(element[DATATYPE], ec.vocab.to_s, ec) if element[DATATYPE]
|
172
|
+
literal_opts[:language] = element[LANGUAGE].to_sym if element[LANGUAGE]
|
173
|
+
object = RDF::Literal.new(element[LITERAL], literal_opts)
|
200
174
|
add_triple(path, subject, property, object) if subject && property
|
175
|
+
return
|
201
176
|
end
|
202
177
|
|
203
178
|
# 2.2) ... Otherwise, if the local context is known perform the following steps:
|
204
179
|
# 2.2.1) If a @ key is found, the processor sets the active subject to the
|
205
180
|
# value after Object Processing has been performed.
|
206
|
-
if element[
|
207
|
-
active_subject = expand_term(element[
|
208
|
-
|
209
|
-
#
|
210
|
-
#
|
211
|
-
# subject, the inherited property for the property, and the active
|
212
|
-
# subject for the object.
|
213
|
-
add_triple(path, subject, property, active_subject) if subject && property
|
214
|
-
|
215
|
-
subject = active_subject
|
181
|
+
if element[SUBJECT].is_a?(String)
|
182
|
+
active_subject = expand_term(element[SUBJECT], ec.base, ec)
|
183
|
+
elsif element[SUBJECT]
|
184
|
+
# Recursively process hash or Array values
|
185
|
+
traverse("#{path}[@]", element[SUBJECT], subject, property, ec)
|
216
186
|
else
|
217
187
|
# 2.2.7) If the end of the associative array is detected, and a active subject
|
218
188
|
# was not discovered, then:
|
219
189
|
# 2.2.7.1) Generate a blank node identifier and set it as the active subject.
|
220
|
-
|
190
|
+
active_subject = RDF::Node.new
|
221
191
|
end
|
192
|
+
|
193
|
+
# 2.2.1.1) If the inherited subject and inherited property values are
|
194
|
+
# specified, generate a triple using the inherited subject for the
|
195
|
+
# subject, the inherited property for the property, and the active
|
196
|
+
# subject for the object.
|
197
|
+
# 2.2.7.2) Complete any previously incomplete triples by running all substeps of Step 2.2.1.
|
198
|
+
add_triple(path, subject, property, active_subject) if subject && property
|
199
|
+
subject = active_subject
|
222
200
|
|
223
201
|
element.each do |key, value|
|
224
202
|
# 2.2.3) If a key that is not @context, @, or a, set the active property by
|
@@ -319,23 +297,12 @@ module JSON::LD
|
|
319
297
|
# @raise [RDF::ReaderError] on a syntax error, or a reference to a term which is not defined.
|
320
298
|
def parse_context(ec, context)
|
321
299
|
context.each do |key, value|
|
322
|
-
|
300
|
+
add_debug("parse_context(#{key})", value.inspect)
|
323
301
|
case key
|
324
|
-
when
|
325
|
-
when
|
326
|
-
when
|
327
|
-
#
|
328
|
-
# overwriting duplicate values. In the case where a mapping is indicated to a list of properties
|
329
|
-
# (e.g., { "xsd:anyURI": ["foaf:homepage", "foaf:member"] }, does this overwrite a previous mapping
|
330
|
-
# of { "xsd:anyURI": "foaf:knows" }, or add to it.
|
331
|
-
add_error RDF::ReaderError, "Expected @coerce to reference an associative array" unless value.is_a?(Hash)
|
332
|
-
value.each do |type, property|
|
333
|
-
type_uri = expand_term(type, ec.vocab, ec)
|
334
|
-
[property].flatten.compact.each do |prop|
|
335
|
-
p = expand_term(prop, ec.vocab, ec)
|
336
|
-
ec.coerce[p] = type_uri
|
337
|
-
end
|
338
|
-
end
|
302
|
+
when VOCAB then ec.vocab = value
|
303
|
+
when BASE then ec.base = uri(value)
|
304
|
+
when COERCE
|
305
|
+
# Process after prefix mapping
|
339
306
|
else
|
340
307
|
# Spec confusion: The text indicates to merge each key-value pair into the active context. Is any
|
341
308
|
# processing performed on the values. For instance, could a value be a CURIE, or {"@iri": <value>}?
|
@@ -345,6 +312,21 @@ module JSON::LD
|
|
345
312
|
end
|
346
313
|
end
|
347
314
|
|
315
|
+
if context[COERCE]
|
316
|
+
# Spec confusion: doc says to merge each key-value mapping to the local context's @coerce mapping,
|
317
|
+
# overwriting duplicate values. In the case where a mapping is indicated to a list of properties
|
318
|
+
# (e.g., { "xsd:anyURI": ["foaf:homepage", "foaf:member"] }, does this overwrite a previous mapping
|
319
|
+
# of { "xsd:anyURI": "foaf:knows" }, or add to it.
|
320
|
+
add_error RDF::ReaderError, "Expected @coerce to reference an associative array" unless context[COERCE].is_a?(Hash)
|
321
|
+
context[COERCE].each do |type, property|
|
322
|
+
type_uri = expand_term(type, ec.vocab, ec)
|
323
|
+
[property].flatten.compact.each do |prop|
|
324
|
+
p = expand_term(prop, ec.vocab, ec)
|
325
|
+
ec.coerce[p] = type_uri
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
348
330
|
ec
|
349
331
|
end
|
350
332
|
|
data/lib/json/ld/writer.rb
CHANGED
@@ -2,10 +2,662 @@ module JSON::LD
|
|
2
2
|
##
|
3
3
|
# A JSON-LD parser in Ruby.
|
4
4
|
#
|
5
|
+
# Note that the natural interface is to write a whole graph at a time.
|
6
|
+
# Writing statements or Triples will create a graph to add them to
|
7
|
+
# and then serialize the graph.
|
8
|
+
#
|
9
|
+
# @example Obtaining a JSON-LD writer class
|
10
|
+
# RDF::Writer.for(:jsonld) #=> RDF::N3::Writer
|
11
|
+
# RDF::Writer.for("etc/test.json")
|
12
|
+
# RDF::Writer.for(:file_name => "etc/test.json")
|
13
|
+
# RDF::Writer.for(:file_extension => "json")
|
14
|
+
# RDF::Writer.for(:content_type => "application/turtle")
|
15
|
+
#
|
16
|
+
# @example Serializing RDF graph into an JSON-LD file
|
17
|
+
# JSON::LD::Writer.open("etc/test.json") do |writer|
|
18
|
+
# writer << graph
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @example Serializing RDF statements into an JSON-LD file
|
22
|
+
# JSON::LD::Writer.open("etc/test.json") do |writer|
|
23
|
+
# graph.each_statement do |statement|
|
24
|
+
# writer << statement
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# @example Serializing RDF statements into an JSON-LD string
|
29
|
+
# JSON::LD::Writer.buffer do |writer|
|
30
|
+
# graph.each_statement do |statement|
|
31
|
+
# writer << statement
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# The writer will add prefix definitions, and use them for creating @context definitions, and minting CURIEs
|
36
|
+
#
|
37
|
+
# @example Creating @base, @vocab and @context prefix definitions in output
|
38
|
+
# JSON::LD::Writer.buffer(
|
39
|
+
# :base_uri => "http://example.com/",
|
40
|
+
# :vocab => "http://example.net/"
|
41
|
+
# :prefixes => {
|
42
|
+
# nil => "http://example.com/ns#",
|
43
|
+
# :foaf => "http://xmlns.com/foaf/0.1/"}
|
44
|
+
# ) do |writer|
|
45
|
+
# graph.each_statement do |statement|
|
46
|
+
# writer << statement
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# Select the :canonicalize option to output JSON-LD in canonical form
|
51
|
+
#
|
5
52
|
# @see http://json-ld.org/spec/ED/20110507/
|
53
|
+
# @see http://json-ld.org/spec/ED/20110507/#the-normalization-algorithm
|
6
54
|
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
7
55
|
class Writer < RDF::Writer
|
8
56
|
format Format
|
57
|
+
|
58
|
+
# @attr [Graph] Graph of statements serialized
|
59
|
+
attr :graph
|
60
|
+
# @attr [URI] Base IRI used for relativizing IRIs
|
61
|
+
attr :base_uri
|
62
|
+
# @attr [String] Vocabulary prefix used for relativizing IRIs
|
63
|
+
attr :vocab
|
64
|
+
|
65
|
+
# Type coersion to use for serialization. Defaults to DEFAULT_COERCION
|
66
|
+
#
|
67
|
+
# Maintained as a reverse mapping of `property` => `type`.
|
68
|
+
#
|
69
|
+
# @attr [Hash{RDF::URI => RDF::URI}]
|
70
|
+
attr :coerce, true
|
71
|
+
|
72
|
+
##
|
73
|
+
# Return the pre-serialized Hash before turning into JSON
|
74
|
+
#
|
75
|
+
# @return [Hash]
|
76
|
+
def self.hash(*args, &block)
|
77
|
+
hash = {}
|
78
|
+
self.new(hash, *args, &block)
|
79
|
+
hash
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Initializes the RDF-LD writer instance.
|
84
|
+
#
|
85
|
+
# @param [IO, File] output
|
86
|
+
# the output stream
|
87
|
+
# @param [Hash{Symbol => Object}] options
|
88
|
+
# any additional options
|
89
|
+
# @option options [Encoding] :encoding (Encoding::UTF_8)
|
90
|
+
# the encoding to use on the output stream (Ruby 1.9+)
|
91
|
+
# @option options [Boolean] :canonicalize (false)
|
92
|
+
# whether to canonicalize literals when serializing
|
93
|
+
# @option options [Hash] :prefixes (Hash.new)
|
94
|
+
# the prefix mappings to use (not supported by all writers)
|
95
|
+
# @option options [#to_s] :base_uri (nil)
|
96
|
+
# Base IRI used for relativizing IRIs
|
97
|
+
# @option options [#to_s] :vocab (nil)
|
98
|
+
# Vocabulary prefix used for relativizing IRIs
|
99
|
+
# @option options [Boolean] :standard_prefixes (false)
|
100
|
+
# Add standard prefixes to @prefixes, if necessary.
|
101
|
+
# @yield [writer] `self`
|
102
|
+
# @yieldparam [RDF::Writer] writer
|
103
|
+
# @yieldreturn [void]
|
104
|
+
# @yield [writer]
|
105
|
+
# @yieldparam [RDF::Writer] writer
|
106
|
+
def initialize(output = $stdout, options = {}, &block)
|
107
|
+
super do
|
108
|
+
@graph = RDF::Graph.new
|
109
|
+
@iri_to_prefix = DEFAULT_CONTEXT.dup.delete_if {|k,v| k == COERCE}.invert
|
110
|
+
@coerce = DEFAULT_COERCE.merge(options[:coerce] || {})
|
111
|
+
if block_given?
|
112
|
+
case block.arity
|
113
|
+
when 0 then instance_eval(&block)
|
114
|
+
else block.call(self)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
##
|
121
|
+
# Write whole graph
|
122
|
+
#
|
123
|
+
# @param [Graph] graph
|
124
|
+
# @return [void]
|
125
|
+
def write_graph(graph)
|
126
|
+
add_debug "Add graph #{graph.inspect}"
|
127
|
+
@graph = graph
|
128
|
+
end
|
129
|
+
|
130
|
+
##
|
131
|
+
# Addes a statement to be serialized
|
132
|
+
# @param [RDF::Statement] statement
|
133
|
+
# @return [void]
|
134
|
+
def write_statement(statement)
|
135
|
+
@graph.insert(statement)
|
136
|
+
end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Addes a triple to be serialized
|
140
|
+
# @param [RDF::Resource] subject
|
141
|
+
# @param [RDF::URI] predicate
|
142
|
+
# @param [RDF::Value] object
|
143
|
+
# @return [void]
|
144
|
+
# @raise [NotImplementedError] unless implemented in subclass
|
145
|
+
# @abstract
|
146
|
+
def write_triple(subject, predicate, object)
|
147
|
+
@graph.insert(Statement.new(subject, predicate, object))
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Outputs the Serialized JSON-LD representation of all stored triples.
|
152
|
+
#
|
153
|
+
# @return [void]
|
154
|
+
# @see #write_triple
|
155
|
+
def write_epilogue
|
156
|
+
@base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri] && !@options[:canonicalize]
|
157
|
+
@vocab = @options[:vocab] unless @options[:canonicalize]
|
158
|
+
@debug = @options[:debug]
|
159
|
+
|
160
|
+
reset
|
161
|
+
|
162
|
+
add_debug "\nserialize: graph: #{@graph.size}"
|
163
|
+
|
164
|
+
preprocess
|
165
|
+
|
166
|
+
# Don't generate context for canonical output
|
167
|
+
json_hash = @options[:canonicalize] ? {} : start_document
|
168
|
+
|
169
|
+
elements = []
|
170
|
+
order_subjects.each do |subject|
|
171
|
+
unless is_done?(subject)
|
172
|
+
elements << subject(subject, json_hash)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
return if elements.empty?
|
177
|
+
|
178
|
+
if elements.length == 1 && elements.first.is_a?(Hash)
|
179
|
+
json_hash.merge!(elements.first)
|
180
|
+
else
|
181
|
+
json_hash[SUBJECT] = elements
|
182
|
+
end
|
183
|
+
|
184
|
+
if @output.is_a?(Hash)
|
185
|
+
@output.merge!(json_hash)
|
186
|
+
else
|
187
|
+
json_state = if @options[:canonicalize]
|
188
|
+
JSON::State.new(
|
189
|
+
:indent => "",
|
190
|
+
:space => "",
|
191
|
+
:space_before => "",
|
192
|
+
:object_nl => "",
|
193
|
+
:array_nl => ""
|
194
|
+
)
|
195
|
+
else
|
196
|
+
JSON::State.new(
|
197
|
+
:indent => " ",
|
198
|
+
:space => " ",
|
199
|
+
:space_before => "",
|
200
|
+
:object_nl => "\n",
|
201
|
+
:array_nl => "\n"
|
202
|
+
)
|
203
|
+
end
|
204
|
+
@output.write(json_hash.to_json(json_state))
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# Returns the representation of a IRI reference.
|
210
|
+
#
|
211
|
+
# Spec confusion: should a subject URI be normalized?
|
212
|
+
#
|
213
|
+
# @param [RDF::URI] value
|
214
|
+
# @param [Hash{Symbol => Object}] options
|
215
|
+
# @option options [:subject, :predicate, :object] position
|
216
|
+
# Useful when determining how to serialize.
|
217
|
+
# @option options [RDF::URI] property
|
218
|
+
# Property for object reference, which can be used to return
|
219
|
+
# bare strings, rather than {"iri":}
|
220
|
+
# @return [Object]
|
221
|
+
def format_uri(value, options = {})
|
222
|
+
result = case options[:position]
|
223
|
+
when :subject
|
224
|
+
# attempt base_uri replacement
|
225
|
+
short = value.to_s.sub(base_uri.to_s, "")
|
226
|
+
short == value.to_s ? (get_curie(value) || value.to_s) : short
|
227
|
+
when :predicate
|
228
|
+
# attempt vocab replacement
|
229
|
+
short = TYPE if value == RDF.type
|
230
|
+
short ||= value.to_s.sub(@vocab.to_s, "")
|
231
|
+
short == value.to_s ? (get_curie(value) || value.to_s) : short
|
232
|
+
else
|
233
|
+
# Encode like a subject
|
234
|
+
iri_range?(options[:property]) ?
|
235
|
+
format_uri(value, :position => :subject) :
|
236
|
+
{:iri => format_uri(value, :position => :subject)}
|
237
|
+
end
|
238
|
+
|
239
|
+
add_debug("format_uri(#{options.inspect}, #{value.inspect}) => #{result.inspect}")
|
240
|
+
result
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# @param [RDF::Node] value
|
245
|
+
# @param [Hash{Symbol => Object}] options
|
246
|
+
# @return [String]
|
247
|
+
# @raise [NotImplementedError] unless implemented in subclass
|
248
|
+
# @abstract
|
249
|
+
def format_node(value, options = {})
|
250
|
+
format_uri(value, options)
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Returns the representation of a literal.
|
255
|
+
#
|
256
|
+
# @param [RDF::Literal, String, #to_s] literal
|
257
|
+
# @param [Hash{Symbol => Object}] options
|
258
|
+
# @option options [RDF::URI] property
|
259
|
+
# Property referencing literal for type coercion
|
260
|
+
# @return [Object]
|
261
|
+
def format_literal(literal, options = {})
|
262
|
+
if options[:canonical] || @options[:canonicalize]
|
263
|
+
return {
|
264
|
+
:literal => literal.value,
|
265
|
+
:datatype => (format_uri(literal.datatype, :position => :subject) if literal.has_datatype?),
|
266
|
+
:language => (literal.language.to_s if literal.has_language?)
|
267
|
+
}.delete_if {|k,v| v.nil?}
|
268
|
+
end
|
269
|
+
|
270
|
+
case literal
|
271
|
+
when RDF::Literal::Integer, RDF::Literal::Boolean
|
272
|
+
literal.object
|
273
|
+
when RDF::Literal
|
274
|
+
if datatype_range?(options[:property]) || !(literal.has_datatype? || literal.has_language?)
|
275
|
+
# Datatype coercion where literal has the same datatype
|
276
|
+
literal.value
|
277
|
+
else
|
278
|
+
format_literal(literal, :canonical => true)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
##
|
284
|
+
# Serialize an RDF list
|
285
|
+
# @param [RDF::URI] object
|
286
|
+
# @param [Hash{Symbol => Object}] options
|
287
|
+
# @option options [RDF::URI] property
|
288
|
+
# Property referencing literal for type coercion
|
289
|
+
# @return [Array<Array<Object>>]
|
290
|
+
def format_list(object, options = {})
|
291
|
+
predicate = options[:property]
|
292
|
+
list = []
|
293
|
+
|
294
|
+
add_debug "format_list(#{object}, #{predicate})"
|
295
|
+
|
296
|
+
@depth += 1
|
297
|
+
while object do
|
298
|
+
subject_done(object)
|
299
|
+
p = @graph.properties(object)
|
300
|
+
item = p.fetch(RDF.first.to_s, []).first
|
301
|
+
if item
|
302
|
+
add_debug "format_list serialize #{item.inspect}"
|
303
|
+
list << if predicate || item.literal?
|
304
|
+
property(predicate, item)
|
305
|
+
else
|
306
|
+
subject(item)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
object = p.fetch(RDF.rest.to_s, []).first
|
310
|
+
end
|
311
|
+
@depth -= 1
|
312
|
+
|
313
|
+
# Returns
|
314
|
+
add_debug "format_list => #{[list].inspect}"
|
315
|
+
[list]
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
##
|
320
|
+
# Generate @context
|
321
|
+
# @return [Hash]
|
322
|
+
def start_document
|
323
|
+
ctx = {}
|
324
|
+
ctx[BASE] = base_uri.to_s if base_uri
|
325
|
+
ctx[VOCAB] = vocab.to_s if vocab
|
326
|
+
|
327
|
+
# Prefixes
|
328
|
+
prefixes.keys.sort {|a,b| a.to_s <=> b.to_s}.each do |k|
|
329
|
+
next if DEFAULT_CONTEXT.has_key?(k.to_s)
|
330
|
+
add_debug "prefix[#{k}] => #{prefixes[k]}"
|
331
|
+
ctx[k.to_s] = prefixes[k].to_s
|
332
|
+
end
|
333
|
+
|
334
|
+
# Coerce
|
335
|
+
add_debug "start_doc: coerce= #{coerce.inspect}"
|
336
|
+
unless coerce == DEFAULT_COERCE
|
337
|
+
c_h = {}
|
338
|
+
coerce.keys.sort.each do |k|
|
339
|
+
next if coerce[k] == DEFAULT_COERCE[k] ||
|
340
|
+
coerce[k] == false ||
|
341
|
+
coerce[k] == RDF::XSD.integer ||
|
342
|
+
coerce[k] == RDF::XSD.boolean
|
343
|
+
k_iri = format_uri(k, :position => :predicate)
|
344
|
+
d_iri = format_uri(coerce[k], :position => :subject)
|
345
|
+
add_debug "coerce[#{k_iri}] => #{d_iri}"
|
346
|
+
case c_h[d_iri]
|
347
|
+
when nil
|
348
|
+
c_h[d_iri] = k_iri
|
349
|
+
when Array
|
350
|
+
c_h[d_iri] << k_iri
|
351
|
+
else
|
352
|
+
c_h[d_iri] = [c_h[d_iri], k_iri]
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
ctx[COERCE] = c_h unless c_h.empty?
|
357
|
+
end
|
358
|
+
|
359
|
+
add_debug "start_doc: context=#{ctx.inspect}"
|
360
|
+
# Return hash with @context, or empty
|
361
|
+
ctx.empty? ? {} : {CONTEXT => ctx}
|
362
|
+
end
|
363
|
+
|
364
|
+
# Perform any preprocessing of statements required
|
365
|
+
def preprocess
|
366
|
+
# Load defined prefixes
|
367
|
+
(@options[:prefixes] || {}).each_pair do |k, v|
|
368
|
+
@iri_to_prefix[v.to_s] = k
|
369
|
+
end
|
370
|
+
@options[:prefixes] = {} # Will define actual used when matched
|
371
|
+
|
372
|
+
@graph.each {|statement| preprocess_statement(statement)}
|
373
|
+
end
|
374
|
+
|
375
|
+
# Perform any statement preprocessing required. This is used to perform reference counts and determine required
|
376
|
+
# prefixes.
|
377
|
+
# @param [Statement] statement
|
378
|
+
def preprocess_statement(statement)
|
379
|
+
add_debug "preprocess: #{statement.inspect}"
|
380
|
+
references = ref_count(statement.object) + 1
|
381
|
+
@references[statement.object] = references
|
382
|
+
@subjects[statement.subject] = true
|
383
|
+
|
384
|
+
# Pre-fetch qnames, to fill prefixes
|
385
|
+
get_curie(statement.subject)
|
386
|
+
get_curie(statement.predicate)
|
387
|
+
if statement.object.literal?
|
388
|
+
datatype_range?(statement.predicate) # To figure out coercion requirements
|
389
|
+
else
|
390
|
+
iri_range?(statement.predicate)
|
391
|
+
get_curie(statement.object)
|
392
|
+
end
|
393
|
+
|
394
|
+
@references[statement.predicate] = ref_count(statement.predicate) + 1
|
395
|
+
end
|
396
|
+
|
397
|
+
# Serialize a subject
|
398
|
+
# Option contains referencing property, if this is recursive
|
399
|
+
# @return [Hash]
|
400
|
+
def subject(subject, options = {})
|
401
|
+
defn = {}
|
402
|
+
|
403
|
+
raise RDF::WriterError, "Illegal use of subject #{subject.inspect}, not supported" unless subject.resource?
|
404
|
+
|
405
|
+
subject_done(subject)
|
406
|
+
properties = @graph.properties(subject)
|
407
|
+
add_debug "subject: #{subject.inspect}, props: #{properties.inspect}"
|
408
|
+
|
409
|
+
@graph.query(:subject => subject).each do |st|
|
410
|
+
raise RDF::WriterError, "Illegal use of predicate #{st.predicate.inspect}, not supported in RDF/XML" unless st.predicate.uri?
|
411
|
+
end
|
412
|
+
|
413
|
+
if subject.node? && ref_count(subject) > (options[:property] ? 1 : 0) && options[:canonicalize]
|
414
|
+
raise RDF::WriterError, "Can't serialize named node when normalizing"
|
415
|
+
end
|
416
|
+
|
417
|
+
# Subject may be a list
|
418
|
+
if is_valid_list?(subject)
|
419
|
+
add_debug "subject is a list"
|
420
|
+
defn[SUBJECT] = format_list(subject)
|
421
|
+
properties.delete(RDF.first.to_s)
|
422
|
+
properties.delete(RDF.rest.to_s)
|
423
|
+
|
424
|
+
# Special case, if there are no properties, then we can just serialize the list itself
|
425
|
+
return defn[SUBJECT].first if properties.empty?
|
426
|
+
elsif subject.uri? || ref_count(subject) > 1
|
427
|
+
add_debug "subject is a uri"
|
428
|
+
# Don't need to set subject if it's a Node without references
|
429
|
+
defn[SUBJECT] = format_uri(subject, :position => :subject)
|
430
|
+
else
|
431
|
+
add_debug "subject is an unreferenced BNode"
|
432
|
+
end
|
433
|
+
|
434
|
+
prop_list = order_properties(properties)
|
435
|
+
#add_debug "=> property order: #{prop_list.to_sentence}"
|
436
|
+
|
437
|
+
prop_list.each do |prop|
|
438
|
+
predicate = RDF::URI.intern(prop)
|
439
|
+
|
440
|
+
p_iri = format_uri(predicate, :position => :predicate)
|
441
|
+
@depth += 1
|
442
|
+
defn[p_iri] = property(predicate, properties[prop])
|
443
|
+
add_debug "prop(#{p_iri}) => #{properties[prop]} => #{defn[p_iri].inspect}"
|
444
|
+
@depth -= 1
|
445
|
+
end
|
446
|
+
|
447
|
+
add_debug "subject: #{subject} has defn: #{defn.inspect}"
|
448
|
+
defn
|
449
|
+
end
|
450
|
+
|
451
|
+
##
|
452
|
+
# Serialize objects for a property
|
453
|
+
#
|
454
|
+
# Spec confusion: sorting of multi-valued properties not adequately specified.
|
455
|
+
#
|
456
|
+
# @param [RDF::URI] predicate
|
457
|
+
# @param [Array<RDF::URI>, RDF::URI] objects
|
458
|
+
# @param [Hash{Symbol => Object}] options
|
459
|
+
# @return [Array, Hash, Object]
|
460
|
+
def property(predicate, objects, options = {})
|
461
|
+
objects = objects.first if objects.is_a?(Array) && objects.length == 1
|
462
|
+
case objects
|
463
|
+
when Array
|
464
|
+
objects.sort_by(&:to_s).map {|o| property(predicate, o, options)}
|
465
|
+
when RDF::Literal
|
466
|
+
format_literal(objects, options.merge(:property => predicate))
|
467
|
+
else
|
468
|
+
if is_valid_list?(objects)
|
469
|
+
format_list(objects, :property => predicate)
|
470
|
+
elsif is_done?(objects) || !@subjects.include?(objects)
|
471
|
+
format_uri(objects, :position => :object, :property => predicate)
|
472
|
+
else
|
473
|
+
subject(objects, :property => predicate)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
##
|
479
|
+
# Return a CURIE for the IRI, or nil. Adds namespace of CURIE to defined prefixes
|
480
|
+
# @param [RDF::Resource] resource
|
481
|
+
# @return [String, nil] value to use to identify IRI
|
482
|
+
def get_curie(resource)
|
483
|
+
add_debug "get_curie(#{resource.inspect})"
|
484
|
+
case resource
|
485
|
+
when RDF::Node
|
486
|
+
return resource.to_s
|
487
|
+
when RDF::URI
|
488
|
+
iri = resource.to_s
|
489
|
+
return iri if options[:canonicalize]
|
490
|
+
else
|
491
|
+
return nil
|
492
|
+
end
|
493
|
+
|
494
|
+
curie = case
|
495
|
+
when @iri_to_curie.has_key?(iri)
|
496
|
+
return @iri_to_curie[iri]
|
497
|
+
when u = @iri_to_prefix.keys.detect {|u| iri.index(u.to_s) == 0}
|
498
|
+
# Use a defined prefix
|
499
|
+
prefix = @iri_to_prefix[u]
|
500
|
+
prefix(prefix, u) # Define for output
|
501
|
+
iri.sub(u.to_s, "#{prefix}:")
|
502
|
+
when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| iri.index(v.to_uri.to_s) == 0}
|
503
|
+
prefix = vocab.__name__.to_s.split('::').last.downcase
|
504
|
+
@iri_to_prefix[vocab.to_uri.to_s] = prefix
|
505
|
+
prefix(prefix, vocab.to_uri) # Define for output
|
506
|
+
iri.sub(vocab.to_uri.to_s, "#{prefix}:")
|
507
|
+
else
|
508
|
+
nil
|
509
|
+
end
|
510
|
+
|
511
|
+
@iri_to_curie[iri] = curie
|
512
|
+
rescue Addressable::URI::InvalidURIError => e
|
513
|
+
raise RDF::WriterError, "Invalid IRI #{resource.inspect}: #{e.message}"
|
514
|
+
end
|
515
|
+
|
516
|
+
##
|
517
|
+
# Take a hash from predicate IRIs to lists of values.
|
518
|
+
# Sort the lists of values. Return a sorted list of properties.
|
519
|
+
# @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings
|
520
|
+
# @return [Array<String>}] Ordered list of properties.
|
521
|
+
def order_properties(properties)
|
522
|
+
# Make sorted list of properties
|
523
|
+
prop_list = []
|
524
|
+
|
525
|
+
properties.keys.sort do |a,b|
|
526
|
+
format_uri(a, :position => :predicate) <=> format_uri(b, :position => :predicate)
|
527
|
+
end.each do |prop|
|
528
|
+
prop_list << prop.to_s
|
529
|
+
end
|
530
|
+
|
531
|
+
prop_list
|
532
|
+
end
|
533
|
+
|
534
|
+
# Order subjects for output. Override this to output subjects in another order.
|
535
|
+
#
|
536
|
+
# Uses #base_uri.
|
537
|
+
# @return [Array<Resource>] Ordered list of subjects
|
538
|
+
def order_subjects
|
539
|
+
seen = {}
|
540
|
+
subjects = []
|
541
|
+
|
542
|
+
return @subjects.keys.sort do |a,b|
|
543
|
+
format_iri(a, :position => :subject) <=> format_iri(b, :position => :subject)
|
544
|
+
end if @options[:canonicalize]
|
545
|
+
|
546
|
+
# Start with base_uri
|
547
|
+
if base_uri && @subjects.keys.include?(base_uri)
|
548
|
+
subjects << base_uri
|
549
|
+
seen[base_uri] = true
|
550
|
+
end
|
551
|
+
|
552
|
+
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
553
|
+
recursable = @subjects.keys.
|
554
|
+
select {|s| !seen.include?(s)}.
|
555
|
+
map {|r| [r.is_a?(RDF::Node) ? 1 : 0, ref_count(r), r]}.
|
556
|
+
sort
|
557
|
+
|
558
|
+
subjects += recursable.map{|r| r.last}
|
559
|
+
end
|
560
|
+
|
561
|
+
# Return the number of times this node has been referenced in the object position
|
562
|
+
# @return [Integer]
|
563
|
+
def ref_count(node)
|
564
|
+
@references.fetch(node, 0)
|
565
|
+
end
|
566
|
+
|
567
|
+
##
|
568
|
+
# Does predicate have a range of IRI?
|
569
|
+
# @param [RDF::URI] predicate
|
570
|
+
# @return [Boolean]
|
571
|
+
def iri_range?(predicate)
|
572
|
+
return false if predicate.nil? || @options[:canonicalize]
|
573
|
+
|
574
|
+
unless coerce.has_key?(predicate)
|
575
|
+
# objects of all statements with the predicate may not be literal
|
576
|
+
coerce[predicate] = @graph.query(:predicate => predicate).to_a.any? {|st| st.object.literal?} ?
|
577
|
+
false : RDF::XSD.anyURI
|
578
|
+
end
|
579
|
+
|
580
|
+
add_debug "iri_range(#{predicate}) = #{coerce[predicate].inspect}"
|
581
|
+
coerce[predicate] == RDF::XSD.anyURI
|
582
|
+
end
|
583
|
+
|
584
|
+
##
|
585
|
+
# Does predicate have a range of specific typed literal?
|
586
|
+
# @param [RDF::URI] predicate
|
587
|
+
# @return [Boolean]
|
588
|
+
def datatype_range?(predicate)
|
589
|
+
unless coerce.has_key?(predicate)
|
590
|
+
# objects of all statements with the predicate must be literal
|
591
|
+
# and have the same non-nil datatype
|
592
|
+
dt = nil
|
593
|
+
@graph.query(:predicate => predicate) do |st|
|
594
|
+
if st.object.literal? && st.object.has_datatype?
|
595
|
+
dt = st.object.datatype if dt.nil?
|
596
|
+
dt = false unless dt == st.object.datatype
|
597
|
+
else
|
598
|
+
dt = false
|
599
|
+
end
|
600
|
+
end
|
601
|
+
add_debug "range(#{predicate}) = #{dt.inspect}"
|
602
|
+
coerce[predicate] = dt
|
603
|
+
end
|
604
|
+
|
605
|
+
coerce[predicate]
|
606
|
+
end
|
607
|
+
|
608
|
+
# Reset internal helper instance variables
|
609
|
+
def reset
|
610
|
+
@depth = 0
|
611
|
+
@references = {}
|
612
|
+
@serialized = {}
|
613
|
+
@subjects = {}
|
614
|
+
@iri_to_curie = {}
|
615
|
+
end
|
616
|
+
|
617
|
+
# Add debug event to debug array, if specified
|
618
|
+
#
|
619
|
+
# @param [String] message::
|
620
|
+
def add_debug(message)
|
621
|
+
msg = "#{" " * @depth * 2}#{message}"
|
622
|
+
STDERR.puts msg if ::JSON::LD::debug?
|
623
|
+
@debug << msg if @debug.is_a?(Array)
|
624
|
+
end
|
625
|
+
|
626
|
+
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
627
|
+
def is_valid_list?(l)
|
628
|
+
props = @graph.properties(l)
|
629
|
+
unless l.node? && props.has_key?(RDF.first.to_s) || l == RDF.nil
|
630
|
+
add_debug "is_valid_list: false, #{l.inspect}: #{props.inspect}"
|
631
|
+
return false
|
632
|
+
end
|
633
|
+
|
634
|
+
while l && l != RDF.nil do
|
635
|
+
#add_debug "is_valid_list(length): #{props.length}"
|
636
|
+
return false unless props.has_key?(RDF.first.to_s) && props.has_key?(RDF.rest.to_s)
|
637
|
+
n = props[RDF.rest.to_s]
|
638
|
+
unless n.is_a?(Array) && n.length == 1
|
639
|
+
add_debug "is_valid_list: false, #{n.inspect}"
|
640
|
+
return false
|
641
|
+
end
|
642
|
+
l = n.first
|
643
|
+
unless l.node? || l == RDF.nil
|
644
|
+
add_debug "is_valid_list: false, #{l.inspect}"
|
645
|
+
return false
|
646
|
+
end
|
647
|
+
props = @graph.properties(l)
|
648
|
+
end
|
649
|
+
add_debug "is_valid_list: valid"
|
650
|
+
true
|
651
|
+
end
|
652
|
+
|
653
|
+
def is_done?(subject)
|
654
|
+
@serialized.include?(subject)
|
655
|
+
end
|
656
|
+
|
657
|
+
# Mark a subject as done.
|
658
|
+
def subject_done(subject)
|
659
|
+
@serialized[subject] = true
|
660
|
+
end
|
9
661
|
end
|
10
662
|
end
|
11
663
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: json-ld
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Gregg Kellogg
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-06-29 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
|
-
- -
|
22
|
+
- - ">="
|
23
23
|
- !ruby/object:Gem::Version
|
24
24
|
version: 0.3.3
|
25
25
|
type: :runtime
|
@@ -63,11 +63,66 @@ dependencies:
|
|
63
63
|
requirement: &id005 !ruby/object:Gem::Requirement
|
64
64
|
none: false
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.3.2
|
69
69
|
type: :development
|
70
70
|
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rdf-n3
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 0.3.3
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rdf-isomorphic
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.3.4
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id007
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: sxp
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
type: :development
|
103
|
+
version_requirements: *id008
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: sparql-algebra
|
106
|
+
prerelease: false
|
107
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: "0"
|
113
|
+
type: :development
|
114
|
+
version_requirements: *id009
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: sparql-grammar
|
117
|
+
prerelease: false
|
118
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: "0"
|
124
|
+
type: :development
|
125
|
+
version_requirements: *id010
|
71
126
|
description: JSON-LD reader/writer for Ruby.
|
72
127
|
email: public-rdf-ruby@w3.org
|
73
128
|
executables: []
|
@@ -81,6 +136,7 @@ files:
|
|
81
136
|
- README
|
82
137
|
- UNLICENSE
|
83
138
|
- VERSION
|
139
|
+
- lib/json/ld/extensions.rb
|
84
140
|
- lib/json/ld/format.rb
|
85
141
|
- lib/json/ld/reader.rb
|
86
142
|
- lib/json/ld/version.rb
|