Class: ActiveFedora::Base

Inherits:
Object
  • Object
show all
Includes:
Model, SemanticNode, MediaShelfClassLevelInheritableAttributes, Solrizer::FieldNameMapper
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::MetadataDatastream do |m|
    m.field "narrator",  :string
    m.field "narrator",  :text
  end
end

The above example creates a FedoraObject 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.

Implementation

This class is really a facade for a basic Fedora::FedoraObject, which is stored internally.

Direct Known Subclasses

ContentModel

Constant Summary

Constants included from SemanticNode

SemanticNode::PREDICATE_MAPPINGS

Instance Attribute Summary collapse

Attributes included from SemanticNode

#load_from_solr, #named_relationship_desc, #relationships_are_dirty

Attributes included from Model

#properties

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SemanticNode

#add_named_relationship, #assert_kind_of_model, #class_from_name, #inbound_named_relationship_predicates, #inbound_relationship_names, #inbound_relationships, included, #is_named_relationship?, #kind_of_model?, #named_inbound_relationships, #named_outbound_relationships, #named_relationship, #named_relationship_predicates, #named_relationship_predicates_from_class, #named_relationship_type, #named_relationships, #named_relationships_desc, #named_relationships_desc_from_class, #named_relationships_from_class, #outbound_named_relationship_predicates, #outbound_relationship_names, #outbound_relationships, #register_named_relationship, #register_named_subject, #register_predicate, #register_subject, #register_triple, #relationship_exists?, #relationship_names, #relationships_from_class, #remove_named_relationship, #to_rels_ext, #unregister_triple

Methods included from MediaShelfClassLevelInheritableAttributes

included

Methods included from Model

#add_metadata, #create_property_getter, #create_property_setter, #datastream, included

Constructor Details

#initialize(attrs = {}) ⇒ Base

Constructor. If attrs does not comtain :pid, we assume we’re making a new one, and call off to 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.

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



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/active_fedora/base.rb', line 65

def initialize(attrs = {})
  unless attrs[:pid]
    if attrs[:namespace]
      attrs = attrs.merge!({:pid=>Fedora::Repository.instance.nextid({:namespace=>attrs[:namespace]})})
    else
      attrs = attrs.merge!({:pid=>Fedora::Repository.instance.nextid})  
    end
    @new_object=true
  else
    @new_object = attrs[:new_object] == false ? false : true
  end
  @inner_object = Fedora::FedoraObject.new(attrs)
  @datastreams = {}
  configure_defined_datastreams
end

Instance Attribute Details

#named_datastreams_descObject

** EXPERIMENTAL **

Returns the hash that stores arguments passed to has_datastream calls within an ActiveFedora::Base child class.

has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav" 
has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'

The above examples result in the following hash

