module ODBA::Persistable

Constants

Exact
Find
ODBA_EXCLUDE_VARS

Classes which include Persistable may override ODBA_EXCLUDE_VARS to prevent data from being stored in the database (e.g. passwords, file descriptors). Simply redefine: ODBA_EXCLUDE_VARS = ['@foo']

ODBA_PREFETCH
ODBA_SERIALIZABLE

If you want to prevent Persistables from being disconnected and stored separately (Array and Hash are Persistable by default), redefine: ODBA_SERIALIZABLE = ['@bar']

Attributes

odba_id[W]
odba_name[RW]
odba_persistent[RW]

Classes which include Persistable have a class-method 'odba_index'

odba_prefetch[RW]

Public Class Methods

append_features(mod) click to toggle source
Calls superclass method
# File lib/odba/persistable.rb, line 32
def Persistable.append_features(mod)
  super
  mod.module_eval {
    class << self
      def odba_index(*keys)
        require 'odba/index_definition'
        origin_klass = self
        resolve_origin = nil
        resolve_target = :none
        resolve = {}
        opts = {}
        if(keys.size > 1)
          if(keys.last.is_a?(Hash))
            opts = keys.pop
          end
          if(keys.last.is_a?(Class))
            origin_klass = keys.pop
            resolve = keys.pop
            resolve_origin = keys.pop
          elsif(keys.last.is_a?(Symbol))
            keys.each { |key|
              resolve.store(key, {'resolve' => key})
            }
          else
            resolve = keys.pop
          end
        else
          resolve = keys.first
        end
        keys.each { |key|
          if RUBY_VERSION >= '1.9'
            key = key.to_sym
          else
            key = key.to_s
          end
          unless(instance_methods.include?(key))
            attr_accessor key
          end
        }
        index_prefix = self.name.downcase.gsub(/::/, '_')
        index_suffix = Persistable.sanitize(keys.join('_and_'))
        index_name = sprintf("%s_%s", index_prefix, index_suffix)
        search_name = sprintf("search_by_%s", index_suffix)
        exact_name = sprintf("search_by_exact_%s", index_suffix)
        find_name = sprintf("find_by_%s", index_suffix)
        keys_name = sprintf("%s_keys", index_suffix)
        index_definition = IndexDefinition.new
        index_definition.index_name = index_name
        index_definition.origin_klass = origin_klass
        index_definition.target_klass = self
        index_definition.resolve_search_term = resolve
        index_definition.resolve_origin = resolve_origin.to_s
        index_definition.resolve_target = resolve_target
        opts.each { |key, val| index_definition.send "#{key}=", val }
        ODBA.cache.ensure_index_deferred(index_definition)
        meta_eval {
          define_method(search_name) { |*vals|
            if(vals.size > 1)
              args = {}
              vals.each_with_index { |val, idx|
                cond = case val
                       when Numeric, Date
                         '='
                       else
                         'like'
                       end
                args.store(keys.at(idx),
                           { 'value' => val, 'condition' => cond })
              }
              ODBA.cache.retrieve_from_index(index_name, args)
            else
              ODBA.cache.retrieve_from_index(index_name, vals.first)
            end
          }
          define_method(exact_name) {  |*vals|
            if(vals.size > 1)
              args = {}
              vals.each_with_index { |val, idx|
                args.store(keys.at(idx), val)
              }
              ODBA.cache.retrieve_from_index(index_name, args,
                                             ODBA::Persistable::Exact)
            else
              ODBA.cache.retrieve_from_index(index_name, vals.first,
                                             ODBA::Persistable::Exact)
            end
          }
          define_method(find_name) {  |*vals|
            if(vals.size > 1)
              args = {}
              vals.each_with_index { |val, idx|
                cond = case val
                       when Numeric, Date
                         '='
                       else
                         'like'
                       end
                args.store(keys.at(idx),
                           { 'value' => val, 'condition' => cond })
              }
              ODBA.cache.retrieve_from_index(index_name, args,
                                             ODBA::Persistable::Find)
            else
              ODBA.cache.retrieve_from_index(index_name, vals.first,
                                             ODBA::Persistable::Find)
            end.first
          }
          define_method(keys_name) { |*vals|
            # TODO fix this for fulltext and condition indices
            length, = vals
            ODBA.cache.index_keys(index_name, length)
          }
        }
        index_definition
      end
      def odba_extent
        all = ODBA.cache.extent(self)
        if(block_given?)
          all.each { |instance| yield instance }
          nil
        else
          all
        end
      end
      def odba_count
        ODBA.cache.count(self)
      end
    end
  }
