json-ld 3.2.0 → 3.2.3

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -7
  3. data/VERSION +1 -1
  4. data/lib/json/ld/api.rb +55 -16
  5. data/lib/json/ld/context.rb +7 -7
  6. data/lib/json/ld/expand.rb +2 -2
  7. data/lib/json/ld/format.rb +83 -116
  8. data/lib/json/ld/reader.rb +2 -2
  9. data/lib/json/ld/streaming_reader.rb +2 -1
  10. data/lib/json/ld/to_rdf.rb +9 -8
  11. data/lib/json/ld/writer.rb +9 -10
  12. data/lib/json/ld.rb +3 -0
  13. data/spec/api_spec.rb +67 -19
  14. data/spec/format_spec.rb +7 -3
  15. data/spec/frame_spec.rb +25 -1
  16. data/spec/reader_spec.rb +14 -14
  17. data/spec/suite_helper.rb +1 -0
  18. data/spec/test-files/{test-1-compacted.json → test-1-compacted.jsonld} +0 -0
  19. data/spec/test-files/{test-1-context.json → test-1-context.jsonld} +0 -0
  20. data/spec/test-files/{test-1-expanded.json → test-1-expanded.jsonld} +0 -0
  21. data/spec/test-files/{test-1-input.json → test-1-input.jsonld} +0 -0
  22. data/spec/test-files/{test-2-compacted.json → test-2-compacted.jsonld} +0 -0
  23. data/spec/test-files/{test-2-context.json → test-2-context.jsonld} +0 -0
  24. data/spec/test-files/{test-2-expanded.json → test-2-expanded.jsonld} +0 -0
  25. data/spec/test-files/{test-2-input.json → test-2-input.jsonld} +0 -0
  26. data/spec/test-files/{test-3-compacted.json → test-3-compacted.jsonld} +0 -0
  27. data/spec/test-files/{test-3-context.json → test-3-context.jsonld} +0 -0
  28. data/spec/test-files/{test-3-expanded.json → test-3-expanded.jsonld} +0 -0
  29. data/spec/test-files/{test-3-input.json → test-3-input.jsonld} +0 -0
  30. data/spec/test-files/{test-4-compacted.json → test-4-compacted.jsonld} +0 -0
  31. data/spec/test-files/{test-4-context.json → test-4-context.jsonld} +0 -0
  32. data/spec/test-files/{test-4-expanded.json → test-4-expanded.jsonld} +0 -0
  33. data/spec/test-files/{test-4-input.json → test-4-input.jsonld} +0 -0
  34. data/spec/test-files/{test-5-compacted.json → test-5-compacted.jsonld} +0 -0
  35. data/spec/test-files/{test-5-context.json → test-5-context.jsonld} +0 -0
  36. data/spec/test-files/{test-5-expanded.json → test-5-expanded.jsonld} +0 -0
  37. data/spec/test-files/{test-5-input.json → test-5-input.jsonld} +0 -0
  38. data/spec/test-files/{test-6-compacted.json → test-6-compacted.jsonld} +0 -0
  39. data/spec/test-files/{test-6-context.json → test-6-context.jsonld} +0 -0
  40. data/spec/test-files/{test-6-expanded.json → test-6-expanded.jsonld} +0 -0
  41. data/spec/test-files/{test-6-input.json → test-6-input.jsonld} +0 -0
  42. data/spec/test-files/{test-7-compacted.json → test-7-compacted.jsonld} +0 -0
  43. data/spec/test-files/{test-7-context.json → test-7-context.jsonld} +0 -0
  44. data/spec/test-files/{test-7-expanded.json → test-7-expanded.jsonld} +0 -0
  45. data/spec/test-files/{test-7-input.json → test-7-input.jsonld} +0 -0
  46. data/spec/test-files/{test-8-compacted.json → test-8-compacted.jsonld} +0 -0
  47. data/spec/test-files/{test-8-context.json → test-8-context.jsonld} +0 -0
  48. data/spec/test-files/{test-8-expanded.json → test-8-expanded.jsonld} +0 -0
  49. data/spec/test-files/{test-8-frame.json → test-8-frame.jsonld} +0 -0
  50. data/spec/test-files/{test-8-framed.json → test-8-framed.jsonld} +0 -0
  51. data/spec/test-files/{test-8-input.json → test-8-input.jsonld} +0 -0
  52. data/spec/test-files/{test-9-compacted.json → test-9-compacted.jsonld} +0 -0
  53. data/spec/test-files/{test-9-context.json → test-9-context.jsonld} +0 -0
  54. data/spec/test-files/{test-9-expanded.json → test-9-expanded.jsonld} +0 -0
  55. data/spec/test-files/{test-9-input.json → test-9-input.jsonld} +0 -0
  56. metadata +94 -89
  57. data/spec/test-files/test-1-normalized.json +0 -8
  58. data/spec/test-files/test-2-normalized.json +0 -32
  59. data/spec/test-files/test-3-normalized.json +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e157972b224a8cbe834defdf32656a0b9f090357760fab179076e728427d1639
