class ActiveFedora::Base


Datastreams defined with has_metadata are accessed via the datastreams member hash.
narrator and bio field.
The above example creates a Fedora object with a metadata datastream named “properties”, which is composed of a
end
end
m.field “narrator”, :text
m.field “narrator”, :string
has_metadata :name => “properties”, :type => ActiveFedora::MetadataDatastream do |m|
class Oralhistory < ActiveFedora::Base
=The Basics
space, this is the class you want to extend.
fedora. If you want to represent a fedora object in the ruby
implements something akin to an ActiveRecord-alike interface to
This class ties together many of the lower-level modules, and

def self.assign_pid(obj)

Returns:
  • (String) - the unique pid for a new object
def self.assign_pid(obj)
    args = {}
    args[:namespace] = obj.namespace if obj.namespace
    raise RuntimeError, "When using shards, you must override #{self}.assign_pid()" if ActiveFedora.config.sharded?
    d = REXML::Document.new(connection_for_pid('0').next_pid(args))
    d.elements['//pid'].text
end

def self.connection_for_pid(pid)

Returns:
  • (Rubydora::Repository) - The repository that the identifier exists in.

Parameters:
  • pid (String) -- the identifier of the object to get the connection for
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

def self.create(args = {})

def self.create(args = {})
  obj = self.new(args)
  obj.save
  obj
end

def self.datastream_class_for_name(dsid)

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

def self.load_instance_from_solr(pid,solr_doc=nil)

be loaded and if needed you should use find instead.
It will anything stored within solr such as metadata and relationships. Non-metadata datastreams will not

one passed to populate the object.
If a value is passed in for optional parameter solr_doc it will not query solr again and just use the

to populate this object.
a pid is passed in it will query solr for a corresponding solr document and then use it
It is most useful for objects used in read-only displays in order to speed up loading time. If only
It works similarly except it populates an object from Solr instead of Fedora.
This method can be used instead of ActiveFedora::Model::ClassMethods.find.
def self.load_instance_from_solr(pid,solr_doc=nil)
  if solr_doc.nil?
    result = find_by_solr(pid)
    raise ActiveFedora::ObjectNotFoundError, "Object #{pid} not found in solr" if result.nil?
    solr_doc = result.first
    #double check pid and id in record match
    raise ActiveFedora::ObjectNotFoundError, "Object #{pid} not found in Solr" unless !result.nil? && !solr_doc.nil? && pid == solr_doc[SOLR_DOCUMENT_ID]
  else
    raise "Solr document record id and pid do not match" unless pid == solr_doc[SOLR_DOCUMENT_ID]
  end
  klass = if class_str = solr_doc['active_fedora_model_s']
    class_str.first.constantize
  else
    ActiveFedora::Base
  end
  profile_json = Array(solr_doc[ActiveFedora::Base.profile_solr_name]).first
  unless profile_json.present?
    raise ActiveFedora::ObjectNotFoundError, "Object #{pid} does not contain a solrized profile"
  end
  profile_hash = ActiveSupport::JSON.decode(profile_json)
  obj = klass.allocate.init_with(SolrDigitalObject.new(solr_doc, profile_hash, klass))
  #set by default to load any dependent relationship objects from solr as well
  #need to call rels_ext once so it exists when iterating over datastreams
  obj.rels_ext
  obj.datastreams.each_value do |ds|
    if ds.respond_to?(:profile_from_hash) and (ds_prof = profile_hash['datastreams'][ds.dsid])
      ds.profile_from_hash(ds_prof)
    end
    if ds.respond_to?(:from_solr)
      ds.from_solr(solr_doc) if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::NokogiriDatastream) || ( ds.kind_of?(ActiveFedora::RelsExtDatastream))
    end
  end
  obj.inner_object.freeze
  obj
end

def self.pids_from_uris(uris)

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

def self.shard_index(pid)

Returns:
  • (Integer) - the index of the shard this object is stored in
def self.shard_index(pid)
  0
end

def ==(comparison_object)

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

def adapt_to(klass)

This is intended to minimize redundant interactions with Fedora
This method adapts the inner_object to a new ActiveFedora::Base implementation
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

def adapt_to_cmodel

Examine the :has_model assertions in the RELS-EXT. Adapt this class to the first first known model
def adapt_to_cmodel
  the_model = ActiveFedora::ContentModel.known_models_for( self ).first
  self.class != the_model ? self.adapt_to(the_model) : self
end

def attributes=(properties)

def attributes=(properties)
  properties.each do |k, v|
    respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}")
  end
end

def create_date

