mongo_cache_store 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 00920d89d10ee3956b56d6de55b5bdd9c93a54db
4
+ data.tar.gz: 09a537ab5771d079419f9a4506a65001e0a125e6
5
+ SHA512:
6
+ metadata.gz: 58456d15add616876bfd56b98e42a129379412799f8b6576e2a40c51a7e7f1860a8e16f34b8106a0f2c034ed9270ba25da11e0e9b9be2f78ceafcb88d8ae936a
7
+ data.tar.gz: 889281aeedcbfab621b5811e3141bef5e5ae877b6b7a71aca26de07a7ce4eac3d2fc973f7d8e8cb17dce32dd6afa09c41249d93f04099eafaa10a98be7b9b175
data/README.rdoc CHANGED
@@ -56,6 +56,25 @@ configured or used.
56
56
  Useful for write conditions and read preferences
57
57
 
58
58
 
59
+ == Increment / Decrement
60
+
61
+ Increment and decrement values must be an Integer. In the ActiveSupport test
62
+ suite strings and integers are used interchangeably. This cache store however
63
+ uses MongoDB's $inc operator which must, be an integer.
64
+
65
+
66
+ == Keys can be a Hash
67
+
68
+ The following test from ActiveSupport fails with MongoCacheStore:
69
+
70
+ def test_hash_as_cache_key
71
+ @cache.write({:foo => 1, :fu => 2}, "bar")
72
+ assert_equal "bar", @cache.read("foo=1/fu=2")
73
+ end
74
+
75
+ This is because a key can be a true Hash. It will not be converted to a string.
76
+
77
+
59
78
  == Backends
60
79
 
61
80
  == TTL
@@ -67,7 +86,7 @@ TTL backend for MongoCacheStore
67
86
  Entries are kept in a namespaced TTL collection that will
68
87
  automatically flush any entries as they pass their expiration
69
88
  time. This keeps the size of the cache in check over time.
70
- <b>Requires MongoDB 2.0 or higher</b>
89
+ <b>Requires MongoDB 2.2 or higher</b>
71
90
 
72
91
  === Additional Options
73
92
 
@@ -81,7 +81,7 @@ module ActiveSupport
81
81
  when defined?(Rails) && Rails.logger
82
82
  @logger = Rails.logger
83
83
  else
84
- #@logger = Logger.new(STDOUT)
84
+ @logger = Logger.new(STDOUT)
85
85
  end
86
86
 
87
87
  @logger
@@ -7,15 +7,70 @@ module ActiveSupport
7
7
  # Base methods used by all MongoCacheStore backends
8
8
  module Base
9
9
 
10
- # No public methods are defined for this module
10
+
11
+ def increment(name, amount = 1, options = {})
12
+ write_counter(name,amount.to_i,options)
13
+ end
14
+
15
+ def decrement(name, amount = 1, options = {})
16
+ write_counter(name,amount.to_i*-1,options)
17
+ end
18
+
19
+ def read_multi(*names)
20
+ options = names.extract_options!
21
+ options = merged_options(options)
22
+ results = {}
23
+
24
+ col = get_collection(options)
25
+
26
+ key_map = names.inject({}) do |h, name|
27
+ h[namespaced_key(name,options)] = name
28
+ h
29
+ end
30
+
31
+ safe_rescue do
32
+ query = {
33
+ :_id => { '$in' => key_map.keys},
34
+ :expires_at => {
35
+ '$gt' => Time.now
36
+ }
37
+ }
38
+
39
+ col.find(query) do |cursor|
40
+ cursor.each do |r|
41
+ results[key_map[r['_id']]] = inflate_entry(r).value
42
+ puts results.inspect
43
+ end
44
+ end
45
+ end
46
+
47
+ results
48
+ end
11
49
 
12
50
  private
13
51
 
14
- def expand_key(key)
15
- return key
52
+ def expanded_key(key)
53
+ return key.cache_key.to_s if key.respond_to?(:cache_key)
54
+
55
+ case key
56
+ when Array
57
+ if key.size > 1
58
+ key = key.collect{|element| expanded_key(element)}
59
+ else
60
+ key = key.first
61
+ end
62
+ when Hash
63
+ return key
64
+ end
65
+
66
+ key.to_param
16
67
  end
17
68
 
18
69
  def namespaced_key(key, options)
70
+ key = expanded_key(key)
71
+ key = key.join('/') if key.is_a?(Array)
72
+ key = key.cache_key if key.methods.include?(:cache_key)
73
+
19
74
  return key
20
75
  end
21
76
 
@@ -31,25 +86,62 @@ module ActiveSupport
31
86
  }
32
87
 
33
88
  response = col.find_one(query)
34
- return nil if response.nil?
89
+ return inflate_entry(response)
90
+ end
91
+ nil
92
+ end
93
+
94
+
95
+ def inflate_entry(from_mongo)
96
+ return nil if from_mongo.nil?
35
97
 
36
98
  entry_options = {
37
- :compressed => response[:compressed],
38
- :expires_in => response[:expires_in]
99
+ :compressed => from_mongo['compressed'],
100
+ :expires_in => from_mongo['expires_in']
39
101
  }
