Class: ActiveFedora::Base
- Inherits:
-
Object
- Object
- ActiveFedora::Base
- 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
Instance Attribute Summary
Attributes included from SemanticNode
#load_from_solr, #relationships_loaded, #subject
Class Method Summary collapse
-
.assign_pid(obj) ⇒ String
if you are doing sharding, override this method to do something other than use a sequence.
-
.connection_for_pid(pid) ⇒ Rubydora::Repository
Uses Base.shard_index to find or create the rubydora connection for this pid.
-
.create(attributes = nil, &block) ⇒ Object
Creates an object (or multiple objects) and saves it to the repository, if validations pass.
- .datastream_class_for_name(dsid) ⇒ Object
-
.load_instance_from_solr(pid, solr_doc = nil) ⇒ Object
This method can be used instead of ActiveFedora::Model::ClassMethods.find.
- .pids_from_uris(uris) ⇒ Object
-
.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.
Instance Method Summary collapse
- #==(comparison_object) ⇒ Object
-
#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.
-
#adapt_to_cmodel ⇒ Object
Examine the :has_model assertions in the RELS-EXT.
- #clone ⇒ Object
-
#clone_into(new_object) ⇒ Object
Clone the datastreams from this object into the provided object, while preserving the pid of the provided object.
-
#create_date ⇒ Object
return the create_date of the inner object (unless it’s a new object).
-
#id ⇒ Object
Needed for the nested form helper.
-
#init_with(inner_obj) ⇒ Object
Initialize an empty model object and set the
inner_obj
example:. -
#initialize(attrs = nil) ⇒ Base
constructor
Constructor.
-
#inner_object ⇒ Object
:nodoc.
- #inspect ⇒ Object
-
#internal_uri ⇒ Object
return the internal fedora URI.
-
#label ⇒ Object
return the label of the inner object (unless it’s a new object).
- #label=(new_label) ⇒ Object
- #method_missing(name, *args) ⇒ Object
-
#modified_date ⇒ Object
return the modification date of the inner object (unless it’s a new object).
- #new? ⇒ Boolean
-
#new_object? ⇒ Boolean
Has this object been saved?.
-
#new_record? ⇒ Boolean
Required by associations.
-
#owner_id ⇒ Object
return the owner id.
- #owner_id=(owner_id) ⇒ Object
- #persisted? ⇒ Boolean
-
#pid ⇒ Object
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.
-
#reify ⇒ Object
** EXPERIMENTAL ** This method returns a new object of the same class, with the internal SolrDigitalObject replaced with an actual DigitalObject.
-
#reify! ⇒ Object
** EXPERIMENTAL ** This method reinitializes a lightweight, loaded-from-solr object with an actual DigitalObject inside.
-
#reload ⇒ Object
Reloads the object from Fedora.
-
#solrize_profile(solr_doc = Hash.new) ⇒ Object
:nodoc:.
-
#solrize_relationships(solr_doc = Hash.new) ⇒ Object
Serialize the datastream’s RDF relationships to solr.
-
#state ⇒ Object
return the state of the inner object.
- #to_key ⇒ Object
- #to_param ⇒ Object
-
#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.
Methods included from SemanticNode
#add_relationship, #assert_kind_of, #build_statement, #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.
68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/active_fedora/base.rb', line 68 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 |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/active_fedora/base.rb', line 32 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
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/active_fedora/base.rb', line 202 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) d = REXML::Document.new( fedora_connection[0].connection.next_pid(args)) pid =d.elements['//pid'].text pid end |
.connection_for_pid(pid) ⇒ Rubydora::Repository
Uses shard_index to find or create the rubydora connection for this pid
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/active_fedora/base.rb', line 116 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 |
.create(attributes = nil, &block) ⇒ Object
Creates an object (or multiple objects) and saves it to the repository, if validations pass. The resulting object is returned whether the object was saved successfully to the repository or not.
The attributes
parameter can be either be a Hash or an Array of Hashes. These Hashes describe the attributes on the objects that are to be created.
Examples
# Create a single new object
User.create(:first_name => 'Jamie')
# Create an Array of new objects
User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
# Create a single object and pass it into a block to set other attributes.
User.create(:first_name => 'Jamie') do |u|
u.is_admin = false
end
# Creating an Array of new objects using a block, where the block is executed for each object:
User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
u.is_admin = false
end
170 171 172 173 174 175 176 177 178 179 |
# File 'lib/active_fedora/base.rb', line 170 def self.create(attributes = nil, &block) if attributes.is_a?(Array) attributes.collect { |attr| create(attr, &block) } else object = new(attributes) yield(object) if block_given? object.save object end end |
.datastream_class_for_name(dsid) ⇒ Object
144 145 146 |
# File 'lib/active_fedora/base.rb', line 144 def self.datastream_class_for_name(dsid) ds_specs[dsid] ? ds_specs[dsid].fetch(:type, ActiveFedora::Datastream) : ActiveFedora::Datastream end |
.load_instance_from_solr(pid, solr_doc = nil) ⇒ Object
This method can be used instead of ActiveFedora::Model::ClassMethods.find.
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 find instead.
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 419 420 |
# File 'lib/active_fedora/base.rb', line 387 def self.load_instance_from_solr(pid,solr_doc=nil) if solr_doc.nil? result = find_with_conditions(:id=>pid) raise ActiveFedora::ObjectNotFoundError, "Object #{pid} not found in solr" if result.empty? 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['has_model_s'] ActiveFedora::SolrService.class_from_solr_document(solr_doc) 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 ds.from_solr(solr_doc) if ds.respond_to?(:from_solr) end obj.inner_object.freeze obj end |
.pids_from_uris(uris) ⇒ Object
422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/active_fedora/base.rb', line 422 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.
135 136 137 138 139 140 141 |
# File 'lib/active_fedora/base.rb', line 135 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
280 281 282 283 284 285 |
# File 'lib/active_fedora/base.rb', line 280 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
343 344 345 346 347 348 |
# File 'lib/active_fedora/base.rb', line 343 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_cmodel ⇒ Object
Examine the :has_model assertions in the RELS-EXT. Adapt this class to the first first known model
351 352 353 354 |
# File 'lib/active_fedora/base.rb', line 351 def adapt_to_cmodel the_model = ActiveFedora::ContentModel.known_models_for( self ).first self.class != the_model ? self.adapt_to(the_model) : self end |
#clone ⇒ Object
181 182 183 184 |
# File 'lib/active_fedora/base.rb', line 181 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
188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/active_fedora/base.rb', line 188 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_date ⇒ Object
return the create_date of the inner object (unless it’s a new object)
262 263 264 |
# File 'lib/active_fedora/base.rb', line 262 def create_date @inner_object.new? ? Time.now : @inner_object.profile["objCreateDate"] end |
#id ⇒ Object
Needed for the nested form helper
230 231 232 |
# File 'lib/active_fedora/base.rb', line 230 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'
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/active_fedora/base.rb', line 95 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].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_object ⇒ Object
:nodoc
218 219 220 |
# File 'lib/active_fedora/base.rb', line 218 def inner_object # :nodoc @inner_object end |
#inspect ⇒ Object
289 290 291 |
# File 'lib/active_fedora/base.rb', line 289 def inspect "#<#{self.class}:#{self.hash} @pid=\"#{pid}\" >" end |
#internal_uri ⇒ Object
return the internal fedora URI
243 244 245 |
# File 'lib/active_fedora/base.rb', line 243 def internal_uri "info:fedora/#{pid}" end |
#label ⇒ Object
return the label of the inner object (unless it’s a new object)
272 273 274 |
# File 'lib/active_fedora/base.rb', line 272 def label @inner_object.label end |
#label=(new_label) ⇒ Object
276 277 278 |
# File 'lib/active_fedora/base.rb', line 276 def label=(new_label) @inner_object.label = new_label end |
#modified_date ⇒ Object
return the modification date of the inner object (unless it’s a new object)
267 268 269 |
# File 'lib/active_fedora/base.rb', line 267 def modified_date @inner_object.new? ? Time.now : @inner_object.profile["objLastModDate"] end |
#new? ⇒ Boolean
45 46 47 |
# File 'lib/active_fedora/base.rb', line 45 def new? new_object? end |
#new_object? ⇒ Boolean
Has this object been saved?
50 51 52 |
# File 'lib/active_fedora/base.rb', line 50 def new_object? inner_object.new? end |
#new_record? ⇒ Boolean
Required by associations
55 56 57 |
# File 'lib/active_fedora/base.rb', line 55 def new_record? self.new_object? end |
#owner_id ⇒ Object
return the owner id
253 254 255 |
# File 'lib/active_fedora/base.rb', line 253 def owner_id @inner_object.ownerId end |
#owner_id=(owner_id) ⇒ Object
257 258 259 |
# File 'lib/active_fedora/base.rb', line 257 def owner_id=(owner_id) @inner_object.ownerId=(owner_id) end |
#persisted? ⇒ Boolean
59 60 61 |
# File 'lib/active_fedora/base.rb', line 59 def persisted? !new_object? end |
#pid ⇒ Object
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
225 226 227 |
# File 'lib/active_fedora/base.rb', line 225 def pid @inner_object.pid end |
#reify ⇒ Object
** EXPERIMENTAL ** This method returns a new object of the same class, with the internal SolrDigitalObject replaced with an actual DigitalObject.
359 360 361 362 363 364 |
# File 'lib/active_fedora/base.rb', line 359 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.
369 370 371 372 373 374 |
# File 'lib/active_fedora/base.rb', line 369 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 |
#reload ⇒ Object
Reloads the object from Fedora.
81 82 83 |
# File 'lib/active_fedora/base.rb', line 81 def reload init_with(self.class.find(self.pid).inner_object) end |
#solrize_profile(solr_doc = Hash.new) ⇒ Object
:nodoc:
313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/active_fedora/base.rb', line 313 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 |
#solrize_relationships(solr_doc = Hash.new) ⇒ Object
Serialize the datastream’s RDF relationships to solr
330 331 332 333 334 335 336 337 338 |
# File 'lib/active_fedora/base.rb', line 330 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 |
#state ⇒ Object
return the state of the inner object
248 249 250 |
# File 'lib/active_fedora/base.rb', line 248 def state @inner_object.state end |
#to_key ⇒ Object
238 239 240 |
# File 'lib/active_fedora/base.rb', line 238 def to_key persisted? ? [pid] : nil end |
#to_param ⇒ Object
234 235 236 |
# File 'lib/active_fedora/base.rb', line 234 def to_param persisted? ? to_key.join('-') : nil 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.
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/active_fedora/base.rb', line 297 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| solr_doc = ds.to_solr(solr_doc) end solr_doc = solrize_relationships(solr_doc) unless opts[:model_only] solr_doc end |