rubydora 0.3.1 → 0.4.0pre1

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.
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