40
- if response['serialized']
41
- r_value = response['value'].to_s
102
+ if from_mongo['serialized']
103
+ r_value = from_mongo['value'].to_s
42
104
  else
43
- r_value = Marshal.dump(response['value'])
105
+ r_value = Marshal.dump(from_mongo['value'])
44
106
  end
45
- ActiveSupport::Cache::Entry.create(r_value,response[:created_at],entry_options)
46
- end
107
+ ActiveSupport::Cache::Entry.create(r_value,from_mongo['created_at'],entry_options)
47
108
  end
48
109
 
110
+ def write_counter(name, amount, options)
111
+ col = get_collection(options)
112
+ key = namespaced_key(name,options)
113
+
114
+ safe_rescue do
115
+ doc = col.find_and_modify(
116
+ :query => {
117
+ :_id => key
118
+ },
119
+ :update => {
120
+ :$inc => {
121
+ :value => amount
122
+ }
123
+ }
124
+ )
125
+
126
+ doc['value'] + amount
127
+ end
128
+ end
49
129
 
50
130
  def write_entry(key,entry,options)
51
131
  col = get_collection(options)
52
132
  serialize = options[:serialize] == :always ? true : false
133
+ serialize = false if entry.value.is_a?(Integer) || entry.value.nil?
134
+
135
+ value = begin
136
+ if entry.compressed?
137
+ BSON::Binary.new(entry.raw_value)
138
+ elsif serialize
139
+ BSON::Binary.new(entry.raw_value)
140
+ else
141
+ entry.value
142
+ end
143
+ end
144
+
53
145
  try_cnt = 0
54
146
 
55
147
  save_doc = {
@@ -59,7 +151,7 @@ module ActiveSupport
59
151
  :expires_at => entry.expires_in.nil? ? Time.utc(9999) : Time.at(entry.expires_at),
60
152
  :compressed => entry.compressed?,
61
153
  :serialized => serialize,
62
- :value => serialize ? BSON::Binary.new(entry.raw_value) : entry.value
154
+ :value => value
63
155
  }.merge(options[:xentry] || {})
64
156
 
65
157
  safe_rescue do
@@ -74,8 +166,10 @@ module ActiveSupport
74
166
  end
75
167
  end
76
168
  end
169
+
77
170
  end
78
171
 
172
+
79
173
  def delete_entry(key,options)
80
174
  col = get_collection(options)
81
175
  safe_rescue do
@@ -86,7 +180,7 @@ module ActiveSupport
86
180
  def get_collection_name(options = {})
87
181
  name_parts = ['cache']
88
182
  name_parts.push(backend_name)
89
- name_parts.push options[:namespace] unless options[:namespace].nil?
183
+ name_parts.push options[:namespace] if !options[:namespace].nil?
90
184
  name = name_parts.join('.')
91
185
  return name
92
186
  end
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module MongoCacheStore
3
- VERSION = "0.2.3"
3
+ VERSION = "0.2.4"
4
4
  end
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency 'mongo'
21
21
  gem.add_dependency 'activesupport', '~>3'
22
22
  gem.add_development_dependency 'rspec'
23
+ gem.add_development_dependency 'mocha', '~>0.13.3'
23
24
  end
