rubydora 0.0.2

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,267 @@
1
+ module Rubydora
2
+
3
+ # Provide low-level access to the Fedora Commons REST API
4
+ module RestApiClient
5
+ # Fedora API documentation available at {https://wiki.duraspace.org/display/FCR30/REST+API}
6
+ API_DOCUMENTATION = 'https://wiki.duraspace.org/display/FCR30/REST+API'
7
+ # Create an authorized HTTP client for the Fedora REST API
8
+ # @param [Hash] config
9
+ # @option config [String] :url
10
+ # @option config [String] :user
11
+ # @option config [String] :password
12
+ # @return [RestClient::Resource]
13
+ def client config = {}
14
+ config = self.config.merge(config)
15
+ @client ||= RestClient::Resource.new config[:url], :user => config[:user], :password => config[:password]
16
+ end
17
+
18
+ # {include:RestApiClient::API_DOCUMENTATION}
19
+ # @param [Hash] options
20
+ # @return [String]
21
+ def next_pid options = {}
22
+ options[:format] ||= 'xml'
23
+ client[url_for(object_url() + "/nextPID", options)].post nil
24
+ end
25
+
26
+ # {include:RestApiClient::API_DOCUMENTATION}
27
+ # @param [Hash] options
28
+ # @return [String]
29
+ def find_objects options = {}
30
+ raise "" if options[:terms] and options[:query]
31
+ options[:resultFormat] ||= 'xml'
32
+
33
+ client[object_url(nil, options)].get
34
+ end
35
+
36
+ # {include:RestApiClient::API_DOCUMENTATION}
37
+ # @param [Hash] options
38
+ # @option options [String] :pid
39
+ # @return [String]
40
+ def object options = {}
41
+ pid = options.delete(:pid)
42
+ options[:format] ||= 'xml'
43
+ client[object_url(pid, options)].get
44
+ end
45
+
46
+ # {include:RestApiClient::API_DOCUMENTATION}
47
+ # @param [Hash] options
48
+ # @option options [String] :pid
49
+ # @return [String]
50
+ def ingest options = {}
51
+ pid = options.delete(:pid) || 'new'
52
+ file = options.delete(:file)
53
+ client[object_url(pid, options)].post file, :content_type => 'text/xml'
54
+ end
55
+
56
+ # {include:RestApiClient::API_DOCUMENTATION}
57
+ # @param [Hash] options
58
+ # @option options [String] :pid
59
+ # @return [String]
60
+ def modify_object options = {}
61
+ pid = options.delete(:pid)
62
+ client[object_url(pid, options)].put nil
63
+ end
64
+
65
+ # {include:RestApiClient::API_DOCUMENTATION}
66
+ # @param [Hash] options
67
+ # @option options [String] :pid
68
+ # @return [String]
69
+ def purge_object options = {}
70
+ pid = options.delete(:pid)
71
+ client[object_url(pid, options)].delete
72
+ end
73
+
74
+ # {include:RestApiClient::API_DOCUMENTATION}
75
+ # @param [Hash] options
76
+ # @option options [String] :pid
77
+ # @return [String]
78
+ def object_versions options = {}
79
+ pid = options.delete(:pid)
80
+ options[:format] ||= 'xml'
81
+ raise "" unless pid
82
+ client[url_for(object_url(pid) + "/versions", options)].get
83
+ end
84
+
85
+ # {include:RestApiClient::API_DOCUMENTATION}
86
+ # @param [Hash] options
87
+ # @option options [String] :pid
88
+ # @return [String]
89
+ def object_xml options = {}
90
+ pid = options.delete(:pid)
91
+ raise "" unless pid
92
+ options[:format] ||= 'xml'
93
+ client[url_for(object_url(pid) + "/objectXML", options)].get
94
+ end
95
+
96
+ # {include:RestApiClient::API_DOCUMENTATION}
97
+ # @param [Hash] options
98
+ # @option options [String] :pid
99
+ # @option options [String] :dsid
100
+ # @return [String]
101
+ def datastream options = {}
102
+ pid = options.delete(:pid)
103
+ dsid = options.delete(:dsid)
104
+ options[:format] ||= 'xml'
105
+ client[datastream_url(pid, dsid, options)].get
106
+ end
107
+
108
+ alias_method :datastreams, :datastream
109
+
110
+ # {include:RestApiClient::API_DOCUMENTATION}
111
+ # @param [Hash] options
112
+ # @option options [String] :pid
113
+ # @option options [String] :dsid
114
+ # @return [String]
115
+ def set_datastream_options options = {}
116
+ pid = options.delete(:pid)
117
+ dsid = options.delete(:dsid)
118
+ client[datastream_url(pid, dsid, options)].put nil
119
+ end
120
+
121
+ # {include:RestApiClient::API_DOCUMENTATION}
122
+ # @param [Hash] options
123
+ # @option options [String] :pid
124
+ # @option options [String] :dsid
125
+ # @return [String]
126
+ def datastream_versions options = {}
127
+ pid = options.delete(:pid)
128
+ dsid = options.delete(:dsid)
129
+ raise "" unless dsid
130
+ options[:format] ||= 'xml'
131
+ client[url_for(datastream_url(pid, dsid) + "/versions", options)].get
132
+ end
133
+
134
+ # {include:RestApiClient::API_DOCUMENTATION}
135
+ # @param [Hash] options
136
+ # @option options [String] :pid
137
+ # @option options [String] :dsid
138
+ # @return [String]
139
+ def datastream_dissemination options = {}
140
+ pid = options.delete(:pid)
141
+ dsid = options.delete(:dsid)
142
+ raise "" unless dsid
143
+ client[url_for(datastream_url(pid, dsid) + "/content", options)].get
144
+ end
145
+
146
+ # {include:RestApiClient::API_DOCUMENTATION}
147
+ # @param [Hash] options
148
+ # @option options [String] :pid
149
+ # @option options [String] :dsid
150
+ # @return [String]
151
+ def add_datastream options = {}
152
+ pid = options.delete(:pid)
153
+ dsid = options.delete(:dsid)
154
+ file = options.delete(:file)
155
+ content_type = options.delete(:content_type) || options[:mimeType] || (MIME::Types.type_for(file.path).first if file.respond_to? :path) || 'text/plain'
156
+ client[datastream_url(pid, dsid, options)].post file, :content_type => content_type.to_s
157
+ end
158
+
159
+ # {include:RestApiClient::API_DOCUMENTATION}
160
+ # @param [Hash] options
161
+ # @option options [String] :pid
162
+ # @option options [String] :dsid
163
+ # @return [String]
164
+ def modify_datastream options = {}
165
+ pid = options.delete(:pid)
166
+ dsid = options.delete(:dsid)
167
+ file = options.delete(:file)
168
+ content_type = options.delete(:content_type) || options[:mimeType] || (MIME::Types.type_for(file.path).first if file.respond_to? :path) || 'text/plain'
169
+ client[datastream_url(pid, dsid, options)].put file, :content_type => content_type.to_s
170
+ end
171
+
172
+ # {include:RestApiClient::API_DOCUMENTATION}
173
+ # @param [Hash] options
174
+ # @option options [String] :pid
175
+ # @option options [String] :dsid
176
+ # @return [String]
177
+ def purge_datastream options = {}
178
+ pid = options.delete(:pid)
179
+ dsid = options.delete(:dsid)
180
+ client[datastream_url(pid, dsid, options)].delete
181
+ end
182
+
183
+ # {include:RestApiClient::API_DOCUMENTATION}
184
+ # @param [Hash] options
185
+ # @option options [String] :pid
186
+ # @return [String]
187
+ def relationships options = {}
188
+ pid = options.delete(:pid) || options[:subject]
189
+ raise "" unless pid
190
+ options[:format] ||= 'xml'
191
+ client[url_for(object_url(pid) + "/relationships", options)].get
192
+ end
193
+
194
+ # {include:RestApiClient::API_DOCUMENTATION}
195
+ # @param [Hash] options
196
+ # @option options [String] :pid
197
+ # @return [String]
198
+ def add_relationship options = {}
199
+ pid = options.delete(:pid) || options[:subject]
200
+ client[url_for(object_url(pid) + "/relationships/new", options)].post nil
201
+ end
202
+
203
+ # {include:RestApiClient::API_DOCUMENTATION}
204
+ # @param [Hash] options
205
+ # @option options [String] :pid
206
+ # @return [String]
207
+ def purge_relationship options = {}
208
+ pid = options.delete(:pid) || options[:subject]
209
+ client[url_for(object_url(pid) + "/relationships", options)].delete
210
+ end
211
+
212
+ # {include:RestApiClient::API_DOCUMENTATION}
213
+ # @param [Hash] options
214
+ # @option options [String] :pid
215
+ # @option options [String] :sdef
216
+ # @option options [String] :method
217
+ # @return [String]
218
+ def dissemination options = {}
219
+ pid = options.delete(:pid)
220
+ sdef = options.delete(:sdef)
221
+ method = options.delete(:method)
222
+ options[:format] ||= 'xml' unless pid and sdef and method
223
+ client[dissemination_url(pid,sdef,method,options)].get
224
+ end
225
+
226
+ private
227
+
228
+ # Generate a REST API compatible URI
229
+ # @param [String] base base URI
230
+ # @param [Hash] options to convert to URL parameters
231
+ # @return [String] URI
232
+ def url_for base, options = nil
233
+ return base unless options.is_a? Hash
234
+ "#{base}" + (("?#{options.map { |key, value| "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"}.join("&") }" if options and not options.empty?) || '')
235
+ end
236
+
237
+ # Generate a base object REST API endpoint URI
238
+ # @param [String] pid
239
+ # @param [Hash] options to convert to URL parameters
240
+ # @return [String] URI
241
+ def object_url pid = nil, options = nil
242
+ url_for("objects" + (("/#{CGI::escape(pid.to_s.gsub('info:fedora/', ''))}" if pid) || ''), options)
243
+ end
244
+
245
+ # Generate a base object dissemination REST API endpoint URI
246
+ # @param [String] pid
247
+ # @param [String] sdef
248
+ # @param [String] method
249
+ # @param [Hash] options to convert to URL parameters
250
+ # @return [String] URI
251
+ def dissemination_url pid, sdef = nil, method = nil, options = nil
252
+ raise "" unless pid
253
+ url_for(object_url(pid) + "/methods" + (("/#{CGI::escape(sdef)}" if sdef) || '') + (("/#{CGI::escape(method)}" if method) || ''), options)
254
+ end
255
+
256
+ # Generate a base datastream REST API endpoint URI
257
+ # @param [String] pid
258
+ # @param [String] dsid
259
+ # @param [Hash] options to convert to URL parameters
260
+ # @return [String] URI
261
+ def datastream_url pid, dsid = nil, options = nil
262
+ raise "" unless pid
263
+ url_for(object_url(pid) + "/datastreams" + (("/#{CGI::escape(dsid)}" if dsid) || ''), options)
264
+ end
265
+
266
+ end
267
+ end
data/lib/rubydora.rb ADDED
@@ -0,0 +1,46 @@
1
+ # Fedora Commons REST API module
2
+ module Rubydora
3
+ autoload :Datastream, "rubydora/datastream"
4
+ autoload :Repository, "rubydora/repository"
5
+ autoload :ResourceIndex, "rubydora/resource_index"
6
+ autoload :RestApiClient, "rubydora/rest_api_client"
7
+ autoload :ModelsMixin, "rubydora/models_mixin"
8
+ autoload :Ext, "rubydora/ext"
9
+ autoload :RelationshipsMixin, "rubydora/relationships_mixin"
10
+ autoload :DigitalObject, "rubydora/digital_object"
11
+ autoload :ExtensionParameters, "rubydora/extension_parameters"
12
+ autoload :Callbacks, "rubydora/callbacks"
13
+ autoload :ArrayWithCallback, "rubydora/array_with_callback"
14
+
15
+ require 'fastercsv'
16
+ require 'restclient'
17
+ require 'nokogiri'
18
+
19
+ # @return [String] version
20
+ def self.version
21
+ @version ||= File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp
22
+ end
23
+
24
+ # version string
25
+ VERSION = self.version
26
+
27
+ # Connect to Fedora Repository
28
+ # @return Rubydora::Repository
29
+ def self.connect *args
30
+ Repository.new *args
31
+ end
32
+
33
+ # Connect to the default Fedora Repository
34
+ # @return Rubydora::Repository
35
+ def self.repository
36
+ @repository ||= self.connect(self.default_config)
37
+ end
38
+
39
+ # Default repository connection information
40
+ # TODO: read ENV variables?
41
+ # @return Hash
42
+ def self.default_config
43
+ {}
44
+ end
45
+
46
+ end
data/rubydora.gemspec ADDED
@@ -0,0 +1,104 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rubydora}
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Chris Beer"]
12
+ s.date = %q{2011-04-18}
13
+ s.description = %q{Fedora Commons REST API ruby library : REQUIRES FCREPO 3.4+}
14
+ s.email = %q{chris@cbeer.info}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "LICENSE.txt",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/rubydora.rb",
26
+ "lib/rubydora/array_with_callback.rb",
27
+ "lib/rubydora/callbacks.rb",
28
+ "lib/rubydora/datastream.rb",
29
+ "lib/rubydora/digital_object.rb",
30
+ "lib/rubydora/ext.rb",
31
+ "lib/rubydora/ext/solr.rb",
32
+ "lib/rubydora/extension_parameters.rb",
33
+ "lib/rubydora/models_mixin.rb",
34
+ "lib/rubydora/relationships_mixin.rb",
35
+ "lib/rubydora/repository.rb",
36
+ "lib/rubydora/resource_index.rb",
37
+ "lib/rubydora/rest_api_client.rb",
38
+ "lib/rubydora/rest_api_client/v33.rb",
39
+ "rubydora.gemspec",
40
+ "spec/datastream_spec.rb",
41
+ "spec/digital_object_spec.rb",
42
+ "spec/ext_solr_spec.rb",
43
+ "spec/integration_test_spec.rb",
44
+ "spec/repository_spec.rb",
45
+ "spec/resource_index_spec.rb",
46
+ "spec/rest_api_client_spec.rb",
47
+ "spec/spec_helper.rb"
48
+ ]
49
+ s.homepage = %q{http://github.com/cbeer/rubydora}
50
+ s.licenses = ["MIT"]
51
+ s.require_paths = ["lib"]
52
+ s.rubygems_version = %q{1.5.3}
53
+ s.summary = %q{Fedora Commons REST API ruby library}
54
+ s.test_files = [
55
+ "spec/datastream_spec.rb",
56
+ "spec/digital_object_spec.rb",
57
+ "spec/ext_solr_spec.rb",
58
+ "spec/integration_test_spec.rb",
59
+ "spec/repository_spec.rb",
60
+ "spec/resource_index_spec.rb",
61
+ "spec/rest_api_client_spec.rb",
62
+ "spec/spec_helper.rb"
63
+ ]
64
+
65
+ if s.respond_to? :specification_version then
66
+ s.specification_version = 3
67
+
68
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
+ s.add_runtime_dependency(%q<fastercsv>, [">= 0"])
70
+ s.add_runtime_dependency(%q<rest-client>, [">= 0"])
71
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
72
+ s.add_runtime_dependency(%q<mime-types>, [">= 0"])
73
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
74
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
75
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
76
+ s.add_development_dependency(%q<rcov>, [">= 0"])
77
+ s.add_development_dependency(%q<rspec>, [">= 0"])
78
+ s.add_development_dependency(%q<yard>, [">= 0"])
79
+ else
80
+ s.add_dependency(%q<fastercsv>, [">= 0"])
81
+ s.add_dependency(%q<rest-client>, [">= 0"])
82
+ s.add_dependency(%q<nokogiri>, [">= 0"])
83
+ s.add_dependency(%q<mime-types>, [">= 0"])
84
+ s.add_dependency(%q<shoulda>, [">= 0"])
85
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
87
+ s.add_dependency(%q<rcov>, [">= 0"])
88
+ s.add_dependency(%q<rspec>, [">= 0"])
89
+ s.add_dependency(%q<yard>, [">= 0"])
90
+ end
91
+ else
92
+ s.add_dependency(%q<fastercsv>, [">= 0"])
93
+ s.add_dependency(%q<rest-client>, [">= 0"])
94
+ s.add_dependency(%q<nokogiri>, [">= 0"])
95
+ s.add_dependency(%q<mime-types>, [">= 0"])
96
+ s.add_dependency(%q<shoulda>, [">= 0"])
97
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
98
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
99
+ s.add_dependency(%q<rcov>, [">= 0"])
100
+ s.add_dependency(%q<rspec>, [">= 0"])
101
+ s.add_dependency(%q<yard>, [">= 0"])
102
+ end
103
+ end
104
+
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rubydora::Datastream do
4
+ describe "create" do
5
+ before(:each) do
6
+ @mock_repository = mock(Rubydora::Repository)
7
+ @mock_object = mock(Rubydora::DigitalObject)
8
+ @mock_object.should_receive(:repository).any_number_of_times.and_return(@mock_repository)
9
+ @mock_object.should_receive(:pid).any_number_of_times.and_return 'pid'
10
+ @datastream = Rubydora::Datastream.new @mock_object, 'dsid'
11
+ end
12
+
13
+ it "should be new" do
14
+ @mock_repository.should_receive(:datastream).and_raise("")
15
+ @datastream.new?.should == true
16
+ end
17
+
18
+ it "should be dirty" do
19
+ @mock_repository.should_receive(:datastream).and_raise("")
20
+ @datastream.dirty?.should == true
21
+ end
22
+
23
+ it "should call the appropriate api on save" do
24
+ @mock_repository.should_receive(:datastream).and_raise("")
25
+ @mock_repository.should_receive(:add_datastream).with(hash_including(:pid => 'pid', :dsid => 'dsid', :controlGroup => 'M', :dsState => 'A'))
26
+ @datastream.save
27
+ end
28
+
29
+ it "should be able to override defaults" do
30
+ @mock_repository.should_receive(:datastream).and_raise("")
31
+ @mock_repository.should_receive(:add_datastream).with(hash_including(:controlGroup => 'E'))
32
+ @datastream.controlGroup = 'E'
33
+ @datastream.save
34
+ end
35
+ end
36
+
37
+ describe "retrieve" do
38
+ before(:each) do
39
+ @mock_repository = mock(Rubydora::Repository)
40
+ @mock_object = mock(Rubydora::DigitalObject)
41
+ @mock_object.should_receive(:repository).any_number_of_times.and_return(@mock_repository)
42
+ @mock_object.should_receive(:pid).any_number_of_times.and_return 'pid'
43
+ @datastream = Rubydora::Datastream.new @mock_object, 'dsid'
44
+ @mock_repository.should_receive(:datastream).any_number_of_times.and_return <<-XML
45
+ <datastreamProfile>
46
+ <dsLocation>some:uri</dsLocation>
47
+ <dsLabel>label</dsLabel>
48
+ </datastreamProfile>
49
+ XML
50
+ end
51
+
52
+ it "should not be new" do
53
+ @datastream.new?.should == false
54
+ @datastream.dirty?.should == false
55
+ end
56
+
57
+ it "should provide attribute defaults from dsProfile" do
58
+ @datastream.dsLocation.should == 'some:uri'
59
+ @datastream.dsLabel.should == 'label'
60
+ end
61
+
62
+ it "should mediate access to datastream contents" do
63
+ @mock_repository.should_receive(:datastream_dissemination).with(hash_including(:pid => 'pid', :dsid => 'dsid')).and_return('asdf')
64
+ @datastream.content.should == "asdf"
65
+ end
66
+
67
+
68
+ end
69
+
70
+ describe "update" do
71
+
72
+ before(:each) do
73
+ @mock_repository = mock(Rubydora::Repository)
74
+ @mock_object = mock(Rubydora::DigitalObject)
75
+ @mock_object.should_receive(:repository).any_number_of_times.and_return(@mock_repository)
76
+ @mock_object.should_receive(:pid).any_number_of_times.and_return 'pid'
77
+ @datastream = Rubydora::Datastream.new @mock_object, 'dsid'
78
+ @mock_repository.should_receive(:datastream).any_number_of_times.and_return <<-XML
79
+ <datastreamProfile>
80
+ <dsLocation>some:uri</dsLocation>
81
+ <dsLabel>label</dsLabel>
82
+ </datastreamProfile>
83
+ XML
84
+ end
85
+
86
+ it "should allow profile attributes to be replaced" do
87
+ @datastream.dsLabel = "New Label"
88
+ @datastream.dsLabel.should == "New Label"
89
+ end
90
+
91
+ it "should call the appropriate api with any dirty attributes" do
92
+ @mock_repository.should_receive(:modify_datastream).with(hash_including(:dsLabel => "New Label"))
93
+ @datastream.dsLabel = "New Label"
94
+ @datastream.save
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+