json-ld 1.99.2 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,14 +6,68 @@ module JSON::LD
6
6
  include Utils
7
7
 
8
8
  ##
9
- # @param [Hash{String => Object}] node
10
- # @param [RDF::Resource] graph_name
9
+ #
10
+ # @param [Hash{String => Hash}] active_graph
11
+ # A hash of IRI to Node definitions
11
12
  # @yield statement
12
13
  # @yieldparam [RDF::Statement] statement
13
- # @return RDF::Resource the subject of this item
14
- def item_to_rdf(item, graph_name: nil, &block)
15
- # Just return value object as Term
16
- if value?(item)
14
+ def graph_to_rdf(active_graph, &block)
15
+ log_debug('graph_to_rdf') {"graph_to_rdf: #{active_graph.inspect}"}
16
+
17
+ # For each id-node in active_graph
18
+ active_graph.each do |id, node|
19
+ # Initialize subject as the IRI or BNode representation of id
20
+ subject = as_resource(id)
21
+ log_debug("graph_to_rdf") {"subject: #{subject.to_ntriples rescue 'malformed rdf'} (id: #{id})"}
22
+
23
+ # For each property-values in node
24
+ node.each do |property, values|
25
+ case property
26
+ when '@type'
27
+ # If property is @type, construct triple as an RDF Triple composed of id, rdf:type, and object from values where id and object are represented either as IRIs or Blank Nodes
28
+ values.each do |value|
29
+ object = as_resource(value)
30
+ log_debug("graph_to_rdf") {"type: #{object.to_ntriples rescue 'malformed rdf'}"}
31
+ yield RDF::Statement(subject, RDF.type, object)
32
+ end
33
+ when /^@/
34
+ # Otherwise, if @type is any other keyword, skip to the next property-values pair
35
+ else
36
+ # Otherwise, property is an IRI or Blank Node identifier
37
+ # Initialize predicate from property as an IRI or Blank node
38
+ predicate = as_resource(property)
39
+ log_debug("graph_to_rdf") {"predicate: #{predicate.to_ntriples rescue 'malformed rdf'}"}
40
+
41
+ # For each item in values
42
+ values.each do |item|
43
+ if item.has_key?('@list')
44
+ log_debug("graph_to_rdf") {"list: #{item.inspect}"}
45
+ # If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
46
+ object = parse_list(item['@list']) {|stmt| yield stmt}
47
+
48
+ # Append a triple composed of subject, prediate, and object to results and add all triples from list_results to results.
49
+ yield RDF::Statement(subject, predicate, object)
50
+ else
51
+ # Otherwise, item is a value object or a node definition. Generate object as the result of the Object Converstion algorithm passing item.
52
+ object = parse_object(item)
53
+ log_debug("graph_to_rdf") {"object: #{object.to_ntriples rescue 'malformed rdf'}"}
54
+ # Append a triple composed of subject, prediate, and literal to results.
55
+ yield RDF::Statement(subject, predicate, object)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ ##
64
+ # Parse an item, either a value object or a node definition
65
+ # @param [Hash] item
66
+ # @return [RDF::Value]
67
+ def parse_object(item)
68
+ if item.has_key?('@value')
69
+ # Otherwise, if element is a JSON object that contains the key @value
70
+ # Initialize value to the value associated with the @value key in element. Initialize datatype to the value associated with the @type key in element, or null if element does not contain that key.
17
71
  value, datatype = item.fetch('@value'), item.fetch('@type', nil)
18
72
 
19
73
  case value
@@ -21,7 +75,7 @@ module JSON::LD
21
75
  # If value is true or false, then set value its canonical lexical form as defined in the section Data Round Tripping. If datatype is null, set it to xsd:boolean.
22
76
  value = value.to_s
23
77
  datatype ||= RDF::XSD.boolean.to_s
24
- when Integer, Float, Fixnum
78
+ when Float, Fixnum
25
79
  # Otherwise, if value is a number, then set value to its canonical lexical form as defined in the section Data Round Tripping. If datatype is null, set it to either xsd:integer or xsd:double, depending on if the value contains a fractional and/or an exponential component.
26
80
  lit = RDF::Literal.new(value, canonicalize: true)
27
81
  value = lit.to_s
@@ -34,77 +88,13 @@ module JSON::LD
34
88
 
35
89
  # Initialize literal as an RDF literal using value and datatype. If element has the key @language and datatype is xsd:string, then add the value associated with the @language key as the language of the object.