{"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
 "external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}

This hash is later used when adding a named datastream such as an “audio_file” as defined above.



541
542
543
# File 'lib/active_fedora/base.rb', line 541

def named_datastreams_desc
  @named_datastreams_desc
end

Class Method Details

.create_named_datastream_finders(name, prefix) ⇒ Object

** EXPERIMENTAL **

Creates the following helper methods for a datastream name

[datastream_name]  - Returns array of named datastreams
[datastream_name]_ids - Returns array of named datastream dsids

Examples for “thumbnails” datastream

thumbnails        -  Get array of thumbnail datastreams
thumbnails_ids    -  Get array of dsid's for thumbnail datastreams


642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
# File 'lib/active_fedora/base.rb', line 642

def self.create_named_datastream_finders(name, prefix)
  class_eval <<-END
  def #{name}(opts={})
    id_array = []
    keys = datastreams.keys
    id_array = keys.select {|v| v =~ /#{prefix}\d*/}
    if opts[:response_format] == :id_array
      return id_array
    else
      named_ds = []
      id_array.each do |name|
        if datastreams.has_key?(name)
          named_ds.push(datastreams[name])
        end
      end
      return named_ds
    end
  end
  def #{name}_ids
    #{name}(:response_format => :id_array)
  end
  END
end

.create_named_datastream_update_methods(name) ⇒ Object

** EXPERIMENTAL **

Creates the following helper methods for a datastream name

[datastream_name]_append  - Add a named datastream

Examples for “thumbnails” datastream

thumbnails_append -  Append a thumbnail datastream

TODO: Add [datastream_name]_remove



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
# File 'lib/active_fedora/base.rb', line 615

def self.create_named_datastream_update_methods(name)
  append_file_method_name = "#{name.to_s.downcase}_file_append"
  append_method_name = "#{name.to_s.downcase}_append"
  #remove_method_name = "#{name.to_s.downcase}_remove"
  self.send(:define_method,:"#{append_file_method_name}") do |*args| 
    file,opts = *args
    opts ||= {}
    add_named_file_datastream(name,file,opts)
  end
  
  self.send(:define_method,:"#{append_method_name}") do |*args| 
    opts = *args
    opts ||= {}
    #call add_named_datastream instead of add_file_named_datastream in case not managed datastream
    add_named_datastream(name,opts)
  end
end

.deserialize(doc) ⇒ Object

:nodoc:



778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
# File 'lib/active_fedora/base.rb', line 778

def self.deserialize(doc) #:nodoc:
  if doc.instance_of?(REXML::Document)
    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
  elsif doc.instance_of?(Nokogiri::XML::Document)
    pid = doc.xpath('/foxml:digitalObject').first["PID"]
  
    proto = self.new(:pid=>pid, :new_object=>false)
    proto.datastreams.each do |name,ds|
      doc.xpath("//foxml:datastream[@ID='#{name}']").each do |node|
        # datastreams remain marked as new if the foxml doesn't have an entry for that datastream
        ds.new_object = false
        # Nokogiri Datstreams use a new syntax for .from_xml (tmpl is optional!) and expects the actual xml content rather than the foxml datstream xml
        # NB: Base.deserialize, or a separately named method, should set any other info from the foxml if necessary though by this point it's all been grabbed elsewhere... 
        if ds.kind_of?(ActiveFedora::NokogiriDatastream) 
          xml_content = Fedora::Repository.instance.fetch_custom(pid, "datastreams/#{name}/content")
          # node = node.search('./foxml:datastreamVersion[last()]/foxml:xmlContent/*').first
          proto.datastreams[name]=ds.class.from_xml(xml_content, ds)
        else
          proto.datastreams[name]=ds.class.from_xml(ds, node)          
        end
      end
    end
    proto.inner_object.new_object = false
    return proto
  end
end

.has_datastream(args) ⇒ Object

** EXPERIMENTAL **

Allows for a datastream to be treated like any other attribute of a model class while enforcing mimeType and/or datastream type (ie. external, managed, etc.) if defined.

Examples

has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'                     
has_datastream :name=>"EADs", :type=>ActiveFedora::Datastream, :mimeType=>"application/xml", :controlGroup=>'M' 
has_datastream :name=>"external_images", :type=>ActiveFedora::Datastream, :controlGroup=>'E'

Required Keys in args

:name  -  name to give this datastream (must be unique)

Optional Keys in args

:prefix - used to create the DSID plus an index ie. THUMB1, THUMB2.  If :prefix is not specified, defaults to :name value in all uppercase 
:type - defaults to ActiveFedora::Datastream if you would like content specific class to be used supply it here
:mimeType - if supplied it will ensure any datastreams added are of this type, if not supplied any mimeType is acceptabl e
:controlGroup -  possible values "X", "M", "R", or "E" (InlineXML, Managed Content, Redirect, or External Referenced) If controlGroup is 'E' or 'R' it expects a dsLocation be defined when adding the datastream.

You use the datastream attribute using helper methods created for each datastream name:

Helper Method Examples

thumbnails_append -  Append a thumbnail datastream
thumbnails        -  Get array of thumbnail datastreams
thumbnails_ids    -  Get array of dsid's for thumbnail datastreams

When loading the list of datastreams for a name from Fedora it uses the DSID prefix to find them in Fedora



585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/active_fedora/base.rb', line 585

def self.has_datastream(args)
  unless args.has_key?(:name)
    return false
  end
  unless args.has_key?(:prefix)
    args.merge!({:prefix=>args[:name].to_s.upcase})
  end
  unless named_datastreams_desc.has_key?(args[:name]) 
    named_datastreams_desc[args[:name]] = {} 
  end
      
  args.merge!({:mimeType=>args[:mime_type]}) if args.has_key?(:mime_type)
  
  unless named_datastreams_desc[args[:name]].has_key?(:type) 
    #default to type ActiveFedora::Datastream
    args.merge!({:type => "ActiveFedora::Datastream"})
  end
  named_datastreams_desc[args[:name]]= args   
  create_named_datastream_finders(args[:name],args[:prefix])
  create_named_datastream_update_methods(args[:name])
end

.has_metadata(args, &block) ⇒ Object

This method is used to specify the details of a datastream. args must include :name. Note that this method doesn’t actually execute the block, but stores it at the class level, to be executed by any future instantiations.



85
86
87
88
# File 'lib/active_fedora/base.rb', line 85

def self.(args, &block)
  @ds_specs ||= Hash.new
  @ds_specs[args[:name]]= [args[:type], block]
end

.load_instance_from_solr(pid, solr_doc = nil) ⇒ Object

** EXPERIMENTAL **

This method can be used instead of ActiveFedora::Model::ClassMethods.load_instance.

It works similarly except it populates an object from Solr instead of Fedora. It is most useful for objects used in read-only displays in order to speed up loading time. If only a pid is passed in it will query solr for a corresponding solr document and then use it to populate this object.

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

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



884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
# File 'lib/active_fedora/base.rb', line 884

def self.load_instance_from_solr(pid,solr_doc=nil)
  if solr_doc.nil?
    result = find_by_solr(pid)
    raise "Object #{pid} not found in solr" if result.nil?
    solr_doc = result.hits.first
    #double check pid and id in record match
    raise "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
 
  create_date = solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date)].nil? ? solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date).to_s] : solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date)]
  modified_date = solr_doc[ActiveFedora::SolrService.solr_name(:system_create, :date)].nil? ? solr_doc[ActiveFedora::SolrService.solr_name(:system_modified, :date).to_s] : solr_doc[ActiveFedora::SolrService.solr_name(:system_modified, :date)]
  obj = self.new({:pid=>solr_doc[SOLR_DOCUMENT_ID],:create_date=>create_date,:modified_date=>modified_date})
  obj.new_object = false
  #set by default to load any dependent relationship objects from solr as well
  obj.load_from_solr = true
  #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?(: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
