Class: ActiveFedora::Base

Inherits:
Object
  • Object
show all
Includes:
SemanticNode
Defined in:
lib/active_fedora/base.rb

Overview

This class ties together many of the lower-level modules, and implements something akin to an ActiveRecord-alike interface to fedora. If you want to represent a fedora object in the ruby space, this is the class you want to extend.

The Basics

class Oralhistory < ActiveFedora::Base
   :name => "properties", :type => ActiveFedora::SimpleDatastream do |m|
    m.field "narrator",  :string
    m.field "narrator",  :text
  end
end

The above example creates a Fedora object with a metadata datastream named “properties”, which is composed of a narrator and bio field.

Datastreams defined with has_metadata are accessed via the datastreams member hash.

Direct Known Subclasses

ContentModel

Instance Attribute Summary

Attributes included from SemanticNode

#load_from_solr, #relationships_loaded, #subject

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SemanticNode

#add_relationship, #assert_kind_of, #clear_relationship, #conforms_to?, #ids_for_outbound, #inbound_relationship_predicates, #inbound_relationships, #load_relationships, #object_relations, #outbound_relationship_predicates, #outbound_relationships, #relationship_predicates, #relationships, #relationships_are_dirty, #relationships_are_dirty=, #relationships_desc, #remove_relationship

Constructor Details

#initialize(attrs = nil) ⇒ Base

Constructor. You may supply a custom :pid, or we call the Fedora Rest API for the next available Fedora pid, and mark as new object. Also, if attrs does not contain :pid but does contain :namespace it will pass the :namespace value to Fedora::Repository.nextid to generate the next pid available within the given namespace.



84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/active_fedora/base.rb', line 84

def initialize(attrs = nil)
  attrs = {} if attrs.nil?
  @association_cache = {}
  attributes = attrs.dup
  @inner_object = UnsavedDigitalObject.new(self.class, attributes.delete(:namespace), attributes.delete(:pid))
  self.relationships_loaded = true
  load_datastreams

  [:new_object,:create_date, :modified_date].each { |k| attributes.delete(k)}
  self.attributes=attributes
  run_callbacks :initialize
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/active_fedora/base.rb', line 35

def method_missing(name, *args)
  dsid = corresponding_datastream_name(name)
  if dsid
    ### Create and invoke a proxy method 
    self.class.send :define_method, name do
        datastreams[dsid]
    end
    self.send(name)
  else 
    super
  end
end

Class Method Details

.assign_pid(obj) ⇒ String

if you are doing sharding, override this method to do something other than use a sequence

Returns:

  • (String)

    the unique pid for a new object



188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/active_fedora/base.rb', line 188

def self.assign_pid(obj)
  args = {}
  args[:namespace] = obj.namespace if obj.namespace
  # TODO: This juggling of Fedora credentials & establishing connections should be handled by 
  # an establish_fedora_connection method,possibly wrap it all into a fedora_connection method - MZ 06-05-2012
  if ActiveFedora.config.sharded?
    credentials = ActiveFedora.config.credentials[0]
  else
    credentials = ActiveFedora.config.credentials
  end
  fedora_connection[0] ||= ActiveFedora::RubydoraConnection.new(credentials)
  fedora_connection[0].connection.mint(args)
end

.connection_for_pid(pid) ⇒ Rubydora::Repository

Uses shard_index to find or create the rubydora connection for this pid

Parameters:

  • pid (String)

    the identifier of the object to get the connection for

Returns:

  • (Rubydora::Repository)

    The repository that the identifier exists in.



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/active_fedora/base.rb', line 135

def self.connection_for_pid(pid)
  idx = shard_index(pid)
  unless fedora_connection.has_key? idx
    if ActiveFedora.config.sharded?
      fedora_connection[idx] = RubydoraConnection.new(ActiveFedora.config.credentials[idx])
    else
      fedora_connection[idx] = RubydoraConnection.new(ActiveFedora.config.credentials)
    end
  end
  fedora_connection[idx].connection
end

.datastream_class_for_name(dsid) ⇒ Object



163
164
165
# File 'lib/active_fedora/base.rb', line 163

def self.datastream_class_for_name(dsid)
  ds_specs[dsid] ? ds_specs[dsid].fetch(:type, ActiveFedora::Datastream) : ActiveFedora::Datastream
end

.pids_from_uris(uris) ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
# File 'lib/active_fedora/base.rb', line 315

