Skip to content

Commit

Permalink
Great refactoring applied; Global storage; misc changes
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.verbdev.com/rb/caches.rb/trunk@58 f28541f9-331e-0410-82d8-e175c77b79a1
  • Loading branch information
yrashk committed Apr 1, 2007
1 parent 099cd63 commit 16084ac
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 103 deletions.
5 changes: 3 additions & 2 deletions init.rb
Expand Up @@ -2,8 +2,9 @@

begin
Kernel.const_get :ActiveRecord
ActiveRecord::Base.extend CachesConfig
ActiveRecord::Base.extend Caches
ActiveRecord::Base.caches_storage = CachesStorage::ClassVarById
ActiveRecord::Base.class_eval do
include CachesStorage::ClassVarById
end
rescue
end
180 changes: 94 additions & 86 deletions lib/caches.rb
Expand Up @@ -7,117 +7,83 @@ def starts_with?(prefix)

module CachesStorage
module Instance
def invalidate_all_caches(*opts)
unless opts.empty?
opthash = opts.first
except = opthash[:except]
if except
except = [except] unless except.kind_of? Enumerable
@propcache.each_pair {|k,v| @propcache[k] = nil unless except.any? {|exception| k.starts_with?(exception.to_s)} }
end
else
@propcache = {}
end

protected

def _propcache
@propcache
end

def _propcache=(v)
@propcache=v
end

def _call_key(name,*args)
"#{name}#{args.hash}"
end

def _object_key(name)
"#{name}"
end

end

module Global

protected

def invalidate____cache(name,*args)
@propcache ||= {}
key = name.to_s+args.hash.to_s
@propcache[key] = nil
def _propcache
$cachesrb_propcache ||= {}
end

def invalidate_all____caches(name)
@propcache ||= {}
key = name.to_s
@propcache.keys.each {|k| @propcache[k] = nil if k.starts_with?(key) }
def _propcache=(v)
$cachesrb_propcache=v
end

def ____(name,sanitized_name,saved_getter,options,*args)
@propcache ||= {}
key = name.to_s+args.hash.to_s
cached = @propcache[key]
unless cached
@propcache[key] = { :value => self.send(saved_getter.to_sym,*args), :expires_at => Time.now.to_i + options[:timeout]}
return @propcache[key][:value]
else
unless Time.now.to_i > cached[:expires_at]
cached[:value]
else
self.send "invalidate_#{sanitized_name}_cache".to_sym, *args
self.send name.to_sym, *args
end
end
def _call_key(name,*args)
"#{self.class.name}#{name}#{args.hash}"
end

def _object_key(name)
"#{self.class.name}#{name}"
end

end


module ClassVarById

def invalidate_all_caches(*opts)
unless opts.empty?
opthash = opts.first
except = opthash[:except]
if except
except = [except] unless except.kind_of? Enumerable
@@propcache.each_pair {|k,v| @@propcache[k] = nil unless except.any? {|exception| k.starts_with?(exception.to_s)} }
end
else
@@propcache = {}
end
end

protected

def invalidate____cache(name,*args)
def _propcache
@@propcache ||= {}
key = "#{name}#{args.hash}_#{self.id}"
@@propcache[key] = nil
end

def invalidate_all____caches(name)
@@propcache ||= {}
key = "#{name}_#{self.id}"
@@propcache.keys.each {|k| @@propcache[k] = nil if k.starts_with?(key) }
def _propcache=(v)
@@propcache=v
end

def ____(name,sanitized_name,saved_getter,options,*args)
@@propcache ||= {}
key = "#{name}#{args.hash}_#{self.id}"
cached = @@propcache[key]
unless cached
@@propcache[key] = { :value => self.send(saved_getter.to_sym,*args), :expires_at => Time.now.to_i + options[:timeout]}
return @@propcache[key][:value]
else
unless Time.now.to_i > cached[:expires_at]
cached[:value]
else
self.send "invalidate_#{sanitized_name}_cache".to_sym, *args
self.send name.to_sym, *args
end
end
def _call_key(name,*args)
"#{name}#{args.hash}_#{self.id}"
end

def _object_key(name)
"#{name}_#{self.id}"
end

end
end
module CachesConfig
def caches_storage=(storage)
@@caches_storage = storage
end

def caches_storage
@@caches_storage
end

end


module Caches
def cached_methods
@@cached_methods ||= {}
end