end

.named_datastreams_descObject

** EXPERIMENTAL **

Accessor for class variable for hash that stores arguments passed to has_datastream calls within an ActiveFedora::Base child class.

has_datastream :name=>"audio_file", :prefix=>"AUDIO", :type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav" 
has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'

The above examples result in the following hash

{"audio_file"=>{:prefix=>"AUDIO",:type=>ActiveFedora::Datastream, :mimeType=>"audio/x-wav", :controlGroup=>'M'},
 "external_images=>{:prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'}}

This hash is later used when adding a named datastream such as an “audio_file” as defined above.



679
680
681
# File 'lib/active_fedora/base.rb', line 679

def self.named_datastreams_desc
  @class_named_datastreams_desc ||= {}
end

.pids_from_uris(uris) ⇒ Object



1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/active_fedora/base.rb', line 1008

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

Instance Method Details

#add(datastream) ⇒ Object

:nodoc:



191
192
193
194
# File 'lib/active_fedora/base.rb', line 191

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

#add_datastream(datastream, opts = {}) ⇒ Object

Adds datastream to the object. Saves the datastream to fedora upon adding. If datastream does not have a DSID, a unique DSID is generated :prefix option will set the prefix on auto-generated DSID



182
183
184
185
186
187
188
189
190
# File 'lib/active_fedora/base.rb', line 182

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

#add_file_datastream(file, opts = {}) ⇒ Object

Add the given file as a datastream in the object

Parameters:

  • file (File)

    the file to add

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

    options: :dsid, :label, :mimeType



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/active_fedora/base.rb', line 260

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

#add_named_datastream(name, opts = {}) ⇒ Object

** EXPERIMENTAL **

This object is used by [datastream_name]_append helper to add a named datastream but can also be called directly.

Parameters

name: name of datastream to add
opts: hash defining datastream attributes

The following are expected keys in opts hash:

:label => Defaults to the file name
:blob or :file  => Required to point to the datastream file being added if managed content
:controlGroup => defaults to 'M' for managed, can also be 'E' external and 'R' for redirected
:content_type => required if the file does not respond to 'content_type' and should match :mimeType in has_datastream definition if defined
:dsLocation => holds uri location of datastream.  Required only if :controlGroup is type 'E' or 'R'.
:dsid or :dsId => Optional, and used to update an existing datastream with dsid supplied.  Will throw an error if dsid does not exist and does not match prefix pattern for datastream name


359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/active_fedora/base.rb', line 359

def add_named_datastream(name,opts={})
  
  unless named_datastreams_desc.has_key?(name) && named_datastreams_desc[name].has_key?(:type) 
    raise "Failed to add datastream. Named datastream #{name} not defined for object #{pid}." 
  end

  if opts.has_key?(:mime_type)
    opts.merge!({:content_type=>opts[:mime_type]})
  elsif opts.has_key?(:mimeType)
    opts.merge!({:content_type=>opts[:mimeType]})
  end
  opts.merge!(named_datastreams_desc[name])
    
  label = opts.has_key?(:label) ? opts[:label] : ""

  #only do these steps for managed datastreams
  unless (opts.has_key?(:controlGroup)&&opts[:controlGroup]!="M")
    if opts.has_key?(:file)
      opts.merge!({:blob => opts[:file]}) 
      opts.delete(:file)
    end
    
    raise "You must define parameter blob for this managed datastream to load for #{pid}" unless opts.has_key?(:blob)
    
    #if no explicit label and is a file use original file name for label
    if !opts.has_key?(:label)&&opts[:blob].respond_to?(:original_filename)
      label = opts[:blob].original_filename
    end
    
    if opts[:blob].respond_to?(:content_type)&&!opts[:blob].content_type.nil? && !opts.has_key?(:content_type)
      opts.merge!({:content_type=>opts[:blob].content_type})
    end

    raise "The blob must respond to content_type or the hash must have :content_type or :mime_type property set" unless opts.has_key?(:content_type)
    
    #throw error for mimeType mismatch
    if named_datastreams_desc[name].has_key?(:mimeType) && !named_datastreams_desc[name][:mimeType].eql?(opts[:content_type])
      raise "Content type mismatch for add datastream #{name} to #{pid}.  Expected: #{named_datastreams_desc[name][:mimeType]}, Actual: #{opts[:content_type]}"
    end
  else 
    label = opts[:dsLocation] if (opts.has_key?(:dsLocation)) 
  end
  
  opts.merge!(:dsLabel => label)
  
  #make sure both dsid and dsID populated if a dsid is supplied
  opts.merge!(:dsid=>opts[:dsID]) if opts.has_key?(:dsID)
  opts.merge!(:dsID=>opts[:dsid]) if opts.has_key?(:dsid)
  
  ds = create_datastream(named_datastreams_desc[name][:type],opts)
  #Must be of type datastream
  assert_kind_of 'datastream',  ds, ActiveFedora::Datastream
  #make sure dsid is nil so that it uses the prefix for mapping purposes
  #check dsid works for the prefix if it is set
  if !ds.dsid.nil? && opts.has_key?(:prefix)
    raise "dsid supplied does not conform to pattern #{opts[:prefix]}[number]" unless ds.dsid =~ /#{opts[:prefix]}[0-9]/
  end
  
  add_datastream(ds,opts)
end

#add_named_file_datastream(name, file, opts = {}) ⇒ Object

** EXPERIMENTAL **

Calls add_named_datastream while assuming it will be managed content and sets :blob and :controlGroup values accordingly

Parameters

name: Datastream name
file: The file to add for this datastream
opts: Options hash.  See +add_named_datastream+ for expected keys and values


340
341
342
343
# File 'lib/active_fedora/base.rb', line 340

def add_named_file_datastream(name, file, opts={})
  opts.merge!({:blob=>file,:controlGroup=>'M'})
  add_named_datastream(name,opts)
end

#add_relationship(predicate, obj) ⇒ Object

Add a Rels-Ext relationship to the Object.

Parameters:

  • predicate
  • object

    Either a string URI or an object that responds to .pid



698
699
700
701
702
703
704
705
706
# File 'lib/active_fedora/base.rb', line 698

def add_relationship(predicate, obj)
  r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>obj)
  unless relationship_exists?(r.subject, r.predicate, r.object)
    rels_ext.add_relationship(r)
    #need to call here to indicate update of named_relationships
    @relationships_are_dirty = true
    rels_ext.dirty = true
  end
