cache_bar 0.0.1

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