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.



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/active_fedora/base.rb', line 79

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



184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/active_fedora/base.rb', line 184

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.



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/active_fedora/base.rb', line 131

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



159
160
161
# File 'lib/active_fedora/base.rb', line 159

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



334
335
336
337
338
339
340
341
342
343
344
# File 'lib/active_fedora/base.rb', line 334

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



150
151
152
153
154
155
156
# File 'lib/active_fedora/base.rb', line 150

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



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

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



278
279
280
281
282
283
# File 'lib/active_fedora/base.rb', line 278

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

Examines the :has_model assertions in the RELS-EXT.

If the object is of type ActiveFedora::Base, then use the first :has_model in the RELS-EXT for this object. Due to how the RDF writer works, this is currently just the first alphabetical model.

If the object was instantiated with any other class, then use that class as the base of the type the user wants rather than casting to the first :has_model found on the object.

In either case, if an extended model of that initial base model of the two cases above exists in the :has_model, then instantiate as that extended model type instead.



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/active_fedora/base.rb', line 298

def adapt_to_cmodel
  best_model_match = self.class unless self.instance_of? ActiveFedora::Base

  ActiveFedora::ContentModel.known_models_for( self ).each do |model_value|
    # If this is of type ActiveFedora::Base, then set to the first found :has_model.
    best_model_match ||= model_value

    # If there is an inheritance structure, use the most specific case.
    if best_model_match > model_value
      best_model_match = model_value
    end
  end

  self.instance_of?(best_model_match) ? self : self.adapt_to(best_model_match)
end

#cloneObject



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

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



170
171
172
173
174
175
176
177
178
179
180
# File 'lib/active_fedora/base.rb', line 170

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)



245
246
247
248
249
250
251
252
253
# File 'lib/active_fedora/base.rb', line 245

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



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

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'


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/active_fedora/base.rb', line 109

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] != nil && !@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



198
199
200
# File 'lib/active_fedora/base.rb', line 198

def inner_object # :nodoc
  @inner_object
end

#internal_uriObject

return the internal fedora URI



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

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

#labelObject



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

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)



256
257
258
# File 'lib/active_fedora/base.rb', line 256

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



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

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

#owner_id=(owner_id) ⇒ Object



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

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



205
206
207
# File 'lib/active_fedora/base.rb', line 205

def pid
   @inner_object.pid
end

#pretty_pidObject



268
269
270
271
272
273
274
# File 'lib/active_fedora/base.rb', line 268

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.



317
318
319
320
321
322
# File 'lib/active_fedora/base.rb', line 317

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.



327
328
329
330
331
332
# File 'lib/active_fedora/base.rb', line 327

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.



93
94
95
96
97
# File 'lib/active_fedora/base.rb', line 93

def reload
  raise ActiveFedora::ObjectNotFoundError, "Can't reload an object that hasn't been saved" unless persisted?
  clear_association_cache
  init_with(self.class.find(self.pid, cast: false).inner_object)
end

#stateObject



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

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

#to_keyObject



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

def to_key
  persisted? ? [pid] : nil
end

#to_paramObject



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

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