@@ -0,0 +1,64 @@
1
+ ORIG_ARGV = ARGV.dup
2
+
3
+ #begin
4
+ # old, $VERBOSE = $VERBOSE, nil
5
+ # require File.expand_path('../../../load_paths', __FILE__)
6
+ #ensure
7
+ # $VERBOSE = old
8
+ #end
9
+
10
+ lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
11
+ $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
12
+
13
+ require 'active_support/core_ext/kernel/reporting'
14
+
15
+ require 'active_support/core_ext/string/encoding'
16
+ if "ruby".encoding_aware?
17
+ # These are the normal settings that will be set up by Railties
18
+ # TODO: Have these tests support other combinations of these values
19
+ silence_warnings do
20
+ Encoding.default_internal = "UTF-8"
21
+ Encoding.default_external = "UTF-8"
22
+ end
23
+ end
24
+
25
+ require 'test/unit'
26
+ #require 'empty_bool'
27
+
28
+ silence_warnings { require 'mocha' }
29
+
30
+ ENV['NO_RELOAD'] = '1'
31
+ require 'active_support'
32
+
33
+ # Include shims until we get off 1.8.6
34
+ require 'active_support/ruby/shim' if RUBY_VERSION < '1.8.7'
35
+
36
+ def uses_memcached(test_name)
37
+ require 'memcache'
38
+ begin
39
+ MemCache.new('localhost:11211').stats
40
+ yield
41
+ rescue MemCache::MemCacheError
42
+ $stderr.puts "Skipping #{test_name} tests. Start memcached and try again."
43
+ end
44
+ end
45
+
46
+ def with_kcode(code)
47
+ if RUBY_VERSION < '1.9'
48
+ begin
49
+ old_kcode, $KCODE = $KCODE, code
50
+ yield
51
+ ensure
52
+ $KCODE = old_kcode
53
+ end
54
+ else
55
+ yield
56
+ end
57
+ end
58
+
59
+ # Show backtraces for deprecated behavior for quicker cleanup.
60
+ ActiveSupport::Deprecation.debug = true
61
+
62
+ if RUBY_VERSION < '1.9'
63
+ $KCODE = 'UTF8'
64
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'abstract_unit'
2
+ require 'mongo'
3
+ require 'active_support/test_case'
4
+ require 'active_support/cache'
5
+ require_relative 'test_modules'
6
+
7
+
8
+
9
+ class MongoCacheStoreTTLTest < ActiveSupport::TestCase
10
+ def setup
11
+ @cache = ActiveSupport::Cache.lookup_store(
12
+ :mongo_cache_store, :TTL,
13
+ :db => Mongo::DB.new('db_name',Mongo::Connection.new),
14
+ :expires_in => 60
15
+ )
16
+ @cache.clear
17
+ end
18
+
19
+ include CacheStoreBehavior
20
+ # include LocalCacheBehavior
21
+ include CacheIncrementDecrementBehavior
22
+ # include EncodedKeyCacheBehavior
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'abstract_unit'
2
+ require 'mongo'
3
+ require 'active_support/test_case'
4
+ require 'active_support/cache'
5
+ require_relative 'test_modules'
6
+
7
+
8
+
9
+ class MongoCacheStoreTTLTest < ActiveSupport::TestCase
10
+ def setup
11
+ @cache = ActiveSupport::Cache.lookup_store(
12
+ :mongo_cache_store, :Standard,
13
+ :db => Mongo::DB.new('db_name',Mongo::Connection.new),
14
+ :expires_in => 60
15
+ )
16
+ @cache.clear
17
+ end
18
+
19
+ include CacheStoreBehavior
20
+ # include LocalCacheBehavior
21
+ include CacheIncrementDecrementBehavior
22
+ # include EncodedKeyCacheBehavior
23
+
24
+ end
@@ -0,0 +1,388 @@
1
+ # Tests the base functionality that should be identical across all cache stores.
2
+
3
+ module EncodedKeyCacheBehavior
4
+ if defined?(Encoding)
5
+ Encoding.list.each do |encoding|
6
+ define_method "test_#{encoding.name.underscore}_encoded_values" do
7
+ key = "foo".force_encoding(encoding)
8
+ assert @cache.write(key, 1, :raw => true)
9
+ assert_equal 1, @cache.read(key)
10
+ assert_equal 1, @cache.fetch(key)
11
+ assert @cache.delete(key)
12
+ assert_equal 2, @cache.fetch(key, :raw => true) { 2 }
13
+ assert_equal 3, @cache.increment(key)
14
+ assert_equal 2, @cache.decrement(key)
15
+ end
16
+ end
17
+
18
+ def test_common_utf8_values
19
+ key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
20
+ assert @cache.write(key, 1, :raw => true)
21
+ assert_equal 1, @cache.read(key)
22
+ assert_equal 1, @cache.fetch(key)
23
+ assert @cache.delete(key)
24
+ assert_equal 2, @cache.fetch(key, :raw => true) { 2 }
25
+ assert_equal 3, @cache.increment(key)
26
+ assert_equal 2, @cache.decrement(key)
27
+ end
28
+
29
+ def test_retains_encoding
30
+ key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
31
+ assert @cache.write(key, 1, :raw => true)
32
+ assert_equal Encoding::UTF_8, key.encoding
33
+ end
34
+ end
35
+ end
36
+
37
+ module CacheIncrementDecrementBehavior
38
+ def test_increment
39
+ @cache.write('foo', 1, :raw => true)
40
+ assert_equal 1, @cache.read('foo').to_i
41
+ assert_equal 2, @cache.increment('foo')
42
+ assert_equal 2, @cache.read('foo').to_i
43
+ assert_equal 3, @cache.increment('foo')
44
+ assert_equal 3, @cache.read('foo').to_i
45
+ end
46
+
47
+ def test_decrement
48
+ @cache.write('foo', 3, :raw => true)
49
+ assert_equal 3, @cache.read('foo').to_i
50
+ assert_equal 2, @cache.decrement('foo')
51
+ assert_equal 2, @cache.read('foo').to_i
52
+ assert_equal 1, @cache.decrement('foo')
53
+ assert_equal 1, @cache.read('foo').to_i
54
+ end
55
+ end
56
+
57
+ module LocalCacheBehavior
58
+ def test_local_writes_are_persistent_on_the_remote_cache
59
+ retval = @cache.with_local_cache do
60
+ @cache.write('foo', 'bar')
61
+ end
62
+ assert retval
63
+ assert_equal 'bar', @cache.read('foo')
64
+ end
65
+
66
+ def test_clear_also_clears_local_cache
67
+ @cache.with_local_cache do
68
+ @cache.write('foo', 'bar')
69
+ @cache.clear
70
+ assert_nil @cache.read('foo')
71
+ end
72
+
73
+ assert_nil @cache.read('foo')
74
+ end
75
+
76
+ def test_local_cache_of_write
77
+ @cache.with_local_cache do
78
+ @cache.write('foo', 'bar')
79
+ @peek.delete('foo')
80
+ assert_equal 'bar', @cache.read('foo')
81
+ end
82
+ end
83
+
84
+ def test_local_cache_of_read
85
+ @cache.write('foo', 'bar')
86
+ @cache.with_local_cache do
87
+ assert_equal 'bar', @cache.read('foo')
88
+ end
89
+ end
90
+
91
+ def test_local_cache_of_write_nil
92
+ @cache.with_local_cache do
93
+ assert @cache.write('foo', nil)
94
+ assert_nil @cache.read('foo')
95
+ @peek.write('foo', 'bar')
96
+ assert_nil @cache.read('foo')
97
+ end
98
+ end
99
+
100
+ def test_local_cache_of_delete
101
+ @cache.with_local_cache do
102
+ @cache.write('foo', 'bar')
103
+ @cache.delete('foo')
104
+ assert_nil @cache.read('foo')
105
+ end
106
+ end
107
+
108
+ def test_local_cache_of_exist
109
+ @cache.with_local_cache do
110
+ @cache.write('foo', 'bar')
111
+ @peek.delete('foo')
112
+ assert @cache.exist?('foo')
113
+ end
114
+ end
115
+
116
+ def test_local_cache_of_increment
117
+ @cache.with_local_cache do
118
+ @cache.write('foo', 1, :raw => true)
119
+ @peek.write('foo', 2, :raw => true)
120
+ @cache.increment('foo')
121
+ assert_equal 3, @cache.read('foo')
122
+ end
123
+ end
124
+
125
+ def test_local_cache_of_decrement
126
+ @cache.with_local_cache do
127
+ @cache.write('foo', 1, :raw => true)
128
+ @peek.write('foo', 3, :raw => true)
129
+ @cache.decrement('foo')
130
+ assert_equal 2, @cache.read('foo')
131
+ end
132
+ end
133
+
134
+ def test_middleware
135
+ app = lambda { |env|
136
+ result = @cache.write('foo', 'bar')
137
+ assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
138
+ assert result
139
+ }
140
+ app = @cache.middleware.new(app)
141
+ app.call({})
142
+ end
143
+ end
144
+
145
+ module CacheStoreBehavior
146
+ def test_should_read_and_write_strings
147
+ assert @cache.write('foo', 'bar')
148
+ assert_equal 'bar', @cache.read('foo')
149
+ end
150
+
151
+ def test_should_overwrite
152
+ @cache.write('foo', 'bar')
153
+ @cache.write('foo', 'baz')
154
+ assert_equal 'baz', @cache.read('foo')
155
+ end
156
+
157
+ def test_fetch_without_cache_miss
158
+ @cache.write('foo', 'bar')
159
+ @cache.expects(:write).never
160
+ assert_equal 'bar', @cache.fetch('foo') { 'baz' }
161
+ end
162
+
163
+ def test_fetch_with_cache_miss
164
+ @cache.expects(:write).with('foo', 'baz', @cache.options)
165
+ assert_equal 'baz', @cache.fetch('foo') { 'baz' }
166
+ end
167
+
168
+ def test_fetch_with_forced_cache_miss
169
+ @cache.write('foo', 'bar')
170
+ @cache.expects(:read).never
171
+ @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
172
+ @cache.fetch('foo', :force => true) { 'bar' }
173
+ end
174
+
175
+ def test_fetch_with_cached_nil
176
+ @cache.write('foo', nil)
177
+ #@cache.expects(:write).never
178
+ assert_nil @cache.fetch('foo') { 'baz' }
179
+ end
180
+
181
+ def test_should_read_and_write_hash
182
+ assert @cache.write('foo', {:a => "b"})
183
+ assert_equal({:a => "b"}, @cache.read('foo'))
184
+ end
185
+
186
+ def test_should_read_and_write_integer
187
+ assert @cache.write('foo', 1)
188
+ assert_equal 1, @cache.read('foo')
189
+ end
190
+
191
+ def test_should_read_and_write_nil
192
+ assert @cache.write('foo', nil)
193
+ assert_equal nil, @cache.read('foo')
194
+ end
195
+
196
+ def test_should_read_and_write_false
197
+ assert @cache.write('foo', false)
198
+ assert_equal false, @cache.read('foo')
199
+ end
200
+
201
+ def test_should_read_cached_numeric_from_previous_rails_versions
202
+ @old_cache = ActiveSupport::Cache::Entry.create( 1, Time.now )
203
+ assert_equal( 1, @old_cache.value )
204
+ end
205
+
206
+ def test_should_read_cached_hash_from_previous_rails_versions
207
+ @old_cache = ActiveSupport::Cache::Entry.create( {}, Time.now )
208
+ assert_equal( {}, @old_cache.value )
209
+ end
210
+
211
+ def test_should_read_cached_string_from_previous_rails_versions
212
+ @old_cache = ActiveSupport::Cache::Entry.create( 'string', Time.now )
213
+ assert_equal( 'string', @old_cache.value )
214
+ end
215
+
216
+ def test_read_multi
217
+ @cache.write('foo', 'bar')
218
+ @cache.write('fu', 'baz')
219
+ @cache.write('fud', 'biz')
220
+ assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
221
+ end
222
+
223
+ def test_read_multi_with_expires
224
+ @cache.write('foo', 'bar', :expires_in => 0.001)
225
+ @cache.write('fu', 'baz')
226
+ @cache.write('fud', 'biz')
227
+ sleep(0.002)
228
+ assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
229
+ end
230
+
231
+ def test_read_and_write_compressed_small_data
232
+ @cache.write('foo', 'bar', :compress => true)
233
+ raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
234
+ assert_equal 'bar', @cache.read('foo')
235
+ assert_equal 'bar', Marshal.load(raw_value)
236
+ end
237
+
238
+ def test_read_and_write_compressed_large_data
239
+ @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
240
+ raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
241
+ assert_equal 'bar', @cache.read('foo')
242
+ assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
243
+ end
244
+
245
+ def test_read_and_write_compressed_nil
246
+ @cache.write('foo', nil, :compress => true)
247
+ assert_nil @cache.read('foo')
248
+ end
249
+
250
+ def test_cache_key
251
+ obj = Object.new
252
+ def obj.cache_key
253
+ :foo
254
+ end
255
+ @cache.write(obj, "bar")
256
+ assert_equal "bar", @cache.read("foo")
257
+ end
258
+
259
+ def test_param_as_cache_key
260
+ obj = Object.new
261
+ def obj.to_param
262
+ "foo"
263
+ end
264
+ @cache.write(obj, "bar")
265
+ assert_equal "bar", @cache.read("foo")
266
+ end
267
+
268
+ def test_array_as_cache_key
269
+ @cache.write([:fu, "foo"], "bar")
270
+ assert_equal "bar", @cache.read("fu/foo")
271
+ end
272
+
273
+ def test_hash_as_cache_key
274
+ @cache.write({:foo => 1, :fu => 2}, "bar")
275
+ assert_equal "bar", @cache.read({:foo => 1, :fu => 2})
276
+ end
277
+
278
+ def test_keys_are_case_sensitive
279
+ @cache.write("foo", "bar")
280
+ assert_nil @cache.read("FOO")
281
+ end
282
+
283
+ def test_exist
284
+ @cache.write('foo', 'bar')
285
+ assert @cache.exist?('foo')
286
+ assert !@cache.exist?('bar')
287
+ end
288
+
289
+ def test_nil_exist
290
+ @cache.write('foo', nil)
291
+ assert @cache.exist?('foo')
292
+ end
293
+
294
+ def test_delete
295
+ @cache.write('foo', 'bar')
296
+ assert @cache.exist?('foo')
297
+ assert @cache.delete('foo')
298
+ assert !@cache.exist?('foo')
299
+ end
300
+
301
+ def test_read_should_return_a_different_object_id_each_time_it_is_called
302
+ @cache.write('foo', 'bar')
303
+ assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
304
+ value = @cache.read('foo')
305
+ value << 'bingo'
306
+ assert_not_equal value, @cache.read('foo')
307
+ end
308
+
309
+ def test_original_store_objects_should_not_be_immutable
310
+ bar = 'bar'
311
+ @cache.write('foo', bar)
312
+ assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
313
+ end
314
+
315
+ def test_expires_in
316
+ time = Time.local(2008, 4, 24)
317
+ Time.stubs(:now).returns(time)
318
+
319
+ @cache.write('foo', 'bar')
320
+ assert_equal 'bar', @cache.read('foo')
321
+
322
+ Time.stubs(:now).returns(time + 30)
323
+ assert_equal 'bar', @cache.read('foo')
324
+
325
+ Time.stubs(:now).returns(time + 61)
326
+ assert_nil @cache.read('foo')
327
+ end
328
+
329
+ def test_race_condition_protection
330
+ time = Time.now
331
+ @cache.write('foo', 'bar', :expires_in => 60)
332
+ Time.stubs(:now).returns(time + 61)
333
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
334
+ assert_equal 'bar', @cache.read('foo')
335
+ "baz"
336
+ end
337
+ assert_equal "baz", result
338
+ end
339
+
340
+ def test_race_condition_protection_is_limited
341
+ time = Time.now
342
+ @cache.write('foo', 'bar', :expires_in => 60)
343
+ Time.stubs(:now).returns(time + 71)
344
+ result = @cache.fetch('foo', :race_condition_ttl => 10) do
345
+ assert_equal nil, @cache.read('foo')
346
+ "baz"
347
+ end
348
+ assert_equal "baz", result
349
+ end
350
+
351
+ def test_race_condition_protection_is_safe
352
+ time = Time.now
353
+ @cache.write('foo', 'bar', :expires_in => 60)
354
+ Time.stubs(:now).returns(time + 61)
355
+ begin
356
+ @cache.fetch('foo', :race_condition_ttl => 10) do
357
+ assert_equal 'bar', @cache.read('foo')
358
+ raise ArgumentError.new
359
+ end
360
+ rescue ArgumentError
361
+ end
362
+ assert_equal "bar", @cache.read('foo')
363
+ Time.stubs(:now).returns(time + 71)
364
+ assert_nil @cache.read('foo')
365
+ end
366
+
367
+ def test_crazy_key_characters
368
+ crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
369
+ assert @cache.write(crazy_key, 1, :raw => true)
370
+ assert_equal 1, @cache.read(crazy_key)
371
+ assert_equal 1, @cache.fetch(crazy_key)
372
+ assert @cache.delete(crazy_key)
373
+ assert_equal 2, @cache.fetch(crazy_key, :raw => true) { 2 }
374
+ assert_equal 3, @cache.increment(crazy_key)
375
+ assert_equal 2, @cache.decrement(crazy_key)
376
+ end
377
+
378
+ def test_really_long_keys
379
+ key = ""
380
+ 900.times{key << "x"}
381
+ assert @cache.write(key, "bar")
382
+ assert_equal "bar", @cache.read(key)
383
+ assert_equal "bar", @cache.fetch(key)
384
+ assert_nil @cache.read("#{key}x")
385
+ assert_equal({key => "bar"}, @cache.read_multi(key))
386
+ assert @cache.delete(key)
387
+ end
388
+ end
data/test/ttl_test.rb ADDED
@@ -0,0 +1,24 @@
1
+ require_relative 'abstract_unit'
2
+ require 'mongo'
3
+ require 'active_support/test_case'
4
+ require 'active_support/cache'
5
+ require_relative 'test_modules'
6
+
7
+
8
+
9
+ class MongoCacheStoreTTLTest < ActiveSupport::TestCase
10
+ def setup
11
+ @cache = ActiveSupport::Cache.lookup_store(
12
+ :mongo_cache_store, :TTL,
13
+ :db => Mongo::DB.new('db_name',Mongo::Connection.new),
14
+ :expires_in => 60
15
+ )
16
+ @cache.clear
17
+ end
18
+
19
+ include CacheStoreBehavior
20
+ # include LocalCacheBehavior
21
+ include CacheIncrementDecrementBehavior
22
+ # include EncodedKeyCacheBehavior
23
+
24
+ end
metadata CHANGED
@@ -1,36 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo_cache_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
5
- prerelease:
4
+ version: 0.2.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kevin McGrath
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-24 00:00:00.000000000 Z
11
+ date: 2013-03-25 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: mongo
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: activesupport
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,19 +41,31 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.13.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 0.13.3
62
69
  description: A MongoDB ActiveSupport Cache
