rubydora 0.3.1 → 0.4.0pre1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.4.0pre1
@@ -15,11 +15,11 @@ module Rubydora
15
15
  attr_reader :digital_object, :dsid
16
16
 
17
17
  # mapping datastream attributes (and api parameters) to datastream profile names
18
- DS_ATTRIBUTES = {:controlGroup => :dsControlGroup, :dsLocation => :dsLocation, :altIDs => nil, :dsLabel => :dsLabel, :versionable => :dsVersionable, :dsState => :dsState, :formatURI => :dsFormatURI, :checksumType => :dsChecksumType, :checksum => :dsChecksum, :mimeType => :dsMIME, :logMessage => nil, :ignoreContent => nil, :lastModifiedDate => nil, :content => nil}
18
+ DS_ATTRIBUTES = {:controlGroup => :dsControlGroup, :dsLocation => :dsLocation, :altIDs => nil, :dsLabel => :dsLabel, :versionable => :dsVersionable, :dsState => :dsState, :formatURI => :dsFormatURI, :checksumType => :dsChecksumType, :checksum => :dsChecksum, :mimeType => :dsMIME, :logMessage => nil, :ignoreContent => nil, :lastModifiedDate => nil, :content => nil, :asOfDateTime => nil}
19
19
  DS_DEFAULT_ATTRIBUTES = { :controlGroup => 'M', :dsState => 'A', :checksumType => 'DISABLED', :versionable => true }
20
20
 
21
21
  define_attribute_methods DS_ATTRIBUTES.keys
22
-
22
+
23
23
  # accessors for datastream attributes
24
24
  DS_ATTRIBUTES.each do |attribute, profile_name|
25
25
  class_eval %Q{
@@ -34,6 +34,37 @@ module Rubydora
34
34
  }
35
35
  end
36
36
 
37
+ DS_READONLY_ATTRIBUTES = [ :dsCreateDate , :dsSize, :dsVersionID ]
38
+ DS_READONLY_ATTRIBUTES.each do |attribute|
39
+ class_eval %Q{
40
+ def #{attribute.to_s}
41
+ @#{attribute} || profile['#{attribute.to_s}'] || DS_DEFAULT_ATTRIBUTES[:#{attribute}]
42
+ end
43
+ }
44
+ end
45
+
46
+
47
+ # Create humanized accessors for the DS attribute (dsState -> state, dsCreateDate -> createDate)
48
+ (DS_ATTRIBUTES.keys + DS_READONLY_ATTRIBUTES).select { |k| k.to_s =~ /^ds/ }.each do |attribute|
49
+ simple_attribute = attribute.to_s.sub(/^ds/, '')
50
+ simple_attribute = simple_attribute[0].chr.downcase + simple_attribute[1..-1]
51
+
52
+ alias_method simple_attribute, attribute
53
+
54
+ if self.respond_to? "#{attribute}="
55
+ alias_method "#{simple_attribute}=", "#{attribute}="
56
+ end
57
+ end
58
+
59
+
60
+ def asOfDateTime asOfDateTime = nil
61
+ if asOfDateTime == nil
62
+ return @asOfDateTime
63
+ end
64
+
65
+ return self.class.new(@digital_object, @dsid, @options.merge(:asOfDateTime => asOfDateTime))
66
+ end
67
+
37
68
  ##
38
69
  # Initialize a Rubydora::Datastream object, which may or
39
70
  # may not already exist in the datastore.
@@ -47,6 +78,7 @@ module Rubydora
47
78
  _run_initialize_callbacks do
48
79
  @digital_object = digital_object
49
80
  @dsid = dsid
81
+ @options = options
50
82
  options.each do |key, value|
51
83
  self.send(:"#{key}=", value)
52
84
  end
@@ -68,7 +100,10 @@ module Rubydora
68
100
  # @return [String]
69
101
  def content
70
102
  begin
71
- @content ||= repository.datastream_dissemination :pid => pid, :dsid => dsid
103
+ options = { :pid => pid, :dsid => dsid }
104
+ options[:asOfDateTime] = asOfDateTime if asOfDateTime
105
+
106
+ @content ||= repository.datastream_dissemination options
72
107
  rescue RestClient::ResourceNotFound
73
108
  end
74
109
 
@@ -80,7 +115,9 @@ module Rubydora
80
115
  # Get the URL for the datastream content
81
116
  # @return [String]
82
117
  def url
83
- repository.datastream_url(pid, dsid) + "/content"
118
+ options = { }
119
+ options[:asOfDateTime] = asOfDateTime if asOfDateTime
120
+ repository.datastream_url(pid, dsid, options) + "/content"
84
121
  end
85
122
 
86
123
  # Set the content of the datastream
