json-ld 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,7 @@ module JSON::LD
8
8
  include Utils
9
9
 
10
10
  ##
11
- # @param [Hash{String => Object}] node
11
+ # @param [Hash{String => Object}] item
12
12
  # @param [RDF::Resource] graph_name
13
13
  # @yield statement
14
14
  # @yieldparam [RDF::Statement] statement
@@ -23,6 +23,15 @@ module JSON::LD
23
23
  value.is_a?(Hash) && value.keys == %w(@id)
24
24
  end
25
25
 
26
+ ##
27
+ # Is value a node or a node reference reference?
28
+ # @param [Object] value
29
+ # @return [Boolean]
30
+ def node_or_ref?(value)
31
+ value.is_a?(Hash) &&
32
+ !(value.has_key?('@value') || value.has_key?('@list') || value.has_key?('@set'))
33
+ end
34
+
26
35
  ##
27
36
  # Is value a blank node? Value is a blank node
28
37
  #
@@ -95,7 +104,7 @@ module JSON::LD
95
104
  # @return [Boolean] v1 and v2 are considered equal
96
105
  def compare_values(v1, v2)
97
106
  case
98
- when node?(v1) && node?(v2) then v1['@id'] && v1['@id'] == v2['@id']
107
+ when node_or_ref?(v1) && node_or_ref?(v2) then v1['@id'] && v1['@id'] == v2['@id']
99
108
  when value?(v1) && value?(v2)
100
109
  v1['@value'] == v2['@value'] &&
101
110
  v1['@type'] == v2['@type'] &&
@@ -37,6 +37,21 @@ describe JSON::LD::Context do
37
37
  end
38
38
  subject {context}
39
39
 
40
+ describe ".parse" do
41
+ let(:ctx) {[
42
+ {"foo" => "http://example.com/foo"},
43
+ {"bar" => "foo"}
44
+ ]}
45
+
46
+ it "merges definitions from each context" do
47
+ ec = described_class.parse(ctx)
48
+ expect(ec.send(:mappings)).to produce({
49
+ "foo" => "http://example.com/foo",
50
+ "bar" => "http://example.com/foo"
51
+ }, logger)
52
+ end
53
+ end
54
+
40
55
  describe "#parse" do
41
56
  context "remote" do
42
57
 
@@ -116,15 +131,13 @@ describe JSON::LD::Context do
116
131
  end
117
132
 
118
133
  context "Array" do
119
- before(:all) do
120
- @ctx = [
121
- {"foo" => "http://example.com/foo"},
122
- {"bar" => "foo"}
123
- ]
124
- end
134
+ let(:ctx) {[
135
+ {"foo" => "http://example.com/foo"},
136
+ {"bar" => "foo"}
137
+ ]}
125
138
 
126
139
  it "merges definitions from each context" do
127
- ec = subject.parse(@ctx)
140
+ ec = subject.parse(ctx)
128
141
  expect(ec.send(:mappings)).to produce({
129
142
  "foo" => "http://example.com/foo",
130
143
  "bar" => "http://example.com/foo"
@@ -43,6 +43,25 @@ describe JSON::LD::API do
43
43
  {"http://example.com/bar" => [{"@value" => "bar"}]}
44
44
  ]
45
45
  },
