class ActiveFedora::Base

This class is really a facade for a basic Fedora::FedoraObject, which is stored internally.
=Implementation
Datastreams defined with has_metadata are accessed via the datastreams member hash.
narrator and bio field.
The above example creates a FedoraObject 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.deserialize(doc) #:nodoc:

:nodoc:
def self.deserialize(doc) #:nodoc:
  pid = doc.elements['/foxml:digitalObject'].attributes['PID']
  
  proto = self.new(:pid=>pid, :new_object=>false)
  proto.datastreams.each do |name,ds|
    doc.elements.each("//foxml:datastream[@ID='#{name}']") do |el|
      # datastreams remain marked as new if the foxml doesn't have an entry for that datastream
      ds.new_object = false
      proto.datastreams[name]=ds.class.from_xml(ds, el)
    end
  end
  proto.inner_object.new_object = false
  return proto
end

def self.has_metadata(args, &block)

by any future instantiations.
execute the block, but stores it at the class level, to be executed
args must include :name. Note that this method doesn't actually
This method is used to specify the details of a datastream.
def self.has_metadata(args, &block)
  @ds_specs ||= Hash.new
  @ds_specs[args[:name]]= [args[:type], block]
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 add(datastream) # :nodoc:

:nodoc:
def add(datastream) # :nodoc:
  warn "Warning: ActiveFedora::Base.add has been deprected.  Use add_datastream"
  add_datastream(datastream)
end

def add_datastream(datastream, opts={})

@returns DSID of the added datastream
:prefix option will set the prefix on auto-generated DSID
If datastream does not have a DSID, a unique DSID is generated
Adds datastream to the object. Saves the datastream to fedora upon adding.
def add_datastream(datastream, opts={})
  datastream.pid = self.pid
  if datastream.dsid == nil || datastream.dsid.empty?
    prefix = opts.has_key?(:prefix) ? opts[:prefix] : "DS"
    datastream.dsid = generate_dsid(prefix)
  end
  datastreams[datastream.dsid] = datastream
  return datastream.dsid
end

def add_file_datastream(file, opts={})

def add_file_datastream(file, opts={})
  label = opts.has_key?(:label) ? opts[:label] : ""
  ds = ActiveFedora::Datastream.new(:dsLabel => label, :controlGroup => 'M', :blob => file)
  opts.has_key?(:dsid) ? ds.dsid=(opts[:dsid]) : nil
  add_datastream(ds)
end

def add_relationship(predicate, obj)

Parameters:
  • object () -- Either a string URI or an object that responds to .pid
  • predicate () --
def add_relationship(predicate, obj)
  #predicate = ActiveFedora::RelsExtDatastream.predicate_lookup(predicate)
  r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>obj)
  rels_ext.add_relationship(r)
  rels_ext.dirty = true
end

def collection_members_append(obj)

def collection_members_append(obj)
  add_relationship(:has_collection_member, obj)
end

def collection_members_remove()

def collection_members_remove()
  # will rely on SemanticNode.remove_relationship once it is implemented
end

def configure_defined_datastreams

def configure_defined_datastreams
  if self.class.ds_specs
    self.class.ds_specs.each do |name,ar|
      ds = ar.first.new(:dsid=>name)
      ar.last.call(ds)
      self.add_datastream(ds)
    end
  end
end

def create

Deals with preparing new object to be saved to Fedora, then pushes it and its datastreams into Fedora.
def create
  add_relationship(:has_model, ActiveFedora::ContentModel.pid_from_ruby_class(self.class))
  @metadata_is_dirty = true
  update
  #@datastreams = datastreams_in_fedora
end

def create_date

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

def datastreams

the copy in Fedora.
Datastreams that have been modified in memory are given preference over
saved to fedora, the persisted datastreams will be included.
Returns all known datastreams for the object. If the object has been
def datastreams
  if @new_object
    @datastreams = datastreams_in_memory
  else
    @datastreams = (@datastreams == {}) ? datastreams_in_fedora : datastreams_in_memory
    #@datastreams = datastreams_in_fedora.merge(datastreams_in_memory)
  end
end

def datastreams_in_fedora #:nodoc:

:nodoc:
def datastreams_in_fedora #:nodoc:
  mds = {}
  self.datastreams_xml['datastream'].each do |ds|
    ds.merge!({:pid => self.pid, :dsID => ds["dsid"], :dsLabel => ds["label"]})
    if ds["dsid"] == "RELS-EXT" 
      mds.merge!({ds["dsid"] => ActiveFedora::RelsExtDatastream.new(ds)})
    else
      mds.merge!({ds["dsid"] => ActiveFedora::Datastream.new(ds)})
    end
    mds[ds["dsid"]].new_object = false
  end
  mds
