json-ld 1.99.2 → 2.0.0.beta1

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.
@@ -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