rdf-ldp 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -87,9 +87,11 @@ module RDF::LDP
87
87
  predicate = inserted_content_relation
88
88
  return resource.to_uri if predicate == MEMBER_SUBJECT_URI
89
89
 
90
- raise(NotAcceptable, "#{resource.to_uri} is an LDP-NR; cannot add " \
91
- 'it to an IndirectContainer with a content ' \
92
- 'relation.') if resource.non_rdf_source?
90
+ if resource.non_rdf_source?
91
+ raise(NotAcceptable, "#{resource.to_uri} is an LDP-NR; cannot add " \
92
+ 'it to an IndirectContainer with a content ' \
93
+ 'relation.')
94
+ end
93
95
 
94
96
  target = transaction || resource.graph
95
97
  statements = target.query([resource.subject_uri, predicate, :o])
@@ -1,14 +1,16 @@
1
1
  module RDF
2
2
  module LDP
3
+ ##
4
+ # Provides an interaction model registry.
3
5
  class InteractionModel
4
6
  class << self
5
7
  ##
6
8
  # Interaction models are in reverse order of preference for POST/PUT
7
- # requests; e.g. if a client sends a request with Resource, RDFSource, and
8
- # BasicContainer headers, the server gives a basic container.
9
+ # requests; e.g. if a client sends a request with Resource, RDFSource,
10
+ # oand BasicContainer headers, the server gives a basic container.
9
11
  #
10
- # Interaction models are initialized in the correct order, but with no classed
11
- # registered to handle them.
12
+ # Interaction models are initialized in the correct order, but with no
13
+ # class registered to handle them.
12
14
  @@interaction_models = {
13
15
  RDF::LDP::RDFSource.to_uri => nil,
14
16
  RDF::LDP::Container.to_uri => nil,
@@ -17,43 +19,47 @@ module RDF
17
19
  RDF::LDP::IndirectContainer.to_uri => nil,
18
20
  RDF::LDP::NonRDFSource.to_uri => nil
19
21
  }
20
-
22
+
21
23
  ##
22
- # Register a new interaction model for one or more Link header URIs. klass.to_uri
23
- # will automatically be registered.
24
+ # Register a new interaction model for one or more Link header URIs.
25
+ # klass.to_uri will automatically be registered.
24
26
  #
25
- # @param [RDF::LDP::Resource] klass the implementation class to register
27
+ # @param [RDF::LDP::Resource] klass the implementation class to
28
+ # register
26
29
  # @param [Hash <Symbol, *>] opts registration options:
27
- # :default [true, false] if true, klass will become the new default klass for
28
- # unrecognized Link headers
29
- # :for [RDF::URI, Array<RDF::URI>] additional URIs for which klass should become
30
- # the interaction model
30
+ # :default [true, false] if true, klass will become the new default
31
+ # klass for unrecognized Link headers
32
+ # :for [RDF::URI, Array<RDF::URI>] additional URIs for which klass
33
+ # should become the interaction model
31
34
  #
32
35
  # @return [RDF::LDP::Resource] klass
33
- def register(klass, opts={})
36
+ def register(klass, opts = {})
34
37
  unless klass.ancestors.include?(RDF::LDP::Resource)
35
- raise ArgumentError, "Interaction models must subclass `RDF::LDP::Resource`"
38
+ raise ArgumentError,
39
+ 'Interaction models must subclass `RDF::LDP::Resource`'
36
40
  end
37
- @@default = klass if opts[:default] or @@default.nil?
41
+ @@default = klass if opts[:default] || @@default.nil?
38
42
  @@interaction_models[klass.to_uri] = klass
39
43
  Array(opts[:for]).each do |model|
40
44
  @@interaction_models[model] = klass
41
45
  end
42
46
  klass
43
47
  end
44
-
48
+
45
49
  ##
46
- # Find the appropriate interaction model given a set of Link header URIs.
50
+ # Find the appropriate interaction model given a set of Link header
51
+ # URIs.
47
52
  #
48
53
  # @param [Array<RDF::URI>] uris
49
54
  #
50
- # @return [Class] a subclass of {RDF::LDP::Resource} that most narrowly matches the
51
- # supplied `uris`, or the default interaction model if nothing matches
55
+ # @return [Class] a subclass of {RDF::LDP::Resource} that most narrowly
56
+ # matches the supplied `uris`, or the default interaction model if
57
+ # nothing matches
52
58
  def find(uris)
