json-ld 3.1.5 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65b570568511427e573b9ffecdc5eedfee1b708c62e88ffc24dce4ffa8d62c2f
4
- data.tar.gz: a3255398de49dd88c3d4b3beb918857c8939de6eed4cf92c2aa1f61764455b59
3
+ metadata.gz: 2509fe2ee3a0766a3e332d062f11ad05d02bf7a3c00e423dfe4ac8eec23f9746
4
+ data.tar.gz: 277416c70b52a06ec85d89394fe18ccbe9eacb53538a6c65432ed390728b93ca
5
5
  SHA512:
6
- metadata.gz: 6f84d29c4d742ffe07dbdc585f8d51e612b7426dc3a58d21061d959587ebdfff0801bc02d3c7edf11426d03d6b9d500a54bdd172afc34b460bd5957fc0f9e4c5
7
- data.tar.gz: ee8b915d0fb258e909e751c668ea31750b255d5d7e0276123439a149eece298284324fb63a8d13df9b23109f0662dcbcd0db41f56e23e22f745da17f50f7b75b
6
+ metadata.gz: 20db323ff35ccd493d9fbd0b6f73f4c5fc517678363aff6fbf21f114ce33e2e491f4717e87c3ad3f2a19c31beb728c627b00cb7f8bff268575c6655cc29e6ff2
7
+ data.tar.gz: da506865a8f17c2fc304159bfb61411d11c433b656ea58bf1b19371618a2c0f02c08274f8a5a6ca97f1f9087a7d797b9be087314fd0cd41e154fe53721fcb6d8
data/README.md CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  [JSON-LD][] reader/writer for [RDF.rb][RDF.rb] and fully conforming [JSON-LD API][] processor. Additionally this gem implements [JSON-LD Framing][].
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/json-ld.png)](https://badge.fury.io/rb/json-ld)
6
- [![Build Status](https://secure.travis-ci.org/ruby-rdf/json-ld.png?branch=master)](https://travis-ci.org/ruby-rdf/json-ld)
7
- [![Coverage Status](https://coveralls.io/repos/ruby-rdf/json-ld/badge.svg)](https://coveralls.io/r/ruby-rdf/json-ld)
5
+ [![Gem Version](https://badge.fury.io/rb/json-ld.png)](https://rubygems.org/gems/json-ld)
6
+ [![Build Status](https://secure.travis-ci.org/ruby-rdf/json-ld.png?branch=develop)](https://github.com/ruby-rdf/json-ld/actions?query=workflow%3ACI)
7
+ [![Coverage Status](https://coveralls.io/repos/ruby-rdf/json-ld/badge.svg)](https://coveralls.io/github/ruby-rdf/json-ld)
8
+ [![Gitter chat](https://badges.gitter.im/ruby-rdf.png)](https://gitter.im/gitterHQ/gitter)
8
9
 
9
10
  ## Features
10
11
 
@@ -14,6 +15,7 @@ JSON::LD can now be used to create a _context_ from an RDFS/OWL definition, and
14
15
 
15
16
  * If the [jsonlint][] gem is installed, it will be used when validating an input document.
16
17
  * If available, uses [Nokogiri][] and/or [Nokogumbo][] for parsing HTML, falls back to REXML otherwise.
18
+ * Provisional support for [JSON-LD*][JSON-LD*].
17
19
 
18
20
  [Implementation Report](file.earl.html)
19
21
 
@@ -35,6 +37,59 @@ The order of triples retrieved from the `RDF::Enumerable` dataset determines the
35
37
  ### MultiJson parser
36
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.
37
39
 
40
+ ### JSON-LD* (RDFStar)
41
+
42
+ The {JSON::LD::API.toRdf} and {JSON::LD::API.fromRdf} API methods, along with the {JSON::LD::Reader} and {JSON::LD::Writer}, include provisional support for [JSON-LD*][JSON-LD*].
43
+
44
+ Internally, an `RDF::Statement` is treated as another resource, along with `RDF::URI` and `RDF::Node`, which allows an `RDF::Statement` to have a `#subject` or `#object` which is also an `RDF::Statement`.
45
+
46
+ In JSON-LD, with the `rdfstar` option set, the value of `@id`, in addition to an IRI or Blank Node Identifier, can be a JSON-LD node object having exactly one property with an optional `@id`, which may also be an embedded object. (It may also have `@context` and `@index` values).
47
+
48
+ {
49
+ "@id": {
50
+ "@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
51
+ "@index": "ignored",
52
+ "@id": "bob",
53
+ "foaf:age" 23
54
+ },
55
+ "ex:certainty": 0.9
56
+ }
57
+
58
+ **Note: This feature is subject to change or elimination as the standards process progresses.**
59
+
60
+ #### Serializing a Graph containing embedded statements
61
+
62
+ require 'json-ld'
63
+ statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23))
64
+ graph = RDF::Graph.new << [statement, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
65
+ graph.dump(:jsonld, validate: false, standard_prefixes: true)
66
+ # => {"@id": {"@id": "bob", "foaf:age" 23}, "ex:certainty": 0.9}
67
+
68
+ Alternatively, using the {JSON::LD::API.fromRdf} method:
69
+
70
+ JSON::LD::API::fromRdf(graph)
71
+ # => {"@id": {"@id": "bob", "foaf:age" 23}, "ex:certainty": 0.9}
72
+
73
+ #### Reading a Graph containing embedded statements
74
+
75
+ By default, {JSON::LD::API.toRdf} (and {JSON::LD::Reader}) will reject a document containing a subject resource.
76
+
77
+ jsonld = %({
78
+ "@id": {
79
+ "@id": "bob", "foaf:age" 23
80
+ },
81
+ "ex:certainty": 0.9
82
+ })
83
+ graph = RDF::Graph.new << JSON::LD::API.toRdf(input)
84
+ # => JSON::LD::JsonLdError::InvalidIdValue
85
+
86
+ {JSON::LD::API.toRdf} (and {JSON::LD::Reader}) support a boolean valued `rdfstar` option; only one statement is asserted, although the reified statement is contained within the graph.
87
+
88
+ graph = RDF::Graph.new do |graph|
89
+ JSON::LD::Reader.new(jsonld, rdfstar: true) {|reader| graph << reader}
90
+ end
91
+ graph.count #=> 1
92
+
38
93
  ## Examples
39
94
 
40
95
  ```ruby
@@ -568,6 +623,7 @@ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
568
623
  [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
569
624
  [PDD]: https://unlicense.org/#unlicensing-contributions
570
625
  [RDF.rb]: https://rubygems.org/gems/rdf
626
+ [JSON-LD*]: https://json-ld.github.io/json-ld-star/
571
627
  [Rack::LinkedData]: https://rubygems.org/gems/rack-linkeddata
572
628
  [Backports]: https://rubygems.org/gems/backports
573
629
  [JSON-LD]: https://www.w3.org/TR/json-ld11/ "JSON-LD 1.1"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.5
1
+ 3.1.6
@@ -137,6 +137,7 @@ module JSON
137
137
  class InvalidNestValue < JsonLdError; @code = "invalid @nest value"; end
138
138
  class InvalidPrefixValue < JsonLdError; @code = "invalid @prefix value"; end
139
139
  class InvalidPropagateValue < JsonLdError; @code = "invalid @propagate value"; end
140
+ class InvalidEmbeddedNode < JsonLdError; @code = "invalid reified node"; end
140
141
  class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
141
142
  class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
142
143
  class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
@@ -89,6 +89,8 @@ module JSON::LD
89
89
  # @option options [String] :processingMode
90
90
  # Processing mode, json-ld-1.0 or json-ld-1.1.
91
91
  # If `processingMode` is not specified, a mode of `json-ld-1.0` or `json-ld-1.1` is set, the context used for `expansion` or `compaction`.
92
+ # @option options [Boolean] rdfstar (false)
93
+ # support parsing JSON-LD* statement resources.
92
94
  # @option options [Boolean] :rename_bnodes (true)
93
95
  # Rename bnodes as part of expansion, or keep them the same.
94
96
  # @option options [Boolean] :unique_bnodes (false)
@@ -273,12 +273,23 @@ module JSON::LD
273
273
  context.expand_iri(v, as_string: true, base: @options[:base], documentRelative: true)
274
274
  end
275
275
  when Hash
276
- raise JsonLdError::InvalidIdValue,
277
- "value of @id must be a string unless framing: #{value.inspect}" unless framing
278
- raise JsonLdError::InvalidTypeValue,
279
- "value of @id must be a an empty object for framing: #{value.inspect}" unless
280
- value.empty?
281
- [{}]
276
+ if framing
277
+ raise JsonLdError::InvalidTypeValue,
278
+ "value of @id must be a an empty object for framing: #{value.inspect}" unless
279
+ value.empty?
280
+ [{}]
281
+ elsif @options[:rdfstar]
282
+ # Result must have just a single statement
283
+ rei_node = expand(value, active_property, context, log_depth: log_depth.to_i + 1)
284
+ statements = to_enum(:item_to_rdf, rei_node)
285
+ raise JsonLdError::InvalidEmbeddedNode,
286
+ "Embedded node with #{statements.size} statements" unless
287
+ statements.count == 1
288
+ rei_node
289
+ else
290
+ raise JsonLdError::InvalidIdValue,
291
+ "value of @id must be a string unless framing: #{value.inspect}" unless framing
292
+ end
282
293
  else
283
294
  raise JsonLdError::InvalidIdValue,
284
295
  "value of @id must be a string or hash if framing: #{value.inspect}"
@@ -165,7 +165,16 @@ module JSON::LD
165
165
  end
166
166
  end
167
167
  end
168
- end
168
+ end,
169
+ options: [
170
+ RDF::CLI::Option.new(
171
+ symbol: :context,
172
+ datatype: RDF::URI,
173
+ control: :url2,
174
+ use: :required,
175
+ on: ["--context CONTEXT"],
176
+ description: "Context to use when compacting.") {|arg| RDF::URI(arg)},
177
+ ]
169
178
  },
170
179
  frame: {
171
180
  description: "Frame JSON-LD or parsed RDF",
@@ -22,7 +22,6 @@ module JSON::LD
22
22
  referenced_once = {}
23
23
 
24
24
  value = nil
25
- ec = @context
26
25
 
27
26
  # Create an entry for compound-literal node detection
28
27
  compound_literal_subjects = {}
@@ -33,7 +32,7 @@ module JSON::LD
33
32
  dataset.each do |statement|
34
33
  #log_debug("statement") { statement.to_nquads.chomp}
35
34
 
36
- name = statement.graph_name ? ec.expand_iri(statement.graph_name, base: @options[:base]).to_s : '@default'
35
+ name = statement.graph_name ? @context.expand_iri(statement.graph_name, base: @options[:base]).to_s : '@default'
37
36
 
38
37
  # Create a graph entry as needed
39
38
  node_map = graph_map[name] ||= {}
@@ -41,30 +40,29 @@ module JSON::LD
41
40
 
42
41
  default_graph[name] ||= {'@id' => name} unless name == '@default'
43
42
 
44
- subject = ec.expand_iri(statement.subject, as_string: true, base: @options[:base])
45
- node = node_map[subject] ||= {'@id' => subject}
43
+ subject = statement.subject.to_s
44
+ node = node_map[subject] ||= resource_representation(statement.subject,useNativeTypes)
46
45
 
47
46
  # If predicate is rdf:datatype, note subject in compound literal subjects map
48
47
  if @options[:rdfDirection] == 'compound-literal' && statement.predicate == RDF.to_uri + 'direction'
49
48
  compound_literal_subjects[name][subject] ||= true
50
49
  end
51
50
 
52
- # If object is an IRI or blank node identifier, and node map does not have an object member, create one and initialize its value to a new JSON object consisting of a single member @id whose value is set to object.
53
- node_map[statement.object.to_s] ||= {'@id' => statement.object.to_s} unless
54
- statement.object.literal?
51
+ # If object is an IRI, blank node identifier, or statement, and node map does not have an object member, create one and initialize its value to a new JSON object consisting of a single member @id whose value is set to object.
52
+ unless statement.object.literal?
53
+ node_map[statement.object.to_s] ||=
54
+ resource_representation(statement.object, useNativeTypes)
55
+ end
55
56
 
56
57
  # If predicate equals rdf:type, and object is an IRI or blank node identifier, append object to the value of the @type member of node. If no such member exists, create one and initialize it to an array whose only item is object. Finally, continue to the next RDF triple.
58
+ # XXX JSON-LD* does not support embedded value of @type
57
59
  if statement.predicate == RDF.type && statement.object.resource? && !useRdfType
58
60
  merge_value(node, '@type', statement.object.to_s)
59
61
  next
60
62
  end
61
63
 
62
64
  # Set value to the result of using the RDF to Object Conversion algorithm, passing object, rdfDirection, and use native types.
63
- value = ec.expand_value(nil,
64
- statement.object,
65
- rdfDirection: @options[:rdfDirection],
66
- useNativeTypes: useNativeTypes,
67
- base: @options[:base])
65
+ value = resource_representation(statement.object, useNativeTypes)
68
66
 
69
67
  merge_value(node, statement.predicate.to_s, value)
70
68
 
@@ -162,5 +160,31 @@ module JSON::LD
162
160
  #log_debug("fromRdf") {result.to_json(JSON_STATE) rescue 'malformed json'}
163
161
  result
164
162
  end
163
+
164
+ private
165
+ def resource_representation(resource, useNativeTypes)
166
+ case resource
167
+ when RDF::Statement
168
+ # Note, if either subject or object are a BNode which is used elsewhere,
169
+ # this might not work will with the BNode accounting from above.
170
+ rep = {'@id' => resource_representation(resource.subject, false)}
171
+ if resource.predicate == RDF.type
172
+ rep['@id'].merge!('@type' => resource.object.to_s)
173
+ else
174
+ rep['@id'].merge!(
175
+ resource.predicate.to_s =>
176
+ as_array(resource_representation(resource.object, useNativeTypes)))
177
+ end
178
+ rep
179
+ when RDF::Literal
180
+ @context.expand_value(nil,
181
+ resource,
182
+ rdfDirection: @options[:rdfDirection],
183
+ useNativeTypes: useNativeTypes,
184
+ base: @options[:base])
185
+ else
186
+ {'@id' => resource.to_s}
187
+ end
188
+ end
165
189
  end
166
190
  end
@@ -16,6 +16,8 @@ module JSON::LD
16
16
  # @return RDF::Resource the subject of this item
17
17
  def item_to_rdf(item, graph_name: nil, &block)
18
18
  # Just return value object as Term
19
+ return unless item
20
+
19
21
  if value?(item)
20
22
  value, datatype = item.fetch('@value'), item.fetch('@type', nil)
21
23
 
@@ -76,11 +78,13 @@ module JSON::LD
76
78
  return parse_list(item['@list'], graph_name: graph_name, &block)
77
79
  end
78
80
 
79
- # Skip if '@id' is nil
80
- subject = if item.has_key?('@id')
81
- item['@id'].nil? ? nil : as_resource(item['@id'])
82
- else
83
- node
81
+ subject = case item['@id']
82
+ when nil then node
83
+ when String then as_resource(item['@id'])
84
+ when Object
85
+ # Embedded statement
86
+ # (No error checking, as this is done in expansion)
87
+ to_enum(:item_to_rdf, item['@id']).to_a.first
84
88
  end
85
89
 
86
90
  #log_debug("item_to_rdf") {"subject: #{subject.to_ntriples rescue 'malformed rdf'}"}
@@ -3376,6 +3376,254 @@ describe JSON::LD::API do
3376
3376
  end
3377
3377
  end
3378
3378
 
3379
+ context "JSON-LD*" do
3380
+ {
3381
+ "node with embedded subject without rdfstar option": {
3382
+ input: %({
3383
+ "@id": {
3384
+ "@id": "ex:rei",
3385
+ "ex:prop": "value"
3386
+ },
3387
+ "ex:prop": "value2"
3388
+ }),
3389
+ exception: JSON::LD::JsonLdError::InvalidIdValue
3390
+ },
3391
+ }.each do |title, params|
3392
+ it(title) {run_expand params}
3393
+ end
3394
+
3395
+ {
3396
+ "node with embedded subject having no @id": {
3397
+ input: %({
3398
+ "@id": {
3399
+ "ex:prop": "value"
3400
+ },
3401
+ "ex:prop": "value2"
3402
+ }),
3403
+ output: %([{
3404
+ "@id": {
3405
+ "ex:prop": [{"@value": "value"}]
3406
+ },
3407
+ "ex:prop": [{"@value": "value2"}]
3408
+ }])
3409
+ },
3410
+ "node with embedded subject having IRI @id": {
3411
+ input: %({
3412
+ "@id": {
3413
+ "@id": "ex:rei",
3414
+ "ex:prop": "value"
3415
+ },
3416
+ "ex:prop": "value2"
3417
+ }),
3418
+ output: %([{
3419
+ "@id": {
3420
+ "@id": "ex:rei",
3421
+ "ex:prop": [{"@value": "value"}]
3422
+ },
3423
+ "ex:prop": [{"@value": "value2"}]
3424
+ }])
3425
+ },
3426
+ "node with embedded subject having BNode @id": {
3427
+ input: %({
3428
+ "@id": {
3429
+ "@id": "_:rei",
3430
+ "ex:prop": "value"
3431
+ },
3432
+ "ex:prop": "value2"
3433
+ }),
3434
+ output: %([{
3435
+ "@id": {
3436
+ "@id": "_:rei",
3437
+ "ex:prop": [{"@value": "value"}]
3438
+ },
3439
+ "ex:prop": [{"@value": "value2"}]
3440
+ }])
3441
+ },
3442
+ "node with embedded subject having a type": {
3443
+ input: %({
3444
+ "@id": {
3445
+ "@id": "ex:rei",
3446
+ "@type": "ex:Type"
3447
+ },
3448
+ "ex:prop": "value2"
3449
+ }),
3450
+ output: %([{
3451
+ "@id": {
3452
+ "@id": "ex:rei",
3453
+ "@type": ["ex:Type"]
3454
+ },
3455
+ "ex:prop": [{"@value": "value2"}]
3456
+ }])
3457
+ },
3458
+ "node with embedded subject having an IRI value": {
3459
+ input: %({
3460
+ "@id": {
3461
+ "@id": "ex:rei",
3462
+ "ex:prop": {"@id": "ex:value"}
3463
+ },
3464
+ "ex:prop": "value2"
3465
+ }),
3466
+ output: %([{
3467
+ "@id": {
3468
+ "@id": "ex:rei",
3469
+ "ex:prop": [{"@id": "ex:value"}]
3470
+ },
3471
+ "ex:prop": [{"@value": "value2"}]
3472
+ }])
3473
+ },
3474
+ "node with embedded subject having an BNode value": {
3475
+ input: %({
3476
+ "@id": {
3477
+ "@id": "ex:rei",
3478
+ "ex:prop": {"@id": "_:value"}
3479
+ },
3480
+ "ex:prop": "value2"
3481
+ }),
3482
+ output: %([{
3483
+ "@id": {
3484
+ "@id": "ex:rei",
3485
+ "ex:prop": [{"@id": "_:value"}]
3486
+ },
3487
+ "ex:prop": [{"@value": "value2"}]
3488
+ }])
3489
+ },
3490
+ "node with recursive embedded subject": {
3491
+ input: %({
3492
+ "@id": {
3493
+ "@id": {
3494
+ "@id": "ex:rei",
3495
+ "ex:prop": "value3"
3496
+ },
3497
+ "ex:prop": "value"
3498
+ },
3499
+ "ex:prop": "value2"
3500
+ }),
3501
+ output: %([{
3502
+ "@id": {
3503
+ "@id": {
3504
+ "@id": "ex:rei",
3505
+ "ex:prop": [{"@value": "value3"}]
3506
+ },
3507
+ "ex:prop": [{"@value": "value"}]
3508
+ },
3509
+ "ex:prop": [{"@value": "value2"}]
3510
+ }])
3511
+ },
3512
+ "illegal node with subject having no property": {
3513
+ input: %({
3514
+ "@id": {
3515
+ "@id": "ex:rei"
3516
+ },
3517
+ "ex:prop": "value3"
3518
+ }),
3519
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
3520
+ },
3521
+ "illegal node with subject having multiple properties": {
3522
+ input: %({
3523
+ "@id": {
3524
+ "@id": "ex:rei",
3525
+ "ex:prop": ["value1", "value2"]
3526
+ },
3527
+ "ex:prop": "value3"
3528
+ }),
3529
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
3530
+ },
3531
+ "illegal node with subject having multiple types": {
3532
+ input: %({
3533
+ "@id": {
3534
+ "@id": "ex:rei",
3535
+ "@type": ["ex:Type1", "ex:Type2"]
3536
+ },
3537
+ "ex:prop": "value3"
3538
+ }),
3539
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
3540
+ },
3541
+ "illegal node with subject having type and property": {
3542
+ input: %({
3543
+ "@id": {
3544
+ "@id": "ex:rei",
3545
+ "@type": "ex:Type",
3546
+ "ex:prop": "value"
3547
+ },
3548
+ "ex:prop": "value2"
3549
+ }),
3550
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
3551
+ },
3552
+ "node with embedded object": {
3553
+ input: %({
3554
+ "@id": "ex:subj",
3555
+ "ex:value": {
3556
+ "@id": {
3557
+ "@id": "ex:rei",
3558
+ "ex:prop": "value"
3559
+ }
3560
+ }
3561
+ }),
3562
+ output: %([{
3563
+ "@id": "ex:subj",
3564
+ "ex:value": [{
3565
+ "@id": {
3566
+ "@id": "ex:rei",
3567
+ "ex:prop": [{"@value": "value"}]
3568
+ }
3569
+ }]
3570
+ }])
3571
+ },
3572
+ "illegal node with embedded object having properties": {
3573
+ input: %({
3574
+ "@id": "ex:subj",
3575
+ "ex:value": {
3576
+ "@id": {
3577
+ "@id": "ex:rei",
3578
+ "ex:prop": "value"
3579
+ },
3580
+ "ex:prop": "value2"
3581
+ }
3582
+ }),
3583
+ output: %([{
3584
+ "@id": "ex:subj",
3585
+ "ex:value": [{
3586
+ "@id": {
3587
+ "@id": "ex:rei",
3588
+ "ex:prop": [{"@value": "value"}]
3589
+ },
3590
+ "ex:prop": [{"@value": "value2"}]
3591
+ }]
3592
+ }])
3593
+ },
3594
+ "node with recursive embedded object": {
3595
+ input: %({
3596
+ "@id": "ex:subj",
3597
+ "ex:value": {
3598
+ "@id": {
3599
+ "@id": {
3600
+ "@id": "ex:rei",
3601
+ "ex:prop": "value3"
3602
+ },
3603
+ "ex:prop": "value"
3604
+ },
3605
+ "ex:prop": "value2"
3606
+ }
3607
+ }),
3608
+ output: %([{
3609
+ "@id": "ex:subj",
3610
+ "ex:value": [{
3611
+ "@id": {
3612
+ "@id": {
3613
+ "@id": "ex:rei",
3614
+ "ex:prop": [{"@value": "value3"}]
3615
+ },
3616
+ "ex:prop":[{"@value": "value"}]
3617
+ },
3618
+ "ex:prop": [{"@value": "value2"}]
3619
+ }]
3620
+ }])
3621
+ },
3622
+ }.each do |title, params|
3623
+ it(title) {run_expand params.merge(rdfstar: true)}
3624
+ end
3625
+ end
3626
+
3379
3627
  begin
3380
3628
  require 'nokogiri'
3381
3629
  rescue LoadError
@@ -766,6 +766,187 @@ describe JSON::LD::API do
766
766
  end
767
767
  end
768
768
 
769
+ context "RDF*" do
770
+ {
771
+ "subject-iii": {
772
+ input: RDF::Statement(
773
+ RDF::Statement(
774
+ RDF::URI('http://example/s1'),
775
+ RDF::URI('http://example/p1'),
776
+ RDF::URI('http://example/o1')),
777
+ RDF::URI('http://example/p'),
778
+ RDF::URI('http://example/o')),
779
+ output: %([{
780
+ "@id": {
781
+ "@id": "http://example/s1",
782
+ "http://example/p1": [{"@id": "http://example/o1"}]
783
+ },
784
+ "http://example/p": [{"@id": "http://example/o"}]
785
+ }])
786
+ },
787
+ "subject-iib": {
788
+ input: RDF::Statement(
789
+ RDF::Statement(
790
+ RDF::URI('http://example/s1'),
791
+ RDF::URI('http://example/p1'),
792
+ RDF::Node.new('o1')),
793
+ RDF::URI('http://example/p'),
794
+ RDF::URI('http://example/o')),
795
+ output: %([{
796
+ "@id": {
797
+ "@id": "http://example/s1",
798
+ "http://example/p1": [{"@id": "_:o1"}]
799
+ },
800
+ "http://example/p": [{"@id": "http://example/o"}]
801
+ }])
802
+ },
803
+ "subject-iil": {
804
+ input: RDF::Statement(
805
+ RDF::Statement(
806
+ RDF::URI('http://example/s1'),
807
+ RDF::URI('http://example/p1'),
808
+ RDF::Literal('o1')),
809
+ RDF::URI('http://example/p'),
810
+ RDF::URI('http://example/o')),
811
+ output: %([{
812
+ "@id": {
813
+ "@id": "http://example/s1",
814
+ "http://example/p1": [{"@value": "o1"}]
815
+ },
816
+ "http://example/p": [{"@id": "http://example/o"}]
817
+ }])
818
+ },
819
+ "subject-bii": {
820
+ input: RDF::Statement(
821
+ RDF::Statement(
822
+ RDF::Node('s1'),
823
+ RDF::URI('http://example/p1'),
824
+ RDF::URI('http://example/o1')),
825
+ RDF::URI('http://example/p'),
826
+ RDF::URI('http://example/o')),
827
+ output: %([{
828
+ "@id": {
829
+ "@id": "_:s1",
830
+ "http://example/p1": [{"@id": "http://example/o1"}]
831
+ },
832
+ "http://example/p": [{"@id": "http://example/o"}]
833
+ }])
834
+ },
835
+ "subject-bib": {
836
+ input: RDF::Statement(
837
+ RDF::Statement(
838
+ RDF::Node('s1'),
839
+ RDF::URI('http://example/p1'),
840
+ RDF::Node.new('o1')),
841
+ RDF::URI('http://example/p'), RDF::URI('http://example/o')),
842
+ output: %([{
843
+ "@id": {
844
+ "@id": "_:s1",
845
+ "http://example/p1": [{"@id": "_:o1"}]
846
+ },
847
+ "http://example/p": [{"@id": "http://example/o"}]
848
+ }])
849
+ },
850
+ "subject-bil": {
851
+ input: RDF::Statement(
852
+ RDF::Statement(
853
+ RDF::Node('s1'),
854
+ RDF::URI('http://example/p1'),
855
+ RDF::Literal('o1')),
856
+ RDF::URI('http://example/p'),
857
+ RDF::URI('http://example/o')),
858
+ output: %([{
859
+ "@id": {
860
+ "@id": "_:s1",
861
+ "http://example/p1": [{"@value": "o1"}]
862
+ },
863
+ "http://example/p": [{"@id": "http://example/o"}]
864
+ }])
865
+ },
866
+ "object-iii": {
867
+ input: RDF::Statement(
868
+ RDF::URI('http://example/s'),
869
+ RDF::URI('http://example/p'),
870
+ RDF::Statement(
871
+ RDF::URI('http://example/s1'),
872
+ RDF::URI('http://example/p1'),
873
+ RDF::URI('http://example/o1'))),
874
+ output: %([{
875
+ "@id": "http://example/s",
876
+ "http://example/p": [{
877
+ "@id": {
878
+ "@id": "http://example/s1",
879
+ "http://example/p1": [{"@id": "http://example/o1"}]
880
+ }
881
+ }]
882
+ }])
883
+ },
884
+ "object-iib": {
885
+ input: RDF::Statement(
886
+ RDF::URI('http://example/s'),
887
+ RDF::URI('http://example/p'),
888
+ RDF::Statement(
889
+ RDF::URI('http://example/s1'),
890
+ RDF::URI('http://example/p1'),
891
+ RDF::Node.new('o1'))),
892
+ output: %([{
893
+ "@id": "http://example/s",
894
+ "http://example/p": [{
895
+ "@id": {
896
+ "@id": "http://example/s1",
897
+ "http://example/p1": [{"@id": "_:o1"}]
898
+ }
899
+ }]
900
+ }])
901
+ },
902
+ "object-iil": {
903
+ input: RDF::Statement(
904
+ RDF::URI('http://example/s'),
905
+ RDF::URI('http://example/p'),
906
+ RDF::Statement(
907
+ RDF::URI('http://example/s1'),
908
+ RDF::URI('http://example/p1'),
909
+ RDF::Literal('o1'))),
910
+ output: %([{
911
+ "@id": "http://example/s",
912
+ "http://example/p": [{
913
+ "@id": {
914
+ "@id": "http://example/s1",
915
+ "http://example/p1": [{"@value": "o1"}]
916
+ }
917
+ }]
918
+ }])
919
+ },
920
+ "recursive-subject": {
921
+ input: RDF::Statement(
922
+ RDF::Statement(
923
+ RDF::Statement(
924
+ RDF::URI('http://example/s2'),
925
+ RDF::URI('http://example/p2'),
926
+ RDF::URI('http://example/o2')),
927
+ RDF::URI('http://example/p1'),
928
+ RDF::URI('http://example/o1')),
929
+ RDF::URI('http://example/p'),
930
+ RDF::URI('http://example/o')),
931
+ output: %([{
932
+ "@id": {
933
+ "@id": {
934
+ "@id": "http://example/s2",
935
+ "http://example/p2": [{"@id": "http://example/o2"}]
936
+ },
937
+ "http://example/p1": [{"@id": "http://example/o1"}]
938
+ },
939
+ "http://example/p": [{"@id": "http://example/o"}]
940
+ }])
941
+ },
942
+ }.each do |name, params|
943
+ it name do
944
+ graph = RDF::Graph.new {|g| g << params[:input]}
945
+ do_fromRdf(params.merge(input: graph, prefixes: {ex: 'http://example/'}))
946
+ end
947
+ end
948
+ end
949
+
769
950
  context "problems" do
770
951
  {
771
952
  "xsd:boolean as value" => {
@@ -9,6 +9,7 @@ describe JSON::LD do
9
9
  m.entries.each do |t|
10
10
  specify "#{t.property('@id')}: #{t.name}#{' (negative test)' unless t.positiveTest?}" do
11
11
  pending "Generalized RDF" if t.options[:produceGeneralizedRdf]
12
+ pending "RDF*" if t.property('@id') == '#te122'
12
13
  if %w(#t0118).include?(t.property('@id'))
13
14
  expect {t.run self}.to write(/Statement .* is invalid/).to(:error)
14
15
  elsif %w(#te075).include?(t.property('@id'))
@@ -1175,6 +1175,212 @@ describe JSON::LD::API do
1175
1175
  end
1176
1176
  end
1177
1177
 
1178
+ context "JSON-LD*" do
1179
+ {
1180
+ "node with embedded subject without rdfstar option": {
1181
+ input: %({
1182
+ "@id": {
1183
+ "@id": "ex:rei",
1184
+ "ex:prop": "value"
1185
+ },
1186
+ "ex:prop": "value2"
1187
+ }),
1188
+ exception: JSON::LD::JsonLdError::InvalidIdValue
1189
+ },
1190
+ }.each do |title, params|
1191
+ it(title) {run_to_rdf params}
1192
+ end
1193
+
1194
+ {
1195
+ "node with embedded subject having no @id": {
1196
+ input: %({
1197
+ "@id": {
1198
+ "ex:prop": "value"
1199
+ },
1200
+ "ex:prop": "value2"
1201
+ }),
1202
+ expected: %(
1203
+ <<_:b0 <ex:prop> "value">> <ex:prop> "value2" .
1204
+ ),
1205
+ },
1206
+ "node with embedded subject having IRI @id": {
1207
+ input: %({
1208
+ "@id": {
1209
+ "@id": "ex:rei",
1210
+ "ex:prop": "value"
1211
+ },
1212
+ "ex:prop": "value2"
1213
+ }),
1214
+ expected: %(
1215
+ <<<ex:rei> <ex:prop> "value">> <ex:prop> "value2" .
1216
+ ),
1217
+ },
1218
+ "node with embedded subject having BNode @id": {
1219
+ input: %({
1220
+ "@id": {
1221
+ "@id": "_:rei",
1222
+ "ex:prop": "value"
1223
+ },
1224
+ "ex:prop": "value2"
1225
+ }),
1226
+ expected: %(
1227
+ <<_:b0 <ex:prop> "value">> <ex:prop> "value2" .
1228
+ ),
1229
+ },
1230
+ "node with embedded subject having a type": {
1231
+ input: %({
1232
+ "@id": {
1233
+ "@id": "ex:rei",
1234
+ "@type": "ex:Type"
1235
+ },
1236
+ "ex:prop": "value2"
1237
+ }),
1238
+ expected: %(
1239
+ <<<ex:rei> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <ex:Type>>> <ex:prop> "value2" .
1240
+ ),
1241
+ },
1242
+ "node with embedded subject having an IRI value": {
1243
+ input: %({
1244
+ "@id": {
1245
+ "@id": "ex:rei",
1246
+ "ex:prop": {"@id": "ex:value"}
1247
+ },
1248
+ "ex:prop": "value2"
1249
+ }),
1250
+ expected: %(
1251
+ <<<ex:rei> <ex:prop> <ex:value>>> <ex:prop> "value2" .
1252
+ ),
1253
+ },
1254
+ "node with embedded subject having an BNode value": {
1255
+ input: %({
1256
+ "@id": {
1257
+ "@id": "ex:rei",
1258
+ "ex:prop": {"@id": "_:value"}
1259
+ },
1260
+ "ex:prop": "value2"
1261
+ }),
1262
+ expected: %(
1263
+ <<<ex:rei> <ex:prop> _:b0>> <ex:prop> "value2" .
1264
+ ),
1265
+ },
1266
+ "node with recursive embedded subject": {
1267
+ input: %({
1268
+ "@id": {
1269
+ "@id": {
1270
+ "@id": "ex:rei",
1271
+ "ex:prop": "value3"
1272
+ },
1273
+ "ex:prop": "value"
1274
+ },
1275
+ "ex:prop": "value2"
1276
+ }),
1277
+ expected: %(
1278
+ <<<<<ex:rei> <ex:prop> "value3">> <ex:prop> "value">> <ex:prop> "value2" .
1279
+ ),
1280
+ },
1281
+ "illegal node with subject having no property": {
1282
+ input: %({
1283
+ "@id": {
1284
+ "@id": "ex:rei"
1285
+ },
1286
+ "ex:prop": "value3"
1287
+ }),
1288
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
1289
+ },
1290
+ "illegal node with subject having multiple properties": {
1291
+ input: %({
1292
+ "@id": {
1293
+ "@id": "ex:rei",
1294
+ "ex:prop": ["value1", "value2"]
1295
+ },
1296
+ "ex:prop": "value3"
1297
+ }),
1298
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
1299
+ },
1300
+ "illegal node with subject having multiple types": {
1301
+ input: %({
1302
+ "@id": {
1303
+ "@id": "ex:rei",
1304
+ "@type": ["ex:Type1", "ex:Type2"]
1305
+ },
1306
+ "ex:prop": "value3"
1307
+ }),
1308
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
1309
+ },
1310
+ "illegal node with subject having type and property": {
1311
+ input: %({
1312
+ "@id": {
1313
+ "@id": "ex:rei",
1314
+ "@type": "ex:Type",
1315
+ "ex:prop": "value"
1316
+ },
1317
+ "ex:prop": "value2"
1318
+ }),
1319
+ exception: JSON::LD::JsonLdError::InvalidEmbeddedNode
1320
+ },
1321
+ "node with embedded object": {
1322
+ input: %({
1323
+ "@id": "ex:subj",
1324
+ "ex:value": {
1325
+ "@id": {
1326
+ "@id": "ex:rei",
1327
+ "ex:prop": "value"
1328
+ }
1329
+ }
1330
+ }),
1331
+ expected: %(
1332
+ <ex:subj> <ex:value> <<<ex:rei> <ex:prop> "value">> .
1333
+ ),
1334
+ },
1335
+ "node with embedded object having properties": {
1336
+ input: %({
1337
+ "@id": "ex:subj",
1338
+ "ex:value": {
1339
+ "@id": {
1340
+ "@id": "ex:rei",
1341
+ "ex:prop": "value"
1342
+ },
1343
+ "ex:prop": "value2"
1344
+ }
1345
+ }),
1346
+ expected: %(
1347
+ <ex:subj> <ex:value> <<<ex:rei> <ex:prop> "value">> .
1348
+ <<<ex:rei> <ex:prop> "value">> <ex:prop> "value2" .
1349
+ ),
1350
+ },
1351
+ "node with recursive embedded object": {
1352
+ input: %({
1353
+ "@id": "ex:subj",
1354
+ "ex:value": {
1355
+ "@id": {
1356
+ "@id": {
1357
+ "@id": "ex:rei",
1358
+ "ex:prop": "value3"
1359
+ },
1360
+ "ex:prop": "value"
1361
+ },
1362
+ "ex:prop": "value2"
1363
+ }
1364
+ }),
1365
+ expected: %(
1366
+ <ex:subj> <ex:value> <<<<<ex:rei> <ex:prop> "value3">> <ex:prop> "value">> .
1367
+ <<<<<ex:rei> <ex:prop> "value3">> <ex:prop> "value">> <ex:prop> "value2" .
1368
+ ),
1369
+ },
1370
+ }.each do |title, params|
1371
+ context(title) do
1372
+ it "Generates statements" do
1373
+ output_graph = RDF::Graph.new {|g| g << RDF::NTriples::Reader.new(params[:expected], rdfstar: true)}
1374
+ run_to_rdf params.merge(rdfstar: true, output: output_graph)
1375
+ end if params[:expected]
1376
+
1377
+ it "Exception" do
1378
+ run_to_rdf params.merge(rdfstar: true)
1379
+ end if params[:exception]
1380
+ end
1381
+ end
1382
+ end
1383
+
1178
1384
  context "exceptions" do
1179
1385
  {
1180
1386
  "Invalid subject" => {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-ld
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.5
4
+ version: 3.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg Kellogg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-15 00:00:00.000000000 Z
11
+ date: 2020-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdf
@@ -426,85 +426,85 @@ required_rubygems_version: !ruby/object:Gem::Requirement
426
426
  - !ruby/object:Gem::Version
427
427
  version: '0'
428
428
  requirements: []
429
- rubygems_version: 3.1.4
429
+ rubygems_version: 3.2.0.rc.2
430
430
  signing_key:
431
431
  specification_version: 4
432
432
  summary: JSON-LD reader/writer for Ruby.
433
433
  test_files:
434
- - spec/spec_helper.rb
435
- - spec/matchers.rb
436
434
  - spec/api_spec.rb
437
- - spec/suite_from_rdf_spec.rb
435
+ - spec/compact_spec.rb
436
+ - spec/conneg_spec.rb
438
437
  - spec/context_spec.rb
438
+ - spec/expand_spec.rb
439
+ - spec/flatten_spec.rb
440
+ - spec/format_spec.rb
441
+ - spec/frame_spec.rb
439
442
  - spec/from_rdf_spec.rb
440
- - spec/suite_helper.rb
443
+ - spec/matchers.rb
441
444
  - spec/reader_spec.rb
442
- - spec/streaming_writer_spec.rb
443
445
  - spec/resource_spec.rb
444
- - spec/suite_to_rdf_spec.rb
445
- - spec/suite_remote_doc_spec.rb
446
- - spec/format_spec.rb
447
- - spec/frame_spec.rb
448
- - spec/to_rdf_spec.rb
449
- - spec/conneg_spec.rb
450
- - spec/support/extensions.rb
451
- - spec/suite_html_spec.rb
452
- - spec/flatten_spec.rb
453
- - spec/suite_frame_spec.rb
446
+ - spec/spec_helper.rb
447
+ - spec/streaming_reader_spec.rb
448
+ - spec/streaming_writer_spec.rb
454
449
  - spec/suite_compact_spec.rb
455
- - spec/expand_spec.rb
456
450
  - spec/suite_expand_spec.rb
457
451
  - spec/suite_flatten_spec.rb
458
- - spec/compact_spec.rb
452
+ - spec/suite_frame_spec.rb
453
+ - spec/suite_from_rdf_spec.rb
454
+ - spec/suite_helper.rb
455
+ - spec/suite_html_spec.rb
459
456
  - spec/suite_http_spec.rb
460
- - spec/streaming_reader_spec.rb
457
+ - spec/suite_remote_doc_spec.rb
458
+ - spec/suite_to_rdf_spec.rb
459
+ - spec/support/extensions.rb
460
+ - spec/to_rdf_spec.rb
461
461
  - spec/writer_spec.rb
462
- - spec/test-files/test-3-compacted.json
463
- - spec/test-files/test-2-rdf.ttl
464
- - spec/test-files/test-4-compacted.json
465
- - spec/test-files/test-5-input.json
466
- - spec/test-files/test-3-rdf.ttl
467
- - spec/test-files/test-5-compacted.json
468
- - spec/test-files/test-9-expanded.json
469
- - spec/test-files/test-8-input.json
470
- - spec/test-files/test-2-compacted.json
471
- - spec/test-files/test-8-compacted.json
472
- - spec/test-files/test-7-expanded.json
473
- - spec/test-files/test-1-rdf.ttl
474
- - spec/test-files/test-7-context.json
475
- - spec/test-files/test-3-expanded.json
476
- - spec/test-files/test-8-rdf.ttl
462
+ - spec/test-files/test-1-compacted.json
463
+ - spec/test-files/test-1-context.json
464
+ - spec/test-files/test-1-expanded.json
465
+ - spec/test-files/test-1-input.json
477
466
  - spec/test-files/test-1-normalized.json
467
+ - spec/test-files/test-1-rdf.ttl
468
+ - spec/test-files/test-2-compacted.json
469
+ - spec/test-files/test-2-context.json
470
+ - spec/test-files/test-2-expanded.json
478
471
  - spec/test-files/test-2-input.json
479
- - spec/test-files/test-7-compacted.json
480
- - spec/test-files/test-1-context.json
481
- - spec/test-files/test-6-context.json
482
- - spec/test-files/test-6-compacted.json
483
- - spec/test-files/test-7-input.json
484
- - spec/test-files/test-1-compacted.json
485
- - spec/test-files/test-4-expanded.json
486
- - spec/test-files/test-9-compacted.json
472
+ - spec/test-files/test-2-normalized.json
473
+ - spec/test-files/test-2-rdf.ttl
474
+ - spec/test-files/test-3-compacted.json
475
+ - spec/test-files/test-3-context.json
476
+ - spec/test-files/test-3-expanded.json
477
+ - spec/test-files/test-3-input.json
478
+ - spec/test-files/test-3-normalized.json
479
+ - spec/test-files/test-3-rdf.ttl
480
+ - spec/test-files/test-4-compacted.json
487
481
  - spec/test-files/test-4-context.json
482
+ - spec/test-files/test-4-expanded.json
488
483
  - spec/test-files/test-4-input.json
489
- - spec/test-files/test-3-context.json
490
- - spec/test-files/test-5-rdf.ttl
491
- - spec/test-files/test-1-expanded.json
492
- - spec/test-files/test-9-input.json
493
- - spec/test-files/test-8-framed.json
484
+ - spec/test-files/test-4-rdf.ttl
485
+ - spec/test-files/test-5-compacted.json
486
+ - spec/test-files/test-5-context.json
494
487
  - spec/test-files/test-5-expanded.json
495
- - spec/test-files/test-3-normalized.json
496
- - spec/test-files/test-2-expanded.json
488
+ - spec/test-files/test-5-input.json
489
+ - spec/test-files/test-5-rdf.ttl
490
+ - spec/test-files/test-6-compacted.json
491
+ - spec/test-files/test-6-context.json
497
492
  - spec/test-files/test-6-expanded.json
498
- - spec/test-files/test-2-context.json
499
- - spec/test-files/test-5-context.json
500
- - spec/test-files/test-1-input.json
501
- - spec/test-files/test-4-rdf.ttl
502
- - spec/test-files/test-8-frame.json
503
493
  - spec/test-files/test-6-input.json
504
- - spec/test-files/test-8-context.json
505
- - spec/test-files/test-8-expanded.json
506
494
  - spec/test-files/test-6-rdf.ttl
495
+ - spec/test-files/test-7-compacted.json
496
+ - spec/test-files/test-7-context.json
497
+ - spec/test-files/test-7-expanded.json
498
+ - spec/test-files/test-7-input.json
507
499
  - spec/test-files/test-7-rdf.ttl
508
- - spec/test-files/test-3-input.json
509
- - spec/test-files/test-2-normalized.json
500
+ - spec/test-files/test-8-compacted.json
501
+ - spec/test-files/test-8-context.json
502
+ - spec/test-files/test-8-expanded.json
503
+ - spec/test-files/test-8-frame.json
504
+ - spec/test-files/test-8-framed.json
505
+ - spec/test-files/test-8-input.json
506
+ - spec/test-files/test-8-rdf.ttl
507
+ - spec/test-files/test-9-compacted.json
510
508
  - spec/test-files/test-9-context.json
509
+ - spec/test-files/test-9-expanded.json
510
+ - spec/test-files/test-9-input.json