module ODBA::Persistable
Constants
- Exact
- Find
- ODBA_EXCLUDE_VARS
Classes which include
Persistable
may overrideODBA_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
andHash
arePersistable
by default), redefine:ODBA_SERIALIZABLE
= ['@bar']
Attributes
Classes which include Persistable
have a class-method 'odba_index'
Public Class Methods
# 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
# File lib/odba/persistable.rb, line 156 def odba_count ODBA.cache.count(self) end
# 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
# 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
# File lib/odba/persistable.rb, line 163 def Persistable.sanitize(name) name.gsub(@@sanitize_ptrn, '_') end
Public Instance Methods
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
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
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
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
Delete all observers associated with this object.
# File lib/odba/persistable.rb, line 230 def odba_delete_observers @odba_observers = nil end
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
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
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
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
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
# File lib/odba/persistable.rb, line 310 def odba_observers @odba_observers ||= [] end
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
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
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
# File lib/odba/persistable.rb, line 476 def odba_replace_excluded! odba_exclude_vars.each { |name| instance_variable_set(name, nil) } end