end

#assert_kind_of(n, o, t) ⇒ Object

** EXPERIMENTAL **

Throws an assertion failure unless the object ‘o’ is kind_of? class ‘t’

Parameters

n: Name of object
o: The object to test
t: The class type to check is kind_of?


442
443
444
# File 'lib/active_fedora/base.rb', line 442

def assert_kind_of(n, o,t)
  raise "Assertion failure: #{n}: #{o} is not of type #{t}" unless o.kind_of?(t)
end

#collection_members_append(obj) ⇒ Object



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

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

#collection_members_removeObject



322
323
324
# File 'lib/active_fedora/base.rb', line 322

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

#create_datastream(type, opts = {}) ⇒ Object



553
554
555
556
# File 'lib/active_fedora/base.rb', line 553

def create_datastream(type,opts={})
  type.to_s.split('::').inject(Kernel) {|scope, const_name| 
  scope.const_get(const_name)}.new(opts)
end

#create_dateObject

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



754
755
756
# File 'lib/active_fedora/base.rb', line 754

def create_date
  @inner_object.create_date unless new_object?
end

#datastream_namesObject

** EXPERIMENTAL **

Returns array of datastream names defined for this object



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

def datastream_names
  named_datastreams_desc.keys
end

#datastreamsObject

Returns all known datastreams for the object. If the object has been saved to fedora, the persisted datastreams will be included. Datastreams that have been modified in memory are given preference over the copy in Fedora.