53
- match = @@interaction_models.keys.reverse.find { |u| uris.include? u }
59
+ match = @@interaction_models.keys.reverse.find { |u| uris.include? u }
54
60
  self.for(match) || @@default
55
61
  end
56
-
62
+
57
63
  ##
58
64
  # Find the interaction model registered for a given uri
59
65
  #
@@ -63,23 +69,27 @@ module RDF
63
69
  def for(uri)
64
70
  @@interaction_models[uri]
65
71
  end
66
-
72
+
67
73
  ##
68
74
  # The default registered interaction model
69
75
  def default
70
76
  @@default
71
77
  end
72
-
78
+
73
79
  ##
74
- # Test an array of URIs to see if their interaction models are compatible (e.g., all of the URIs
75
- # refer either to RDF models or non-RDF models, but not a combination of both).
80
+ # Test an array of URIs to see if their interaction models are
81
+ # compatible (e.g., all of the URIs refer either to RDF models or
82
+ # non-RDF models, but not a combination of both).
76
83
  #
77
84
  # @param [Array<RDF::URI>] uris
78
- # @return [TrueClass or FalseClass] true if the models specified by `uris` are compatible
85
+ # @return [TrueClass or FalseClass] true if the models specified by
86
+ # `uris` are compatible
79
87
  def compatible?(uris)
80
- classes = uris.collect { |m| self.for(m) }
81
- (rdf,non_rdf) = classes.compact.partition { |c| c.ancestors.include?(RDFSource) }
82
- rdf.empty? or non_rdf.empty?
88
+ classes = uris.collect { |m| self.for(m) }
89
+ (rdf, non_rdf) =
90
+ classes.compact.partition { |c| c.ancestors.include?(RDFSource) }
91
+
92
+ rdf.empty? || non_rdf.empty?
83
93
  end
84
94
  end
85
95
  end
@@ -30,7 +30,9 @@ module RDF::LDP
30
30
  # @param [storage_adapter] a class implementing the StorageAdapter interface
31
31
  #
32
32
  # @see RDF::LDP::Resource#initialize
33
- def initialize(subject_uri, data = RDF::Repository.new, storage_adapter = DEFAULT_ADAPTER)
33
+ def initialize(subject_uri,
34
+ data = RDF::Repository.new,
35
+ storage_adapter = DEFAULT_ADAPTER)
34
36
  data ||= RDF::Repository.new # allows explict `nil` pass
35
37
  @storage = storage_adapter.new(self)
36
38
  super(subject_uri, data)
@@ -132,7 +134,7 @@ module RDF::LDP
132
134
  #
133
135
  # @raise [RDF::LDP::RequestError] when the request fails
134
136
  def to_response
135
- (exists? && !destroyed?) ? storage.io : []
137
+ exists? && !destroyed? ? storage.io : []
136
138
  end
137
139
 
138
140
  private
@@ -38,14 +38,15 @@ module RDF::LDP
38
38
  # resource.exists? # => true
39
39
  # resource.metagraph.dump :ttl
40
40
  # # => "<http://example.org/moomin> a <http://www.w3.org/ns/ldp#Resource>;
41
- # <http://purl.org/dc/terms/modified> "2015-10-25T14:24:56-07:00"^^<http://www.w3.org/2001/XMLSchema#dateTime> ."
41
+ # # <http://purl.org/dc/terms/modified>
42
+ # # "2015-10-25T14:24:56-07:00"^^xsd:dateTime ."
42
43
  #
43
44
  # @example updating a Resource updates the `#last_modified` date
44
45
  # resource.last_modified
45
- # # => #<DateTime: 2015-10-25T14:32:01-07:00 ((2457321j,77521s,571858283n),-25200s,2299161j)>
46
+ # # => #<DateTime: 2015-10-25T14:32:01-07:00...>
46
47
  # resource.update('blah', 'text/plain')
47
48
  # resource.last_modified
48
- # # => #<DateTime: 2015-10-25T14:32:04-07:00 ((2457321j,77524s,330658065n),-25200s,2299161j)>
49
+ # # => #<DateTime: 2015-10-25T14:32:04-07:00...>
49
50
  #
50
51
  # @example destroying a Resource
51
52
  # resource.exists? # => true
@@ -75,8 +76,8 @@ module RDF::LDP
75
76
  # #<RDF::LDP::Resource:0x00564f4a646028
