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.
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