46
+ "@graph value (expands to array form)" => {
47
+ input: {
48
+ "@context" => {"ex" => "http://example.com/"},
49
+ "ex:p" => {
50
+ "@id" => "ex:Sub1",
51
+ "@graph" => {
52
+ "ex:q" => "foo"
53
+ }
54
+ }
55
+ },
56
+ output: [{
57
+ "http://example.com/p" => [{
58
+ "@id" => "http://example.com/Sub1",
59
+ "@graph" => [{
60
+ "http://example.com/q" => [{"@value" => "foo"}],
61
+ }]
62
+ }]
63
+ }]
64
+ },
46
65
  "@type with CURIE" => {
47
66
  input: {
48
67
  "@context" => {"ex" => "http://example.com/"},
@@ -78,25 +78,25 @@ describe JSON::LD::Format do
78
78
  expect {RDF::CLI.exec(["expand", ttl], format: :ttl)}.to write.to(:output)
79
79
  end
80
80
  it "expands JSON" do
81
- expect {RDF::CLI.exec(["expand", json], format: :jsonld)}.to write.to(:output)
81
+ expect {RDF::CLI.exec(["expand", json], format: :jsonld, validate: false)}.to write.to(:output)
82
82
  end
83
83
  end
84
84
 
85
85
  describe "#compact" do
86
86
  it "compacts RDF" do
87
- expect {RDF::CLI.exec(["compact", ttl], context: context, format: :ttl)}.to write.to(:output)
87
+ expect {RDF::CLI.exec(["compact", ttl], context: context, format: :ttl, validate: false)}.to write.to(:output)
88
88
  end
89
89
  it "compacts JSON" do
90
- expect {RDF::CLI.exec(["compact", json], context: context, format: :jsonld)}.to write.to(:output)
90
+ expect {RDF::CLI.exec(["compact", json], context: context, format: :jsonld, validate: false)}.to write.to(:output)
91
91
  end
92
92
  end
93
93
 
94
94
  describe "#flatten" do
95
95
  it "flattens RDF" do
96
- expect {RDF::CLI.exec(["flatten", ttl], context: context, format: :ttl)}.to write.to(:output)
96
+ expect {RDF::CLI.exec(["flatten", ttl], context: context, format: :ttl, validate: false)}.to write.to(:output)
97
97
  end
98
98
  it "flattens JSON" do
99
- expect {RDF::CLI.exec(["flatten", json], context: context, format: :jsonld)}.to write.to(:output)
99
+ expect {RDF::CLI.exec(["flatten", json], context: context, format: :jsonld, validate: false)}.to write.to(:output)
100
100
  end
101
101
  end
102
102
 
@@ -105,7 +105,7 @@ describe JSON::LD::Format do
105
105
  expect {RDF::CLI.exec(["frame", ttl], frame: context, format: :ttl)}.to write.to(:output)
106
106
  end
107
107
  it "frames JSON" do
108
- expect {RDF::CLI.exec(["frame", json], frame: context, format: :jsonld)}.to write.to(:output)
108
+ expect {RDF::CLI.exec(["frame", json], frame: context, format: :jsonld, validate: false)}.to write.to(:output)
109
109
  end
110
110
  end
111
111
  end
@@ -7,592 +7,818 @@ describe JSON::LD::API do
7
7
 
8
8
  describe ".frame" do
9
9
  {
10
- "frame with @type matches subject with @type" => {
11
- frame: {
12
- "@context" => {"ex" => "http://example.org/"},
13
- "@type" => "ex:Type1"
14
- },
15
- input: [
16
- {
17
- "@context" => {"ex" => "http://example.org/"},
18
- "@id" => "ex:Sub1",
19
- "@type" => "ex:Type1"
20
- },
10
+ "exact @type match" => {
11
+ frame: %({
12
+ "@context": {"ex": "http://example.org/"},
13
+ "@type": "ex:Type1"
14
+ }),
15
+ input: %([
21
16
  {
22
- "@context" => {"ex" => "http://example.org/"},
23
- "@id" => "ex:Sub2",
24
- "@type" => "ex:Type2"
25
- },
26
- ],
27
- output: {
28
- "@context" => {"ex" => "http://example.org/"},
29
- "@graph" => [{
30
- "@id" => "ex:Sub1",
31
- "@type" => "ex:Type1"
17
+ "@context": {"ex": "http://example.org/"},
18
+ "@id": "ex:Sub1",
19
+ "@type": "ex:Type1"
20
+ }, {
21
+ "@context": { "ex":"http://example.org/"},
22
+ "@id": "ex:Sub2",
23
+ "@type": "ex:Type2"
24
+ }
25
+ ]),
26
+ output: %({
27
+ "@context": {"ex": "http://example.org/"},
28
+ "@graph": [{
29
+ "@id": "ex:Sub1",
30
+ "@type": "ex:Type1"
32
31
  }]
33
- }
32
+ })
34
33
  },
35
- "implicitly includes unframed properties" => {
36
- frame: {
37
- "@context" => {"ex" => "http://example.org/"},
38
- "@type" => "ex:Type1"
39
- },
40
- input: [
34
+ "wildcard @type match" => {
35
+ frame: %({
36
+ "@context": {"ex": "http://example.org/"},
37
+ "@type": {}
38
+ }),
39
+ input: %([
41
40
  {
42
- "@context" => {"ex" => "http://example.org/"},
43
- "@id" => "ex:Sub1",
44
- "@type" => "ex:Type1",
45
- "ex:prop1" => "Property 1",
46
- "ex:prop2" => {"@id" => "ex:Obj1"}
41
+ "@context": {"ex": "http://example.org/"},
42
+ "@id": "ex:Sub1",
43
+ "@type": "ex:Type1"
44
+ }, {
45
+ "@context": { "ex":"http://example.org/"},
46
+ "@id": "ex:Sub2",
47
+ "@type": "ex:Type2"
47
48
  }
48
- ],
49
- output: {
50
- "@context" => {"ex" => "http://example.org/"},
51
- "@graph" => [{
52
- "@id" => "ex:Sub1",
53
- "@type" => "ex:Type1",
54
- "ex:prop1" => "Property 1",
55
- "ex:prop2" => {"@id" => "ex:Obj1"}
49
+ ]),
50
+ output: %({
51
+ "@context": {"ex": "http://example.org/"},
52
+ "@graph": [{
53
+ "@id": "ex:Sub1",
54
+ "@type": "ex:Type1"
55
+ }, {
56
+ "@id": "ex:Sub2",
57
+ "@type": "ex:Type2"
56
58
  }]
57
- }
59
+ })
58
60
  },
59
- "explicitly excludes unframed properties" => {
60
- frame: {
61
- "@context" => {"ex" => "http://example.org/"},
62
- "@explicit" => true,
63
- "@type" => "ex:Type1"
64
- },
65
- input: [
61
+ "match none @type match" => {
62
+ frame: %({
63
+ "@context": {"ex": "http://example.org/"},
64
+ "@type": []
65
+ }),
66
+ input: %([
66
67
  {
67
- "@context" => {"ex" => "http://example.org/"},
68
- "@id" => "ex:Sub1",
69
- "@type" => "ex:Type1",
70
- "ex:prop1" => "Property 1",
71
- "ex:prop2" => {"@id" => "ex:Obj1"}
68
+ "@context": {"ex": "http://example.org/"},
69
+ "@id": "ex:Sub1",
70
+ "@type": "ex:Type1",
71
+ "ex:p": "Foo"
72
+ }, {
73
+ "@context": { "ex":"http://example.org/"},
74
+ "@id": "ex:Sub2",
75
+ "ex:p": "Bar"
72
76
  }
73
- ],
74
- output: {
75
- "@context" => {"ex" => "http://example.org/"},
76
- "@graph" => [{
77
- "@id" => "ex:Sub1",
78
- "@type" => "ex:Type1"
77
+ ]),
78
+ output: %({
79
+ "@context": {"ex": "http://example.org/"},
80
+ "@graph": [{
81
+ "@id": "ex:Sub2",
82
+ "ex:p": "Bar"
79
83
  }]
80
- }
84
+ })
81
85
  },
82
- "explicitly includes unframed properties" => {
83
- frame: {
84
- "@context" => {"ex" => "http://example.org/"},
85
- "@explicit" => false,
86
- "@type" => "ex:Type1"
87
- },
88
- input: [
86
+ "multiple matches on @type" => {
87
+ frame: %({
88
+ "@context": {"ex": "http://example.org/"},
89
+ "@type": "ex:Type1"
90
+ }),
91
+ input: %([{
92
+ "@context": {"ex": "http://example.org/"},
93
+ "@id": "ex:Sub1",
94
+ "@type": "ex:Type1"
95
+ }, {
96
+ "@context": {"ex": "http://example.org/"},
97
+ "@id": "ex:Sub2",
98
+ "@type": "ex:Type1"
99
+ }, {
100
+ "@context": {"ex": "http://example.org/"},
101
+ "@id": "ex:Sub3",
102
+ "@type": ["ex:Type1", "ex:Type2"]
103
+ }]),
104
+ output: %({
105
+ "@context": {"ex": "http://example.org/"},
106
+ "@graph": [{
107
+ "@id": "ex:Sub1",
108
+ "@type": "ex:Type1"
109
+ }, {
110
+ "@id": "ex:Sub2",
111
+ "@type": "ex:Type1"
112
+ }, {
113
+ "@id": "ex:Sub3",
114
+ "@type": ["ex:Type1", "ex:Type2"]
115
+ }]
116
+ })
117
+ },
118
+ "single @id match" => {
119
+ frame: %({
120
+ "@context": {"ex": "http://example.org/"},
121
+ "@id": "ex:Sub1"
122
+ }),
123
+ input: %([
89
124
  {
90
- "@context" => {"ex" => "http://example.org/"},
91
- "@id" => "ex:Sub1",
92
- "@type" => "ex:Type1",
93
- "ex:prop1" => "Property 1",
94
- "ex:prop2" => {"@id" => "ex:Obj1"}
125
+ "@context": {"ex": "http://example.org/"},
126
+ "@id": "ex:Sub1",
127
+ "@type": "ex:Type1"
128
+ }, {
129
+ "@context": { "ex":"http://example.org/"},
130
+ "@id": "ex:Sub2",
131
+ "@type": "ex:Type2"
95
132
  }
96
- ],
97
- output: {
98
- "@context" => {"ex" => "http://example.org/"},
99
- "@graph" => [{
100
- "@id" => "ex:Sub1",
101
- "@type" => "ex:Type1",
102
- "ex:prop1" => "Property 1",
103
- "ex:prop2" => {"@id" => "ex:Obj1"}
133
+ ]),
134
+ output: %({
135
+ "@context": {"ex": "http://example.org/"},
136
+ "@graph": [{
137
+ "@id": "ex:Sub1",
138
+ "@type": "ex:Type1"
104
139
  }]
105
- }
140
+ })
106
141
  },
107
- "frame without @type matches only subjects containing listed properties (duck typing)" => {
108
- frame: {
109
- "@context" => {"ex" => "http://example.org/"},
110
- "ex:prop1" => {},
111
- "ex:prop2" => {}
112
- },
113
- input: [
114
- {
115
- "@context" => {"ex" => "http://example.org/"},
116
- "@id" => "ex:Sub1",
117
- "ex:prop1" => "Property 1"
118
- },
119
- {
120
- "@context" => {"ex" => "http://example.org/"},
121
- "@id" => "ex:Sub2",
122
- "ex:prop2" => "Property 2"
123
- },
142
+ "multiple @id match" => {
143
+ frame: %({
144
+ "@context": {"ex": "http://example.org/"},
145
+ "@id": ["ex:Sub1", "ex:Sub2"]
146
+ }),
147
+ input: %([
124
148
  {
125
- "@context" => {"ex" => "http://example.org/"},
126
- "@id" => "ex:Sub3",
127
- "ex:prop1" => "Property 1",
128
- "ex:prop2" => "Property 2"
129
- },
130
- ],
131
- output: {
132
- "@context" => {"ex" => "http://example.org/"},
133
- "@graph" => [{
134
- "@id" => "ex:Sub3",
135
- "ex:prop1" => "Property 1",
136
- "ex:prop2" => "Property 2"
149
+ "@context": {"ex": "http://example.org/"},
150
+ "@id": "ex:Sub1",
151
+ "@type": "ex:Type1"
152
+ }, {
153
+ "@context": { "ex":"http://example.org/"},
154
+ "@id": "ex:Sub2",
155
+ "@type": "ex:Type2"
156
+ }, {
157
+ "@context": { "ex":"http://example.org/"},
158
+ "@id": "ex:Sub3",
159
+ "@type": "ex:Type3"
160
+ }
161
+ ]),
162
+ output: %({
163
+ "@context": {"ex": "http://example.org/"},
164
+ "@graph": [{
165
+ "@id": "ex:Sub1",
166
+ "@type": "ex:Type1"
167
+ }, {
168
+ "@id": "ex:Sub2",
169
+ "@type": "ex:Type2"
137
170
  }]
138
- }
171
+ })
139
172
  },
140
- "embed matched frames" => {
141
- frame: {
142
- "@context" => {"ex" => "http://example.org/"},
143
- "@type" => "ex:Type1",
144
- "ex:includes" => {
145
- "@type" => "ex:Type2"
146
- }
147
- },
148
- input: [
149
- {
150
- "@context" => {"ex" => "http://example.org/"},
151
- "@id" => "ex:Sub1",
152
- "@type" => "ex:Type1",
153
- "ex:includes" => {"@id" => "ex:Sub2"}
154
- },
173
+ "wildcard and match none" => {
174
+ frame: %({
175
+ "@context": {"ex": "http://example.org/"},
176
+ "ex:p": [],
177
+ "ex:q": {}
178
+ }),
179
+ input: %([
155
180
  {
156
- "@context" => {"ex" => "http://example.org/"},
157
- "@id" => "ex:Sub2",
158
- "@type" => "ex:Type2",
159
- "ex:includes" => {"@id" => "ex:Sub1"}
160
- },
161
- ],
162
- output:{
163
- "@context" => {"ex" => "http://example.org/"},
164
- "@graph" => [{
165
- "@id" => "ex:Sub1",
166
- "@type" => "ex:Type1",
167
- "ex:includes" => {
168
- "@id" => "ex:Sub2",
169
- "@type" => "ex:Type2",
170
- "ex:includes" => {"@id" => "ex:Sub1"}
171
- }
181
+ "@context": {"ex": "http://example.org/"},
182
+ "@id": "ex:Sub1",
183
+ "ex:q": "bar"
184
+ }, {
185
+ "@context": { "ex":"http://example.org/"},
186
+ "@id": "ex:Sub2",
187
+ "ex:p": "foo",
188
+ "ex:q": "bar"
189
+ }
190
+ ]),
191
+ output: %({
192
+ "@context": {"ex": "http://example.org/"},
193
+ "@graph": [{
194
+ "@id": "ex:Sub1",
195
+ "ex:p": null,
196
+ "ex:q": "bar"
172
197
  }]
173
- }
198
+ })
174
199
  },
175
- "multiple matches" => {
176
- frame: {
177
- "@context" => {"ex" => "http://example.org/"},
178
- "@type" => "ex:Type1"
179
- },
180
- input: [
181
- {
182
- "@context" => {"ex" => "http://example.org/"},
183
- "@id" => "ex:Sub1",
184
- "@type" => "ex:Type1"
185
- },
200
+ "match on any property if @requireAll is false" => {
201
+ frame: %({
202
+ "@context": {"ex": "http://example.org/"},
203
+ "@requireAll": false,
204
+ "ex:p": {},
205
+ "ex:q": {}
206
+ }),
207
+ input: %([
186
208
  {
187
- "@context" => {"ex" => "http://example.org/"},
188
- "@id" => "ex:Sub2",
189
- "@type" => "ex:Type1"
190
- },
191
- ],
192
- output: {
193
- "@context" => {"ex" => "http://example.org/"},
194
- "@graph" => [
195
- {
196
- "@id" => "ex:Sub1",
197
- "@type" => "ex:Type1"
198
- },
199
- {
200
- "@id" => "ex:Sub2",
201
- "@type" => "ex:Type1"
202
- }
203
- ]
209
+ "@context": {"ex": "http://example.org/"},
210
+ "@id": "ex:Sub1",
211
+ "ex:p": "foo"
212
+ }, {
213
+ "@context": { "ex":"http://example.org/"},
214
+ "@id": "ex:Sub2",
215
+ "ex:q": "bar"
204
216
  }
217
+ ]),
218
+ output: %({
219
+ "@context": {"ex": "http://example.org/"},
220
+ "@graph": [{
221
+ "@id": "ex:Sub1",
222
+ "ex:p": "foo",
223
+ "ex:q": null
224
+ }, {
225
+ "@id": "ex:Sub2",
226
+ "ex:p": null,
227
+ "ex:q": "bar"
228
+ }]
229
+ })
205
230
  },
206
- "non-existent framed properties create null property" => {
207
- frame: {
208
- "@context" => {"ex" => "http://example.org/"},
209
- "@type" => "ex:Type1",
210
- "ex:null" => []
211
- },
212
- input: [
231
+ "match on defeaults if @requireAll is true and at least one property matches" => {
232
+ frame: %({
233
+ "@context": {"ex": "http://example.org/"},
234
+ "@requireAll": true,
235
+ "ex:p": {"@default": "Foo"},
236
+ "ex:q": {"@default": "Bar"}
237
+ }),
238
+ input: %([
213
239
  {
214
- "@context" => {"ex" => "http://example.org/"},
215
- "@id" => "ex:Sub1",
216
- "@type" => "ex:Type1",
217
- "ex:prop1" => "Property 1",
218
- "ex:prop2" => {"@id" => "ex:Obj1"}
240
+ "@context": {"ex": "http://example.org/"},
241
+ "@id": "ex:Sub1",
242
+ "ex:p": "foo"
243
+ }, {
244
+ "@context": { "ex":"http://example.org/"},
245
+ "@id": "ex:Sub2",
246
+ "ex:q": "bar"
247
+ }, {
248
+ "@context": { "ex":"http://example.org/"},
249
+ "@id": "ex:Sub3",
250
+ "ex:p": "foo",
251
+ "ex:q": "bar"
252
+ }, {
253
+ "@context": { "ex":"http://example.org/"},
254
+ "@id": "ex:Sub4",
255
+ "ex:r": "baz"
219
256
  }
220
- ],
221
- output: {
222
- "@context" => {"ex" => "http://example.org/"},
223
- "@graph" => [{
224
- "@id" => "ex:Sub1",
225
- "@type" => "ex:Type1",
226
- "ex:prop1" => "Property 1",
227
- "ex:prop2" => {"@id" => "ex:Obj1"},
228
- "ex:null" => nil
257
+ ]),
258
+ output: %({
259
+ "@context": {"ex": "http://example.org/"},
260
+ "@graph": [{
261
+ "@id": "ex:Sub1",
262
+ "ex:p": "foo",
263
+ "ex:q": "Bar"
264
+ }, {
265
+ "@id": "ex:Sub2",
266
+ "ex:p": "Foo",
267
+ "ex:q": "bar"
268
+ }, {
269
+ "@id": "ex:Sub3",
270
+ "ex:p": "foo",
271
+ "ex:q": "bar"
229
272
  }]
230
- }
273
+ })
231
274
  },
232
- "non-existent framed properties create default property" => {
233
- frame: {
234
- "@context" => {"ex" => "http://example.org/", "ex:null" => {"@container" => "@set"}},
235
- "@type" => "ex:Type1",
236
- "ex:null" => [{"@default" => "foo"}]
237
- },
238
- input: [
275
+ "match with @requireAll with one default" => {
276
+ frame: %({
277
+ "@context": {"ex": "http://example.org/"},
278
+ "@requireAll": true,
279
+ "ex:p": {},
280
+ "ex:q": {"@default": "Bar"}
281
+ }),
282
+ input: %([
239
283
  {
240
- "@context" => {"ex" => "http://example.org/"},
241
- "@id" => "ex:Sub1",
242
- "@type" => "ex:Type1",
243
- "ex:prop1" => "Property 1",
244
- "ex:prop2" => {"@id" => "ex:Obj1"}
284
+ "@context": {"ex": "http://example.org/"},
285
+ "@id": "ex:Sub1",
286
+ "ex:p": "foo"
287
+ }, {
288
+ "@context": { "ex":"http://example.org/"},
289
+ "@id": "ex:Sub2",
290
+ "ex:q": "bar"
291
+ }, {
292
+ "@context": { "ex":"http://example.org/"},
293
+ "@id": "ex:Sub3",
294
+ "ex:p": "foo",
295
+ "ex:q": "bar"
245
296
  }
246
- ],
247
- output: {
248
- "@context" => {"ex" => "http://example.org/", "ex:null" => {"@container" => "@set"}},
249
- "@graph" => [{
250
- "@id" => "ex:Sub1",
251
- "@type" => "ex:Type1",
252
- "ex:prop1" => "Property 1",
253
- "ex:prop2" => {"@id" => "ex:Obj1"},
254
- "ex:null" => ["foo"]
297
+ ]),
298
+ output: %({
299
+ "@context": {"ex": "http://example.org/"},
300
+ "@graph": [{
301
+ "@id": "ex:Sub1",
302
+ "ex:p": "foo",
303
+ "ex:q": "Bar"
304
+ }, {
305
+ "@id": "ex:Sub3",
306
+ "ex:p": "foo",
307
+ "ex:q": "bar"
308
+ }]
309
+ })
310
+ },
311
+ "implicitly includes unframed properties (default @explicit false)" => {
312
+ frame: %({
313
+ "@context": {"ex": "http://example.org/"},
314
+ "@type": "ex:Type1"
315
+ }),
316
+ input: %q({
317
+ "@context": {"ex": "http://example.org/"},
318
+ "@id": "ex:Sub1",
319
+ "@type": "ex:Type1",
320
+ "ex:prop1": "Property 1",
321
+ "ex:prop2": {"@id": "ex:Obj1"}
322
+ }),
323
+ output: %({
324
+ "@context": {"ex": "http://example.org/"},
325
+ "@graph": [{
326
+ "@id": "ex:Sub1",
327
+ "@type": "ex:Type1",
328
+ "ex:prop1": "Property 1",
329
+ "ex:prop2": {"@id": "ex:Obj1"}
330
+ }]
331
+ })
332
+ },
333
+ "explicitly includes unframed properties @explicit false" => {
334
+ frame: %({
335
+ "@context": {"ex": "http://example.org/"},
336
+ "@explicit": false,
337
+ "@type": "ex:Type1"
338
+ }),
339
+ input: %q({
340
+ "@context": {"ex": "http://example.org/"},
341
+ "@id": "ex:Sub1",
342
+ "@type": "ex:Type1",
343
+ "ex:prop1": "Property 1",
344
+ "ex:prop2": {"@id": "ex:Obj1"}
345
+ }),
346
+ output: %({
347
+ "@context": {"ex": "http://example.org/"},
348
+ "@graph": [{
349
+ "@id": "ex:Sub1",
350
+ "@type": "ex:Type1",
351
+ "ex:prop1": "Property 1",
352
+ "ex:prop2": {"@id": "ex:Obj1"}
353
+ }]
354
+ })
355
+ },
356
+ "explicitly excludes unframed properties (@explicit: true)" => {
357
+ frame: %({
358
+ "@context": {"ex": "http://example.org/"},
359
+ "@explicit": true,
360
+ "@type": "ex:Type1"
361
+ }),
362
+ input: %({
363
+ "@context": {"ex": "http://example.org/"},
364
+ "@id": "ex:Sub1",
365
+ "@type": "ex:Type1",
366
+ "ex:prop1": "Property 1",
367
+ "ex:prop2": {"@id": "ex:Obj1"}
368
+ }),
369
+ output: %({
370
+ "@context": {"ex": "http://example.org/"},
371
+ "@graph": [{
372
+ "@id": "ex:Sub1",
373
+ "@type": "ex:Type1"
374
+ }]
375
+ })
376
+ },
377
+ "non-existent framed properties create null property" => {
378
+ frame: %({
379
+ "@context": {"ex": "http://example.org/"},
380
+ "@type": "ex:Type1",
381
+ "ex:null": []
382
+ }),
383
+ input: %({
384
+ "@context": {"ex": "http://example.org/"},
385
+ "@id": "ex:Sub1",
386
+ "@type": "ex:Type1",
387
+ "ex:prop1": "Property 1",
388
+ "ex:prop2": {"@id": "ex:Obj1"}
389
+ }),
390
+ output: %({
391
+ "@context": {
392
+ "ex": "http://example.org/"
393
+ },
394
+ "@graph": [{
395
+ "@id": "ex:Sub1",
396
+ "@type": "ex:Type1",
397
+ "ex:prop1": "Property 1",
398
+ "ex:prop2": {
399
+ "@id": "ex:Obj1"
400
+ },
401
+ "ex:null": null
402
+ }]
403
+ })
404
+ },
405
+ "non-existent framed properties create default property" => {
406
+ frame: %({
407
+ "@context": {
408
+ "ex": "http://example.org/",
409
+ "ex:null": {"@container": "@set"}
410
+ },
411
+ "@type": "ex:Type1",
412
+ "ex:null": [{"@default": "foo"}]
413
+ }),
414
+ input: %({
415
+ "@context": {"ex": "http://example.org/"},
416
+ "@id": "ex:Sub1",
417
+ "@type": "ex:Type1",
418
+ "ex:prop1": "Property 1",
419
+ "ex:prop2": {"@id": "ex:Obj1"}
420
+ }),
421
+ output: %({
422
+ "@context": {
423
+ "ex": "http://example.org/",
424
+ "ex:null": {"@container": "@set"}
425
+ },
426
+ "@graph": [{
427
+ "@id": "ex:Sub1",
428
+ "@type": "ex:Type1",
429
+ "ex:prop1": "Property 1",
430
+ "ex:prop2": {"@id": "ex:Obj1"},
431
+ "ex:null": ["foo"]
255
432
  }]
256
433
  }
434
+ )
257
435
  },
258
436
  "mixed content" => {
259
- frame: {
260
- "@context" => {"ex" => "http://example.org/"},
261
- "ex:mixed" => {"@embed" => false}
262
- },
263
- input: [
264
- {
265
- "@context" => {"ex" => "http://example.org/"},
266
- "@id" => "ex:Sub1",
267
- "ex:mixed" => [
268
- {"@id" => "ex:Sub2"},
269
- "literal1"
270
- ]
271
- }
272
- ],
273
- output: {
274
- "@context" => {"ex" => "http://example.org/"},
275
- "@graph" => [{
276
- "@id" => "ex:Sub1",
277
- "ex:mixed" => [
278
- {"@id" => "ex:Sub2"},
437
+ frame: %({
438
+ "@context": {"ex": "http://example.org/"},
439
+ "ex:mixed": {"@embed": false}
440
+ }),
441
+ input: %({
442
+ "@context": {"ex": "http://example.org/"},
443
+ "@id": "ex:Sub1",
444
+ "ex:mixed": [
445
+ {"@id": "ex:Sub2"},
446
+ "literal1"
447
+ ]
448
+ }),
449
+ output: %({
450
+ "@context": {"ex": "http://example.org/"},
451
+ "@graph": [{
452
+ "@id": "ex:Sub1",
453
+ "ex:mixed": [
454
+ {"@id": "ex:Sub2"},
279
455
  "literal1"
280
456
  ]
281
457
  }]
282
- }
458
+ })
283
459
  },
284
- "no embedding" => {
285
- frame: {
286
- "@context" => {"ex" => "http://example.org/"},
287
- "ex:embed" => {"@embed" => false}
288
- },
289
- input: [
290
- {
291
- "@context" => {"ex" => "http://example.org/"},
292
- "@id" => "ex:Sub1",
293
- "ex:embed" => {
294
- "@id" => "ex:Sub2",
295
- "ex:prop" => "property"
296
- }
460
+ "no embedding (@embed: false)" => {
461
+ frame: %({
462
+ "@context": {"ex": "http://example.org/"},
463
+ "ex:embed": {"@embed": false}
464
+ }),
465
+ input: %({
466
+ "@context": {"ex": "http://example.org/"},
467
+ "@id": "ex:Sub1",
468
+ "ex:embed": {
469
+ "@id": "ex:Sub2",
470
+ "ex:prop": "property"
297
471
  }
298
- ],
299
- output: {
300
- "@context" => {"ex" => "http://example.org/"},
301
- "@graph" => [{
302
- "@id" => "ex:Sub1",
303
- "ex:embed" => {"@id" => "ex:Sub2"}
472
+ }),
473
+ output: %({
474
+ "@context": {"ex": "http://example.org/"},
475
+ "@graph": [{
476
+ "@id": "ex:Sub1",
477
+ "ex:embed": {"@id": "ex:Sub2"}
304
478
  }]
305
- }
479
+ })
306
480
  },
307
481
  "mixed list" => {
308
- frame: {
309
- "@context" => {"ex" => "http://example.org/"},
310
- "ex:mixedlist" => {}
311
- },
312
- input: {
313
- "@context" => {"ex" => "http://example.org/"},
314
- "@id" => "ex:Sub1",
315
- "@type" => "ex:Type1",
316
- "ex:mixedlist" => {"@list" => [
317
- {
318
- "@id" => "ex:Sub2",
319
- "@type" => "ex:Type2"
320
- },
321
- "literal1"
322
- ]}
323
- },
324
- output: {
325
- "@context" => {"ex" => "http://example.org/"},
326
- "@graph" => [{
327
- "@id" => "ex:Sub1",
328
- "@type" => "ex:Type1",
329
- "ex:mixedlist" => {"@list" => [
330
- {
331
- "@id" => "ex:Sub2",
332
- "@type" => "ex:Type2"
333
- },
482
+ frame: %({
483
+ "@context": {"ex": "http://example.org/"},
484
+ "ex:mixedlist": {}
485
+ }),
486
+ input: %({
487
+ "@context": {"ex": "http://example.org/"},
488
+ "@id": "ex:Sub1",
489
+ "@type": "ex:Type1",
490
+ "ex:mixedlist": {
491
+ "@list": [
492
+ {"@id": "ex:Sub2", "@type": "ex:Type2"},
334
493
  "literal1"
335
- ]}
494
+ ]
495
+ }
496
+ }),
497
+ output: %({
498
+ "@context": {"ex": "http://example.org/"},
499
+ "@graph": [{
500
+ "@id": "ex:Sub1",
501
+ "@type": "ex:Type1",
502
+ "ex:mixedlist": {
503
+ "@list": [
504
+ {"@id": "ex:Sub2", "@type": "ex:Type2"},
505
+ "literal1"
506
+ ]
507
+ }
336
508
  }]
337
- }
509
+ })
338
510
  },
339
- "presentation example" => {
340
- frame: {
341
- "@context" => {
342
- "primaryTopic" => {"@id" => "http://xmlns.com/foaf/0.1/primaryTopic","@type" => "@id"},
343
- "sameAs" => {"@id" => "http://www.w3.org/2002/07/owl#sameAs","@type" => "@id"},
511
+ "framed list" => {
512
+ frame: %({
513
+ "@context": {
514
+ "ex": "http://example.org/",
515
+ "list": {"@id": "ex:list", "@container": "@list"}
344
516
  },
345
- "primaryTopic" => {
346
- "@type" => "http://dbpedia.org/class/yago/Buzzwords",
347
- "sameAs" => {}
348
- }
349
- },
350
- input: [
351
- {
352
- "@id" => "http://en.wikipedia.org/wiki/Linked_Data",
353
- "http://xmlns.com/foaf/0.1/primaryTopic" => [{"@id" => "http://dbpedia.org/resource/Linked_Data"}]
517
+ "list": [{"@type": "ex:Element"}]
518
+ }),
519
+ input: %({
520
+ "@context": {
521
+ "ex": "http://example.org/",
522
+ "list": {"@id": "ex:list", "@container": "@list"}
354
523
  },
355
- {
356
- "@id" => "http://www4.wiwiss.fu-berlin.de/flickrwrappr/photos/Linked_Data",
357
- "http://www.w3.org/2002/07/owl#sameAs" => [{"@id" => "http://dbpedia.org/resource/Linked_Data"}]
358
- },
359
- {
360
- "@id" => "http://dbpedia.org/resource/Linked_Data",
361
- "@type" => ["http://dbpedia.org/class/yago/Buzzwords"],
362
- "http://www.w3.org/2002/07/owl#sameAs" => [{"@id" => "http://rdf.freebase.com/ns/m/02r2kb1"}]
524
+ "@id": "ex:Sub1",
525
+ "@type": "ex:Type1",
526
+ "list": [
527
+ {"@id": "ex:Sub2", "@type": "ex:Element"},
528
+ "literal1"
529
+ ]
530
+ }),
531
+ output: %({
532
+ "@context": {
533
+ "ex": "http://example.org/",
534
+ "list": {"@id": "ex:list", "@container": "@list"}
363
535
  },
364
- {
365
- "@id" => "http://mpii.de/yago/resource/Linked_Data",
366
- "http://www.w3.org/2002/07/owl#sameAs" => [{"@id" => "http://dbpedia.org/resource/Linked_Data"}]
367
- }
368
- ],
369
- output: {
370
- "@context" => {
371
- "primaryTopic" => {
372
- "@id" => "http://xmlns.com/foaf/0.1/primaryTopic",
373
- "@type" => "@id"
536
+ "@graph": [{
537
+ "@id": "ex:Sub1",
538
+ "@type": "ex:Type1",
539
+ "list": [
540
+ {"@id": "ex:Sub2", "@type": "ex:Element"},
541
+ "literal1"
542
+ ]
543
+ }]
544
+ })
545
+ },
546
+ "presentation example" => {
547
+ frame: %({
548
+ "@context": {
549
+ "primaryTopic": {
550
+ "@id": "http://xmlns.com/foaf/0.1/primaryTopic",
551
+ "@type": "@id"
374
552
  },
375
- "sameAs" => {
376
- "@id" => "http://www.w3.org/2002/07/owl#sameAs",
377
- "@type" => "@id"
553
+ "sameAs": {
554
+ "@id": "http://www.w3.org/2002/07/owl#sameAs",
555
+ "@type": "@id"
378
556
  }
379
557
  },
380
- "@graph" => [
381
- {
382
- "@id" => "http://en.wikipedia.org/wiki/Linked_Data",
383
- "primaryTopic" => {
384
- "@id" => "http://dbpedia.org/resource/Linked_Data",
385
- "@type" => "http://dbpedia.org/class/yago/Buzzwords",
386
- "sameAs" => "http://rdf.freebase.com/ns/m/02r2kb1"
387
- }
388
- }
389
- ]
558
+ "primaryTopic": {
559
+ "@type": "http://dbpedia.org/class/yago/Buzzwords",
560
+ "sameAs": {}
561
+ }
562
+ }),
563
+ input: %([{
564
+ "@id": "http://en.wikipedia.org/wiki/Linked_Data",
565
+ "http://xmlns.com/foaf/0.1/primaryTopic": {"@id": "http://dbpedia.org/resource/Linked_Data"}
566
+ }, {
567
+ "@id": "http://www4.wiwiss.fu-berlin.de/flickrwrappr/photos/Linked_Data",
568
+ "http://www.w3.org/2002/07/owl#sameAs": {"@id": "http://dbpedia.org/resource/Linked_Data"}
569
+ }, {
570
+ "@id": "http://dbpedia.org/resource/Linked_Data",
571
+ "@type": "http://dbpedia.org/class/yago/Buzzwords",
572
+ "http://www.w3.org/2002/07/owl#sameAs": {"@id": "http://rdf.freebase.com/ns/m/02r2kb1"}
573
+ }, {
574
+ "@id": "http://mpii.de/yago/resource/Linked_Data",
575
+ "http://www.w3.org/2002/07/owl#sameAs": {"@id": "http://dbpedia.org/resource/Linked_Data"}
390
576
  }
577
+ ]),
578
+ output: %({
579
+ "@context": {
580
+ "primaryTopic": {"@id": "http://xmlns.com/foaf/0.1/primaryTopic", "@type": "@id"},
581
+ "sameAs": {"@id": "http://www.w3.org/2002/07/owl#sameAs", "@type": "@id"}
582
+ },
583
+ "@graph": [{
584
+ "@id": "http://en.wikipedia.org/wiki/Linked_Data",
585
+ "primaryTopic": {
586
+ "@id": "http://dbpedia.org/resource/Linked_Data",
587
+ "@type": "http://dbpedia.org/class/yago/Buzzwords",
588
+ "sameAs": "http://rdf.freebase.com/ns/m/02r2kb1"
589
+ }
590
+ }]
591
+ })
391
592
  },
392
593
  "microdata manifest" => {
393
- frame: {
394
- "@context" => {
395
- "xsd" => "http://www.w3.org/2001/XMLSchema#",
396
- "rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
397
- "mf" => "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
398
- "mq" => "http://www.w3.org/2001/sw/DataAccess/tests/test-query#",
399
-
400
- "comment" => "rdfs:comment",
401
- "entries" => {"@id" => "mf:entries", "@container" => "@list"},
402
- "name" => "mf:name",
403
- "action" => "mf:action",
404
- "data" => {"@id" => "mq:data", "@type" => "@id"},
405
- "query" => {"@id" => "mq:query", "@type" => "@id"},
406
- "result" => {"@id" => "mf:result", "@type" => "xsd:boolean"}
594
+ frame: %({
595
+ "@context": {
596
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
597
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
598
+ "mf": "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
599
+ "mq": "http://www.w3.org/2001/sw/DataAccess/tests/test-query#",
600
+ "comment": "rdfs:comment",
601
+ "entries": {"@id": "mf:entries", "@container": "@list"},
602
+ "name": "mf:name",
603
+ "action": "mf:action",
604
+ "data": {"@id": "mq:data", "@type": "@id"},
605
+ "query": {"@id": "mq:query", "@type": "@id"},
606
+ "result": {"@id": "mf:result", "@type": "xsd:boolean"}
407
607
  },
408
- "@type" => "mf:Manifest",
409
- "entries" => [{
410
- "@type" => "mf:ManifestEntry",
411
- "action" => {
412
- "@type" => "mq:QueryTest"
608
+ "@type": "mf:Manifest",
609
+ "entries": [{
610
+ "@type": "mf:ManifestEntry",
611
+ "action": {
612
+ "@type": "mq:QueryTest"
413
613
  }
414
614
  }]
415
- },
416
- input: {
417
- "@context" => {
418
- "md" => "http://www.w3.org/ns/md#",
419
- "mf" => "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
420
- "mq" => "http://www.w3.org/2001/sw/DataAccess/tests/test-query#",
421
- "rdfs" => "http://www.w3.org/2000/01/rdf-schema#"
615
+ }),
616
+ input: %q({
617
+ "@context": {
618
+ "md": "http://www.w3.org/ns/md#",
619
+ "mf": "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
620
+ "mq": "http://www.w3.org/2001/sw/DataAccess/tests/test-query#",
621
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#"
422
622
  },
423
- "@graph" => [
424
- {
425
- "@id" => "_:manifest",
426
- "@type" => "mf:Manifest",
427
- "mf:entries" => {"@list" => [
428
- {"@id" => "_:entry"}
429
- ]},
430
- "rdfs:comment" => "Positive processor tests"
431
- },
432
- {
433
- "@id" => "_:entry",
434
- "@type" => "mf:ManifestEntry",
435
- "mf:action" => {"@id" => "_:query"},
436
- "mf:name" => "Test 0001",
437
- "mf:result" => "true",
438
- "rdfs:comment" => "Item with no itemtype and literal itemprop"
439
- },
440
- {
441
- "@id" => "_:query",
442
- "@type" => "mq:QueryTest",
443
- "mq:data" => {
444
- "@id" => "http://www.w3.org/TR/microdata-rdf/tests/0001.html"
623
+ "@graph": [{
624
+ "@id": "_:manifest",
625
+ "@type": "mf:Manifest",
626
+ "mf:entries": {"@list": [{"@id": "_:entry"}]},
627
+ "rdfs:comment": "Positive processor tests"
628
+ }, {
629
+ "@id": "_:entry",
630
+ "@type": "mf:ManifestEntry",
631
+ "mf:action": {"@id": "_:query"},
632
+ "mf:name": "Test 0001",
633
+ "mf:result": "true",
634
+ "rdfs:comment": "Item with no itemtype and literal itemprop"
635
+ }, {
636
+ "@id": "_:query",
637
+ "@type": "mq:QueryTest",
638
+ "mq:data": {"@id": "http://www.w3.org/TR/microdata-rdf/tests/0001.html"},
639
+ "mq:query": {"@id": "http://www.w3.org/TR/microdata-rdf/tests/0001.ttl"}
640
+ }]
641
+ }),
642
+ output: %({
643
+ "@context": {
644
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
645
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
646
+ "mf": "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
647
+ "mq": "http://www.w3.org/2001/sw/DataAccess/tests/test-query#",
648
+ "comment": "rdfs:comment",
649
+ "entries": {"@id": "mf:entries","@container": "@list"},
650
+ "name": "mf:name",
651
+ "action": "mf:action",
652
+ "data": {"@id": "mq:data", "@type": "@id"},
653
+ "query": {"@id": "mq:query", "@type": "@id"},
654
+ "result": {"@id": "mf:result", "@type": "xsd:boolean"}
655
+ },
656
+ "@graph": [{
657
+ "@id": "_:b0",
658
+ "@type": "mf:Manifest",
659
+ "comment": "Positive processor tests",
660
+ "entries": [{
661
+ "@id": "_:b1",
662
+ "@type": "mf:ManifestEntry",
663
+ "action": {
664
+ "@id": "_:b2",
665
+ "@type": "mq:QueryTest",
666
+ "data": "http://www.w3.org/TR/microdata-rdf/tests/0001.html",
667
+ "query": "http://www.w3.org/TR/microdata-rdf/tests/0001.ttl"
445
668
  },
446
- "mq:query" => {
447
- "@id" => "http://www.w3.org/TR/microdata-rdf/tests/0001.ttl"
448
- }
449
- }
450
- ]
451
- },
452
- output: {
453
- "@context" => {
454
- "xsd" => "http://www.w3.org/2001/XMLSchema#",
455
- "rdfs" => "http://www.w3.org/2000/01/rdf-schema#",
456
- "mf" => "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#",
457
- "mq" => "http://www.w3.org/2001/sw/DataAccess/tests/test-query#",
458
- "comment" => "rdfs:comment",
459
- "entries" => {
460
- "@id" => "mf:entries",
461
- "@container" => "@list"
462
- },
463
- "name" => "mf:name",
464
- "action" => "mf:action",
465
- "data" => {
466
- "@id" => "mq:data",
467
- "@type" => "@id"
468
- },
469
- "query" => {
470
- "@id" => "mq:query",
471
- "@type" => "@id"
472
- },
473
- "result" => {
474
- "@id" => "mf:result",
475
- "@type" => "xsd:boolean"
669
+ "comment": "Item with no itemtype and literal itemprop",
670
+ "mf:result": "true",
671
+ "name": "Test 0001"
672
+ }]
673
+ }]
674
+ })
675
+ },
676
+ "library" => {
677
+ frame: %({
678
+ "@context": {
679
+ "dc": "http://purl.org/dc/elements/1.1/",
680
+ "ex": "http://example.org/vocab#",
681
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
682
+ "ex:contains": { "@type": "@id" }
683
+ },
684
+ "@type": "ex:Library",
685
+ "ex:contains": {}
686
+ }),
687
+ input: %({
688
+ "@context": {
689
+ "dc": "http://purl.org/dc/elements/1.1/",
690
+ "ex": "http://example.org/vocab#",
691
+ "xsd": "http://www.w3.org/2001/XMLSchema#"
692
+ },
693
+ "@id": "http://example.org/library",
694
+ "@type": "ex:Library",
695
+ "dc:name": "Library",
696
+ "ex:contains": {
697
+ "@id": "http://example.org/library/the-republic",
698
+ "@type": "ex:Book",
699
+ "dc:creator": "Plato",
700
+ "dc:title": "The Republic",
701
+ "ex:contains": {
702
+ "@id": "http://example.org/library/the-republic#introduction",
703
+ "@type": "ex:Chapter",
704
+ "dc:description": "An introductory chapter on The Republic.",
705
+ "dc:title": "The Introduction"
476
706
  }
707
+ }
708
+ }),
709
+ output: %({
710
+ "@context": {
711
+ "dc": "http://purl.org/dc/elements/1.1/",
712
+ "ex": "http://example.org/vocab#",
713
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
714
+ "ex:contains": { "@type": "@id" }
477
715
  },
478
- "@graph" => [
716
+ "@graph": [
479
717
  {
480
- "@id" => "_:b0",
481
- "@type" => "mf:Manifest",
482
- "comment" => "Positive processor tests",
483
- "entries" => [
484
- {
485
- "@id" => "_:b1",
486
- "@type" => "mf:ManifestEntry",
487
- "action" => {
488
- "@id" => "_:b2",
489
- "@type" => "mq:QueryTest",
490
- "data" => "http://www.w3.org/TR/microdata-rdf/tests/0001.html",
491
- "query" => "http://www.w3.org/TR/microdata-rdf/tests/0001.ttl"
492
- },
493
- "comment" => "Item with no itemtype and literal itemprop",
494
- "mf:result" => "true",
495
- "name" => "Test 0001"
718
+ "@id": "http://example.org/library",
719
+ "@type": "ex:Library",
720
+ "dc:name": "Library",
721
+ "ex:contains": {
722
+ "@id": "http://example.org/library/the-republic",
723
+ "@type": "ex:Book",
724
+ "dc:creator": "Plato",
725
+ "dc:title": "The Republic",
726
+ "ex:contains": {
727
+ "@id": "http://example.org/library/the-republic#introduction",
728
+ "@type": "ex:Chapter",
729
+ "dc:description": "An introductory chapter on The Republic.",
730
+ "dc:title": "The Introduction"
496
731
  }
497
- ]
732
+ }
498
733
  }
499
734
  ]
500
- }
735
+ })
501
736
  }
502
737
  }.each do |title, params|
503
738
  it title do
504
- begin
505
- jld = JSON::LD::API.frame(params[:input], params[:frame], logger: logger)
506
- expect(jld).to produce(params[:output], logger)
507
- rescue JSON::LD::JsonLdError, JSON::LD::JsonLdError, JSON::LD::InvalidFrame => e
508
- fail("#{e.class}: #{e.message}\n" +
509
- "#{logger}\n" +
510
- "Backtrace:\n#{e.backtrace.join("\n")}")
511
- end
739
+ do_frame(params)
512
740
  end
513
741
  end
514
742
 
515
743
  describe "@reverse" do
516
744
  {
517
745
  "embed matched frames with @reverse" => {
518
- frame: {
519
- "@context" => {"ex" => "http://example.org/"},
520
- "@type" => "ex:Type1",
521
- "@reverse" => {
522
- "ex:includes" => {}
523
- }
524
- },
525
- input: [
526
- {
527
- "@context" => {"ex" => "http://example.org/"},
528
- "@id" => "ex:Sub1",
529
- "@type" => "ex:Type1"
530
- },
531
- {
532
- "@context" => {"ex" => "http://example.org/"},
533
- "@id" => "ex:Sub2",
534
- "@type" => "ex:Type2",
535
- "ex:includes" => {"@id" => "ex:Sub1"}
536
- },
537
- ],
538
- output:{
539
- "@context" => {"ex" => "http://example.org/"},
540
- "@graph" => [{
541
- "@id" => "ex:Sub1",
542
- "@type" => "ex:Type1",
543
- "@reverse" => {
544
- "ex:includes" => {
545
- "@id" => "ex:Sub2",
546
- "@type" => "ex:Type2",
547
- "ex:includes" => {"@id" => "ex:Sub1"}
746
+ frame: %({
747
+ "@context": {"ex": "http://example.org/"},
748
+ "@type": "ex:Type1",
749
+ "@reverse": {"ex:includes": {}}
750
+ }),
751
+ input: %([{
752
+ "@context": {"ex": "http://example.org/"},
753
+ "@id": "ex:Sub1",
754
+ "@type": "ex:Type1"
755
+ }, {
756
+ "@context": {"ex": "http://example.org/"},
757
+ "@id": "ex:Sub2",
758
+ "@type": "ex:Type2",
759
+ "ex:includes": {"@id": "ex:Sub1"}
760
+ }]),
761
+ output: %({
762
+ "@context": {"ex": "http://example.org/"},
763
+ "@graph": [{
764
+ "@id": "ex:Sub1",
765
+ "@type": "ex:Type1",
766
+ "@reverse": {
767
+ "ex:includes": {
768
+ "@id": "ex:Sub2",
769
+ "@type": "ex:Type2",
770
+ "ex:includes": {
771
+ "@id": "ex:Sub1"
772
+ }
548
773
  }
549
774
  }
550
775
  }]
551
- }
776
+ })
552
777
  },
553
778
  "embed matched frames with reversed property" => {
554
- frame: {
555
- "@context" => {
556
- "ex" => "http://example.org/",
557
- "excludes" => {"@reverse" => "ex:includes"}
779
+ frame: %({
780
+ "@context": {
781
+ "ex": "http://example.org/",
782
+ "excludes": {"@reverse": "ex:includes"}
558
783
  },
559
- "@type" => "ex:Type1",
560
- "excludes" => {}
561
- },
562
- input: [
563
- {
564
- "@context" => {"ex" => "http://example.org/"},
565
- "@id" => "ex:Sub1",
566
- "@type" => "ex:Type1"
567
- },
568
- {
569
- "@context" => {"ex" => "http://example.org/"},
570
- "@id" => "ex:Sub2",
571
- "@type" => "ex:Type2",
572
- "ex:includes" => {"@id" => "ex:Sub1"}
784
+ "@type": "ex:Type1",
785
+ "excludes": {}
786
+ }),
787
+ input: %([{
788
+ "@context": {"ex": "http://example.org/"},
789
+ "@id": "ex:Sub1",
790
+ "@type": "ex:Type1"
791
+ }, {
792
+ "@context": {"ex": "http://example.org/"},
793
+ "@id": "ex:Sub2",
794
+ "@type": "ex:Type2",
795
+ "ex:includes": {"@id": "ex:Sub1"}
796
+ }]),
797
+ output: %({
798
+ "@context": {
799
+ "ex": "http://example.org/",
800
+ "excludes": {"@reverse": "ex:includes"}
573
801
  },
574
- ],
575
- output:{
576
- "@context" => {
577
- "ex" => "http://example.org/",
578
- "excludes" => {"@reverse" => "ex:includes"}
579
- },
580
- "@graph" => [{
581
- "@id" => "ex:Sub1",
582
- "@type" => "ex:Type1",
583
- "excludes" => {
584
- "@id" => "ex:Sub2",
585
- "@type" => "ex:Type2",
586
- "ex:includes" => {"@id" => "ex:Sub1"}
802
+ "@graph": [{
803
+ "@id": "ex:Sub1",
804
+ "@type": "ex:Type1",
805
+ "excludes": {
806
+ "@id": "ex:Sub2",
807
+ "@type": "ex:Type2",
808
+ "ex:includes": {"@id": "ex:Sub1"}
587
809
  }
588
810
  }]
589
- }
811
+ })
590
812
  },
591
813
  }.each do |title, params|
592
814
  it title do
593
815
  begin
594
- jld = JSON::LD::API.frame(params[:input], params[:frame], logger: logger)
595
- expect(jld).to produce(params[:output], logger)
816
+ input, frame, output = params[:input], params[:frame], params[:output]
817
+ input = ::JSON.parse(input) if input.is_a?(String)
818
+ frame = ::JSON.parse(frame) if frame.is_a?(String)
819
+ output = ::JSON.parse(output) if output.is_a?(String)
820
+ jld = JSON::LD::API.frame(input, frame, logger: logger)
821
+ expect(jld).to produce(output, logger)
596
822
  rescue JSON::LD::JsonLdError, JSON::LD::JsonLdError, JSON::LD::InvalidFrame => e
597
823
  fail("#{e.class}: #{e.message}\n" +
598
824
  "#{logger}\n" +
@@ -601,6 +827,515 @@ describe JSON::LD::API do
601
827
  end
602
828
  end
603
829
  end
830
+
831
+ describe "node pattern" do
832
+ {
833
+ "matches a deep node pattern" => {
834
+ frame: %({
835
+ "@context": {"ex": "http://example.org/"},
836
+ "ex:p": {
837
+ "ex:q": {}
838
+ }
839
+ }),
840
+ input: %({
841
+ "@context": {"ex": "http://example.org/"},
842
+ "@graph": [{
843
+ "@id": "ex:Sub1",
844
+ "@type": "ex:Type1",
845
+ "ex:p": {
846
+ "@id": "ex:Sub2",
847
+ "@type": "ex:Type2",
848
+ "ex:q": "foo"
849
+ }
850
+ }, {
851
+ "@id": "ex:Sub3",
852
+ "@type": "ex:Type1",
853
+ "ex:q": {
854
+ "@id": "ex:Sub4",
855
+ "@type": "ex:Type2",
856
+ "ex:r": "bar"
857
+ }
858
+ }]
859
+ }),
860
+ output: %({
861
+ "@context": {"ex": "http://example.org/"},
862
+ "@graph": [{
863
+ "@id": "ex:Sub1",
864
+ "@type": "ex:Type1",
865
+ "ex:p": {
866
+ "@id": "ex:Sub2",
867
+ "@type": "ex:Type2",
868
+ "ex:q": "foo"
869
+ }
870
+ }]
871
+ })
872
+ },
873
+ }.each do |title, params|
874
+ it title do
875
+ do_frame(params)
876
+ end
877
+ end
878
+ end
879
+
880
+ describe "value pattern" do
881
+ {
882
+ "matches exact values" => {
883
+ frame: %({
884
+ "@context": {"ex": "http://example.org/"},
885
+ "@id": "ex:Sub1",
886
+ "ex:p": "P",
887
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
888
+ "ex:r": {"@value": "R", "@language": "r"}
889
+ }),
890
+ input: %({
891
+ "@context": {"ex": "http://example.org/"},
892
+ "@id": "ex:Sub1",
893
+ "ex:p": "P",
894
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
895
+ "ex:r": {"@value": "R", "@language": "r"}
896
+ }),
897
+ output: %({
898
+ "@context": {"ex": "http://example.org/"},
899
+ "@graph": [{
900
+ "@id": "ex:Sub1",
901
+ "ex:p": "P",
902
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
903
+ "ex:r": {"@value": "R", "@language": "r"}
904
+ }]
905
+ })
906
+ },
907
+ "matches wildcard @value" => {
908
+ frame: %({
909
+ "@context": {"ex": "http://example.org/"},
910
+ "@id": "ex:Sub1",
911
+ "ex:p": {"@value": {}},
912
+ "ex:q": {"@value": {}, "@type": "ex:q"},
913
+ "ex:r": {"@value": {}, "@language": "r"}
914
+ }),
915
+ input: %({
916
+ "@context": {"ex": "http://example.org/"},
917
+ "@id": "ex:Sub1",
918
+ "ex:p": "P",
919
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
920
+ "ex:r": {"@value": "R", "@language": "r"}
921
+ }),
922
+ output: %({
923
+ "@context": {"ex": "http://example.org/"},
924
+ "@graph": [{
925
+ "@id": "ex:Sub1",
926
+ "ex:p": "P",
927
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
928
+ "ex:r": {"@value": "R", "@language": "r"}
929
+ }]
930
+ })
931
+ },
932
+ "matches wildcard @type" => {
933
+ frame: %({
934
+ "@context": {"ex": "http://example.org/"},
935
+ "@id": "ex:Sub1",
936
+ "ex:q": {"@value": "Q", "@type": {}}
937
+ }),
938
+ input: %({
939
+ "@context": {"ex": "http://example.org/"},
940
+ "@id": "ex:Sub1",
941
+ "ex:q": {"@value": "Q", "@type": "ex:q"}
942
+ }),
943
+ output: %({
944
+ "@context": {"ex": "http://example.org/"},
945
+ "@graph": [{
946
+ "@id": "ex:Sub1",
947
+ "ex:q": {"@value": "Q", "@type": "ex:q"}
948
+ }]
949
+ })
950
+ },
951
+ "matches wildcard @language" => {
952
+ frame: %({
953
+ "@context": {"ex": "http://example.org/"},
954
+ "@id": "ex:Sub1",
955
+ "ex:r": {"@value": "R", "@language": {}}
956
+ }),
957
+ input: %({
958
+ "@context": {"ex": "http://example.org/"},
959
+ "@id": "ex:Sub1",
960
+ "ex:r": {"@value": "R", "@language": "r"}
961
+ }),
962
+ output: %({
963
+ "@context": {"ex": "http://example.org/"},
964
+ "@graph": [{
965
+ "@id": "ex:Sub1",
966
+ "ex:r": {"@value": "R", "@language": "r"}
967
+ }]
968
+ })
969
+ },
970
+ "match none @type" => {
971
+ frame: %({
972
+ "@context": {"ex": "http://example.org/"},
973
+ "@id": "ex:Sub1",
974
+ "ex:p": {"@value": {}, "@type": []},
975
+ "ex:q": {"@value": {}, "@type": "ex:q"},
976
+ "ex:r": {"@value": {}, "@language": "r"}
977
+ }),
978
+ input: %({
979
+ "@context": {"ex": "http://example.org/"},
980
+ "@id": "ex:Sub1",
981
+ "ex:p": "P",
982
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
983
+ "ex:r": {"@value": "R", "@language": "r"}
984
+ }),
985
+ output: %({
986
+ "@context": {"ex": "http://example.org/"},
987
+ "@graph": [{
988
+ "@id": "ex:Sub1",
989
+ "ex:p": "P",
990
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
991
+ "ex:r": {"@value": "R", "@language": "r"}
992
+ }]
993
+ })
994
+ },
995
+ "match none @language" => {
996
+ frame: %({
997
+ "@context": {"ex": "http://example.org/"},
998
+ "@id": "ex:Sub1",
999
+ "ex:p": {"@value": {}, "@language": []},
1000
+ "ex:q": {"@value": {}, "@type": "ex:q"},
1001
+ "ex:r": {"@value": {}, "@language": "r"}
1002
+ }),
1003
+ input: %({
1004
+ "@context": {"ex": "http://example.org/"},
1005
+ "@id": "ex:Sub1",
1006
+ "ex:p": "P",
1007
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
1008
+ "ex:r": {"@value": "R", "@language": "r"}
1009
+ }),
1010
+ output: %({
1011
+ "@context": {"ex": "http://example.org/"},
1012
+ "@graph": [{
1013
+ "@id": "ex:Sub1",
1014
+ "ex:p": "P",
1015
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
1016
+ "ex:r": {"@value": "R", "@language": "r"}
1017
+ }]
1018
+ })
1019
+ },
1020
+ "matches some @value" => {
1021
+ frame: %({
1022
+ "@context": {"ex": "http://example.org/"},
1023
+ "@id": "ex:Sub1",
1024
+ "ex:p": {"@value": ["P", "Q", "R"]},
1025
+ "ex:q": {"@value": ["P", "Q", "R"], "@type": "ex:q"},
1026
+ "ex:r": {"@value": ["P", "Q", "R"], "@language": "r"}
1027
+ }),
1028
+ input: %({
1029
+ "@context": {"ex": "http://example.org/"},
1030
+ "@id": "ex:Sub1",
1031
+ "ex:p": "P",
1032
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
1033
+ "ex:r": {"@value": "R", "@language": "r"}
1034
+ }),
1035
+ output: %({
1036
+ "@context": {"ex": "http://example.org/"},
1037
+ "@graph": [{
1038
+ "@id": "ex:Sub1",
1039
+ "ex:p": "P",
1040
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
1041
+ "ex:r": {"@value": "R", "@language": "r"}
1042
+ }]
1043
+ })
1044
+ },
1045
+ "matches some @type" => {
1046
+ frame: %({
1047
+ "@context": {"ex": "http://example.org/"},
1048
+ "@id": "ex:Sub1",
1049
+ "ex:q": {"@value": "Q", "@type": ["ex:q", "ex:Q"]}
1050
+ }),
1051
+ input: %({
1052
+ "@context": {"ex": "http://example.org/"},
1053
+ "@id": "ex:Sub1",
1054
+ "ex:q": {"@value": "Q", "@type": "ex:q"}
1055
+ }),
1056
+ output: %({
1057
+ "@context": {"ex": "http://example.org/"},
1058
+ "@graph": [{
1059
+ "@id": "ex:Sub1",
1060
+ "ex:q": {"@value": "Q", "@type": "ex:q"}
1061
+ }]
1062
+ })
1063
+ },
1064
+ "matches some @language" => {
1065
+ frame: %({
1066
+ "@context": {"ex": "http://example.org/"},
1067
+ "@id": "ex:Sub1",
1068
+ "ex:r": {"@value": "R", "@language": ["p", "q", "r"]}
1069
+ }),
1070
+ input: %({
1071
+ "@context": {"ex": "http://example.org/"},
1072
+ "@id": "ex:Sub1",
1073
+ "ex:r": {"@value": "R", "@language": "r"}
1074
+ }),
1075
+ output: %({
1076
+ "@context": {"ex": "http://example.org/"},
1077
+ "@graph": [{
1078
+ "@id": "ex:Sub1",
1079
+ "ex:r": {"@value": "R", "@language": "r"}
1080
+ }]
1081
+ })
1082
+ },
1083
+ "excludes non-matched values" => {
1084
+ frame: %({
1085
+ "@context": {"ex": "http://example.org/"},
1086
+ "@id": "ex:Sub1",
1087
+ "ex:p": {"@value": {}},
1088
+ "ex:q": {"@value": {}, "@type": "ex:q"},
1089
+ "ex:r": {"@value": {}, "@language": "R"}
1090
+ }),
1091
+ input: %({
1092
+ "@context": {"ex": "http://example.org/"},
1093
+ "@id": "ex:Sub1",
1094
+ "ex:p": ["P", {"@value": "P", "@type": "ex:p"}, {"@value": "P", "@language": "P"}],
1095
+ "ex:q": ["Q", {"@value": "Q", "@type": "ex:q"}, {"@value": "Q", "@language": "Q"}],
1096
+ "ex:r": ["R", {"@value": "R", "@type": "ex:r"}, {"@value": "R", "@language": "R"}]
1097
+ }),
1098
+ output: %({
1099
+ "@context": {"ex": "http://example.org/"},
1100
+ "@graph": [{
1101
+ "@id": "ex:Sub1",
1102
+ "ex:p": "P",
1103
+ "ex:q": {"@value": "Q", "@type": "ex:q"},
1104
+ "ex:r": {"@value": "R", "@language": "r"}
1105
+ }]
1106
+ })
1107
+ },
1108
+ }.each do |title, params|
1109
+ it title do
1110
+ do_frame(params)
1111
+ end
1112
+ end
1113
+ end
1114
+
1115
+ describe "named graphs" do
1116
+ {
1117
+ "Merge graphs if no outer @graph is used" => {
1118
+ frame: %({
1119
+ "@context": {"@vocab": "urn:"},
1120
+ "@type": "Class"
1121
+ }),
1122
+ input: %({
1123
+ "@context": {"@vocab": "urn:"},
1124
+ "@id": "urn:id-1",
1125
+ "@type": "Class",
1126
+ "preserve": {
1127
+ "@graph": {
1128
+ "@id": "urn:id-2",
1129
+ "term": "data"
1130
+ }
1131
+ }
1132
+ }),
1133
+ output: %({
1134
+ "@context": {"@vocab": "urn:"},
1135
+ "@graph": [{
1136
+ "@id": "urn:id-1",
1137
+ "@type": "Class",
1138
+ "preserve": {
1139
+ "@id": "_:b0"
1140
+ }
1141
+ }]
1142
+ })
1143
+ },
1144
+ "Frame default graph if outer @graph is used" => {
1145
+ frame: %({
1146
+ "@context": {"@vocab": "urn:"},
1147
+ "@type": "Class",
1148
+ "@graph": {}
1149
+ }),
1150
+ input: %({
1151
+ "@context": {"@vocab": "urn:"},
1152
+ "@id": "urn:id-1",
1153
+ "@type": "Class",
1154
+ "preserve": {
1155
+ "@id": "urn:gr-1",
1156
+ "@graph": {
1157
+ "@id": "urn:id-2",
1158
+ "term": "data"
1159
+ }
1160
+ }
1161
+ }),
1162
+ output: %({
1163
+ "@context": {"@vocab": "urn:"},
1164
+ "@graph": [{
1165
+ "@id": "urn:id-1",
1166
+ "@type": "Class",
1167
+ "preserve": {
1168
+ "@id": "urn:gr-1",
1169
+ "@graph": [{
1170
+ "@id": "urn:id-2",
1171
+ "term": "data"
1172
+ }]
1173
+ }
1174
+ }]
1175
+ })
1176
+ },
1177
+ "Merge one graph and preserve another" => {
1178
+ frame: %({
1179
+ "@context": {"@vocab": "urn:"},
1180
+ "@type": "Class",
1181
+ "preserve": {
1182
+ "@graph": {}
1183
+ }
1184
+ }),
1185
+ input: %({
1186
+ "@context": {"@vocab": "urn:"},
1187
+ "@id": "urn:id-1",
1188
+ "@type": "Class",
1189
+ "merge": {
1190
+ "@id": "urn:id-2",
1191
+ "@graph": {
1192
+ "@id": "urn:id-2",
1193
+ "term": "foo"
1194
+ }
1195
+ },
1196
+ "preserve": {
1197
+ "@id": "urn:graph-1",
1198
+ "@graph": {
1199
+ "@id": "urn:id-3",
1200
+ "term": "bar"
1201
+ }
1202
+ }
1203
+ }),
1204
+ output: %({
1205
+ "@context": {"@vocab": "urn:"},
1206
+ "@graph": [{
1207
+ "@id": "urn:id-1",
1208
+ "@type": "Class",
1209
+ "merge": {
1210
+ "@id": "urn:id-2",
1211
+ "term": "foo"
1212
+ },
1213
+ "preserve": {
1214
+ "@id": "urn:graph-1",
1215
+ "@graph": [{
1216
+ "@id": "urn:id-3",
1217
+ "term": "bar"
1218
+ }]
1219
+ }
1220
+ }]
1221
+ })
1222
+ },
1223
+ "Merge one graph and deep preserve another" => {
1224
+ frame: %({
1225
+ "@context": {"@vocab": "urn:"},
1226
+ "@type": "Class",
1227
+ "preserve": {
1228
+ "deep": {
1229
+ "@graph": {}
1230
+ }
1231
+ }
1232
+ }),
1233
+ input: %({
1234
+ "@context": {"@vocab": "urn:"},
1235
+ "@id": "urn:id-1",
1236
+ "@type": "Class",
1237
+ "merge": {
1238
+ "@id": "urn:id-2",
1239
+ "@graph": {
1240
+ "@id": "urn:id-2",
1241
+ "term": "foo"
1242
+ }
1243
+ },
1244
+ "preserve": {
1245
+ "deep": {
1246
+ "@graph": {
1247
+ "@id": "urn:id-3",
1248
+ "term": "bar"
1249
+ }
1250
+ }
1251
+ }
1252
+ }),
1253
+ output: %({
1254
+ "@context": {"@vocab": "urn:"},
1255
+ "@graph": [{
1256
+ "@id": "urn:id-1",
1257
+ "@type": "Class",
1258
+ "merge": {
1259
+ "@id": "urn:id-2",
1260
+ "term": "foo"
1261
+ },
1262
+ "preserve": {
1263
+ "@id": "_:b0",
1264
+ "deep": {
1265
+ "@id": "_:b1",
1266
+ "@graph": [{
1267
+ "@id": "urn:id-3",
1268
+ "term": "bar"
1269
+ }]
1270
+ }
1271
+ }
1272
+ }]
1273
+ })
1274
+ },
1275
+ "library" => {
1276
+ frame: %({
1277
+ "@context": {"@vocab": "http://example.org/"},
1278
+ "@type": "Library",
1279
+ "contains": {
1280
+ "@id": "http://example.org/graphs/books",
1281
+ "@graph": {"@type": "Book"}
1282
+ }
1283
+ }),
1284
+ input: %({
1285
+ "@context": {"@vocab": "http://example.org/"},
1286
+ "@id": "http://example.org/library",
1287
+ "@type": "Library",
1288
+ "name": "Library",
1289
+ "contains": {
1290
+ "@id": "http://example.org/graphs/books",
1291
+ "@graph": {
1292
+ "@id": "http://example.org/library/the-republic",
1293
+ "@type": "Book",
1294
+ "creator": "Plato",
1295
+ "title": "The Republic",
1296
+ "contains": {
1297
+ "@id": "http://example.org/library/the-republic#introduction",
1298
+ "@type": "Chapter",
1299
+ "description": "An introductory chapter on The Republic.",
1300
+ "title": "The Introduction"
1301
+ }
1302
+ }
1303
+ }
1304
+ }),
1305
+ output: %({
1306
+ "@context": {"@vocab": "http://example.org/"},
1307
+ "@graph": [
1308
+ {
1309
+ "@id": "http://example.org/library",
1310
+ "@type": "Library",
1311
+ "name": "Library",
1312
+ "contains": {
1313
+ "@id": "http://example.org/graphs/books",
1314
+ "@graph": [
1315
+ {
1316
+ "@id": "http://example.org/library/the-republic",
1317
+ "@type": "Book",
1318
+ "creator": "Plato",
1319
+ "title": "The Republic",
1320
+ "contains": {
1321
+ "@id": "http://example.org/library/the-republic#introduction",
1322
+ "@type": "Chapter",
1323
+ "description": "An introductory chapter on The Republic.",
1324
+ "title": "The Introduction"
1325
+ }
1326
+ }
1327
+ ]
1328
+ }
1329
+ }
1330
+ ]
1331
+ })
1332
+ }
1333
+ }.each do |title, params|
1334
+ it title do
1335
+ do_frame(params)
1336
+ end
1337
+ end
1338
+ end
604
1339
  end
605
1340
 
606
1341
  context "problem cases" do
@@ -676,8 +1411,22 @@ describe JSON::LD::API do
676
1411
  }
677
1412
  ]
678
1413
  })
679
- framed = JSON::LD::API.frame(input, frame, logger: logger)
680
- expect(framed).to produce(expected, logger)
1414
+ do_frame(input: input, frame: frame, output: expected)
1415
+ end
1416
+ end
1417
+
1418
+ def do_frame(params)
1419
+ begin
1420
+ input, frame, output = params[:input], params[:frame], params[:output]
1421
+ input = ::JSON.parse(input) if input.is_a?(String)
1422
+ frame = ::JSON.parse(frame) if frame.is_a?(String)
1423
+ output = ::JSON.parse(output) if output.is_a?(String)
1424
+ jld = JSON::LD::API.frame(input, frame, logger: logger)
1425
+ expect(jld).to produce(output, logger)
1426
+ rescue JSON::LD::JsonLdError, JSON::LD::JsonLdError, JSON::LD::InvalidFrame => e
1427
+ fail("#{e.class}: #{e.message}\n" +
1428
+ "#{logger}\n" +
1429
+ "Backtrace:\n#{e.backtrace.join("\n")}")
681
1430
  end
682
1431
  end
683
1432
  end