def self.pids_from_uris(uris) 
  if uris.class == String
    return uris.gsub("info:fedora/", "")
  elsif uris.class == Array
    arr = []
    uris.each do |uri|
      arr << uri.gsub("info:fedora/", "")
    end
    return arr
  end
end

.shard_index(pid) ⇒ Integer

This is where your sharding strategy is implemented – it’s how we figure out which shard an object will be (or was) written to. Given a pid, it decides which shard that pid will be written to (and thus retrieved from). For a given pid, as long as your shard configuration remains the same it will always return the same value. If you’re not using sharding, this will always return 0, meaning use the first/only Fedora Repository in your configuration. Default strategy runs a modulo of the md5 of the pid against the number of shards. If you want to use a different sharding strategy, override this method. Make sure that it will always return the same value for a given pid and shard configuration.

Returns:

  • (Integer)

    the index of the shard this object is stored in



154
155
156
157
158
159
160
# File 'lib/active_fedora/base.rb', line 154

def self.shard_index(pid)
  if ActiveFedora.config.sharded?
    Digest::MD5.hexdigest(pid).hex % ActiveFedora.config.credentials.length
  else
    0
  end
end

Instance Method Details

#==(comparison_object) ⇒ Object



264
265
266
267
268
269
# File 'lib/active_fedora/base.rb', line 264

def ==(comparison_object)
     comparison_object.equal?(self) ||
       (comparison_object.instance_of?(self.class) &&
         comparison_object.pid == pid &&
         !comparison_object.new_record?)
end

#adapt_to(klass) ⇒ Object

This method adapts the inner_object to a new ActiveFedora::Base implementation This is intended to minimize redundant interactions with Fedora



282
283
284
285
286
287
# File 'lib/active_fedora/base.rb', line 282

def adapt_to(klass)
  unless klass.ancestors.include? ActiveFedora::Base
    raise "Cannot adapt #{self.class.name} to #{klass.name}: Not a ActiveFedora::Base subclass"
  end
  klass.allocate.init_with(inner_object)
end

#adapt_to_cmodelObject

Examine the :has_model assertions in the RELS-EXT. Adapt this class to the first first known model



290
291
292
293
# File 'lib/active_fedora/base.rb', line 290

def adapt_to_cmodel
  the_model = ActiveFedora::ContentModel.known_models_for( self ).first
  self.class != the_model ? self.adapt_to(the_model) : self
end

#cloneObject



167
168
169
170
# File 'lib/active_fedora/base.rb', line 167

def clone
  new_object = self.class.create
  clone_into(new_object)
end

#clone_into(new_object) ⇒ Object

Clone the datastreams from this object into the provided object, while preserving the pid of the provided object

Parameters:

  • new_object (Base)

    clone into this object



174
175
176
177
178
179
180
181
182
183
184
# File 'lib/active_fedora/base.rb', line 174

def clone_into(new_object)
  rels = Nokogiri::XML( rels_ext.content)
  rels.xpath("//rdf:Description/@rdf:about").first.value = new_object.internal_uri
  new_object.rels_ext.content = rels.to_xml

  datastreams.each do |k, v|
    next if k == 'RELS-EXT'
    new_object.datastreams[k].content = v.content
  end
  new_object if new_object.save
end

#create_dateObject

return the create_date of the inner object (unless it’s a new object)



249
250
251
252
253
254
255
256
257
# File 'lib/active_fedora/base.rb', line 249

def create_date
  if @inner_object.new?
    Time.now
  elsif @inner_object.respond_to? :createdDate
    Array(@inner_object.createdDate).first
  else
    @inner_object.profile['objCreateDate']
  end
end

#idObject

Needed for the nested form helper



214
215
216
# File 'lib/active_fedora/base.rb', line 214

def id   ### Needed for the nested form helper
  self.pid
end

#init_with(inner_obj) ⇒ Object

Initialize an empty model object and set the inner_obj example:

class Post < ActiveFedora::Base
   :name => "properties", :type => ActiveFedora::SimpleDatastream
end

post = Post.allocate
post.init_with(DigitalObject.find(pid))
post.properties.title # => 'hello world'


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/active_fedora/base.rb', line 113