end
odba_count() click to toggle source
# File lib/odba/persistable.rb, line 156
def odba_count
  ODBA.cache.count(self)
end
odba_extent() { |instance| ... } click to toggle source
# File lib/odba/persistable.rb, line 147
def odba_extent
  all = ODBA.cache.extent(self)
  if(block_given?)
    all.each { |instance| yield instance }
    nil
  else
    all
  end
end
odba_index(*keys) click to toggle source
# File lib/odba/persistable.rb, line 36
def odba_index(*keys)
  require 'odba/index_definition'
  origin_klass = self
  resolve_origin = nil
  resolve_target = :none
  resolve = {}
  opts = {}
  if(keys.size > 1)
    if(keys.last.is_a?(Hash))
      opts = keys.pop
    end
    if(keys.last.is_a?(Class))
      origin_klass = keys.pop
      resolve = keys.pop
      resolve_origin = keys.pop
    elsif(keys.last.is_a?(Symbol))
      keys.each { |key|
        resolve.store(key, {'resolve' => key})
      }
    else
      resolve = keys.pop
    end
  else
    resolve = keys.first
  end
  keys.each { |key|
    if RUBY_VERSION >= '1.9'
      key = key.to_sym
    else
      key = key.to_s
    end
    unless(instance_methods.include?(key))
      attr_accessor key
    end
  }
  index_prefix = self.name.downcase.gsub(/::/, '_')
  index_suffix = Persistable.sanitize(keys.join('_and_'))
  index_name = sprintf("%s_%s", index_prefix, index_suffix)
  search_name = sprintf("search_by_%s", index_suffix)
  exact_name = sprintf("search_by_exact_%s", index_suffix)
  find_name = sprintf("find_by_%s", index_suffix)
  keys_name = sprintf("%s_keys", index_suffix)
  index_definition = IndexDefinition.new
  index_definition.index_name = index_name
  index_definition.origin_klass = origin_klass
  index_definition.target_klass = self
  index_definition.resolve_search_term = resolve
  index_definition.resolve_origin = resolve_origin.to_s
  index_definition.resolve_target = resolve_target
  opts.each { |key, val| index_definition.send "#{key}=", val }
  ODBA.cache.ensure_index_deferred(index_definition)
  meta_eval {
    define_method(search_name) { |*vals|
      if(vals.size > 1)
        args = {}
        vals.each_with_index { |val, idx|
          cond = case val
                 when Numeric, Date
                   '='
                 else
                   'like'
                 end
          args.store(keys.at(idx),
                     { 'value' => val, 'condition' => cond })
        }
        ODBA.cache.retrieve_from_index(index_name, args)
      else
        ODBA.cache.retrieve_from_index(index_name, vals.first)
      end
    }
    define_method(exact_name) {  |*vals|
      if(vals.size > 1)
        args = {}
        vals.each_with_index { |val, idx|
          args.store(keys.at(idx), val)
        }
        ODBA.cache.retrieve_from_index(index_name, args,
                                       ODBA::Persistable::Exact)
      else
        ODBA.cache.retrieve_from_index(index_name, vals.first,
                                       ODBA::Persistable::Exact)
      end
    }
    define_method(find_name) {  |*vals|
      if(vals.size > 1)
        args = {}
        vals.each_with_index { |val, idx|
          cond = case val
                 when Numeric, Date
                   '='
                 else
                   'like'
                 end
          args.store(keys.at(idx),
                     { 'value' => val, 'condition' => cond })
        }
        ODBA.cache.retrieve_from_index(index_name, args,
                                       ODBA::Persistable::Find)
      else
        ODBA.cache.retrieve_from_index(index_name, vals.first,
                                       ODBA::Persistable::Find)
      end.first
    }
    define_method(keys_name) { |*vals|
      # TODO fix this for fulltext and condition indices
      length, = vals
      ODBA.cache.index_keys(index_name, length)
    }
  }
  index_definition
end
sanitize(name) click to toggle source
# File lib/odba/persistable.rb, line 163
def Persistable.sanitize(name)
  name.gsub(@@sanitize_ptrn, '_')
end

Public Instance Methods

odba_add_observer(obj) click to toggle source

Add an observer for Cache#store(self), Cache#delete(self) and Cache#clean removing the object from the Cache

# File lib/odba/persistable.rb, line 202
def odba_add_observer(obj)
  odba_observers.push(obj)
  obj
end
odba_cut_connection(remove_object) click to toggle source

