cache_method 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/DEVELOPER_NOTES.rdoc +45 -0
- data/Gemfile +4 -0
- data/README.rdoc +95 -0
- data/Rakefile +22 -0
- data/cache_method.gemspec +33 -0
- data/lib/cache_method/cache/epoch.rb +45 -0
- data/lib/cache_method/cache/key.rb +56 -0
- data/lib/cache_method/cache.rb +65 -0
- data/lib/cache_method/config.rb +45 -0
- data/lib/cache_method/version.rb +3 -0
- data/lib/cache_method.rb +61 -0
- data/test/helper.rb +51 -0
- data/test/test_cache_class_methods.rb +53 -0
- data/test/test_cache_instance_methods.rb +90 -0
- metadata +183 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
= Developer notes
|
2
|
+
|
3
|
+
== Lots of cache method signatures...
|
4
|
+
|
5
|
+
def get(k)
|
6
|
+
if defined?(::Memcached) and raw_client.is_a?(::Memcached)
|
7
|
+
# def get(keys, marshal=true)
|
8
|
+
begin; raw_client.get(k.to_s); rescue ::Memcached::NotFound; nil; end
|
9
|
+
elsif defined?(::Memcached::Rails) and raw_client.is_a?(::Memcached::Rails)
|
10
|
+
# def get(key, raw=false)
|
11
|
+
raw_client.get k.to_s
|
12
|
+
elsif defined?(::Dalli::Client) and raw_client.is_a?(::Dalli::Client)
|
13
|
+
# def get(key, options=nil)
|
14
|
+
raw_client.get k.to_s
|
15
|
+
elsif defined?(::MemCache) and raw_client.is_a?(::MemCache)
|
16
|
+
# def get(key, raw = false)
|
17
|
+
raw_client.get k.to_s
|
18
|
+
elsif defined?(::ActiveSupport::Cache::Store) and raw_client.is_a?(::ActiveSupport::Cache::Store)
|
19
|
+
# def read(name, options = nil)
|
20
|
+
raw_client.read k.to_s
|
21
|
+
else
|
22
|
+
raise "Unknown client: #{raw_client.inspect}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def set(k, v)
|
27
|
+
if defined?(::Memcached) and raw_client.is_a?(::Memcached)
|
28
|
+
# def set(key, value, ttl=@default_ttl, marshal=true, flags=FLAGS)
|
29
|
+
raw_client.set k.to_s, v, ttl
|
30
|
+
elsif defined?(::Memcached::Rails) and raw_client.is_a?(::Memcached::Rails)
|
31
|
+
# def set(key, value, ttl=@default_ttl, raw=false)
|
32
|
+
raw_client.set k.to_s, v, ttl
|
33
|
+
elsif defined?(::Dalli::Client) and raw_client.is_a?(::Dalli::Client)
|
34
|
+
# def set(key, value, ttl=nil, options=nil)
|
35
|
+
raw_client.set k.to_s, v, ttl
|
36
|
+
elsif defined?(::MemCache) and raw_client.is_a?(::MemCache)
|
37
|
+
# def set(key, value, expiry = 0, raw = false)
|
38
|
+
raw_client.set k.to_s, v, ttl
|
39
|
+
elsif defined?(::ActiveSupport::Cache::Store) and raw_client.is_a?(::ActiveSupport::Cache::Store)
|
40
|
+
# def write(name, value, options = nil)
|
41
|
+
raw_client.write k.to_s, v, :expires_in => ttl
|
42
|
+
else
|
43
|
+
raise "Unknown client: #{raw_client.inspect}"
|
44
|
+
end
|
45
|
+
end
|
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
= cache_method
|
2
|
+
|
3
|
+
It's like <tt>alias_method</tt>, but it's <tt>cache_method</tt>!
|
4
|
+
|
5
|
+
== Example
|
6
|
+
|
7
|
+
require 'cache_method'
|
8
|
+
class Blog
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :url
|
11
|
+
|
12
|
+
def initialize(name, url)
|
13
|
+
@name = name
|
14
|
+
@url = url
|
15
|
+
end
|
16
|
+
|
17
|
+
# The method we're going to cache
|
18
|
+
def get_latest_entries
|
19
|
+
sleep 5
|
20
|
+
end
|
21
|
+
|
22
|
+
# What you get with this gem
|
23
|
+
cache_method :get_latest_entries
|
24
|
+
|
25
|
+
# Per Ruby convention, a "hash code" representing the internal state of an instance.
|
26
|
+
# It's recommended that you construct a String or a Hash and then call #hash on it.
|
27
|
+
def hash
|
28
|
+
{ :name => name, :url => url }.hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Then you can do
|
33
|
+
|
34
|
+
my_blog.get_latest_entries => first time won't be cached
|
35
|
+
my_blog.get_latest_entries => second time will come from cache
|
36
|
+
|
37
|
+
And clear them too
|
38
|
+
|
39
|
+
my_blog.clear_method_cache :get_latest_entries
|
40
|
+
|
41
|
+
(which doesn't delete the rest of your cache)
|
42
|
+
|
43
|
+
== Configuration (and supported cache clients)
|
44
|
+
|
45
|
+
You need to set a client that will be used to access the cache:
|
46
|
+
|
47
|
+
CacheMethod.config.client = Memcached.new '127.0.0.1:11211'
|
48
|
+
|
49
|
+
or
|
50
|
+
|
51
|
+
CacheMethod.config.client = Redis.new
|
52
|
+
|
53
|
+
or this might even work...
|
54
|
+
|
55
|
+
CacheMethod.config.client = Rails.cache
|
56
|
+
|
57
|
+
See <tt>Config</tt> for the full list of supported caches.
|
58
|
+
|
59
|
+
== Defining a #hash method (not the same as #to_hash)
|
60
|
+
|
61
|
+
Since we're not pure functional programmers, sometimes cache hits depend on object state in addition to method arguments. To illustrate:
|
62
|
+
|
63
|
+
my_blog.get_latest_entries
|
64
|
+
|
65
|
+
get_latest_entries doesn't take any arguments, so it must depend on my_blog.url or something. This works because we define:
|
66
|
+
|
67
|
+
class Blog
|
68
|
+
# [...]
|
69
|
+
def hash
|
70
|
+
{ :name => name, :url => url }.hash
|
71
|
+
end
|
72
|
+
# [...]
|
73
|
+
end
|
74
|
+
|
75
|
+
You should follow Ruby convention and have <tt>#hash</tt> return a <tt>Fixnum</tt>.
|
76
|
+
|
77
|
+
Ideally, you should try to make a <tt>String</tt> or a <tt>Hash</tt> and call the standard <tt>#hash</tt> on that.
|
78
|
+
|
79
|
+
Note: this is NOT the same thing as <tt>#to_hash</tt>! That returns a <b><tt>Hash</tt></b>. What we want is an integer "hash code."
|
80
|
+
|
81
|
+
== Rationale
|
82
|
+
|
83
|
+
* It should be easy to cache a method using memcached.
|
84
|
+
* The main clients should be supported
|
85
|
+
* memcache-client (for people who use the Rails default)
|
86
|
+
* dalli (for people on heroku)
|
87
|
+
* memcached (for people using Evan Weaver's ultra-fast gem)
|
88
|
+
* redis (for people who like that sort of thing, but you won't get expiration)
|
89
|
+
* It should be easy to uncache a method without clearing the whole cache
|
90
|
+
* It should be easy to cache instance methods
|
91
|
+
* It should be easy to cache methods that depend on object state
|
92
|
+
|
93
|
+
== Copyright
|
94
|
+
|
95
|
+
Copyright 2011 Seamus Abshere
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test) do |test|
|
7
|
+
test.libs << 'lib' << 'test'
|
8
|
+
test.pattern = 'test/**/test_*.rb'
|
9
|
+
test.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
require 'rake/rdoctask'
|
15
|
+
Rake::RDocTask.new do |rdoc|
|
16
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
17
|
+
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = "cache_method #{version}"
|
20
|
+
rdoc.rdoc_files.include('README*')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "cache_method/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "cache_method"
|
7
|
+
s.version = CacheMethod::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Seamus Abshere"]
|
10
|
+
s.email = ["seamus@abshere.net"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Provides cache_method}
|
13
|
+
s.description = %q{Lets you cache methods (to memcached, redis, etc.) sort of like you can memoize them}
|
14
|
+
|
15
|
+
s.rubyforge_project = "cache_method"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency 'redis'
|
22
|
+
s.add_development_dependency 'activesupport', '>=2.3.4'
|
23
|
+
s.add_development_dependency 'i18n' # activesupport
|
24
|
+
s.add_development_dependency 'test-unit'
|
25
|
+
s.add_development_dependency 'memcached'
|
26
|
+
s.add_development_dependency 'dalli'
|
27
|
+
s.add_development_dependency 'memcache-client'
|
28
|
+
# if RUBY_VERSION >= '1.9'
|
29
|
+
# s.add_development_dependency 'ruby-debug19'
|
30
|
+
# else
|
31
|
+
# s.add_development_dependency 'ruby-debug'
|
32
|
+
# end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module CacheMethod
|
2
|
+
class Cache
|
3
|
+
class Epoch
|
4
|
+
class << self
|
5
|
+
def mark_passing(options = {})
|
6
|
+
e = new options
|
7
|
+
e.mark_passing
|
8
|
+
end
|
9
|
+
def current(options = {})
|
10
|
+
e = new options
|
11
|
+
e.current
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(options = {})
|
16
|
+
options.each do |k, v|
|
17
|
+
instance_variable_set "@#{k}", v
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :obj
|
22
|
+
attr_reader :method_id
|
23
|
+
|
24
|
+
def method_signature
|
25
|
+
@method_signature ||= Key.method_signature(obj, method_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def obj_hash
|
29
|
+
@obj_hash ||= obj.hash
|
30
|
+
end
|
31
|
+
|
32
|
+
def cache_key
|
33
|
+
[ 'CacheMethod', method_signature, obj_hash ].join ','
|
34
|
+
end
|
35
|
+
|
36
|
+
def current
|
37
|
+
Cache.instance.get(cache_key).to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
def mark_passing
|
41
|
+
Cache.instance.set cache_key, (current+1), 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
module CacheMethod
|
3
|
+
class Cache
|
4
|
+
class Key
|
5
|
+
class << self
|
6
|
+
def digest(*ary)
|
7
|
+
::Digest::MD5.hexdigest ary.flatten.map { |i| i.to_s }.join
|
8
|
+
end
|
9
|
+
def parse(str)
|
10
|
+
method_signature, epoch, obj_hash, args_digest = str.split ','
|
11
|
+
new :method_signature => method_signature, :epoch => epoch, :obj_hash => obj_hash, :args_digest => args_digest
|
12
|
+
end
|
13
|
+
def klass_name(obj)
|
14
|
+
obj.is_a?(::Class) ? obj.to_s : obj.class.to_s
|
15
|
+
end
|
16
|
+
def method_delimiter(obj)
|
17
|
+
obj.is_a?(::Class) ? '.' : '#'
|
18
|
+
end
|
19
|
+
def method_signature(obj, method_id)
|
20
|
+
[ klass_name(obj), method_id ].join method_delimiter(obj)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(options = {})
|
25
|
+
options.each do |k, v|
|
26
|
+
instance_variable_set "@#{k}", v
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :obj
|
31
|
+
attr_reader :method_id
|
32
|
+
attr_reader :args
|
33
|
+
|
34
|
+
def obj_hash
|
35
|
+
@obj_hash ||= obj.hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def args_digest
|
39
|
+
@args_digest ||= Key.digest(args)
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_signature
|
43
|
+
@method_signature ||= Key.method_signature(obj, method_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
def epoch
|
47
|
+
@epoch ||= Epoch.current(:obj => obj, :method_id => method_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_str
|
51
|
+
[ method_signature, epoch, obj_hash, args_digest ].join ','
|
52
|
+
end
|
53
|
+
alias :to_s :to_str
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
module CacheMethod
|
3
|
+
# All cache requests go through a clearinghouse, allowing uncaching.
|
4
|
+
class Cache #:nodoc: all
|
5
|
+
autoload :Key, 'cache_method/cache/key'
|
6
|
+
autoload :Epoch, 'cache_method/cache/epoch'
|
7
|
+
|
8
|
+
include ::Singleton
|
9
|
+
|
10
|
+
def flush
|
11
|
+
bare_client.send %w{ flush flush_all clear flushdb }.detect { |c| bare_client.respond_to? c }
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch(obj, method_id, ttl, *args)
|
15
|
+
k = Key.new :obj => obj, :method_id => method_id, :args => args
|
16
|
+
if cached_v = get(k.to_s)
|
17
|
+
return cached_v
|
18
|
+
end
|
19
|
+
v = yield
|
20
|
+
set k.to_s, v, ttl
|
21
|
+
v
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(obj, method_id)
|
25
|
+
Epoch.mark_passing :obj => obj, :method_id => method_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def get(k)
|
29
|
+
if defined?(::Memcached) and bare_client.is_a?(::Memcached)
|
30
|
+
begin; bare_client.get(k); rescue ::Memcached::NotFound; nil; end
|
31
|
+
elsif defined?(::Redis) and bare_client.is_a?(::Redis)
|
32
|
+
if cached_v = bare_client.get(k)
|
33
|
+
::Marshal.load cached_v
|
34
|
+
end
|
35
|
+
elsif bare_client.respond_to?(:get)
|
36
|
+
bare_client.get k
|
37
|
+
elsif bare_client.respond_to?(:read)
|
38
|
+
bare_client.read k
|
39
|
+
else
|
40
|
+
raise "Don't know how to work with #{bare_client.inspect}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set(k, v, ttl)
|
45
|
+
ttl ||= ::CacheMethod.config.default_ttl
|
46
|
+
if defined?(::Redis) and bare_client.is_a?(::Redis)
|
47
|
+
bare_client.set k, ::Marshal.dump(v)
|
48
|
+
elsif bare_client.respond_to?(:set)
|
49
|
+
bare_client.set k, v, ttl
|
50
|
+
elsif bare_client.respond_to?(:write)
|
51
|
+
if ttl == 0
|
52
|
+
bare_client.write k, v # never expire
|
53
|
+
else
|
54
|
+
bare_client.write k, v, :expires_in => ttl
|
55
|
+
end
|
56
|
+
else
|
57
|
+
raise "Don't know how to work with #{bare_client.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def bare_client
|
62
|
+
::CacheMethod.config.client
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
module CacheMethod
|
3
|
+
# Here's where you set config options.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
# CacheMethod.config.client = Memcached.new '127.0.0.1:11211'
|
7
|
+
# CacheMethod.config.default_ttl = 120 # seconds
|
8
|
+
#
|
9
|
+
# You'd probably put this in your Rails config/initializers, for example.
|
10
|
+
class Config
|
11
|
+
include ::Singleton
|
12
|
+
|
13
|
+
# Client for accessing the cache.
|
14
|
+
#
|
15
|
+
# Supported memcached clients:
|
16
|
+
# * memcached[https://github.com/fauna/memcached] (either a Memcached or a Memcached::Rails)
|
17
|
+
# * dalli[https://github.com/mperham/dalli] (either a Dalli::Client or an ActiveSupport::Cache::DalliStore)
|
18
|
+
# * memcache-client[https://github.com/mperham/memcache-client] (MemCache, the one commonly used by Rails)
|
19
|
+
#
|
20
|
+
# Supported Redis clients:
|
21
|
+
# * redis[https://github.com/ezmobius/redis-rb] (NOTE: AUTOMATIC CACHE EXPIRATION NOT SUPPORTED)
|
22
|
+
#
|
23
|
+
# Example:
|
24
|
+
# CacheMethod.config.client = Memcached.new '127.0.0.1:11211'
|
25
|
+
def client=(client)
|
26
|
+
@client = client
|
27
|
+
end
|
28
|
+
|
29
|
+
def client #:nodoc:
|
30
|
+
@client || raise("You need to set CacheMethod.config.client with a cache client of your choice")
|
31
|
+
end
|
32
|
+
|
33
|
+
# TTL for method caches. Defaults to 60 seconds.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
# CacheMethod.config.default_ttl = 120 # seconds
|
37
|
+
def default_ttl=(seconds)
|
38
|
+
@default_ttl = seconds
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_ttl #:nodoc:
|
42
|
+
@default_ttl || 60
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/cache_method.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# See the README.rdoc for more info!
|
2
|
+
module CacheMethod
|
3
|
+
autoload :Config, 'cache_method/config'
|
4
|
+
autoload :Cache, 'cache_method/cache'
|
5
|
+
|
6
|
+
def self.config #:nodoc:
|
7
|
+
Config.instance
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.cache #:nodoc:
|
11
|
+
Cache.instance
|
12
|
+
end
|
13
|
+
|
14
|
+
# All Objects, including instances and Classes, get the <tt>#clear_method_cache</tt> method.
|
15
|
+
module InstanceMethods
|
16
|
+
# Clear the cache for a particular method.
|
17
|
+
#
|
18
|
+
# Note: Remember to define <tt>#hash</tt> on any object whose instance methods might get cached.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
# my_blog.clear_method_cache :get_latest_entries
|
22
|
+
def clear_method_cache(method_id)
|
23
|
+
::CacheMethod.cache.delete self, method_id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# All Classes (but not instances), get the <tt>.cache_method</tt> method.
|
28
|
+
module ClassMethods
|
29
|
+
# Cache a method. TTL in seconds, defaults to whatever's in CacheMethod.config.default_ttl
|
30
|
+
#
|
31
|
+
# Note: Remember to define <tt>#hash</tt> on any object whose instance methods might get cached.
|
32
|
+
#
|
33
|
+
# Note 2: Check out CacheMethod.config.default_ttl... the default is only 60 seconds.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
# class Blog
|
37
|
+
# # [...]
|
38
|
+
# def get_latest_entries
|
39
|
+
# sleep 5
|
40
|
+
# end
|
41
|
+
# # [...]
|
42
|
+
# cache_method :get_latest_entries
|
43
|
+
# # if you wanted a different ttl...
|
44
|
+
# # cache_method :get_latest_entries, 800 #seconds
|
45
|
+
# end
|
46
|
+
def cache_method(method_id, ttl = nil)
|
47
|
+
original_method_id = "_uncached_#{method_id}"
|
48
|
+
alias_method original_method_id, method_id
|
49
|
+
define_method method_id do |*args|
|
50
|
+
::CacheMethod.cache.fetch self, method_id, ttl, *args do
|
51
|
+
send original_method_id, *args
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
unless ::Object.method_defined? :cache_method
|
59
|
+
::Object.send :include, ::CacheMethod::InstanceMethods
|
60
|
+
::Object.extend ::CacheMethod::ClassMethods
|
61
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
4
|
+
require 'test/unit'
|
5
|
+
# require 'ruby-debug'
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
|
+
require 'cache_method'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
def setup
|
12
|
+
CacheMethod.cache.flush
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'memcached'
|
17
|
+
require 'memcache'
|
18
|
+
require 'redis'
|
19
|
+
require 'dalli'
|
20
|
+
require 'active_support/all'
|
21
|
+
require 'active_support/cache/dalli_store'
|
22
|
+
def random_cache
|
23
|
+
c = if ENV['C']
|
24
|
+
ENV['C'].to_i
|
25
|
+
else
|
26
|
+
rand 6
|
27
|
+
end
|
28
|
+
case c
|
29
|
+
when 0
|
30
|
+
$stderr.puts 'using memcached'
|
31
|
+
Memcached.new 'localhost:11211'
|
32
|
+
when 1
|
33
|
+
$stderr.puts 'using memcache-client'
|
34
|
+
MemCache.new ['localhost:11211']
|
35
|
+
when 2
|
36
|
+
$stderr.puts 'using dalli'
|
37
|
+
Dalli::Client.new ['localhost:11211']
|
38
|
+
when 3
|
39
|
+
$stderr.puts 'using dalli_store'
|
40
|
+
ActiveSupport::Cache::DalliStore.new ['localhost:11211']
|
41
|
+
when 4
|
42
|
+
$stderr.puts 'using memcached-rails'
|
43
|
+
Memcached::Rails.new 'localhost:11211'
|
44
|
+
when 5
|
45
|
+
$stderr.puts 'using Redis'
|
46
|
+
Redis.new
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
$my_cache = random_cache
|
51
|
+
CacheMethod.config.client = $my_cache
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Blog2
|
4
|
+
class << self
|
5
|
+
attr_writer :request_count
|
6
|
+
def request_count
|
7
|
+
@request_count ||= 0
|
8
|
+
end
|
9
|
+
def get_latest_entries
|
10
|
+
self.request_count += 1
|
11
|
+
'danke schoen'
|
12
|
+
end
|
13
|
+
cache_method :get_latest_entries
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class TestCacheClassMethods < Test::Unit::TestCase
|
18
|
+
def setup
|
19
|
+
super
|
20
|
+
Blog2.request_count = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_cache_method
|
24
|
+
assert_equal 0, Blog2.request_count
|
25
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
26
|
+
assert_equal 1, Blog2.request_count
|
27
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
28
|
+
assert_equal 1, Blog2.request_count
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_clear_method
|
32
|
+
assert_equal 0, Blog2.request_count
|
33
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
34
|
+
assert_equal 1, Blog2.request_count
|
35
|
+
Blog2.clear_method_cache :get_latest_entries
|
36
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
37
|
+
assert_equal 2, Blog2.request_count
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_clear_method_doesnt_overstep
|
41
|
+
assert_equal 0, Blog2.request_count
|
42
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
43
|
+
assert_equal 1, Blog2.request_count
|
44
|
+
|
45
|
+
Blog2.clear_method_cache :foobar
|
46
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
47
|
+
assert_equal 1, Blog2.request_count
|
48
|
+
|
49
|
+
Blog2.clear_method_cache :get_latest_entries
|
50
|
+
assert_equal 'danke schoen', Blog2.get_latest_entries
|
51
|
+
assert_equal 2, Blog2.request_count
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Blog1
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :url
|
6
|
+
def initialize(name, url)
|
7
|
+
@name = name
|
8
|
+
@url = url
|
9
|
+
end
|
10
|
+
attr_writer :request_count
|
11
|
+
def request_count
|
12
|
+
@request_count ||= 0
|
13
|
+
end
|
14
|
+
def get_latest_entries
|
15
|
+
self.request_count += 1
|
16
|
+
["hello from #{name}"]
|
17
|
+
end
|
18
|
+
cache_method :get_latest_entries
|
19
|
+
def get_latest_entries2
|
20
|
+
self.request_count += 1
|
21
|
+
["voo vaa #{name}"]
|
22
|
+
end
|
23
|
+
cache_method :get_latest_entries2, 1 # second
|
24
|
+
def hash
|
25
|
+
{ :name => name, :url => url }.hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class TestCacheInstanceMethods < Test::Unit::TestCase
|
30
|
+
def new_instance_of_my_blog
|
31
|
+
Blog1.new 'my_blog', 'http://my_blog.example.com'
|
32
|
+
end
|
33
|
+
def new_instance_of_another_blog
|
34
|
+
Blog1.new 'another_blog', 'http://another_blog.example.com'
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_cache_method
|
38
|
+
a = new_instance_of_my_blog
|
39
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
40
|
+
assert_equal 1, a.request_count
|
41
|
+
a = new_instance_of_my_blog
|
42
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
43
|
+
assert_equal 0, a.request_count
|
44
|
+
xxx = new_instance_of_another_blog
|
45
|
+
assert_equal ["hello from #{xxx.name}"], xxx.get_latest_entries
|
46
|
+
assert_equal 1, xxx.request_count
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_clear_method
|
50
|
+
a = new_instance_of_my_blog
|
51
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
52
|
+
assert_equal 1, a.request_count
|
53
|
+
a.clear_method_cache :get_latest_entries
|
54
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
55
|
+
assert_equal 2, a.request_count
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_clear_a_different_method
|
59
|
+
a = new_instance_of_my_blog
|
60
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
61
|
+
assert_equal 1, a.request_count
|
62
|
+
a.clear_method_cache :foobar
|
63
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
64
|
+
assert_equal 1, a.request_count
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_clear_from_somebody_else
|
68
|
+
a = new_instance_of_my_blog
|
69
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
70
|
+
assert_equal 1, a.request_count
|
71
|
+
xxx = new_instance_of_another_blog
|
72
|
+
assert_equal ["hello from #{xxx.name}"], xxx.get_latest_entries
|
73
|
+
assert_equal 1, xxx.request_count
|
74
|
+
xxx.clear_method_cache :get_latest_entries
|
75
|
+
assert_equal ["hello from #{a.name}"], a.get_latest_entries
|
76
|
+
assert_equal 1, a.request_count
|
77
|
+
assert_equal ["hello from #{xxx.name}"], xxx.get_latest_entries
|
78
|
+
assert_equal 2, xxx.request_count
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_ttl
|
82
|
+
a = new_instance_of_my_blog
|
83
|
+
assert_equal ["voo vaa #{a.name}"], a.get_latest_entries2
|
84
|
+
assert_equal 1, a.request_count
|
85
|
+
a = new_instance_of_my_blog
|
86
|
+
sleep 2
|
87
|
+
assert_equal ["voo vaa #{a.name}"], a.get_latest_entries2
|
88
|
+
assert_equal 1, a.request_count
|
89
|
+
end
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cache_method
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Seamus Abshere
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-15 00:00:00 -06:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: redis
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: activesupport
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 11
|
44
|
+
segments:
|
45
|
+
- 2
|
46
|
+
- 3
|
47
|
+
- 4
|
48
|
+
version: 2.3.4
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: i18n
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :development
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: test-unit
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
type: :development
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: memcached
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
type: :development
|
92
|
+
version_requirements: *id005
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: dalli
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
type: :development
|
106
|
+
version_requirements: *id006
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
name: memcache-client
|
109
|
+
prerelease: false
|
110
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
type: :development
|
120
|
+
version_requirements: *id007
|
121
|
+
description: Lets you cache methods (to memcached, redis, etc.) sort of like you can memoize them
|
122
|
+
email:
|
123
|
+
- seamus@abshere.net
|
124
|
+
executables: []
|
125
|
+
|
126
|
+
extensions: []
|
127
|
+
|
128
|
+
extra_rdoc_files: []
|
129
|
+
|
130
|
+
files:
|
131
|
+
- .gitignore
|
132
|
+
- DEVELOPER_NOTES.rdoc
|
133
|
+
- Gemfile
|
134
|
+
- README.rdoc
|
135
|
+
- Rakefile
|
136
|
+
- cache_method.gemspec
|
137
|
+
- lib/cache_method.rb
|
138
|
+
- lib/cache_method/cache.rb
|
139
|
+
- lib/cache_method/cache/epoch.rb
|
140
|
+
- lib/cache_method/cache/key.rb
|
141
|
+
- lib/cache_method/config.rb
|
142
|
+
- lib/cache_method/version.rb
|
143
|
+
- test/helper.rb
|
144
|
+
- test/test_cache_class_methods.rb
|
145
|
+
- test/test_cache_instance_methods.rb
|
146
|
+
has_rdoc: true
|
147
|
+
homepage: ""
|
148
|
+
licenses: []
|
149
|
+
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
hash: 3
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
version: "0"
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
hash: 3
|
170
|
+
segments:
|
171
|
+
- 0
|
172
|
+
version: "0"
|
173
|
+
requirements: []
|
174
|
+
|
175
|
+
rubyforge_project: cache_method
|
176
|
+
rubygems_version: 1.3.7
|
177
|
+
signing_key:
|
178
|
+
specification_version: 3
|
179
|
+
summary: Provides cache_method
|
180
|
+
test_files:
|
181
|
+
- test/helper.rb
|
182
|
+
- test/test_cache_class_methods.rb
|
183
|
+
- test/test_cache_instance_methods.rb
|