module Spira::Persistence
Public Instance Methods
The number of RDF::Statements this projection has.
@see rdf.rubyforge.org/RDF/Enumerable.html#count
# File lib/spira/persistence.rb, line 337 def count each.count end
# File lib/spira/persistence.rb, line 293 def destroy(*args) run_callbacks :destroy do destroy_model_data(*args) end end
# File lib/spira/persistence.rb, line 299 def destroy!(*args) destroy(*args) || raise(RecordNotSaved) end
# File lib/spira/persistence.rb, line 274 def destroyed? @destroyed end
Enumerate each RDF
statement that makes up this projection. This makes each instance an `RDF::Enumerable`, with all of the nifty benefits thereof. See <rdf.rubyforge.org/RDF/Enumerable.html> for information on arguments.
@see rdf.rubyforge.org/RDF/Enumerable.html
# File lib/spira/persistence.rb, line 310 def each if block_given? self.class.properties.each do |name, property| if value = read_attribute(name) if self.class.reflect_on_association(name) value.each do |val| node = build_rdf_value(val, property[:type]) yield RDF::Statement.new(subject, property[:predicate], node) if valid_object?(node) end else node = build_rdf_value(value, property[:type]) yield RDF::Statement.new(subject, property[:predicate], node) if valid_object?(node) end end end self.class.types.each do |t| yield RDF::Statement.new(subject, RDF.type, t) end else enum_for(:each) end end
A resource is considered to be new if the repository does not have statements where subject == resource type
# File lib/spira/persistence.rb, line 270 def new_record? !self.class.repository.has_subject?(subject) end
# File lib/spira/persistence.rb, line 278 def persisted? # FIXME: an object should be considered persisted # when its attributes (and their exact values) are all available in the storage. # This should check for !(changed? || new_record? || destroyed?) actually. !(new_record? || destroyed?) end
Reload all attributes for this instance. This resource will block if the underlying repository blocks the next time it accesses attributes.
NB: “props” argument is ignored, it is handled in Base
# File lib/spira/persistence.rb, line 368 def reload(props = {}) sts = self.class.repository.query({subject: subject}) self.class.properties.each do |name, options| name = name.to_s if sts objects = sts.select { |s| s.predicate == options[:predicate] } attributes[name] = retrieve_attribute(name, options, objects) end end end
# File lib/spira/persistence.rb, line 285 def save(*) create_or_update end
# File lib/spira/persistence.rb, line 289 def save!(*) create_or_update || raise(RecordNotSaved) end
Update multiple attributes of this repository.
@example Update multiple attributes
person.update_attributes(name: 'test', age: 10) #=> person person.name #=> 'test' person.age #=> 10 person.dirty? #=> true
@param [Hash{Symbol => Any}] properties @param [Hash{Symbol => Any}] options @return [self]
# File lib/spira/persistence.rb, line 356 def update_attributes(properties, options = {}) assign_attributes properties save options end
Private Instance Methods
Return the appropriate class object for a string or symbol representation. Throws errors correctly if the given class cannot be located, or if it is not a Spira::Base
# File lib/spira/persistence.rb, line 473 def classize_resource(type) return type unless type.is_a?(Symbol) || type.is_a?(String) klass = nil begin klass = qualified_const_get(type.to_s) rescue NameError raise NameError, "Could not find relation class #{type} (referenced as #{type} by #{self})" end klass end
# File lib/spira/persistence.rb, line 382 def create_or_update run_callbacks :save do # "create" callback is triggered only when persisting a resource definition persistance_callback = new_record? && type ? :create : :update run_callbacks persistance_callback do materizalize persist! reset_changes end end self end
Destroy all model data AND non-model data, where this resource is referred to as object.
# File lib/spira/persistence.rb, line 462 def destroy_model_data(*args) if self.class.repository.delete(statements) && self.class.repository.delete([nil, nil, subject]) @destroyed = true freeze end end
“Materialize” the resource: assign a persistable subject to a non-persisted resource, so that it can be properly stored.
# File lib/spira/persistence.rb, line 431 def materizalize if new_record? && subject.anonymous? && type # TODO: doesn't subject.anonymous? imply subject.id == nil ??? @subject = self.class.id_for(subject.id) end end
Save changes to the repository
# File lib/spira/persistence.rb, line 398 def persist! repo = self.class.repository self.class.properties.each do |name, property| value = read_attribute name if self.class.reflect_on_association(name) # TODO: for now, always persist associations, # as it's impossible to reliably determine # whether the "association property" was changed # (e.g. for "in-place" changes like "association << 1") # This should be solved by splitting properties # into "true attributes" and associations # and not mixing the both in @properties. repo.delete [subject, property[:predicate], nil] value.each do |val| store_attribute(name, val, property[:predicate], repo) end else if attribute_changed?(name.to_s) repo.delete [subject, property[:predicate], nil] store_attribute(name, value, property[:predicate], repo) end end end types.each do |type| # NB: repository won't accept duplicates, # but this should be avoided anyway, for performance repo.insert RDF::Statement.new(subject, RDF.type, type) end end
Resolve a constant from a string, relative to this class' namespace, if available, and from root, otherwise.
FIXME: this is not really 'qualified', but it's one of those impossible-to-name functions. Open to suggestions.
@author njh @private
# File lib/spira/persistence.rb, line 493 def qualified_const_get(str) path = str.to_s.split('::') from_root = path[0].empty? if from_root from_root = [] path = path[1..-1] else start_ns = ((Class === self)||(Module === self)) ? self : self.class from_root = start_ns.to_s.split('::') end until from_root.empty? begin return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) } rescue NameError from_root.delete_at(-1) end end path.inject(Object) { |ns,name| ns.const_get(name) } end
Directly retrieve an attribute value from the storage
# File lib/spira/persistence.rb, line 446 def retrieve_attribute(name, options, sts) if self.class.reflections[name] sts.inject([]) do |values, statement| if statement.predicate == options[:predicate] values << build_value(statement.object, options[:type]) else values end end else sts.first ? build_value(sts.first.object, options[:type]) : nil end end
# File lib/spira/persistence.rb, line 438 def store_attribute(property, value, predicate, repository) unless value.nil? val = build_rdf_value(value, self.class.properties[property][:type]) repository.insert RDF::Statement.new(subject, predicate, val) if valid_object?(val) end end