@@ -94,34 +131,56 @@ module Rubydora
94
131
  # Content_will_change! would be dynamically created by ActiveModel::Dirty, but it would eagerly load the content.
95
132
  # We don't want to do that.
96
133
  def content_will_change!
134
+ raise "Can't change values on older versions" if @asOfDateTime
97
135
  changed_attributes[:content] = nil
98
136
  end
99
137
 
100
138
  # Retrieve the datastream profile as a hash (and cache it)
101
139
  # @return [Hash] see Fedora #getDatastream documentation for keys
102
- def profile
140
+ def profile profile_xml = nil
103
141
  @profile ||= begin
104
- profile_xml = repository.datastream(:pid => pid, :dsid => dsid)
105
- profile_xml.gsub! '<datastreamProfile', '<datastreamProfile xmlns="http://www.fedora.info/definitions/1/0/management/"' unless profile_xml =~ /xmlns=/
106
- doc = Nokogiri::XML(profile_xml)
107
- h = doc.xpath('/management:datastreamProfile/*', {'management' => "http://www.fedora.info/definitions/1/0/management/"} ).inject({}) do |sum, node|
108
- sum[node.name] ||= []
109
- sum[node.name] << node.text
110
- sum
111
- end
112
- h.select { |key, value| value.length == 1 }.each do |key, value|
113
- h[key] = value.first
114
- end
115
-
116
- h
142
+ options = { :pid => pid, :dsid => dsid }
143
+ options[:asOfDateTime] = asOfDateTime if asOfDateTime
144
+ profile_xml ||= repository.datastream(options)
145
+ self.profile_xml_to_hash(profile_xml)
146
+
117
147
  rescue
118
148
  {}
119
149
  end
120
150
  end
151
+ alias_method :profile=, :profile
152
+
153
+ def profile_xml_to_hash profile_xml
154
+ profile_xml.gsub! '<datastreamProfile', '<datastreamProfile xmlns="http://www.fedora.info/definitions/1/0/management/"' unless profile_xml =~ /xmlns=/
155
+ doc = Nokogiri::XML(profile_xml)
156
+ h = doc.xpath('/management:datastreamProfile/*', {'management' => "http://www.fedora.info/definitions/1/0/management/"} ).inject({}) do |sum, node|
157
+ sum[node.name] ||= []
158
+ sum[node.name] << node.text
159
+ sum
160
+ end
161
+ h.select { |key, value| value.length == 1 }.each do |key, value|
162
+ h[key] = value.first
163
+ end
164
+
165
+ h['dsSize'] &&= h['dsSize'].to_i rescue h['dsSize']
166
+ h['dsCreateDate'] &&= Time.parse(h['dsCreateDate']) rescue h['dsCreateDate']
167
+
168
+ h
169
+ end
170
+
171
+ def versions
172
+ versions_xml = repository.datastream_versions(:pid => pid, :dsid => dsid)
173
+ versions_xml.gsub! '<datastreamProfile', '<datastreamProfile xmlns="http://www.fedora.info/definitions/1/0/management/"' unless versions_xml =~ /xmlns=/
174
+ doc = Nokogiri::XML(versions_xml)
175
+ doc.xpath('//management:datastreamProfile', {'management' => "http://www.fedora.info/definitions/1/0/management/"} ).map do |ds|
176
+ self.class.new @digital_object, @dsid, :profile => ds.to_s, :asOfDateTime => ds.xpath('management:dsCreateDate', 'management' => "http://www.fedora.info/definitions/1/0/management/").text
177
+ end
178
+ end
121
179
 
122
180
  # Add datastream to Fedora
123
181
  # @return [Rubydora::Datastream]
124
182
  def create
183
+ check_if_read_only
125
184
  run_callbacks :create do
126
185
  repository.add_datastream to_api_params.merge({ :pid => pid, :dsid => dsid })
127
186
  reset_profile_attributes
@@ -132,6 +191,7 @@ module Rubydora
132
191
  # Modify or save the datastream
133
192
  # @return [Rubydora::Datastream]
134
193
  def save
194
+ check_if_read_only
135
195
  run_callbacks :save do
136
196
  return create if new?
137
197
  repository.modify_datastream to_api_params.merge({ :pid => pid, :dsid => dsid })
@@ -143,6 +203,7 @@ module Rubydora
143
203
  # Purge the datastream from Fedora
144
204
  # @return [Rubydora::Datastream] `self`
145
205
  def delete
206
+ check_if_read_only
146
207
  run_callbacks :destroy do
147
208
  repository.purge_datastream(:pid => pid, :dsid => dsid) unless self.new?
148
209
  digital_object.datastreams.delete(dsid)
@@ -184,5 +245,19 @@ module Rubydora
184
245
  def repository