Removes all connections to another persistable. This method is called by the Cache server when remove_object is deleted from the database

# File lib/odba/persistable.rb, line 211
def odba_cut_connection(remove_object)
        odba_potentials.each { |name|
                var = instance_variable_get(name)
                if(var.eql?(remove_object))
                        instance_variable_set(name, nil)
                end
        }
end
odba_delete() click to toggle source

Permanently deletes this Persistable from the database and remove all connections to it

# File lib/odba/persistable.rb, line 221
def odba_delete
        ODBA.cache.delete(self)
end
odba_delete_observer(observer) click to toggle source

Delete observer as an observer on this object. It will no longer receive notifications.

# File lib/odba/persistable.rb, line 226
def odba_delete_observer(observer)
  @odba_observers.delete(observer) if(@odba_observers)
end
odba_delete_observers() click to toggle source

Delete all observers associated with this object.

# File lib/odba/persistable.rb, line 230
def odba_delete_observers
  @odba_observers = nil
end
odba_id() click to toggle source

Returns the odba unique id of this Persistable. If no id had been assigned, this is now done. No attempt is made to store the Persistable in the db.

# File lib/odba/persistable.rb, line 265
def odba_id
        @odba_id ||= ODBA.cache.next_id
end
odba_isolated_store() click to toggle source

Convenience method equivalent to ODBA.cache.store(self)

# File lib/odba/persistable.rb, line 272
def odba_isolated_store
        @odba_persistent = true
        ODBA.cache.store(self)
end
odba_isolated_stub() click to toggle source

Returns a new instance of Stub, which can be used as a stand-in replacement for this Persistable.

# File lib/odba/persistable.rb, line 278
def odba_isolated_stub
        Stub.new(self.odba_id, nil, self)
end
odba_isolated_twin() click to toggle source

Returns a duplicate of this Persistable, for which all connected Persistables have been replaced by a Stub

# File lib/odba/persistable.rb, line 283
def odba_isolated_twin
        # ensure a valid odba_id
        self.odba_id
        twin = self.odba_dup
        twin.odba_replace_persistables
        twin.odba_replace_excluded!
        twin
end
odba_notify_observers(*args) click to toggle source

Invoke the update method in each currently associated observer in turn, passing it the given arguments

# File lib/odba/persistable.rb, line 307
def odba_notify_observers(*args)
  odba_observers.each { |obs| obs.odba_update(*args) }
end
odba_observers() click to toggle source
# File lib/odba/persistable.rb, line 310
def odba_observers
  @odba_observers ||= []
end
odba_prefetch?() click to toggle source

A Persistable instance can be prefetchable. This means that the object can be loaded at startup by calling ODBA.cache.prefetch, and that it will never expire from the Cache. The prefetch status can be controlled per instance by setting the instance variable @odba_prefetch, and per class by overriding the module constant ODBA_PREFETCH

# File lib/odba/persistable.rb, line 296
          def odba_prefetch?
@odba_prefetch ||= nil
                  @odba_prefetch \
  || (defined?(self::class::ODBA_PREFETCH) && self::class::ODBA_PREFETCH)
          end
odba_store(name = nil) click to toggle source

Stores this Persistable and recursively all connected unsaved persistables, until no more direcly connected unsaved persistables can be found. The optional parameter name can be used later to retrieve this Persistable using Cache#fetch_named

# File lib/odba/persistable.rb, line 376
        def odba_store(name = nil)
                begin
                        unless (name.nil?)
                                old_name = @odba_name
                                @odba_name = name
                        end
                        odba_store_unsaved
self
                rescue
                        @odba_name = old_name
                        raise
                end
        end
odba_take_snapshot() click to toggle source

Recursively stores all connected Persistables.

# File lib/odba/persistable.rb, line 427
def odba_take_snapshot
        @odba_snapshot_level ||= 0
        snapshot_level = @odba_snapshot_level.next
        current_level = [self]
        tree_level = 0
        while(!current_level.empty?)
                tree_level += 1
                obj_count = 0
                next_level = []
                current_level.each { |item|
                        if(item.odba_unsaved?(snapshot_level))
                                obj_count += 1
                                next_level += item.odba_unsaved_neighbors(snapshot_level)
                                item.odba_snapshot(snapshot_level)
                        end
                }
                current_level = next_level #.uniq
        end
end

Protected Instance Methods

odba_replace_excluded!() click to toggle source
# File lib/odba/persistable.rb, line 476
def odba_replace_excluded!
        odba_exclude_vars.each { |name|
                instance_variable_set(name, nil)
        }
end