dalli 2.3.0 → 2.5.0

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 CHANGED
@@ -1,6 +1,21 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
+ 2.5.0
5
+ =======
6
+
7
+ - Don't escape non-ASCII keys, memcached binary protocol doesn't care. [#257]
8
+ - :dalli_store now implements LocalCache [#236]
9
+ - Removed lots of old session_store test code, tests now all run without a default memcached server [#275]
10
+ - Changed Dalli ActiveSupport adapter to always attempt instrumentation [brianmario, #284]
11
+ - Change write operations (add/set/replace) to return false when value is too large to store [brianmario, #283]
12
+
13
+ 2.4.0
14
+ =======
15
+ - Added the ability to swap out the compressed used to [de]compress cache data [brianmario, #276]
16
+ - Fix get\_multi performance issues with lots of memcached servers [tmm1]
17
+ - Throw more specific exceptions [tmm1]
18
+
4
19
  2.3.0
5
20
  =======
6
21
  - Added the ability to swap out the serializer used to [de]serialize cache data [brianmario, #274]
data/README.md CHANGED
@@ -108,6 +108,10 @@ Dalli::Client accepts the following options. All times are in seconds.
108
108
 
109
109
  **compress**: Boolean, if true Dalli will gzip-compress values larger than 1K.
110
110
 
111
+ **compression_min_size**: Minimum value byte size for which to attempt compression. Default is 1K.
112
+
113
+ **compression_max_size**: Maximum value byte size for which to attempt compression. Default is unlimited.
114
+
111
115
  **socket_timeout**: Timeout for all socket operations (connect, read, write). Default is 0.5.
112
116
 
113
117
  **socket_max_failures**: When a socket operation fails after socket_timeout, the same operation is retried. This is to not immediately mark a server down when there's a very slight network problem. Default is 2.
@@ -45,6 +45,8 @@ module ActiveSupport
45
45
  addresses
46
46
  end
47
47
  @data = Dalli::Client.new(servers, @options)
48
+
49
+ extend Strategy::LocalCache
48
50
  end
49
51
 
50
52
  def fetch(name, options=nil)
@@ -106,8 +108,9 @@ module ActiveSupport
106
108
  options ||= {}
107
109
  name = expanded_key name
108
110
 
109
- log(:delete, name, options)
110
- delete_entry(name, options)
111
+ instrument(:delete, name, options) do |payload|
112
+ delete_entry(name, options)
113
+ end
111
114
  end
112
115
 
113
116
  # Reads multiple keys from the cache using a single call to the
@@ -251,20 +254,15 @@ module ActiveSupport
251
254
  def escape(key)
252
255
  key = key.to_s.dup
253
256
  key = key.force_encoding("BINARY") if key.respond_to?(:encode)
254
- key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
255
257
  key
256
258
  end
257
259
 
258
260
  def instrument(operation, key, options=nil)
259
261
  log(operation, key, options)
260
262
 
261
- if ActiveSupport::Cache::Store.instrument
262
- payload = { :key => key }
263
- payload.merge!(options) if options.is_a?(Hash)
264
- ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
265
- else
266
- yield(nil)
267
- end
263
+ payload = { :key => key }
264
+ payload.merge!(options) if options.is_a?(Hash)
265
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
268
266
  end
269
267
 
270
268
  def log(operation, key, options=nil)
@@ -4,6 +4,7 @@ require 'dalli/server'
4
4
  require 'dalli/socket'
5
5
  require 'dalli/version'
6
6
  require 'dalli/options'
7
+ require 'dalli/compressor'
7
8
  require 'dalli/railtie' if defined?(::Rails::Railtie)
8
9
 
9
10
  module Dalli
@@ -13,8 +14,10 @@ module Dalli
13
14
  class NetworkError < DalliError; end
14
15
  # no server available/alive error
15
16
  class RingError < DalliError; end
16
- # application error in marshalling
17
+ # application error in marshalling serialization
17
18
  class MarshalError < DalliError; end
19
+ # application error in marshalling deserialization or decompression
20
+ class UnmarshalError < DalliError; end
18
21
 
19
22
  def self.logger
20
23
  @logger ||= (rails_logger || default_logger)
@@ -46,6 +49,17 @@ module Dalli
46
49
  def self.serializer=(serializer)
47
50
  @serializer = serializer
48
51
  end
52
+
53
+ # Default serialization to Dalli::Compressor
54
+ @compressor = Compressor
55
+
56
+ def self.compressor
57
+ @compressor
58
+ end
59
+
60
+ def self.compressor=(compressor)
61
+ @compressor = compressor
62
+ end
49
63
  end
50
64
 
51
65
  if defined?(RAILS_VERSION) && RAILS_VERSION < '3'
@@ -1,4 +1,5 @@
1
1
  require 'digest/md5'
2
+ require 'set'
2
3
 
3
4
  # encoding: ascii
4
5
  module Dalli
@@ -20,13 +21,14 @@ module Dalli
20
21
  # - :failover - if a server is down, look for and store values on another server in the ring. Default: true.
21
22
  # - :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
22
23
  # - :expires_in - default TTL in seconds if you do not pass TTL as a parameter to an individual operation, defaults to 0 or forever
23
- # - :compress - defaults to false, if true Dalli will compress values larger than 100 bytes before
24
+ # - :compress - defaults to false, if true Dalli will compress values larger than 1024 bytes before
24
25
  # sending them to memcached.
25
26
  #
26
27
  def initialize(servers=nil, options={})
27
28
  @servers = servers || env_servers || '127.0.0.1:11211'
28
29
  @options = normalize_options(options)
29
30
  @ring = nil
31
+ @servers_in_use = nil
30
32
  end
31
33
 
32
34
  #
@@ -58,29 +60,33 @@ module Dalli
58
60
  options = nil
59
61
  options = keys.pop if keys.last.is_a?(Hash) || keys.last.nil?
60
62
  ring.lock do
63
+ self.servers_in_use = Set.new
64
+
61
65
  keys.flatten.each do |key|
62
66
  begin
63
67
  perform(:getkq, key)
64
68
  rescue DalliError, NetworkError => e
65
- Dalli.logger.debug { e.message }
69
+ Dalli.logger.debug { e.inspect }
66
70
  Dalli.logger.debug { "unable to get key #{key}" }
67
71
  end
68
72
  end
69
73
 
70
74
  values = {}
71
- ring.servers.each do |server|
75
+ servers_in_use.each do |server|
72
76
  next unless server.alive?
73
77
  begin
74
78
  server.request(:noop).each_pair do |key, value|
75
79
  values[key_without_namespace(key)] = value
76
80
  end
77
81
  rescue DalliError, NetworkError => e
78
- Dalli.logger.debug { e.message }
82
+ Dalli.logger.debug { e.inspect }
79
83
  Dalli.logger.debug { "results from this server will be missing" }
80
84
  end
81
85
  end
82
86
  values
83
87
  end
88
+ ensure
89
+ self.servers_in_use = nil
84
90
  end
85
91
 
86
92
  def fetch(key, ttl=nil, options=nil)
@@ -264,14 +270,24 @@ module Dalli
264
270
  key = validate_key(key)
265
271
  begin
266
272
  server = ring.server_for_key(key)
267
- server.request(op, key, *args)
273
+ ret = server.request(op, key, *args)
274
+ servers_in_use << server if servers_in_use
275
+ ret
268
276
  rescue NetworkError => e
269
- Dalli.logger.debug { e.message }
277
+ Dalli.logger.debug { e.inspect }
270
278
  Dalli.logger.debug { "retrying request with new server" }
271
279
  retry
272
280
  end
273
281
  end
274
282
 
283
+ def servers_in_use
284
+ Thread.current[:"#{object_id}-servers"]
285
+ end
286
+
287
+ def servers_in_use=(value)
288
+ Thread.current[:"#{object_id}-servers"] = value
289
+ end
290
+
275
291
  def validate_key(key)
276
292
  raise ArgumentError, "key cannot be blank" if !key || key.length == 0
277
293
  key = key_with_namespace(key)
@@ -0,0 +1,13 @@
1
+ require 'zlib'
2
+
3
+ module Dalli
4
+ class Compressor
5
+ def self.compress(data)
6
+ Zlib::Deflate.deflate(data)
7
+ end
8
+
9
+ def self.decompress(data)
10
+ Zlib::Inflate.inflate(data)
11
+ end
12
+ end
13
+ end
@@ -1,6 +1,5 @@
1
1
  require 'socket'
2
2
  require 'timeout'
3
- require 'zlib'
4
3
 
5
4
  module Dalli
6
5
  class Server
@@ -20,6 +19,10 @@ module Dalli
20
19
  :socket_failure_delay => 0.01,
21
20
  # max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
22
21
  :value_max_bytes => 1024 * 1024,
22
+ # min byte size to attempt compression
23
+ :compression_min_size => 1024,
24
+ # max byte size for compression
25
+ :compression_max_size => false,
23
26
  :username => nil,
24
27
  :password => nil,
25
28
  :keepalive => true
@@ -164,24 +167,37 @@ module Dalli
164
167
  def set(key, value, ttl, cas, options)
165
168
  (value, flags) = serialize(key, value, options)
166
169
 
167
- req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:set])
168
- write(req)
169
- generic_response unless multi?
170
+ if under_max_value_size?(value)
171
+ req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:set])
172
+ write(req)
173
+ generic_response unless multi?
174
+ else
175
+ false
176
+ end
170
177
  end
171
178
 
172
179
  def add(key, value, ttl, options)
173
180
  (value, flags) = serialize(key, value, options)
174
181
 
175
- req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:add])
176
- write(req)
177
- generic_response unless multi?
182
+ if under_max_value_size?(value)
183
+ req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:add])
184
+ write(req)
185
+ generic_response unless multi?
186
+ else
187
+ false
188
+ end
178
189
  end