145
146
147
148
149
150
151
152
153
# File 'lib/active_fedora/base.rb', line 145

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

#datastreams_attributesObject

** EXPERIMENTAL **

For all datastream objects, this returns hash of dsid mapped to attribute hash within the corresponding datastream object.



520
521
522
523
524
525
526
# File 'lib/active_fedora/base.rb', line 520

def datastreams_attributes
  ds_values = {}
  self.datastreams.each_pair do |dsid,ds|
    ds_values.merge!({dsid=>ds.attributes})
  end
  return ds_values
end

#datastreams_in_fedoraObject

:nodoc:



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/active_fedora/base.rb', line 155

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

#datastreams_in_memoryObject

:ndoc:



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

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

#datastreams_xmlObject

return the datastream xml representation direclty from Fedora



174
175
176
# File 'lib/active_fedora/base.rb', line 174

def datastreams_xml
  datastreams_xml = XmlSimple.xml_in(Fedora::Repository.instance.fetch_custom(self.pid, :datastreams))
end

#dcObject

Return the Dublin Core (DC) Datastream. You can also get at this via the datastreams.



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

def dc
  #dc = REXML::Document.new(datastreams["DC"].content)
  return  datastreams["DC"] 
end

#deleteObject

Deletes a Base object, also deletes the info indexed in Solr, and the underlying inner_object. If this object is held in any relationships (ie inbound relationships outside of this object it will remove it from those items rels-ext as well



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/active_fedora/base.rb', line 116

def delete
  inbound_relationships(:objects).each_pair do |predicate, objects|
    objects.each do |obj|
      if obj.respond_to?(:remove_relationship)
        obj.remove_relationship(predicate,self)
        obj.save
      end 
    end
  end
  
  Fedora::Repository.instance.delete(@inner_object)
  if ENABLE_SOLR_UPDATES
    ActiveFedora::SolrService.instance.conn.delete(pid) 
    # if defined?( Solrizer::Solrizer ) 
    #   solrizer = Solrizer::Solrizer.new
    #   solrizer.solrize_delete(pid)
    # end
  end
end

#errorsObject

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



764
765
766
# File 'lib/active_fedora/base.rb', line 764

def errors
  @inner_object.errors
end

#fieldsObject

Return a hash of all available metadata fields for all ActiveFedora::MetadataDatastream datastreams, as well as system_create_date, system_modified_date, active_fedora_model_field, and the object id.



820
821
822
823
824
825
826
# File 'lib/active_fedora/base.rb', line 820

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

#file_objects(opts = {}) ⇒ Array of ActiveFedora objects, ...

List the objects that assert isPartOf pointing at this object plus all objects that this object asserts hasPart for

Note: Previous versions of ActiveFedora used hasCollectionMember to represent this type of relationship.  
To accommodate this, until active-fedora-1.3, .file_assets will also return anything that this asserts hasCollectionMember for and will output a warning in the logs.

Parameters:

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

    – same options as auto-generated methods for relationships (ie. :response_format)

Returns:

  • (Array of ActiveFedora objects, Array of PIDs, or Solr::Result)

    – same options as auto-generated methods for relationships (ie. :response_format)



282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/active_fedora/base.rb', line 282

def file_objects(opts={})
  cm_array = collection_members(:response_format=>:id_array)
  
  if !cm_array.empty?
    logger.warn "This object has collection member assertions.  hasCollectionMember will no longer be used to track file_object relationships after active_fedora 1.3.  Use isPartOf assertions in the RELS-EXT of child objects instead."
    if opts[:response_format] == :solr || opts[:response_format] == :load_from_solr
      logger.warn ":solr and :load_from_solr response formats for file_objects search only uses parts relationships (usage of hasCollectionMember is no longer supported)"
      result = parts(opts)
    else
      cm_result = collection_members(opts)
      parts_result = parts(opts)
      ary = cm_result+parts_result
      result = ary.uniq
    end
  else
    result = parts(opts)
  end
  return result
end

#file_objects_append(obj) ⇒ Object

Add the given file as a datastream in the object

Parameters:



305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/active_fedora/base.rb', line 305

def file_objects_append(obj)
  # collection_members_append(obj)
  unless obj.kind_of? ActiveFedora::Base
    begin
      obj = ActiveFedora::Base.load_instance(obj)
    rescue ActiveFedora::ObjectNotFoundError
      "You must provide either an ActiveFedora object or a valid pid to add it as a file object.  You submitted #{obj.inspect}"
    end
  end
  obj.add_relationship(:is_part_of, self)
  obj.save
end

#file_streamsObject

return all datastreams not of type ActiveFedora::MetadataDatastream (that aren’t Dublin Core or RELS-EXT streams either)



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/active_fedora/base.rb', line 209

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

#generate_dsid(prefix = "DS") ⇒ Object

return a valid dsid that is not currently in use. Uses a prefix (default “DS”) and an auto-incrementing integer 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.



224
225
226
227
228
229
230
231
232
# File 'lib/active_fedora/base.rb', line 224

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

#get_values_from_datastream(dsid, field_key, default = []) ⇒ Object



1000
1001
1002
1003
1004
1005
1006
# File 'lib/active_fedora/base.rb', line 1000

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

#inner_objectObject

:nodoc



721
722
723
# File 'lib/active_fedora/base.rb', line 721

def inner_object # :nodoc
  @inner_object
end

#internal_uriObject

return the internal fedora URI



735
736
737
# File 'lib/active_fedora/base.rb', line 735

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

#is_named_datastream?(name) ⇒ Boolean

** EXPERIMENTAL **

Returns true if the name is a defined named datastream

Returns:

  • (Boolean)


449
450
451
# File 'lib/active_fedora/base.rb', line 449

def is_named_datastream?(name)
  named_datastreams_desc.has_key?(name)
end

#labelObject

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



769
770
771
# File 'lib/active_fedora/base.rb', line 769

def label
  @inner_object.label
end

#label=(new_label) ⇒ Object



773
774
775
# File 'lib/active_fedora/base.rb', line 773

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

#loggerObject



1020
1021
1022
# File 'lib/active_fedora/base.rb', line 1020

def logger      
  @logger ||= defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT)
