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::MetadataDatastream 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(args = {}) ⇒ Object
- .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
If you want to use sharding override this method with your strategy.
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.
- #attributes=(properties) ⇒ Object
-
#create_date ⇒ Object
return the create_date of the inner object (unless it’s a new object).
-
#fields ⇒ Object
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.
- #get_values_from_datastream(dsid, field_key, default = []) ⇒ 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_object=(bool) ⇒ Object
-
#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.
-
#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_solr(solr_doc = Hash.new, opts = {}) ⇒ Object
Return a Hash representation of this object where keys in the hash are appropriate Solr field names.
-
#to_xml(xml = Nokogiri::XML::Document.parse("<xml><fields/><content/></xml>")) ⇒ Object
Returns the xml version of this object as a string.
-
#update_datastream_attributes(params = {}, opts = {}) ⇒ Object
Updates the attributes for each datastream named in the params Hash.
-
#update_indexed_attributes(params = {}, opts = {}) ⇒ Object
A convenience method for updating indexed attributes.
Methods included from SemanticNode
#add_relationship, #assert_kind_of, #build_statement, #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.
79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/active_fedora/base.rb', line 79 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
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/active_fedora/base.rb', line 31 def method_missing(name, *args) # if [:collection_members, :part_of, :parts, :part_of_append, :file_objects].include? name # ActiveSupport::Deprecation.warn("Deprecation: FileManagement will not be included by default in the next version. To use #{name} add 'include ActiveFedora::FileManagement' to your model") # self.class.send :include, FileManagement # send name, *args # else dsid = corresponding_datastream_name(name) if dsid ### Create and invoke a proxy method self.class.send :define_method, name do datastreams[dsid] end self.send(name) else super end end |
Class Method Details
.assign_pid(obj) ⇒ String
if you are doing sharding, override this method to do something other than use a sequence
155 156 157 158 159 160 161 |
# File 'lib/active_fedora/base.rb', line 155 def self.assign_pid(obj) args = {} args[:namespace] = obj.namespace if obj.namespace raise RuntimeError, "When using shards, you must override #{self}.assign_pid()" if ActiveFedora.config.sharded? d = REXML::Document.new(connection_for_pid('0').next_pid(args)) d.elements['//pid'].text end |
.connection_for_pid(pid) ⇒ Rubydora::Repository
Uses shard_index to find or create the rubydora connection for this pid
123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/active_fedora/base.rb', line 123 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(args = {}) ⇒ Object
146 147 148 149 150 |
# File 'lib/active_fedora/base.rb', line 146 def self.create(args = {}) obj = self.new(args) obj.save obj end |
.datastream_class_for_name(dsid) ⇒ Object
142 143 144 |
# File 'lib/active_fedora/base.rb', line 142 def self.datastream_class_for_name(dsid) ds_specs[dsid] ? ds_specs[dsid][:type] : 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.
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 |
# File 'lib/active_fedora/base.rb', line 364 def self.load_instance_from_solr(pid,solr_doc=nil) if solr_doc.nil? result = find_by_solr(pid) raise ActiveFedora::ObjectNotFoundError, "Object #{pid} not found in solr" if result.nil? solr_doc = result.first #double check pid and id in record match raise ActiveFedora::ObjectNotFoundError, "Object #{pid} not found in Solr" unless !result.nil? && !solr_doc.nil? && pid == solr_doc[SOLR_DOCUMENT_ID] else raise "Solr document record id and pid do not match" unless pid == solr_doc[SOLR_DOCUMENT_ID] end klass = if class_str = solr_doc['active_fedora_model_s'] class_str.first.constantize else ActiveFedora::Base end profile_json = Array(solr_doc[ActiveFedora::Base.profile_solr_name]).first unless profile_json.present? raise ActiveFedora::ObjectNotFoundError, "Object #{pid} does not contain a solrized profile" end profile_hash = ActiveSupport::JSON.decode(profile_json) obj = klass.allocate.init_with(SolrDigitalObject.new(solr_doc, profile_hash, klass)) #set by default to load any dependent relationship objects from solr as well #need to call rels_ext once so it exists when iterating over datastreams obj.rels_ext obj.datastreams.each_value do |ds| if ds.respond_to?(:profile_from_hash) and (ds_prof = profile_hash['datastreams'][ds.dsid]) ds.profile_from_hash(ds_prof) end if ds.respond_to?(:from_solr) ds.from_solr(solr_doc) if ds.kind_of?(ActiveFedora::MetadataDatastream) || ds.kind_of?(ActiveFedora::NokogiriDatastream) || ( ds.kind_of?(ActiveFedora::RelsExtDatastream)) end end obj.inner_object.freeze obj end |
.pids_from_uris(uris) ⇒ Object
465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/active_fedora/base.rb', line 465 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
If you want to use sharding override this method with your strategy
137 138 139 |
# File 'lib/active_fedora/base.rb', line 137 def self.shard_index(pid) 0 end |
Instance Method Details
#==(comparison_object) ⇒ Object
257 258 259 260 261 262 |
# File 'lib/active_fedora/base.rb', line 257 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
320 321 322 323 324 325 |
# File 'lib/active_fedora/base.rb', line 320 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
328 329 330 331 |
# File 'lib/active_fedora/base.rb', line 328 def adapt_to_cmodel the_model = ActiveFedora::ContentModel.known_models_for( self ).first self.class != the_model ? self.adapt_to(the_model) : self end |
#attributes=(properties) ⇒ Object
68 69 70 71 72 |
# File 'lib/active_fedora/base.rb', line 68 def attributes=(properties) properties.each do |k, v| respond_to?(:"#{k}=") ? send(:"#{k}=", v) : raise(UnknownAttributeError, "unknown attribute: #{k}") end end |
#create_date ⇒ Object
return the create_date of the inner object (unless it’s a new object)
203 204 205 |
# File 'lib/active_fedora/base.rb', line 203 def create_date @inner_object.new? ? Time.now : @inner_object.profile["objCreateDate"] end |
#fields ⇒ Object
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.
225 226 227 228 229 230 231 232 |
# File 'lib/active_fedora/base.rb', line 225 def fields # TODO this can likely be removed once find_by_fields_by_solr is removed fields = {:id => {:values => [pid]}, :system_create_date => {:values => [self.create_date], :type=>:date}, :system_modified_date => {:values => [self.modified_date], :type=>:date}, :active_fedora_model => {:values => [self.class.inspect], :type=>:symbol}} datastreams.values.each do |ds| fields.merge!(ds.fields) if [ActiveFedora::MetadataDatastream, ActiveFedora::QualifiedDublinCoreDatastream].include?(ds.class) end return fields end |
#get_values_from_datastream(dsid, field_key, default = []) ⇒ Object
457 458 459 460 461 462 463 |
# File 'lib/active_fedora/base.rb', line 457 def get_values_from_datastream(dsid,field_key,default=[]) if datastreams.include?(dsid) return datastreams[dsid].get_values(field_key,default) else return nil end end |
#id ⇒ Object
Needed for the nested form helper
175 176 177 |
# File 'lib/active_fedora/base.rb', line 175 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::MetadataDatastream
end
post = Post.allocate
post.init_with(DigitalObject.find(pid))
post.properties.title # => 'hello world'
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/active_fedora/base.rb', line 102 def init_with(inner_obj) @inner_object = inner_obj unless @inner_object.is_a? SolrDigitalObject @inner_object.original_class = self.class ## Replace existing unchanged datastreams with the definitions found in this class if they have a different type. ## Any datastream that is deleted here will cause a reload from fedora, so avoid it whenever possible ds_specs.keys.each do |key| if !@inner_object.datastreams[key].changed.include?(:content) && @inner_object.datastreams[key].class != self.class.ds_specs[key][:type] @inner_object.datastreams.delete(key) end end end load_datastreams run_callbacks :find run_callbacks :initialize self end |
#inner_object ⇒ Object
:nodoc
163 164 165 |
# File 'lib/active_fedora/base.rb', line 163 def inner_object # :nodoc @inner_object end |
#inspect ⇒ Object
264 265 266 |
# File 'lib/active_fedora/base.rb', line 264 def inspect "#<#{self.class}:#{self.hash} @pid=\"#{pid}\" >" end |
#internal_uri ⇒ Object
return the internal fedora URI
184 185 186 |
# File 'lib/active_fedora/base.rb', line 184 def internal_uri "info:fedora/#{pid}" end |
#label ⇒ Object
return the label of the inner object (unless it’s a new object)
213 214 215 |
# File 'lib/active_fedora/base.rb', line 213 def label @inner_object.label end |
#label=(new_label) ⇒ Object
217 218 219 |
# File 'lib/active_fedora/base.rb', line 217 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)
208 209 210 |
# File 'lib/active_fedora/base.rb', line 208 def modified_date @inner_object.new? ? Time.now : @inner_object.profile["objLastModDate"] end |
#new_object=(bool) ⇒ Object
55 56 57 |
# File 'lib/active_fedora/base.rb', line 55 def new_object=(bool) ActiveSupport::Deprecation.warn("ActiveFedora::Base.new_object= has been deprecated and nolonger has any effect") end |
#new_object? ⇒ Boolean
Has this object been saved?
51 52 53 |
# File 'lib/active_fedora/base.rb', line 51 def new_object? inner_object.new? end |
#new_record? ⇒ Boolean
Required by associations
60 61 62 |
# File 'lib/active_fedora/base.rb', line 60 def new_record? self.new_object? end |
#owner_id ⇒ Object
return the owner id
194 195 196 |
# File 'lib/active_fedora/base.rb', line 194 def owner_id @inner_object.ownerId end |
#owner_id=(owner_id) ⇒ Object
198 199 200 |
# File 'lib/active_fedora/base.rb', line 198 def owner_id=(owner_id) @inner_object.ownerId=(owner_id) end |
#persisted? ⇒ Boolean
64 65 66 |
# File 'lib/active_fedora/base.rb', line 64 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
170 171 172 |
# File 'lib/active_fedora/base.rb', line 170 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.
336 337 338 339 340 341 |
# File 'lib/active_fedora/base.rb', line 336 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.
346 347 348 349 350 351 |
# File 'lib/active_fedora/base.rb', line 346 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 |
#solrize_profile(solr_doc = Hash.new) ⇒ Object
:nodoc:
290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/active_fedora/base.rb', line 290 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
307 308 309 310 311 312 313 314 315 |
# File 'lib/active_fedora/base.rb', line 307 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
189 190 191 |
# File 'lib/active_fedora/base.rb', line 189 def state @inner_object.state end |
#to_key ⇒ Object
179 180 181 |
# File 'lib/active_fedora/base.rb', line 179 def to_key persisted? ? [pid] : 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.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/active_fedora/base.rb', line 272 def to_solr(solr_doc = Hash.new, opts={}) unless opts[:model_only] c_time = create_date c_time = Time.parse(c_time) unless c_time.is_a?(Time) m_time = modified_date m_time = Time.parse(m_time) unless m_time.is_a?(Time) solr_doc.merge!(SOLR_DOCUMENT_ID.to_sym => pid, ActiveFedora::SolrService.solr_name(:system_create, :date) => c_time.utc.xmlschema, ActiveFedora::SolrService.solr_name(:system_modified, :date) => m_time.utc.xmlschema, ActiveFedora::SolrService.solr_name(:active_fedora_model, :symbol) => self.class.inspect) solrize_profile(solr_doc) end datastreams.each_value do |ds| ds.ensure_xml_loaded if ds.respond_to? :ensure_xml_loaded ### Can't put this in the model because it's often implemented in Solrizer::XML::TerminologyBasedSolrizer #puts "\n\nQDC #{ds.to_solr(solr_doc).inspect}" if ds.kind_of?(ActiveFedora::QualifiedDublinCoreDatastream) solr_doc = ds.to_solr(solr_doc) if ds.kind_of?(ActiveFedora::RDFDatastream) || ds.kind_of?(ActiveFedora::NokogiriDatastream) || ds.kind_of?(ActiveFedora::MetadataDatastream) end solr_doc = solrize_relationships(solr_doc) unless opts[:model_only] solr_doc end |
#to_xml(xml = Nokogiri::XML::Document.parse("<xml><fields/><content/></xml>")) ⇒ Object
Returns the xml version of this object as a string.
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/active_fedora/base.rb', line 235 def to_xml(xml=Nokogiri::XML::Document.parse("<xml><fields/><content/></xml>")) fields_xml = xml.xpath('//fields').first builder = Nokogiri::XML::Builder.with(fields_xml) do |fields_xml| fields_xml.id_ pid fields_xml.system_create_date self.create_date fields_xml.system_modified_date self.modified_date fields_xml.active_fedora_model self.class.inspect end # {:id => pid, :system_create_date => self.create_date, :system_modified_date => self.modified_date, :active_fedora_model => self.class.inspect}.each_pair do |attribute_name, value| # el = REXML::Element.new(attribute_name.to_s) # el.text = value # fields_xml << el # end datastreams.each_value do |ds| ds.to_xml(fields_xml) if ds.class.included_modules.include?(ActiveFedora::MetadataDatastreamHelper) ds.to_rels_ext if ds.kind_of?(ActiveFedora::RelsExtDatastream) end return xml.to_s end |
#update_datastream_attributes(params = {}, opts = {}) ⇒ Object
Updates the attributes for each datastream named in the params Hash
445 446 447 448 449 450 451 452 453 454 455 |
# File 'lib/active_fedora/base.rb', line 445 def update_datastream_attributes(params={}, opts={}) result = params.dup params.each_pair do |dsid, ds_params| if datastreams.include?(dsid) result[dsid] = datastreams[dsid].update_indexed_attributes(ds_params) else result.delete(dsid) end end return result end |
#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"])
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/active_fedora/base.rb', line 418 def update_indexed_attributes(params={}, opts={}) if ds = opts[:datastreams] ds_array = [] ds = [ds] unless ds.respond_to? :each ds.each do |dsname| ds_array << datastreams[dsname] end else ds_array = end result = {} ds_array.each do |d| result[d.dsid] = d.update_indexed_attributes(params,opts) end return result end |