185
246
  digital_object.repository
186
247
  end
248
+
249
+ def asOfDateTime= val
250
+ @asOfDateTime = val
251
+ end
252
+
253
+ def check_if_read_only
254
+ raise "Can't change values on older versions" if @asOfDateTime
255
+ end
256
+
257
+ private
258
+ def attribute_will_change! *args
259
+ check_if_read_only
260
+ super
261
+ end
187
262
  end
188
263
  end
@@ -43,8 +43,8 @@ module Rubydora
43
43
  # TODO: raise an error if the object does not yet exist
44
44
  # @param [String] pid
45
45
  # @param [Rubydora::Repository] context
46
- def self.find pid, repository = nil
47
- self.new pid, repository
46
+ def self.find pid, repository = nil, options = {}
47
+ self.new pid, repository, options
48
48
  end
49
49
 
50
50
  # create a new fedora object (see also DigitalObject#save)
@@ -72,6 +72,7 @@ module Rubydora
72
72
  self.pid = pid
73
73
  @repository = repository
74
74
  @new = true
75
+ @options = options
75
76
 
76
77
  options.each do |key, value|
77
78
  self.send(:"#{key}=", value)
@@ -94,11 +95,25 @@ module Rubydora
94
95
  @new
95
96
  end
96
97
 
98
+ def asOfDateTime asOfDateTime = nil
99
+ if asOfDateTime == nil
100
+ return @asOfDateTime
101
+ end
102
+
103
+ return self.class.new(pid, @repository, @options.merge(:asOfDateTime => asOfDateTime))
104
+ end
105
+
106
+ def asOfDateTime= val
107
+ @asOfDateTime = val
108
+ end
109
+
97
110
  # Retrieve the object profile as a hash (and cache it)
98
111
  # @return [Hash] see Fedora #getObject documentation for keys
99
112
  def profile
100
113
  @profile ||= begin
101
- profile_xml = repository.object(:pid => pid)
114
+ options = { :pid => pid }
115
+ options[:asOfDateTime] = asOfDateTime if asOfDateTime
116
+ profile_xml = repository.object(options)
102
117
  profile_xml.gsub! '<objectProfile', '<objectProfile xmlns="http://www.fedora.info/definitions/1/0/access/"' unless profile_xml =~ /xmlns=/
103
118
  doc = Nokogiri::XML(profile_xml)
104
119
  h = doc.xpath('/access:objectProfile/*', {'access' => "http://www.fedora.info/definitions/1/0/access/"} ).inject({}) do |sum, node|
@@ -123,6 +138,16 @@ module Rubydora
123
138
  end.freeze
124
139
  end
125
140
 
141
+ def versions
142
+ versions_xml = repository.object_versions(:pid => pid)
143
+ versions_xml.gsub! '<fedoraObjectHistory', '<fedoraObjectHistory xmlns="http://www.fedora.info/definitions/1/0/access/"' unless versions_xml =~ /xmlns=/
144
+ doc = Nokogiri::XML(versions_xml)
145
+ doc.xpath('//access:objectChangeDate', {'access' => 'http://www.fedora.info/definitions/1/0/access/' } ).map do |changeDate|
146
+ self.class.new pid, repository, :asOfDateTime => changeDate.text
147
+ end
148
+ end
149
+
150
+
126
151
  # List of datastreams
127
152
  # @return [Array<Rubydora::Datastream>]
128
153
  def datastreams
@@ -130,7 +155,9 @@ module Rubydora
130
155
  h = Hash.new { |h,k| h[k] = datastream_object_for(k) }
131
156
 
132
157
  begin
133
- datastreams_xml = repository.datastreams(:pid => pid)
158
+ options = { :pid => pid }
159
+ options[:asOfDateTime] = asOfDateTime if asOfDateTime
160
+ datastreams_xml = repository.datastreams(options)
134
161
  datastreams_xml.gsub! '<objectDatastreams', '<objectDatastreams xmlns="http://www.fedora.info/definitions/1/0/access/"' unless datastreams_xml =~ /xmlns=/
135
162
  doc = Nokogiri::XML(datastreams_xml)
136
163
  doc.xpath('//access:datastream', {'access' => "http://www.fedora.info/definitions/1/0/access/"}).each do |ds|
@@ -158,6 +185,7 @@ module Rubydora
158
185
  #
159
186
  # @return [Rubydora::DigitalObject] a new copy of this object
160
187
  def save
188
+ check_if_read_only
161
189
  run_callbacks :save do
162
190
  if self.new?
163
191
  self.pid = repository.ingest to_api_params.merge(:pid => pid)
@@ -175,6 +203,7 @@ module Rubydora
175
203
  # Purge the object from Fedora
