Class: Rubydora::Datastream

Inherits:
Object
  • Object
show all
Extended by:
ActiveModel::Callbacks
Includes:
ActiveModel::Dirty
Defined in:
lib/rubydora/datastream.rb

Overview

This class represents a Fedora datastream object and provides helper methods for creating and manipulating them.

Constant Summary collapse

DS_ATTRIBUTES =

mapping datastream attributes (and api parameters) to datastream profile names

{: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}
DS_DEFAULT_ATTRIBUTES =
{ :controlGroup => 'M', :dsState => 'A', :versionable => true }
DS_READONLY_ATTRIBUTES =
[ :dsCreateDate , :dsSize, :dsVersionID ]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(digital_object, dsid, options = {}, default_instance_attributes = {}) ⇒ Datastream

Initialize a Rubydora::Datastream object, which may or may not already exist in the datastore.

Provides ‘after_initialize` callback for extensions

Parameters:

  • (Rubydora::DigitalObject)
  • Datastream (String)

    ID

  • default (Hash)

    attribute values (used esp. for creating new datastreams)



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rubydora/datastream.rb', line 102

def initialize digital_object, dsid, options = {}, default_instance_attributes = {}
  _run_initialize_callbacks do
    @digital_object = digital_object
    @dsid = dsid
    @options = options
    @default_attributes = default_attributes.merge(default_instance_attributes)
    options.each do |key, value|
      self.send(:"#{key}=", value)
    end
  end
end

Instance Attribute Details

#digital_objectObject (readonly)

Returns the value of attribute digital_object.



16
17
18
# File 'lib/rubydora/datastream.rb', line 16

def digital_object
  @digital_object
end

#dsidObject (readonly)

Returns the value of attribute dsid.



16
17
18
# File 'lib/rubydora/datastream.rb', line 16

def dsid
  @dsid
end

Class Method Details

.default_attributesObject



81
82
83
# File 'lib/rubydora/datastream.rb', line 81

def self.default_attributes
  DS_DEFAULT_ATTRIBUTES
end

Instance Method Details

#asOfDateTime(asOfDateTime = nil) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/rubydora/datastream.rb', line 73

def asOfDateTime asOfDateTime = nil
  if asOfDateTime == nil
    return @asOfDateTime
  end

  return self.class.new(@digital_object, @dsid, @options.merge(:asOfDateTime => asOfDateTime))
end

#changed?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/rubydora/datastream.rb', line 199

def changed?
  super || content_changed?
end

#contentObject Also known as: read

This method is overridden in ActiveFedora, so we didn’t



126
127
128
# File 'lib/rubydora/datastream.rb', line 126

def content
  local_or_remote_content(true)
end

#content=(new_content) ⇒ String or IO

Set the content of the datastream

Parameters:

  • (String or IO)

Returns:

  • (String or IO)


174
175
176
177
# File 'lib/rubydora/datastream.rb', line 174

def content= new_content
  raise "Can't change values on older versions" if @asOfDateTime
   @content = new_content
end

#content_changed?Boolean

Returns:

  • (Boolean)


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/rubydora/datastream.rb', line 179

def content_changed?
  return false if ['E','R'].include? controlGroup
  return true if new? and !local_or_remote_content(false).blank? # new datastreams must have content

  if controlGroup == "X"
    if self.eager_load_datastream_content
      return !EquivalentXml.equivalent?(Nokogiri::XML(local_or_remote_content(false)), Nokogiri::XML(datastream_content))
    else
      return !EquivalentXml.equivalent?(Nokogiri::XML(local_or_remote_content(false)), Nokogiri::XML(@datastream_content))
    end
  else
    if self.eager_load_datastream_content
      return local_or_remote_content(false) != datastream_content
    else
      return local_or_remote_content(false) != @datastream_content
    end
  end
  super
end

#createRubydora::Datastream

Add datastream to Fedora



288
289
290
291
292
293
294
295
# File 'lib/rubydora/datastream.rb', line 288

def create
  check_if_read_only
  run_callbacks :create do
    repository.add_datastream to_api_params.merge({ :pid => pid, :dsid => dsid, :content => content })
    reset_profile_attributes
    self.class.new(digital_object, dsid, @options)
  end
end

#datastream_contentObject



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rubydora/datastream.rb', line 151

def datastream_content
  return nil if new?

  @datastream_content ||=begin
    options = { :pid => pid, :dsid => dsid }
    options[:asOfDateTime] = asOfDateTime if asOfDateTime

    repository.datastream_dissemination options
  rescue RestClient::ResourceNotFound
  end
end

#datastream_will_change!Object



322
323
324
# File 'lib/rubydora/datastream.rb', line 322

def datastream_will_change!
  attribute_will_change! :profile
end

#default_attributesObject



85
86
87
# File 'lib/rubydora/datastream.rb', line 85

def default_attributes
  @default_attributes ||= self.class.default_attributes
end

#default_attributes=(attributes) ⇒ Object



89
90
91
# File 'lib/rubydora/datastream.rb', line 89

def default_attributes= attributes
  @default_attributes = default_attributes.merge attributes
end

#deleteRubydora::Datastream

Purge the datastream from Fedora

Returns:



312
313
314
315
316
317
318
319
320
# File 'lib/rubydora/datastream.rb', line 312

def delete
  check_if_read_only
  run_callbacks :destroy do
    repository.purge_datastream(:pid => pid, :dsid => dsid) unless self.new?
    digital_object.datastreams.delete(dsid)
    reset_profile_attributes
    self
  end
end

#has_content?Boolean

Returns:

  • (Boolean)


203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/rubydora/datastream.rb', line 203

def has_content?
  # persisted objects are required to have content
  return true unless new?

  # type E and R objects should have content.
  return !dsLocation.blank? if ['E','R'].include? controlGroup

  # if we've set content, then we have content.

  # return true if instance_variable_defined? :@content

  behaves_like_io?(@content) || !content.blank?
end

#local_or_remote_content(ensure_fetch = true) ⇒ String

Retrieve the content of the datastream (and cache it)

Parameters:

  • ensure_fetch (Boolean) (defaults to: true)

    <true> if true, it will grab the content from the repository if is not already loaded

Returns:

  • (String)


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rubydora/datastream.rb', line 133

def local_or_remote_content(ensure_fetch = true)
  return @content if new? 

  @content ||= ensure_fetch ? datastream_content : @datastream_content

  if behaves_like_io?(@content)
    begin
      @content.rewind
      @content.read
    ensure
      @content.rewind
    end
  else
    @content
  end
end

#new?Boolean

Does this datastream already exist?

Returns:

  • (Boolean)


121
122
123
# File 'lib/rubydora/datastream.rb', line 121

def new?
  digital_object.nil? || digital_object.new? || profile_xml.blank?
end

#pidObject

Helper method to get digital object pid



115
116
117
# File 'lib/rubydora/datastream.rb', line 115

def pid
  digital_object.pid
end

#profile(opts = {}) ⇒ Hash

Retrieve the datastream profile as a hash (and cache it)

Parameters:

  • opts (Hash) (defaults to: {})

    :validateChecksum if you want fedora to validate the checksum

Returns:

  • (Hash)

    see Fedora #getDatastream documentation for keys



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/rubydora/datastream.rb', line 220

def profile opts= {}
  if @profile && !(opts[:validateChecksum] && !@profile.has_key?('dsChecksumValid'))
    ## Force a recheck of the profile if they've passed :validateChecksum and we don't have dsChecksumValid
    return @profile
  end
  
  return @profile = {} unless digital_object.respond_to? :repository
  
  @profile = begin
    xml = profile_xml(opts)

    (self.profile_xml_to_hash(xml) unless xml.blank?) || {}
  end
end

#profile=(profile_xml) ⇒ Object



253
254
255
# File 'lib/rubydora/datastream.rb', line 253

def profile= profile_xml
  @profile = self.profile_xml_to_hash(profile_xml)
end

#profile_xml(opts = {}) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rubydora/datastream.rb', line 235

def profile_xml opts = {}
  @profile_xml = nil unless opts.empty?
  
  @profile_xml ||= begin

    options = { :pid => pid, :dsid => dsid }
    options.merge!(opts)
    options[:asOfDateTime] = asOfDateTime if asOfDateTime
    options[:validateChecksum] = true if repository.config[:validateChecksum]
    repository.datastream(options)
  rescue RestClient::Unauthorized => e
    raise e
  rescue RestClient::ResourceNotFound
    # the datastream is new
    ''
  end
end

#profile_xml_to_hash(profile_xml) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rubydora/datastream.rb', line 257

def profile_xml_to_hash profile_xml
  profile_xml.gsub! '<datastreamProfile', '<datastreamProfile xmlns="http://www.fedora.info/definitions/1/0/management/"' unless profile_xml =~ /xmlns=/
  doc = Nokogiri::XML(profile_xml)
  h = doc.xpath('/management:datastreamProfile/*', {'management' => "http://www.fedora.info/definitions/1/0/management/"} ).inject({}) do |sum, node|
               sum[node.name] ||= []
               sum[node.name] << node.text
               sum
             end.reject { |key, values| values.empty? }
  h.select { |key, values| values.length == 1 }.each do |key, values|
    h[key] = values.reject { |x| x.empty? }.first 
  end

  h['dsSize'] &&= h['dsSize'].to_i rescue h['dsSize']
  h['dsCreateDate'] &&= Time.parse(h['dsCreateDate']) rescue h['dsCreateDate']
  h['dsChecksumValid'] &&= h['dsChecksumValid'] == 'true' 
  h['dsVersionable'] &&= h['dsVersionable'] == 'true' 
  h
end

#saveRubydora::Datastream

Modify or save the datastream



299
300
301
302
303
304
305
306
307
308
# File 'lib/rubydora/datastream.rb', line 299

def save
  check_if_read_only
  run_callbacks :save do
    raise RubydoraError.new("Unable to save #{self.inspect} without content") unless has_content?
    return create if new?
    repository.modify_datastream to_api_params.merge({ :pid => pid, :dsid => dsid })
    reset_profile_attributes
    self.class.new(digital_object, dsid, @options)
  end
end

#urlString

Get the URL for the datastream content

Returns:

  • (String)


165
166
167
168
169
# File 'lib/rubydora/datastream.rb', line 165

def url
  options = { }
  options[:asOfDateTime] = asOfDateTime if asOfDateTime
  repository.datastream_url(pid, dsid, options) + "/content"
end

#versionsObject



276
277
278
279
280
281
282
283
284
# File 'lib/rubydora/datastream.rb', line 276

def versions
  versions_xml = repository.datastream_versions(:pid => pid, :dsid => dsid)
  return [] if versions_xml.nil?
  versions_xml.gsub! '<datastreamProfile', '<datastreamProfile xmlns="http://www.fedora.info/definitions/1/0/management/"' unless versions_xml =~ /xmlns=/
  doc = Nokogiri::XML(versions_xml)
  doc.xpath('//management:datastreamProfile', {'management' => "http://www.fedora.info/definitions/1/0/management/"} ).map do |ds|
    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
  end
end