wf4ever-rosrs-client 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +20 -0
- data/README.md +180 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/lib/wf4ever/rosrs/annotation.rb +76 -0
- data/lib/wf4ever/rosrs/exceptions.rb +19 -0
- data/lib/wf4ever/rosrs/folder.rb +127 -0
- data/lib/wf4ever/rosrs/folder_entry.rb +44 -0
- data/lib/wf4ever/rosrs/helper.rb +12 -0
- data/lib/wf4ever/rosrs/namespaces.rb +42 -0
- data/lib/wf4ever/rosrs/rdf_graph.rb +72 -0
- data/lib/wf4ever/rosrs/research_object.rb +233 -0
- data/lib/wf4ever/rosrs/resource.rb +59 -0
- data/lib/wf4ever/rosrs/session.rb +672 -0
- data/lib/wf4ever/rosrs_client.rb +20 -0
- data/test/helper.rb +17 -0
- data/test/test_abstract_interaction.rb +200 -0
- data/test/test_rosrs_session.rb +393 -0
- metadata +166 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create use 1.9.3@rosrs_client
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem "json"
|
4
|
+
gem "rdf"
|
5
|
+
gem "rdf-raptor"
|
6
|
+
|
7
|
+
# Add dependencies to develop your gem here.
|
8
|
+
# Include everything needed to run rake, tests, features, etc.
|
9
|
+
group :development do
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1.2.1"
|
12
|
+
gem "jeweler", "~> 1.8.4"
|
13
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Finn Bacall
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
ROSRSClient
|
2
|
+
=================
|
3
|
+
|
4
|
+
Partial port of Python ROSRS_Session code to Ruby.
|
5
|
+
|
6
|
+
[This project [8]][ref8] is intended to provide a Ruby-callable API for [myExperiment [9]][ref9] to access [Research Objects [1]][ref1], [[2]][ref2] stored in RODL, using the [ROSRS API [3]][ref3]. The functions provided closely follow the ROSRS API specification. The code is based on an implementation in Python used by the RO_Manager utility; a full implementat ROSRS test suite can be found in the [GitHub `wf4ever/ro-manager` project [7]][ref7].
|
7
|
+
|
8
|
+
[ref1]: http://www.wf4ever-project.org/wiki/pages/viewpage.action?pageId=2065079 "What is an RO?"
|
9
|
+
|
10
|
+
[ref2]: http://wf4ever.github.com/ro/ "Wf4Ever Research Object Model"
|
11
|
+
|
12
|
+
[ref3]: http://www.wf4ever-project.org/wiki/display/docs/RO+SRS+interface+6 "ROSRS interface (v6)"
|
13
|
+
|
14
|
+
[ref7]: https://github.com/wf4ever/ro-manager/blob/master/src/rocommand/ROSRS_Session.py "Python ROSRS_Session in RO Manager. See also test suite: https://github.com/wf4ever/ro-manager/blob/master/src/rocommand/test/TestROSRS_Session.py"
|
15
|
+
|
16
|
+
[ref8]: https://github.com/gklyne/ruby-http-session "ruby-http-session project, or successor"
|
17
|
+
|
18
|
+
[ref9]: http://www.myexperiment.org/ "De Roure, D., Goble, C. and Stevens, R. (2009) The Design and Realisation of the myExperiment Virtual Research Environment for Social Sharing of Workflows. Future Generation Computer Systems 25, pp. 561-567. doi:10.1016/j.future.2008.06.010"
|
19
|
+
|
20
|
+
|
21
|
+
## Contents
|
22
|
+
|
23
|
+
* Contents
|
24
|
+
* Package structure
|
25
|
+
* API calling conventions
|
26
|
+
* A simple example
|
27
|
+
* Development setup
|
28
|
+
* URIs
|
29
|
+
* Further work
|
30
|
+
* References
|
31
|
+
|
32
|
+
|
33
|
+
## Package structure
|
34
|
+
|
35
|
+
Key functions are currently contained in four files:
|
36
|
+
|
37
|
+
* `lib/wf4ever/rosrs_session.rb`
|
38
|
+
* `lib/wf4ever/rdf_graph.rb`
|
39
|
+
* `lib/wf4ever/namespaces.rb`
|
40
|
+
* `lib/wf4ever/folders.rb`
|
41
|
+
* `test/test_rosrs_session`
|
42
|
+
|
43
|
+
The main functions provided by this package are in `rosrs_session`. This module provides a class whose instances manage a session with a specified ROSRS service endpoint. A service URI is provided when an instance is created, and is used as a base URI for accessing ROs and other resources using relative URI references. Any attempt to access a resource on a different host or post is rejected.
|
44
|
+
|
45
|
+
`rdf_graph` implements a simplified interface to the [Ruby RDF library [4]][ref4], handling parsing of RDF from strings, serialization to strings and simplified search and access to individual triples. Most of the functions provided are quite trivial; the module is intended to provide (a) a distillation of knowledge about how to perform desired functions using the RDF and associated libraries, and (b) a shim layer for adapting between different conventions used by the RDF libraries and the `rosrs_session` library. The [Raptor library [5]][ref5] and its [Ruby RDF interface[6]][ref6] are used for RDF/XML parsing and serialization.
|
46
|
+
|
47
|
+
[ref4]: http://rdf.rubyforge.org/ "RDF.rb: Linked Data for Ruby"
|
48
|
+
|
49
|
+
[ref5]: http://librdf.org/raptor/ "Raptor RDF Syntax Library"
|
50
|
+
|
51
|
+
[ref6]: http://rdf.rubyforge.org/raptor/ "Raptor RDF Parser Plugin for RDF.rb"
|
52
|
+
|
53
|
+
`namespaces` provides definitions of URIs for namespaces and namespace terms used in RDF graphs. These are in similar form to the namespaces provided by the RDF library.
|
54
|
+
|
55
|
+
`folders` contains objects to represent and traverse RO's folder structure.
|
56
|
+
|
57
|
+
`test_rosrs_session` is a test suite for all the above. It serves to provide regression testing for implemented functions, and also to provide examples of how the various ROSRS API functions provided can be accessed.
|
58
|
+
|
59
|
+
|
60
|
+
## API calling conventions
|
61
|
+
|
62
|
+
Many API functions have a small number of mandatory parameters which are provided as normal positional parameters, and a (possibly larger) number of optional keyword parameters that are provided as a Ruby hash. The Ruby calling convention of collecting multiple `key => value` parameter expressions into a single has is used.
|
63
|
+
|
64
|
+
Return values are generally in the form of an array, which can be used with parallel assignment for easy access to the return values.
|
65
|
+
|
66
|
+
Example:
|
67
|
+
|
68
|
+
code, reason, headers, body = rosrs.do_request("POST", rouri,
|
69
|
+
:body => data
|
70
|
+
:ctype => "text/plain"
|
71
|
+
:accept => "application/rdf+xml"
|
72
|
+
:headers => reqheaders)
|
73
|
+
|
74
|
+
|
75
|
+
## A simple example
|
76
|
+
|
77
|
+
Here is a flavour of how the `rosrs_session` module may be used:
|
78
|
+
|
79
|
+
# Create an ROSRS session
|
80
|
+
rosrs = ROSRSSession.new(
|
81
|
+
"http://sandbox.wf4ever-project.org/rodl/ROs/",
|
82
|
+
"47d5423c-b507-4e1c-8")
|
83
|
+
|
84
|
+
# Create a new RO
|
85
|
+
code, reason, rouri, manifest = @rosrs.create_research_object("Test-RO-name")
|
86
|
+
if code != 201
|
87
|
+
raise "Failed to create new RO: "+reason
|
88
|
+
end
|
89
|
+
|
90
|
+
# Aggregate a resource into the new RO
|
91
|
+
res_body = %q(
|
92
|
+
New resource body
|
93
|
+
)
|
94
|
+
options = { :body => res_body, :ctype => "text/plain" }
|
95
|
+
|
96
|
+
# Create and aggregate "internal" resource in new RO
|
97
|
+
code, reason, proxyuri, resourceuri = rosrs.aggregate_internal_resource(
|
98
|
+
rouri, "data/test_resource",
|
99
|
+
:body => res_body,
|
100
|
+
:ctype => "text/plain")
|
101
|
+
if code != 201
|
102
|
+
raise "Failed to create new resource: "+reason
|
103
|
+
|
104
|
+
# Create a new folder
|
105
|
+
folder_contents = [{:name => 'test_data.txt', :uri => 'http://www.example.com/ro/file1.txt'},
|
106
|
+
{:uri => 'http://www.myexperiment.org/workflows/7'}]
|
107
|
+
folder_uri = rosrs.create_folder(rouri, "Input Data", folder_contents).uri
|
108
|
+
|
109
|
+
# Examine a folder
|
110
|
+
folder = rosrs.get_folder(folder_uri)
|
111
|
+
puts folder.name
|
112
|
+
puts folder.contents.inspect
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
# When finished, close session
|
118
|
+
rosrs.close
|
119
|
+
|
120
|
+
|
121
|
+
## Development setup
|
122
|
+
|
123
|
+
Development has been performed using Ruby 1.8.7 on Ubuntu Linux 10.04 and 12.04. The code uses `rubygems`, `json`, `rdf` and `rdf-raptor` libraries beyond the standard Ruby libraries.
|
124
|
+
|
125
|
+
The `rdf-raptor` Ruby library uses the Ubuntu `raptor-util` and `libraptor-dev` packages. NOTE: the Ruby RDF documentation does not mention `libraptor-dev`, but I found that without this the RDF libraries would not work for parsing and serializing RDF/XML.
|
126
|
+
|
127
|
+
Once the environment is set up, I find the following statements are sufficient include the required libraries:
|
128
|
+
|
129
|
+
require "wf4ever/rosrs_client"
|
130
|
+
|
131
|
+
## URIs
|
132
|
+
|
133
|
+
Be aware that the standard Ruby library provides a URI class, and that the RDF library provides a different, incompatible URI class:
|
134
|
+
|
135
|
+
# Standard Ruby library URI:
|
136
|
+
uri1 = URI("http://example.com/")
|
137
|
+
|
138
|
+
# URI class used by RDF library:
|
139
|
+
uri2 = RDF::URI("http://example.com")
|
140
|
+
|
141
|
+
These URIs are not equivalent, and are not even directly comparable.
|
142
|
+
|
143
|
+
Currently, the HTTP handling code uses the standard Ruby library URIs, and the RDF handling code uses URIs provided by the RDF library. The `namespaces` module returns `RDF::URI` values.
|
144
|
+
|
145
|
+
I'm not currently sure if this will prove to cause problems. Take care when dereferencing URIs obtained from RDF.
|
146
|
+
|
147
|
+
## Further work
|
148
|
+
|
149
|
+
At the time of writing this, the code is very much a work in progress. Some of the things possibly yet to-do include:
|
150
|
+
|
151
|
+
* Fork project into the wf4ever organization. Rename to rosrs_session.
|
152
|
+
* Complete the APi functions
|
153
|
+
* Work out strategy for dealing with different URI classes.
|
154
|
+
* When creating an RO, use the supplied RO information to create some initial annotations (similar to RO Manager)?
|
155
|
+
* Refactor `rosrs_session.rb` to separate out `http_session`
|
156
|
+
* May want to investigate "streaming" RDF data between HTTP and RDF libraries, or using RDF reader/writer classes, rather than transferring via strings. Currently, I assume the RDF is small enough that this doesn't matter.
|
157
|
+
* Refactor test suite per tested module (may require simple HTTP server setup if HTTP factored out as above)
|
158
|
+
|
159
|
+
|
160
|
+
## References
|
161
|
+
|
162
|
+
[[1] _What is an RO?_][ref1]; Wf4Ever Research Object description and notes.
|
163
|
+
|
164
|
+
[[2] _Wf4Ever Research Object Model_][ref2]; Specification of RO model.
|
165
|
+
|
166
|
+
[[3] _Wf4ever ROSRS interface (v6)_][ref3]; Description of the HTTP/REST interface for accessing and updating Research Objects, implemented by Wf4Ever RODL.
|
167
|
+
|
168
|
+
[[4] `RDF.rb`][ref4]; Linked Data for Ruby
|
169
|
+
|
170
|
+
[[5] _Raptor_][ref5]; Raptor RDF Syntax Library
|
171
|
+
|
172
|
+
[[6] `rdf_raptor`][ref6]; Raptor RDF Parser Plugin for RDF.rb
|
173
|
+
|
174
|
+
[[7] _Python ROSRS\_Session in RO Manager_][ref7]; See also the test suite: [TestROSRS_Session.py ](https://github.com/wf4ever/ro-manager/blob/master/src/rocommand/test/TestROSRS_Session.py).
|
175
|
+
|
176
|
+
[[8] `ruby-http-session` project, or successor][ref8]
|
177
|
+
|
178
|
+
[[9] _myExperiment_][ref9]; "De Roure, D., Goble, C. and Stevens, R. (2009) The Design and Realisation of the myExperiment Virtual Research Environment for Social Sharing of Workflows. Future Generation Computer Systems 25, pp. 561-567. doi:10.1016/j.future.2008.06.010"
|
179
|
+
|
180
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "wf4ever-rosrs-client"
|
18
|
+
gem.homepage = "http://github.com/fbacall/rosrs_client"
|
19
|
+
gem.summary = %Q{A client to interact with the ROSRS API.}
|
20
|
+
gem.description = %Q{A port of the ROSRS API Python client into Ruby.}
|
21
|
+
gem.email = "finn.bacall@cs.man.ac.uk"
|
22
|
+
gem.authors = ["Graham Klyne","Finn Bacall"]
|
23
|
+
# dependencies defined in Gemfile
|
24
|
+
end
|
25
|
+
Jeweler::RubygemsDotOrgTasks.new
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :test
|
35
|
+
|
36
|
+
require 'rdoc/task'
|
37
|
+
Rake::RDocTask.new do |rdoc|
|
38
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
39
|
+
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = "rosrs_client #{version}"
|
42
|
+
rdoc.rdoc_files.include('README*')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ROSRS
|
2
|
+
class Annotation
|
3
|
+
|
4
|
+
attr_reader :uri, :body_uri, :resource_uri, :created_at, :created_by, :research_object
|
5
|
+
|
6
|
+
def initialize(research_object, uri, body_uri, resource_uri, options = {})
|
7
|
+
@research_object = research_object
|
8
|
+
@session = @research_object.session
|
9
|
+
@uri = uri
|
10
|
+
@body_uri = body_uri
|
11
|
+
@resource_uri = resource_uri
|
12
|
+
@created_at = options[:created_at]
|
13
|
+
@created_by = options[:created_by]
|
14
|
+
@loaded = false
|
15
|
+
if options[:body]
|
16
|
+
@body = options[:body]
|
17
|
+
@loaded = true
|
18
|
+
end
|
19
|
+
load if options[:load]
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# The resource which this annotation relates to
|
24
|
+
def resource
|
25
|
+
@resource ||= @research_object.resources(@resource_uri) ||
|
26
|
+
@research_object.folders(@resource_uri) ||
|
27
|
+
ROSRS::Resource.new(@research_object, @resource_uri)
|
28
|
+
end
|
29
|
+
|
30
|
+
def loaded?
|
31
|
+
@loaded
|
32
|
+
end
|
33
|
+
|
34
|
+
def load
|
35
|
+
c,r,u,@body = @session.get_annotation(body_uri)
|
36
|
+
@loaded = true
|
37
|
+
end
|
38
|
+
|
39
|
+
def body
|
40
|
+
load unless loaded?
|
41
|
+
@body
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete
|
45
|
+
code = @session.remove_annotation(uri)
|
46
|
+
@loaded = false
|
47
|
+
@research_object.remove_annotation(self)
|
48
|
+
code == 204
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.create(ro, resource_uri, annotation)
|
52
|
+
if ROSRS::Helper.is_uri?(annotation)
|
53
|
+
code, reason, annotation_uri = ro.session.create_annotation_stub(ro.uri, resource_uri, annotation)
|
54
|
+
self.new(ro, annotation_uri, annotation, resource_uri)
|
55
|
+
else
|
56
|
+
code, reason, annotation_uri, body_uri = ro.session.create_internal_annotation(ro.uri, resource_uri, annotation)
|
57
|
+
self.new(ro, annotation_uri, body_uri, resource_uri, :body => annotation)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def update(resource_uri, annotation)
|
62
|
+
if ROSRS::Helper.is_uri?(annotation)
|
63
|
+
code, reason = @session.update_annotation_stub(@research_object.uri, @uri, resource_uri, body_uri)
|
64
|
+
@loaded = false
|
65
|
+
else
|
66
|
+
code, reason, body_uri = @session.update_internal_annotation(@research_object.uri, @uri, resource_uri, annotation)
|
67
|
+
@loaded = true
|
68
|
+
@body = annotation
|
69
|
+
end
|
70
|
+
@resource_uri = resource_uri
|
71
|
+
@body_uri = body_uri
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Exception class used to signal HTTP Session errors
|
2
|
+
module ROSRS
|
3
|
+
|
4
|
+
class Exception < Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
class NotFoundException < Exception
|
8
|
+
end
|
9
|
+
|
10
|
+
class ForbiddenException < Exception
|
11
|
+
end
|
12
|
+
|
13
|
+
class UnauthorizedException < Exception
|
14
|
+
end
|
15
|
+
|
16
|
+
class ConflictException < Exception
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module ROSRS
|
2
|
+
|
3
|
+
# A representation of a folder in a Research Object.
|
4
|
+
class Folder < Resource
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
##
|
9
|
+
# +research_object+:: The Wf4Ever::ResearchObject that aggregates this folder.
|
10
|
+
# +uri+:: The URI for the resource referred to by the Folder.
|
11
|
+
# +options+:: A hash of options:
|
12
|
+
# [:contents_graph] An RDFGraph of the folder contents, if on hand (to save having to make a request).
|
13
|
+
# [:root_folder] A boolean flag to say if this folder is the root folder of the RO.
|
14
|
+
def initialize(research_object, uri, proxy_uri = nil, options = {})
|
15
|
+
super(research_object, uri, proxy_uri)
|
16
|
+
@name = uri.to_s.split('/').last
|
17
|
+
@session = research_object.session
|
18
|
+
@loaded = false
|
19
|
+
if options[:contents_graph]
|
20
|
+
@contents = parse_folder_description(options[:contents_graph])
|
21
|
+
@loaded = true
|
22
|
+
end
|
23
|
+
@root_folder = options[:root_folder]
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Fetch the entry with name +child_name+ from the Folder's contents
|
28
|
+
def child(child_name)
|
29
|
+
contents.select {|child| child.name == child_name}.first
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Returns boolean stating whether or not a description of this Folder's contents has been fetched and loaded.
|
34
|
+
def loaded?
|
35
|
+
@loaded
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Returns whether or not this folder is the root folder of the RO
|
40
|
+
def root?
|
41
|
+
@root_folder
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Returns an array of FolderEntry objects
|
46
|
+
def contents
|
47
|
+
load unless loaded?
|
48
|
+
@contents
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Fetch and parse the Folder's description to get the Folder's contents.
|
53
|
+
def load
|
54
|
+
@contents = fetch_folder_contents
|
55
|
+
@loaded = true
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Delete this folder from the RO. Contents will be preserved.
|
60
|
+
# Also deletes any entries in other folders pointing to this one.
|
61
|
+
def delete
|
62
|
+
code = @session.delete_folder(@uri)[0]
|
63
|
+
@loaded = false
|
64
|
+
@research_object.remove_folder(self)
|
65
|
+
code == 204
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Add an entry to the folder. +resource+ can be a ROSRS::Resource object or a URI
|
70
|
+
def add(resource, entry_name = nil)
|
71
|
+
if resource.instance_of?(ROSRS::Resource)
|
72
|
+
contents << ROSRS::FolderEntry.create(self, entry_name, resource.uri)
|
73
|
+
else
|
74
|
+
contents << ROSRS::FolderEntry.create(self, entry_name, resource)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Remove an entry from the folder.
|
81
|
+
def remove(entry)
|
82
|
+
entry.delete
|
83
|
+
contents.delete(entry)
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Create a folder in the RO and add it to this folder as a subfolder
|
88
|
+
def create_folder(name)
|
89
|
+
# Makes folder name parent/child instead of just child
|
90
|
+
folder_name = (URI(uri) + URI(name) - URI(@research_object.uri)).to_s
|
91
|
+
folder = @research_object.create_folder(folder_name)
|
92
|
+
add(folder.uri, name)
|
93
|
+
folder
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.create(ro, name, contents = [])
|
97
|
+
code, reason, uri, proxy_uri, folder_description = ro.session.create_folder(ro.uri, name, contents)
|
98
|
+
self.new(ro, uri, proxy_uri, :contents_graph => folder_description)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Get folder contents from remote resource map file
|
104
|
+
def fetch_folder_contents
|
105
|
+
code, reason, headers, uripath, graph = @session.get_folder(uri)
|
106
|
+
parse_folder_description(graph)
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_folder_description(graph)
|
110
|
+
contents = []
|
111
|
+
|
112
|
+
query = RDF::Query.new do
|
113
|
+
pattern [:folder_entry, RDF.type, RDF::RO.FolderEntry]
|
114
|
+
pattern [:folder_entry, RDF::RO.entryName, :name]
|
115
|
+
pattern [:folder_entry, RDF::ORE.proxyFor, :target]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Create instances for each item.
|
119
|
+
graph.query(query).each do |result|
|
120
|
+
contents << ROSRS::FolderEntry.new(self, result.name.to_s, result.folder_entry.to_s, result.target.to_s)
|
121
|
+
end
|
122
|
+
|
123
|
+
contents
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ROSRS
|
2
|
+
|
3
|
+
# An item within a folder.
|
4
|
+
|
5
|
+
class FolderEntry
|
6
|
+
|
7
|
+
attr_reader :parent_folder, :name, :uri
|
8
|
+
|
9
|
+
##
|
10
|
+
# +parent_folder+:: The ROSRS::Folder object in which this entry resides..
|
11
|
+
# +name+:: The display name of the ROSRS::FolderEntry.
|
12
|
+
# +uri+:: The URI of this ROSRS::FolderEntry
|
13
|
+
# +resource_uri+:: The URI for the resource referred to by this ROSRS::FolderEntry.
|
14
|
+
def initialize(parent_folder, name, uri, resource_uri)
|
15
|
+
@uri = uri
|
16
|
+
@name = name
|
17
|
+
@resource_uri = resource_uri
|
18
|
+
@parent_folder = parent_folder
|
19
|
+
@session = @parent_folder.research_object.session
|
20
|
+
end
|
21
|
+
|
22
|
+
# The resource that this entry points to. Lazily loaded.
|
23
|
+
def resource
|
24
|
+
@resource ||= @parent_folder.research_object.resources(@resource_uri) ||
|
25
|
+
@parent_folder.research_object.folders(@resource_uri) ||
|
26
|
+
ROSRS::Resource.new(@parent_folder.research_object, @resource_uri)
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Removes this folder entry from the folder. Does not delete the resource.
|
31
|
+
def delete
|
32
|
+
code = @session.delete_resource(@uri)[0]
|
33
|
+
@loaded = false
|
34
|
+
code == 204
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.create(parent_folder, name, resource_uri)
|
38
|
+
code, reason, uri = parent_folder.research_object.session.add_folder_entry(parent_folder.uri, resource_uri, name)
|
39
|
+
self.new(parent_folder, name, uri, resource_uri)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#:nodoc: all
|
2
|
+
|
3
|
+
module RDF
|
4
|
+
|
5
|
+
class AO < Vocabulary("http://purl.org/ao/")
|
6
|
+
# Declaring these might not be necessary
|
7
|
+
property :Annotation
|
8
|
+
property :body
|
9
|
+
property :annotatesResource
|
10
|
+
end
|
11
|
+
|
12
|
+
class ORE < Vocabulary("http://www.openarchives.org/ore/terms/")
|
13
|
+
property :Aggregation
|
14
|
+
property :AggregatedResource
|
15
|
+
property :Proxy
|
16
|
+
property :aggregates
|
17
|
+
property :proxyFor
|
18
|
+
property :proxyIn
|
19
|
+
property :isDescribedBy
|
20
|
+
end
|
21
|
+
|
22
|
+
class RO < Vocabulary("http://purl.org/wf4ever/ro#")
|
23
|
+
property :ResearchObject
|
24
|
+
property :AggregatedAnnotation
|
25
|
+
property :annotatesAggregatedResource
|
26
|
+
property :FolderEntry
|
27
|
+
property :Folder
|
28
|
+
property :Resource
|
29
|
+
property :entryName
|
30
|
+
end
|
31
|
+
|
32
|
+
class ROEVO < Vocabulary("http://purl.org/wf4ever/roevo#")
|
33
|
+
property :LiveRO
|
34
|
+
end
|
35
|
+
|
36
|
+
class ROTERMS < Vocabulary("http://ro.example.org/ro/terms/")
|
37
|
+
property :note
|
38
|
+
property :resource
|
39
|
+
property :defaultBase
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Shim class for RDF graph parsing, serialization, access
|
2
|
+
module ROSRS
|
3
|
+
class RDFGraph
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
# options: :uri => (URI to load),
|
7
|
+
# :data => (string to load)
|
8
|
+
# :format => (format of data)
|
9
|
+
@format = :rdfxml
|
10
|
+
@graph = RDF::Graph.new
|
11
|
+
if options[:uri]
|
12
|
+
load_resource(options[:uri], options[:format])
|
13
|
+
end
|
14
|
+
if options[:data]
|
15
|
+
load_data(options[:data], options[:format])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_data(data, format=nil)
|
20
|
+
@graph << RDF::Reader.for(map_format(format)).new(data)
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_resource(uri, format=nil)
|
24
|
+
raise NotImplementedError.new("Attempt to initialize RDFGraph from web resource: #{uri}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def serialize(format=nil)
|
28
|
+
return RDF::Writer.for(map_format(format)).buffer { |w| w << @graph }
|
29
|
+
end
|
30
|
+
|
31
|
+
# other methods here
|
32
|
+
|
33
|
+
def has_statement?(stmt)
|
34
|
+
return @graph.has_statement?(stmt)
|
35
|
+
end
|
36
|
+
|
37
|
+
def each_statement(&block)
|
38
|
+
@graph.each_statement(&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def query(pattern, &block)
|
42
|
+
@graph.query(pattern, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def match?(pattern)
|
46
|
+
pattern_found = false
|
47
|
+
@graph.query(pattern) { |s| pattern_found = true }
|
48
|
+
return pattern_found
|
49
|
+
end
|
50
|
+
|
51
|
+
# Private helpers
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def map_format(format=nil)
|
56
|
+
rdf_format = :rdfxml # default
|
57
|
+
if format
|
58
|
+
if format == :xml
|
59
|
+
rdf_format = :rdfxml
|
60
|
+
elsif format == :ntriples
|
61
|
+
rdf_format = :ntriples
|
62
|
+
elsif format == :turtle
|
63
|
+
rdf_format = :turtle
|
64
|
+
else
|
65
|
+
raise ArgumentError.new("Unrecognized RDF format: #{format}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
return rdf_format
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|