module Spira::Persistence

Public Instance Methods

count() click to toggle source

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
destroy(*args) click to toggle source
# File lib/spira/persistence.rb, line 293
def destroy(*args)
  run_callbacks :destroy do
    destroy_model_data(*args)
  end
end
destroy!(*args) click to toggle source
# File lib/spira/persistence.rb, line 299
def destroy!(*args)
  destroy(*args) || raise(RecordNotSaved)
end
destroyed?() click to toggle source
# File lib/spira/persistence.rb, line 274
def destroyed?
  @destroyed
end
each() { |statement| ... } click to toggle source

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
new_record?() click to toggle source

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
persisted?() click to toggle source
# 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(props = {}) click to toggle source

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
save(*) click to toggle source
# File lib/spira/persistence.rb, line 285
def save(*)
  create_or_update
end
save!(*) click to toggle source
# File lib/spira/persistence.rb, line 289
def save!(*)
  create_or_update || raise(RecordNotSaved)
end
update_attributes(properties, options = {}) click to toggle source

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

classize_resource(type) click to toggle source

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
create_or_update() click to toggle source
# 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_model_data(*args) click to toggle source

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
materizalize() click to toggle source

“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
persist!() click to toggle source

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
qualified_const_get(str) click to toggle source

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
retrieve_attribute(name, options, sts) click to toggle source

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
store_attribute(property, value, predicate, repository) click to toggle source
# 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