mcmire-cache 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +7 -0
  2. data/CHANGELOG +21 -0
  3. data/Gemfile +22 -0
  4. data/README.md +229 -0
  5. data/Rakefile +41 -0
  6. data/benchmarks/afterrefactor.txt +86 -0
  7. data/benchmarks/midrefactor.txt +89 -0
  8. data/benchmarks/v0.0.2.txt +95 -0
  9. data/benchmarks/v0.0.3.txt +96 -0
  10. data/benchmarks/v0.1.2.txt +94 -0
  11. data/benchmarks/v0.2.1.txt +94 -0
  12. data/benchmarks/v0.2.2.txt +94 -0
  13. data/lib/cache.rb +233 -0
  14. data/lib/cache/active_support_cache_dalli_store.rb +15 -0
  15. data/lib/cache/active_support_cache_file_store.rb +11 -0
  16. data/lib/cache/active_support_cache_memory_store.rb +11 -0
  17. data/lib/cache/active_support_cache_store.rb +37 -0
  18. data/lib/cache/config.rb +27 -0
  19. data/lib/cache/dalli_client.rb +54 -0
  20. data/lib/cache/mem_cache.rb +46 -0
  21. data/lib/cache/memcached.rb +54 -0
  22. data/lib/cache/memcached_rails.rb +34 -0
  23. data/lib/cache/nothing.rb +20 -0
  24. data/lib/cache/redis.rb +44 -0
  25. data/lib/cache/redis_namespace.rb +7 -0
  26. data/lib/cache/version.rb +3 -0
  27. data/mcmire-cache.gemspec +22 -0
  28. data/test/helper.rb +29 -0
  29. data/test/profile/benchmark.rb +258 -0
  30. data/test/shared_tests.rb +169 -0
  31. data/test/test_active_support_cache_dalli_store.rb +77 -0
  32. data/test/test_active_support_cache_file_store.rb +19 -0
  33. data/test/test_active_support_cache_memory_store.rb +12 -0
  34. data/test/test_dalli_client.rb +70 -0
  35. data/test/test_mem_cache.rb +64 -0
  36. data/test/test_memcached.rb +64 -0
  37. data/test/test_memcached_rails.rb +61 -0
  38. data/test/test_memcached_with_binary.rb +17 -0
  39. data/test/test_missing_driver.rb +16 -0
  40. data/test/test_nothing.rb +147 -0
  41. data/test/test_redis.rb +60 -0
  42. data/test/test_redis_namespace.rb +64 -0
  43. metadata +108 -0
@@ -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,11 @@
1
+ require 'cache/active_support_cache_store'
2
+
3
+ module Cache::ActiveSupportCacheFileStore
4
+ def self.extended(base)
5
+ base.extend Cache::ActiveSupportCacheStore
6
+ end
7
+
8
+ def _stats
9
+ {}
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'cache/active_support_cache_store'
2
+
3
+ module Cache::ActiveSupportCacheMemoryStore
4
+ def self.extended(base)
5
+ base.extend Cache::ActiveSupportCacheStore
6
+ end
7
+
8
+ def _stats
9
+ {}
10
+ end
11
+ 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
@@ -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