4
- data.tar.gz: a63cd584853e5a830c85a427d30711bc84999e958949c81bc7d236df20f14ee6
3
+ metadata.gz: ff11a49b52d7ca0faf6deaac7324f08530747b53ec857ebe007c027a1bf412b4
4
+ data.tar.gz: b0a808fc12be08ecaa47f399c0f20ff26120cc36820bf45d6c434ad1f31f53e3
5
5
  SHA512:
6
- metadata.gz: 77a85aa85eea6844e1adb9107fe0b2d5a4ad82b877456adcdbbc56031f884da21cc992169232c326f255491d8aa6f607aacbeaeb386578fe8a1ac374cc795ff5
7
- data.tar.gz: 2f8353dca7bb6ea9bed3ed7fbea3e0bf883bdec9a3687d3b084f936ae0171a21df1d55bda8cf44fc0ae6d6682d5235234792901fb2fc7ffed327d2956fec289f
6
+ metadata.gz: c3e17fbb3280f393ece72136d09182d1032982ca116d49577f03dc72ca35ebc8193495054a1944e525acdad8ae67ce6cb341f6859c5e97a7d2497ca047d7ee40
7
+ data.tar.gz: eef60e136ca7d86ac66cd0b74d5b01862b4f5c7039352c7d6e678b73ab783bf7bbf2f397f7fd928421bc52e332389cab1432e69175d6fa0d260dfcf7a315a66a
data/README.md CHANGED
@@ -32,10 +32,10 @@ This gem also implements an optimized streaming writer used for generating JSON-
32
32
  * Each statement written as a separate node in expanded/flattened form.
33
33
  * `RDF List`s are written as separate nodes using `rdf:first` and `rdf:rest` properties.
34
34
 
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_.
36
36
 
37
37
  ### 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.
39
39
 
40
40
  ### JSON-LD-star (RDFStar)
41
41
 
@@ -72,7 +72,7 @@ In the first case, the embedded node is not asserted, and only appears as the su
72
72
 
73
73
  #### Serializing a Graph containing embedded statements
74
74
 
75
- require 'json-ld'
75
+ require 'json/ld'
76
76
  statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23))