end

def datastreams_in_memory #:ndoc:

:ndoc:
def datastreams_in_memory #:ndoc:
  @datastreams ||= Hash.new
end

def datastreams_xml

return the datastream xml representation direclty from Fedora
def datastreams_xml
  datastreams_xml = XmlSimple.xml_in(Fedora::Repository.instance.fetch_custom(self.pid, :datastreams))
end

def dc

the +datastreams["DC"]+.
Return the Dublin Core (DC) Datastream. You can also get at this via
def dc
  #dc = REXML::Document.new(datastreams["DC"].content)
  return  datastreams["DC"] 
end

def delete

the underlying inner_object.
Deletes a Base object, also deletes the info indexed in Solr, and
def delete
  Fedora::Repository.instance.delete(@inner_object)
  escaped_pid = self.pid.gsub(/(:)/, '\\:')
  SolrService.instance.conn.delete(escaped_pid) if ENABLE_SOLR_UPDATES 
end

def errors

return the error list of the inner object (unless it's a new object)
def errors
  @inner_object.errors
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
  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 ds.kind_of?(ActiveFedora::MetadataDatastream)
  end
  return fields
end

def file_objects

def file_objects
  collection_members
end

def file_objects_append(obj)

def file_objects_append(obj)
  collection_members_append(obj)
end

def file_streams

(that aren't Dublin Core or RELS-EXT streams either)
return all datastreams not of type ActiveFedora::MetadataDatastream
def file_streams
  results = []
  datastreams.each_value do |ds|
    if !ds.kind_of?(ActiveFedora::MetadataDatastream) 
      dsid = ds.dsid
      if dsid != "DC" && dsid != "RELS-EXT"
        results<<ds
      end
    end
  end
  return results
end

def generate_dsid(prefix="DS")

Example: if there are already datastreams with IDs DS1 and DS2, this method will return DS3. If you specify FOO as the prefix, it will return FOO1.
return a valid dsid that is not currently in use. Uses a prefix (default "DS") and an auto-incrementing integer
def generate_dsid(prefix="DS")
  keys = datastreams.keys
  next_index = keys.select {|v| v =~ /(#{prefix}\d*$)/}.length + 1
  new_dsid = prefix.to_s + next_index.to_s
  # while keys.include?(new_dsid)
  #         next_index += 1
  #         new_dsid = prefix.to_s + rand(range).to_s
  #       end
end

def initialize(attrs = {})

we configure any defined datastreams.
If there is a pid, we're re-hydrating an existing object, and new object is false. Once the @inner_object is stored,

and call off to the Fedora Rest API for the next available Fedora pid, and mark as new object.
Constructor. If +attrs+ does not comtain +:pid+, we assume we're making a new one,
def initialize(attrs = {})
  unless attrs[:pid]
    attrs = attrs.merge!({:pid=>Fedora::Repository.instance.nextid})  
    @new_object=true
  else
    @new_object = attrs[:new_object] == false ? false : true
  end
  @inner_object = Fedora::FedoraObject.new(attrs)
  @datastreams = {}
  configure_defined_datastreams
end

def inner_object # :nodoc

:nodoc
def inner_object # :nodoc
  @inner_object
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 metadata_streams

return all datastreams of type ActiveFedora::MetadataDatastream
def metadata_streams
  results = []
  datastreams.each_value do |ds|
    if ds.kind_of?(ActiveFedora::MetadataDatastream) 
      results<<ds
    end
  end
  return results
end

def modified_date

return the modification date of the inner object (unless it's a new object)
def modified_date
  @inner_object.modified_date unless new_object?
end

def new_object=(bool)

def new_object=(bool)
  @new_object = bool
  inner_object.new_object = bool
end

def new_object?

Has this object been saved?
def new_object?
  @new_object
end

def owner_id

return the owner id
def owner_id
  @inner_object.owner_id
end

def owner_id=(owner_id)

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

def pid

return the pid of the Fedora Object
def pid
  @inner_object.pid
end

def refresh

Note: Currently just registers any new datastreams that have appeared in fedora
Refreshes the object's info from Fedora
def refresh
  inner_object.load_attributes_from_fedora
  @datastreams = datastreams_in_fedora.merge(datastreams_in_memory)
end

def relationships

Overrides accessor for relationships array used by SemanticNode.
Rely on rels_ext datastream to track relationships array
@returns Hash of relationships, as defined by SemanticNode
def relationships
  return rels_ext.relationships
end

def rels_ext

Failing that, creates a new RelsExtDatastream and adds it to the object
Failing that, attempts to load from Fedora and addst to in-memory datastreams
Tries to grab from in-memory datastreams first
Returns the RELS-EXT Datastream
def rels_ext
  if !datastreams.has_key?("RELS-EXT") 
    add_datastream(ActiveFedora::RelsExtDatastream.new)
  end
  return datastreams["RELS-EXT"]
end

def save

the Solr index for this object.
Saves a Base object, and any dirty datastreams, then updates
def save
  #@metadata_is_dirty = false
  # If it's a new object, set the conformsTo relationship for Fedora CMA
  if new_object? 
    result = create
  else
    result = update
  end
  @new_object = false
  self.update_index if @metadata_is_dirty == true && ENABLE_SOLR_UPDATES
  @metadata_is_dirty == false
  return result
end

def state

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

def to_param

For Rails compatibility with url generators.
def to_param
  self.pid
end

def to_solr(solr_doc = Solr::Document.new)

Return a Solr::Document version of this object.
def to_solr(solr_doc = Solr::Document.new)
  solr_doc << {SOLR_DOCUMENT_ID.to_sym => pid, solr_name(:system_create, :date) => self.create_date, solr_name(:system_modified, :date) => self.modified_date, solr_name(:active_fedora_model, :symbol) => self.class.inspect}
  datastreams.each_value do |ds|
    solr_doc = ds.to_solr(solr_doc) if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::RelsExtDatastream)
  end
  return solr_doc
end

def to_xml(xml=REXML::Document.new("<xml><fields/><content/></xml>"))

Returns the xml version of this object as a string.
def to_xml(xml=REXML::Document.new("<xml><fields/><content/></xml>"))
  fields_xml = xml.root.elements['fields']
  {: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.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::RelsExtDatastream)
  end
  return xml.to_s
end

def update

Pushes the object and all of its new or dirty datastreams into Fedora
def update
  result = Fedora::Repository.instance.save(@inner_object)
  datastreams_in_memory.each do |k,ds|
    if ds.dirty? || ds.new_object? 
      if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.instance_of?(ActiveFedora::RelsExtDatastream)
        @metadata_is_dirty = true
      end
      result = ds.save
    end 
  end
  refresh
  return result
end

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

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

are _both_ overwritten.
the object's datastreams. This means DS1.fubar_values and DS2.fubar_values
This will attempt to set the values for any fields named fubar in any of

m.update_attributes(:fubar=>'baz')

Example Usage:

An ActiveRecord-ism to udpate metadata values.
def update_attributes(params={}, opts={})
  params.each do |k,v|
    if v == :delete || v == "" || v == nil
      v = []
    end
    if opts[:datastreams]
      ds_array = []
      opts[:datastreams].each do |dsname|
        ds_array << datastreams[dsname]
      end
    else
      ds_array = datastreams.values
    end
    ds_array.each do |d|
      if d.fields[k.to_sym]
        d.send("#{k}_values=", v)
      end
    end
  end
end

def update_index

Updates Solr index with self.
def update_index
  SolrService.instance.conn.update(self.to_solr)
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 opts[:datastreams]
    ds_array = []
    opts[:datastreams].each do |dsname|
      ds_array << datastreams[dsname]
    end
  else
    ds_array = datastreams.values
  end
  result = params.dup
  params.each do |key,value|
    result[key] = value.dup
    ds_array.each do |dstream|
      if dstream.fields[key.to_sym]
        aname="#{key}_values"
        curval = dstream.send("#{aname}")
        cpv=value.dup#copy this, we'll need the original for the next ds
        cpv.delete_if do |y,z| 
          if curval[y.to_i] and y.to_i > -1
            curval[y.to_i]=z
            true
          else
            false
          end
        end 
        cpv.each do |y,z| 
          curval<<z #just append everything left
          if y == "-1"
            new_array_index = curval.length - 1
            result[key][new_array_index.to_s] = params[key]["-1"]
          end
        end
        curval.delete_if {|x| x == :delete || x == "" || x == nil}
        dstream.send("#{aname}=", curval) #write it back to the ds
      end
    end
    result[key].delete("-1")
  end
  return result
end