cache_bar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Warren Konkel
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,20 @@
1
+ Preface
2
+ =======
3
+
4
+ This is a project that is currently used in production environments but isn't "production ready" unless
5
+ you're adventurous. Consider this as a "sneak peak" rather than a "first release".
6
+
7
+
8
+ CacheBar
9
+ ========
10
+
11
+ CacheBar is a pure ruby implementation of memcached client protocol. Several things to note:
12
+
13
+ 1. unlike default memcached behavior, incr/decr will automatically and safely create the underlying
14
+ key if it doesn't already exist. this saves you a step of creating the key if it doesn't exist.
15
+ 2. gzip is supported by passing in :gzip => true into options of any setting function.
16
+ 3. strings and integers are stored natively in memcached (allowing incr/decr/prepend/append to
17
+ work). everything else is serialized using ruby Marshal dump/load.
18
+ 4. default ttl is 0, meaning never expire
19
+
20
+ Copyright (c) 2009 Warren Konkel, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the cache_bar plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the cache_bar plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'CacheBar'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * socket timeouts
2
+ * blow up on memcached 1.2.2
3
+ * auto mix into activerecord and figure out how to not clash with ActiveRecord::Base.cache (query caching)
data/cache_bar.gemspec ADDED
@@ -0,0 +1,13 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "cache_bar"
3
+ s.version = "0.0.1"
4
+ s.date = "2009-10-06"
5
+ s.summary = "A pure ruby memcached client."
6
+ s.email = "wkonkel@gmail.com"
7
+ s.homepage = "http://github.com/wkonkel/cache_bar"
8
+ s.description = "A pure ruby memcached client."
9
+ s.has_rdoc = false
10
+ s.authors = ["Warren Konkel"]
11
+ s.files = Dir.glob('**/*') - Dir.glob('test/*.rb')
12
+ s.test_files = Dir.glob('test/*.rb')
13
+ end
data/init.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'cache_bar'
2
+ require 'cache_bar/cache_store'
3
+ require 'acts_as_cached'
4
+
5
+ config.cache_store = CacheBar::CacheStore.new
6
+ config.after_initialize do
7
+ ActionView::Base.send(:acts_as_cached)
8
+ ActionController::Base.send(:acts_as_cached)
9
+ #ActiveRecord::Base.send(:acts_as_cached)
10
+ end
@@ -0,0 +1,35 @@
1
+ Object.class_eval do
2
+ def self.acts_as_cached(options={})
3
+ self.class_inheritable_hash :acts_as_cached_options
4
+ self.acts_as_cached_options = options
5
+
6
+ class_eval do
7
+ def cache(*params, &block)
8
+ params.push({}) unless params.last.is_a?(Hash)
9
+ params.last.merge!(:erb => self) if defined?(ActionView::Base) && self.is_a?(ActionView::Base)
10
+ self.class.cache(*params, &block)
11
+ end
12
+
13
+ def self.cache(key=nil, options={}, &block)
14
+ proxy_object = CacheBar.pool.with_options(:namespace => self.name)
15
+ if key && block
16
+ begin
17
+ value = proxy_object.get(key, options)
18
+ rescue CacheBar::NotFound
19
+ value = options[:erb] ? options[:erb].capture(&block) : block.call
20
+ proxy_object.set(key, value, options)
21
+ end
22
+
23
+ options[:erb] ? options[:erb].concat(value) && nil : value
24
+ else
25
+ proxy_object
26
+ end
27
+ end
28
+
29
+ # def self.method_missing_with_cache_bar(method, *params, &block)
30
+ # # find
31
+ # end
32
+ # alias_method_chain :method_missing, :cache_bar
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ class CacheBar::CacheStore < ActiveSupport::Cache::Store
2
+ def read(key, options = {})
3
+ log("read", key, options)
4
+ CacheBar.pool.get(key)
5
+ end
6
+
7
+ def write(key, options = {})
8
+ log("write", key, options)
9
+ CacheBar.pool.set(key, options.merge(:ttl => options.delete(:expires_in)))
10
+ end
11
+
12
+ def delete(key, options = {})
13
+ log("delete", key, options)
14
+ CacheBar.pool.delete(key)
15
+ end
16
+
17
+ def delete_matched(key, options = {})
18
+ super
19
+ raise "Not supported"
20
+ end
21
+
22
+ def exist?(key, options = {})
23
+ log("exist?", key, options)
24
+ !read(key, options).nil?
25
+ end
26
+
27
+ def increment(key, amount = 1)
28
+ log("incrementing", key, amount)
29
+ CacheBar.pool.incr(key, amount)
30
+ end
31
+
32
+ def decrement(key, amount = 1)
33
+ log("decrementing", key, amount)
34
+ CacheBar.pool.decr(key, amount)
35
+ end
36
+ end
data/lib/cache_bar.rb ADDED
@@ -0,0 +1,281 @@
1
+ require 'digest/md5'
2
+ require 'socket'
3
+ require 'zlib'
4
+ require 'timeout'
5
+
6
+ class CacheBar
7
+ class NotFound < StandardError; end
8
+ class NotStored < StandardError; end
9
+ class ConnectionError < StandardError; end
10
+
11
+ def self.pool(name=nil)
12
+ name = name ? name.to_sym : :default
13
+ (@pools ||= {})[name] ||= begin
14
+ @config ||= begin
15
+ yaml = YAML.load(File.read(File.join(RAILS_ROOT, 'config', 'memcached.yml')))
16
+ (yaml['defaults'] || {}).merge(yaml[RAILS_ENV] || {})
17
+ end
18
+
19
+ if name == :default
20
+ new(@config.symbolize_keys)
21
+ else
22
+ raise "CacheBar Pool not found (#{name})" unless @config.has_key?(name.to_s) && @config[name.to_s].is_a?(Hash)
23
+ (@pools ||= {})[name.to_sym] ||= new(@config[name.to_s].symbolize_keys)
24
+ end
25
+ end
26
+ end
27
+
28
+ def initialize(options={})
29
+ @options = {
30
+ :namespace => 'default',
31
+ :gzip => false,
32
+ :ttl => 0,
33
+ :servers => '0.0.0.0'
34
+ }.merge(options)
35
+ @options[:servers] = [@options[:servers]].flatten
36
+ end
37
+
38
+ def set(key, value, options={})
39
+ generic_set(:set, key, value, options)
40
+ end
41
+
42
+ def add(key, value, options={})
43
+ generic_set(:add, key, value, options)
44
+ end
45
+
46
+ def replace(key, value, options={})
47
+ generic_set(:replace, key, value, options)
48
+ end
49
+
50
+ def append(key, value, options={})
51
+ generic_set(:append, key, value, options)
52
+ end
53
+
54
+ def prepend(key, value, options={})
55
+ generic_set(:prepend, key, value, options)
56
+ end
57
+
58
+ def cas(key, value, cas, options={})
59
+ generic_set(:cas, key, value, options.merge(:cas => cas))
60
+ end
61
+
62
+ def incr(key, value=1, options={})
63
+ generic_incr_or_decr(:incr, key, value, options)
64
+ end
65
+
66
+ def decr(key, value=1, options={})
67
+ generic_incr_or_decr(:decr, key, value, options)
68
+ end
69
+
70
+ def get(key, options={})
71
+ generic_get(:get, [key], options).first[:value]
72
+ end
73
+
74
+ def gets(key, options={})
75
+ results = generic_get(:gets, [key], options)
76
+ [results.first[:value], results.first[:cas]]
77
+ end
78
+
79
+ def get_multi(*keys)
80
+ options = keys.last.is_a?(Hash) ? keys.pop : {}
81
+ generic_get(:get, keys, options).inject({}) { |hash,results| hash[results[:key]] = results[:value]; hash }
82
+ end
83
+
84
+ def gets_multi(*keys)
85
+ options = keys.last.is_a?(Hash) ? keys.pop : {}
86
+ generic_get(:gets, keys, options).inject({}) { |hash,results| hash[results[:key]] = [results[:value], results[:cas]]; hash }
87
+ end
88
+
89
+ def delete(key, options={})
90
+ generic_key_command(:delete, key, nil, options)
91
+ true
92
+ end
93
+
94
+ def flush_all
95
+ all_servers.inject({}) do |hash,(server,server_id)|
96
+ socket_for_server_id(server_id) do |socket|
97
+ socket.write("flush_all\r\n")
98
+ hash[server] = (socket.gets.strip == 'OK')
99
+ end
100
+ hash
101
+ end
102
+ end
103
+
104
+ def stats
105
+ all_servers.inject({}) do |hash,(server,server_id)|
106
+ socket_for_server_id(server_id) do |socket|
107
+ socket.write("stats\r\n")
108
+ while true do
109
+ result, key, value = socket.gets.split(' ')
110
+ break if result == 'END'
111
+ (hash[server] ||= {})[key.to_sym] = value
112
+ end
113
+ end
114
+ hash
115
+ end
116
+ end
117
+
118
+ def [](key)
119
+ get(key)
120
+ rescue NotFound
121
+ nil
122
+ end
123
+
124
+ def []=(key, value)
125
+ set(key, value)
126
+ end
127
+
128
+ # get or set... a = cache.gos('a') { expensive_function_here() }
129
+ def gos(key, options={}, &block)
130
+ get(key, options)
131
+ rescue NotFound
132
+ set(key, block.call, options)
133
+ end
134
+
135
+ def with_options(options={})
136
+ (proxy = Object.new).instance_eval %(
137
+ def [](key)
138
+ @cache.get(key, @options)
139
+ rescue NotFound
140
+ nil
141
+ end
142
+
143
+ def []=(key, value)
144
+ @cache.set(key, value, @options)
145
+ end
146
+
147
+ def method_missing(method, *params, &block)
148
+ params.push({}) unless params.last.is_a?(Hash)
149
+ params.last.merge!(@options)
150
+ @cache.send(method, *params, &block)
151
+ end
152
+ )
153
+ proxy.instance_variable_set('@cache', self)
154
+ proxy.instance_variable_set('@options', options)
155
+ proxy
156
+ end
157
+
158
+ protected
159
+
160
+ FLAG_INTEGER = 0x001
161
+ FLAG_MARSHAL = 0x010
162
+ FLAG_GZIP = 0x100
163
+
164
+ def namespace(key, options)
165
+ Digest::MD5.hexdigest("#{@options[:namespace]}:#{"#{options[:namespace]}:" if options[:namespace]}#{key}")
166
+ end
167
+
168
+ def generic_incr_or_decr(command, key, value, options)
169
+ generic_key_command(command, key, value, options).to_i
170
+ rescue NotFound
171
+ begin
172
+ add(key, 0, options)
173
+ rescue NotStored
174
+ # this is fine... race condition, somebody else added the key already
175
+ end
176
+ generic_key_command(command, key, value, options).to_i
177
+ end
178
+
179
+ def generic_key_command(command, key, data, options)
180
+ key = namespace(key, options)
181
+ results = socket_for_server_id(server_id_for_key(key)) do |socket|
182
+ socket.write("#{command} #{key} #{data}\r\n")
183
+ socket.gets.strip
184
+ end
185
+
186
+ case results
187
+ when "NOT_STORED", "EXISTS" then raise NotStored
188
+ when "NOT_FOUND" then raise NotFound
189
+ else results
190
+ end
191
+ end
192
+
193
+ def generic_set(command, key, value, options)
194
+ flags = 0
195
+ if value.is_a?(Integer)
196
+ data = value.to_s
197
+ flags |= FLAG_INTEGER
198
+ else
199
+ if value.is_a?(String)
200
+ data = value
201
+ else
202
+ data = Marshal.dump(value)
203
+ flags |= FLAG_MARSHAL
204
+ end
205
+
206
+ if (options.has_key?(:gzip) && options[:gzip]) || @options[:gzip]
207
+ data = Zlib::Deflate.deflate(data)
208
+ flags |= FLAG_GZIP
209
+ end
210
+ end
211
+
212
+ generic_key_command(command, key, "#{flags} #{options[:ttl] || @options[:ttl]} #{data.length} #{options[:cas]}\r\n#{data}", options)
213
+ value
214
+ end
215
+
216
+ def generic_get(command, keys, options)
217
+ md5_keys = keys.inject({}) { |hash, key| hash[namespace(key, options)] = key; hash }
218
+ server_keys = md5_keys.keys.inject({}) { |hash,key| (hash[server_id_for_key(key)] ||= []) << key; hash }
219
+ results = server_keys.inject([]) do |array, (server_id, keys)|
220
+ socket_for_server_id(server_id) do |socket|
221
+ socket.write("#{command} #{keys.join(' ')}\r\n")
222
+
223
+ while true
224
+ raise ConnectionError unless line = socket.gets
225
+ result, key, flag, length, cas = line.split(' ')
226
+ break if result == 'END'
227
+
228
+ raise ConnectionError unless value = socket.read(length.to_i)
229
+ raise ConnectionError unless socket.read(2)
230
+
231
+ value = value.to_i if flag.to_i & FLAG_INTEGER > 0
232
+ value = Zlib::Inflate.inflate(value) if flag.to_i & FLAG_GZIP > 0
233
+ begin
234
+ value = Marshal.load(value) if flag.to_i & FLAG_MARSHAL > 0
235
+ rescue ArgumentError => e
236
+ if e.message.match("undefined class/module (.*)")
237
+ $1.split('::').reject { |n| n.empty? }.inject(Object) do |constant,name|
238
+ constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
239
+ end
240
+ retry
241
+ else
242
+ raise
243
+ end
244
+ end
245
+
246
+ array << { :value => value, :key => md5_keys[key], :cas => cas.to_i }
247
+ end
248
+ end
249
+ array
250
+ end
251
+
252
+ if keys.length == 1 && results.length == 0
253
+ raise NotFound
254
+ else
255
+ results
256
+ end
257
+ end
258
+
259
+ def server_id_for_key(key)
260
+ key.hex % all_servers.length
261
+ end
262
+
263
+ def socket_for_server_id(server_id, &block)
264
+ timeout(1) do
265
+ (@sockets ||= {})[server_id] ||= begin
266
+ host, port = @options[:servers][server_id].split(':')
267
+ TCPSocket.new(host, port ? port.to_i : 11211)
268
+ end
269
+ block.call(@sockets[server_id])
270
+ end
271
+ rescue ConnectionError, Errno::EACCES, Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Timeout::Error
272
+ @sockets.delete(server_id)
273
+ raise ConnectionError
274
+ end
275
+
276
+ def all_servers
277
+ @options[:servers].inject([{},0]) { |(hash,index),server| hash[server] = index; [hash, index+1] }.first
278
+ end
279
+
280
+ end
281
+
@@ -0,0 +1,30 @@
1
+ development:
2
+ namespace: default
3
+ gzip: false
4
+ ttl: 0
5
+ servers:
6
+ - 0.0.0.0
7
+
8
+ test:
9
+ namespace: default
10
+ gzip: false
11
+ ttl: 0
12
+ servers:
13
+ - 0.0.0.0
14
+
15
+ production:
16
+ namespace: default
17
+ gzip: false
18
+ ttl: 0
19
+ servers:
20
+ - 0.0.0.0:11211
21
+ - 0.0.0.0:11212
22
+
23
+ secondary:
24
+ namespace: default
25
+ gzip: false
26
+ ttl: 0
27
+ servers:
28
+ - 0.0.0.0:11213
29
+ - 0.0.0.0:11214
30
+
@@ -0,0 +1,25 @@
1
+ require 'test/unit'
2
+ require File.join(File.dirname(__FILE__), '../lib/cache_bar')
3
+ require File.join(File.dirname(__FILE__), '../lib/acts_as_cached')
4
+ require 'erb'
5
+
6
+ Object.send(:acts_as_cached)
7
+
8
+ class ActsAsCachedTest < Test::Unit::TestCase
9
+
10
+ def setup
11
+ CacheBar.singleton = CacheBar.new(:servers => '0.0.0.0', :namespace => Time.now.to_f)
12
+ end
13
+
14
+ def test_basic
15
+ assert_raises(CacheBar::NotFound) { Integer.cache.get('a') }
16
+ assert_equal Integer.cache.set('a', 'b'), 'b'
17
+ assert_equal Integer.cache.get('a'), 'b'
18
+ assert_raises(CacheBar::NotFound) { Hash.cache.get('a') }
19
+ end
20
+
21
+ # def test_erb
22
+ # assert_equal ERB.new("test<% cache('test') do %>test2<% end %>").result, 'testtest2'
23
+ # end
24
+
25
+ end
@@ -0,0 +1,20 @@
1
+ require 'benchmark'
2
+
3
+ require File.join(File.dirname(__FILE__), '../lib/cache_bar')
4
+ c = CacheBar.new(:servers => '0.0.0.0')
5
+
6
+ 5.times do
7
+ puts 'set: ' + Benchmark.realtime {
8
+ 10000.times do |i|
9
+ c.set('test', 'testing 123')
10
+ end
11
+ }.to_s
12
+ end
13
+
14
+ 5.times do
15
+ puts 'get: ' + Benchmark.realtime {
16
+ 10000.times do |i|
17
+ c.get('test')
18
+ end
19
+ }.to_s
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'benchmark'
2
+
3
+ require 'rubygems'
4
+ gem 'memcached'
5
+ require 'memcached'
6
+ c = Memcached.new('0.0.0.0')
7
+
8
+ 5.times do
9
+ puts 'set: ' + Benchmark.realtime {
10
+ 10000.times do |i|
11
+ c.set('test', 'testing 123')
12
+ end
13
+ }.to_s
14
+ end
15
+
16
+ 5.times do
17
+ puts 'get: ' + Benchmark.realtime {
18
+ 10000.times do |i|
19
+ c.get('test')
20
+ end
21
+ }.to_s
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'benchmark'
2
+
3
+ require 'rubygems'
4
+ gem 'memcache-client'
5
+ require 'memcache'
6
+ c = MemCache.new('0.0.0.0')
7
+
8
+ 5.times do
9
+ puts 'set: ' + Benchmark.realtime {
10
+ 10000.times do |i|
11
+ c.set('test', 'testing 123')
12
+ end
13
+ }.to_s
14
+ end
15
+
16
+ 5.times do
17
+ puts 'get: ' + Benchmark.realtime {
18
+ 10000.times do |i|
19
+ c.get('test')
20
+ end
21
+ }.to_s
22
+ end
@@ -0,0 +1,153 @@
1
+ require 'test/unit'
2
+ require File.join(File.dirname(__FILE__), '../lib/cache_bar')
3
+
4
+ class CacheBarTest < Test::Unit::TestCase
5
+
6
+ def test_server_no_port
7
+ assert_get_set_delete(CacheBar.new(:servers => '0.0.0.0', :namespace => Time.now.to_f))
8
+ end
9
+
10
+ def test_server_with_port
11
+ assert_get_set_delete(CacheBar.new(:servers => '0.0.0.0:11211', :namespace => Time.now.to_f))
12
+ end
13
+
14
+ def test_multiple_servers
15
+ assert_get_set_delete(CacheBar.new(:servers => ['0.0.0.0', '0.0.0.0'], :namespace => Time.now.to_f))
16
+ end
17
+
18
+ def test_bogus_server
19
+ client = CacheBar.new(:servers => '255.255.255.255', :namespace => Time.now.to_f)
20
+ assert_raises(CacheBar::ConnectionError) { client.get('test') }
21
+ assert_raises(CacheBar::ConnectionError) { client.set('test', 'test') }
22
+ assert_raises(CacheBar::ConnectionError) { client.add('test', 'test') }
23
+ assert_raises(CacheBar::ConnectionError) { client.delete('add') }
24
+ end
25
+
26
+ def test_simple_get_and_set_with_brackets
27
+ with_default_cache do |cache|
28
+ assert_nil cache['test']
29
+ assert_equal 'this is a test', (cache['test'] = 'this is a test')
30
+ assert_equal 'this is a test', cache['test']
31
+ end
32
+ end
33
+
34
+ def test_encoding_and_marshaling
35
+ with_default_cache do |cache|
36
+ assert_get_set_delete(cache, nil)
37
+ assert_get_set_delete(cache, true)
38
+ assert_get_set_delete(cache, false)
39
+ assert_get_set_delete(cache, 5)
40
+ assert_get_set_delete(cache, "5")
41
+ assert_get_set_delete(cache, 'test string')
42
+ assert_get_set_delete(cache, 'uʍop ǝpısdn ǝɹɐ ʇɐɥʇ sƃuıɥʇ')
43
+ assert_get_set_delete(cache, [1,2,3,[4,5]])
44
+ assert_get_set_delete(cache, { :a => 1, 'b' => 2 })
45
+ end
46
+ end
47
+
48
+ def test_delete_missing_key
49
+ with_default_cache do |cache|
50
+ assert_raises(CacheBar::NotFound) { cache.delete('test') }
51
+ end
52
+ end
53
+
54
+ def test_add_raises
55
+ with_default_cache do |cache|
56
+ assert_equal 'test string', cache.add('test', 'test string')
57
+ assert_equal 'test string', cache.get('test')
58
+ assert_raises(CacheBar::NotStored) { cache.add('test', 'test string') }
59
+ end
60
+ end
61
+
62
+ def test_not_founds_raises
63
+ with_default_cache do |cache|
64
+ assert_raises(CacheBar::NotFound) { cache.get('test') }
65
+ assert_raises(CacheBar::NotFound) { cache.gets('test') }
66
+ end
67
+ end
68
+
69
+ def test_gets_and_cas
70
+ with_default_cache do |cache|
71
+ assert_equal 'test string', cache.add('test', 'test string')
72
+ value, cas = cache.gets('test')
73
+ assert 'test string', value
74
+
75
+ assert_equal 'test string2', cache.set('test', 'test string2')
76
+ value, cas2 = cache.gets('test')
77
+ assert 'test string2', value
78
+
79
+ assert cas2 > cas
80
+
81
+ assert_raises(CacheBar::NotStored) { cache.cas('test', 'test string 3', cas) }
82
+ assert_equal 'test string 3', cache.cas('test', 'test string 3', cas2)
83
+ end
84
+ end
85
+
86
+ def test_append_and_prepend
87
+ with_default_cache do |cache|
88
+ cache.set('test', 'aaa')
89
+ cache.append('test', 'bbb')
90
+ cache.prepend('test', 'ccc')
91
+ assert_equal 'cccaaabbb', cache.get('test')
92
+ end
93
+ end
94
+
95
+ def test_replace
96
+ with_default_cache do |cache|
97
+ assert_raises(CacheBar::NotStored) { cache.replace('test', 'aaa') }
98
+ assert_equal cache.set('test', 'bbb'), 'bbb'
99
+ assert_equal cache.replace('test', 'ccc'), 'ccc'
100
+ assert_equal cache.get('test', 'ccc'), 'ccc'
101
+ end
102
+ end
103
+
104
+ def test_get_or_set
105
+ with_default_cache do |cache|
106
+ assert_equal 'test', cache.gos('test') { 'test' }
107
+ assert_equal 'test', cache.get('test')
108
+ cache.set('test', 'test2')
109
+ assert_equal 'test2', cache.gos('test') { 'test' }
110
+ end
111
+ end
112
+
113
+ def test_get_multi
114
+ with_default_cache do |cache|
115
+ assert_equal 'test1', cache.set('test1', 'test1')
116
+ assert_equal 'test2', cache.set('test2', 'test2')
117
+ results = cache.get_multi('test1', 'test2')
118
+ assert_equal results['test1'], 'test1'
119
+ assert_equal results['test2'], 'test2'
120
+ end
121
+ end
122
+
123
+ def test_ttl
124
+ with_default_cache do |cache|
125
+ cache.set('test', 'test', :ttl => 1)
126
+ assert_equal 'test', cache.get('test')
127
+ sleep(1.1)
128
+ assert_raises(CacheBar::NotFound) { cache.get('test') }
129
+ end
130
+ end
131
+
132
+ def test_stats
133
+ with_default_cache do |cache|
134
+ assert cache.stats['0.0.0.0'][:curr_connections]
135
+ end
136
+ end
137
+
138
+ protected
139
+
140
+ def assert_get_set_delete(cache, value='test string')
141
+ assert_raises(CacheBar::NotFound) { cache.get('test') }
142
+ assert_equal value, cache.set('test', value)
143
+ assert_equal value, cache.get('test')
144
+ assert_equal true, cache.delete('test')
145
+ assert_raises(CacheBar::NotFound) { cache.get('test') }
146
+ end
147
+
148
+ def with_default_cache
149
+ yield CacheBar.new(:servers => '0.0.0.0', :namespace => Time.now.to_f)
150
+ end
151
+
152
+ end
153
+
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cache_bar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Warren Konkel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-06 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A pure ruby memcached client.
17
+ email: wkonkel@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - cache_bar.gemspec
26
+ - init.rb
27
+ - lib/acts_as_cached.rb
28
+ - lib/cache_bar/cache_store.rb
29
+ - lib/cache_bar.rb
30
+ - memcached.yml.sample
31
+ - MIT-LICENSE
32
+ - Rakefile
33
+ - README
34
+ - TODO
35
+ has_rdoc: true
36
+ homepage: http://github.com/wkonkel/cache_bar
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: A pure ruby memcached client.
63
+ test_files:
64
+ - test/acts_as_cached_test.rb
65
+ - test/benchmark-cachebar.rb
66
+ - test/benchmark-libmemcached.rb
67
+ - test/benchmark-memcache-client.rb
68
+ - test/cache_bar_test.rb