method_cache 0.6.3 → 0.6.4
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/README.rdoc +2 -4
- data/VERSION.yml +2 -2
- data/lib/method_cache/proxy.rb +44 -14
- data/lib/method_cache.rb +20 -9
- data/method_cache.gemspec +14 -20
- data/test/method_cache_test.rb +54 -0
- metadata +31 -14
data/README.rdoc
CHANGED
@@ -33,10 +33,8 @@ Ruby.
|
|
33
33
|
|
34
34
|
== Install:
|
35
35
|
|
36
|
-
|
37
|
-
sudo gem install ninjudd-cache_version -s http://gems.github.com
|
38
|
-
sudo gem install ninjudd-method_cache -s http://gems.github.com
|
36
|
+
gem install method_cache
|
39
37
|
|
40
38
|
== License:
|
41
39
|
|
42
|
-
Copyright (c)
|
40
|
+
Copyright (c) 2010 Justin Balthrop, Geni.com; Published under The MIT License, see LICENSE
|
data/VERSION.yml
CHANGED
data/lib/method_cache/proxy.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
1
3
|
module MethodCache
|
2
4
|
class Proxy
|
3
5
|
attr_reader :method_name, :opts, :args, :target
|
6
|
+
NULL = 'NULL'
|
4
7
|
|
5
8
|
def initialize(method_name, opts)
|
9
|
+
opts[:cache] ||= :counters if opts[:counter]
|
6
10
|
@method_name = method_name
|
7
|
-
@opts = opts
|
11
|
+
@opts = opts
|
8
12
|
end
|
9
13
|
|
10
14
|
def bind(target, args)
|
@@ -46,11 +50,12 @@ module MethodCache
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def value
|
49
|
-
value = cache[key] unless MethodCache.disabled?
|
53
|
+
value = opts[:counter] ? cache.count(key) : cache[key] unless MethodCache.disabled?
|
50
54
|
value = nil unless valid?(:load, value)
|
51
55
|
|
52
56
|
if value.nil?
|
53
57
|
value = target.send(method_name_without_caching, *args)
|
58
|
+
raise "non-integer value returned by counter method" if opts[:counter] and not value.kind_of?(Fixnum)
|
54
59
|
write_to_cache(key, value) if valid?(:save, value)
|
55
60
|
end
|
56
61
|
|
@@ -62,7 +67,6 @@ module MethodCache
|
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
65
|
-
NULL = 'NULL'
|
66
70
|
def method_with_caching
|
67
71
|
proxy = self # Need access to the proxy in the closure.
|
68
72
|
|
@@ -71,6 +75,18 @@ module MethodCache
|
|
71
75
|
end
|
72
76
|
end
|
73
77
|
|
78
|
+
def counter_method(method_name)
|
79
|
+
proxy = self # Need access to the proxy in the closure.
|
80
|
+
|
81
|
+
lambda do |*args|
|
82
|
+
if args.last.kind_of?(Hash) and args.last.keys == [:by]
|
83
|
+
amount = args.last[:by]
|
84
|
+
args.pop
|
85
|
+
end
|
86
|
+
proxy.bind(self, args).send(method_name, amount || 1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
74
90
|
def method_name_without_caching
|
75
91
|
@method_name_without_caching ||= begin
|
76
92
|
base_name, punctuation = method_name.to_s.sub(/([?!=])$/, ''), $1
|
@@ -100,6 +116,7 @@ module MethodCache
|
|
100
116
|
object_key(arg)
|
101
117
|
end.join('|')
|
102
118
|
@key = ['m', version, arg_string].compact.join('|')
|
119
|
+
@key = "m|#{Digest::SHA1.hexdigest(@key)}" if @key.length > 250
|
103
120
|
end
|
104
121
|
@key
|
105
122
|
end
|
@@ -122,8 +139,8 @@ module MethodCache
|
|
122
139
|
if opts[name].kind_of?(Proc)
|
123
140
|
proc = opts[name].bind(target)
|
124
141
|
case proc.arity
|
125
|
-
when 0
|
126
|
-
when 1
|
142
|
+
when 0 then proc.call()
|
143
|
+
when 1 then proc.call(value)
|
127
144
|
else
|
128
145
|
proc.call(value, *args)
|
129
146
|
end
|
@@ -134,25 +151,38 @@ module MethodCache
|
|
134
151
|
|
135
152
|
def write_to_cache(key, value)
|
136
153
|
if cache.kind_of?(Hash)
|
137
|
-
raise 'expiry not permitted when cache is a Hash'
|
154
|
+
raise 'expiry not permitted when cache is a Hash' if opts[:expiry]
|
155
|
+
raise 'counter cache not permitted when cache is a Hash' if opts[:counter]
|
138
156
|
cache[key] = value
|
157
|
+
elsif opts[:counter]
|
158
|
+
cache.write(key, value.to_s, :expiry => expiry(value))
|
139
159
|
else
|
140
|
-
value
|
160
|
+
value = value.nil? ? NULL : value
|
141
161
|
cache.set(key, value, :expiry => expiry(value))
|
142
162
|
end
|
143
163
|
end
|
144
164
|
|
165
|
+
def increment(amount)
|
166
|
+
raise "cannot increment non-counter method" unless opts[:counter]
|
167
|
+
cache.incr(key, amount)
|
168
|
+
end
|
169
|
+
|
170
|
+
def decrement(amount)
|
171
|
+
raise "cannot decrement non-counter method" unless opts[:counter]
|
172
|
+
cache.decr(key, amount)
|
173
|
+
end
|
174
|
+
|
145
175
|
def object_key(arg)
|
146
176
|
return "#{class_key(arg.class)}-#{arg.string_hash}" if arg.respond_to?(:string_hash)
|
147
177
|
|
148
178
|
case arg
|
149
|
-
when NilClass
|
150
|
-
when TrueClass
|
151
|
-
when FalseClass
|
152
|
-
when Numeric
|
153
|
-
when Symbol
|
154
|
-
when String
|
155
|
-
when Class, Module
|
179
|
+
when NilClass then 'nil'
|
180
|
+
when TrueClass then 'true'
|
181
|
+
when FalseClass then 'false'
|
182
|
+
when Numeric then arg.to_s
|
183
|
+
when Symbol then ":#{arg}"
|
184
|
+
when String then "'#{arg}'"
|
185
|
+
when Class, Module then class_key(arg)
|
156
186
|
when Hash
|
157
187
|
'{' + arg.collect {|key, value| "#{object_key(key)}=#{object_key(value)}"}.sort.join(',') + '}'
|
158
188
|
when Array
|
data/lib/method_cache.rb
CHANGED
@@ -17,13 +17,15 @@ module MethodCache
|
|
17
17
|
end
|
18
18
|
|
19
19
|
cached_instance_methods[method_name] = nil
|
20
|
-
|
20
|
+
if method_defined?(method_name) or private_method_defined?(method_name)
|
21
|
+
if proxy.opts[:counter]
|
22
|
+
define_method "increment_#{method_name}", proxy.counter_method(:increment)
|
23
|
+
define_method "decrement_#{method_name}", proxy.counter_method(:decrement)
|
24
|
+
end
|
25
|
+
|
21
26
|
# Replace instance method.
|
22
27
|
alias_method proxy.method_name_without_caching, method_name
|
23
28
|
define_method method_name, proxy.method_with_caching
|
24
|
-
rescue NameError => e
|
25
|
-
# The method has not been defined yet. We will alias it in method_added.
|
26
|
-
# pp e, e.backtrace
|
27
29
|
end
|
28
30
|
cached_instance_methods[method_name] = proxy
|
29
31
|
|
@@ -47,19 +49,28 @@ module MethodCache
|
|
47
49
|
|
48
50
|
method_name = method_name.to_sym
|
49
51
|
cached_class_methods[method_name] = nil
|
50
|
-
|
51
|
-
# Replace class method.
|
52
|
+
if class_method_defined?(method_name)
|
52
53
|
(class << self; self; end).module_eval do
|
54
|
+
if proxy.opts[:counter]
|
55
|
+
define_method "increment_#{method_name}", proxy.counter_method(:increment)
|
56
|
+
define_method "decrement_#{method_name}", proxy.counter_method(:decrement)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Replace class method.
|
53
60
|
alias_method proxy.method_name_without_caching, method_name
|
54
61
|
define_method method_name, proxy.method_with_caching
|
55
62
|
end
|
56
|
-
rescue NameError => e
|
57
|
-
# The method has not been defined yet. We will alias it in singleton_method_added.
|
58
|
-
# pp e, e.backtrace
|
59
63
|
end
|
60
64
|
cached_class_methods[method_name] = proxy
|
61
65
|
end
|
62
66
|
|
67
|
+
def class_method_defined?(method_name)
|
68
|
+
method(method_name)
|
69
|
+
true
|
70
|
+
rescue NameError
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
63
74
|
def self.default_cache
|
64
75
|
@default_cache ||= {}
|
65
76
|
end
|
data/method_cache.gemspec
CHANGED
@@ -1,47 +1,41 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{method_cache}
|
8
|
-
s.version = "0.6.
|
8
|
+
s.version = "0.6.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Justin Balthrop"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-08-23}
|
13
13
|
s.description = %q{Simple memcache-based memoization library for Ruby}
|
14
14
|
s.email = %q{code@justinbalthrop.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
|
17
|
+
"README.rdoc"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
20
|
"LICENSE",
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION.yml",
|
24
|
+
"lib/method_cache.rb",
|
25
|
+
"lib/method_cache/proxy.rb",
|
26
|
+
"method_cache.gemspec",
|
27
|
+
"test/method_cache_test.rb",
|
28
|
+
"test/test_helper.rb"
|
29
29
|
]
|
30
30
|
s.homepage = %q{http://github.com/ninjudd/method_cache}
|
31
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
32
31
|
s.require_paths = ["lib"]
|
33
|
-
s.rubygems_version = %q{1.
|
32
|
+
s.rubygems_version = %q{1.5.2}
|
34
33
|
s.summary = %q{Simple memcache-based memoization library for Ruby}
|
35
|
-
s.test_files = [
|
36
|
-
"test/method_cache_test.rb",
|
37
|
-
"test/test_helper.rb"
|
38
|
-
]
|
39
34
|
|
40
35
|
if s.respond_to? :specification_version then
|
41
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
42
36
|
s.specification_version = 3
|
43
37
|
|
44
|
-
if Gem::Version.new(Gem::
|
38
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
45
39
|
s.add_runtime_dependency(%q<memcache>, [">= 1.0.0"])
|
46
40
|
else
|
47
41
|
s.add_dependency(%q<memcache>, [">= 1.0.0"])
|
data/test/method_cache_test.rb
CHANGED
@@ -20,6 +20,17 @@ class Foo
|
|
20
20
|
@@i += i
|
21
21
|
end
|
22
22
|
cache_method :baz, :cache => :remote
|
23
|
+
|
24
|
+
cache_class_method :bap
|
25
|
+
def self.bap(i)
|
26
|
+
@i ||= 0
|
27
|
+
@i += i
|
28
|
+
end
|
29
|
+
|
30
|
+
cache_class_method :zap, :counter => true
|
31
|
+
def self.zap
|
32
|
+
0
|
33
|
+
end
|
23
34
|
end
|
24
35
|
|
25
36
|
module Bar
|
@@ -30,10 +41,16 @@ module Bar
|
|
30
41
|
@i ||= 0
|
31
42
|
@i += i
|
32
43
|
end
|
44
|
+
|
45
|
+
cache_method :foo_count, :counter => true, :cache => :default
|
46
|
+
def foo_count(key)
|
47
|
+
100
|
48
|
+
end
|
33
49
|
end
|
34
50
|
|
35
51
|
class Baz
|
36
52
|
include Bar
|
53
|
+
extend Bar
|
37
54
|
end
|
38
55
|
|
39
56
|
class TestMethodCache < Test::Unit::TestCase
|
@@ -84,6 +101,13 @@ class TestMethodCache < Test::Unit::TestCase
|
|
84
101
|
assert b2 == a.baz(2)
|
85
102
|
end
|
86
103
|
|
104
|
+
should 'cache class methods' do
|
105
|
+
assert_equal 10, Foo.bap(10)
|
106
|
+
assert_equal 23, Foo.bap(13)
|
107
|
+
assert_equal 10, Foo.bap(10)
|
108
|
+
assert_equal 23, Foo.bap(13)
|
109
|
+
end
|
110
|
+
|
87
111
|
should 'cache methods for mixins' do
|
88
112
|
a = Baz.new
|
89
113
|
|
@@ -93,6 +117,13 @@ class TestMethodCache < Test::Unit::TestCase
|
|
93
117
|
assert_equal 3, a.foo(2)
|
94
118
|
end
|
95
119
|
|
120
|
+
should 'cache class methods for mixins' do
|
121
|
+
assert_equal 1, Baz.foo(1)
|
122
|
+
assert_equal 1, Baz.foo(1)
|
123
|
+
assert_equal 3, Baz.foo(2)
|
124
|
+
assert_equal 3, Baz.foo(2)
|
125
|
+
end
|
126
|
+
|
96
127
|
should 'invalidate cached method' do
|
97
128
|
a = Foo.new
|
98
129
|
|
@@ -105,6 +136,29 @@ class TestMethodCache < Test::Unit::TestCase
|
|
105
136
|
assert_equal 3, a.foo(2)
|
106
137
|
end
|
107
138
|
|
139
|
+
should 'cache counters' do
|
140
|
+
b = Baz.new
|
141
|
+
|
142
|
+
assert_equal 100, b.foo_count(:bar)
|
143
|
+
b.increment_foo_count(:bar, :by => 42)
|
144
|
+
assert_equal 142, b.foo_count(:bar)
|
145
|
+
b.decrement_foo_count(:bar, :by => 99)
|
146
|
+
assert_equal 43, b.foo_count(:bar)
|
147
|
+
b.increment_foo_count(:bar)
|
148
|
+
assert_equal 44, b.foo_count(:bar)
|
149
|
+
|
150
|
+
assert_equal 100, b.foo_count(:baz)
|
151
|
+
b.increment_foo_count(:baz)
|
152
|
+
assert_equal 101, b.foo_count(:baz)
|
153
|
+
assert_equal 44, b.foo_count(:bar) # make sure :bar wasn't affected
|
154
|
+
|
155
|
+
assert_equal 0, Foo.zap
|
156
|
+
Foo.increment_zap(:by => 3)
|
157
|
+
assert_equal 3, Foo.zap
|
158
|
+
Foo.decrement_zap
|
159
|
+
assert_equal 2, Foo.zap
|
160
|
+
end
|
161
|
+
|
108
162
|
should 'use consistent local keys' do
|
109
163
|
a = Foo.new
|
110
164
|
o = Object.new
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_cache
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 4
|
10
|
+
version: 0.6.4
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Justin Balthrop
|
@@ -9,19 +15,25 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2011-08-23 00:00:00 -07:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: memcache
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
27
|
- - ">="
|
22
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 0
|
23
34
|
version: 1.0.0
|
24
|
-
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
25
37
|
description: Simple memcache-based memoization library for Ruby
|
26
38
|
email: code@justinbalthrop.com
|
27
39
|
executables: []
|
@@ -46,29 +58,34 @@ homepage: http://github.com/ninjudd/method_cache
|
|
46
58
|
licenses: []
|
47
59
|
|
48
60
|
post_install_message:
|
49
|
-
rdoc_options:
|
50
|
-
|
61
|
+
rdoc_options: []
|
62
|
+
|
51
63
|
require_paths:
|
52
64
|
- lib
|
53
65
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
54
67
|
requirements:
|
55
68
|
- - ">="
|
56
69
|
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
57
73
|
version: "0"
|
58
|
-
version:
|
59
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
60
76
|
requirements:
|
61
77
|
- - ">="
|
62
78
|
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
80
|
+
segments:
|
81
|
+
- 0
|
63
82
|
version: "0"
|
64
|
-
version:
|
65
83
|
requirements: []
|
66
84
|
|
67
85
|
rubyforge_project:
|
68
|
-
rubygems_version: 1.
|
86
|
+
rubygems_version: 1.5.2
|
69
87
|
signing_key:
|
70
88
|
specification_version: 3
|
71
89
|
summary: Simple memcache-based memoization library for Ruby
|
72
|
-
test_files:
|
73
|
-
|
74
|
-
- test/test_helper.rb
|
90
|
+
test_files: []
|
91
|
+
|