Skip to content

Commit 690135f

Browse files
committed
Finish 3.2.2
2 parents 8ee7317 + 6b73ffe commit 690135f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+168
-109
lines changed

README.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ This gem also implements an optimized streaming writer used for generating JSON-
3232
* Each statement written as a separate node in expanded/flattened form.
3333
* `RDF List`s are written as separate nodes using `rdf:first` and `rdf:rest` properties.
3434

35-
The order of triples retrieved from the `RDF::Enumerable` dataset determines the way that JSON-LD node objects are written; for best results, statements should be ordered by _graph name_, _subect_, _predicate_ and _object_.
35+
The order of triples retrieved from the `RDF::Enumerable` dataset determines the way that JSON-LD node objects are written; for best results, statements should be ordered by _graph name_, _subject_, _predicate_ and _object_.
3636

3737
### MultiJson parser
38-
The [MultiJson](https://rubygems.org/gems/multi_json) gem is used for parsing JSON; this defaults to the native JSON parser, but will use a more performant parser if one is available. A specific parser can be specified by adding the `:adapter` option to any API call. See [MultiJson](https://rubygems.org/gems/multi_json) for more information.
38+
The [MultiJson](https://rubygems.org/gems/multi_json) gem is used for parsing and serializing JSON; this defaults to the native JSON parser/serializer, but will use a more performant parser if one is available. A specific parser can be specified by adding the `:adapter` option to any API call. Additionally, a custom serialilzer may be specified by passing the `:serializer` option to {JSON::LD::Writer} or methods of {JSON::LD::API}. See [MultiJson](https://rubygems.org/gems/multi_json) for more information.
3939

4040
### JSON-LD-star (RDFStar)
4141

@@ -624,8 +624,7 @@ To get a local working copy of the development repository, do:
624624
which you will be asked to agree to on the first commit to a repo within the organization.
625625
Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
626626

627-
License
628-
-------
627+
## License
629628

630629
This is free and unencumbered public domain software. For more information,
631630
see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
@@ -641,7 +640,7 @@ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
641640
[Backports]: https://rubygems.org/gems/backports
642641
[JSON-LD]: https://www.w3.org/TR/json-ld11/ "JSON-LD 1.1"
643642
[JSON-LD API]: https://www.w3.org/TR/json-ld11-api/ "JSON-LD 1.1 Processing Algorithms and API"
644-
[JSON-LD Framing]: https://www.w3.org/TR/json-ld11-framing/ "JSON-LD Framing 1.1"
643+
[JSON-LD Framing]: https://www.w3.org/TR/json-ld11-framing/ "JSON-LD 1.1 Framing"
645644
[Promises]: https://dom.spec.whatwg.org/#promises
646645
[jsonlint]: https://rubygems.org/gems/jsonlint
647646
[Sinatra]: https://www.sinatrarb.com/

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.1
1+
3.2.2

lib/json/ld.rb

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ module LD
4646
# Default context when compacting without one being specified
4747
DEFAULT_CONTEXT = "http://schema.org"
4848

49+
# Acceptable MultiJson adapters
50+
MUTLI_JSON_ADAPTERS = %i(oj json_gem json_pure ok_json yajl nsjsonseerialization)
51+
4952
KEYWORDS = Set.new(%w(
5053
@annotation
5154
@base

lib/json/ld/api.rb

+53-16
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ def initialize(input, context, **options, &block)
126126

127127
case remote_doc.document
128128
when String
129-
MultiJson.load(remote_doc.document, **options)
129+
mj_opts = options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
130+
MultiJson.load(remote_doc.document, **mj_opts)
130131
else
131132
# Already parsed
132133
remote_doc.document
@@ -155,6 +156,9 @@ def initialize(input, context, **options, &block)
155156
#
156157
# @param [String, #read, Hash, Array] input
157158
# The JSON-LD object to copy and perform the expansion upon.
159+
# @param [Proc] serializer (nil)
160+
# A Serializer method used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
161+
# See {JSON::LD::API.serializer}.
158162
# @param [Hash{Symbol => Object}] options
159163
# @option options (see #initialize)
160164
# @raise [JsonLdError]
@@ -167,7 +171,7 @@ def initialize(input, context, **options, &block)
167171
# @return [Object, Array<Hash>]
168172
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
169173
# @see https://www.w3.org/TR/json-ld11-api/#expansion-algorithm
170-
def self.expand(input, framing: false, **options, &block)
174+
def self.expand(input, framing: false, serializer: nil, **options, &block)
171175
result = doc_base = nil
172176
API.new(input, options[:expandContext], **options) do
173177
result = self.expand(self.value, nil, self.context,
@@ -180,6 +184,7 @@ def self.expand(input, framing: false, **options, &block)
180184

181185
# Finally, if element is a JSON object, it is wrapped into an array.
182186
result = [result].compact unless result.is_a?(Array)
187+
result = serializer.call(result, **options) if serializer
183188

184189
if block_given?
185190
case block.arity
@@ -204,6 +209,9 @@ def self.expand(input, framing: false, **options, &block)
204209
# The JSON-LD object to copy and perform the compaction upon.
205210
# @param [String, #read, Hash, Array, JSON::LD::Context] context
206211
# The base context to use when compacting the input.
212+
# @param [Proc] serializer (nil)
213+
# A Serializer instance used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
214+
# See {JSON::LD::API.serializer}.
207215
# @param [Boolean] expanded (false) Input is already expanded
208216
# @param [Hash{Symbol => Object}] options
209217
# @option options (see #initialize)
@@ -215,7 +223,7 @@ def self.expand(input, framing: false, **options, &block)
215223
# If a block is given, the result of evaluating the block is returned, otherwise, the compacted JSON-LD document
216224
# @raise [JsonLdError]
217225
# @see https://www.w3.org/TR/json-ld11-api/#compaction-algorithm
218-
def self.compact(input, context, expanded: false, **options)
226+
def self.compact(input, context, expanded: false, serializer: nil, **options)
219227
result = nil
220228
options = {compactToRelative: true}.merge(options)
221229

@@ -238,6 +246,7 @@ def self.compact(input, context, expanded: false, **options)
238246
end
239247
result = ctx.merge(result) unless ctx.fetch('@context', {}).empty?
240248
end
249+
result = serializer.call(result, **options) if serializer
241250
block_given? ? yield(result) : result
242251
end
243252

@@ -251,6 +260,9 @@ def self.compact(input, context, expanded: false, **options)
251260
# @param [String, #read, Hash, Array, JSON::LD::EvaluationContext] context
252261
# An optional external context to use additionally to the context embedded in input when expanding the input.
253262
# @param [Boolean] expanded (false) Input is already expanded
263+
# @param [Proc] serializer (nil)
264+
# A Serializer instance used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
265+
# See {JSON::LD::API.serializer}.
254266
# @param [Hash{Symbol => Object}] options
255267
# @option options (see #initialize)
256268
# @option options [Boolean] :createAnnotations
@@ -262,7 +274,7 @@ def self.compact(input, context, expanded: false, **options)
262274
# @return [Object, Hash]
263275
# If a block is given, the result of evaluating the block is returned, otherwise, the flattened JSON-LD document
264276
# @see https://www.w3.org/TR/json-ld11-api/#framing-algorithm
265-
def self.flatten(input, context, expanded: false, **options)
277+
def self.flatten(input, context, expanded: false, serializer: nil, **options)
266278
flattened = []
267279
options = {
268280
compactToRelative: true,
@@ -318,6 +330,7 @@ def self.flatten(input, context, expanded: false, **options)
318330
end
319331
end
320332

333+
flattened = serializer.call(flattened, **options) if serializer
321334
block_given? ? yield(flattened) : flattened
322335
end
323336

@@ -350,7 +363,7 @@ def self.flatten(input, context, expanded: false, **options)
350363
# If a block is given, the result of evaluating the block is returned, otherwise, the framed JSON-LD document
351364
# @raise [InvalidFrame]
352365
# @see https://www.w3.org/TR/json-ld11-api/#framing-algorithm
353-
def self.frame(input, frame, expanded: false, **options)
366+
def self.frame(input, frame, expanded: false, serializer: nil, **options)
354367
result = nil
355368
options = {
356369
base: (RDF::URI(input) if input.is_a?(String)),
@@ -379,7 +392,8 @@ def self.frame(input, frame, expanded: false, **options)
379392
requestProfile: 'http://www.w3.org/ns/json-ld#frame',
380393
**options)
381394
if remote_doc.document.is_a?(String)
382-
MultiJson.load(remote_doc.document)
395+
mj_opts = options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
396+
MultiJson.load(remote_doc.document, **mj_opts)
383397
else
384398
remote_doc.document
385399
end
@@ -467,6 +481,7 @@ def self.frame(input, frame, expanded: false, **options)
467481
result
468482
end
469483

484+
result = serializer.call(result, **options) if serializer
470485
block_given? ? yield(result) : result
471486
end
472487

@@ -528,18 +543,21 @@ def self.toRdf(input, expanded: false, **options, &block)
528543
# The resulting `Array` is either returned or yielded, if a block is given.
529544
#
530545
# @param [RDF::Enumerable] input
546+
# @param [Boolean] useRdfType (false)
547+
# If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
548+
# @param [Boolean] useNativeTypes (false) use native representations
549+
# @param [Proc] serializer (nil)
550+
# A Serializer instance used for generating the JSON serialization of the result. If absent, the internal Ruby objects are returned, which can be transformed to JSON externally via `#to_json`.
551+
# See {JSON::LD::API.serializer}.
531552
# @param [Hash{Symbol => Object}] options
532553
# @option options (see #initialize)
533-
# @option options [Boolean] :useRdfType (false)
534-
# If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
535-
# @option options [Boolean] :useNativeTypes (false) use native representations
536554
# @yield jsonld
537555
# @yieldparam [Hash] jsonld
538556
# The JSON-LD document in expanded form
539557
# @yieldreturn [Object] returned object
540558
# @return [Object, Hash]
541559
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
542-
def self.fromRdf(input, useRdfType: false, useNativeTypes: false, **options, &block)
560+
def self.fromRdf(input, useRdfType: false, useNativeTypes: false, serializer: nil, **options, &block)
543561
result = nil
544562

545563
API.new(nil, nil, **options) do
@@ -548,6 +566,7 @@ def self.fromRdf(input, useRdfType: false, useNativeTypes: false, **options, &bl
548566
useNativeTypes: useNativeTypes)
549567
end
550568

569+
result = serializer.call(result, **options) if serializer
551570
block_given? ? yield(result) : result
552571
end
553572

@@ -648,7 +667,8 @@ def self.loadRemoteDocument(url,
648667
end
649668
else
650669
validate_input(remote_doc.document, url: remote_doc.documentUrl) if validate
651-
MultiJson.load(remote_doc.document, **options)
670+
mj_opts = options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
671+
MultiJson.load(remote_doc.document, **mj_opts)
652672
end
653673
end
654674

@@ -682,8 +702,8 @@ def self.documentLoader(url, extractAllScripts: false, profile: nil, requestProf
682702
base_uri ||= url.base_uri if url.respond_to?(:base_uri)
683703
content_type = options[:content_type]
684704
content_type ||= url.content_type if url.respond_to?(:content_type)
685-
context_url = if url.respond_to?(:links) && url.links
686-
(content_type == 'appliaction/json' || content_type.match?(%r(application/(^ld)+json)))
705+
context_url = if url.respond_to?(:links) && url.links &&
706+
(content_type == 'application/json' || content_type.match?(%r(application/(^ld)+json)))
687707
link = url.links.find_link(LINK_REL_CONTEXT)
688708
link.href if link
689709
end
@@ -759,7 +779,8 @@ def self.load_html(input, url:,
759779
raise JSON::LD::JsonLdError::LoadingDocumentFailed, "Script tag has type=#{element.attributes['type']}" unless element.attributes['type'].to_s.start_with?('application/ld+json')
760780
content = element.inner_html
761781
validate_input(content, url: url) if options[:validate]
762-
MultiJson.load(content, **options)
782+
mj_opts = options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
783+
MultiJson.load(content, **mj_opts)
763784
elsif extractAllScripts
764785
res = []
765786
elements = if profile
@@ -773,7 +794,8 @@ def self.load_html(input, url:,
773794
elements.each do |element|
774795
content = element.inner_html
775796
validate_input(content, url: url) if options[:validate]
776-
r = MultiJson.load(content, **options)
797+
mj_opts = options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
798+
r = MultiJson.load(content, **mj_opts)
777799
if r.is_a?(Hash)
778800
res << r
779801
elsif r.is_a?(Array)
@@ -788,12 +810,27 @@ def self.load_html(input, url:,
788810
raise JSON::LD::JsonLdError::LoadingDocumentFailed, "No script tag found" unless element
789811
content = element.inner_html
790812
validate_input(content, url: url) if options[:validate]
791-
MultiJson.load(content, **options)
813+
mj_opts = options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
814+
MultiJson.load(content, **mj_opts)
792815
end
793816
rescue MultiJson::ParseError => e
794817
raise JSON::LD::JsonLdError::InvalidScriptElement, e.message
795818
end
796819

820+
##
821+
# The default serializer for serialzing Ruby Objects to JSON.
822+
#
823+
# Defaults to `MultiJson.dump`
824+
#
825+
# @param [Object] object
826+
# @param [Array<Object>] args
827+
# other arguments that may be passed for some specific implementation.
828+
# @param [Hash<Symbol, Object>] options
829+
# options passed from the invoking context.
830+
def self.serializer(object, *args, **options)
831+
MultiJson.dump(object, JSON_STATE)
832+
end
833+
797834
##
798835
# Validate JSON using JsonLint, if loaded
799836
private

lib/json/ld/reader.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def self.options
5757
end
5858

5959
##
60-
# Initializes the RDF/JSON reader instance.
60+
# Initializes the JSON-LD reader instance.
6161
#
6262
# @param [IO, File, String] input
6363
# @param [Hash{Symbol => Object}] options

lib/json/ld/streaming_reader.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def stream_statement(&block)
2626
unique_bnodes, rename_bnodes = @options[:unique_bnodes], @options.fetch(:rename_bnodes, true)
2727
# FIXME: document loader doesn't stream
2828
@base = RDF::URI(@options[:base] || base_uri)
29-
value = MultiJson.load(@doc, **@options)
29+
mj_opts = @options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
30+
value = MultiJson.load(@doc, mj_opts)
3031
context_ref = @options[:expandContext]
3132
#context_ref = @options.fetch(:expandContext, remote_doc.contextUrl)
3233
context = Context.parse(context_ref, **@options)

lib/json/ld/to_rdf.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module ToRDF
1111
##
1212
# @param [Hash{String => Object}] item
1313
# @param [RDF::Resource] graph_name
14-
# @param [Boolean] emitted triples are quoted triples.
14+
# @param [Boolean] quoted emitted triples are quoted triples.
1515
# @yield statement
1616
# @yieldparam [RDF::Statement] statement
1717
# @return RDF::Resource the subject of this item

lib/json/ld/writer.rb

+8-9
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,6 @@ class << self
186186
# @return [Boolean]
187187
# @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
188188
def accept?(accept_params)
189-
# Profiles that aren't specific IANA relations represent the URL
190-
# of a context or frame that may be subject to black- or white-listing
191-
profile = accept_params[:profile].to_s.split(/\s+/)
192-
193189
if block_given?
194190
yield(accept_params)
195191
else
@@ -229,6 +225,8 @@ def default_context=(url); @default_context = url; end
229225
# frame to use when serializing.
230226
# @option options [Boolean] :unique_bnodes (false)
231227
# Use unique bnode identifiers, defaults to using the identifier which the node was originall initialized with (if any).
228+
# @option options [Proc] serializer (JSON::LD::API.serializer)
229+
# A Serializer method used for generating the JSON serialization of the result.
232230
# @option options [Boolean] :stream (false)
233231
# Do not attempt to optimize graph presentation, suitable for streaming large graphs.
234232
# @yield [writer] `self`
@@ -239,6 +237,7 @@ def default_context=(url); @default_context = url; end
239237
def initialize(output = $stdout, **options, &block)
240238
options[:base_uri] ||= options[:base] if options.key?(:base)
241239
options[:base] ||= options[:base_uri] if options.key?(:base_uri)
240+
@serializer = options.fetch(:serializer, JSON::LD::API.method(:serializer))
242241
super do
243242
@repo = RDF::Repository.new
244243

@@ -300,7 +299,7 @@ def write_epilogue
300299
else
301300

302301
log_debug("writer") { "serialize #{@repo.count} statements, #{@options.inspect}"}
303-
result = API.fromRdf(@repo, **@options)
302+
result = API.fromRdf(@repo, **@options.merge(serializer: nil))
304303

305304
# Some options may be indicated from accept parameters
306305
profile = @options.fetch(:accept_params, {}).fetch(:profile, "").split(' ')
@@ -322,20 +321,20 @@ def write_epilogue
322321

323322
# Rename BNodes to uniquify them, if necessary
324323
if options[:unique_bnodes]
325-
result = API.flatten(result, context, **@options)
324+
result = API.flatten(result, context, **@options.merge(serializer: nil))
326325
end
327326

328327
if frame = @options[:frame]
329328
# Perform framing, if given a frame
330329
log_debug("writer") { "frame result"}
331-
result = API.frame(result, frame, **@options)
330+
result = API.frame(result, frame, **@options.merge(serializer: nil))
332331
elsif context
333332
# Perform compaction, if we have a context
334333
log_debug("writer") { "compact result"}
335-
result = API.compact(result, context, **@options)
334+
result = API.compact(result, context, **@options.merge(serializer: nil))
336335
end
337336

338-
@output.write(result.to_json(JSON_STATE))
337+
@output.write(@serializer.call(result, **@options))
339338
end
340339

341340
super

0 commit comments

Comments
 (0)