63
70
  email:
64
71
  - kmcgrath@baknet.com
@@ -79,32 +86,37 @@ files:
79
86
  - lib/active_support/cache/mongo_cache_store/backend/ttl.rb
80
87
  - lib/mongo_cache_store/version.rb
81
88
  - mongo_cache_store.gemspec
82
- - spec/active_support/cache/mongo_cache_store_spec.rb
83
- - spec/spec_helper.rb
89
+ - test/abstract_unit.rb
90
+ - test/multi_ttl_test.rb
91
+ - test/standard_test.rb
92
+ - test/test_modules.rb
93
+ - test/ttl_test.rb
84
94
  homepage: https://github.com/kmcgrath/mongo_cache_store
85
95
  licenses: []
96
+ metadata: {}
86
97
  post_install_message:
87
98
  rdoc_options: []
88
99
  require_paths:
89
100
  - lib
90
101
  required_ruby_version: !ruby/object:Gem::Requirement
91
- none: false
92
102
  requirements:
93
- - - ! '>='
103
+ - - '>='
94
104
  - !ruby/object:Gem::Version
95
105
  version: '0'
96
106
  required_rubygems_version: !ruby/object:Gem::Requirement
97
- none: false
98
107
  requirements:
99
- - - ! '>='
108
+ - - '>='
100
109
  - !ruby/object:Gem::Version