77
77
  graph = RDF::Graph.new << [statement, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
78
78
  graph.dump(:jsonld, validate: false, standard_prefixes: true)
@@ -565,7 +565,7 @@ The {JSON::LD::ContentNegotiation#call} method looks for a result which includes
565
565
  See [Rack::LinkedData][] to do the same thing with an RDF Graph or Dataset as the source, rather than Ruby objects.
566
566
 
567
567
  ## Documentation
568
- Full documentation available on [RubyDoc](https://rubydoc.info/gems/json-ld/file/README.md)
568
+ Full documentation available on [RubyDoc](https://ruby-rdf.github.io/json-ld/file/README.md)
569
569
 
570
570
  ## Differences from [JSON-LD API][]
571
571
  The specified JSON-LD API is based on a WebIDL definition implementing [Promises][] intended for use within a browser.
@@ -624,8 +624,7 @@ To get a local working copy of the development repository, do:
624
624
  which you will be asked to agree to on the first commit to a repo within the organization.
625
625
  Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
626
626
 
627
- License
628
- -------
627
+ ## License
629
628
 
630
629
  This is free and unencumbered public domain software. For more information,
631
630
  see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
@@ -641,7 +640,7 @@ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
641
640
  [Backports]: https://rubygems.org/gems/backports
642
641
  [JSON-LD]: https://www.w3.org/TR/json-ld11/ "JSON-LD 1.1"
643
642
  [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"
645
644
  [Promises]: https://dom.spec.whatwg.org/#promises
646
645
  [jsonlint]: https://rubygems.org/gems/jsonlint
647
646
  [Sinatra]: https://www.sinatrarb.com/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.2.3
data/lib/json/ld/api.rb CHANGED
@@ -126,7 +126,8 @@ module JSON::LD
126
126
 
127
127
  case remote_doc.document
128
128
  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)
130
131
  else
131
132
  # Already parsed
132
133
  remote_doc.document
@@ -155,6 +156,9 @@ module JSON::LD
155
156
  #
156
157
  # @param [String, #read, Hash, Array] input
157
158
  # 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}.
158
162
  # @param [Hash{Symbol => Object}] options
159
163
  # @option options (see #initialize)
160
164
  # @raise [JsonLdError]
@@ -167,7 +171,7 @@ module JSON::LD
167
171
  # @return [Object, Array<Hash>]
168
172
  # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
169
173
  # @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)
171
175
  result = doc_base = nil
172
176
  API.new(input, options[:expandContext], **options) do
173
177
  result = self.expand(self.value, nil, self.context,
@@ -180,6 +184,7 @@ module JSON::LD
180
184
 
181
185
  # Finally, if element is a JSON object, it is wrapped into an array.
182
186
  result = [result].compact unless result.is_a?(Array)
187
+ result = serializer.call(result, **options) if serializer
183
188
 
184
189
  if block_given?
185
190
  case block.arity
@@ -204,6 +209,9 @@ module JSON::LD
204
209
  # The JSON-LD object to copy and perform the compaction upon.
205
210
  # @param [String, #read, Hash, Array, JSON::LD::Context] context
206
211
  # 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}.
207
215
  # @param [Boolean] expanded (false) Input is already expanded
208
216
  # @param [Hash{Symbol => Object}] options
209
217
  # @option options (see #initialize)
@@ -215,7 +223,7 @@ module JSON::LD
215
223
  # If a block is given, the result of evaluating the block is returned, otherwise, the compacted JSON-LD document
216
224
  # @raise [JsonLdError]
217
225
  # @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)
219
227
  result = nil
220
228
  options = {compactToRelative: true}.merge(options)
221
229
 
@@ -238,6 +246,7 @@ module JSON::LD
238
246
  end
239
247
  result = ctx.merge(result) unless ctx.fetch('@context', {}).empty?
240
248
  end
249
+ result = serializer.call(result, **options) if serializer
241
250
  block_given? ? yield(result) : result
242
251
  end
243
252
 
@@ -251,6 +260,9 @@ module JSON::LD
251
260
  # @param [String, #read, Hash, Array, JSON::LD::EvaluationContext] context
252
261
  # An optional external context to use additionally to the context embedded in input when expanding the input.
253
262
  # @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}.
254
266
  # @param [Hash{Symbol => Object}] options
255
267
  # @option options (see #initialize)
256
268
  # @option options [Boolean] :createAnnotations
@@ -262,7 +274,7 @@ module JSON::LD
262
274
  # @return [Object, Hash]
263
275
  # If a block is given, the result of evaluating the block is returned, otherwise, the flattened JSON-LD document
264
276
  # @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)
266
278
  flattened = []
267
279
  options = {
268
280
  compactToRelative: true,
@@ -318,6 +330,7 @@ module JSON::LD
318
330
  end
319
331
  end
320
332
 
333
+ flattened = serializer.call(flattened, **options) if serializer
321
334
  block_given? ? yield(flattened) : flattened
322
335
  end
323
336
 
@@ -350,7 +363,7 @@ module JSON::LD
350
363
  # If a block is given, the result of evaluating the block is returned, otherwise, the framed JSON-LD document
351
364
  # @raise [InvalidFrame]
352
365
  # @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)
354
367
  result = nil
355
368
  options = {
356
369
  base: (RDF::URI(input) if input.is_a?(String)),
@@ -379,7 +392,8 @@ module JSON::LD
379
392
  requestProfile: 'http://www.w3.org/ns/json-ld#frame',
380
393
  **options)
381
394
  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)
383
397
  else
384
398
  remote_doc.document
385
399
  end
@@ -467,6 +481,7 @@ module JSON::LD
467
481
  result
468
482
  end
469
483
 
484
+ result = serializer.call(result, **options) if serializer
470
485
  block_given? ? yield(result) : result
471
486
  end
472
487
 
@@ -528,18 +543,21 @@ module JSON::LD
528
543
  # The resulting `Array` is either returned or yielded, if a block is given.
529
544
  #
530
545
  # @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}.
531
552
  # @param [Hash{Symbol => Object}] options
532
553
  # @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
536
554
  # @yield jsonld
537
555
  # @yieldparam [Hash] jsonld
538
556
  # The JSON-LD document in expanded form
539
557
  # @yieldreturn [Object] returned object
540
558
  # @return [Object, Hash]
541
559
  # 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)
543
561
  result = nil
544
562
 
545
563
  API.new(nil, nil, **options) do
@@ -548,6 +566,7 @@ module JSON::LD
548
566
  useNativeTypes: useNativeTypes)
549
567
  end
550
568
 
569
+ result = serializer.call(result, **options) if serializer
551
570
  block_given? ? yield(result) : result
552
571
  end
553
572
 
@@ -648,7 +667,8 @@ module JSON::LD
648
667
  end
649
668
  else
650
669
  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)
652
672
  end
653
673
  end
654
674
 
@@ -682,8 +702,8 @@ module JSON::LD
682
702
  base_uri ||= url.base_uri if url.respond_to?(:base_uri)
683
703
  content_type = options[:content_type]
684
704
  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)))
