wf4ever-rosrs-client 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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