36
90
  language = item.fetch('@language', nil)
37
- return RDF::Literal.new(value, datatype: datatype, language: language)
91
+ RDF::Literal.new(value, datatype: datatype, language: language)
92
+ else
93
+ # Otherwise, value must be a node definition containing only @id whos value is an IRI or Blank Node identifier
94
+ raise "Expected node reference, got #{item.inspect}" unless item.keys == %w(@id)
95
+ # Return value associated with @id as an IRI or Blank node
96
+ as_resource(item['@id'])
38
97
  end
39
-
40
- subject = item['@id'] ? as_resource(item['@id']) : node
41
- debug("item_to_rdf") {"subject: #{subject.to_ntriples rescue 'malformed rdf'}"}
42
- item.each do |property, values|
43
- case property
44
- when '@type'
45
- # If property is @type, construct triple as an RDF Triple composed of id, rdf:type, and object from values where id and object are represented either as IRIs or Blank Nodes
46
- values.each do |v|
47
- object = as_resource(v)
48
- debug("item_to_rdf") {"type: #{object.to_ntriples rescue 'malformed rdf'}"}
49
- yield RDF::Statement(subject, RDF.type, object, graph_name: graph_name)
50
- end
51
- when '@graph'
52
- values = [values].compact unless values.is_a?(Array)
53
- values.each do |nd|
54
- item_to_rdf(nd, graph_name: subject, &block)
55
- end
56
- when '@reverse'
57
- raise "Huh?" unless values.is_a?(Hash)
58
- values.each do |prop, vv|
59
- predicate = as_resource(prop)
60
- debug("item_to_rdf") {"@reverse predicate: #{predicate.to_ntriples rescue 'malformed rdf'}"}
61
- # For each item in values
62
- vv.each do |v|
63
- if list?(v)
64
- debug("item_to_rdf") {"list: #{v.inspect}"}
65
- # If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
66
- object = parse_list(v['@list'], graph_name: graph_name, &block)
67
-
68
- # Append a triple composed of object, prediate, and object to results and add all triples from list_results to results.
69
- yield RDF::Statement(object, predicate, subject, graph_name: graph_name)
70
- else
71
- # Otherwise, item is a value object or a node definition. Generate object as the result of the Object Converstion algorithm passing item.
72
- object = item_to_rdf(v, graph_name: graph_name, &block)
73
- debug("item_to_rdf") {"subject: #{object.to_ntriples rescue 'malformed rdf'}"}
74
- # yield subject, prediate, and literal to results.
75
- yield RDF::Statement(object, predicate, subject, graph_name: graph_name)
76
- end
77
- end
78
- end
79
- when /^@/
80
- # Otherwise, if @type is any other keyword, skip to the next property-values pair
81
- else
82
- # Otherwise, property is an IRI or Blank Node identifier
83
- # Initialize predicate from property as an IRI or Blank node
84
- predicate = as_resource(property)
85
- debug("item_to_rdf") {"predicate: #{predicate.to_ntriples rescue 'malformed rdf'}"}
86
-
87
- # For each item in values
88
- values.each do |v|
89
- if list?(v)
90
- debug("item_to_rdf") {"list: #{v.inspect}"}
91
- # If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
92
- object = parse_list(v['@list'], graph_name: graph_name, &block)
93
-
94
- # Append a triple composed of subject, prediate, and object to results and add all triples from list_results to results.
95
- yield RDF::Statement(subject, predicate, object, graph_name: graph_name)
96
- else
97
- # Otherwise, item is a value object or a node definition. Generate object as the result of the Object Converstion algorithm passing item.
98
- object = item_to_rdf(v, graph_name: graph_name, &block)
99
- debug("item_to_rdf") {"object: #{object.to_ntriples rescue 'malformed rdf'}"}
100
- # yield subject, prediate, and literal to results.
101
- yield RDF::Statement(subject, predicate, object, graph_name: graph_name)
102
- end
103
- end
104
- end
105
- end
106
-
107
- subject
108
98
  end
109
99
 
110
100
  ##
@@ -116,24 +106,24 @@ module JSON::LD
116
106
  # @yieldparam [RDF::Resource] statement
117
107
  # @return [Array<RDF::Statement>]
118
108
  # Statements for each item in the list
119
- def parse_list(list, graph_name: nil, &block)
120
- debug('parse_list') {"list: #{list.inspect}"}
109
+ def parse_list(list)
110
+ log_debug('parse_list') {"list: #{list.inspect}"}
121
111
 