687
707
  link = url.links.find_link(LINK_REL_CONTEXT)
688
708
  link.href if link
689
709
  end
@@ -759,7 +779,8 @@ module JSON::LD
759
779
  raise JSON::LD::JsonLdError::LoadingDocumentFailed, "Script tag has type=#{element.attributes['type']}" unless element.attributes['type'].to_s.start_with?('application/ld+json')
760
780
  content = element.inner_html
761
781
  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)
763
784
  elsif extractAllScripts
764
785
  res = []
765
786
  elements = if profile
@@ -773,7 +794,8 @@ module JSON::LD
773
794
  elements.each do |element|
774
795
  content = element.inner_html
775
796
  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)
777
799
  if r.is_a?(Hash)
778
800
  res << r
779
801
  elsif r.is_a?(Array)
@@ -788,12 +810,29 @@ module JSON::LD
788
810
  raise JSON::LD::JsonLdError::LoadingDocumentFailed, "No script tag found" unless element
789
811
  content = element.inner_html
790
812
  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)
792
815
  end
793
816
  rescue MultiJson::ParseError => e
794
817
  raise JSON::LD::JsonLdError::InvalidScriptElement, e.message
795
818
  end
796
819
 
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
+ # @option options [Object] :serializer_opts (JSON_STATE)
831
+ def self.serializer(object, *args, **options)
832
+ serializer_opts = options.fetch(:serializer_opts, JSON_STATE)
833
+ MultiJson.dump(object, serializer_opts)
834
+ end
835
+
797
836
  ##
798
837
  # Validate JSON using JsonLint, if loaded
799
838
  private
@@ -5,13 +5,6 @@ require 'bigdecimal'
5
5
  require 'set'
6
6
  require 'rdf/util/cache'
7
7
 
8
- begin
9
- # Attempt to load this to avoid unnecessary context fetches
10
- require 'json-ld-preloaded'
11
- rescue LoadError
12
- # Silently allow this to fail
13
- end
14
-
15
8
  module JSON::LD
16
9
  class Context
17
10
  include Utils
@@ -50,6 +43,13 @@ module JSON::LD
50
43
  end
51
44
  end
52
45
 
46
+ begin
47
+ # Attempt to load this to avoid unnecessary context fetches
48
+ require 'json/ld/preloaded'
49
+ rescue LoadError
50
+ # Silently allow this to fail
51
+ end
52
+
53
53
  # The base.
54
54
  #
55
55
  # @return [RDF::URI] Current base IRI, used for expanding relative IRIs.
@@ -88,7 +88,7 @@ module JSON::LD
88
88
 
89
89
  # If element contains the key @context, set active context to the result of the Context Processing algorithm, passing active context and the value of the @context key as local context.
90
90
  if input.key?('@context')
91
- context = context.parse(input.delete('@context'), base: @options[:base])
91
+ context = context.parse(input['@context'], base: @options[:base])
92
92
  log_debug("expand", depth: log_depth.to_i) {"context: #{context.inspect}"}