return the create_date of the inner object (unless it's a new object)
def create_date
  @inner_object.new? ? Time.now : @inner_object.profile["objCreateDate"]
end

def fields

and the object id.
system_create_date, system_modified_date, active_fedora_model_field,
ActiveFedora::MetadataDatastream datastreams, as well as
Return a hash of all available metadata fields for all
def fields
# TODO this can likely be removed once find_by_fields_by_solr is removed
  fields = {:id => {:values => [pid]}, :system_create_date => {:values => [self.create_date], :type=>:date}, :system_modified_date => {:values => [self.modified_date], :type=>:date}, :active_fedora_model => {:values => [self.class.inspect], :type=>:symbol}}
  datastreams.values.each do |ds|        
    fields.merge!(ds.fields) if [ActiveFedora::MetadataDatastream, ActiveFedora::QualifiedDublinCoreDatastream].include?(ds.class)
  end
  return fields
end

def get_values_from_datastream(dsid,field_key,default=[])

def get_values_from_datastream(dsid,field_key,default=[])
  if datastreams.include?(dsid)
    return datastreams[dsid].get_values(field_key,default)
  else
    return nil
  end
end

def id ### Needed for the nested form helper

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

def init_with(inner_obj)

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

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

example:
Initialize an empty model object and set the +inner_obj+
def init_with(inner_obj)
  @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].changed.include?(:content) && @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

def initialize(attrs = nil)

the given namespace.
+:namespace+ value to Fedora::Repository.nextid to generate the next pid available within
Also, if +attrs+ does not contain +:pid+ but does contain +:namespace+ it will pass the
next available Fedora pid, and mark as new object.
Constructor. You may supply a custom +:pid+, or we call the Fedora Rest API for the
def initialize(attrs = nil)
  attrs = {} if attrs.nil?
  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

def inner_object # :nodoc

:nodoc
def inner_object # :nodoc
  @inner_object
end

def inspect

def inspect
  "#<#{self.class}:#{self.hash} @pid=\"#{pid}\" >"
end

def internal_uri

return the internal fedora URI
def internal_uri
  "info:fedora/#{pid}"
end

def label

