cache_bar 0.0.1 → 0.0.2

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