176
204
  # @return [Rubydora::DigitalObject] `self`
177
205
  def delete
206
+ check_if_read_only
178
207
  my_pid = pid
179
208
  run_callbacks :destroy do
180
209
  @datastreams = nil
@@ -219,8 +248,24 @@ module Rubydora
219
248
  # instantiate a datastream object for a dsid
220
249
  # @param [String] dsid
221
250
  # @return [Datastream]
222
- def datastream_object_for dsid
223
- Datastream.new self, dsid
251
+ def datastream_object_for dsid, options = {}
252
+ options[:asOfDateTime] ||= asOfDateTime if asOfDateTime
253
+ Datastream.new self, dsid, options
254
+ end
255
+
256
+ def check_if_read_only
257
+ raise "Can't change values on older versions" if @asOfDateTime
258
+ end
259
+
260
+ def check_if_read_only
261
+ raise "Can't change values on older versions" if @asOfDateTime
262
+ end
263
+
264
+ private
265
+ def attribute_will_change! *args
266
+ check_if_read_only
267
+ super
224
268
  end
269
+
225
270
  end
226
271
  end
@@ -198,13 +198,15 @@ module Rubydora
198
198
  raise ArgumentError, "Must supply dsid" unless dsid
199
199
  options[:format] ||= 'xml'
200
200
  begin
201
- return client[url_for(datastream_url(pid, dsid) + "/versions", options)].get
201
+ return client[url_for(datastream_url(pid, dsid) + "/history", options)].get
202
202
  rescue => e
203
203
  logger.error e.response
204
204
  raise "Error getting versions for datastream #{dsid} for object #{pid}. See logger for details"
205
205
  end
206
206
  end
207
207
 
208
+ alias_method :datastream_history, :datastream_versions
209
+
208
210
  # {include:RestApiClient::API_DOCUMENTATION}
209
211
  # @param [Hash] options
210
212
  # @option options [String] :pid
data/lib/rubydora.rb CHANGED
@@ -17,6 +17,8 @@ module Rubydora
17
17
 
18
18
 
19
19
  require 'csv'
20
+ require 'time'
21
+
20
22
  if CSV.const_defined? :Reader
21
23
  require 'fastercsv'
22
24
  end
@@ -7,6 +7,7 @@ describe Rubydora::Datastream do
7
7
  @mock_object.stub(:repository => @mock_repository, :pid => 'pid')
8
8
  end
9
9
 
10
+
10
11
  describe "create" do
11
12
  before(:each) do
12
13
  @mock_repository.stub(:datastream) { raise("") }
@@ -118,6 +119,218 @@ describe Rubydora::Datastream do
118
119
 
119
120
  end
120
121
 
