cache_bar 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/cache_bar.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cache_bar"
3
- s.version = "0.0.1"
3
+ s.version = "0.0.2"
4
4
  s.date = "2009-10-06"
5
5
  s.summary = "A pure ruby memcached client."
6
6
  s.email = "wkonkel@gmail.com"
data/init.rb CHANGED
@@ -1,10 +1 @@
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
1
+ require 'cache_bar'
@@ -33,3 +33,7 @@ Object.class_eval do
33
33
  end
34
34
  end
35
35
  end
36
+
37
+ # hooks into rails
38
+ ActionView::Base.send(:acts_as_cached)
39
+ ActionController::Base.send(:acts_as_cached)
@@ -33,4 +33,6 @@ class CacheBar::CacheStore < ActiveSupport::Cache::Store
33
33
  log("decrementing", key, amount)
34
34
  CacheBar.pool.decr(key, amount)
35
35
  end
36
- end
36
+ end
37
+
38
+ silence_warnings { Object.const_set "RAILS_CACHE", CacheBar::CacheStore.new }
@@ -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
+
data/lib/cache_bar.rb CHANGED
@@ -1,281 +1,3 @@
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
-
1
+ require 'cache_bar/ruby_client'
2
+ require 'cache_bar/acts_as_cached'
3
+ require 'cache_bar/cache_store'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cache_bar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Warren Konkel
@@ -22,10 +22,12 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
 
24
24
  files:
25
+ - cache_bar-0.0.2.gem
25
26
  - cache_bar.gemspec
26
27
  - init.rb
27
- - lib/acts_as_cached.rb
28
+ - lib/cache_bar/acts_as_cached.rb
28
29
  - lib/cache_bar/cache_store.rb
30
+ - lib/cache_bar/ruby_client.rb
29
31
  - lib/cache_bar.rb
30
32
  - memcached.yml.sample
31
33
  - MIT-LICENSE