mcmire-cache 0.3.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/.gitignore +7 -0
- data/CHANGELOG +21 -0
- data/Gemfile +22 -0
- data/README.md +229 -0
- data/Rakefile +41 -0
- data/benchmarks/afterrefactor.txt +86 -0
- data/benchmarks/midrefactor.txt +89 -0
- data/benchmarks/v0.0.2.txt +95 -0
- data/benchmarks/v0.0.3.txt +96 -0
- data/benchmarks/v0.1.2.txt +94 -0
- data/benchmarks/v0.2.1.txt +94 -0
- data/benchmarks/v0.2.2.txt +94 -0
- data/lib/cache.rb +233 -0
- data/lib/cache/active_support_cache_dalli_store.rb +15 -0
- data/lib/cache/active_support_cache_file_store.rb +11 -0
- data/lib/cache/active_support_cache_memory_store.rb +11 -0
- data/lib/cache/active_support_cache_store.rb +37 -0
- data/lib/cache/config.rb +27 -0
- data/lib/cache/dalli_client.rb +54 -0
- data/lib/cache/mem_cache.rb +46 -0
- data/lib/cache/memcached.rb +54 -0
- data/lib/cache/memcached_rails.rb +34 -0
- data/lib/cache/nothing.rb +20 -0
- data/lib/cache/redis.rb +44 -0
- data/lib/cache/redis_namespace.rb +7 -0
- data/lib/cache/version.rb +3 -0
- data/mcmire-cache.gemspec +22 -0
- data/test/helper.rb +29 -0
- data/test/profile/benchmark.rb +258 -0
- data/test/shared_tests.rb +169 -0
- data/test/test_active_support_cache_dalli_store.rb +77 -0
- data/test/test_active_support_cache_file_store.rb +19 -0
- data/test/test_active_support_cache_memory_store.rb +12 -0
- data/test/test_dalli_client.rb +70 -0
- data/test/test_mem_cache.rb +64 -0
- data/test/test_memcached.rb +64 -0
- data/test/test_memcached_rails.rb +61 -0
- data/test/test_memcached_with_binary.rb +17 -0
- data/test/test_missing_driver.rb +16 -0
- data/test/test_nothing.rb +147 -0
- data/test/test_redis.rb +60 -0
- data/test/test_redis_namespace.rb +64 -0
- metadata +108 -0
data/lib/cache.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'cache/config'
|
2
|
+
|
3
|
+
class Cache
|
4
|
+
class Error < StandardError; end
|
5
|
+
class DriverNotFound < Error
|
6
|
+
def initialize(client_class)
|
7
|
+
@client_class = client_class
|
8
|
+
end
|
9
|
+
|
10
|
+
def message
|
11
|
+
"The cache gem doesn't support #{@client_class} yet"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :config
|
16
|
+
attr_reader :metal
|
17
|
+
|
18
|
+
# For compatibility with Rails 2.x
|
19
|
+
attr_accessor :logger
|
20
|
+
|
21
|
+
# Create a new Cache instance by wrapping a client of your choice.
|
22
|
+
#
|
23
|
+
# Supported memcached clients:
|
24
|
+
#
|
25
|
+
# * memcached[https://github.com/evan/memcached] (either a Memcached or a Memcached::Rails)
|
26
|
+
# * dalli[https://github.com/mperham/dalli] (either a Dalli::Client or an ActiveSupport::Cache::DalliStore)
|
27
|
+
# * memcache-client[https://github.com/mperham/memcache-client] (MemCache, the one commonly used by Rails)
|
28
|
+
#
|
29
|
+
# Supported Redis clients:
|
30
|
+
#
|
31
|
+
# * redis[https://github.com/ezmobius/redis-rb] (Redis)
|
32
|
+
# * redis-namespace[https://github.com/defunkt/redis-namespace] (Redis::Namespace)
|
33
|
+
#
|
34
|
+
# metal - Either an instance of one of the above classes, or :nothing if you
|
35
|
+
# want to use the Cache API but not actually cache anything.
|
36
|
+
#
|
37
|
+
# Examples:
|
38
|
+
#
|
39
|
+
# # Use Memcached
|
40
|
+
# raw_client = Memcached.new('127.0.0.1:11211')
|
41
|
+
# cache = Cache.new(raw_client)
|
42
|
+
# cache.get(...)
|
43
|
+
# cache.set(...)
|
44
|
+
#
|
45
|
+
# # Don't cache anything
|
46
|
+
# cache = Cache.new(:nothing)
|
47
|
+
# cache.get(...)
|
48
|
+
# cache.set(...)
|
49
|
+
#
|
50
|
+
def initialize(metal)
|
51
|
+
@pid = ::Process.pid
|
52
|
+
@config = Config.new
|
53
|
+
if metal == :nothing
|
54
|
+
@metal = nil
|
55
|
+
client_class = 'Nothing'
|
56
|
+
driver_class = 'Nothing'
|
57
|
+
else
|
58
|
+
@metal = Cache === metal ? metal.metal : metal
|
59
|
+
client_class = @metal.class.to_s
|
60
|
+
driver_class = client_class.gsub('::', '')
|
61
|
+
end
|
62
|
+
filename = client_class.
|
63
|
+
gsub(/([a-z])([A-Z]+)/) { [$1.downcase, $2.downcase].join('_') }.
|
64
|
+
gsub('::', '_').downcase
|
65
|
+
begin
|
66
|
+
require "cache/#{filename}"
|
67
|
+
extend Cache.const_get(driver_class)
|
68
|
+
rescue LoadError, NameError => e
|
69
|
+
puts "#{e.class}: #{e.message}"
|
70
|
+
puts e.backtrace[0..5].join("\n")
|
71
|
+
raise DriverNotFound, client_class
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get a value.
|
76
|
+
#
|
77
|
+
# Example:
|
78
|
+
# cache.get 'hello'
|
79
|
+
def get(k, ignored_options = nil)
|
80
|
+
handle_fork
|
81
|
+
_get k
|
82
|
+
end
|
83
|
+
|
84
|
+
alias :read :get
|
85
|
+
|
86
|
+
# Get multiple cache entries.
|
87
|
+
#
|
88
|
+
# Example:
|
89
|
+
# cache.get_multi 'hello', 'privyet'
|
90
|
+
def get_multi(*ks)
|
91
|
+
handle_fork
|
92
|
+
_get_multi ks
|
93
|
+
end
|
94
|
+
|
95
|
+
# Store a value. Note that this will Marshal it.
|
96
|
+
#
|
97
|
+
# Example:
|
98
|
+
# cache.set 'hello', 'world'
|
99
|
+
# cache.set 'hello', 'world', 80 # seconds til it expires
|
100
|
+
def set(k, v, ttl = nil, ignored_options = nil)
|
101
|
+
handle_fork
|
102
|
+
_set k, v, _get_ttl(ttl)
|
103
|
+
end
|
104
|
+
|
105
|
+
alias :write :set
|
106
|
+
|
107
|
+
# Delete a value.
|
108
|
+
#
|
109
|
+
# Example:
|
110
|
+
# cache.delete 'hello'
|
111
|
+
def delete(k, ignored_options = nil)
|
112
|
+
handle_fork
|
113
|
+
_delete k
|
114
|
+
end
|
115
|
+
|
116
|
+
# Flush the cache.
|
117
|
+
#
|
118
|
+
# Example:
|
119
|
+
# cache.flush
|
120
|
+
def flush
|
121
|
+
handle_fork
|
122
|
+
_flush
|
123
|
+
end
|
124
|
+
|
125
|
+
alias :clear :flush
|
126
|
+
|
127
|
+
# Check if something exists.
|
128
|
+
#
|
129
|
+
# Example:
|
130
|
+
# cache.exist? 'hello'
|
131
|
+
def exist?(k, ignored_options = nil)
|
132
|
+
handle_fork
|
133
|
+
_exist? k
|
134
|
+
end
|
135
|
+
alias_method :exists?, :exist?
|
136
|
+
|
137
|
+
# Increment a value.
|
138
|
+
#
|
139
|
+
# Example:
|
140
|
+
# cache.increment 'high-fives'
|
141
|
+
def increment(k, amount = 1, ignored_options = nil)
|
142
|
+
handle_fork
|
143
|
+
new_v = _get(k).to_i + amount
|
144
|
+
_set k, new_v, 0
|
145
|
+
new_v
|
146
|
+
end
|
147
|
+
|
148
|
+
# Decrement a value.
|
149
|
+
#
|
150
|
+
# Example:
|
151
|
+
# cache.decrement 'high-fives'
|
152
|
+
def decrement(k, amount = 1, ignored_options = nil)
|
153
|
+
increment k, -amount
|
154
|
+
end
|
155
|
+
|
156
|
+
# Try to get a value and if it doesn't exist, set it to the result of the block.
|
157
|
+
#
|
158
|
+
# Accepts :expires_in for compatibility with Rails.
|
159
|
+
#
|
160
|
+
# Example:
|
161
|
+
# cache.fetch 'hello' { 'world' }
|
162
|
+
def fetch(k, ttl = nil, &blk)
|
163
|
+
handle_fork
|
164
|
+
_fetch(k, _get_ttl(ttl), &blk)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Default implementation of #fetch for drivers that do not define a method
|
168
|
+
# natively for it
|
169
|
+
def _fetch(k, ttl, &blk)
|
170
|
+
if _exist? k
|
171
|
+
_get k
|
172
|
+
elsif blk
|
173
|
+
v = blk.call
|
174
|
+
_set k, v, ttl
|
175
|
+
v
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Get the current value (if any), pass it into a block, and set the result.
|
180
|
+
#
|
181
|
+
# Example:
|
182
|
+
# cache.cas 'hello' { |current| 'world' }
|
183
|
+
def cas(k, ttl = nil, &blk)
|
184
|
+
handle_fork
|
185
|
+
_cas(k, ttl, &blk)
|
186
|
+
end
|
187
|
+
|
188
|
+
alias :compare_and_swap :cas
|
189
|
+
|
190
|
+
# Default implementation of #cas which is overridden in some drivers
|
191
|
+
def _cas(k, ttl, &blk)
|
192
|
+
if blk and _exist?(k)
|
193
|
+
old_v = _get k
|
194
|
+
new_v = blk.call old_v
|
195
|
+
_set k, new_v, _get_ttl(ttl)
|
196
|
+
new_v
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Get stats.
|
201
|
+
#
|
202
|
+
# Example:
|
203
|
+
# cache.stats
|
204
|
+
def stats
|
205
|
+
handle_fork
|
206
|
+
_stats
|
207
|
+
end
|
208
|
+
|
209
|
+
private
|
210
|
+
|
211
|
+
def handle_fork
|
212
|
+
if ::Process.pid != @pid
|
213
|
+
@pid = ::Process.pid
|
214
|
+
after_fork
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def after_fork
|
219
|
+
# nothing
|
220
|
+
end
|
221
|
+
|
222
|
+
def _get_ttl(ttl)
|
223
|
+
if Hash === ttl
|
224
|
+
ttl = ttl[:expires_in] || ttl['expires_in'] || ttl[:ttl] || ttl['ttl']
|
225
|
+
end
|
226
|
+
ttl || config.default_ttl
|
227
|
+
end
|
228
|
+
|
229
|
+
def _valid_ttl?(ttl)
|
230
|
+
ttl and ttl > 0
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'cache/active_support_cache_store'
|
2
|
+
|
3
|
+
module Cache::ActiveSupportCacheDalliStore
|
4
|
+
def self.extended(base)
|
5
|
+
base.extend Cache::ActiveSupportCacheStore
|
6
|
+
end
|
7
|
+
|
8
|
+
def after_fork
|
9
|
+
@metal.reset
|
10
|
+
end
|
11
|
+
|
12
|
+
def _stats
|
13
|
+
@metal.stats
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cache::ActiveSupportCacheStore
|
2
|
+
def _get(k)
|
3
|
+
@metal.read k
|
4
|
+
end
|
5
|
+
|
6
|
+
def _get_multi(ks)
|
7
|
+
@metal.read_multi *ks
|
8
|
+
end
|
9
|
+
|
10
|
+
def _set(k, v, ttl)
|
11
|
+
if _valid_ttl?(ttl)
|
12
|
+
@metal.write k, v, :expires_in => ttl
|
13
|
+
else
|
14
|
+
@metal.write k, v
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def _fetch(k, ttl, &blk)
|
19
|
+
if _valid_ttl?(ttl)
|
20
|
+
@metal.fetch k, { :expires_in => ttl }, &blk
|
21
|
+
else
|
22
|
+
@metal.fetch k, &blk
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def _delete(k)
|
27
|
+
@metal.delete k
|
28
|
+
end
|
29
|
+
|
30
|
+
def _flush
|
31
|
+
@metal.clear
|
32
|
+
end
|
33
|
+
|
34
|
+
def _exist?(k)
|
35
|
+
@metal.exist? k
|
36
|
+
end
|
37
|
+
end
|
data/lib/cache/config.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
class Cache
|
3
|
+
class Config
|
4
|
+
|
5
|
+
# Set the default TTL that keys will live when not specifying a TTL.
|
6
|
+
#
|
7
|
+
# ttl - Integer in seconds.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# cache.config.default_ttl = 120
|
12
|
+
# cache.set('foo', 'val') # equivalent to set('foo', 'val', 120)
|
13
|
+
#
|
14
|
+
def default_ttl=(seconds)
|
15
|
+
@default_ttl = seconds
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the currently set TTL.
|
19
|
+
#
|
20
|
+
# Returns an Integer in seconds.
|
21
|
+
#
|
22
|
+
def default_ttl
|
23
|
+
@default_ttl
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Cache::DalliClient
|
2
|
+
def after_fork
|
3
|
+
@metal.close
|
4
|
+
end
|
5
|
+
|
6
|
+
def _get(k)
|
7
|
+
@metal.get k
|
8
|
+
end
|
9
|
+
|
10
|
+
def _get_multi(ks)
|
11
|
+
@metal.get_multi ks
|
12
|
+
end
|
13
|
+
|
14
|
+
def _set(k, v, ttl)
|
15
|
+
if _valid_ttl?(ttl)
|
16
|
+
@metal.set k, v, ttl
|
17
|
+
else
|
18
|
+
@metal.set k, v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def _fetch(k, ttl, &blk)
|
23
|
+
if _valid_ttl?(ttl)
|
24
|
+
@metal.fetch k, ttl, &blk
|
25
|
+
else
|
26
|
+
@metal.fetch k, &blk
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def _cas(k, ttl, &blk)
|
31
|
+
if _valid_ttl?(ttl)
|
32
|
+
@metal.cas k, ttl, &blk
|
33
|
+
else
|
34
|
+
@metal.cas k, &blk
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def _delete(k)
|
39
|
+
@metal.delete k
|
40
|
+
end
|
41
|
+
|
42
|
+
def _flush
|
43
|
+
@metal.flush
|
44
|
+
end
|
45
|
+
|
46
|
+
# sux
|
47
|
+
def _exist?(k)
|
48
|
+
!@metal.get(k).nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
def _stats
|
52
|
+
@metal.stats
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Cache::MemCache
|
2
|
+
def after_fork
|
3
|
+
@metal.reset
|
4
|
+
end
|
5
|
+
|
6
|
+
def _get(k)
|
7
|
+
@metal.get k
|
8
|
+
end
|
9
|
+
|
10
|
+
def _get_multi(ks)
|
11
|
+
@metal.get_multi ks
|
12
|
+
end
|
13
|
+
|
14
|
+
def _set(k, v, ttl)
|
15
|
+
if _valid_ttl?(ttl)
|
16
|
+
@metal.set k, v, ttl
|
17
|
+
else
|
18
|
+
@metal.set k, v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def _fetch(k, ttl, &blk)
|
23
|
+
if _valid_ttl?(ttl)
|
24
|
+
@metal.fetch k, ttl, &blk
|
25
|
+
else
|
26
|
+
@metal.fetch k, &blk
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def _delete(k)
|
31
|
+
@metal.delete k
|
32
|
+
end
|
33
|
+
|
34
|
+
def _flush
|
35
|
+
@metal.flush_all
|
36
|
+
end
|
37
|
+
|
38
|
+
# sux
|
39
|
+
def _exist?(k)
|
40
|
+
!@metal.get(k).nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def _stats
|
44
|
+
@metal.stats
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Cache::Memcached
|
2
|
+
def thread_metal
|
3
|
+
::Thread.current["#{@pid}/#{self.class.name}/#{object_id}/thread_metal"] ||= @metal.clone
|
4
|
+
end
|
5
|
+
|
6
|
+
def _get(k)
|
7
|
+
thread_metal.get k
|
8
|
+
rescue ::Memcached::NotFound
|
9
|
+
return nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def _get_multi(ks)
|
13
|
+
thread_metal.get ks
|
14
|
+
end
|
15
|
+
|
16
|
+
def _set(k, v, ttl)
|
17
|
+
if _valid_ttl?(ttl)
|
18
|
+
thread_metal.set k, v, ttl
|
19
|
+
else
|
20
|
+
thread_metal.set k, v
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def _cas(k, ttl, &blk)
|
25
|
+
if _valid_ttl?(ttl)
|
26
|
+
thread_metal.cas k, ttl, &blk
|
27
|
+
else
|
28
|
+
thread_metal.cas k, &blk
|
29
|
+
end
|
30
|
+
rescue ::Memcached::NotFound
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def _delete(k)
|
35
|
+
thread_metal.delete k
|
36
|
+
rescue ::Memcached::NotFound
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def _flush
|
41
|
+
thread_metal.flush
|
42
|
+
end
|
43
|
+
|
44
|
+
def _exist?(k)
|
45
|
+
thread_metal.get k
|
46
|
+
true
|
47
|
+
rescue ::Memcached::NotFound
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def _stats
|
52
|
+
thread_metal.stats
|
53
|
+
end
|
54
|
+
end
|