76
77
  # @data=#<RDF::Repository:0x2b27a5391708()>,
77
78
  # @exists=true,
78
- # @metagraph=#<RDF::Graph:0x2b27a5322538(http://example.org/moomin#meta)>,
79
- # @subject_uri=#<RDF::URI:0x2b27a5322fec URI:http://example.org/moomin>>]
79
+ # @metagraph=#<RDF::Graph:0xea7(http://example.org/moomin#meta)>,
80
+ # @subject_uri=#<RDF::URI:0xea8 URI:http://example.org/moomin>>]
80
81
  #
81
82
  # resource.request(:put, 200, {}, {}) # RDF::LDP::MethodNotAllowed: put
82
83
  #
@@ -161,7 +162,7 @@ module RDF::LDP
161
162
  .map { |link| RDF::URI.intern(link.href) }
162
163
 
163
164
  return InteractionModel.default if models.empty?
164
-
165
+
165
166
  raise NotAcceptable unless InteractionModel.compatible?(models)
166
167
 
167
168
  InteractionModel.find(models)
@@ -302,9 +303,9 @@ module RDF::LDP
302
303
  #
303
304
  # @return [String] an HTTP Etag
304
305
  #
305
- # @note these etags are weak, but we allow clients to use them in
306
+ # @note these etags are weak, but we allow clients to use them in
306
307
  # `If-Match` headers, and use weak comparison. This is in conflict with
307
- # https://tools.ietf.org/html/rfc7232#section-3.1. See:
308
+ # https://tools.ietf.org/html/rfc7232#section-3.1. See:
308
309
  # https://github.com/ruby-rdf/rdf-ldp/issues/68
309
310
  #
310
311
  # @see http://www.w3.org/TR/ldp#h-ldpr-gen-etags LDP ETag clause for GET
@@ -558,24 +559,25 @@ module RDF::LDP
558
559
  ##
559
560
  # Sets the last modified date/time to now
560
561
  #
561
- # @param transaction [RDF::Transaction] the transaction scope in which to
562
+ # @param transaction [RDF::Transaction] the transaction scope in which to
562
563
  # apply changes. If none (or `nil`) is given, the change is made outside
563
564
  # any transaction scope.
564
565
  def set_last_modified(transaction = nil)
565
- if transaction
566
- # transactions do not support updates or pattern deletes, so we must
567
- # ask the Repository for the current last_modified to delete the statement
568
- # transactionally
566
+ return metagraph.update([subject_uri, MODIFIED_URI, DateTime.now]) unless
569
567
  transaction
570
- .delete RDF::Statement(subject_uri, MODIFIED_URI, last_modified,
571
- graph_name: metagraph_name) if last_modified
572
568
 
569
+ # transactions do not support updates or pattern deletes, so we must
570
+ # ask the Repository for the current last_modified to delete the
571
+ # statement transactionally
572
+ if last_modified
573
573
  transaction