122
112
  last = list.pop
123
113
  result = first_bnode = last ? node : RDF.nil
124
114
 
125
115
  list.each do |list_item|
126
116
  # Set first to the result of the Object Converstion algorithm passing item.
127
- object = item_to_rdf(list_item, graph_name: graph_name, &block)
128
- yield RDF::Statement(first_bnode, RDF.first, object, graph_name: graph_name)
117
+ object = parse_object(list_item)
118
+ yield RDF::Statement(first_bnode, RDF.first, object)
129
119
  rest_bnode = node
130
- yield RDF::Statement(first_bnode, RDF.rest, rest_bnode, graph_name: graph_name)
120
+ yield RDF::Statement(first_bnode, RDF.rest, rest_bnode)
131
121
  first_bnode = rest_bnode
132
122
  end
133
123
  if last
134
- object = item_to_rdf(last, graph_name: graph_name, &block)
135
- yield RDF::Statement(first_bnode, RDF.first, object, graph_name: graph_name)
136
- yield RDF::Statement(first_bnode, RDF.rest, RDF.nil, graph_name: graph_name)
124
+ object = parse_object(last)
125
+ yield RDF::Statement(first_bnode, RDF.first, object)
126
+ yield RDF::Statement(first_bnode, RDF.rest, RDF.nil)
137
127
  end
138
128
  result
139
129
  end
data/lib/json/ld/utils.rb CHANGED
@@ -70,7 +70,7 @@ module JSON::LD
70
70
  def as_resource(id, base = nil)
71
71
  @nodes ||= {} # Re-use BNodes
72
72
  if id[0,2] == '_:'
73
- (@nodes[id] ||= RDF::Node.new(namer.get_sym(id)))
73
+ (@nodes[id] ||= RDF::Node.new(id[2..-1]))
74
74
  elsif base
75
75
  base.join(id)
76
76
  else
@@ -207,35 +207,6 @@ module JSON::LD
207
207
  end
208
208
  end
209
209
  end
210
-
211
- # Add debug event to debug array, if specified
212
- #
213
- # param [String] message
214
- # yieldreturn [String] appended to message, to allow for lazy-evaluation of message
215
- def debug(*args)
216
- return unless ::JSON::LD.debug? || @options[:debug]
217
- depth = @depth || 0
218
- list = args
219
- list << yield if block_given?
220
- message = " " * depth * 2 + list.join(": ")
221
- case @options[:debug]
222
- when Array
223
- @options[:debug] << message
224
- when TrueClass
225
- $stderr.puts message
226
- else
227
- $stderr.puts message if JSON::LD::debug?
228
- end
229
- end
230
-
231
- # Increase depth around a method invocation
232
- def depth(options = {})
233
- old_depth = @depth || 0
234
- @depth = (options[:depth] || old_depth) + 1
235
- yield
236
- ensure
237
- @depth = old_depth
238
- end
239
210
  end
240
211
 
241
212
  ##
@@ -54,6 +54,7 @@ module JSON::LD
54
54
  class Writer < RDF::Writer
55
55
  include StreamingWriter
56
56
  include Utils
57
+ include RDF::Util::Logger
57
58
  format Format
58
59
 
59
60
  # @!attribute [r] graph
@@ -64,6 +65,39 @@ module JSON::LD
64
65
  # @return [Context] context used to load and administer contexts
65
66
  attr_reader :context
66
67
 
68
+ ##
69
+ # JSON-LD Writer options
70
+ # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Writer#options-class_method
71
+ def self.options
72
+ super + [
73
+ RDF::CLI::Option.new(
74
+ symbol: :compactArrays,
75
+ datatype: TrueClass,
76
+ on: ["--compact-arrays"],
77
+ description: "Replaces arrays with just one element with that element during compaction.") {true},
78
+ RDF::CLI::Option.new(
79
+ symbol: :context,
80
+ datatype: RDF::URI,
81
+ on: ["--context CONTEXT"],
82
+ description: "Context to use when serializing. Constructed context for native serialization.") {|arg| RDF::URI(arg)},
83
+ RDF::CLI::Option.new(
84
+ symbol: :frame,
85
+ datatype: RDF::URI,
86
+ on: ["--frame FRAME"],
87
+ description: "Frame to use when serializing.") {|arg| RDF::URI(arg)},
88
+ RDF::CLI::Option.new(
89
+ symbol: :stream,
90
+ datatype: TrueClass,
91
+ on: ["--stream"],
92
+ description: "Do not attempt to optimize graph presentation, suitable for streaming large graphs.") {true},
93
+ RDF::CLI::Option.new(
94
+ symbol: :useRdfType,
95
+ datatype: TrueClass,
96
+ on: ["--use-rdf-type"],
97
+ description: "Treat `rdf:type` like a normal property instead of using `@type`.") {true},
98
+ ]
99
+ end
100
+
67
101
  ##