def caches(name,options = { :timeout => 60})
def caches(name,options = {})
options = { :timeout => 60}.merge options
sanitized_name = name.to_s.delete('?')
saved_getter = "getter_#{name}"
saved_setter = "setter_#{name}"
Expand All @@ -126,21 +92,52 @@ def caches(name,options = { :timeout => 60})
has_setter = public_method_defined? setter.to_sym
alias_method saved_getter, name
alias_method(saved_setter, setter.to_sym) if has_setter
storage = ( self.respond_to?(:caches_storage) ? self.caches_storage : nil ) || options[:storage] || CachesStorage::Instance
self.cached_methods[name] = options
module_eval do
include storage
include CachesStorage::Instance unless protected_method_defined?(:_propcache)

public

def invalidate_all_caches(*opts)
unless opts.empty?
opthash = opts.first
except = opthash[:except]
if except
except = [except] unless except.kind_of? Enumerable
self._propcache.each_pair {|k,v| self._propcache[k] = nil unless except.any? {|exception| k.starts_with?(exception.to_s)} }
end
else
self._propcache = {}
end
end

define_method("invalidate_#{sanitized_name}_cache") do |*args|
invalidate____cache(name,*args)
self._propcache ||= {}
key = _call_key(name,*args)
self._propcache[key] = nil
end

define_method("invalidate_all_#{sanitized_name}_caches") do
invalidate_all____caches(name)
self._propcache ||= {}
key = _object_key(name)
self._propcache.keys.each {|k| self._propcache[k] = nil if k.starts_with?(key) }
end

define_method("#{name}") do |*args|
____(name,sanitized_name,saved_getter,options,*args)
self._propcache ||= {}
key = _call_key(name,*args)
cached = self._propcache[key]
unless cached
self._propcache[key] = { :value => self.send(saved_getter.to_sym,*args), :expires_at => Time.now.to_i + options[:timeout]}
return self._propcache[key][:value]
else
unless Time.now.to_i > cached[:expires_at]
cached[:value]
else
self.send "invalidate_#{sanitized_name}_cache".to_sym, *args
self.send name.to_sym, *args
end
end
end
if has_setter
define_method("#{setter}") do |new_val|
Expand All @@ -150,14 +147,25 @@ def caches(name,options = { :timeout => 60})
end
end
end

def class_caches(*args)
class_eval do
c = class_eval do
class <<self
extend ::Caches
end
end.send(:caches, *args)
end
c.send(:caches, *args)
c
end


def class_caches_with_storage(storage,*args)
class_caches(*args).class_eval do
include storage
end
end



def caches?(name)
self.cached_methods.has_key? name
end
Expand Down
6 changes: 4 additions & 2 deletions spec/ar_caches_spec.rb
Expand Up @@ -3,9 +3,11 @@ module ActiveRecord ; class Base ; end ; end
require File.dirname(__FILE__) + '/../lib/caches'

context "ActiveRecord instance" do
ActiveRecord::Base.extend CachesConfig
ActiveRecord::Base.extend Caches
ActiveRecord::Base.caches_storage = CachesStorage::ClassVarById
ActiveRecord::Base.class_eval do
include CachesStorage::ClassVarById
end


class Model < ActiveRecord::Base
attr_reader :accessor_counter
Expand Down
41 changes: 28 additions & 13 deletions spec/caches_spec.rb
@@ -1,5 +1,7 @@
require File.dirname(__FILE__) + '/../lib/caches'

gem 'activesupport'
require 'active_support'

context "CachedClassMethod class" do
class CachedClassMethod
Expand All @@ -10,26 +12,38 @@ def self.test
class_caches :test
end

Global_cached_class = %{class GlobalCachedClassMethod
def self.test
Time.now
end
extend Caches
class_caches_with_storage CachesStorage::Global, :test
end}

eval Global_cached_class

specify "should cache class methods" do
oldtime = CachedClassMethod.test
sleep 2
Time.stub!(:now).and_return oldtime+10
CachedClassMethod.test.should == oldtime
reset_time_now
end

specify "should not cache class methods more than once (imitating Rails development mode class reloading)" do
oldtime = GlobalCachedClassMethod.test
Time.stub!(:now).and_return oldtime+10
Class.remove_class GlobalCachedClassMethod
eval Global_cached_class
GlobalCachedClassMethod.test.should == oldtime
reset_time_now
end

specify "should not cache class methods more than once" do
oldtime = CachedClassMethod.test
sleep 2
class CachedClassMethod
def self.test
Time.now
end
extend Caches
class_caches :test
end
CachedClassMethod.test.should == oldtime
private

def reset_time_now
Time.stub!(:now).and_return { @time_now.call }
end


end

context "CachedClass instance" do
Expand Down Expand Up @@ -90,6 +104,7 @@ def method_with_args(a,b)
@cached.accessor_counter.should == 2
end


specify "should remember values for a non-default interval" do
val = @cached.accessor2
@cached.accessor_counter.should == 1
Expand Down

0 comments on commit 16084ac

Please sign in to comment.