end

#metadata_streamsObject

return all datastreams of type ActiveFedora::MetadataDatastream



197
198
199
200
201
202
203
204
205
# File 'lib/active_fedora/base.rb', line 197

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

#modified_dateObject

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



759
760
761
# File 'lib/active_fedora/base.rb', line 759

def modified_date
  @inner_object.modified_date unless new_object?
end

#named_datastreamsObject

** EXPERIMENTAL **

Returns hash of datastream names defined by has_datastream calls mapped to array of datastream objects that have been added

Example

For the following has_datastream entries and a datastream defined for minivan only would be

has_datastream :name=>"minivan", :prefix => "VAN", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'M'
has_datastream :name=>"external_images", :prefix=>"EXTIMG", :type=>ActiveFedora::Datastream,:mimeType=>"image/jpeg", :controlGroup=>'E'

Returns

{"external_images"=>[],"thumbnails"=>{#<ActiveFedora::Datastream:0x7ffd6512daf8 @new_object=true,...}}


464
465
466
467
468
469
470
# File 'lib/active_fedora/base.rb', line 464

def named_datastreams
  ds_values = {}
  self.class.named_datastreams_desc.keys.each do |name|
    ds_values.merge!({name=>self.send("#{name}")})
  end
  return ds_values
end

#named_datastreams_attributesObject

** EXPERIMENTAL **

Returns hash of datastream names mapped to another hash of dsid to attributes for corresponding datastream objects

Example

For the following has_datastream call, assume we have added one datastream.

has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'

It would then return

{"thumbnails"=>{"THUMB1"=>{:prefix=>"VAN", :type=>"ActiveFedora::Datastream", :dsid=>"THUMB1", :dsID=>"THUMB1", :pid=>"changme:33", :mimeType=>"image/jpeg", :dsLabel=>"", :name=>"thumbnails", :content_type=>"image/jpeg", :controlGroup=>"M"}}}


483
484
485
486
487
488
489
490
491
492
493
494
# File 'lib/active_fedora/base.rb', line 483

def named_datastreams_attributes
  ds_values = {}
  self.class.named_datastreams_desc.keys.each do |name|
    ds_array = self.send("#{name}")
    result_hash = {}
    ds_array.each do |ds|
      result_hash[ds.dsid]=ds.attributes
    end
    ds_values.merge!({name=>result_hash})
  end
  return ds_values
end

#named_datastreams_desc_from_classObject

** EXPERIMENTAL **

Get class variable hash that stores has_datastream arguments. It is used to initialize the value returned by public named_datastreams_desc method



549
550
551
# File 'lib/active_fedora/base.rb', line 549

def named_datastreams_desc_from_class
  self.class.named_datastreams_desc
end

#named_datastreams_idsObject

** EXPERIMENTAL **

Returns hash of datastream names mapped to an array of dsid’s for named datastream objects

Example

For the following has_datastream call, assume we have added two datastreams.

has_datastream :name=>"thumbnails",:prefix => "THUMB",:type=>ActiveFedora::Datastream, :mimeType=>"image/jpeg", :controlGroup=>'M'

It would then return

{"thumbnails=>["THUMB1", "THUMB2"]}


507
508
509
510
511
512
513
514
# File 'lib/active_fedora/base.rb', line 507

def named_datastreams_ids
  dsids = {}
  self.class.named_datastreams_desc.keys.each do |name|
    dsid_array = self.send("#{name}_ids")
    dsids[name] = dsid_array
  end
  return dsids
end

#new_object=(bool) ⇒ Object



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

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

#new_object?Boolean

Has this object been saved?

Returns:

  • (Boolean)


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

def new_object?
  @new_object
end

#owner_idObject

return the owner id



745
746
747
# File 'lib/active_fedora/base.rb', line 745

def owner_id
  @inner_object.owner_id
end

#owner_id=(owner_id) ⇒ Object



749
750
751
# File 'lib/active_fedora/base.rb', line 749

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

#pidObject

return the pid of the Fedora Object



726
727
728
# File 'lib/active_fedora/base.rb', line 726

def pid
  @inner_object.pid
end

#refreshObject

Refreshes the object’s info from Fedora Note: Currently just registers any new datastreams that have appeared in fedora



108
109
110
111
# File 'lib/active_fedora/base.rb', line 108

def refresh
  inner_object.load_attributes_from_fedora
  @datastreams = datastreams_in_fedora.merge(datastreams_in_memory)
end

#relationships(outbound_only = true) ⇒ Object

Rely on rels_ext datastream to track relationships array Overrides accessor for relationships array used by SemanticNode. If outbound_only is false, inbound relationships will be included.



691
692
693
# File 'lib/active_fedora/base.rb', line 691

def relationships(outbound_only=true)
  outbound_only ? rels_ext.relationships : rels_ext.relationships.merge(:inbound=>inbound_relationships)
end

#rels_extObject

Returns the RELS-EXT Datastream Tries to grab from in-memory datastreams first Failing that, attempts to load from Fedora and addst to in-memory datastreams Failing that, creates a new RelsExtDatastream and adds it to the object



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

def rels_ext
  if !datastreams.has_key?("RELS-EXT") 
    add_datastream(ActiveFedora::RelsExtDatastream.new)
  end
  return datastreams["RELS-EXT"]
end

#remove_relationship(predicate, obj) ⇒ Object

** EXPERIMENTAL **

Remove a Rels-Ext relationship from the Object.

Parameters:

  • predicate
  • object

    Either a string URI or an object that responds to .pid



713
714
715
716
717
718
719
# File 'lib/active_fedora/base.rb', line 713

def remove_relationship(predicate, obj)
  r = ActiveFedora::Relationship.new(:subject=>:self, :predicate=>predicate, :object=>obj)
  rels_ext.remove_relationship(r)
  #need to call here to indicate update of named_relationships
  @relationships_are_dirty = true
  rels_ext.dirty = true
end

#saveObject

Saves a Base object, and any dirty datastreams, then updates the Solr index for this object.



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/active_fedora/base.rb', line 92

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

#stateObject

return the state of the inner object



740
741
742
# File 'lib/active_fedora/base.rb', line 740

def state 
  @inner_object.state
end

#to_paramObject

For Rails compatibility with url generators.



731
732
733
# File 'lib/active_fedora/base.rb', line 731

def to_param
  self.pid
end

#to_solr(solr_doc = Hash.new, opts = {}) ⇒ Object

Return a Hash representation of this object where keys in the hash are appropriate Solr field names. If opts == true, the base object metadata and the RELS-EXT datastream will be omitted. This is mainly to support shelver, which calls .to_solr for each model an object subscribes to.



854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
# File 'lib/active_fedora/base.rb', line 854

def to_solr(solr_doc = Hash.new, opts={})
  unless opts[:model_only]
    solr_doc.merge!(SOLR_DOCUMENT_ID.to_sym => pid, ActiveFedora::SolrService.solr_name(:system_create, :date) => self.create_date, ActiveFedora::SolrService.solr_name(:system_modified, :date) => self.modified_date, ActiveFedora::SolrService.solr_name(:active_fedora_model, :symbol) => self.class.inspect)
  end
  datastreams.each_value do |ds|
    # solr_doc = ds.to_solr(solr_doc) if ds.class.included_modules.include?(ActiveFedora::MetadataDatastreamHelper) ||( ds.kind_of?(ActiveFedora::RelsExtDatastream) || ( ds.kind_of?(ActiveFedora::QualifiedDublinCoreDatastream) && !opts[:model_only] )
    solr_doc = ds.to_solr(solr_doc) if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::NokogiriDatastream) || ( ds.kind_of?(ActiveFedora::RelsExtDatastream) && !opts[:model_only] )
  end
  begin
    #logger.info("PID: '#{pid}' solr_doc put into solr: #{solr_doc.inspect}")
  rescue
    logger.info("Error encountered trying to output solr_doc details for pid: #{pid}")
  end
  return solr_doc
end

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

Returns the xml version of this object as a string.



829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
# File 'lib/active_fedora/base.rb', line 829

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.kind_of?(ActiveFedora::RelsExtDatastream)
  end
  return xml.to_s
end

#update_attributes(params = {}, opts = {}) ⇒ Object

An ActiveRecord-ism to udpate metadata values.

Example Usage:

m.update_attributes(:fubar=>‘baz’)

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

If you want to specify which datastream(s) to update, use the :datastreams argument like so:

m.update_attributes({:fubar=>'baz'}, :datastreams=>"my_ds")

or

m.update_attributes({:fubar=>'baz'}, :datastreams=>["my_ds", "my_other_ds"])


938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
# File 'lib/active_fedora/base.rb', line 938

def update_attributes(params={}, opts={})
  result = {}
  if opts[:datastreams]
    ds_array = []
    opts[:datastreams].each do |dsname|
      ds_array << datastreams[dsname]
    end
  else
    ds_array = 
  end
  ds_array.each do |d|
    ds_result = d.update_attributes(params,opts)
    result[d.dsid] = ds_result
  end
  return result
end

#update_datastream_attributes(params = {}, opts = {}) ⇒ Object



988
989
990
991
992
993
994
995
996
997
998
# File 'lib/active_fedora/base.rb', line 988

def update_datastream_attributes(params={}, opts={})
  result = params.dup
  params.each_pair do |dsid, ds_params| 
    if datastreams_in_memory.include?(dsid)
      result[dsid] = datastreams_in_memory[dsid].update_attributes(ds_params)
    else
      result.delete(dsid)
    end
  end
  return result
end

#update_indexObject

Updates Solr index with self.



912
913
914
915
916
917
918
919
920
921
# File 'lib/active_fedora/base.rb', line 912

def update_index
  if defined?( Solrizer::Fedora::Solrizer ) 
    #logger.info("Trying to solrize pid: #{pid}")
    solrizer = Solrizer::Fedora::Solrizer.new
    solrizer.solrize( self )
  else
    #logger.info("Trying to update solr for pid: #{pid}")
    SolrService.instance.conn.update(self.to_solr)
  end
end

#update_indexed_attributes(params = {}, opts = {}) ⇒ Object

A convenience method for updating indexed attributes. The passed in hash must look like this :

{{:name=>{"0"=>"a","1"=>"b"}}

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

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

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

If you want to specify which datastream(s) to update, use the :datastreams argument like so:

m.update_attributes({"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}, :datastreams=>"my_ds")

or

m.update_attributes({"fubar"=>{"-1"=>"mork", "0"=>"york", "1"=>"mangle"}}, :datastreams=>["my_ds", "my_other_ds"])


972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
# File 'lib/active_fedora/base.rb', line 972

def update_indexed_attributes(params={}, opts={})
  if opts[:datastreams]
    ds_array = []
    opts[:datastreams].each do |dsname|
      ds_array << datastreams[dsname]
    end
  else
    ds_array = 
  end
  result = {}
  ds_array.each do |d|
    result[d.dsid] = d.update_indexed_attributes(params,opts)
  end
  return result
end

#update_named_datastream(name, opts = {}) ⇒ Object

** EXPERIMENTAL **

Update an existing named datastream. It has same parameters as add_named_datastream except the :dsid key is now required.

TODO

Currently requires you to update file if a managed datastream but could change to allow metadata only updates as well



428
429
430
431
432
433
# File 'lib/active_fedora/base.rb', line 428

def update_named_datastream(name, opts={})
  #check that dsid provided matches existing datastream with that name
  raise "You must define parameter dsid for datastream to update for #{pid}" unless opts.include?(:dsid)
  raise "Datastream with name #{name} and dsid #{opts[:dsid]} does not exist for #{pid}" unless self.send("#{name}_ids").include?(opts[:dsid])
  add_named_datastream(name,opts)
end