dalli 2.0.3 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- data/History.md +10 -0
- data/README.md +6 -31
- data/lib/action_dispatch/middleware/session/dalli_store.rb +5 -0
- data/lib/active_support/cache/dalli_store.rb +29 -5
- data/lib/dalli/server.rb +18 -5
- data/lib/dalli/socket.rb +2 -0
- data/lib/dalli/version.rb +1 -1
- data/test/test_active_support.rb +65 -5
- data/test/test_dalli.rb +14 -0
- data/test/test_failover.rb +22 -1
- data/test/test_session_store.rb +38 -0
- metadata +8 -8
data/History.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
Dalli Changelog
|
2
2
|
=====================
|
3
3
|
|
4
|
+
2.0.4
|
5
|
+
=======
|
6
|
+
|
7
|
+
- Dalli no longer needs to be reset after Unicorn/Passenger fork [#208]
|
8
|
+
- Add option to re-raise errors rescued in the session and cache stores. [pitr, #200]
|
9
|
+
- DalliStore#fetch called the block if the cached value == false [#205]
|
10
|
+
- DalliStore should have accessible options [#195]
|
11
|
+
- Add silence and mute support for DalliStore [#207]
|
12
|
+
- Tracked down and fixed socket corruption due to Timeout [#146]
|
13
|
+
|
4
14
|
2.0.3
|
5
15
|
=======
|
6
16
|
|
data/README.md
CHANGED
@@ -86,38 +86,9 @@ To use Dalli for Rails session storage, in `config/initializers/session_store.rb
|
|
86
86
|
require 'action_dispatch/middleware/session/dalli_store'
|
87
87
|
Rails.application.config.session_store :dalli_store, :memcache_server => ['host1', 'host2'], :namespace => 'sessions', :key => '_foundation_session', :expire_after => 30.minutes
|
88
88
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
Usage with Passenger
|
93
|
-
------------------------
|
94
|
-
|
95
|
-
Put this at the bottom of `config/environment.rb`:
|
96
|
-
|
97
|
-
if defined?(PhusionPassenger)
|
98
|
-
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
99
|
-
# Reset Rails's object cache
|
100
|
-
# Only works with DalliStore
|
101
|
-
Rails.cache.reset if forked
|
89
|
+
Both cache and session stores support `:raise_errors` parameter, which propagates exceptions (e.g. if all memcache servers are down) instead of silently hiding errors.
|
102
90
|
|
103
|
-
|
104
|
-
# If you know a cleaner way to find the session store instance, please let me know
|
105
|
-
ObjectSpace.each_object(ActionDispatch::Session::DalliStore) { |obj| obj.reset }
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
Usage with Unicorn
|
110
|
-
-----------------------
|
111
|
-
|
112
|
-
Modify the `after_fork` block in your unicorn config file:
|
113
|
-
|
114
|
-
after\_fork do |server, worker|
|
115
|
-
Rails.cache.reset if Rails.cache.respond_to?(:reset)
|
116
|
-
|
117
|
-
# Reset Rails's session store
|
118
|
-
# If you know a cleaner way to find the session store instance, please let me know
|
119
|
-
ObjectSpace.each_object(ActionDispatch::Session::DalliStore) { |obj| obj.reset }
|
120
|
-
end
|
91
|
+
Dalli does not support Rails 2.x any longer.
|
121
92
|
|
122
93
|
|
123
94
|
Configuration
|
@@ -151,6 +122,10 @@ Features and Changes
|
|
151
122
|
|
152
123
|
By default, Dalli is thread-safe. Disable thread-safety at your own peril.
|
153
124
|
|
125
|
+
Dalli does not need anything special in Unicorn/Passenger since 2.0.4.
|
126
|
+
It will detect sockets shared with child processes and gracefully reopen the
|
127
|
+
socket.
|
128
|
+
|
154
129
|
Note that Dalli does not require ActiveSupport or Rails. You can safely use it in your own Ruby projects.
|
155
130
|
|
156
131
|
|
@@ -20,6 +20,8 @@ module ActionDispatch
|
|
20
20
|
end
|
21
21
|
@namespace = @default_options[:namespace]
|
22
22
|
|
23
|
+
@raise_errors = !!@default_options[:raise_errors]
|
24
|
+
|
23
25
|
super
|
24
26
|
end
|
25
27
|
|
@@ -49,6 +51,7 @@ module ActionDispatch
|
|
49
51
|
sid
|
50
52
|
rescue Dalli::DalliError
|
51
53
|
Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
|
54
|
+
raise if @raise_errors
|
52
55
|
false
|
53
56
|
end
|
54
57
|
|
@@ -57,6 +60,7 @@ module ActionDispatch
|
|
57
60
|
@pool.delete(session_id)
|
58
61
|
rescue Dalli::DalliError
|
59
62
|
Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
|
63
|
+
raise if @raise_errors
|
60
64
|
end
|
61
65
|
return nil if options[:drop]
|
62
66
|
generate_sid
|
@@ -68,6 +72,7 @@ module ActionDispatch
|
|
68
72
|
end
|
69
73
|
rescue Dalli::DalliError
|
70
74
|
Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
|
75
|
+
raise if @raise_errors
|
71
76
|
false
|
72
77
|
end
|
73
78
|
|
@@ -6,6 +6,23 @@ module ActiveSupport
|
|
6
6
|
module Cache
|
7
7
|
class DalliStore
|
8
8
|
|
9
|
+
attr_reader :silence, :options
|
10
|
+
alias_method :silence?, :silence
|
11
|
+
|
12
|
+
# Silence the logger.
|
13
|
+
def silence!
|
14
|
+
@silence = true
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Silence the logger within a block.
|
19
|
+
def mute
|
20
|
+
previous_silence, @silence = defined?(@silence) && @silence, true
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
@silence = previous_silence
|
24
|
+
end
|
25
|
+
|
9
26
|
ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
|
10
27
|
|
11
28
|
# Creates a new DalliStore object, with the given memcached server
|
@@ -20,9 +37,11 @@ module ActiveSupport
|
|
20
37
|
def initialize(*addresses)
|
21
38
|
addresses = addresses.flatten
|
22
39
|
options = addresses.extract_options!
|
23
|
-
options
|
40
|
+
@options = options.dup
|
41
|
+
@options[:compress] ||= @options[:compression]
|
42
|
+
@raise_errors = !!@options[:raise_errors]
|
24
43
|
addresses << 'localhost:11211' if addresses.empty?
|
25
|
-
@data = Dalli::Client.new(addresses, options)
|
44
|
+
@data = Dalli::Client.new(addresses, @options)
|
26
45
|
end
|
27
46
|
|
28
47
|
def fetch(name, options=nil)
|
@@ -35,7 +54,7 @@ module ActiveSupport
|
|
35
54
|
end
|
36
55
|
end
|
37
56
|
|
38
|
-
if entry
|
57
|
+
if !entry.nil?
|
39
58
|
instrument(:fetch_hit, name, options) { |payload| }
|
40
59
|
entry
|
41
60
|
else
|
@@ -68,7 +87,7 @@ module ActiveSupport
|
|
68
87
|
|
69
88
|
def exist?(name, options=nil)
|
70
89
|
options ||= {}
|
71
|
-
|
90
|
+
!read_entry(name, options).nil?
|
72
91
|
end
|
73
92
|
|
74
93
|
def delete(name, options=nil)
|
@@ -107,6 +126,7 @@ module ActiveSupport
|
|
107
126
|
end
|
108
127
|
rescue Dalli::DalliError => e
|
109
128
|
logger.error("DalliError: #{e.message}") if logger
|
129
|
+
raise if @raise_errors
|
110
130
|
nil
|
111
131
|
end
|
112
132
|
|
@@ -124,6 +144,7 @@ module ActiveSupport
|
|
124
144
|
end
|
125
145
|
rescue Dalli::DalliError => e
|
126
146
|
logger.error("DalliError: #{e.message}") if logger
|
147
|
+
raise if @raise_errors
|
127
148
|
nil
|
128
149
|
end
|
129
150
|
|
@@ -151,6 +172,7 @@ module ActiveSupport
|
|
151
172
|
entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
|
152
173
|
rescue Dalli::DalliError => e
|
153
174
|
logger.error("DalliError: #{e.message}") if logger
|
175
|
+
raise if @raise_errors
|
154
176
|
nil
|
155
177
|
end
|
156
178
|
|
@@ -161,6 +183,7 @@ module ActiveSupport
|
|
161
183
|
@data.send(method, escape(key), value, expires_in, options)
|
162
184
|
rescue Dalli::DalliError => e
|
163
185
|
logger.error("DalliError: #{e.message}") if logger
|
186
|
+
raise if @raise_errors
|
164
187
|
false
|
165
188
|
end
|
166
189
|
|
@@ -169,6 +192,7 @@ module ActiveSupport
|
|
169
192
|
@data.delete(escape(key))
|
170
193
|
rescue Dalli::DalliError => e
|
171
194
|
logger.error("DalliError: #{e.message}") if logger
|
195
|
+
raise if @raise_errors
|
172
196
|
false
|
173
197
|
end
|
174
198
|
|
@@ -195,7 +219,7 @@ module ActiveSupport
|
|
195
219
|
end
|
196
220
|
|
197
221
|
def log(operation, key, options=nil)
|
198
|
-
return unless logger && logger.debug?
|
222
|
+
return unless logger && logger.debug? && !silence?
|
199
223
|
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
|
200
224
|
end
|
201
225
|
|
data/lib/dalli/server.rb
CHANGED
@@ -37,10 +37,12 @@ module Dalli
|
|
37
37
|
@options = DEFAULTS.merge(options)
|
38
38
|
@sock = nil
|
39
39
|
@msg = nil
|
40
|
+
@pid = nil
|
40
41
|
end
|
41
42
|
|
42
43
|
# Chokepoint method for instrumentation
|
43
44
|
def request(op, *args)
|
45
|
+
verify_state
|
44
46
|
raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}" unless alive?
|
45
47
|
begin
|
46
48
|
send(op, *args)
|
@@ -80,6 +82,8 @@ module Dalli
|
|
80
82
|
return unless @sock
|
81
83
|
@sock.close rescue nil
|
82
84
|
@sock = nil
|
85
|
+
@pid = nil
|
86
|
+
@inprogress = false
|
83
87
|
end
|
84
88
|
|
85
89
|
def lock!
|
@@ -92,6 +96,11 @@ module Dalli
|
|
92
96
|
|
93
97
|
private
|
94
98
|
|
99
|
+
def verify_state
|
100
|
+
failure! if @inprogress
|
101
|
+
failure! if @pid && @pid != Process.pid
|
102
|
+
end
|
103
|
+
|
95
104
|
def failure!
|
96
105
|
Dalli.logger.info { "#{hostname}:#{port} failed (count: #{@fail_count})" }
|
97
106
|
|
@@ -368,19 +377,23 @@ module Dalli
|
|
368
377
|
|
369
378
|
def write(bytes)
|
370
379
|
begin
|
371
|
-
@
|
380
|
+
@inprogress = true
|
381
|
+
result = @sock.write(bytes)
|
382
|
+
@inprogress = false
|
383
|
+
result
|
372
384
|
rescue SystemCallError, Timeout::Error
|
373
385
|
failure!
|
374
|
-
retry
|
375
386
|
end
|
376
387
|
end
|
377
388
|
|
378
389
|
def read(count)
|
379
390
|
begin
|
380
|
-
@
|
391
|
+
@inprogress = true
|
392
|
+
data = @sock.readfull(count)
|
393
|
+
@inprogress = false
|
394
|
+
data
|
381
395
|
rescue SystemCallError, Timeout::Error, EOFError
|
382
396
|
failure!
|
383
|
-
retry
|
384
397
|
end
|
385
398
|
end
|
386
399
|
|
@@ -388,6 +401,7 @@ module Dalli
|
|
388
401
|
Dalli.logger.debug { "Dalli::Server#connect #{hostname}:#{port}" }
|
389
402
|
|
390
403
|
begin
|
404
|
+
@pid = Process.pid
|
391
405
|
@sock = KSocket.open(hostname, port, options)
|
392
406
|
@version = version # trigger actual connect
|
393
407
|
sasl_authentication if need_auth?
|
@@ -397,7 +411,6 @@ module Dalli
|
|
397
411
|
rescue SystemCallError, Timeout::Error, EOFError, SocketError
|
398
412
|
# SocketError = DNS resolution failure
|
399
413
|
failure!
|
400
|
-
retry
|
401
414
|
end
|
402
415
|
end
|
403
416
|
|
data/lib/dalli/socket.rb
CHANGED
@@ -16,6 +16,7 @@ begin
|
|
16
16
|
def self.open(host, port, options = {})
|
17
17
|
addr = Socket.pack_sockaddr_in(port, host)
|
18
18
|
sock = start(addr)
|
19
|
+
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
19
20
|
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if options[:keepalive]
|
20
21
|
sock.options = options
|
21
22
|
sock.kgio_wait_writable
|
@@ -49,6 +50,7 @@ rescue LoadError
|
|
49
50
|
def self.open(host, port, options = {})
|
50
51
|
Timeout.timeout(options[:socket_timeout]) do
|
51
52
|
sock = new(host, port)
|
53
|
+
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
52
54
|
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if options[:keepalive]
|
53
55
|
sock.options = { :host => host, :port => port }.merge(options)
|
54
56
|
sock
|
data/lib/dalli/version.rb
CHANGED
data/test/test_active_support.rb
CHANGED
@@ -4,7 +4,23 @@ require 'helper'
|
|
4
4
|
describe 'ActiveSupport' do
|
5
5
|
context 'active_support caching' do
|
6
6
|
|
7
|
-
should '
|
7
|
+
should 'have accessible options' do
|
8
|
+
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:19122', :expires_in => 5.minutes, :frob => 'baz')
|
9
|
+
assert_equal 'baz', @dalli.options[:frob]
|
10
|
+
end
|
11
|
+
|
12
|
+
should 'allow mute and silence' do
|
13
|
+
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:19122')
|
14
|
+
@dalli.mute do
|
15
|
+
assert_equal true, @dalli.write('foo', 'bar', nil)
|
16
|
+
assert_equal 'bar', @dalli.read('foo', nil)
|
17
|
+
end
|
18
|
+
refute @dalli.silence?
|
19
|
+
@dalli.silence!
|
20
|
+
assert_equal true, @dalli.silence?
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'handle nil options' do
|
8
24
|
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:19122')
|
9
25
|
assert_equal true, @dalli.write('foo', 'bar', nil)
|
10
26
|
assert_equal 'bar', @dalli.read('foo', nil)
|
@@ -30,6 +46,10 @@ describe 'ActiveSupport' do
|
|
30
46
|
|
31
47
|
dvalue = @dalli.fetch(rand_key) { o }
|
32
48
|
assert_equal o, dvalue
|
49
|
+
|
50
|
+
@dalli.write('false', false)
|
51
|
+
dvalue = @dalli.fetch('false') { flunk }
|
52
|
+
assert_equal false, dvalue
|
33
53
|
end
|
34
54
|
end
|
35
55
|
end
|
@@ -133,6 +153,21 @@ describe 'ActiveSupport' do
|
|
133
153
|
end
|
134
154
|
end
|
135
155
|
|
156
|
+
should 'support exist command' do
|
157
|
+
with_activesupport do
|
158
|
+
memcached do
|
159
|
+
connect
|
160
|
+
@dalli.write(:foo, 'a')
|
161
|
+
@dalli.write(:false_value, false)
|
162
|
+
|
163
|
+
assert_equal true, @dalli.exist?(:foo)
|
164
|
+
assert_equal true, @dalli.exist?(:false_value)
|
165
|
+
|
166
|
+
assert_equal false, @dalli.exist?(:bar)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
136
171
|
should 'support other esoteric commands' do
|
137
172
|
with_activesupport do
|
138
173
|
memcached do
|
@@ -141,14 +176,39 @@ describe 'ActiveSupport' do
|
|
141
176
|
assert_equal 1, ds.keys.size
|
142
177
|
assert ds[ds.keys.first].keys.size > 0
|
143
178
|
|
144
|
-
assert_equal true, @dalli.write(:foo, 'a')
|
145
|
-
assert_equal true, @dalli.exist?(:foo)
|
146
|
-
assert_equal false, @dalli.exist?(:bar)
|
147
|
-
|
148
179
|
@dalli.reset
|
149
180
|
end
|
150
181
|
end
|
151
182
|
end
|
183
|
+
|
184
|
+
should 'respect "raise_errors" option' do
|
185
|
+
with_activesupport do
|
186
|
+
memcached(29125) do
|
187
|
+
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:29125')
|
188
|
+
@dalli.write 'foo', 'bar'
|
189
|
+
assert_equal @dalli.read('foo'), 'bar'
|
190
|
+
|
191
|
+
memcached_kill(29125)
|
192
|
+
|
193
|
+
assert_equal @dalli.read('foo'), nil
|
194
|
+
|
195
|
+
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:29125', :raise_errors => true)
|
196
|
+
|
197
|
+
exception = [Dalli::RingError, { :message => "No server available" }]
|
198
|
+
|
199
|
+
assert_raises(*exception) { @dalli.read 'foo' }
|
200
|
+
assert_raises(*exception) { @dalli.read 'foo', :raw => true }
|
201
|
+
assert_raises(*exception) { @dalli.write 'foo', 'bar' }
|
202
|
+
assert_raises(*exception) { @dalli.exist? 'foo' }
|
203
|
+
assert_raises(*exception) { @dalli.increment 'foo' }
|
204
|
+
assert_raises(*exception) { @dalli.decrement 'foo' }
|
205
|
+
assert_raises(*exception) { @dalli.delete 'foo' }
|
206
|
+
assert_equal @dalli.read_multi('foo', 'bar'), {}
|
207
|
+
assert_raises(*exception) { @dalli.delete 'foo' }
|
208
|
+
assert_raises(*exception) { @dalli.fetch('foo') { 42 } }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
152
212
|
end
|
153
213
|
|
154
214
|
should 'handle crazy characters from far-away lands' do
|
data/test/test_dalli.rb
CHANGED
@@ -127,6 +127,20 @@ describe 'Dalli' do
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
+
should "support the fetch operation with falsey values" do
|
131
|
+
memcached do |dc|
|
132
|
+
dc.flush
|
133
|
+
|
134
|
+
dc.set("fetch_key", false)
|
135
|
+
res = dc.fetch("fetch_key") { flunk "fetch block called" }
|
136
|
+
assert_equal false, res
|
137
|
+
|
138
|
+
dc.set("fetch_key", nil)
|
139
|
+
res = dc.fetch("fetch_key") { "bob" }
|
140
|
+
assert_equal 'bob', res
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
130
144
|
should "support the cas operation" do
|
131
145
|
memcached do |dc|
|
132
146
|
dc.flush
|
data/test/test_failover.rb
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'failover' do
|
4
|
+
|
5
|
+
describe 'timeouts' do
|
6
|
+
should 'not lead to corrupt sockets' do
|
7
|
+
memcached(29125) do
|
8
|
+
dc = Dalli::Client.new ['localhost:29125']
|
9
|
+
begin
|
10
|
+
Timeout.timeout 0.01 do
|
11
|
+
1_000.times do
|
12
|
+
dc.set("test_123", {:test => "123"})
|
13
|
+
end
|
14
|
+
flunk("Did not timeout")
|
15
|
+
end
|
16
|
+
rescue Timeout::Error => e
|
17
|
+
end
|
18
|
+
|
19
|
+
assert_equal({:test => '123'}, dc.get("test_123"))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
4
25
|
context 'assuming some bad servers' do
|
5
26
|
|
6
27
|
should 'silently reconnect if server hiccups' do
|
data/test/test_session_store.rb
CHANGED
@@ -12,6 +12,8 @@ class Foo
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class TestSessionStore < ActionController::IntegrationTest
|
15
|
+
include MemcachedMock::Helper
|
16
|
+
|
15
17
|
class TestController < ActionController::Base
|
16
18
|
def no_session_access
|
17
19
|
head :ok
|
@@ -201,6 +203,42 @@ class TestSessionStore < ActionController::IntegrationTest
|
|
201
203
|
assert_equal 'foo: nil', response.body
|
202
204
|
end
|
203
205
|
end
|
206
|
+
|
207
|
+
def test_without_raise_errors_option
|
208
|
+
memcached(29125) do
|
209
|
+
with_test_route_set(:memcache_server => '127.0.0.1:29125') do
|
210
|
+
get '/set_session_value'
|
211
|
+
assert_response :success
|
212
|
+
|
213
|
+
get '/get_session_value'
|
214
|
+
assert_response :success
|
215
|
+
assert_equal 'foo: "bar"', response.body
|
216
|
+
|
217
|
+
memcached_kill(29125)
|
218
|
+
|
219
|
+
get '/get_session_value'
|
220
|
+
assert_response :success
|
221
|
+
assert_equal 'foo: nil', response.body
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_with_raise_errors_option
|
227
|
+
memcached(29125) do
|
228
|
+
with_test_route_set(:memcache_server => '127.0.0.1:29125', :raise_errors => true) do
|
229
|
+
get '/set_session_value'
|
230
|
+
assert_response :success
|
231
|
+
|
232
|
+
memcached_kill(29125)
|
233
|
+
|
234
|
+
exception = [Dalli::RingError, { :message => "No server available" }]
|
235
|
+
|
236
|
+
assert_raises(*exception) { get '/get_session_value' }
|
237
|
+
assert_raises(*exception) { get '/set_session_value' }
|
238
|
+
assert_raises(*exception) { get '/call_reset_session' }
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
204
242
|
rescue LoadError, RuntimeError
|
205
243
|
$stderr.puts "Skipping SessionStore tests. Start memcached and try again: #{$!.message}"
|
206
244
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dalli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mini_shoulda
|
16
|
-
requirement: &
|
16
|
+
requirement: &70263471981140 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70263471981140
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: mocha
|
27
|
-
requirement: &
|
27
|
+
requirement: &70263471980400 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70263471980400
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rails
|
38
|
-
requirement: &
|
38
|
+
requirement: &70263471979400 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '3'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70263471979400
|
47
47
|
description: High performance memcached client for Ruby
|
48
48
|
email: mperham@gmail.com
|
49
49
|
executables: []
|