122
+ describe "should check if an object is read-only" do
123
+ before(:each) do
124
+ @datastream = Rubydora::Datastream.new @mock_object, 'dsid'
125
+ @mock_repository.should_receive(:datastream).any_number_of_times.and_return <<-XML
126
+ <datastreamProfile>
127
+ <dsLocation>some:uri</dsLocation>
128
+ <dsLabel>label</dsLabel>
129
+ </datastreamProfile>
130
+ XML
131
+ end
132
+
133
+ it "before updating attributes" do
134
+ @datastream.should_receive(:check_if_read_only)
135
+ @datastream.dsLabel = 'New Label'
136
+ end
137
+
138
+ it "before saving an object" do
139
+ @mock_repository.should_receive(:modify_datastream)
140
+ @datastream.should_receive(:check_if_read_only)
141
+ @datastream.save
142
+ end
143
+
144
+ it "before deleting an object" do
145
+ @mock_repository.should_receive(:purge_datastream)
146
+ @mock_object.should_receive(:datastreams).and_return([])
147
+ @datastream.should_receive(:check_if_read_only)
148
+ @datastream.delete
149
+ end
150
+ end
151
+
152
+ describe "versions" do
153
+ before(:each) do
154
+ @datastream = Rubydora::Datastream.new @mock_object, 'dsid'
155
+ @mock_repository.should_receive(:datastream_versions).any_number_of_times.and_return <<-XML
156
+ <datastreamHistory>
157
+ <datastreamProfile>
158
+ <dsVersionID>dsid.1</dsVersionID>
159
+ <dsCreateDate>2010-01-02T00:00:00.012Z</dsCreateDate>
160
+ </datastreamProfile>
161
+ <datastreamProfile>
162
+ <dsVersionID>dsid.0</dsVersionID>
163
+ <dsCreateDate>2008-08-05T01:30:05.012Z</dsCreateDate>
164
+ </datastreamProfile>
165
+ </datastreamHistory>
166
+ XML
167
+ end
168
+
169
+ it "should have a list of previous versions" do
170
+ @datastream.versions.should have(2).items
171
+ end
172
+
173
+ it "should access versions as read-only copies" do
174
+ expect { @datastream.versions.first.label = "asdf" }.to raise_error
175
+ end
176
+
177
+ it "should lookup content of datastream using the asOfDateTime parameter" do
178
+ @mock_repository.should_receive(:datastream_dissemination).with(hash_including(:asOfDateTime => '2008-08-05T01:30:05.012Z'))
179
+ @datastream.versions.last.content
180
+ end
181
+
182
+ end
183
+
184
+ describe "datastream attributes" do
185
+ before do
186
+ @mock_repository.stub(:datastream => <<-XML
187
+ <datastreamProfile>
188
+ <anyProfileValue />
189
+ </datastreamProfile>
190
+ XML
191
+ )
192
+ end
193
+
194
+ shared_examples "a datastream attribute" do
195
+ subject { Rubydora::Datastream.new @mock_object, 'dsid' }
196
+
197
+ describe "getter" do
198
+ it "should return the value" do
199
+ subject.instance_variable_set("@#{method}", 'asdf')
200
+ subject.send(method).should == 'asdf'
201
+ end
202
+
203
+ it "should look in the object profile" do
204
+ subject.should_receive(:profile) { { Rubydora::Datastream::DS_ATTRIBUTES[method.to_sym].to_s => 'qwerty' } }
205
+ subject.send(method).should == 'qwerty'
206
+ end
207
+
208
+ it "should fall-back to the set of default attributes" do
209
+ Rubydora::Datastream::DS_DEFAULT_ATTRIBUTES.should_receive(:[]).with(method.to_sym) { 'zxcv'}
210
+ subject.send(method).should == 'zxcv'
211
+ end
212
+ end
213
+
214
+ describe "setter" do
215
+ before do
216
+ subject.stub(:datastreams => [])
217
+ end
218
+ it "should mark the object as changed after setting" do
219
+ subject.send("#{method}=", 'new_value')
220
+ subject.should be_changed
221
+ end
222
+
223
+ it "should appear in the save request" do
224
+ @mock_repository.should_receive(:modify_datastream).with(hash_including(method.to_sym => 'new_value'))
225
+ subject.send("#{method}=", 'new_value')
226
+ subject.save
227
+ end
228
+ end
229
+ end
230
+
231
+ shared_examples "a read-only datastream attribute" do
232
+ subject { Rubydora::Datastream.new @mock_object, 'dsid' }
233
+
234
+ describe "getter" do
235
+ it "should return the value" do
236
+ subject.instance_variable_set("@#{method}", 'asdf')
237
+ subject.send(method).should == 'asdf'
238
+ end
239
+
240
+ it "should look in the object profile" do
241
+ subject.should_receive(:profile) { { method => 'qwerty' } }
242
+ subject.send(method).should == 'qwerty'
243
+ end
244
+
245
+ it "should fall-back to the set of default attributes" do
246
+ Rubydora::Datastream::DS_DEFAULT_ATTRIBUTES.should_receive(:[]).with(method.to_sym) { 'zxcv'}
247
+ subject.send(method).should == 'zxcv'
248
+ end
249
+ end
250
+
251
+ end
252
+
253
+ describe "#controlGroup" do
254
+ it_behaves_like "a datastream attribute"
255
+ let(:method) { 'controlGroup' }
256
+ end
257
+
258
+ describe "#dsLocation" do
259
+ it_behaves_like "a datastream attribute"
260
+ let(:method) { 'dsLocation' }
261
+ end
262
+
263
+ describe "#altIDs" do
264
+ it_behaves_like "a datastream attribute"
265
+ let(:method) { 'altIDs' }
266
+ end
267
+
268
+ describe "#dsLabel" do
269
+ it_behaves_like "a datastream attribute"
270
+ let(:method) { 'dsLabel' }
271
+ end
272
+
273
+ describe "#versionable" do
274
+ it_behaves_like "a datastream attribute"
275
+ let(:method) { 'versionable' }
276
+ end
277
+
278
+ describe "#dsState" do
279
+ it_behaves_like "a datastream attribute"
280
+ let(:method) { 'dsState' }
281
+ end
282
+
283
+ describe "#formatURI" do
284
+ it_behaves_like "a datastream attribute"
285
+ let(:method) { 'formatURI' }
286
+ end
287
+
288
+ describe "#checksumType" do
289
+ it_behaves_like "a datastream attribute"
290
+ let(:method) { 'checksumType' }
291
+ end
292
+
293
+ describe "#checksum" do
294
+ it_behaves_like "a datastream attribute"
295
+ let(:method) { 'checksum' }
296
+ end
297
+
298
+ describe "#mimeType" do
299
+ it_behaves_like "a datastream attribute"
300
+ let(:method) { 'mimeType' }
301
+ end
302
+
303
+ describe "#logMessage" do
304
+ it_behaves_like "a datastream attribute"
305
+ let(:method) { 'logMessage' }
306
+ end
307
+
308
+ describe "#ignoreContent" do
309
+ it_behaves_like "a datastream attribute"
310
+ let(:method) { 'ignoreContent' }
311
+ end
312
+
313
+ describe "#lastModifiedDate" do
314
+ it_behaves_like "a datastream attribute"
315
+ let(:method) { 'lastModifiedDate' }
316
+ end
317
+
318
+ describe "#dsCreateDate" do
319
+ it_behaves_like "a read-only datastream attribute"
320
+ let(:method) { 'dsCreateDate' }
321
+ end
322
+
323
+ describe "#dsSize" do
324
+ it_behaves_like "a read-only datastream attribute"
325
+ let(:method) { 'dsSize' }
326
+ end
327
+
328
+ describe "#dsVersionID" do
329
+ it_behaves_like "a read-only datastream attribute"
330
+ let(:method) { 'dsVersionID' }
331
+ end
332
+ end
333
+
121
334
  describe "to_api_params" do
