lib/active_fedora/base.rb in active-fedora-6.8.0 vs lib/active_fedora/base.rb in active-fedora-7.0.0.pre1

- old
+ new

@@ -10,11 +10,11 @@ # 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 - # has_metadata :name => "properties", :type => ActiveFedora::SimpleDatastream do |m| + # has_metadata "properties", type: ActiveFedora::SimpleDatastream do |m| # m.field "narrator", :string # m.field "narrator", :text # end # end # @@ -22,346 +22,29 @@ # narrator and bio field. # # Datastreams defined with +has_metadata+ are accessed via the +datastreams+ member hash. # class Base + extend ActiveModel::Naming + extend ActiveSupport::DescendantsTracker include SemanticNode - - class_attribute :fedora_connection, :profile_solr_name - self.fedora_connection = {} - self.profile_solr_name = ActiveFedora::SolrService.solr_name("object_profile", :displayable) - - delegate :state=,:label=, to: :inner_object - - 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 - - def new? - new_object? - end - - # Has this object been saved? - def new_object? - inner_object.new? - end - - ## Required by associations - def new_record? - self.new_object? - end - - def persisted? - !new_object? - end - - def mark_for_destruction - @marked_for_destruction = true - end - - def marked_for_destruction? - @marked_for_destruction - end - - # 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. - 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 - - # Reloads the object from Fedora. - def reload - raise ActiveFedora::ObjectNotFoundError, "Can't reload an object that hasn't been saved" unless persisted? - clear_association_cache - init_with_object(self.class.find(self.pid, cast: false).inner_object) - end - - # Initialize an empty model object and set the +inner_obj+ - # example: - # - # class Post < ActiveFedora::Base - # has_metadata :name => "properties", :type => ActiveFedora::SimpleDatastream - # end - # - # post = Post.allocate - # post.init_with_object(DigitalObject.find(pid)) - # post.properties.title # => 'hello world' - def init_with_object(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 - - # Uses {shard_index} to find or create the rubydora connection for this pid - # @param [String] pid the identifier of the object to get the connection for - # @return [Rubydora::Repository] The repository that the identifier exists in. - 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 - - # 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. - #@return [Integer] the index of the shard this object is stored in - def self.shard_index(pid) - if ActiveFedora.config.sharded? - Digest::MD5.hexdigest(pid).hex % ActiveFedora.config.credentials.length - else - 0 - end - end - - - def self.datastream_class_for_name(dsid) - ds_specs[dsid] ? ds_specs[dsid].fetch(:type, ActiveFedora::Datastream) : ActiveFedora::Datastream - end - - def clone - new_object = self.class.create - clone_into(new_object) - end - - # Clone the datastreams from this object into the provided object, while preserving the pid of the provided object - # @param [Base] new_object clone into this object - 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 - - ### if you are doing sharding, override this method to do something other than use a sequence - # @return [String] the unique pid for a new object - 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 - - def inner_object # :nodoc - @inner_object - end - - #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 - def pid - @inner_object.pid - end - - - def id ### Needed for the nested form helper - self.pid - end - - def to_param - persisted? ? to_key.join('-') : nil - end - - def to_key - persisted? ? [pid] : nil - end - - #return the internal fedora URI - def internal_uri - "info:fedora/#{pid}" - end - - #return the owner id - def owner_id - Array(@inner_object.ownerId).first - end - - def owner_id=(owner_id) - @inner_object.ownerId=(owner_id) - end - - def label - Array(@inner_object.label).first - end - - def state - Array(@inner_object.state).first - end - - #return the create_date of the inner object (unless it's a new object) - 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 - - #return the modification date of the inner object (unless it's a new object) - def modified_date - @inner_object.new? ? Time.now : Array(@inner_object.lastModifiedDate).first - end - - def ==(comparison_object) - comparison_object.equal?(self) || - (comparison_object.instance_of?(self.class) && - comparison_object.pid == pid && - !comparison_object.new_record?) - end - - - def pretty_pid - if self.pid == UnsavedDigitalObject::PLACEHOLDER - nil - else - self.pid - end - end - - # This method adapts the inner_object to a new ActiveFedora::Base implementation - # This is intended to minimize redundant interactions with Fedora - 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_object(inner_object) - end - - # 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. - 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 - - # ** EXPERIMENTAL ** - # This method returns a new object of the same class, with the internal SolrDigitalObject - # replaced with an actual DigitalObject. - 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 - - # ** EXPERIMENTAL ** - # This method reinitializes a lightweight, loaded-from-solr object with an actual - # DigitalObject inside. - def reify! - if self.inner_object.is_a? DigitalObject - raise "#{self.inspect} is already a full digital object" - end - self.init_with_object DigitalObject.find(self.class,self.pid) - end - - 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 - end - - Base.class_eval do + include Sharding include ActiveFedora::Persistence - extend ActiveSupport::DescendantsTracker - extend Model + include Scoping include Loggable include Indexing include ActiveModel::Conversion include Validations include Callbacks - include Attributes include Datastreams - extend ActiveModel::Naming - include Delegating extend Querying include Associations + include AutosaveAssociation include NestedAttributes include Reflection - include ActiveModel::Dirty + include Attributes + include Serialization + include Core + include FedoraAttributes end end