benhutton-libmemcached_store 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ *.swp
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 37signals
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,51 @@
1
+ = LibmemcachedStore
2
+
3
+ An ActiveSupport cache store that uses the C-based libmemcached client through
4
+ Evan Weaver's Ruby/SWIG wrapper, memcached. libmemcached is fast, lightweight,
5
+ and supports consistent hashing, non-blocking IO, and graceful server failover.
6
+
7
+ == Prerequisites
8
+
9
+ You'll need both the libmemcached client and the memcached gem:
10
+
11
+ * http://tangent.org/552/libmemcached.html
12
+ * http://blog.evanweaver.com/files/doc/fauna/memcached
13
+
14
+ Make sure you install libmemcached first, before you try installing the gem. If
15
+ you're using OS X, the easiest way to install libmemcached is through MacPorts:
16
+
17
+ sudo port install libmemcached
18
+
19
+ For other platforms, download and extract the libmemcached tarball and install
20
+ manually:
21
+
22
+ ./configure
23
+ make && sudo make install
24
+
25
+ Once libmemcached is installed, install the memcached gem:
26
+
27
+ gem install memcached --no-rdoc --no-ri
28
+
29
+ == Usage
30
+
31
+ This is a drop-in replacement for the memcache store that ships with Rails. To
32
+ enable, set the <tt>config.cache_store</tt> option to <tt>:libmemcached_store</tt>
33
+ in the config for your environment
34
+
35
+ config.cache_store = :libmemcached_store
36
+
37
+ If no servers are specified, localhost is assumed. You can specify a list of
38
+ server addresses, either as hostnames or IP addresses, with or without a port
39
+ designation. If no port is given, 11211 is assumed:
40
+
41
+ config.cache_store = :libmemcached_store, %w(cache-01 cache-02 127.0.0.1:11212)
42
+
43
+ You can also use <tt>:libmemcached_store</tt> with <tt>config.session_store</tt>
44
+
45
+ config.session_store = :libmemcached_store
46
+
47
+ == Props
48
+
49
+ Thanks to Brian Aker (http://tangent.org) for creating libmemcached, and Evan
50
+ Weaver (http://blog.evanweaver.com) for the Ruby wrapper.
51
+
data/Rakefile ADDED
@@ -0,0 +1,22 @@
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 libmemcached_store plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the libmemcached_store plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'LibmemcachedStore'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
@@ -0,0 +1,56 @@
1
+ begin
2
+ module ActionDispatch
3
+ module Session
4
+ class LibmemcachedStore < AbstractStore
5
+ require 'memcached'
6
+
7
+ def initialize(app, options = {})
8
+
9
+ super
10
+
11
+ options[:expire_after] ||= options[:expires]
12
+ @default_options = {
13
+ :prefix_key => 'rack:session',
14
+ :memcache_server => 'localhost:11211'
15
+ }.merge(@default_options)
16
+
17
+ @pool = options[:cache] || Memcached.new(@default_options[:memcache_server], @default_options)
18
+ @mutex = Mutex.new
19
+
20
+ super
21
+ end
22
+
23
+ private
24
+ def get_session(env, sid)
25
+ sid ||= generate_sid
26
+ begin
27
+ session = @pool.get(sid) || {}
28
+ rescue Memcached::NotFound
29
+ session = {}
30
+ rescue Memcached::Error => e
31
+ log_error(e)
32
+ session = {}
33
+ end
34
+ [sid, session]
35
+ end
36
+
37
+ def set_session(env, sid, session_data)
38
+ options = env['rack.session.options']
39
+ expiry = options[:expire_after] || 0
40
+ @pool.set(sid, session_data, expiry)
41
+ return sid
42
+ rescue Memcached::Error => e
43
+ log_error(e)
44
+ return false
45
+ end
46
+
47
+ def log_error(exception)
48
+ logger ||= RAILS_DEFAULT_LOGGER
49
+ logger.error "MemcachedError (#{exception.inspect}): #{exception.message}" if logger && !@logger_off
50
+ end
51
+ end
52
+ end
53
+ end
54
+ rescue LoadError
55
+ # MemCache wasn't available so neither can the store be
56
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveSupport
2
+ module Cache
3
+ class CompressedLibmemcachedStore < LibmemcachedStore
4
+ def read(name, options = {})
5
+ if value = super(name, (options || {}).merge(:raw => true))
6
+ Marshal.load(ActiveSupport::Gzip.decompress(value))
7
+ end
8
+ end
9
+
10
+ def write(name, value, options = {})
11
+ super(name, ActiveSupport::Gzip.compress(Marshal.dump(value)), (options || {}).merge(:raw => true))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,121 @@
1
+ require 'memcached'
2
+
3
+ class Memcached
4
+ # The latest version of memcached (0.11) doesn't support hostnames with dashes
5
+ # in their names, so we overwrite it here to be more lenient.
6
+ def set_servers(servers)
7
+ [*servers].each_with_index do |server, index|
8
+ host, port = server.split(":")
9
+ Lib.memcached_server_add(@struct, host, port.to_i)
10
+ end
11
+ end
12
+ end
13
+
14
+ class ActiveSupport::Cache::Entry
15
+ # In 3.0 all values returned from Rails.cache.read are frozen.
16
+ # This makes sense for an in-memory store storing object references,
17
+ # but for a marshalled store we should be able to modify things.
18
+ def value_with_dup
19
+ result = value_without_dup
20
+ result.duplicable? ? result.dup : result
21
+ end
22
+ alias_method_chain :value, :dup
23
+ end
24
+
25
+ module ActiveSupport
26
+ module Cache
27
+ class LibmemcachedStore < Store
28
+ attr_reader :addresses
29
+
30
+ DEFAULT_OPTIONS = {
31
+ :distribution => :consistent_ketama,
32
+ :binary_protocol => true
33
+ }
34
+
35
+ def initialize(*addresses)
36
+ addresses.flatten!
37
+ @options = addresses.extract_options!
38
+ addresses = %w(localhost) if addresses.empty?
39
+
40
+ @addresses = addresses
41
+ @cache = Memcached.new(@addresses, @options.reverse_merge(DEFAULT_OPTIONS))
42
+ end
43
+
44
+ def increment(key, amount=1)
45
+ log 'incrementing', key, amount
46
+ @cache.incr(key, amount)
47
+ rescue Memcached::Error
48
+ nil
49
+ end
50
+
51
+ def decrement(key, amount=1)
52
+ log 'decrementing', key, amount
53
+ @cache.decr(key, amount)
54
+ rescue Memcached::Error
55
+ nil
56
+ end
57
+
58
+ def clear
59
+ @cache.flush
60
+ end
61
+
62
+ def stats
63
+ @cache.stats
64
+ end
65
+
66
+ protected
67
+
68
+ def read_entry(key, options = nil)
69
+ deserialize_entry(@cache.get(key, false))
70
+ rescue Memcached::NotFound
71
+ nil
72
+ rescue Memcached::Error => e
73
+ log_error(e)
74
+ nil
75
+ end
76
+
77
+ # Set the key to the given value. Pass :unless_exist => true if you want to
78
+ # skip setting a key that already exists.
79
+ def write_entry(key, entry, options = nil)
80
+ method = (options && options[:unless_exist]) ? :add : :set
81
+ value = options[:raw] ? entry.value.to_s : entry
82
+
83
+ @cache.send(method, key, value, expires_in(options), marshal?(options))
84
+ true
85
+ rescue Memcached::Error => e
86
+ log_error(e)
87
+ false
88
+ end
89
+
90
+ def delete_entry(key, options = nil)
91
+ @cache.delete(key)
92
+ true
93
+ rescue Memcached::Error => e
94
+ log_error(e)
95
+ false
96
+ end
97
+
98
+ private
99
+ def deserialize_entry(raw_value)
100
+ if raw_value
101
+ entry = Marshal.load(raw_value) rescue raw_value
102
+ entry.is_a?(Entry) ? entry : Entry.new(entry)
103
+ else
104
+ nil
105
+ end
106
+ end
107
+
108
+ def expires_in(options)
109
+ (options || {})[:expires_in] || 0
110
+ end
111
+
112
+ def marshal?(options)
113
+ !(options || {})[:raw]
114
+ end
115
+
116
+ def log_error(exception)
117
+ logger.error "MemcachedError (#{exception.inspect}): #{exception.message}" if logger && !@logger_off
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,3 @@
1
+ require 'active_support/cache/libmemcached_store'
2
+ require 'active_support/cache/compressed_libmemcached_store'
3
+ require 'action_dispatch/session/libmemcached_store'
@@ -0,0 +1,54 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{benhutton-libmemcached_store}
8
+ s.version = "0.3.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ben Hutton", "Jeffrey Hardy"]
12
+ s.date = %q{2010-08-24}
13
+ s.description = %q{An ActiveSupport cache store that uses the C-based libmemcached client through
14
+ Evan Weaver's Ruby/SWIG wrapper, memcached. libmemcached is fast, lightweight,
15
+ and supports consistent hashing, non-blocking IO, and graceful server failover.}
16
+ s.email = %q{benhutton@gmail.com}
17
+ s.extra_rdoc_files = [
18
+ "README"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "MIT-LICENSE",
23
+ "README",
24
+ "Rakefile",
25
+ "lib/active_support/cache/compressed_libmemcached_store.rb",
26
+ "lib/active_support/cache/libmemcached_store.rb",
27
+ "lib/action_dispatch/session/libmemcached_store.rb",
28
+ "lib/libmemcached_store.rb",
29
+ "libmemcached_store.gemspec",
30
+ "test/libmemcached_store_test.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/benhutton/libmemcached_store}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{ActiveSupport::Cache wrapper for libmemcached}
37
+ s.test_files = [
38
+ "test/libmemcached_store_test.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<memcached>, [">= 0"])
47
+ else
48
+ s.add_dependency(%q<memcached>, [">= 0"])
49
+ end
50
+ else
51
+ s.add_dependency(%q<memcached>, [">= 0"])
52
+ end
53
+ end
54
+
@@ -0,0 +1,299 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'active_support'
5
+ require 'memcached'
6
+ require 'mocha'
7
+ require 'active_support/core_ext/module/aliasing'
8
+ require 'active_support/core_ext/object/duplicable'
9
+ require 'active_support/cache/libmemcached_store'
10
+ require 'logger'
11
+
12
+ # Make it easier to get at the underlying cache options during testing.
13
+ class ActiveSupport::Cache::LibmemcachedStore < ActiveSupport::Cache::Store
14
+ delegate :options, :to => '@cache'
15
+ end
16
+
17
+ module CacheStoreBehavior
18
+ def test_should_read_and_write_strings
19
+ assert_equal true, @cache.write('foo', 'bar')
20
+ assert_equal 'bar', @cache.read('foo')
21
+ end
22
+
23
+ def test_should_overwrite
24
+ @cache.write('foo', 'bar')
25
+ @cache.write('foo', 'baz')
26
+ assert_equal 'baz', @cache.read('foo')
27
+ end
28
+
29
+ def test_fetch_without_cache_miss
30
+ @cache.write('foo', 'bar')
31
+ @cache.expects(:write).never
32
+ assert_equal 'bar', @cache.fetch('foo') { 'baz' }
33
+ end
34
+
35
+ def test_fetch_with_cache_miss
36
+ @cache.expects(:write).with('foo', 'baz', @cache.options)
37
+ assert_equal 'baz', @cache.fetch('foo') { 'baz' }
38
+ end
39
+
40
+ def test_fetch_with_forced_cache_miss
41
+ @cache.write('foo', 'bar')
42
+ @cache.expects(:read).never
43
+ @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
44
+ @cache.fetch('foo', :force => true) { 'bar' }
45
+ end
46
+
47
+ def test_fetch_with_cached_nil
48
+ @cache.write('foo', nil)
49
+ @cache.expects(:write).never
50
+ assert_nil @cache.fetch('foo') { 'baz' }
51
+ end
52
+
53
+ def test_should_read_and_write_hash
54
+ assert_equal true, @cache.write('foo', {:a => "b"})
55
+ assert_equal({:a => "b"}, @cache.read('foo'))
56
+ end
57
+
58
+ def test_should_read_and_write_integer
59
+ assert_equal true, @cache.write('foo', 1)
60
+ assert_equal 1, @cache.read('foo')
61
+ end
62
+
63
+ def test_should_read_and_write_nil
64
+ assert_equal true, @cache.write('foo', nil)
65
+ assert_equal nil, @cache.read('foo')
66
+ end
67
+
68
+ def test_read_multi
69
+ @cache.write('foo', 'bar')
70
+ @cache.write('fu', 'baz')
71
+ @cache.write('fud', 'biz')
72
+ assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
73
+ end
74
+
75
+ def test_read_and_write_compressed_small_data
76
+ @cache.write('foo', 'bar', :compress => true)
77
+ raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
78
+ assert_equal 'bar', @cache.read('foo')
79
+ assert_equal 'bar', raw_value
80
+ end
81
+
82
+ def test_read_and_write_compressed_large_data
83
+ @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
84
+ raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
85
+ assert_equal 'bar', @cache.read('foo')
86
+ assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
87
+ end
88
+
89
+ def test_read_and_write_compressed_nil
90
+ @cache.write('foo', nil, :compress => true)
91
+ assert_nil @cache.read('foo')
92
+ end
93
+
94
+ def test_cache_key
95
+ obj = Object.new
96
+ def obj.cache_key
97
+ :foo
98
+ end
99
+ @cache.write(obj, "bar")
100
+ assert_equal "bar", @cache.read("foo")
101
+ end
102
+
103
+ def test_param_as_cache_key
104
+ obj = Object.new
105
+ def obj.to_param
106
+ "foo"
107
+ end
108
+ @cache.write(obj, "bar")
109
+ assert_equal "bar", @cache.read("foo")
110
+ end
111
+
112
+ def test_array_as_cache_key
113
+ @cache.write([:fu, "foo"], "bar")
114
+ assert_equal "bar", @cache.read("fu/foo")
115
+ end
116
+
117
+ def test_hash_as_cache_key
118
+ @cache.write({:foo => 1, :fu => 2}, "bar")
119
+ assert_equal "bar", @cache.read("foo=1/fu=2")
120
+ end
121
+
122
+ def test_keys_are_case_sensitive
123
+ @cache.write("foo", "bar")
124
+ assert_nil @cache.read("FOO")
125
+ end
126
+
127
+ def test_exist
128
+ @cache.write('foo', 'bar')
129
+ assert_equal true, @cache.exist?('foo')
130
+ assert_equal false, @cache.exist?('bar')
131
+ end
132
+
133
+ def test_nil_exist
134
+ @cache.write('foo', nil)
135
+ assert_equal true, @cache.exist?('foo')
136
+ end
137
+
138
+ def test_delete
139
+ @cache.write('foo', 'bar')
140
+ assert @cache.exist?('foo')
141
+ assert_equal true, @cache.delete('foo')
142
+ assert !@cache.exist?('foo')
143
+ end
144
+
145
+ def test_store_objects_should_be_immutable
146
+ @cache.write('foo', 'bar')
147
+ assert_raise(ActiveSupport::FrozenObjectError) { @cache.read('foo').gsub!(/.*/, 'baz') }
148
+ assert_equal 'bar', @cache.read('foo')
149
+ end
150
+
151
+ def test_original_store_objects_should_not_be_immutable
152
+ bar = 'bar'
153
+ @cache.write('foo', bar)
154
+ assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
155
+ end
156
+
157
+ def test_expires_in
158
+ time = Time.local(2008, 4, 24)
159
+ Time.stubs(:now).returns(time)
160
+
161
+ @cache.write('foo', 'bar')
162
+ assert_equal 'bar', @cache.read('foo')
163
+
164
+ Time.stubs(:now).returns(time + 30)
165
+ assert_equal 'bar', @cache.read('foo')
166
+
167
+ Time.stubs(:now).returns(time + 61)
168
+ assert_nil @cache.read('foo')
169
+ end
170
+
171
+ def test_race_condition_protection
172
+ time = Time.now
173
+ @cache.write('foo', 'bar', :expires_in => 60)
174
+ Time.stubs(:now).returns(time + 61)
175
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
176
+ assert_equal 'bar', @cache.read('foo')
177
+ "baz"
178
+ end
179
+ assert_equal "baz", result
180
+ end
181
+
182
+ def test_race_condition_protection_is_limited
183
+ time = Time.now
184
+ @cache.write('foo', 'bar', :expires_in => 60)
185
+ Time.stubs(:now).returns(time + 71)
186
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
187
+ assert_equal nil, @cache.read('foo')
188
+ "baz"
189
+ end
190
+ assert_equal "baz", result
191
+ end
192
+
193
+ def test_race_condition_protection_is_safe
194
+ time = Time.now
195
+ @cache.write('foo', 'bar', :expires_in => 60)
196
+ Time.stubs(:now).returns(time + 61)
197
+ begin
198
+ @cache.fetch('foo', :race_condition_ttl => 10) do
199
+ assert_equal 'bar', @cache.read('foo')
200
+ raise ArgumentError.new
201
+ end
202
+ rescue ArgumentError
203
+ end
204
+ assert_equal "bar", @cache.read('foo')
205
+ Time.stubs(:now).returns(time + 71)
206
+ assert_nil @cache.read('foo')
207
+ end
208
+
209
+ def test_crazy_key_characters
210
+ crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
211
+ assert_equal true, @cache.write(crazy_key, "1", :raw => true)
212
+ assert_equal "1", @cache.read(crazy_key)
213
+ assert_equal "1", @cache.fetch(crazy_key)
214
+ assert_equal true, @cache.delete(crazy_key)
215
+ assert_equal "2", @cache.fetch(crazy_key, :raw => true) { "2" }
216
+ assert_equal 3, @cache.increment(crazy_key)
217
+ assert_equal 2, @cache.decrement(crazy_key)
218
+ end
219
+
220
+ def test_really_long_keys
221
+ key = ""
222
+ 1000.times{key << "x"}
223
+ assert_equal true, @cache.write(key, "bar")
224
+ assert_equal "bar", @cache.read(key)
225
+ assert_equal "bar", @cache.fetch(key)
226
+ assert_nil @cache.read("#{key}x")
227
+ assert_equal({key => "bar"}, @cache.read_multi(key))
228
+ assert_equal true, @cache.delete(key)
229
+ end
230
+ end
231
+
232
+ module CacheIncrementDecrementBehavior
233
+ def test_increment
234
+ @cache.write('foo', 1, :raw => true)
235
+ assert_equal 1, @cache.read('foo').to_i
236
+ assert_equal 2, @cache.increment('foo')
237
+ assert_equal 2, @cache.read('foo').to_i
238
+ assert_equal 3, @cache.increment('foo')
239
+ assert_equal 3, @cache.read('foo').to_i
240
+ end
241
+
242
+ def test_decrement
243
+ @cache.write('foo', 3, :raw => true)
244
+ assert_equal 3, @cache.read('foo').to_i
245
+ assert_equal 2, @cache.decrement('foo')
246
+ assert_equal 2, @cache.read('foo').to_i
247
+ assert_equal 1, @cache.decrement('foo')
248
+ assert_equal 1, @cache.read('foo').to_i
249
+ end
250
+ end
251
+
252
+ class LibmemcachedStoreTest < Test::Unit::TestCase
253
+ include CacheStoreBehavior
254
+ include CacheIncrementDecrementBehavior
255
+
256
+ def setup
257
+ @cache = ActiveSupport::Cache.lookup_store(:libmemcached_store, :expires_in => 60)
258
+ @cache.clear
259
+ @cache.silence!
260
+ @cache.logger = Logger.new("/dev/null")
261
+ end
262
+
263
+ def test_should_identify_cache_store
264
+ assert_kind_of ActiveSupport::Cache::LibmemcachedStore, @cache
265
+ end
266
+
267
+ def test_should_set_server_addresses_to_localhost_if_none_are_given
268
+ assert_equal %w(localhost), @cache.addresses
269
+ end
270
+
271
+ def test_should_set_custom_server_addresses
272
+ store = ActiveSupport::Cache.lookup_store :libmemcached_store, 'localhost', '192.168.1.1'
273
+ assert_equal %w(localhost 192.168.1.1), store.addresses
274
+ end
275
+
276
+ def test_should_enable_consistent_ketema_hashing_by_default
277
+ assert_equal :consistent_ketama, @cache.options[:distribution]
278
+ end
279
+
280
+ def test_should_not_enable_non_blocking_io_by_default
281
+ assert_nil @cache.options[:no_block]
282
+ end
283
+
284
+ def test_should_not_enable_server_failover_by_default
285
+ assert_nil @cache.options[:failover]
286
+ end
287
+
288
+ def test_should_allow_configuration_of_custom_options
289
+ options = {
290
+ :tcp_nodelay => true,
291
+ :distribution => :modula
292
+ }
293
+
294
+ store = ActiveSupport::Cache.lookup_store :libmemcached_store, 'localhost', options
295
+
296
+ assert_equal :modula, store.options[:distribution]
297
+ assert_equal true, store.options[:tcp_nodelay]
298
+ end
299
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: benhutton-libmemcached_store
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
+ platform: ruby
11
+ authors:
12
+ - Ben Hutton
13
+ - Jeffrey Hardy
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-24 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: memcached
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: |-
35
+ An ActiveSupport cache store that uses the C-based libmemcached client through
36
+ Evan Weaver's Ruby/SWIG wrapper, memcached. libmemcached is fast, lightweight,
37
+ and supports consistent hashing, non-blocking IO, and graceful server failover.
38
+ email: benhutton@gmail.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README
45
+ files:
46
+ - .gitignore
47
+ - MIT-LICENSE
48
+ - README
49
+ - Rakefile
50
+ - lib/active_support/cache/compressed_libmemcached_store.rb
51
+ - lib/active_support/cache/libmemcached_store.rb
52
+ - lib/action_dispatch/session/libmemcached_store.rb
53
+ - lib/libmemcached_store.rb
54
+ - libmemcached_store.gemspec
55
+ - test/libmemcached_store_test.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/benhutton/libmemcached_store
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: ActiveSupport::Cache wrapper for libmemcached
88
+ test_files:
89
+ - test/libmemcached_store_test.rb