122
335
 
123
336
  describe "with existing properties" do
@@ -292,6 +292,41 @@ describe Rubydora::DigitalObject do
292
292
  end
293
293
  end
294
294
 
295
+ describe "versions" do
296
+ before(:each) do
297
+ @mock_repository.stub(:object) { <<-XML
298
+ <objectProfile>
299
+ </objectProfile>
300
+ XML
301
+ }
302
+
303
+ @mock_repository.stub(:object_versions) { <<-XML
304
+ <fedoraObjectHistory>
305
+ <objectChangeDate>2011-09-26T20:41:02.450Z</objectChangeDate>
306
+ <objectChangeDate>2011-10-11T21:17:48.124Z</objectChangeDate>
307
+ </fedoraObjectHistory>
308
+ XML
309
+ }
310
+ @object = Rubydora::DigitalObject.new 'pid', @mock_repository
311
+ end
312
+
313
+ it "should have a list of previous versions" do
314
+ @object.versions.should have(2).items
315
+ @object.versions.first.asOfDateTime.should == '2011-09-26T20:41:02.450Z'
316
+ end
317
+
318
+ it "should access versions as read-only copies" do
319
+ expect { @object.versions.first.label = "asdf" }.to raise_error
320
+ end
321
+
322
+ it "should lookup content of datastream using the asOfDateTime parameter" do
323
+ @mock_repository.should_receive(:datastreams).with(hash_including(:asOfDateTime => '2011-09-26T20:41:02.450Z')).and_return('')
324
+ Rubydora::Datastream.should_receive(:new).with(anything, 'my_ds', hash_including(:asOfDateTime => '2011-09-26T20:41:02.450Z'))
325
+ ds = @object.versions.first['my_ds']
326
+ end
327
+
328
+ end
329
+
295
330
  describe "to_api_params" do
296
331
  before(:each) do
297
332
  @object = Rubydora::DigitalObject.new 'pid', @mock_repository
@@ -300,4 +335,66 @@ describe Rubydora::DigitalObject do
300
335
  @object.send(:to_api_params).should == {}
301
336
  end
302
337
  end
338
+
339
+ shared_examples "an object attribute" do
340
+ subject { Rubydora::DigitalObject.new 'pid', @mock_repository }
341
+
342
+ describe "getter" do
343
+ it "should return the value" do
344
+ subject.instance_variable_set("@#{method}", 'asdf')
345
+ subject.send(method).should == 'asdf'
346
+ end
347
+
348
+ it "should look in the object profile" do
349
+ subject.should_receive(:profile) { { Rubydora::DigitalObject::OBJ_ATTRIBUTES[method.to_sym].to_s => 'qwerty' } }
350
+ subject.send(method).should == 'qwerty'
351
+ end
352
+
353
+ it "should fall-back to the set of default attributes" do
354
+ Rubydora::DigitalObject::OBJ_DEFAULT_ATTRIBUTES.should_receive(:[]).with(method.to_sym) { 'zxcv'}
355
+ subject.send(method).should == 'zxcv'
356
+ end
357
+ end
358
+
359
+ describe "setter" do
360
+ before do
361
+ subject.stub(:datastreams => [])
362
+ end
363
+ it "should mark the object as changed after setting" do
364
+ subject.send("#{method}=", 'new_value')
365
+ subject.should be_changed
366
+ end
367
+
368
+ it "should appear in the save request" do
369
+ @mock_repository.should_receive(:ingest).with(hash_including(method.to_sym => 'new_value'))
370
+ subject.send("#{method}=", 'new_value')
371
+ subject.save
372
+ end
373
+ end
374
+ end
375
+
376
+ describe "#state" do
377
+ it_behaves_like "an object attribute"
378
+ let(:method) { 'state' }
379
+ end
380
+
381
+ describe "#ownerId" do
382
+ it_behaves_like "an object attribute"
383
+ let(:method) { 'ownerId' }
384
+ end
385
+
386
+ describe "#label" do
387
+ it_behaves_like "an object attribute"
388
+ let(:method) { 'label' }
389
+ end
390
+
391
+ describe "#logMessage" do
392
+ it_behaves_like "an object attribute"
393
+ let(:method) { 'logMessage' }
394
+ end
395
+
396
+ describe "#lastModifiedDate" do
397
+ it_behaves_like "an object attribute"
398
+ let(:method) { 'lastModifiedDate' }
399
+ end
303
400
  end