93
93
  end
94
94
 
@@ -99,7 +99,7 @@ module JSON::LD
99
99
 
100
100
  # See if keys mapping to @type have terms with a local context
101
101
  type_key = nil
102
- input.keys.sort.
102
+ (input.keys - %w(@context)).sort.
103
103
  select {|k| context.expand_iri(k, vocab: true, base: @options[:base]) == '@type'}.
104
104
  each do |tk|
105
105
 
@@ -23,7 +23,8 @@ module JSON::LD
23
23
  class Format < RDF::Format
24
24
  content_type 'application/ld+json',
25
25
  extension: :jsonld,
26
- alias: 'application/x-ld+json'
26
+ alias: 'application/x-ld+json',
27
+ uri: 'http://www.w3.org/ns/formats/JSON-LD'
27
28
  content_encoding 'utf-8'
28
29
 
29
30
  reader { JSON::LD::Reader }
@@ -44,8 +45,80 @@ module JSON::LD
44
45
  !sample.include?("http://www.w3.org/ns/csvw")
45
46
  end
46
47
 
48
+ # Specify how to execute CLI commands for each supported format.
49
+ # Derived formats (e.g., YAML-LD) define their own entrypoints.
50
+ LD_FORMATS = {
51
+ jsonld: {
52
+ expand: ->(input, **options) {
53
+ JSON::LD::API.expand(input,
54
+ serializer: JSON::LD::API.method(:serializer),
55
+ **options)
56
+ },
57
+ compact: ->(input, **options) {
58
+ JSON::LD::API.compact(input,
59
+ options[:context],
60
+ serializer: JSON::LD::API.method(:serializer),
61
+ **options)
62
+ },
63
+ flatten: ->(input, **options) {
64
+ JSON::LD::API.flatten(input,
65
+ options[:context],
66
+ serializer: JSON::LD::API.method(:serializer),
67
+ **options)
68
+ },
69
+ frame: ->(input, **options) {
70
+ JSON::LD::API.frame(input,
71
+ options[:frame],
72
+ serializer: JSON::LD::API.method(:serializer),
73
+ **options)
74
+ },
75
+ }
76
+ }
77
+
78
+ # Execute the body of a CLI command, generic for each different API method based on definitions on {LD_FORMATS}.
79
+ #
80
+ # Expands the input, or transforms from an RDF format based on the `:format` option, and then executes the appropriate command based on `:output_format` and does appropriate output serialization.
81
+ # @private
82
+ def self.cli_exec(command, files, output: $stdin, **options)
83
+ output.set_encoding(Encoding::UTF_8) if output.respond_to?(:set_encoding) && RUBY_PLATFORM == "java"
84
+ options[:base] ||= options[:base_uri]
85
+
86
+ # Parse using input format, serialize using output format
87
+ in_fmt = LD_FORMATS[options.fetch(:format, :jsonld)]
88
+ out_fmt = LD_FORMATS[options.fetch(:output_format, :jsonld)]
89
+
90
+ if in_fmt
91
+ # Input is a JSON-LD based source (or derived)
92
+ if files.empty?
93
+ # If files are empty, either use options[:evaluate] or STDIN
94
+ input = options[:evaluate] ? StringIO.new(options[:evaluate]) : STDIN
95
+ input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
96
+ expanded = in_fmt[:expand].call(input, serializer: nil, **options)
97
+ output.puts out_fmt[command].call(expanded, expanded: true, **options)
98
+ else
99
+ files.each do |file|
100
+ expanded = in_fmt[:expand].call(file, serializer: nil, **options)
101
+ output.puts out_fmt[command].call(expanded, expanded: true, **options)
102
+ end
103
+ end
104
+ else
105
+ # Turn RDF into JSON-LD first
106
+ RDF::CLI.parse(files, **options) do |reader|
107
+ JSON::LD::API.fromRdf(reader, serializer: nil, **options) do |expanded|
108
+ output.puts out_fmt[command].call(expanded, expanded: true, **options)
109
+ end
110
+ end
111
+ end
112
+ end
113
+
47
114
  ##