68
102
  # Initializes the RDF-LD writer instance.
69
103
  #
@@ -97,7 +131,6 @@ module JSON::LD
97
131
  options[:base] ||= options[:base_uri] if options.has_key?(:base_uri)
98
132
  super do
99
133
  @repo = RDF::Repository.new
100
- @debug = @options[:debug]
101
134
 
102
135
  if block_given?
103
136
  case block.arity
@@ -108,42 +141,39 @@ module JSON::LD
108
141
  end
109
142
  end
110
143
 
111
- ##
112
- # Adds a statement to be serialized
113
- # @param [RDF::Statement] statement
114
- # @return [void]
115
- def write_statement(statement)
116
- case
117
- when @options[:stream]
118
- stream_statement(statement)
119
- else
120
- # Add to repo and output in epilogue
121
- @repo.insert(statement)
122
- end
123
- end
124
-
125
144
  ##
126
145
  # Addes a triple to be serialized
127
146
  # @param [RDF::Resource] subject
128
147
  # @param [RDF::URI] predicate
129
148
  # @param [RDF::Value] object
130
149
  # @return [void]
131
- # @raise [NotImplementedError] unless implemented in subclass
132
150
  # @abstract
133
151
  def write_triple(subject, predicate, object)
134
- write_statement(Statement.new(subject, predicate, object))
152
+ write_quad(subject, predicate, object, nil)
153
+ end
154
+
155
+ ##
156
+ # Outputs the N-Quads representation of a statement.
157
+ #
158
+ # @param [RDF::Resource] subject
159
+ # @param [RDF::URI] predicate
160
+ # @param [RDF::Term] object
161
+ # @return [void]
162
+ def write_quad(subject, predicate, object, graph_name)
163
+ statement = RDF::Statement.new(subject, predicate, object, graph_name: graph_name)
164
+ if @options[:stream]
165
+ stream_statement(statement)
166
+ else
167
+ @repo.insert(statement)
168
+ end
135
169
  end
136
170
 
137
171
  ##
138
172
  # Necessary for streaming
139
173
  # @return [void] `self`
140
174
  def write_prologue
141
- case
142
- when @options[:stream]
143
- stream_prologue
144
- else
145
- super
146
- end
175
+ stream_prologue if @options[:stream]
176
+ super
147
177
  end
148
178
 
149
179
  ##
@@ -155,40 +185,45 @@ module JSON::LD
155
185
  # @return [void]
156
186
  # @see #write_triple
157
187
  def write_epilogue
158
- return stream_epilogue if @options[:stream]
188
+ if @options[:stream]
189
+ stream_epilogue
190
+ else
159
191
 
160
- debug("writer") { "serialize #{@repo.count} statements, #{@options.inspect}"}
161
- result = API.fromRdf(@repo, @options)
192
+ log_debug("writer") { "serialize #{@repo.count} statements, #{@options.inspect}"}
193
+ result = API.fromRdf(@repo, @options)
162
194
 
163
- # If we were provided a context, or prefixes, use them to compact the output
164
- context = RDF::Util::File.open_file(@options[:context]) if @options[:context].is_a?(String)
165
- context ||= @options[:context]
166
- context ||= if @options[:prefixes] || @options[:language] || @options[:standard_prefixes]
167
- ctx = Context.new(@options)
168
- ctx.language = @options[:language] if @options[:language]
169
- @options[:prefixes].each do |prefix, iri|
170
- ctx.set_mapping(prefix, iri) if prefix && iri
171
- end if @options[:prefixes]
172
- ctx
173
- end
195
+ # If we were provided a context, or prefixes, use them to compact the output
196
+ context = RDF::Util::File.open_file(@options[:context]) if @options[:context].is_a?(String)
197
+ context ||= @options[:context]
198
+ context ||= if @options[:prefixes] || @options[:language] || @options[:standard_prefixes]
199
+ ctx = Context.new(@options)
200
+ ctx.language = @options[:language] if @options[:language]
201
+ @options[:prefixes].each do |prefix, iri|
202
+ ctx.set_mapping(prefix, iri) if prefix && iri
203
+ end if @options[:prefixes]
204
+ ctx
205
+ end
174
206
 