@@ -93,6 +93,18 @@ describe "Integration testing against a live Fedora repository", :integration =>
93
93
  obj.datastreams["Test"].content.should match( "Integration testing against a live Fedora repository")
94
94
  end
95
95
 
96
+ it "should have profile attributes" do
97
+ obj = @repository.find('test:1')
98
+ ds = obj.datastreams["Test"]
99
+
100
+ ds.versionID.should == "Test.0"
101
+
102
+ (Time.now - ds.createDate).should be < 60*60 # 1 hour
103
+ ds.state.should == "A"
104
+ ds.controlGroup.should == "M"
105
+ ds.size.should be > 100
106
+ end
107
+
96
108
  it "should delete datastreams" do
97
109
  obj = @repository.find('test:1')
98
110
  ds = obj.datastreams["Test"].delete
@@ -125,6 +137,70 @@ describe "Integration testing against a live Fedora repository", :integration =>
125
137
  obj.datastreams["my_ds"].mimeType.should == "application/x-text"
126
138
  end
127
139
 
140
+
141
+ describe "object versions" do
142
+ it "should have versions" do
143
+ obj = @repository.find('test:1')
144
+ obj.versions.should_not be_empty
145
+ end
146
+
147
+ it "should have read-only versions" do
148
+ obj = @repository.find('test:1')
149
+ expect { obj.versions.first.label = "asdf" }.to raise_error
150
+ end
151
+
152
+ ## This isn't how Fedora object profiles actually work??
153
+ #it "should access profile data using asOfDateTime" do
154
+ # obj = @repository.find('test:3')
155
+ # obj.label = "asdf"
156
+ # obj.save
157
+ #
158
+ # obj = @repository.find('test:3')
159
+ # obj.label = "qwerty"
160
+ # obj.save
161
+ #
162
+ # obj = @repository.find('test:3')
163
+ # obj.versions.map { |x| x.label }.should include('adsf', 'qwerty')
164
+ #end
165
+
166
+ it "should access datastreams list using asOfDateTime (and pass the asOfDateTime through to the datastreams)" do
167
+ obj = @repository.find('test:1')
168
+ oldest = obj.versions.first.datastreams.keys
169
+ newest = obj.versions.last.datastreams.keys
170
+ (newest - oldest).should_not be_empty
171
+
172
+ obj.versions.first.datastreams.values.first.asOfDateTime.should == obj.versions.first.asOfDateTime
173
+ end
174
+ end
175
+
176
+ describe "datastream versions" do
177
+
178
+ it "should have versions" do
179
+ obj = @repository.find('test:1')
180
+ versions = obj.datastreams["my_ds"].versions
181
+ versions.should_not be_empty
182
+ versions.map { |x| x.versionID }.should include('my_ds.1', 'my_ds.0')
183
+ end
184
+
185
+ it "should have read-only versions" do
186
+ obj = @repository.find('test:1')
187
+ ds = obj.datastreams["my_ds"].asOfDateTime(Time.now)
188
+ expect { ds.dsLabel = 'asdf' }.to raise_error
189
+ expect { ds.content = 'asdf' }.to raise_error
190
+ end
191
+
192
+ it "should access the content of older datastreams" do
193
+ obj = @repository.find('test:1')
194
+
195
+ ds = obj.datastreams["my_ds"]
196
+ ds.content = "YYY"
197
+ ds.save
198
+
199
+ versions = obj.datastreams["my_ds"].versions
200
+ versions.map { |x| x.content }.should include("XXX", "YYY")
201
+ end
202
+ end
203
+
128
204
  context "mime types" do
129
205
  before(:each) do
130
206
  obj = @repository.find('test:1')
@@ -163,9 +163,13 @@ describe Rubydora::RestApiClient do
163
163
  end
164
164
 
165
165
  it "datastream_versions" do