def init_with(inner_obj)
  @association_cache = {}
  @inner_object = inner_obj
  unless @inner_object.is_a? SolrDigitalObject
    @inner_object.original_class = self.class
    ## Replace existing unchanged datastreams with the definitions found in this class if they have a different type.
    ## Any datastream that is deleted here will cause a reload from fedora, so avoid it whenever possible
    ds_specs.keys.each do |key|
      if !@inner_object.datastreams[key].content_changed? && @inner_object.datastreams[key].class != self.class.ds_specs[key][:type]
        @inner_object.datastreams.delete(key)
      end
    end
  end
  load_datastreams
  run_callbacks :find
  run_callbacks :initialize
  self
end

#inner_objectObject

:nodoc



202
203
204
# File 'lib/active_fedora/base.rb', line 202

def inner_object # :nodoc
  @inner_object
end

#internal_uriObject

return the internal fedora URI



227
228
229
# File 'lib/active_fedora/base.rb', line 227

def internal_uri
  "info:fedora/#{pid}"
end

#labelObject



240
241
242
# File 'lib/active_fedora/base.rb', line 240

def label
  Array(@inner_object.label).first
end

#mark_for_destructionObject



66
67
68
# File 'lib/active_fedora/base.rb', line 66

def mark_for_destruction
  @marked_for_destruction = true
end

#marked_for_destruction?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/active_fedora/base.rb', line 70

def marked_for_destruction?
  @marked_for_destruction
end

#modified_dateObject

return the modification date of the inner object (unless it’s a new object)



260
261
262
# File 'lib/active_fedora/base.rb', line 260

def modified_date
  @inner_object.new? ? Time.now : Array(@inner_object.lastModifiedDate).first
end

#new?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/active_fedora/base.rb', line 48

def new?
  new_object?
end

#new_object?Boolean

Has this object been saved?

Returns:

  • (Boolean)


53
54
55
# File 'lib/active_fedora/base.rb', line 53

def new_object?
  inner_object.new?
end

#new_record?Boolean

Required by associations

Returns:

  • (Boolean)


58
59
60
# File 'lib/active_fedora/base.rb', line 58

def new_record?
  self.new_object?
end

#owner_idObject

return the owner id



232
233
234
# File 'lib/active_fedora/base.rb', line 232

def owner_id
  Array(@inner_object.ownerId).first
end

#owner_id=(owner_id) ⇒ Object



236
237
238
# File 'lib/active_fedora/base.rb', line 236

def owner_id=(owner_id)
  @inner_object.ownerId=(owner_id)
end

#persisted?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/active_fedora/base.rb', line 62

def persisted?
  !new_object?
end

#pidObject

return the pid of the Fedora Object if there is no fedora object (loaded from solr) get the instance var TODO make inner_object a proxy that can hold the pid



209
210
211
# File 'lib/active_fedora/base.rb', line 209

def pid
   @inner_object.pid
end

#pretty_pidObject



272
273
274
275
276
277
278
# File 'lib/active_fedora/base.rb', line 272

def pretty_pid
  if self.pid == UnsavedDigitalObject::PLACEHOLDER
    nil
  else
    self.pid
  end
end

#reifyObject

** EXPERIMENTAL ** This method returns a new object of the same class, with the internal SolrDigitalObject replaced with an actual DigitalObject.



298
299
300
301
302
303
# File 'lib/active_fedora/base.rb', line 298

def reify
  if self.inner_object.is_a? DigitalObject
    raise "#{self.inspect} is already a full digital object"
  end
  self.class.find self.pid
end

#reify!Object

** EXPERIMENTAL ** This method reinitializes a lightweight, loaded-from-solr object with an actual DigitalObject inside.



308
309
310
311
312
313
# File 'lib/active_fedora/base.rb', line 308

def reify!
  if self.inner_object.is_a? DigitalObject
    raise "#{self.inspect} is already a full digital object"
  end
  self.init_with DigitalObject.find(self.class,self.pid)
end

#reloadObject

Reloads the object from Fedora.



98
99
100
101
# File 'lib/active_fedora/base.rb', line 98

def reload(options = nil)
  @marked_for_destruction = false
  super
end

#stateObject



244
245
246
# File 'lib/active_fedora/base.rb', line 244

def state
  Array(@inner_object.state).first
end

#to_keyObject



222
223
224
# File 'lib/active_fedora/base.rb', line 222

def to_key
  persisted? ? [pid] : nil
end

#to_paramObject



218
219
220
# File 'lib/active_fedora/base.rb', line 218

def to_param
  persisted? ? to_key.join('-') : nil
end