175
- # Rename BNodes to uniquify them, if necessary
176
- if options[:unique_bnodes]
177
- result = API.flatten(result, context, @options)
178
- end
207
+ # Rename BNodes to uniquify them, if necessary
208
+ if options[:unique_bnodes]
209
+ result = API.flatten(result, context, @options)
210
+ end
211
+
212
+ frame = RDF::Util::File.open_file(@options[:frame]) if @options[:frame].is_a?(String)
213
+ if frame ||= @options[:frame]
214
+ # Perform framing, if given a frame
215
+ log_debug("writer") { "frame result"}
216
+ result = API.frame(result, frame, @options)
217
+ elsif context
218
+ # Perform compaction, if we have a context
219
+ log_debug("writer") { "compact result"}
220
+ result = API.compact(result, context, @options)
221
+ end
179
222
 
180
- frame = RDF::Util::File.open_file(@options[:frame]) if @options[:frame].is_a?(String)
181
- if frame ||= @options[:frame]
182
- # Perform framing, if given a frame
183
- debug("writer") { "frame result"}
184
- result = API.frame(result, frame, @options)
185
- elsif context
186
- # Perform compaction, if we have a context
187
- debug("writer") { "compact result"}
188
- result = API.compact(result, context, @options)
223
+ @output.write(result.to_json(JSON_STATE))
189
224
  end
190
225
 
191
- @output.write(result.to_json(JSON_STATE))
226
+ super
192
227
  end
193
228
  end
194
229
  end
data/spec/api_spec.rb CHANGED
@@ -3,7 +3,7 @@ $:.unshift "."
3
3
  require 'spec_helper'
4
4
 
5
5
  describe JSON::LD::API do
6
- before(:each) { @debug = []}
6
+ let(:logger) {RDF::Spec.logger}
7
7
 
8
8
  describe "#initialize" do
9
9
  context "with string input" do
@@ -78,24 +78,24 @@ describe JSON::LD::API do
78
78
 
79
79
  context test, skip: ("Not supported in JRuby" if RUBY_ENGINE == "jruby" && %w(oj yajl).include?(adapter.to_s)) do
80
80
  it "expands" do
81
- options = {debug: @debug, adapter: adapter}
81
+ options = {logger: logger, adapter: adapter}
82
82
  options[:expandContext] = File.open(context) if context
83
83
  jld = described_class.expand(File.open(filename), options)
84
- expect(jld).to produce(JSON.load(File.open(expanded)), @debug)
84
+ expect(jld).to produce(JSON.load(File.open(expanded)), logger)
85
85
  end if File.exist?(expanded)
86
86
 
87
87
  it "compacts" do
88
- jld = described_class.compact(File.open(filename), File.open(context), adapter: adapter, debug: @debug)
89
- expect(jld).to produce(JSON.load(File.open(compacted)), @debug)
88
+ jld = described_class.compact(File.open(filename), File.open(context), adapter: adapter, logger: logger)
89
+ expect(jld).to produce(JSON.load(File.open(compacted)), logger)
90
90
  end if File.exist?(compacted) && File.exist?(context)
91
91
 
92
92
  it "frame" do
93
- jld = described_class.frame(File.open(filename), File.open(frame), adapter: adapter, debug: @debug)
94
- expect(jld).to produce(JSON.load(File.open(framed)), @debug)
93
+ jld = described_class.frame(File.open(filename), File.open(frame), adapter: adapter, logger: logger)
94
+ expect(jld).to produce(JSON.load(File.open(framed)), logger)
95
95
  end if File.exist?(framed) && File.exist?(frame)
96
96
 
97
97
  it "toRdf" do
98
- expect(RDF::Repository.load(filename, format: :jsonld, adapter: adapter, debug: @debug)).to be_equivalent_graph(RDF::Repository.load(ttl), trace: @debug)
98
+ expect(RDF::Repository.load(filename, format: :jsonld, adapter: adapter, logger: logger)).to be_equivalent_graph(RDF::Repository.load(ttl), logger: logger)
99
99
  end if File.exist?(ttl)
100
100
  end
101
101
  end