166
- RestClient::Request.should_receive(:execute).with(hash_including(:url => "http://example.org/objects/mypid/datastreams/aaa/versions?format=xml"))
166
+ RestClient::Request.should_receive(:execute).with(hash_including(:url => "http://example.org/objects/mypid/datastreams/aaa/history?format=xml"))
167
167
  @mock_repository.datastream_versions :pid => 'mypid', :dsid => 'aaa'
168
+ end
168
169
 
170
+ it "datastream_history" do
171
+ RestClient::Request.should_receive(:execute).with(hash_including(:url => "http://example.org/objects/mypid/datastreams/aaa/history?format=xml"))
172
+ @mock_repository.datastream_history :pid => 'mypid', :dsid => 'aaa'
169
173
  end
170
174
 
171
175
  it "relationships" do
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubydora
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
5
- prerelease:
4
+ version: 0.4.0pre1
5
+ prerelease: 5
6
6
  platform: ruby
7
7
  authors:
8
8
  - Chris Beer
@@ -13,7 +13,7 @@ date: 2011-12-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fastercsv
16
- requirement: &70303469325600 !ruby/object:Gem::Requirement
16
+ requirement: &70158854103660 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70303469325600
24
+ version_requirements: *70158854103660
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rest-client
27
- requirement: &70303469325100 !ruby/object:Gem::Requirement
27
+ requirement: &70158854103200 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70303469325100
35
+ version_requirements: *70158854103200
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: nokogiri
38
- requirement: &70303469324580 !ruby/object:Gem::Requirement
38
+ requirement: &70158854102700 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70303469324580
46
+ version_requirements: *70158854102700
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mime-types
49
- requirement: &70303469324120 !ruby/object:Gem::Requirement
49
+ requirement: &70158854102180 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70303469324120
57
+ version_requirements: *70158854102180
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: activesupport
60
- requirement: &70303469323660 !ruby/object:Gem::Requirement
60
+ requirement: &70158854101740 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70303469323660
68
+ version_requirements: *70158854101740
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: activemodel
71
- requirement: &70303469323200 !ruby/object:Gem::Requirement
71
+ requirement: &70158854101320 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *70303469323200
79
+ version_requirements: *70158854101320
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: savon
82
- requirement: &70303469322720 !ruby/object:Gem::Requirement
82
+ requirement: &70158854117240 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *70303469322720
90
+ version_requirements: *70158854117240
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rake
93
- requirement: &70303469322280 !ruby/object:Gem::Requirement
93
+ requirement: &70158854116180 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70303469322280
101
+ version_requirements: *70158854116180
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: shoulda
104
- requirement: &70303469321840 !ruby/object:Gem::Requirement
104
+ requirement: &70158854115100 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70303469321840
112
+ version_requirements: *70158854115100
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: bundler
115
- requirement: &70303469334820 !ruby/object:Gem::Requirement
115
+ requirement: &70158854112280 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: 1.0.14
121
121
  type: :development
122
122
  prerelease: false
123
- version_requirements: *70303469334820
123
+ version_requirements: *70158854112280
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: rspec
126
- requirement: &70303469334120 !ruby/object:Gem::Requirement
126
+ requirement: &70158854111780 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ! '>='
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '0'
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *70303469334120
134
+ version_requirements: *70158854111780
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: yard
137
- requirement: &70303469333480 !ruby/object:Gem::Requirement
137
+ requirement: &70158854111220 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :development
144
144
  prerelease: false
145
- version_requirements: *70303469333480
145
+ version_requirements: *70158854111220
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: jettywrapper
148
- requirement: &70303469333060 !ruby/object:Gem::Requirement
148
+ requirement: &70158854110800 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,7 +153,7 @@ dependencies:
153
153
  version: '0'
154
154
  type: :development
155
155
  prerelease: false
156
- version_requirements: *70303469333060
156
+ version_requirements: *70158854110800
157
157
  description: ! 'Fedora Commons REST API ruby library : REQUIRES FCREPO 3.4+'
158
158
  email:
159
159
  - chris@cbeer.info
@@ -211,16 +211,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
211
211
  version: '0'
212
212
  segments:
213
213
  - 0
214
- hash: 4478861482245070325
214
+ hash: 142486579625571647
215
215
  required_rubygems_version: !ruby/object:Gem::Requirement
216
216
  none: false
217
217
  requirements:
218
- - - ! '>='
218
+ - - ! '>'
219
219
  - !ruby/object:Gem::Version
220
- version: '0'
221
- segments:
222
- - 0
223
- hash: 4478861482245070325
220
+ version: 1.3.1
224
221
  requirements: []
225
222
  rubyforge_project:
226
223
  rubygems_version: 1.8.10