return the label of the inner object (unless it's a new object)
def label
  @inner_object.label
end

def label=(new_label)

def label=(new_label)
  @inner_object.label = new_label
end

def method_missing(name, *args)

def method_missing(name, *args)
  # if [:collection_members, :part_of, :parts, :part_of_append, :file_objects].include? name 
  #   ActiveSupport::Deprecation.warn("Deprecation: FileManagement will not be included by default in the next version.   To use #{name} add 'include ActiveFedora::FileManagement' to your model")
  #   self.class.send :include, FileManagement
  #   send name, *args
  # else 
    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

def modified_date

return the modification date of the inner object (unless it's a new object)
def modified_date
  @inner_object.new? ? Time.now : @inner_object.profile["objLastModDate"]
end

def new_object=(bool)

def new_object=(bool)
  ActiveSupport::Deprecation.warn("ActiveFedora::Base.new_object= has been deprecated and nolonger has any effect")
end

def new_object?

Has this object been saved?
def new_object?
  inner_object.new?
end

def new_record?

# Required by associations
def new_record?
  self.new_object?
end

def owner_id

return the owner id
def owner_id
  @inner_object.ownerId
end

def owner_id=(owner_id)

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

def persisted?

def persisted?
  !new_object?
end

def pid

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

def reify

replaced with an actual DigitalObject.
This method returns a new object of the same class, with the internal SolrDigitalObject
** EXPERIMENTAL **
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

def reify!

DigitalObject inside.
This method reinitializes a lightweight, loaded-from-solr object with an actual
** EXPERIMENTAL **
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

def solrize_profile(solr_doc = Hash.new) # :nodoc:

:nodoc:
def solrize_profile(solr_doc = Hash.new) # :nodoc:
  profile_hash = { 'datastreams' => {} }
  if inner_object.respond_to? :profile
    inner_object.profile.each_pair do |property,value|
      if property =~ /Date/
        value = Time.parse(value) unless value.is_a?(Time)
        value = value.xmlschema
      end
      profile_hash[property] = value
    end
  end
  self.datastreams.each_pair { |dsid,ds| profile_hash['datastreams'][dsid] = ds.solrize_profile }
  solr_doc[self.class.profile_solr_name] = profile_hash.to_json
end

def solrize_relationships(solr_doc = Hash.new)

Parameters:
  • solr_doc (Hash) -- @deafult an empty Hash
def solrize_relationships(solr_doc = Hash.new)
  relationships.each_statement do |statement|
    predicate = RelsExtDatastream.short_predicate(statement.predicate)
    literal = statement.object.kind_of?(RDF::Literal)
    val = literal ? statement.object.value : statement.object.to_str
    ::Solrizer::Extractor.insert_solr_field_value(solr_doc, solr_name(predicate, :symbol), val )
  end
  return solr_doc
end

def state

return the state of the inner object
def state 
  @inner_object.state
end

def to_key

def to_key
  persisted? ? [pid] : nil
end

def to_solr(solr_doc = Hash.new, opts={})

Parameters:
  • opts (Hash) -- (optional)
  • solr_doc (Hash) -- (optional) Hash to insert the fields into
def to_solr(solr_doc = Hash.new, opts={})
  unless opts[:model_only]
    c_time = create_date
    c_time = Time.parse(c_time) unless c_time.is_a?(Time)
    m_time = modified_date
    m_time = Time.parse(m_time) unless m_time.is_a?(Time)
    solr_doc.merge!(SOLR_DOCUMENT_ID.to_sym => pid, ActiveFedora::SolrService.solr_name(:system_create, :date) => c_time.utc.xmlschema, ActiveFedora::SolrService.solr_name(:system_modified, :date) => m_time.utc.xmlschema, ActiveFedora::SolrService.solr_name(:active_fedora_model, :symbol) => self.class.inspect)
    solrize_profile(solr_doc)
  end
  datastreams.each_value do |ds|
    ds.ensure_xml_loaded if ds.respond_to? :ensure_xml_loaded  ### Can't put this in the model because it's often implemented in Solrizer::XML::TerminologyBasedSolrizer 
    #puts "\n\nQDC #{ds.to_solr(solr_doc).inspect}" if ds.kind_of?(ActiveFedora::QualifiedDublinCoreDatastream)
    solr_doc = ds.to_solr(solr_doc) if ds.kind_of?(ActiveFedora::RDFDatastream) || ds.kind_of?(ActiveFedora::NokogiriDatastream) || ds.kind_of?(ActiveFedora::MetadataDatastream)
  end
  solr_doc = solrize_relationships(solr_doc) unless opts[:model_only]
  solr_doc
end

def to_xml(xml=Nokogiri::XML::Document.parse("<xml><fields/><content/></xml>"))

Returns the xml version of this object as a string.
def to_xml(xml=Nokogiri::XML::Document.parse("<xml><fields/><content/></xml>"))
  fields_xml = xml.xpath('//fields').first
  builder = Nokogiri::XML::Builder.with(fields_xml) do |fields_xml|
    fields_xml.id_ pid
    fields_xml.system_create_date self.create_date
    fields_xml.system_modified_date self.modified_date
    fields_xml.active_fedora_model self.class.inspect
  end
  
  # {:id => pid, :system_create_date => self.create_date, :system_modified_date => self.modified_date, :active_fedora_model => self.class.inspect}.each_pair do |attribute_name, value|
  #   el = REXML::Element.new(attribute_name.to_s)
  #   el.text = value
  #   fields_xml << el
  # end
  
  datastreams.each_value do |ds|  
    ds.to_xml(fields_xml) if ds.class.included_modules.include?(ActiveFedora::MetadataDatastreamHelper)
    ds.to_rels_ext if ds.kind_of?(ActiveFedora::RelsExtDatastream)
  end
  return xml.to_s
end

def update_datastream_attributes(params={}, opts={})

Other tags:
    Example: Update the descMetadata and properties datastreams with new values -

Parameters:
  • opts (Hash) -- (currently ignored.)
  • params (Hash) -- A Hash whose keys correspond to datastream ids and whose values are appropriate Hashes to submit to update_indexed_attributes on that datastream
def update_datastream_attributes(params={}, opts={})
  result = params.dup
  params.each_pair do |dsid, ds_params| 
    if datastreams.include?(dsid)
      result[dsid] = datastreams[dsid].update_indexed_attributes(ds_params)
    else
      result.delete(dsid)
    end
  end
  return result
end

def update_indexed_attributes(params={}, opts={})


m.update_attributes({"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}, :datastreams=>["my_ds", "my_other_ds"])
or
m.update_attributes({"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}, :datastreams=>"my_ds")
use the :datastreams argument like so:
If you want to specify which datastream(s) to update,

As in update_attributes, this overwrites _all_ available fields by default.

will be overwritten.
An index of -1 will insert a new value. any existing value at the relevant index

This will result in any datastream field of name :name having the value [a,b]

{{:name=>{"0"=>"a","1"=>"b"}}
must look like this :
A convenience method for updating indexed attributes. The passed in hash
def update_indexed_attributes(params={}, opts={})
  if ds = opts[:datastreams]
    ds_array = []
    ds = [ds] unless ds.respond_to? :each
    ds.each do |dsname|
      ds_array << datastreams[dsname]
    end
  else
    ds_array = metadata_streams
  end
  result = {}
  ds_array.each do |d|
    result[d.dsid] = d.update_indexed_attributes(params,opts)
  end
  return result
end