spymemcached_store 1.0.1-java
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +11 -0
- data/lib/active_support/cache/spymemcached_store.rb +166 -0
- data/lib/spymemcached_store.rb +5 -0
- data/lib/spymemcached_store/version.rb +3 -0
- data/spymemcached_store.gemspec +31 -0
- data/test/autoloading_cache_behavior.rb +50 -0
- data/test/cache_increment_decrement_behavior.rb +24 -0
- data/test/cache_store_behavior.rb +248 -0
- data/test/dependencies_test_helpers.rb +28 -0
- data/test/encoded_key_cache_behavior.rb +34 -0
- data/test/local_cache_behavior.rb +88 -0
- data/test/spymemcached_store_test.rb +90 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0cf1de67233c72496592c0350be6f3ca2800c5b4
|
4
|
+
data.tar.gz: 2e3376af818bf52570cc8ccc4e8450d9c76c2b6e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9a4b8c7c6b64c1ffdeacfa2d732768515e9358c4c6fdb15b3e6eb6da529121a35036caa7e7558b1f6561d70f7dc8a9504324665cb063a12eace84eb6401121cb
|
7
|
+
data.tar.gz: 1eee6860ba61259be722275852131475bf5398dd774885c9475e6b65dab9ba76f9c3bb00470e6b5216cef581fd2df0e20c58aff909bc7c59a310138e3ed9e0ec
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.16
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Xiao Li swing1979@gmail.com
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# SpymemcachedStore
|
2
|
+
|
3
|
+
This is Rails 4 compatible cache store for [spymemcached.jruby](https://github.com/ThoughtWorksStudios/spymemcached.jruby)
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'spymemcached_store'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install spymemcached_store
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
config.cache_store = :spymemcached_store, { :expires_in => 60, :namespace => 'app-namespace', :timeout => 0.1 }
|
24
|
+
|
25
|
+
Supports all Rails cache store options, see [spymemcached.jruby](https://github.com/ThoughtWorksStudios/spymemcached.jruby) for additional options.
|
26
|
+
|
27
|
+
It is not recommended to use :compress and :compress_threshold options, as spymemcached.jruby does it by default.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it ( https://github.com/ThoughtWorksStudios/spymemcached_store/fork )
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'active_support/cache'
|
2
|
+
require 'spymemcached'
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
module Cache
|
6
|
+
class SpymemcachedStore < ActiveSupport::Cache::Store
|
7
|
+
|
8
|
+
# namespace
|
9
|
+
def initialize(*addresses)
|
10
|
+
addresses = addresses.flatten
|
11
|
+
options = addresses.extract_options!
|
12
|
+
super(options)
|
13
|
+
|
14
|
+
unless [String, Spymemcached, NilClass].include?(addresses.first.class)
|
15
|
+
raise ArgumentError, "First argument must be an empty array, an array of hosts or a Spymemcached instance."
|
16
|
+
end
|
17
|
+
@client = if addresses.first.is_a?(Spymemcached)
|
18
|
+
addresses.first
|
19
|
+
else
|
20
|
+
mem_cache_options = options.dup
|
21
|
+
UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
|
22
|
+
Spymemcached.new(addresses, mem_cache_options)
|
23
|
+
end
|
24
|
+
|
25
|
+
extend Strategy::LocalCache
|
26
|
+
extend LocalCacheWithRaw
|
27
|
+
end
|
28
|
+
|
29
|
+
# Read multiple values at once from the cache. Options can be passed
|
30
|
+
# in the last argument.
|
31
|
+
#
|
32
|
+
# Some cache implementation may optimize this method.
|
33
|
+
#
|
34
|
+
# Returns a hash mapping the names provided to the values found.
|
35
|
+
def read_multi(*names)
|
36
|
+
options = names.extract_options!
|
37
|
+
options = merged_options(options)
|
38
|
+
keys_to_names = Hash[names.map{|name| [namespaced_key(name, options), name]}]
|
39
|
+
raw_values = @client.get_multi(keys_to_names.keys)
|
40
|
+
values = {}
|
41
|
+
raw_values.each do |key, value|
|
42
|
+
entry = deserialize_entry(value)
|
43
|
+
values[keys_to_names[key]] = entry.value unless entry.expired?
|
44
|
+
end
|
45
|
+
values
|
46
|
+
end
|
47
|
+
|
48
|
+
# Increment an integer value in the cache.
|
49
|
+
#
|
50
|
+
# Options are passed to the underlying cache implementation.
|
51
|
+
#
|
52
|
+
# All implementations may not support this method.
|
53
|
+
def increment(name, amount = 1, options = nil)
|
54
|
+
options = merged_options(options)
|
55
|
+
|
56
|
+
instrument(:increment, name, :amount => amount) do
|
57
|
+
@client.incr(name, amount)
|
58
|
+
end
|
59
|
+
rescue Spymemcached::Error => e
|
60
|
+
logger.error("Spymemcached::Error (#{e}): #{e.message}") if logger
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Decrement an integer value in the cache.
|
65
|
+
#
|
66
|
+
# Options are passed to the underlying cache implementation.
|
67
|
+
#
|
68
|
+
# All implementations may not support this method.
|
69
|
+
def decrement(name, amount = 1, options = nil)
|
70
|
+
options = merged_options(options)
|
71
|
+
|
72
|
+
instrument(:decrement, name, :amount => amount) do
|
73
|
+
@client.decr(name, amount)
|
74
|
+
end
|
75
|
+
rescue Spymemcached::Error => e
|
76
|
+
logger.error("Spymemcached::Error (#{e}): #{e.message}") if logger
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# Clear the entire cache. Be careful with this method since it could
|
81
|
+
# affect other processes if shared cache is being used.
|
82
|
+
#
|
83
|
+
# The options hash is passed to the underlying cache implementation.
|
84
|
+
#
|
85
|
+
# All implementations may not support this method.
|
86
|
+
def clear(options = nil)
|
87
|
+
@client.flush_all
|
88
|
+
rescue Spymemcached::Error => e
|
89
|
+
logger.error("Spymemcached::Error (#{e}): #{e.message}") if logger
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get the statistics from the memcached servers.
|
94
|
+
def stats
|
95
|
+
@client.stats
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
# Read an entry from the cache implementation. Subclasses must implement
|
100
|
+
# this method.
|
101
|
+
def read_entry(key, options) # :nodoc:
|
102
|
+
deserialize_entry(@client.get(key))
|
103
|
+
rescue Spymemcached::Error => e
|
104
|
+
logger.error("Spymemcached::Error (#{e}): #{e.message}") if logger
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
# Write an entry to the cache implementation. Subclasses must implement
|
109
|
+
# this method.
|
110
|
+
def write_entry(key, entry, options) # :nodoc:
|
111
|
+
method = options && options[:unless_exist] ? :add : :set
|
112
|
+
value = options[:raw] ? entry.value.to_s : entry
|
113
|
+
expires_in = options[:expires_in].to_i
|
114
|
+
if expires_in > 0 && !options[:raw]
|
115
|
+
# Set the memcache expire a few minutes in the future to support race condition ttls on read
|
116
|
+
expires_in += 5.minutes
|
117
|
+
end
|
118
|
+
@client.send(method, key, value, expires_in)
|
119
|
+
rescue Spymemcached::Error => e
|
120
|
+
logger.error("Spymemcached::Error (#{e}): #{e.message}") if logger
|
121
|
+
false
|
122
|
+
end
|
123
|
+
|
124
|
+
# Delete an entry from the cache implementation. Subclasses must
|
125
|
+
# implement this method.
|
126
|
+
def delete_entry(key, options) # :nodoc:
|
127
|
+
@client.delete(key)
|
128
|
+
rescue Spymemcached::Error => e
|
129
|
+
logger.error("Spymemcached::Error (#{e}): #{e.message}") if logger
|
130
|
+
false
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
def deserialize_entry(raw_value)
|
135
|
+
if raw_value
|
136
|
+
entry = Marshal.load(raw_value) rescue raw_value
|
137
|
+
entry.is_a?(Entry) ? entry : Entry.new(entry)
|
138
|
+
else
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Provide support for raw values in the local cache strategy.
|
144
|
+
module LocalCacheWithRaw # :nodoc:
|
145
|
+
protected
|
146
|
+
def read_entry(key, options)
|
147
|
+
entry = super
|
148
|
+
if options[:raw] && local_cache && entry
|
149
|
+
entry = deserialize_entry(entry.value)
|
150
|
+
end
|
151
|
+
entry
|
152
|
+
end
|
153
|
+
|
154
|
+
def write_entry(key, entry, options) # :nodoc:
|
155
|
+
retval = super
|
156
|
+
if options[:raw] && local_cache && retval
|
157
|
+
raw_entry = Entry.new(entry.value.to_s)
|
158
|
+
raw_entry.expires_at = entry.expires_at
|
159
|
+
local_cache.write_entry(key, raw_entry, options)
|
160
|
+
end
|
161
|
+
retval
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'spymemcached_store/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "spymemcached_store"
|
8
|
+
spec.version = SpymemcachedStore::VERSION
|
9
|
+
spec.platform = 'java'
|
10
|
+
spec.authors = ["Xiao Li"]
|
11
|
+
spec.email = ["swing1979@gmail.com"]
|
12
|
+
spec.summary = %q{Rails 3 & 4 cache store for spymemcached.jruby.}
|
13
|
+
spec.description = %q{Rails 3 & 4 cache store for spymemcached.jruby.}
|
14
|
+
spec.homepage = "https://github.com/ThoughtWorksStudios/spymemcached_store"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "spymemcached.jruby"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rack"
|
27
|
+
spec.add_development_dependency "mocha"
|
28
|
+
spec.add_development_dependency "activesupport"
|
29
|
+
spec.add_development_dependency 'minitest', '~> 5.1'
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'dependencies_test_helpers'
|
2
|
+
|
3
|
+
module AutoloadingCacheBehavior
|
4
|
+
class E
|
5
|
+
end
|
6
|
+
class ClassFolder
|
7
|
+
class NestedClass
|
8
|
+
end
|
9
|
+
|
10
|
+
class SiblingClass
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
include DependenciesTestHelpers
|
15
|
+
def test_simple_autoloading
|
16
|
+
with_autoloading_fixtures do
|
17
|
+
@cache.write('foo', E.new)
|
18
|
+
end
|
19
|
+
|
20
|
+
remove_constants(:E)
|
21
|
+
ActiveSupport::Dependencies.clear
|
22
|
+
|
23
|
+
with_autoloading_fixtures do
|
24
|
+
assert_kind_of E, @cache.read('foo')
|
25
|
+
end
|
26
|
+
|
27
|
+
remove_constants(:E)
|
28
|
+
ActiveSupport::Dependencies.clear
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_two_classes_autoloading
|
32
|
+
with_autoloading_fixtures do
|
33
|
+
@cache.write('foo', [E.new, ClassFolder.new])
|
34
|
+
end
|
35
|
+
|
36
|
+
remove_constants(:E, :ClassFolder)
|
37
|
+
ActiveSupport::Dependencies.clear
|
38
|
+
|
39
|
+
with_autoloading_fixtures do
|
40
|
+
loaded = @cache.read('foo')
|
41
|
+
assert_kind_of Array, loaded
|
42
|
+
assert_equal 2, loaded.size
|
43
|
+
assert_kind_of E, loaded[0]
|
44
|
+
assert_kind_of ClassFolder, loaded[1]
|
45
|
+
end
|
46
|
+
|
47
|
+
remove_constants(:E, :ClassFolder)
|
48
|
+
ActiveSupport::Dependencies.clear
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module CacheIncrementDecrementBehavior
|
2
|
+
def test_increment
|
3
|
+
@cache.write('foo', 1, :raw => true)
|
4
|
+
assert_equal 1, @cache.read('foo').to_i
|
5
|
+
assert_equal 2, @cache.increment('foo')
|
6
|
+
assert_equal 2, @cache.read('foo').to_i
|
7
|
+
assert_equal 3, @cache.increment('foo')
|
8
|
+
assert_equal 3, @cache.read('foo').to_i
|
9
|
+
# spymemcached will set zero as default value
|
10
|
+
assert_equal 0, @cache.increment('bar')
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_decrement
|
14
|
+
@cache.write('foo', 3, :raw => true)
|
15
|
+
assert_equal 3, @cache.read('foo').to_i
|
16
|
+
assert_equal 2, @cache.decrement('foo')
|
17
|
+
assert_equal 2, @cache.read('foo').to_i
|
18
|
+
assert_equal 1, @cache.decrement('foo')
|
19
|
+
assert_equal 1, @cache.read('foo').to_i
|
20
|
+
|
21
|
+
# spymemcached will set zero as default value
|
22
|
+
assert_equal 0, @cache.decrement('bar')
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
module CacheStoreBehavior
|
2
|
+
def test_should_read_and_write_strings
|
3
|
+
assert @cache.write('foo', 'bar')
|
4
|
+
assert_equal 'bar', @cache.read('foo')
|
5
|
+
end
|
6
|
+
|
7
|
+
def test_should_overwrite
|
8
|
+
@cache.write('foo', 'bar')
|
9
|
+
@cache.write('foo', 'baz')
|
10
|
+
assert_equal 'baz', @cache.read('foo')
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_fetch_without_cache_miss
|
14
|
+
@cache.write('foo', 'bar')
|
15
|
+
@cache.expects(:write).never
|
16
|
+
assert_equal 'bar', @cache.fetch('foo') { 'baz' }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_fetch_with_cache_miss
|
20
|
+
@cache.expects(:write).with('foo', 'baz', @cache.options)
|
21
|
+
assert_equal 'baz', @cache.fetch('foo') { 'baz' }
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_fetch_with_cache_miss_passes_key_to_block
|
25
|
+
cache_miss = false
|
26
|
+
assert_equal 3, @cache.fetch('foo') { |key| cache_miss = true; key.length }
|
27
|
+
assert cache_miss
|
28
|
+
|
29
|
+
cache_miss = false
|
30
|
+
assert_equal 3, @cache.fetch('foo') { |key| cache_miss = true; key.length }
|
31
|
+
assert !cache_miss
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_fetch_with_forced_cache_miss
|
35
|
+
@cache.write('foo', 'bar')
|
36
|
+
@cache.expects(:read).never
|
37
|
+
@cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
|
38
|
+
@cache.fetch('foo', :force => true) { 'bar' }
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_fetch_with_cached_nil
|
42
|
+
@cache.write('foo', nil)
|
43
|
+
@cache.expects(:write).never
|
44
|
+
assert_nil @cache.fetch('foo') { 'baz' }
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_should_read_and_write_hash
|
48
|
+
assert @cache.write('foo', {:a => "b"})
|
49
|
+
assert_equal({:a => "b"}, @cache.read('foo'))
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_should_read_and_write_integer
|
53
|
+
assert @cache.write('foo', 1)
|
54
|
+
assert_equal 1, @cache.read('foo')
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_should_read_and_write_nil
|
58
|
+
assert @cache.write('foo', nil)
|
59
|
+
assert_equal nil, @cache.read('foo')
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_should_read_and_write_false
|
63
|
+
assert @cache.write('foo', false)
|
64
|
+
assert_equal false, @cache.read('foo')
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_read_multi
|
68
|
+
@cache.write('foo', 'bar')
|
69
|
+
@cache.write('fu', 'baz')
|
70
|
+
@cache.write('fud', 'biz')
|
71
|
+
assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_read_multi_with_expires
|
75
|
+
time = Time.now
|
76
|
+
@cache.write('foo', 'bar', :expires_in => 10)
|
77
|
+
@cache.write('fu', 'baz')
|
78
|
+
@cache.write('fud', 'biz')
|
79
|
+
Time.stubs(:now).returns(time + 11)
|
80
|
+
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_fetch_multi
|
84
|
+
@cache.write('foo', 'bar')
|
85
|
+
@cache.write('fud', 'biz')
|
86
|
+
|
87
|
+
values = @cache.fetch_multi('foo', 'fu', 'fud') {|value| value * 2 }
|
88
|
+
|
89
|
+
assert_equal(["bar", "fufu", "biz"], values)
|
90
|
+
assert_equal("fufu", @cache.read('fu'))
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_multi_with_objects
|
94
|
+
foo = stub(:title => "FOO!", :cache_key => "foo")
|
95
|
+
bar = stub(:cache_key => "bar")
|
96
|
+
|
97
|
+
@cache.write('bar', "BAM!")
|
98
|
+
|
99
|
+
values = @cache.fetch_multi(foo, bar) {|object| object.title }
|
100
|
+
assert_equal(["FOO!", "BAM!"], values)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_read_and_write_compressed_small_data
|
104
|
+
@cache.write('foo', 'bar', :compress => true)
|
105
|
+
assert_equal 'bar', @cache.read('foo')
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_read_and_write_compressed_large_data
|
109
|
+
@cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
|
110
|
+
assert_equal 'bar', @cache.read('foo')
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_read_and_write_compressed_nil
|
114
|
+
@cache.write('foo', nil, :compress => true)
|
115
|
+
assert_nil @cache.read('foo')
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_cache_key
|
119
|
+
obj = Object.new
|
120
|
+
def obj.cache_key
|
121
|
+
:foo
|
122
|
+
end
|
123
|
+
@cache.write(obj, "bar")
|
124
|
+
assert_equal "bar", @cache.read("foo")
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_param_as_cache_key
|
128
|
+
obj = Object.new
|
129
|
+
def obj.to_param
|
130
|
+
"foo"
|
131
|
+
end
|
132
|
+
@cache.write(obj, "bar")
|
133
|
+
assert_equal "bar", @cache.read("foo")
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_array_as_cache_key
|
137
|
+
@cache.write([:fu, "foo"], "bar")
|
138
|
+
assert_equal "bar", @cache.read("fu/foo")
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_hash_as_cache_key
|
142
|
+
@cache.write({:foo => 1, :fu => 2}, "bar")
|
143
|
+
assert_equal "bar", @cache.read("foo=1/fu=2")
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_keys_are_case_sensitive
|
147
|
+
@cache.write("foo", "bar")
|
148
|
+
assert_nil @cache.read("FOO")
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_exist
|
152
|
+
@cache.write('foo', 'bar')
|
153
|
+
assert_equal true, @cache.exist?('foo')
|
154
|
+
assert_equal false, @cache.exist?('bar')
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_nil_exist
|
158
|
+
@cache.write('foo', nil)
|
159
|
+
assert @cache.exist?('foo')
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_delete
|
163
|
+
@cache.write('foo', 'bar')
|
164
|
+
assert @cache.exist?('foo')
|
165
|
+
assert @cache.delete('foo')
|
166
|
+
assert !@cache.exist?('foo')
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_original_store_objects_should_not_be_immutable
|
170
|
+
bar = 'bar'
|
171
|
+
@cache.write('foo', bar)
|
172
|
+
assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_expires_in
|
176
|
+
time = Time.local(2008, 4, 24)
|
177
|
+
Time.stubs(:now).returns(time)
|
178
|
+
|
179
|
+
@cache.write('foo', 'bar')
|
180
|
+
assert_equal 'bar', @cache.read('foo')
|
181
|
+
|
182
|
+
Time.stubs(:now).returns(time + 30)
|
183
|
+
assert_equal 'bar', @cache.read('foo')
|
184
|
+
|
185
|
+
Time.stubs(:now).returns(time + 61)
|
186
|
+
assert_nil @cache.read('foo')
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_race_condition_protection
|
190
|
+
time = Time.now
|
191
|
+
@cache.write('foo', 'bar', :expires_in => 60)
|
192
|
+
Time.stubs(:now).returns(time + 61)
|
193
|
+
result = @cache.fetch('foo', :race_condition_ttl => 10) do
|
194
|
+
assert_equal 'bar', @cache.read('foo')
|
195
|
+
"baz"
|
196
|
+
end
|
197
|
+
assert_equal "baz", result
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_race_condition_protection_is_limited
|
201
|
+
time = Time.now
|
202
|
+
@cache.write('foo', 'bar', :expires_in => 60)
|
203
|
+
Time.stubs(:now).returns(time + 71)
|
204
|
+
result = @cache.fetch('foo', :race_condition_ttl => 10) do
|
205
|
+
assert_equal nil, @cache.read('foo')
|
206
|
+
"baz"
|
207
|
+
end
|
208
|
+
assert_equal "baz", result
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_race_condition_protection_is_safe
|
212
|
+
time = Time.now
|
213
|
+
@cache.write('foo', 'bar', :expires_in => 60)
|
214
|
+
Time.stubs(:now).returns(time + 61)
|
215
|
+
begin
|
216
|
+
@cache.fetch('foo', :race_condition_ttl => 10) do
|
217
|
+
assert_equal 'bar', @cache.read('foo')
|
218
|
+
raise ArgumentError.new
|
219
|
+
end
|
220
|
+
rescue ArgumentError
|
221
|
+
end
|
222
|
+
assert_equal "bar", @cache.read('foo')
|
223
|
+
Time.stubs(:now).returns(time + 91)
|
224
|
+
assert_nil @cache.read('foo')
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_crazy_key_characters
|
228
|
+
crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
|
229
|
+
assert @cache.write(crazy_key, "1", :raw => true)
|
230
|
+
assert_equal "1", @cache.read(crazy_key)
|
231
|
+
assert_equal "1", @cache.fetch(crazy_key)
|
232
|
+
assert @cache.delete(crazy_key)
|
233
|
+
assert_equal "2", @cache.fetch(crazy_key, :raw => true) { "2" }
|
234
|
+
assert_equal 3, @cache.increment(crazy_key)
|
235
|
+
assert_equal 2, @cache.decrement(crazy_key)
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_really_long_keys
|
239
|
+
key = ""
|
240
|
+
900.times{key << "x"}
|
241
|
+
assert @cache.write(key, "bar")
|
242
|
+
assert_equal "bar", @cache.read(key)
|
243
|
+
assert_equal "bar", @cache.fetch(key)
|
244
|
+
assert_nil @cache.read("#{key}x")
|
245
|
+
assert_equal({key => "bar"}, @cache.read_multi(key))
|
246
|
+
assert @cache.delete(key)
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DependenciesTestHelpers
|
2
|
+
def with_loading(*from)
|
3
|
+
old_mechanism, ActiveSupport::Dependencies.mechanism = ActiveSupport::Dependencies.mechanism, :load
|
4
|
+
this_dir = File.dirname(__FILE__)
|
5
|
+
parent_dir = File.dirname(this_dir)
|
6
|
+
path_copy = $LOAD_PATH.dup
|
7
|
+
$LOAD_PATH.unshift(parent_dir) unless $LOAD_PATH.include?(parent_dir)
|
8
|
+
prior_autoload_paths = ActiveSupport::Dependencies.autoload_paths
|
9
|
+
ActiveSupport::Dependencies.autoload_paths = from.collect { |f| "#{this_dir}/#{f}" }
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
$LOAD_PATH.replace(path_copy)
|
13
|
+
ActiveSupport::Dependencies.autoload_paths = prior_autoload_paths
|
14
|
+
ActiveSupport::Dependencies.mechanism = old_mechanism
|
15
|
+
ActiveSupport::Dependencies.explicitly_unloadable_constants = []
|
16
|
+
ActiveSupport::Dependencies.clear
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_autoloading_fixtures(&block)
|
20
|
+
with_loading 'autoloading_fixtures', &block
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_constants(*constants)
|
24
|
+
constants.each do |constant|
|
25
|
+
Object.send(:remove_const, constant) if Object.const_defined?(constant)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
|
2
|
+
# The error is caused by character encodings that can't be compared with ASCII-8BIT regular expressions and by special
|
3
|
+
# characters like the umlaut in UTF-8.
|
4
|
+
module EncodedKeyCacheBehavior
|
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
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module LocalCacheBehavior
|
2
|
+
def test_local_writes_are_persistent_on_the_remote_cache
|
3
|
+
retval = @cache.with_local_cache do
|
4
|
+
@cache.write('foo', 'bar')
|
5
|
+
end
|
6
|
+
assert retval
|
7
|
+
assert_equal 'bar', @cache.read('foo')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_clear_also_clears_local_cache
|
11
|
+
@cache.with_local_cache do
|
12
|
+
@cache.write('foo', 'bar')
|
13
|
+
@cache.clear
|
14
|
+
assert_nil @cache.read('foo')
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_nil @cache.read('foo')
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_local_cache_of_write
|
21
|
+
@cache.with_local_cache do
|
22
|
+
@cache.write('foo', 'bar')
|
23
|
+
@peek.delete('foo')
|
24
|
+
assert_equal 'bar', @cache.read('foo')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_local_cache_of_read
|
29
|
+
@cache.write('foo', 'bar')
|
30
|
+
@cache.with_local_cache do
|
31
|
+
assert_equal 'bar', @cache.read('foo')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_local_cache_of_write_nil
|
36
|
+
@cache.with_local_cache do
|
37
|
+
assert @cache.write('foo', nil)
|
38
|
+
assert_nil @cache.read('foo')
|
39
|
+
@peek.write('foo', 'bar')
|
40
|
+
assert_nil @cache.read('foo')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_local_cache_of_delete
|
45
|
+
@cache.with_local_cache do
|
46
|
+
@cache.write('foo', 'bar')
|
47
|
+
@cache.delete('foo')
|
48
|
+
assert_nil @cache.read('foo')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_local_cache_of_exist
|
53
|
+
@cache.with_local_cache do
|
54
|
+
@cache.write('foo', 'bar')
|
55
|
+
@peek.delete('foo')
|
56
|
+
assert @cache.exist?('foo')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_local_cache_of_increment
|
61
|
+
@cache.with_local_cache do
|
62
|
+
@cache.write('foo', 1, :raw => true)
|
63
|
+
@peek.write('foo', 2, :raw => true)
|
64
|
+
@cache.increment('foo')
|
65
|
+
assert_equal 3, @cache.read('foo')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_local_cache_of_decrement
|
70
|
+
@cache.with_local_cache do
|
71
|
+
@cache.write('foo', 1, :raw => true)
|
72
|
+
@peek.write('foo', 3, :raw => true)
|
73
|
+
@cache.decrement('foo')
|
74
|
+
assert_equal 2, @cache.read('foo')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_middleware
|
79
|
+
app = lambda { |env|
|
80
|
+
result = @cache.write('foo', 'bar')
|
81
|
+
assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
|
82
|
+
assert result
|
83
|
+
[200, {}, []]
|
84
|
+
}
|
85
|
+
app = @cache.middleware.new(app)
|
86
|
+
app.call({})
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
gem 'minitest' # make sure we get the gem, not stdlib
|
2
|
+
require 'minitest'
|
3
|
+
require "minitest/autorun"
|
4
|
+
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/inflector'
|
7
|
+
require 'mocha/setup' # FIXME: stop using mocha
|
8
|
+
|
9
|
+
require 'cache_store_behavior'
|
10
|
+
require 'local_cache_behavior'
|
11
|
+
require 'cache_increment_decrement_behavior'
|
12
|
+
require 'encoded_key_cache_behavior'
|
13
|
+
require 'autoloading_cache_behavior'
|
14
|
+
|
15
|
+
class SpymemcachedStoreTest < ::Minitest::Test
|
16
|
+
|
17
|
+
def setup
|
18
|
+
@cache = @@cache ||= ActiveSupport::Cache.lookup_store(:spymemcached_store, :expires_in => 60)
|
19
|
+
@peek = @@peek ||= ActiveSupport::Cache.lookup_store(:spymemcached_store)
|
20
|
+
@data = @cache.instance_variable_get(:@client)
|
21
|
+
@cache.clear
|
22
|
+
@cache.silence!
|
23
|
+
@cache.logger = ActiveSupport::Logger.new("/dev/null")
|
24
|
+
end
|
25
|
+
|
26
|
+
include CacheStoreBehavior
|
27
|
+
include LocalCacheBehavior
|
28
|
+
include CacheIncrementDecrementBehavior
|
29
|
+
include EncodedKeyCacheBehavior
|
30
|
+
include AutoloadingCacheBehavior
|
31
|
+
|
32
|
+
def test_raw_values
|
33
|
+
cache = @peek
|
34
|
+
cache.write("foo", 2)
|
35
|
+
#spymemcached returns right type
|
36
|
+
assert_equal 2, cache.read("foo")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_raw_values_with_marshal
|
40
|
+
cache = ActiveSupport::Cache.lookup_store(:spymemcached_store, :raw => true)
|
41
|
+
cache.clear
|
42
|
+
cache.write("foo", Marshal.dump([]))
|
43
|
+
assert_equal [], cache.read("foo")
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_local_cache_raw_values
|
47
|
+
cache = ActiveSupport::Cache.lookup_store(:spymemcached_store, :raw => true)
|
48
|
+
cache.clear
|
49
|
+
cache.with_local_cache do
|
50
|
+
cache.write("foo", '2')
|
51
|
+
assert_equal "2", cache.read("foo")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_local_cache_raw_values_with_marshal
|
56
|
+
cache = ActiveSupport::Cache.lookup_store(:spymemcached_store, :raw => true)
|
57
|
+
cache.clear
|
58
|
+
cache.with_local_cache do
|
59
|
+
cache.write("foo", Marshal.dump([]))
|
60
|
+
assert_equal [], cache.read("foo")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_read_should_return_a_different_object_id_each_time_it_is_called
|
65
|
+
cache = ActiveSupport::Cache.lookup_store(:spymemcached_store, :raw => true)
|
66
|
+
cache.write('foo', 'bar')
|
67
|
+
value = cache.read('foo')
|
68
|
+
assert_not_equal value.object_id, cache.read('foo').object_id
|
69
|
+
value << 'bingo'
|
70
|
+
assert_not_equal value, cache.read('foo')
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_namespace
|
74
|
+
cache = ActiveSupport::Cache.lookup_store(:spymemcached_store, :namespace => "abc")
|
75
|
+
cache.write('foo', 'bar')
|
76
|
+
assert_equal 'bar', cache.read('foo')
|
77
|
+
assert_nil @cache.read('foo')
|
78
|
+
assert_equal 'bar', @cache.read('abc:foo')
|
79
|
+
end
|
80
|
+
|
81
|
+
def assert_not_equal(expected, result)
|
82
|
+
assert expected != result, "expect #{expected.inspect} not equal #{result.inspect}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def assert_nothing_raised(&block)
|
86
|
+
block.call
|
87
|
+
rescue => e
|
88
|
+
fail("expected nothing raised, but caught #{e.class}: #{e.message}")
|
89
|
+
end
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spymemcached_store
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: java
|
6
|
+
authors:
|
7
|
+
- Xiao Li
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: spymemcached.jruby
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '0'
|
25
|
+
prerelease: false
|
26
|
+
type: :runtime
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '1.7'
|
39
|
+
prerelease: false
|
40
|
+
type: :development
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '10.0'
|
53
|
+
prerelease: false
|
54
|
+
type: :development
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
prerelease: false
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
prerelease: false
|
82
|
+
type: :development
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
prerelease: false
|
96
|
+
type: :development
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: minitest
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5.1'
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ~>
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '5.1'
|
109
|
+
prerelease: false
|
110
|
+
type: :development
|
111
|
+
description: Rails 3 & 4 cache store for spymemcached.jruby.
|
112
|
+
email:
|
113
|
+
- swing1979@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .ruby-version
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- lib/active_support/cache/spymemcached_store.rb
|
125
|
+
- lib/spymemcached_store.rb
|
126
|
+
- lib/spymemcached_store/version.rb
|
127
|
+
- spymemcached_store.gemspec
|
128
|
+
- test/autoloading_cache_behavior.rb
|
129
|
+
- test/cache_increment_decrement_behavior.rb
|
130
|
+
- test/cache_store_behavior.rb
|
131
|
+
- test/dependencies_test_helpers.rb
|
132
|
+
- test/encoded_key_cache_behavior.rb
|
133
|
+
- test/local_cache_behavior.rb
|
134
|
+
- test/spymemcached_store_test.rb
|
135
|
+
homepage: https://github.com/ThoughtWorksStudios/spymemcached_store
|
136
|
+
licenses:
|
137
|
+
- MIT
|
138
|
+
metadata: {}
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.1.9
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Rails 3 & 4 cache store for spymemcached.jruby.
|
159
|
+
test_files:
|
160
|
+
- test/autoloading_cache_behavior.rb
|
161
|
+
- test/cache_increment_decrement_behavior.rb
|
162
|
+
- test/cache_store_behavior.rb
|
163
|
+
- test/dependencies_test_helpers.rb
|
164
|
+
- test/encoded_key_cache_behavior.rb
|
165
|
+
- test/local_cache_behavior.rb
|
166
|
+
- test/spymemcached_store_test.rb
|