179
190
 
180
191
  def replace(key, value, ttl, options)
181
192
  (value, flags) = serialize(key, value, options)
182
- req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:replace])
183
- write(req)
184
- generic_response unless multi?
193
+
194
+ if under_max_value_size?(value)
195
+ req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:replace])
196
+ write(req)
197
+ generic_response unless multi?
198
+ else
199
+ false
200
+ end
185
201
  end
186
202
 
187
203
  def delete(key)
@@ -268,8 +284,6 @@ module Dalli
268
284
  generic_response
269
285
  end
270
286
 
271
- COMPRESSION_MIN_SIZE = 1024
272
-
273
287
  # http://www.hjp.at/zettel/m/memcached_flags.rxml
274
288
  # Looks like most clients use bit 0 to indicate native language serialization
275
289
  # and bit 1 to indicate gzip compression.
@@ -293,11 +307,12 @@ module Dalli
293
307
  value.to_s
294
308
  end
295
309
  compressed = false
296
- if @options[:compress] && value.bytesize >= COMPRESSION_MIN_SIZE
297
- value = Zlib::Deflate.deflate(value)
310
+ if @options[:compress] && value.bytesize >= @options[:compression_min_size] &&
311
+ (!@options[:compression_max_size] || value.bytesize <= @options[:compression_max_size])
312
+ value = Dalli.compressor.compress(value)
298
313
  compressed = true