48
- # Hash of CLI commands appropriate for this format
115
+ # Hash of CLI commands appropriate for this format:
116
+ #
117
+ # * `expand` => {JSON::LD::API.expand}
118
+ # * `compact` => {JSON::LD::API.compact}
119
+ # * `flatten` => {JSON::LD::API.flatten}
120
+ # * `frame` => {JSON::LD::API.frame}
121
+ #
49
122
  # @return [Hash{Symbol => Hash}]
50
123
  def self.cli_commands
51
124
  {
@@ -53,73 +126,21 @@ module JSON::LD
53
126
  description: "Expand JSON-LD or parsed RDF",
54
127
  parse: false,
55
128
  help: "expand [--context <context-file>] files ...",
56
- filter: {output_format: :jsonld}, # Only shows output format set
129
+ filter: {output_format: LD_FORMATS.keys}, # Only shows output format set
57
130
  lambda: ->(files, **options) do
58
- out = options[:output] || $stdout
59
- out.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
60
131
  options = options.merge(expandContext: options.delete(:context)) if options.key?(:context)
61
- options[:base] ||= options[:base_uri]
62
- if options[:format] == :jsonld
63
- if files.empty?
64
- # If files are empty, either use options[:evaluate] or STDIN
65
- input = options[:evaluate] ? StringIO.new(options[:evaluate]) : STDIN
66
- input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
67
- JSON::LD::API.expand(input, validate: false, **options) do |expanded|
68
- out.puts expanded.to_json(JSON::LD::JSON_STATE)
69
- end
70
- else
71
- files.each do |file|
72
- JSON::LD::API.expand(file, validate: false, **options) do |expanded|
73
- out.puts expanded.to_json(JSON::LD::JSON_STATE)
74
- end
75
- end
76
- end
77
- else
78
- # Turn RDF into JSON-LD first
79
- RDF::CLI.parse(files, **options) do |reader|
80
- JSON::LD::API.fromRdf(reader) do |expanded|
81
- out.puts expanded.to_json(JSON::LD::JSON_STATE)
82
- end
83
- end
84
- end
132
+ cli_exec(:expand, files, **options)
85
133
  end,
86
134
  option_use: {context: :removed}
87
135
  },