101
110
  version: '0'
102
111
  requirements: []
103
112
  rubyforge_project:
104
- rubygems_version: 1.8.24
113
+ rubygems_version: 2.0.0.rc.2
105
114
  signing_key:
106
- specification_version: 3
115
+ specification_version: 4
107
116
  summary: A MongoDB ActiveSupport Cache
108
117
  test_files:
109
- - spec/active_support/cache/mongo_cache_store_spec.rb
110
- - spec/spec_helper.rb
118
+ - test/abstract_unit.rb
119
+ - test/multi_ttl_test.rb
120
+ - test/standard_test.rb
121
+ - test/test_modules.rb
122
+ - test/ttl_test.rb
@@ -1,179 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
-
4
-
5
- class MongoCacheStoreTestSaveClass
6
-
7
- attr_reader :set_me
8
- def initialize(set_me)
9
- @set_me = set_me
10
- end
11
-
12
- end
13
-
14
-
15
- module ActiveSupport
16
- module Cache
17
- class MongoCacheStoreTest
18
- shared_examples "a cache store" do
19
- describe "Caching" do
20
-
21
- it "can write values" do
22
- @store.write('forever!', 'I live forever!')
23
- @store.fetch('forever!').should == "I live forever!"
24
- end
25
-
26
- it "can expire" do
27
- @store.fetch 'testkey', expires_in: 2.minutes do
28
- "I will expire."
29
- end
30
- @store.exist?("testkey").should == true
31
-
32
- sleep 60
33
-
34
- response = @store.fetch 'testkey'
35
- response.should == "I will expire."
36
-
37
- sleep 120
38
-
39
- response = @store.fetch 'testkey'
40
- response.should == nil
41
- end
42
-
43
- it "can cache a hash" do
44
- @store.write('hashme',
45
- {"1"=>{:name=>"Uptime", :zenoss_instance=>"cust", :path=>"/zport/RenderServer/render?gopts=k4VJvRM2K1Dahl3RzJWcqoicTSIz0QJ1Ja10idqC412ptG5_lrmcroRy3BDIqioB561tB4UjtjSg1ib_YaGOXBWOEHBhPbEmA6ZXXjd8LiIOmItqTDZjuFkZvOV38_CF_YEi7t9xUtFKOIxibyVo67jzbyK6P-ORc7i07rK5GUYeA2CnJxm4UDCNKcKRpDCzmdzR-NwcOp0w55KH8YeU6NpgHyM_tpu8G42d7nBQebs1DKvn20sktezqy_LiIizKk3eMzDW-c8w1EZTESWa4rIihXfHrrv9PNxvCw41wU9f3dxQt2c3OFfhSx6riSUbeVsUNEo6s_35KdKJkbr6rEZaaxL2RGR47eLBzifIcZAg="}, "2"=>{:name=>"CPU Utilization", :zenoss_instance=>"cust", :path=>"/zport/RenderServer/render?gopts=WN-Oc9wB_CFbVKzzN75ij-qQcZ6lSOC3jj_ALTzkdMEZ_EzgcvfWOdU7vmlrm9MFY6zrOUeW2Z_uzyJkhRrl_81bVs96xoa7y2S29NVU_oSIIxQj1rUElV04u3wGRXnEbfyYPjLp0m6QEDOthqCs3SLcc7jKkCI15V0Js1b6FpFleWdmO9dCMLvKqdBiE-yhSjfwPS7Rh0wPLAoxXTyIkFFHHBXIu_g69_xnESZhYz0rAWn0W0Ag5N46LuNqwbzW1PwIfczY1Hqw2iKQAq1C4fMry3DNiQvnRmUZdF_w11quhIo-XccH1em3iopVdyy6ik83vMvJN3DfhLUbXcZjnA=="}, "3"=>{:name=>"Memory Utilization", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=_E6b8KAZjy9xzEYtckX_RDQOsGfIRZnG5WRu7ekbUIlsgWmNZNJ8AgEfr7AM0rV6lZMC4PzCPacRN1zeVQ-nHgTBBDbq4sIQuUMl3ETW9DRM2wgjrmnCRwcl7Dz_qGL6PWkhsJzhQ231SuNi7pM6mDw8dbE_pmZ3F6xKa7_8frJLWoMGLji8sIklFf45YckRkIohkMsffZKMsIspx8JEOvR9mP24vGs0Wv4gRXxqFoE1FlgchuKf2KDvWbwp92RrdOmhOAqMxHWmNA2faAC75072ao9bf6UnxZUu5WFUcNsvJJFcEl2U30TEX5vyjf_9C-1Rvnyf8Q6ajvRKA1VNIl6fpItv8dsKTNViX27RWsI="}, "4"=>{:name=>"Swap", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=nBpbINZ6hhzV8IUJq49zWOTsQ4hX5F5ZGPyoHZ6KWPUx_4Xk0_URWbZPQO4yicu14u81lDFqfRCoFTwofkA0NJIqoOQfAx2gWrwbE7RtulqYeMol47KWQpSITLSV4Mkh1Sbp9VZqpc4YVjiu1EvWSOIHCeUPXZPSBWFh0R2VoPB_VutBricdimwkvRsHbU9-BlKyJcT-_vEnTMi2MHlB3iKWY329OkN-J2chI3MtoLvSw9sN2LJbrO4ynFQPX4WFY9D18Zhv1bzOapn0XcCsxj88XHZ2eZeEZ0uv33_9EKSVxKtOOYo3Y08mjEKduOme"}, "5"=>{:name=>"Load Average", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=0y-dExjiyMFT0yWeOf-FR3hBIgIVZZortlKSFUETQYjwMuSDfhB-JhsEYI-feAC0Bmh5T8fZQRuYi5MkD3C2S6Qwd_kzvPrjwxCfxjTaQzidN-DxlWjqTkTfZ-AiPQ3F8JVXr-8kP8ZA10XDcV2VpIHJwZatzxDBVk7isEdKAXEJm-1X_TY7BCdU9uFlcBT1XGEYESM5anyR3282Z77yiYX6PsUcoPwFtLzg1l6Ql8cda8n4d2zLK4ZBX5G25ZQoC0MAmHZPyXRIsI-GGZjKcvXHjZ350tEpyvPiSX8a7PG-dRtWIGEEecM9czU3MF5Xzl-qOVfoRWc8pmlc-5E37Z5TcAoZpUHSlssJdjwoLfOlhFkLGyAVZpVXRCVRWdXNxilLS9N4GbuIEqhsEILyM0MzPvEHwPO3rcPSlzcS4s_SXnjp6XLg2bB6NZjAvbz5Vyb6OPg-8b9C-MmzcpWzVHzP5nYo9RVPR_lpFcmOBUSO_cJO22pIqqMyn1vXbtVz"}, "6"=>{:name=>"Paging", :instance=>"cust", :path=>"/zport/RenderServer/render?gopts=N7sjMwpMD3JE0ZNya07IgWj6t7yn7uX4GJ1XHlmyhRcqcea0HpfXTen6ptASVAZIvWT7fW4kufkvtNBsxKfDMX9JMzqoR-kpfWqoRRfI3eGmqYecg6gcQgXSEnu5Mzf4r-lbo1_Cqp8dYuSmC37uWrKi0gTWVr54ZmQ6w4dSymQ4xSkCH2-MO1X8wxZdtm804NJy6yQ4l7JZYBkWD9rABoPhVfZq4mZpHmZN27UlbK14Z9R3EEF6MG0jKCbJ7gJVEjpOoX_y5ka4S3aRULFSByrZIA290_ZMO21DAnsJbJyMXAEP9r8UanaotdVrPLSOXJkYD4WvdcdDeZ3wMtvdL42oHdhwLDyG9J8i9GkcvwsU04MlTTl270LkX31HJjxjB8IChpDke-9LEmxG_1icmGcAcWr26gTk9HOULwTyso4sX_ZqqCnrrnRRQLCpCZtlqxIB9iAMUtSdkEUdZlsAuw=="}},
46
- expires_in: 1.hour
47
- )
48
-
49
- hash = @store.read('hashme')
50
- hash["1"].should be_a_kind_of(Hash)
51
-
52
- end
53
-
54
- it "can cache class instances" do
55
- @store.fetch 'my_class', expires_in: 30.seconds do
56
- MongoCacheStoreTestSaveClass.new('what did i say?')
57
- end
58
-
59
- my_class = @store.fetch 'my_class'
60
- my_class.set_me.should == 'what did i say?'
61
- end
62
-
63
- it "can use a hash as a key" do
64
- hash_key = {
65
- :class_name => 'my_class',
66
- :option2 => 2
67
- }
68
-
69
- miss_key = {
70
- :class_name => 'my_class',
71
- :option2 => 1
72
- }
73
-
74
- hit_key = {
75
- :class_name => 'my_class',
76
- :option2 => 2
77
- }
78
-
79
-
80
- @store.fetch(hash_key, expires_in: 30.seconds) do
81
- MongoCacheStoreTestSaveClass.new('what did i say?')
82
- end
83
-
84
- my_class = @store.fetch(hash_key)
85
- my_class.set_me.should == 'what did i say?'
86
-
87
- my_class = @store.fetch(hit_key)
88
- my_class.set_me.should == 'what did i say?'
89
-
90
- my_class = @store.fetch(miss_key)
91
- my_class.should == nil
92
-
93
- end
94
-
95
- after(:all) do
96
- @store.clear.should == true
97
- end
98
- end
99
- end
100
-
101
-
102
- describe MongoCacheStore do
103
- describe "initializing" do
104
- it "can take a Mongo::DB object" do
105
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
106
- store = ActiveSupport::Cache::MongoCacheStore.new(:TTL, :db => db)
107
- end
108
- end
109
-
110
- describe "TTL Caching" do
111
- it_behaves_like "a cache store"
112
- before(:all) do
113
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
114
- @store = ActiveSupport::Cache::MongoCacheStore.new(:TTL, :db => db)
115
- end
116
- end
117
-
118
- describe "TTL Caching On Fail Serialization" do
119
- it_behaves_like "a cache store"
120
- before(:all) do
121
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
122
- @store = ActiveSupport::Cache::MongoCacheStore.new(:TTL, :db => db, :serialize => :on_fail)
123
- end
124
- end
125
-
126
- describe "MultiTTL Caching" do
127
- it_behaves_like "a cache store"
128
- before(:all) do
129
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
130
- @store = ActiveSupport::Cache::MongoCacheStore.new(:MultiTTL, :db => db)
131
- end
132
- end
133
-
134
- describe "MultiTTL Caching On Fail Serialization" do
135
- it_behaves_like "a cache store"
136
- before(:all) do
137
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
138
- @store = ActiveSupport::Cache::MongoCacheStore.new(:MultiTTL, :db => db, :serialize => :on_fail)
139
- end
140
- end
141
-
142
- describe "Standard Caching" do
143
- it_behaves_like "a cache store"
144
- before(:all) do
145
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
146
- @store = ActiveSupport::Cache::MongoCacheStore.new(:Standard, :db => db)
147
- end
148
- end
149
-
150
- describe "Standard Caching On Fail Serialization" do
151
- it_behaves_like "a cache store"
152
- before(:all) do
153
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
154
- @store = ActiveSupport::Cache::MongoCacheStore.new(:Standard, :db => db, :serialize => :on_fail)
155
- end
156
- end
157
-
158
- describe "Pass Collection Options" do
159
- before(:all) do
160
- db = Mongo::DB.new('mongo_cache_store_test', Mongo::Connection.new)
161
- @store = ActiveSupport::Cache::MongoCacheStore.new(
162
- :Standard,
163
- :db => db,
164
- :collection_opts => {
165
- :read => :nearest
166
- }
167
- )
168
- end
169
-
170
- it "can set collection opts" do
171
- col = @store.send(:get_collection,{:collection_opts => {:read => :nearest}})
172
- col.instance_eval { @read }.should == :nearest
173
- end
174
-
175
- end
176
- end
177
- end
178
- end
179
- end
data/spec/spec_helper.rb DELETED
@@ -1,6 +0,0 @@
1
- # -*- encoding : utf-8 -*-
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
3
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
- require 'mongo'
5
- require 'active_support/cache/mongo_cache_store'
6
- require 'rspec'