299
314
  end
300
- raise Dalli::DalliError, "Value too large, memcached can only store #{@options[:value_max_bytes]} bytes per key [key: #{key}, size: #{value.bytesize}]" if value.bytesize > @options[:value_max_bytes]
315
+
301
316
  flags = 0
302
317
  flags |= FLAG_COMPRESSED if compressed
303
318
  flags |= FLAG_SERIALIZED if marshalled
@@ -305,13 +320,17 @@ module Dalli
305
320
  end
306
321
 
307
322
  def deserialize(value, flags)
308
- value = Zlib::Inflate.inflate(value) if (flags & FLAG_COMPRESSED) != 0
323
+ value = Dalli.compressor.decompress(value) if (flags & FLAG_COMPRESSED) != 0
309
324
  value = Dalli.serializer.load(value) if (flags & FLAG_SERIALIZED) != 0
310
325
  value
311
- rescue TypeError, ArgumentError
312
- raise DalliError, "Unable to unmarshal value: #{$!.message}"
326
+ rescue TypeError
327
+ raise if $!.message !~ /needs to have method `_load'|exception class\/object expected|instance of IO needed|incompatible marshal file format/
328
+ raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
329
+ rescue ArgumentError
330
+ raise if $!.message !~ /undefined class|marshal data too short/
331
+ raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
313
332
  rescue Zlib::Error
314
- raise DalliError, "Unable to uncompress value: #{$!.message}"
333
+ raise UnmarshalError, "Unable to uncompress value: #{$!.message}"
315
334
  end
316
335
 
317
336
  def cas_response
@@ -335,6 +354,10 @@ module Dalli
335
354
  NORMAL_HEADER = '@4CCnN'
336
355
  KV_HEADER = '@2n@6nN'
337
356
 
357
+ def under_max_value_size?(value)
358
+ value.bytesize <= @options[:value_max_bytes]
359
+ end
360
+
338
361
  def generic_response(unpack=false)
339
362
  header = read(24)
340
363
  raise Dalli::NetworkError, 'No response' if !header
@@ -1,3 +1,3 @@
1
1
  module Dalli
2
- VERSION = '2.3.0'
2
+ VERSION = '2.5.0'
3
3
  end
@@ -2,17 +2,17 @@ $TESTING = true
2
2
  require 'rubygems'
3
3
  # require 'simplecov'
4
4
  # SimpleCov.start
5
+ require 'minitest/pride'
6
+ require 'minitest/autorun'
7
+ require 'mocha/setup'
8
+ require 'mini_shoulda'
9
+ require 'memcached_mock'
10
+
5
11
  WANT_RAILS_VERSION = ENV['RAILS_VERSION'] || '>= 3.0.0'
6
12
  gem 'rails', WANT_RAILS_VERSION
7
13
  require 'rails'
8
14
  puts "Testing with Rails #{Rails.version}"
9
15
 
10
- require 'mini_shoulda'
11
- require 'minitest/pride'
12
- require 'minitest/autorun'
13
- require 'memcached_mock'
14
- require 'mocha'
15
-
16
16
  require 'dalli'
17
17
  require 'logger'
18
18
 
@@ -138,6 +138,11 @@ describe 'ActiveSupport' do
138
138
 
139
139
  dres = @dalli.delete(user)
140
140
  assert_equal true, dres
141
+
142
+ bigkey = '123456789012345678901234567890'
143
+ @dalli.write(bigkey, 'double width')
144
+ assert_equal 'double width', @dalli.read(bigkey)
145
+ assert_equal({bigkey => "double width"}, @dalli.read_multi(bigkey))
141
146
  end
142
147
  end
143
148
  end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ require 'helper'
3
+ require 'json'
4
+ require 'memcached_mock'
5
+
6
+ class NoopCompressor
7
+ def self.compress(data)
8
+ data
9
+ end
10
+
11
+ def self.decompress(data)
12
+ data
13
+ end
14
+ end
15
+
16
+ describe 'Compressor' do
17
+
18
+ should 'default to Dalli::Compressor' do
19
+ assert_equal Dalli::Compressor, Dalli.compressor
20
+ end
21
+
22
+ should 'support a custom compressor' do
23
+ original_compressor = Dalli.compressor
24
+ begin
25
+ Dalli.compressor = NoopCompressor
26
+ assert_equal NoopCompressor, Dalli.compressor
27
+
28
+ memcached(19127) do |dc|
29
+ assert dc.set("string-test", "a test string")
30
+ assert_equal("a test string", dc.get("string-test"))
31
+ end
32
+ ensure
33
+ Dalli.compressor = original_compressor
34
+ end
35
+ end
36
+ end
@@ -27,18 +27,19 @@ describe 'Dalli' do
27
27
 
28
28
  describe 'key validation' do
29
29
  should 'not allow blanks' do
30
- dc = Dalli::Client.new
31
- dc.set ' ', 1
32
- assert_equal 1, dc.get(' ')
33
- dc.set "\t", 1
34
- assert_equal 1, dc.get("\t")
35
- dc.set "\n", 1
36
- assert_equal 1, dc.get("\n")
37
- assert_raises ArgumentError do
38
- dc.set "", 1
39
- end
40
- assert_raises ArgumentError do
41
- dc.set nil, 1
30
+ memcached do |dc|
31
+ dc.set ' ', 1
32
+ assert_equal 1, dc.get(' ')
33
+ dc.set "\t", 1
34
+ assert_equal 1, dc.get("\t")
35
+ dc.set "\n", 1
36
+ assert_equal 1, dc.get("\n")
37
+ assert_raises ArgumentError do
38
+ dc.set "", 1
39
+ end
40
+ assert_raises ArgumentError do
41
+ dc.set nil, 1
42
+ end
42
43
  end
43
44
  end
44
45
  end
@@ -73,11 +74,7 @@ describe 'Dalli' do
73
74
  dc.flush
74
75
 
75
76
  val1 = "1234567890"*105000
76
- assert_error Dalli::DalliError, /too large/ do
77
- dc.set('a', val1)
78
- val2 = dc.get('a')
79
- assert_equal val1, val2
80
- end
77
+ assert_equal false, dc.set('a', val1)
81
78
 
82
79
  val1 = "1234567890"*100000
83
80
  dc.set('a', val1)
@@ -275,7 +272,7 @@ describe 'Dalli' do
275
272
 
276
273
  # rollover the 64-bit value, we'll get something undefined.
277
274
  resp = dc.incr('big', 1)
278
- 0x10000000000000000.wont_equal resp
275
+ refute_equal 0x10000000000000000, resp
279
276
  dc.reset
280
277
  end
281
278
  end
@@ -327,7 +324,7 @@ describe 'Dalli' do
327
324
  should "pass a simple smoke test" do
328
325
  memcached do |dc|
329
326
  resp = dc.flush
330
- resp.wont_be_nil
327
+ refute_nil resp
331
328
  assert_equal [true, true], resp
332
329
 
333
330
  assert_equal true, dc.set(:foo, 'bar')
@@ -348,7 +345,7 @@ describe 'Dalli' do
348
345
  dc.prepend('123', '0')
349
346
  dc.append('123', '0')
350
347
 
351
- assert_raises Dalli::DalliError do
348
+ assert_raises Dalli::UnmarshalError do
352
349
  resp = dc.get('123')
353
350
  end
354
351
 
@@ -403,9 +400,10 @@ describe 'Dalli' do
403
400
  cache.set('b', 11)
404
401
  inc = cache.incr('cat', 10, 0, 10)
405
402
  cache.set('f', 'zzz')
406
- wont_be_nil(cache.cas('f') do |value|
403
+ res = cache.cas('f') do |value|
407
404
  value << 'z'
408
- end)
405
+ end
406
+ refute_nil res
409
407
  assert_equal false, cache.add('a', 11)
410
408
  assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
411
409
  inc = cache.incr('cat', 10)
@@ -469,9 +467,7 @@ describe 'Dalli' do
469
467
  dalli = Dalli::Client.new(dc.instance_variable_get(:@servers), :compress => true)
470
468
 
471
469
  value = "0"*1024*1024
472
- assert_raises Dalli::DalliError, /too large/ do
473
- dc.set('verylarge', value)
474
- end
470
+ assert_equal false, dc.set('verylarge', value)
475
471
  dalli.set('verylarge', value)
476
472
  end
477
473
  end
@@ -6,6 +6,13 @@ require 'rack/mock'
6
6
  require 'thread'
7
7
 
8
8
  describe Rack::Session::Dalli do
9
+ Rack::Session::Dalli::DEFAULT_OPTIONS[:memcache_server] = 'localhost:19129'
10
+
11
+ before do
12
+ memcached(19129) do
13
+ end
14
+ end
15
+
9
16
  session_key = Rack::Session::Dalli::DEFAULT_OPTIONS[:key]
10
17
  session_match = /#{session_key}=([0-9a-fA-F]+);/
11
18
  incrementor = lambda do |env|
@@ -15,7 +15,7 @@ describe 'Serializer' do
15
15
  Dalli.serializer = JSON
16
16
  assert_equal JSON, Dalli.serializer
17
17
 
18
- memcached(19127) do |dc|
18
+ memcached(19128) do |dc|
19
19
  assert dc.set("json_test", {"foo" => "bar"})
20
20
  assert_equal({"foo" => "bar"}, dc.get("json_test"))
21
21
  end
metadata CHANGED
@@ -1,130 +1,138 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: dalli
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.5.0
4
5
  prerelease:
5
- version: 2.3.0
6
6
  platform: ruby
7
- authors:
8
- - Mike Perham
7
+ authors:
8
+ - Mike Perham
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2012-10-15 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: mini_shoulda
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
24
- type: :development
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: mocha
28
- prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
30
- none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
35
- type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: rails
39
- prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: "3"
46
- type: :development
47
- version_requirements: *id003
12
+ date: 2012-11-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mini_shoulda
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: mocha
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3'
48
62
  description: High performance memcached client for Ruby
49
63
  email: mperham@gmail.com
50
64
  executables: []
51
-
52
65
  extensions: []
53
-
54
66
  extra_rdoc_files: []
55
-
56
- files:
57
- - lib/dalli.rb
58
- - lib/action_dispatch/middleware/session/dalli_store.rb
59
- - lib/active_support/cache/dalli_store.rb
60
- - lib/dalli/client.rb
61
- - lib/dalli/options.rb
62
- - lib/dalli/railtie.rb
63
- - lib/dalli/ring.rb
64
- - lib/dalli/server.rb
65
- - lib/dalli/socket.rb
66
- - lib/dalli/version.rb
67
- - lib/rack/session/dalli.rb
68
- - LICENSE
69
- - README.md
70
- - History.md
71
- - Rakefile
72
- - Gemfile
73
- - dalli.gemspec
74
- - Performance.md
75
- - test/abstract_unit.rb
76
- - test/benchmark_test.rb
77
- - test/helper.rb
78
- - test/memcached_mock.rb
79
- - test/test_active_support.rb
80
- - test/test_dalli.rb
81
- - test/test_encoding.rb
82
- - test/test_failover.rb
83
- - test/test_network.rb
84
- - test/test_rack_session.rb
85
- - test/test_ring.rb
86
- - test/test_sasl.rb
87
- - test/test_serializer.rb
88
- - test/test_session_store.rb
67
+ files:
68
+ - lib/action_dispatch/middleware/session/dalli_store.rb
69
+ - lib/active_support/cache/dalli_store.rb
70
+ - lib/dalli/client.rb
71
+ - lib/dalli/compressor.rb
72
+ - lib/dalli/options.rb
73
+ - lib/dalli/railtie.rb
74
+ - lib/dalli/ring.rb
75
+ - lib/dalli/server.rb
76
+ - lib/dalli/socket.rb
77
+ - lib/dalli/version.rb
78
+ - lib/dalli.rb
79
+ - lib/rack/session/dalli.rb
80
+ - LICENSE
81
+ - README.md
82
+ - History.md
83
+ - Rakefile
84
+ - Gemfile
85
+ - dalli.gemspec
86
+ - Performance.md
87
+ - test/benchmark_test.rb
88
+ - test/helper.rb
89
+ - test/memcached_mock.rb
90
+ - test/test_active_support.rb
91
+ - test/test_compressor.rb
92
+ - test/test_dalli.rb
93
+ - test/test_encoding.rb
94
+ - test/test_failover.rb
95
+ - test/test_network.rb
96
+ - test/test_rack_session.rb
97
+ - test/test_ring.rb
98
+ - test/test_sasl.rb
99
+ - test/test_serializer.rb
89
100
  homepage: http://github.com/mperham/dalli
90
101
  licenses: []
91
-
92
102
  post_install_message:
93
- rdoc_options:
94
- - --charset=UTF-8
95
- require_paths:
96
- - lib
97
- required_ruby_version: !ruby/object:Gem::Requirement
103
+ rdoc_options:
104
+ - --charset=UTF-8
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
98
108
  none: false
99
- requirements:
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- version: "0"
103
- required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
114
  none: false
105
- requirements:
106
- - - ">="
107
- - !ruby/object:Gem::Version
108
- version: "0"
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
109
119
  requirements: []
110
-
111
120
  rubyforge_project:
112
- rubygems_version: 1.8.15
121
+ rubygems_version: 1.8.24
113
122
  signing_key:
114
123
  specification_version: 3
115
124
  summary: High performance memcached client for Ruby
116
- test_files:
117
- - test/abstract_unit.rb
118
- - test/benchmark_test.rb
119
- - test/helper.rb
120
- - test/memcached_mock.rb
121
- - test/test_active_support.rb
122
- - test/test_dalli.rb
123
- - test/test_encoding.rb
124
- - test/test_failover.rb
125
- - test/test_network.rb
126
- - test/test_rack_session.rb
127
- - test/test_ring.rb
128
- - test/test_sasl.rb
129
- - test/test_serializer.rb
130
- - test/test_session_store.rb
125
+ test_files:
126
+ - test/benchmark_test.rb
127
+ - test/helper.rb
128
+ - test/memcached_mock.rb
129
+ - test/test_active_support.rb
130
+ - test/test_compressor.rb
131
+ - test/test_dalli.rb
132
+ - test/test_encoding.rb
133
+ - test/test_failover.rb
134
+ - test/test_network.rb
135
+ - test/test_rack_session.rb
136
+ - test/test_ring.rb
137
+ - test/test_sasl.rb
138
+ - test/test_serializer.rb
@@ -1,281 +0,0 @@
1
- # Used to test the full Rails stack.
2
- # Stolen from the Rails 3.0 source.
3
- # Needed for the session store tests.
4
- require 'active_support/core_ext/kernel/reporting'
5
- require 'active_support/core_ext/string/encoding'
6
- if "ruby".encoding_aware?
7
- # These are the normal settings that will be set up by Railties
8
- # TODO: Have these tests support other combinations of these values
9
- silence_warnings do
10
- Encoding.default_internal = "UTF-8"
11
- Encoding.default_external = "UTF-8"
12
- end
13
- end
14
-
15
- require 'test/unit'
16
- require 'abstract_controller'
17
- require 'action_controller'
18
- require 'action_view'
19
- require 'action_dispatch'
20
- require 'active_support/dependencies'
21
- require 'action_controller/caching'
22
- require 'action_controller/caching/sweeping'
23
-
24
- require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
25
-
26
- module Rails
27
- def self.logger
28
- @logger ||= begin
29
- l = Logger.new(STDOUT)
30
- l.level = Logger::INFO; l
31
- end
32
- end
33
- end
34
-
35
- # Monkey patch the old routes initialization to be silenced.
36
- class ActionDispatch::Routing::DeprecatedMapper
37
- def initialize_with_silencer(*args)
38
- ActiveSupport::Deprecation.silence { initialize_without_silencer(*args) }
39
- end
40
- alias_method_chain :initialize, :silencer
41
- end
42
-
43
- ActiveSupport::Dependencies.hook!
44
-
45
- # Show backtraces for deprecated behavior for quicker cleanup.
46
- ActiveSupport::Deprecation.debug = true
47
-
48
- ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
49
-
50
- module RackTestUtils
51
- def body_to_string(body)
52
- if body.respond_to?(:each)
53
- str = ""
54
- body.each {|s| str << s }
55
- str
56
- else
57
- body
58
- end
59
- end
60
- extend self
61
- end
62
-
63
- module SetupOnce
64
- extend ActiveSupport::Concern
65
-
66
- included do
67
- cattr_accessor :setup_once_block
68
- self.setup_once_block = nil
69
-
70
- setup :run_setup_once
71
- end
72
-
73
- module ClassMethods
74
- def setup_once(&block)
75
- self.setup_once_block = block
76
- end
77
- end
78
-
79
- private
80
- def run_setup_once
81
- if self.setup_once_block
82
- self.setup_once_block.call
83
- self.setup_once_block = nil
84
- end
85
- end
86
- end
87
-
88
- SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
89
-
90
- module ActiveSupport
91
- class TestCase
92
- include SetupOnce
93
- # Hold off drawing routes until all the possible controller classes
94
- # have been loaded.
95
- setup_once do
96
- SharedTestRoutes.draw do
97
- match ':controller(/:action(/:id))'
98
- end
99
-
100
- ActionController::IntegrationTest.app.routes.draw do
101
- match ':controller(/:action(/:id))'
102
- end
103
- end
104
- end
105
- end
106
-
107
- class RoutedRackApp
108
- attr_reader :routes
109
-
110
- def initialize(routes, &blk)
111
- @routes = routes
112
- @stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
113
- end
114
-
115
- def call(env)
116
- @stack.call(env)
117
- end
118
- end
119
-
120
- class BasicController
121
- attr_accessor :request
122
-
123
- def config
124
- @config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
125
- # VIEW TODO: View tests should not require a controller
126
- public_dir = File.expand_path("../fixtures/public", __FILE__)
127
- config.assets_dir = public_dir
128
- config.javascripts_dir = "#{public_dir}/javascripts"
129
- config.stylesheets_dir = "#{public_dir}/stylesheets"
130
- config
131
- end
132
- end
133
- end
134
-
135
- class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
136
- setup do
137
- @routes = SharedTestRoutes
138
- end
139
- end
140
-
141
- class ActionController::IntegrationTest < ActiveSupport::TestCase
142
- def self.build_app(routes = nil)
143
- RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
144
- middleware.use "ActionDispatch::Callbacks"
145
- middleware.use "ActionDispatch::ParamsParser"
146
- middleware.use "ActionDispatch::Cookies"
147
- middleware.use "ActionDispatch::Flash"
148
- middleware.use "ActionDispatch::Head"
149
- yield(middleware) if block_given?
150
- end
151
- end
152
-
153
- self.app = build_app
154
-
155
- # Stub Rails dispatcher so it does not get controller references and
156
- # simply return the controller#action as Rack::Body.
157
- class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
158
- protected
159
- def controller_reference(controller_param)
160
- controller_param
161
- end
162
-
163
- def dispatch(controller, action, env)
164
- [200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
165
- end
166
- end
167
-
168
- def self.stub_controllers
169
- old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher
170
- ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
171
- ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher }
172
- yield ActionDispatch::Routing::RouteSet.new
173
- ensure
174
- ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
175
- ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher }
176
- end
177
-
178
- def with_routing(&block)
179
- temporary_routes = ActionDispatch::Routing::RouteSet.new
180
- old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
181
- old_routes = SharedTestRoutes
182
- silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
183
-
184
- yield temporary_routes
185
- ensure
186
- self.class.app = old_app
187
- silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
188
- end
189
-
190
- def with_autoload_path(path)
191
- path = File.join(File.dirname(__FILE__), "fixtures", path)
192
- if ActiveSupport::Dependencies.autoload_paths.include?(path)
193
- yield
194
- else
195
- begin
196
- ActiveSupport::Dependencies.autoload_paths << path
197
- yield
198
- ensure
199
- ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
200
- ActiveSupport::Dependencies.clear
201
- end
202
- end
203
- end
204
- end
205
-
206
- # Temporary base class
207
- class Rack::TestCase < ActionController::IntegrationTest
208
- def self.testing(klass = nil)
209
- if klass
210
- @testing = "/#{klass.name.underscore}".sub!(/_controller$/, '')
211
- else
212
- @testing
213
- end
214
- end
215
-
216
- def get(thing, *args)
217
- if thing.is_a?(Symbol)
218
- super("#{self.class.testing}/#{thing}", *args)
219
- else
220
- super
221
- end
222
- end
223
-
224
- def assert_body(body)
225
- assert_equal body, Array.wrap(response.body).join
226
- end
227
-
228
- def assert_status(code)
229
- assert_equal code, response.status
230
- end
231
-
232
- def assert_response(body, status = 200, headers = {})
233
- assert_body body
234
- assert_status status
235
- headers.each do |header, value|
236
- assert_header header, value
237
- end
238
- end
239
-
240
- def assert_content_type(type)
241
- assert_equal type, response.headers["Content-Type"]
242
- end
243
-
244
- def assert_header(name, value)
245
- assert_equal value, response.headers[name]
246
- end
247
- end
248
-
249
- class ActionController::Base
250
- def self.test_routes(&block)
251
- routes = ActionDispatch::Routing::RouteSet.new
252
- routes.draw(&block)
253
- include routes.url_helpers
254
- end
255
- end
256
-
257
- class ::ApplicationController < ActionController::Base
258
- end
259
-
260
- module ActionController
261
- class Base
262
- include ActionController::Testing
263
- end
264
-
265
- Base.view_paths = []
266
-
267
- class TestCase
268
- include ActionDispatch::TestProcess
269
-
270
- setup do
271
- @routes = SharedTestRoutes
272
- end
273
- end
274
- end
275
-
276
- # This stub emulates the Railtie including the URL helpers from a Rails application
277
- module ActionController
278
- class Base
279
- include SharedTestRoutes.url_helpers
280
- end
281
- end
@@ -1,262 +0,0 @@
1
- require 'helper'
2
- require 'abstract_unit'
3
- require 'action_dispatch/middleware/session/dalli_store'
4
-
5
- class Foo
6
- def initialize(bar='baz')
7
- @bar = bar
8
- end
9
- def inspect
10
- "#<#{self.class} bar:#{@bar.inspect}>"
11
- end
12
- end
13
-
14
- class TestSessionStore < ActionController::IntegrationTest
15
- include MemcachedMock::Helper
16
-
17
- class TestController < ActionController::Base
18
- def no_session_access
19
- head :ok
20
- end
21
-
22
- def set_session_value
23
- session[:foo] = "bar"
24
- head :ok
25
- end
26
-
27
- def set_serialized_session_value
28
- session[:foo] = Foo.new
29
- head :ok
30
- end
31
-
32
- def get_session_value
33
- render :text => "foo: #{session[:foo].inspect}"
34
- end
35
-
36
- def get_session_id
37
- render :text => "#{request.session_options[:id]}"
38
- end
39
-
40
- def call_reset_session
41
- session[:bar]
42
- reset_session
43
- session[:bar] = "baz"
44
- head :ok
45
- end
46
-
47
- def rescue_action(e) raise end
48
- end
49
-
50
- begin
51
- require 'dalli'
52
- memcache = Dalli::Client.new('127.0.0.1:11211')
53
- memcache.set('ping', '')
54
-
55
- def test_setting_and_getting_session_value
56
- with_test_route_set do
57
- get '/set_session_value'
58
- assert_response :success
59
- assert cookies['_session_id']
60
-
61
- get '/get_session_value'
62
- assert_response :success
63
- assert_equal 'foo: "bar"', response.body
64
- end
65
- end
66
-
67
- def test_getting_nil_session_value
68
- with_test_route_set do
69
- get '/get_session_value'
70
- assert_response :success
71
- assert_equal 'foo: nil', response.body
72
- end
73
- end
74
-
75
- def test_getting_session_value_after_session_reset
76
- with_test_route_set do
77
- get '/set_session_value'
78
- assert_response :success
79
- assert cookies['_session_id']
80
- session_cookie = cookies.send(:hash_for)['_session_id']
81
-
82
- get '/call_reset_session'
83
- assert_response :success
84
- assert_not_equal [], headers['Set-Cookie']
85
-
86
- cookies << session_cookie # replace our new session_id with our old, pre-reset session_id
87
-
88
- get '/get_session_value'
89
- assert_response :success
90
- assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from memcached"
91
- end
92
- end
93
-
94
- def test_getting_from_nonexistent_session
95
- with_test_route_set do
96
- get '/get_session_value'
97
- assert_response :success
98
- assert_equal 'foo: nil', response.body
99
- assert_nil cookies['_session_id'], "should only create session on write, not read"
100
- end
101
- end
102
-
103
- def test_setting_session_value_after_session_reset
104
- with_test_route_set do
105
- get '/set_session_value'
106
- assert_response :success
107
- assert cookies['_session_id']
108
- session_id = cookies['_session_id']
109
-
110
- get '/call_reset_session'
111
- assert_response :success
112
- assert_not_equal [], headers['Set-Cookie']
113
-
114
- get '/get_session_value'
115
- assert_response :success
116
- assert_equal 'foo: nil', response.body
117
-
118
- get '/get_session_id'
119
- assert_response :success
120
- assert_not_equal session_id, response.body
121
- end
122
- end
123
-
124
- def test_getting_session_id
125
- with_test_route_set do
126
- get '/set_session_value'
127
- assert_response :success
128
- assert cookies['_session_id']
129
- session_id = cookies['_session_id']
130
-
131
- get '/get_session_id'
132
- assert_response :success
133
- assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
134
- end
135
- end
136
-
137
- def test_deserializes_unloaded_class
138
- with_test_route_set do
139
- with_autoload_path "session_autoload_test" do
140
- get '/set_serialized_session_value'
141
- assert_response :success
142
- assert cookies['_session_id']
143
- end
144
- with_autoload_path "session_autoload_test" do
145
- get '/get_session_id'
146
- assert_response :success
147
- end
148
- with_autoload_path "session_autoload_test" do
149
- get '/get_session_value'
150
- assert_response :success
151
- assert_equal 'foo: #<Foo bar:"baz">', response.body, "should auto-load unloaded class"
152
- end
153
- end
154
- end
155
-
156
- def test_doesnt_write_session_cookie_if_session_id_is_already_exists
157
- with_test_route_set do
158
- get '/set_session_value'
159
- assert_response :success
160
- assert cookies['_session_id']
161
-
162
- get '/get_session_value'
163
- assert_response :success
164
- assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
165
- end
166
- end
167
-
168
- def test_prevents_session_fixation
169
- with_test_route_set do
170
- get '/get_session_value'
171
- assert_response :success
172
- assert_equal 'foo: nil', response.body
173
- session_id = cookies['_session_id']
174
-
175
- reset!
176
-
177
- get '/set_session_value', :_session_id => session_id
178
- assert_response :success
179
- assert_not_equal session_id, cookies['_session_id']
180
- end
181
- end
182
-
183
- def test_expire_after
184
- with_test_route_set(:expire_after => 1) do
185
- get '/set_session_value'
186
- assert_match(/expires/, @response.headers['Set-Cookie'])
187
-
188
- sleep(1)
189
-
190
- get '/get_session_value'
191
- assert_equal 'foo: nil', response.body
192
- end
193
- end
194
-
195
- def test_expires_in
196
- with_test_route_set(:expires_in => 1) do
197
- get '/set_session_value'
198
- assert_no_match(/expires/, @response.headers['Set-Cookie'])
199
-
200
- sleep(1)
201
-
202
- get '/get_session_value'
203
- assert_equal 'foo: nil', response.body
204
- end
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
242
- rescue LoadError, RuntimeError
243
- $stderr.puts "Skipping SessionStore tests. Start memcached and try again: #{$!.message}"
244
- end
245
-
246
- private
247
-
248
- def with_test_route_set(options = {})
249
- options = {:key => '_session_id', :memcache_server => '127.0.0.1:11211'}.merge(options)
250
- with_routing do |set|
251
- set.draw do
252
- match ':action', :to => ::TestSessionStore::TestController
253
- end
254
-
255
- @app = self.class.build_app(set) do |middleware|
256
- middleware.use ActionDispatch::Session::DalliStore, options
257
- end
258
-
259
- yield
260
- end
261
- end
262
- end