88
136
  compact: {
89
137
  description: "Compact JSON-LD or parsed RDF",
90
138
  parse: false,
91
- filter: {output_format: :jsonld}, # Only shows output format set
139
+ filter: {output_format: LD_FORMATS.keys}, # Only shows output format set
92
140
  help: "compact --context <context-file> files ...",
93
141
  lambda: ->(files, **options) do
94
142
  raise ArgumentError, "Compacting requires a context" unless options[:context]
95
- out = options[:output] || $stdout
96
- out.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
97
- options[:base] ||= options[:base_uri]
98
- if options[:format] == :jsonld
99
- if files.empty?
100
- # If files are empty, either use options[:evaluate] or STDIN
101
- input = options[:evaluate] ? StringIO.new(options[:evaluate]) : STDIN
102
- input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
103
- JSON::LD::API.compact(input, options[:context], **options) do |compacted|
104
- out.puts compacted.to_json(JSON::LD::JSON_STATE)
105
- end
106
- else
107
- files.each do |file|
108
- JSON::LD::API.compact(file, options[:context], **options) do |compacted|
109
- out.puts compacted.to_json(JSON::LD::JSON_STATE)
110
- end
111
- end
112
- end
113
- else
114
- # Turn RDF into JSON-LD first
115
- RDF::CLI.parse(files, **options) do |reader|
116
- JSON::LD::API.fromRdf(reader) do |expanded|
117
- JSON::LD::API.compact(expanded, options[:context], **options) do |compacted|
118
- out.puts compacted.to_json(JSON::LD::JSON_STATE)
119
- end
120
- end
121
- end
122
- end
143
+ cli_exec(:compact, files, **options)
123
144
  end,
124
145
  options: [
125
146
  RDF::CLI::Option.new(
@@ -135,36 +156,9 @@ module JSON::LD
135
156
  description: "Flatten JSON-LD or parsed RDF",
136
157
  parse: false,
137
158
  help: "flatten [--context <context-file>] files ...",
138
- filter: {output_format: :jsonld}, # Only shows output format set
159
+ filter: {output_format: LD_FORMATS.keys}, # Only shows output format set
139
160
  lambda: ->(files, **options) do
140
- out = options[:output] || $stdout
141
- out.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
142
- options[:base] ||= options[:base_uri]
143
- if options[:format] == :jsonld
144
- if files.empty?
145
- # If files are empty, either use options[:evaluate] or STDIN
146
- input = options[:evaluate] ? StringIO.new(options[:evaluate]) : STDIN
147
- input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
148
- JSON::LD::API.flatten(input, options[:context], **options) do |flattened|
149
- out.puts flattened.to_json(JSON::LD::JSON_STATE)
150
- end
151
- else
152
- files.each do |file|
153
- JSON::LD::API.flatten(file, options[:context], **options) do |flattened|
154
- out.puts flattened.to_json(JSON::LD::JSON_STATE)
155
- end
156
- end
157
- end
158
- else
159
- # Turn RDF into JSON-LD first
160
- RDF::CLI.parse(files, **options) do |reader|
161
- JSON::LD::API.fromRdf(reader) do |expanded|
162
- JSON::LD::API.flatten(expanded, options[:context], **options) do |flattened|
163
- out.puts flattened.to_json(JSON::LD::JSON_STATE)
164
- end
165
- end
166
- end
167
- end
161
+ cli_exec(:compact, files, **options)
168
162
  end,
169
163
  options: [
170
164
  RDF::CLI::Option.new(
@@ -187,37 +181,10 @@ module JSON::LD
187
181
  description: "Frame JSON-LD or parsed RDF",
188
182
  parse: false,
189
183
  help: "frame --frame <frame-file> files ...",
190
- filter: {output_format: :jsonld}, # Only shows output format set
184
+ filter: {output_format: LD_FORMATS.keys}, # Only shows output format set
191
185
  lambda: ->(files, **options) do
192
186
  raise ArgumentError, "Framing requires a frame" unless options[:frame]
193
- out = options[:output] || $stdout
194
- out.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
195
- options[:base] ||= options[:base_uri]
196
- if options[:format] == :jsonld
197
- if files.empty?
198
- # If files are empty, either use options[:evaluate] or STDIN
199
- input = options[:evaluate] ? StringIO.new(options[:evaluate]) : STDIN
200
- input.set_encoding(options.fetch(:encoding, Encoding::UTF_8))
201
- JSON::LD::API.frame(input, options[:frame], **options) do |framed|
202
- out.puts framed.to_json(JSON::LD::JSON_STATE)
203
- end
204
- else
205
- files.each do |file|
206
- JSON::LD::API.frame(file, options[:frame], **options) do |framed|
207
- out.puts framed.to_json(JSON::LD::JSON_STATE)
208
- end
209
- end
210
- end
211
- else
212
- # Turn RDF into JSON-LD first
213
- RDF::CLI.parse(files, **options) do |reader|
214
- JSON::LD::API.fromRdf(reader) do |expanded|
215
- JSON::LD::API.frame(expanded, options[:frame], **options) do |framed|
216
- out.puts framed.to_json(JSON::LD::JSON_STATE)
217
- end
218
- end
219
- end
220
- end
187
+ cli_exec(:compact, files, **options)
221
188
  end,
222
189
  option_use: {context: :removed},
223
190
  options: [
@@ -12,7 +12,7 @@ module JSON::LD
12
12
 
13
13
  ##
14
14
  # JSON-LD Reader options
15
- # @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Reader#options-class_method
15
+ # @see https://ruby-rdf.github.io/rdf/RDF/Reader#options-class_method
16
16
  def self.options
17
17
  super + [
18
18
  RDF::CLI::Option.new(
@@ -57,7 +57,7 @@ module JSON::LD
57
57
  end
58
58
 
59
59
  ##
60
- # Initializes the RDF/JSON reader instance.
60
+ # Initializes the JSON-LD reader instance.
61
61
  #
62
62
  # @param [IO, File, String] input
63
63
  # @param [Hash{Symbol => Object}] options
@@ -26,7 +26,8 @@ module JSON::LD
26
26
  unique_bnodes, rename_bnodes = @options[:unique_bnodes], @options.fetch(:rename_bnodes, true)
27
27
  # FIXME: document loader doesn't stream
28
28
  @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)
30
31
  context_ref = @options[:expandContext]
31
32
  #context_ref = @options.fetch(:expandContext, remote_doc.contextUrl)
32
33
  context = Context.parse(context_ref, **@options)