574
- .insert RDF::Statement(subject_uri, MODIFIED_URI, DateTime.now,
574
+ .delete RDF::Statement(subject_uri, MODIFIED_URI, last_modified,
575
575
  graph_name: metagraph_name)
576
- else
577
- metagraph.update([subject_uri, MODIFIED_URI, DateTime.now])
578
576
  end
577
+
578
+ transaction
579
+ .insert RDF::Statement(subject_uri, MODIFIED_URI, DateTime.now,
580
+ graph_name: metagraph_name)
579
581
  end
580
582
 
581
583
  ##
@@ -0,0 +1,6 @@
1
+ require 'rdf/ldp/spec/resource'
2
+ require 'rdf/ldp/spec/rdf_source'
3
+ require 'rdf/ldp/spec/non_rdf_source'
4
+ require 'rdf/ldp/spec/container'
5
+ require 'rdf/ldp/spec/direct_container'
6
+ require 'rdf/ldp/spec/indirect_container'
@@ -0,0 +1,337 @@
1
+ require 'rdf/ldp/spec/rdf_source'
2
+
3
+ shared_examples 'a Container' do
4
+ it_behaves_like 'an RDFSource'
5
+
6
+ let(:uri) { RDF::URI('http://ex.org/moomin') }
7
+ subject { described_class.new(uri) }
8
+
9
+ it { is_expected.to be_container }
10
+
11
+ describe '#container_class' do
12
+ it 'returns a uri' do
13
+ expect(subject.container_class).to be_a RDF::URI
14
+ end
15
+ end
16
+
17
+ describe '#containment_triples' do
18
+ let(:resource) { RDF::URI('http://ex.org/mymble') }
19
+
20
+ it 'returns a uri' do
21
+ subject.add_containment_triple(resource)
22
+ expect(subject.containment_triples)
23
+ .to contain_exactly(an_instance_of(RDF::Statement))
24
+ end
25
+ end
26
+
27
+ describe '#add' do
28
+ let(:resource) { RDF::URI('http://ex.org/mymble') }
29
+ before { subject.create(StringIO.new, 'application/n-triples') }
30
+
31
+ it 'returns self' do
32
+ expect(subject.add(resource)).to eq subject
33
+ end
34
+
35
+ it 'containment triple is added to graph' do
36
+ expect(subject.add(resource).graph)
37
+ .to include subject.make_containment_triple(resource)
38
+ end
39
+ end
40
+
41
+ describe '#add_containment_triple' do
42
+ let(:resource) { RDF::URI('http://ex.org/mymble') }
43
+
44
+ it 'returns self' do
45
+ expect(subject.add_containment_triple(resource)).to eq subject
46
+ end
47
+
48
+ it 'containment triple is added to graph' do
49
+ expect(subject.add_containment_triple(resource).graph)
50
+ .to include subject.make_containment_triple(resource)
51
+ end
52
+ end
53
+
54
+ describe '#remove_containment_triple' do
55
+ before { subject.add_containment_triple(resource) }
56
+
57
+ let(:resource) { RDF::URI('http://ex.org/mymble') }
58
+
59
+ it 'returns self' do
60
+ expect(subject.remove_containment_triple(resource)).to eq subject
61
+ end
62
+
63
+ it 'membership triple is added to graph' do
64
+ expect(subject.remove_containment_triple(resource).graph)
65
+ .not_to include subject.make_containment_triple(resource)
66
+ end
67
+
68
+ it 'updates last_modified for container' do
69
+ expect { subject.remove_containment_triple(resource) }
70
+ .to change { subject.last_modified }
71
+ end
72
+
73
+ it 'updates etag for container' do
74
+ expect { subject.remove_containment_triple(resource) }
75
+ .to change { subject.etag }
76
+ end
77
+ end
78
+
79
+ describe '#make_containment_triple' do
80
+ let(:resource) { uri / 'papa' }
81
+
82
+ it 'returns a statement' do
83
+ expect(subject.make_containment_triple(resource)).to be_a RDF::Statement
84
+ end
85
+
86
+ it 'statement subject *or* object is #subject_uri' do
87
+ sub = subject.make_containment_triple(resource).subject
88
+ obj = subject.make_containment_triple(resource).object
89
+ expect([sub, obj]).to include subject.subject_uri
90
+ end
91
+
92
+ it 'converts Resource classes to URI' do
93
+ sub = subject.make_containment_triple(subject).subject
94
+ obj = subject.make_containment_triple(subject).object
95
+ expect([sub, obj]).to include subject.subject_uri
96
+ end
97
+ end
98
+
99
+ describe '#request' do
100
+ let(:graph) { RDF::Graph.new }
101
+
102
+ let(:env) do
103
+ { 'rack.input' => StringIO.new(graph.dump(:ntriples)),
104
+ 'CONTENT_TYPE' => 'application/n-triples' }
105
+ end
106
+
107
+ let(:statement) do
108
+ RDF::Statement(subject.subject_uri,
109
+ RDF::Vocab::LDP.contains,
110
+ 'moomin')
111
+ end
112
+
113
+ context 'with :PATCH',
114
+ if: described_class.private_method_defined?(:patch) do
115
+
116
+ it 'raises conflict error when editing containment triples' do
117
+ patch_statement = statement.clone
118
+ patch_statement.object = 'snorkmaiden'
119
+ patch = '@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .' \
120
+ "\n\nAdd { #{statement.subject.to_base} " \
121
+ "#{statement.predicate.to_base} #{statement.object.to_base} } ."
122
+ env = { 'CONTENT_TYPE' => 'text/ldpatch',
123
+ 'rack.input' => StringIO.new(patch) }
124
+
125
+ expect { subject.request(:patch, 200, {}, env) }
126
+ .to raise_error RDF::LDP::Conflict
127
+ end
128
+ end
129
+
130
+ context 'with :PUT',
131
+ if: described_class.private_method_defined?(:put) do
132
+ context 'when PUTing containment triples' do
133
+ it 'when creating a resource raises a Conflict error' do
134
+ graph << statement
135
+
136
+ expect { subject.request(:PUT, 200, { 'abc' => 'def' }, env) }
137
+ .to raise_error RDF::LDP::Conflict
138
+ end
139
+
140
+ it 'when resource exists raises a Conflict error' do
141
+ subject.create(StringIO.new, 'application/n-triples')
142
+ graph << statement
143
+ expect { subject.request(:PUT, 200, { 'abc' => 'def' }, env) }
144
+ .to raise_error RDF::LDP::Conflict
145
+ end
146
+
147
+ it 'can put existing containment triple' do
148
+ subject.create(StringIO.new, 'application/n-triples')
149
+ subject.graph << statement
150
+ graph << statement
151
+ expect(subject.request(:PUT, 200, { 'abc' => 'def' }, env).first)
152
+ .to eq 200
153
+ end
154
+
155
+ it 'writes data when putting existing containment triple' do
156
+ subject.create(StringIO.new, 'application/n-triples')
157
+ subject.graph << statement
158
+ graph << statement
159
+
160
+ new_st = RDF::Statement(RDF::URI('http://example.org/new_moomin'),
161
+ RDF::Vocab::DC.title,
162
+ 'moomin')
163
+ graph << new_st
164
+ expect(subject.request(:PUT, 200, { 'abc' => 'def' }, env).last.graph)
165
+ .to have_statement new_st
166
+ end
167
+
168
+ it 'raises conflict error when without existing containment triples' do
169
+ subject.create(StringIO.new, 'application/n-triples')
170
+ subject.graph << statement
171
+ expect { subject.request(:PUT, 200, { 'abc' => 'def' }, env) }
172
+ .to raise_error RDF::LDP::Conflict
173
+ end
174
+ end
175
+ end
176
+
177
+ context 'when POST is implemented',
178
+ if: described_class.private_method_defined?(:post) do
179
+ let(:graph) { RDF::Graph.new }
180
+ before { subject.create(StringIO.new, 'application/n-triples') }
181
+
182
+ let(:env) do
183
+ { 'rack.input' => StringIO.new(graph.dump(:ntriples)),
184
+ 'CONTENT_TYPE' => 'application/n-triples' }
185
+ end
186
+
187
+ it 'returns status 201' do
188
+ expect(subject.request(:POST, 200, {}, env).first).to eq 201
189
+ end
190
+
191
+ it 'gives created resource as body' do
192
+ expect(subject.request(:POST, 200, {}, env).last)
193
+ .to be_a RDF::LDP::Resource
194
+ end
195
+
196
+ it 'generates an id' do
197
+ expect(subject.request(:POST, 200, {}, env).last.subject_uri)
198
+ .to be_starts_with subject.subject_uri.to_s
199
+ end
200
+
201
+ it 'adds containment statement to resource' do
202
+ expect { subject.request(:POST, 200, {}, env) }
203
+ .to change { subject.containment_triples.count }.from(0).to(1)
204
+ end
205
+
206
+ it 'updates last_modified for container' do
207
+ expect { subject.request(:POST, 200, {}, env) }
208
+ .to change { subject.last_modified }
209
+ end
210
+
211
+ it 'updates etag for container' do
212
+ expect { subject.request(:POST, 200, {}, env) }
213
+ .to change { subject.etag }
214
+ end
215
+
216
+ context 'with Container interaction model' do
217
+ it 'creates a basic container' do
218
+ env['HTTP_LINK'] = "<#{RDF::LDP::Container.to_uri}>;rel=\"type\""
219
+ expect(subject.request(:POST, 200, {}, env).last)
220
+ .to be_a RDF::LDP::Container
221
+ end
222
+
223
+ context 'BasicContainer' do
224
+ it 'creates a basic container' do
225
+ env['HTTP_LINK'] =
226
+ '<http://www.w3.org/ns/ldp#BasicContainer>;rel="type"'
227
+ expect(subject.request(:POST, 200, {}, env).last)
228
+ .to be_a RDF::LDP::Container
229
+ end
230
+ end
231
+
232
+ context 'DirectContainer' do
233
+ it 'creates a direct container' do
234
+ env['HTTP_LINK'] =
235
+ "<#{RDF::LDP::DirectContainer.to_uri}>;rel=\"type\""
236
+
237
+ expect(subject.request(:POST, 200, {}, env).last)
238
+ .to be_a RDF::LDP::DirectContainer
239
+ end
240
+ end
241
+
242
+ context 'IndirectContainer' do
243
+ it 'creates a indirect container' do
244
+ env['HTTP_LINK'] =
245
+ "<#{RDF::LDP::IndirectContainer.to_uri}>;rel=\"type\""
246
+
247
+ expect(subject.request(:POST, 200, {}, env).last)
248
+ .to be_a RDF::LDP::IndirectContainer
249
+ end
250
+ end
251
+ end
252
+
253
+ context 'with a Slug' do
254
+ it 'creates resource with Slug' do
255
+ env['HTTP_SLUG'] = 'snork'
256
+ expect(subject.request(:POST, 200, {}, env).last.subject_uri)
257
+ .to eq subject.subject_uri / env['HTTP_SLUG']
258
+ end
259
+
260
+ it 'mints a uri when empty Slug is given' do
261
+ env['HTTP_SLUG'] = ''
262
+ expect(subject.request(:POST, 200, {}, env).last.subject_uri)
263
+ .to be_starts_with subject.subject_uri
264
+ end
265
+
266
+ it 'raises a 409 Conflict when slug is already taken' do
267
+ env['HTTP_SLUG'] = 'snork'
268
+ subject.request(:POST, 200, {}, env)
269
+
270
+ expect { subject.request(:POST, 200, {}, env) }
271
+ .to raise_error RDF::LDP::Conflict
272
+ end
273
+
274
+ it 'raises a 409 Conflict when slug is already taken but destroyed' do
275
+ env['HTTP_SLUG'] = 'snork'
276
+ created = subject.request(:POST, 200, {}, env).last
277
+ allow(created).to receive(:destroyed?).and_return true
278
+
279
+ expect { subject.request(:POST, 200, {}, env) }
280
+ .to raise_error RDF::LDP::Conflict
281
+ end
282
+
283
+ it 'raises a 406 NotAcceptable if slug has a uri fragment `#`' do
284
+ env['HTTP_SLUG'] = 'snork#maiden'
285
+
286
+ expect { subject.request(:POST, 200, {}, env) }
287
+ .to raise_error RDF::LDP::NotAcceptable
288
+ end
289
+
290
+ it 'url-encodes Slug' do
291
+ env['HTTP_SLUG'] = 'snork maiden'
292
+ expect(subject.request(:POST, 200, {}, env).last.subject_uri)
293
+ .to eq subject.subject_uri / 'snork%20maiden'
294
+ end
295
+ end
296
+
297
+ context 'with graph content' do
298
+ before do
299
+ graph << RDF::Statement(uri, RDF::Vocab::DC.title, 'moomin')
300
+ graph << RDF::Statement(RDF::Node.new, RDF::Vocab::DC.creator, 'tove')
301
+ graph <<
302
+ RDF::Statement(RDF::Node.new, RDF.type, RDF::Vocab::FOAF.Person)
303
+ end
304
+
305
+ it 'parses graph into created resource' do
306
+ expect(subject.request(:POST, 200, {}, env).last.to_response)
307
+ .to be_isomorphic_with graph
308
+ end
309
+
310
+ it 'adds a Location header' do
311
+ expect(subject.request(:POST, 200, {}, env)[1]['Location'])
312
+ .to start_with subject.subject_uri.to_s
313
+ end
314
+
315
+ context 'with quads' do
316
+ let(:graph) do
317
+ RDF::Graph.new(graph_name: subject.subject_uri,
318
+ data: RDF::Repository.new)
319
+ end
320
+
321
+ let(:env) do
322
+ { 'rack.input' => StringIO.new(graph.dump(:nquads)),
323
+ 'CONTENT_TYPE' => 'application/n-quads' }
324
+ end
325
+
326
+ it 'parses graph into created resource without regard for context' do
327
+ context_free_graph = RDF::Graph.new
328
+ context_free_graph << graph.statements
329
+
330
+ expect(subject.request(:POST, 200, {}, env).last.to_response)
331
+ .to be_isomorphic_with context_free_graph
332
+ end
333
+ end
334
+ end
335
+ end
336
+ end
337
+ end