wf4ever-rosrs-client 0.1.1

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.
@@ -0,0 +1,672 @@
1
+ # ROSRS session class
2
+ module ROSRS
3
+ class Session
4
+
5
+ attr_reader :uri
6
+
7
+ ANNOTATION_CONTENT_TYPES =
8
+ { "application/rdf+xml" => :xml,
9
+ "text/turtle" => :turtle,
10
+ #"text/n3" => :n3,
11
+ "text/nt" => :ntriples,
12
+ #"application/json" => :jsonld,
13
+ #"application/xhtml" => :rdfa,
14
+ }
15
+
16
+ PARSEABLE_CONTENT_TYPES = ['application/vnd.wf4ever.folderentry',
17
+ 'application/vnd.wf4ever.folder',
18
+ 'application/rdf+xml']
19
+
20
+ # -------------
21
+ # General setup
22
+ # -------------
23
+
24
+ def initialize(uri, accesskey=nil)
25
+ @uri = URI(uri.to_s) # Force string or URI to be a URI - tried coerce, didn't work
26
+ @key = accesskey
27
+ @http = Net::HTTP.new(@uri.host, @uri.port)
28
+ end
29
+
30
+ def close
31
+ if @http and @http.started?
32
+ @http.finish
33
+ @http = nil
34
+ end
35
+ end
36
+
37
+ def error(code, msg, value=nil)
38
+ # Raise exception with supplied message and optional value
39
+ if value
40
+ msg += " (#{value})"
41
+ end
42
+ msg = "Exception on #@uri #{msg}"
43
+ case code
44
+ when 401
45
+ raise ROSRS::UnauthorizedException.new(msg)
46
+ when 403
47
+ raise ROSRS::ForbiddenException.new(msg)
48
+ when 404
49
+ raise ROSRS::NotFoundException.new(msg)
50
+ when 409
51
+ raise ROSRS::ConflictException.new(msg)
52
+ else
53
+ raise ROSRS::Exception.new(msg)
54
+ end
55
+ end
56
+
57
+ # -------
58
+ # Helpers
59
+ # -------
60
+
61
+ private
62
+
63
+ ##
64
+ # Parse links from headers; returns a hash indexed by link relation
65
+ # Headerlist is a hash indexed by header field name (see HTTP:Response)
66
+ def parse_links(headers)
67
+ links = {}
68
+ link_header = headers["link"] || headers["Link"]
69
+ link_header.split(",").each do |link|
70
+ matches = link.strip.match(/<([^>]*)>\s*;.*rel\s*=\s*"?([^;"]*)"?/)
71
+ if matches
72
+ links[matches[2]] ||= []
73
+ links[matches[2]] << matches[1]
74
+ end
75
+ end
76
+ links
77
+ end
78
+
79
+ ##
80
+ # Extract path (incl query) for HTTP request
81
+ # Should accept URI, RDF::URI or string values
82
+ # Must be same host and port as session URI
83
+ # Relative values are based on session URI
84
+ def get_request_path(uripath)
85
+ uripath = URI(uripath.to_s)
86
+ if uripath.scheme && (uripath.scheme != @uri.scheme)
87
+ error(nil, "Request URI scheme does not match session: #{uripath}")
88
+ end
89
+ if (uripath.host && uripath.host != @uri.host) ||
90
+ (uripath.port && uripath.port != @uri.port)
91
+ error(nil, "Request URI host or port does not match session: #{uripath}")
92
+ end
93
+ requri = URI.join(@uri.to_s, uripath.path).path
94
+ if uripath.query
95
+ requri += "?"+uripath.query
96
+ end
97
+ requri
98
+ end
99
+
100
+ def get_request_headers(options = {})
101
+ if options[:headers]
102
+ # Convert symbol keys to strings
103
+ reqheaders = options[:headers].inject({}) do |headers, (header, value)|
104
+ headers[header.to_s] = value
105
+ headers
106
+ end
107
+ else
108
+ reqheaders = {}
109
+ end
110
+ if @key
111
+ reqheaders["authorization"] = "Bearer "+@key
112
+ end
113
+ if options[:ctype]
114
+ reqheaders["content-type"] = options[:ctype]
115
+ end
116
+ if options[:accept]
117
+ reqheaders['accept'] = options[:accept]
118
+ end
119
+ if options[:link]
120
+ reqheaders['Link'] = options[:link]
121
+ end
122
+ reqheaders
123
+ end
124
+
125
+ public
126
+
127
+ ##
128
+ # Perform HTTP request
129
+ #
130
+ # +method+:: HTTP method name
131
+ # +uripath+:: is reference or URI of resource (see get_request_path)
132
+ # options:
133
+ # [:body] body to accompany request
134
+ # [:ctype] content type of supplied body
135
+ # [:accept] accept content types for response
136
+ # [:headers] additional headers for request
137
+ # Return [code, reason(text), response headers, response body]
138
+ #
139
+ def do_request(method, uripath, options = {})
140
+
141
+ req = nil
142
+
143
+ case method
144
+ when 'GET'
145
+ req = Net::HTTP::Get.new(get_request_path(uripath))
146
+ when 'PUT'
147
+ req = Net::HTTP::Put.new(get_request_path(uripath))
148
+ when 'POST'
149
+ req = Net::HTTP::Post.new(get_request_path(uripath))
150
+ when 'DELETE'
151
+ req = Net::HTTP::Delete.new(get_request_path(uripath))
152
+ else
153
+ error(nil, "Unrecognized HTTP method #{method}")
154
+ end
155
+
156
+ if options[:body]
157
+ req.body = options[:body]
158
+ end
159
+
160
+ get_request_headers(options).each { |h,v| req.add_field(h, v) }
161
+ resp = @http.request(req)
162
+ [Integer(resp.code), resp.message, resp, resp.body]
163
+ end
164
+
165
+ ##
166
+ # Perform HTTP request, following 302, 303 307 redirects
167
+ # Return [code, reason(text), response headers, final uri, response body]
168
+ def do_request_follow_redirect(method, uripath, options = {})
169
+ code, reason, headers, data = do_request(method, uripath, options)
170
+ if [302,303,307].include?(code)
171
+ uripath = headers["location"]
172
+ code, reason, headers, data = do_request(method, uripath, options)
173
+ end
174
+ if [302,307].include?(code)
175
+ # Allow second temporary redirect
176
+ uripath = headers["location"]
177
+ code, reason, headers, data = do_request(method, uripath, options)
178
+ end
179
+ [code, reason, headers, uripath, data]
180
+ end
181
+
182
+ ##
183
+ # Perform HTTP request expecting an RDF/XML response
184
+ # Return [code, reason(text), response headers, manifest graph]
185
+ # Returns the manifest as a graph if the request is successful
186
+ # otherwise returns the raw response data.
187
+ def do_request_rdf(method, uripath, options = {})
188
+ options[:accept] ||= "application/rdf+xml"
189
+ code, reason, headers, uripath, data = do_request_follow_redirect(method, uripath, options)
190
+ if code >= 200 and code < 300
191
+ begin
192
+ data = ROSRS::RDFGraph.new(:data => data, :format => :xml)
193
+ rescue Exception => e
194
+ code = 902
195
+ reason = "RDF parse failure (#{e.message})"
196
+ end
197
+ end
198
+ [code, reason, headers, uripath, data]
199
+ end
200
+
201
+ # ---------------
202
+ # RO manipulation
203
+ # ---------------
204
+
205
+ ##
206
+ # Returns [copde, reason, uri, manifest]
207
+ def create_research_object(name)
208
+ code, reason, headers, uripath, data = do_request_rdf("POST", "",
209
+ :headers => {'slug' => name})
210
+ if code == 201
211
+ [code, reason, headers["location"], data]
212
+ else
213
+ error(code, "Error creating RO: #{code} #{reason}")
214
+ end
215
+ end
216
+
217
+ def delete_research_object(ro_uri)
218
+ # code, reason = delete_research_object(ro_uri)
219
+ code, reason = do_request("DELETE", ro_uri,
220
+ :accept => "application/rdf+xml")
221
+ if [204, 404].include?(code)
222
+ [code, reason]
223
+ else
224
+ error(code, "Error deleting RO #{ro_uri}: #{code} #{reason}")
225
+ end
226
+ end
227
+
228
+ # ---------------------
229
+ # Resource manipulation
230
+ # ---------------------
231
+
232
+ ##
233
+ # Aggregate internal resource
234
+ #
235
+ # options:
236
+ # [:body] body to accompany request
237
+ # [:ctype] content type of supplied body
238
+ # [:accept] accept content types for response
239
+ # [:headers] additional headers for request
240
+ #
241
+ # Returns: [code, reason, proxyuri, resource_uri], where code is 200 or 201
242
+
243
+ def aggregate_internal_resource(ro_uri, respath=nil, options={})
244
+ if respath
245
+ options[:headers] ||= {}
246
+ options[:headers]['slug'] = respath
247
+ end
248
+ # POST resource content to indicated URI
249
+ code, reason, headers = do_request("POST", ro_uri, options)
250
+ unless [200,201].include?(code)
251
+ error(code, "Error creating aggregated resource content",
252
+ "#{code}, #{reason}, #{respath}")
253
+ end
254
+ proxyuri = headers["location"]
255
+ resource_uri = parse_links(headers)[RDF::ORE.proxyFor.to_s].first
256
+ [code, reason, proxyuri, resource_uri]
257
+ end
258
+
259
+
260
+ ##
261
+ # Aggregate external resource
262
+ #
263
+ # Returns: [code, reason, proxyuri, resource_uri], where code is 200 or 201
264
+ def aggregate_external_resource(ro_uri, resource_uri=nil)
265
+ proxydata = %(
266
+ <rdf:RDF
267
+ xmlns:ore="http://www.openarchives.org/ore/terms/"
268
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
269
+ <ore:Proxy>
270
+ <ore:proxyFor rdf:resource="#{resource_uri}"/>
271
+ </ore:Proxy>
272
+ </rdf:RDF>
273
+ )
274
+ code, reason, headers = do_request("POST", ro_uri,
275
+ :ctype => "application/vnd.wf4ever.proxy",
276
+ :body => proxydata)
277
+ if code != 201
278
+ error(code, "Error creating aggregation proxy",
279
+ "#{code} #{reason} #{resource_uri}")
280
+ else
281
+ proxyuri = headers["location"]
282
+ resuri = parse_links(headers)[RDF::ORE.proxyFor.to_s].first
283
+ [code, reason, proxyuri, resuri]
284
+ end
285
+ end
286
+
287
+ # -----------------------
288
+ # Resource access
289
+ # -----------------------
290
+
291
+ ##
292
+ # Retrieve resource from RO
293
+ #
294
+ # resuriref is relative reference or URI of resource
295
+ # ro_uri is URI of RO, used as base for relative reference
296
+ # options:
297
+ # [:accept] content type
298
+ # [:headers] additional headers for request
299
+ # Returns:
300
+ # [code, reason, headers, data], where code is 200 or 404
301
+ def get_resource(resuriref, ro_uri=nil, options={})
302
+ if ro_uri
303
+ resuriref = URI.join(ro_uri.to_s, resuriref.to_s)
304
+ end
305
+ code, reason, headers, uri, data = do_request_follow_redirect("GET", resuriref, options)
306
+ if parseable?(headers["content-type"])
307
+ data = ROSRS::RDFGraph.new(:data => data, :format => :xml)
308
+ end
309
+ unless [200,404].include?(code)
310
+ error(code, "Error retrieving RO resource: #{code}, #{reason}, #{resuriref}")
311
+ end
312
+ [code, reason, headers, uri, data]
313
+ end
314
+
315
+ ##
316
+ # Retrieve RDF resource from RO
317
+ #
318
+ # resource_uri is relative reference or URI of resource
319
+ # ro_uri is URI of RO, used as base for relative reference
320
+ # options:
321
+ # [:headers] additional headers for request
322
+ #
323
+ # Returns:
324
+ # [code, reason, headers, uri, data], where code is 200 or 404
325
+ #
326
+ # If code isreturned as 200, data is returned as an RDFGraph value
327
+ def get_resource_rdf(resource_uri, ro_uri=nil, options={})
328
+ if ro_uri
329
+ resource_uri = URI.join(ro_uri.to_s, resource_uri.to_s)
330
+ end
331
+ code, reason, headers, uri, data = do_request_rdf("GET", resource_uri, options)
332
+ unless [200,404].include?(code)
333
+ error(code, "Error retrieving RO resource: #{code}, #{reason}, #{resource_uri}")
334
+ end
335
+ [code, reason, headers, uri, data]
336
+ end
337
+
338
+ ##
339
+ # Retrieve an RO manifest
340
+ #
341
+ # Returns [manifesturi, manifest]
342
+ def get_manifest(ro_uri)
343
+ code, reason, headers, uri, data = do_request_rdf("GET", ro_uri)
344
+ if code != 200
345
+ error(code, "Error retrieving RO manifest: #{code} #{reason}")
346
+ end
347
+ [uri, data]
348
+ end
349
+
350
+ # -----------------------
351
+ # Annotation manipulation
352
+ # -----------------------
353
+
354
+ ##
355
+ # Create an annotation body from a supplied annnotation graph.
356
+ #
357
+ # Returns: [code, reason, body_uri]
358
+ def create_annotation_body(ro_uri, annotation_graph)
359
+ code, reason, bodyproxyuri, body_uri = aggregate_internal_resource(ro_uri, nil,
360
+ :ctype => "application/rdf+xml",
361
+ :body => annotation_graph.serialize(format=:xml))
362
+ if code != 201
363
+ error(code, "Error creating annotation body resource",
364
+ "#{code}, #{reason}, #{ro_uri}")
365
+ end
366
+ [code, reason, body_uri]
367
+ end
368
+
369
+ ##
370
+ # Create entity body for annotation stub
371
+ def create_annotation_stub_rdf(ro_uri, resource_uri, body_uri)
372
+ v = { :xmlbase => ro_uri.to_s,
373
+ :resource_uri => resource_uri.to_s,
374
+ :body_uri => body_uri.to_s
375
+ }
376
+ annotation_stub = %Q(<?xml version="1.0" encoding="UTF-8"?>
377
+ <rdf:RDF
378
+ xmlns:ro="http://purl.org/wf4ever/ro#"
379
+ xmlns:ao="http://purl.org/ao/"
380
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
381
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
382
+ xml:base="#{v[:xmlbase]}"
383
+ >
384
+ <ro:AggregatedAnnotation>
385
+ <ao:annotatesResource rdf:resource="#{v[:resource_uri]}" />
386
+ <ao:body rdf:resource="#{v[:body_uri]}" />
387
+ </ro:AggregatedAnnotation>
388
+ </rdf:RDF>
389
+ )
390
+ annotation_stub
391
+ end
392
+
393
+ ##
394
+ # Create an annotation stub for supplied resource using indicated body
395
+ #
396
+ # Returns: [code, reason, stuburi]
397
+ def create_annotation_stub(ro_uri, resource_uri, body_uri)
398
+ annotation = create_annotation_stub_rdf(ro_uri, resource_uri, body_uri)
399
+ code, reason, headers, data = do_request("POST", ro_uri,
400
+ :ctype => "application/vnd.wf4ever.annotation",
401
+ :body => annotation)
402
+ if code != 201
403
+ error(code, "Error creating annotation #{code}, #{reason}, #{resource_uri}")
404
+ end
405
+ [code, reason, headers["location"]]
406
+ end
407
+
408
+ ##
409
+ # Create internal annotation
410
+ #
411
+ # Returns: [code, reason, annotation_uri, body_uri]
412
+ def create_internal_annotation(ro_uri, resource_uri, annotation_graph)
413
+ code, reason, headers, data = do_request("POST", ro_uri,
414
+ :ctype => "application/rdf+xml",
415
+ :body => annotation_graph.serialize(format=:xml),
416
+ :link => "<#{resource_uri}>; rel=\"#{RDF::AO.annotatesResource}\"")
417
+ if code != 201
418
+ error(code, "Error creating annotation #{code}, #{reason}, #{resource_uri}")
419
+ end
420
+ [code, reason, headers["location"], parse_links(headers)[RDF::AO.body.to_s].first]
421
+ end
422
+
423
+ ##
424
+ # Create a resource annotation using an existing (possibly external) annotation body
425
+ #
426
+ # Returns: (code, reason, annotation_uri)
427
+ def create_external_annotation(ro_uri, resource_uri, body_uri)
428
+ create_annotation_stub(ro_uri, resource_uri, body_uri)
429
+ end
430
+
431
+ ##
432
+ # Update an indicated annotation for supplied resource using indicated body
433
+ #
434
+ # Returns: [code, reason]
435
+ def update_annotation_stub(ro_uri, stuburi, resource_uri, body_uri)
436
+ annotation = create_annotation_stub_rdf(ro_uri, resource_uri, body_uri)
437
+ code, reason, headers, data = do_request("PUT", stuburi,
438
+ :ctype => "application/vnd.wf4ever.annotation",
439
+ :body => annotation)
440
+ if code != 200
441
+ error(code, "Error updating annotation #{code}, #{reason}, #{resource_uri}")
442
+ end
443
+ [code, reason]
444
+ end
445
+
446
+ ##
447
+ # Update an annotation with a new internal annotation body
448
+ #
449
+ # returns: [code, reason, body_uri]
450
+ def update_internal_annotation(ro_uri, stuburi, resource_uri, annotation_graph)
451
+ code, reason, body_uri = create_annotation_body(ro_uri, annotation_graph)
452
+ if code != 201
453
+ error(code, "Error creating annotation #{code}, #{reason}, #{resource_uri}")
454
+ end
455
+ code, reason = update_annotation_stub(ro_uri, stuburi, resource_uri, body_uri)
456
+ [code, reason, body_uri]
457
+ end
458
+
459
+ ##
460
+ # Update an annotation with an existing (possibly external) annotation body
461
+ #
462
+ # returns: (code, reason)
463
+ def update_external_annotation(ro_uri, annotation_uri, resource_uri, body_uri)
464
+ update_annotation_stub(ro_uri, annotation_uri, resource_uri, body_uri)
465
+ end
466
+
467
+ ##
468
+ # Enumerate annnotation URIs associated with a resource
469
+ # (or all annotations for an RO)
470
+ #
471
+ # Returns an array of annotation URIs
472
+ def get_annotation_stub_uris(ro_uri, resource_uri=nil)
473
+ manifesturi, manifest = get_manifest(ro_uri)
474
+ stuburis = []
475
+ manifest.query(:object => RDF::URI(resource_uri)) do |stmt|
476
+ if [RDF::AO.annotatesResource,RDF::RO.annotatesAggregatedResource].include?(stmt.predicate)
477
+ stuburis << stmt.subject
478
+ end
479
+ end
480
+ stuburis
481
+ end
482
+
483
+ ##
484
+ # Enumerate annnotation body URIs associated with a resource
485
+ # (or all annotations for an RO)
486
+ #
487
+ # Returns an array of annotation body URIs
488
+ def get_annotation_body_uris(ro_uri, resource_uri=nil)
489
+ manifesturi, manifest = get_manifest(ro_uri)
490
+ body_uris = []
491
+
492
+ query1 = RDF::Query.new do
493
+ pattern [:annotation_uri, RDF::AO.annotatesResource, RDF::URI(resource_uri)]
494
+ pattern [:annotation_uri, RDF::AO.body, :body_uri]
495
+ end
496
+
497
+ query2 = RDF::Query.new do
498
+ pattern [:annotation_uri, RDF::RO.annotatesAggregatedResource, RDF::URI(resource_uri)]
499
+ pattern [:annotation_uri, RDF::AO.body, :body_uri]
500
+ end
501
+
502
+ manifest.query(query1) do |result|
503
+ body_uris << result.body_uri.to_s
504
+ end
505
+
506
+ manifest.query(query2) do |result|
507
+ body_uris << result.body_uri.to_s
508
+ end
509
+
510
+ body_uris.uniq
511
+ end
512
+
513
+ ##
514
+ # Build RDF graph of all annnotations associated with a resource
515
+ # (or all annotations for an RO)
516
+ #
517
+ # Returns graph of merged annotations
518
+ def get_annotation_graph(ro_uri, resource_uri=nil)
519
+ annotation_graph = ROSRS::RDFGraph.new
520
+ get_annotation_body_uris(ro_uri, resource_uri).each do |auri|
521
+ code, reason, headers, buri, bodytext = do_request_follow_redirect("GET", auri, {})
522
+ if code == 200
523
+ content_type = headers['content-type'].split(';', 2)[0].strip.downcase
524
+ if ANNOTATION_CONTENT_TYPES.include?(content_type)
525
+ bodyformat = ANNOTATION_CONTENT_TYPES[content_type]
526
+ annotation_graph.load_data(bodytext, bodyformat)
527
+ else
528
+ warn("Warning: #{buri} has unrecognized content-type: #{content_type}")
529
+ end
530
+ else
531
+ error(code, "Failed to GET #{buri}: #{code} #{reason}")
532
+ end
533
+ end
534
+ annotation_graph
535
+ end
536
+
537
+ ##
538
+ # Retrieve annotation for given annotation URI
539
+ #
540
+ # Returns: [code, reason, uri, annotation_graph]
541
+ def get_annotation(annotation_uri)
542
+ code, reason, headers, uri, annotation_graph = get_resource(annotation_uri)
543
+ [code, reason, uri, annotation_graph]
544
+ end
545
+
546
+ ##
547
+ # Remove annotation at given annotation URI
548
+ #
549
+ # Returns: (code, reason)
550
+ def remove_annotation(annotation_uri)
551
+ code, reason = do_request("DELETE", annotation_uri)
552
+ if code == 204
553
+ [code, reason]
554
+ else
555
+ error(code, "Failed to DELETE annotation #{annotation_uri}: #{code} #{reason}")
556
+ end
557
+ end
558
+
559
+ # -----------------------
560
+ # Folders
561
+ # -----------------------
562
+
563
+ ##
564
+ # Returns [code, reason, headers, uri, folder_contents]
565
+ def get_folder(folder_uri)
566
+ code, reason, headers, uri, folder_contents = do_request_rdf("GET", folder_uri,
567
+ :accept => 'application/vnd.wf4ever.folder')
568
+ if code != 200
569
+ error(code, reason)
570
+ end
571
+
572
+ [code, reason, headers, uri, folder_contents]
573
+ end
574
+
575
+ ##
576
+ # +contents+ is an Array containing Hash elements, which must consist of a :uri and an optional :name.
577
+ # Example:
578
+ # folder_contents = [{:name => 'test_data.txt', :uri => 'http://www.example.com/ro/file1.txt'},
579
+ # {:uri => 'http://www.myexperiment.org/workflows/7'}]
580
+ # create_folder('ros/new_ro/', 'example_data', folder_contents)
581
+ #
582
+ # Returns [code, reason, uri, proxy_uri, folder_description_graph]
583
+ def create_folder(ro_uri, name, contents = [])
584
+ name << "/" unless name[-1] == "/" # Need trailing slash on folders...
585
+ code, reason, headers, uripath, folder_description = do_request_rdf("POST", ro_uri,
586
+ :body => create_folder_description(contents),
587
+ :headers => {"Slug" => name,
588
+ "Content-Type" => 'application/vnd.wf4ever.folder',},
589
+ :accept => 'application/vnd.wf4ever.folder')
590
+
591
+ if code == 201
592
+ uri = parse_links(headers)[RDF::ORE.proxyFor.to_s].first
593
+ [code, reason, uri, headers["location"], folder_description]
594
+ else
595
+ error(code, "Error creating folder: #{code} #{reason}")
596
+ end
597
+ end
598
+
599
+ def add_folder_entry(folder_uri, resource_uri, resource_name = nil)
600
+ code, reason, headers, body= do_request("POST", folder_uri,
601
+ :body => create_folder_entry_description(resource_uri, resource_name),
602
+ :headers => {"Content-Type" => 'application/vnd.wf4ever.folderentry',})
603
+ if code == 201
604
+ [code, reason, headers["Location"], parse_links(headers)[RDF::ORE.proxyFor.to_s].first]
605
+ else
606
+ error(code, "Error adding resource to folder: #{code} #{reason}")
607
+ end
608
+ end
609
+
610
+ #--------
611
+
612
+ def delete_resource(resource_uri)
613
+ code, reason = do_request_follow_redirect("DELETE", resource_uri)
614
+ error(code, "Error deleting resource #{resource_uri}: #{code} #{reason}") unless [204,404].include?(code)
615
+ [code, reason]
616
+ end
617
+
618
+ private
619
+
620
+ def parseable?(content_type)
621
+ PARSEABLE_CONTENT_TYPES.include?(content_type.downcase)
622
+ end
623
+
624
+ ##
625
+ # Takes +contents+, an Array containing Hash elements, which must consist of a :uri and an optional :name,
626
+ # and returns an RDF description of the folder contents.
627
+ def create_folder_description(contents)
628
+ body = %(
629
+ <rdf:RDF
630
+ xmlns:ore="#{RDF::ORE.to_uri.to_s}"
631
+ xmlns:rdf="#{RDF.to_uri.to_s}"
632
+ xmlns:ro="#{RDF::RO.to_uri.to_s}" >
633
+ <ro:Folder>
634
+ #{contents.collect {|r| "<ore:aggregates rdf:resource=\"#{r[:uri]}\" />" }.join("\n")}
635
+ </ro:Folder>
636
+ )
637
+ contents.each do |r|
638
+ if r[:name]
639
+ body << create_folder_entry_body(r[:uri], r[:name])
640
+ end
641
+ end
642
+ body << %(
643
+ </rdf:RDF>
644
+ )
645
+
646
+ body
647
+ end
648
+
649
+ def create_folder_entry_description(uri, name = nil)
650
+ %(
651
+ <rdf:RDF
652
+ xmlns:ore="#{RDF::ORE.to_uri.to_s}"
653
+ xmlns:rdf="#{RDF.to_uri.to_s}"
654
+ xmlns:ro="#{RDF::RO.to_uri.to_s}" >
655
+ #{create_folder_entry_body(uri, name)}
656
+ </rdf:RDF>
657
+ )
658
+ end
659
+
660
+ def create_folder_entry_body(uri, name = nil)
661
+ body = %(
662
+ <ro:FolderEntry>
663
+ )
664
+ body << "<ro:entryName>#{name}</ro:entryName>" if name
665
+ body << %(<ore:proxyFor rdf:resource="#{uri}" />
666
+ </ro:FolderEntry>
667
+ )
668
+ body
669
+ end
670
+
671
+ end
672
+ end
@@ -0,0 +1,20 @@
1
+ require 'net/http'
2
+ require 'logger'
3
+
4
+ require 'rubygems'
5
+ require 'json'
6
+ require 'rdf'
7
+ require 'rdf/raptor'
8
+
9
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'namespaces'))
10
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'rdf_graph'))
11
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'session'))
12
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'helper'))
13
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'exceptions'))
14
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'research_object'))
15
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'annotation'))
16
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'resource'))
17
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'folder'))
18
+ require File.expand_path(File.join(File.dirname(__FILE__), 'rosrs', 'folder_entry'))
19
+
20
+
data/test/helper.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'wf4ever/rosrs_client'
15
+
16
+ class Test::Unit::TestCase
17
+ end