dalli 0.9.4 → 0.9.5

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/Gemfile CHANGED
@@ -3,5 +3,5 @@ source :rubygems
3
3
  gemspec
4
4
 
5
5
  group :test do
6
- gem 'simplecov-html'
6
+ gem 'simplecov'
7
7
  end
data/History.md CHANGED
@@ -1,6 +1,15 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
+ HEAD
5
+ -----
6
+
7
+ - Major design change - raw support is back to maximize compatibility with Rails
8
+ and the increment/decrement operations. You can now pass :raw => true to most methods
9
+ to bypass (un)marshalling.
10
+ - Support symbols as keys (ddollar)
11
+
12
+
4
13
  0.9.4
5
14
  -----
6
15
 
@@ -23,6 +32,7 @@ Dalli Changelog
23
32
 
24
33
  - Verify proper operation in Heroku.
25
34
 
35
+
26
36
  0.9.1
27
37
  ----
28
38
 
data/README.md CHANGED
@@ -91,10 +91,7 @@ Put this at the bottom of `config/environment.rb`:
91
91
  Features and Changes
92
92
  ------------------------
93
93
 
94
- Dalli is **NOT** 100% API compatible with memcache-client. If you have code which uses the MemCache
95
- API directly, it will likely need small tweaks. Method parameters and return values changed slightly.
96
-
97
- memcache-client allowed developers to store either raw or marshalled values with each API call. I feel this is needless complexity; Dalli allows you to control marshalling per-Client with the `:marshal => false` flag but you cannot explicitly set the raw flag for each API call. By default, marshalling is enabled.
94
+ Dalli is **NOT** 100% API compatible with memcache-client. If you have code which uses the MemCache API directly, it will likely need small tweaks. Method parameters and return values changed slightly. See Upgrade.md for more detail.
98
95
 
99
96
  I've removed support for key namespaces and automatic pruning of keys longer than 250 characters. ActiveSupport::Cache implements these features so there is little need for Dalli to reinvent them.
100
97
 
@@ -114,6 +111,10 @@ Thanks
114
111
 
115
112
  Brian Mitchell - for his remix-stash project which was helpful when implementing and testing the binary protocol support.
116
113
 
114
+ [NorthScale](http://northscale.com) - for their project sponsorship
115
+
116
+ [Bootspring](http://bootspring.com) is my Ruby and Rails consulting company. We specialize in Ruby infrastructure, performance and scalability tuning for Rails applications. If you need help, please [contact us](mailto:info@bootspring.com) today.
117
+
117
118
 
118
119
  Author
119
120
  ----------
data/Rakefile CHANGED
@@ -9,4 +9,9 @@ Rake::TestTask.new(:bench) do |test|
9
9
  test.pattern = 'test/benchmark_test.rb'
10
10
  end
11
11
 
12
- task :default => :test
12
+ task :default => :test
13
+
14
+ task :test_all do
15
+ system('rake test RAILS_VERSION="~> 2.3.0"')
16
+ system('rake test RAILS_VERSION="~> 3.0.0"')
17
+ end
data/Upgrade.md CHANGED
@@ -1,37 +1,26 @@
1
1
  Upgrading from memcache-client
2
2
  ========
3
3
 
4
- Dalli is not meant to be 100% compatible with memcache-client, there are serveral differences in the API.
4
+ Dalli is not meant to be 100% compatible with memcache-client, there are a few minor differences in the API.
5
5
 
6
6
 
7
7
  Marshalling
8
8
  ---------------
9
9
 
10
- Dalli has removed support for specifying the marshalling behavior for each operation.
11
-
12
- Take this typical operation:
10
+ Dalli has changed the raw parameter to a :raw option. The memcache-client API allowed you to control marshalling on a per-method basis using a boolean 'raw' parameter to several of the API methods:
13
11
 
14
12
  cache = MemCache.new
15
- cache.set('abc', 123)
16
-
17
- Technically 123 is an Integer and presumably you want `cache.get('abc')` to return an Integer. Since memcached stores values as binary blobs, Dalli will serialize the value to a binary blob for storage. When you get() the value back, Ruby will deserialize it properly to an Integer and all will be well. Without marshalling, Dalli will convert values to Strings and so get() would return a String, not an Integer.
18
-
19
- The memcache-client API allowed you to control marshalling on a per-method basis using a boolean 'raw' parameter to several of the API methods:
20
-
21
- cache = MemCache.new
22
13
  cache.set('abc', 123, 0, true)
23
14
  cache.get('abc', true) => '123'
24
-
15
+
25
16
  cache.set('abc', 123, 0)
26
17
  cache.get('abc') => 123
27
18
 
28
- Note that the last 'raw' parameter is set to true in the first two API calls and so `get` returns a string, not an integer. In the second example, we don't provide the raw parameter. Since it defaults to false, it works exactly like Dalli.
29
-
30
- If the code specifies raw as false, you can simply remove that parameter. If the code is using raw = true, you will need to use the :marshal option to create a Dalli::Client instance that does not perform marshalling:
19
+ Note that the last parameter is set to true in the first two API calls and so `get` returns a string, not an integer. In the second example, we don't provide the raw parameter. Since it defaults to false, it works exactly like Dalli.
31
20
 
32
- cache = Dalli::Client.new(servers, :marshal => false)
21
+ If the code specifies raw as false, you can simply remove that parameter. If the code is using raw = true, you will need to use the :raw option:
33
22
 
34
- If the code is mixing marshal modes (performing operations where raw is both true and false), you will need to use two different client instances.
23
+ cache.set('abc', 123, 0, :raw => true)
35
24
 
36
25
 
37
26
  Return Values
data/dalli.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Dalli::VERSION
8
8
 
9
9
  s.authors = ["Mike Perham"]
10
- s.date = %q{2010-08-28}
10
+ s.date = Time.now.utc.strftime("%Y-%m-%d")
11
11
  s.description = %q{High performance memcached client for Ruby}
12
12
  s.email = %q{mperham@gmail.com}
13
13
  s.files = Dir.glob("lib/**/*") + [
@@ -15,13 +15,6 @@ module ActiveSupport
15
15
  # DalliStore implements the Strategy::LocalCache strategy which implements
16
16
  # an in memory cache inside of a block.
17
17
  class DalliStore < Store
18
- module Response # :nodoc:
19
- STORED = "STORED\r\n"
20
- NOT_STORED = "NOT_STORED\r\n"
21
- EXISTS = "EXISTS\r\n"
22
- NOT_FOUND = "NOT_FOUND\r\n"
23
- DELETED = "DELETED\r\n"
24
- end
25
18
 
26
19
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
27
20
 
@@ -46,13 +39,9 @@ module ActiveSupport
46
39
  options = addresses.extract_options!
47
40
  super(options)
48
41
 
49
- if addresses.first.respond_to?(:get)
50
- @data = addresses.first
51
- else
52
- mem_cache_options = options.dup
53
- UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
54
- @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
55
- end
42
+ mem_cache_options = options.dup
43
+ UNIVERSAL_OPTIONS.each{|name| mem_cache_options.delete(name)}
44
+ @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
56
45
 
57
46
  extend Strategy::LocalCache
58
47
  extend LocalCacheWithRaw
@@ -64,7 +53,7 @@ module ActiveSupport
64
53
  options = names.extract_options!
65
54
  options = merged_options(options)
66
55
  keys_to_names = names.inject({}){|map, name| map[escape_key(namespaced_key(name, options))] = name; map}
67
- raw_values = @data.get_multi(keys_to_names.keys)
56
+ raw_values = @data.get_multi(keys_to_names.keys, options)
68
57
  values = {}
69
58
  raw_values.each do |key, value|
70
59
  entry = deserialize_entry(value)
@@ -111,13 +100,13 @@ module ActiveSupport
111
100
  end
112
101
 
113
102
  def reset
114
- @pool.reset
103
+ @data.reset
115
104
  end
116
105
 
117
106
  protected
118
107
  # Read an entry from the cache.
119
108
  def read_entry(key, options) # :nodoc:
120
- deserialize_entry(@data.get(escape_key(key)))
109
+ deserialize_entry(@data.get(escape_key(key), options))
121
110
  rescue Dalli::DalliError => e
122
111
  logger.error("DalliError (#{e}): #{e.message}") if logger
123
112
  nil
@@ -132,7 +121,7 @@ module ActiveSupport
132
121
  # Set the memcache expire a few minutes in the future to support race condition ttls on read
133
122
  expires_in += 5.minutes
134
123
  end
135
- @data.send(method, escape_key(key), value, expires_in)
124
+ @data.send(method, escape_key(key), value, expires_in, options)
136
125
  rescue Dalli::DalliError => e
137
126
  logger.error("DalliError (#{e}): #{e.message}") if logger
138
127
  false
@@ -14,19 +14,11 @@ module ActiveSupport
14
14
  # DalliStore implements the Strategy::LocalCache strategy which implements
15
15
  # an in memory cache inside of a block.
16
16
  class DalliStore < Store
17
- module Response # :nodoc:
18
- STORED = "STORED\r\n"
19
- NOT_STORED = "NOT_STORED\r\n"
20
- EXISTS = "EXISTS\r\n"
21
- NOT_FOUND = "NOT_FOUND\r\n"
22
- DELETED = "DELETED\r\n"
23
- end
24
17
 
25
18
  def self.build_mem_cache(*addresses)
26
19
  addresses = addresses.flatten
27
20
  options = addresses.extract_options!
28
21
  addresses = ["localhost"] if addresses.empty?
29
- # No need to marshal since we marshal here.
30
22
  Dalli::Client.new(addresses, options)
31
23
  end
32
24
 
@@ -42,13 +34,10 @@ module ActiveSupport
42
34
  def initialize(*addresses)
43
35
  addresses = addresses.flatten
44
36
  options = addresses.extract_options!
45
- if addresses.first.respond_to?(:get)
46
- @data = addresses.first
47
- else
48
- mem_cache_options = options.dup
49
- @namespace = mem_cache_options.delete(:namespace)
50
- @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
51
- end
37
+
38
+ mem_cache_options = options.dup
39
+ @namespace = mem_cache_options.delete(:namespace)
40
+ @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
52
41
 
53
42
  extend Strategy::LocalCache
54
43
  end
@@ -78,26 +67,26 @@ module ActiveSupport
78
67
  # to zero.
79
68
  def increment(key, amount = 1) # :nodoc:
80
69
  log("incrementing", key, amount)
81
-
82
- response = @data.incr(escape_key(key), amount)
83
- response == Response::NOT_FOUND ? nil : response
70
+ @data.incr(escape_key(key), amount)
84
71
  rescue Dalli::DalliError
72
+ logger.error("DalliError (#{e}): #{e.message}")
85
73
  nil
86
74
  end
75
+
87
76
  # Decrement a cached value. This method uses the memcached decr atomic
88
77
  # operator and can only be used on values written with the :raw option.
89
78
  # Calling it on a value not stored with :raw will initialize that value
90
79
  # to zero.
91
80
  def decrement(key, amount = 1) # :nodoc:
92
81
  log("decrement", key, amount)
93
- response = @data.decr(escape_key(key), amount)
94
- response == Response::NOT_FOUND ? nil : response
82
+ @data.decr(escape_key(key), amount)
95
83
  rescue Dalli::DalliError
84
+ logger.error("DalliError (#{e}): #{e.message}")
96
85
  nil
97
86
  end
98
87
 
99
88
  def reset
100
- @pool.reset
89
+ @data.reset
101
90
  end
102
91
 
103
92
  # Clear the entire cache on all memcached servers. This method should
@@ -114,9 +103,9 @@ module ActiveSupport
114
103
  # Read an entry from the cache.
115
104
  def read(key, options = nil) # :nodoc:
116
105
  super
117
- value = @data.get(escape_key(key))
106
+ value = @data.get(escape_key(key), options)
118
107
  return nil if value.nil?
119
- value = Marshal.load value
108
+ value = options && options[:raw] ? value : Marshal.load(value)
120
109
  value
121
110
  rescue Dalli::DalliError => e
122
111
  logger.error("DalliError (#{e}): #{e.message}")
@@ -133,8 +122,8 @@ module ActiveSupport
133
122
  def write(key, value, options = nil)
134
123
  super
135
124
  method = options && options[:unless_exist] ? :add : :set
136
- value = Marshal.dump value
137
- @data.send(method, escape_key(key), value, expires_in(options))
125
+ value = options && options[:raw] ? value.to_s : Marshal.dump(value)
126
+ @data.send(method, escape_key(key), value, expires_in(options), options)
138
127
  rescue Dalli::DalliError => e
139
128
  logger.error("DalliError (#{e}): #{e.message}")
140
129
  false
data/lib/dalli.rb CHANGED
@@ -11,11 +11,12 @@ module Dalli
11
11
  class NetworkError < DalliError; end
12
12
 
13
13
  def self.logger
14
- @logger ||= begin
15
- (defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
16
- (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER) ||
17
- default_logger
18
- end
14
+ @logger ||= (rails_logger || default_logger)
15
+ end
16
+
17
+ def self.rails_logger
18
+ (defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
19
+ (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
19
20
  end
20
21
 
21
22
  def self.default_logger
data/lib/dalli/client.rb CHANGED
@@ -6,7 +6,7 @@ module Dalli
6
6
  # the memcached server. Usage:
7
7
  # <pre>
8
8
  # Dalli::Client.new(['localhost:11211:10', 'cache-2.example.com:11211:5', '192.168.0.1:22122:5'],
9
- # :threadsafe => false, :marshal => false)
9
+ # :threadsafe => true, :failover => true)
10
10
  # </pre>
11
11
  # servers is an Array of "host:port:weight" where weight allows you to distribute cache unevenly.
12
12
  # Both weight and port are optional.
@@ -14,64 +14,62 @@ module Dalli
14
14
  # Options:
15
15
  # :failover - if a server is down, store the value on another server. Default: true.
16
16
  # :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
17
- # :marshal - ensure that the value you store is exactly what is returned. Otherwise you can see this:
18
- # set('abc', 123)
19
- # get('abc') ==> '123' (Note you set an Integer but got back a String)
20
- # Default: true.
21
17
  #
22
18
  def initialize(servers=nil, options={})
23
19
  @servers = servers
24
20
  @options = options
25
- self.extend(Dalli::Marshal) unless options[:marshal] == false
26
21
  end
27
22
 
28
23
  #
29
24
  # The standard memcached instruction set
30
25
  #
31
26
 
32
- def get(key)
27
+ def get(key, options=nil)
33
28
  resp = perform(:get, key)
34
- (!resp || resp == 'Not found') ? nil : deserialize(resp)
29
+ (!resp || resp == 'Not found') ? nil : deserialize(resp, options)
35
30
  end
36
31
 
37
32
  def get_multi(*keys)
33
+ return {} if keys.empty?
34
+ options = nil
35
+ options = keys.pop if keys.last.is_a?(Hash)
38
36
  ring.lock do
39
37
  keys.flatten.each do |key|
40
38
  perform(:getkq, key)
41
39
  end
42
40
  values = ring.servers.inject({}) { |hash, s| hash.merge!(s.request(:noop)); hash }
43
- values.inject(values) { |memo, (k,v)| memo[k] = deserialize(v); memo }
41
+ values.inject(values) { |memo, (k,v)| memo[k] = deserialize(v, options); memo }
44
42
  end
45
43
  end
46
44
 
47
- def fetch(key, ttl=0)
48
- val = get(key)
45
+ def fetch(key, ttl=0, options=nil)
46
+ val = get(key, options)
49
47
  if val.nil? && block_given?
50
48
  val = yield
51
- add(key, val, ttl)
49
+ add(key, val, ttl, options)
52
50
  end
53
51
  val
54
52
  end
55
53
 
56
- def cas(key, ttl=0, &block)
54
+ def cas(key, ttl=0, options=nil, &block)
57
55
  (value, cas) = perform(:cas, key)
58
- value = (!value || value == 'Not found') ? nil : deserialize(value)
56
+ value = (!value || value == 'Not found') ? nil : deserialize(value, options)
59
57
  if value
60
58
  newvalue = block.call(value)
61
- perform(:add, key, serialize(newvalue), ttl, cas)
59
+ perform(:add, key, serialize(newvalue, options), ttl, cas)
62
60
  end
63
61
  end
64
62
 
65
- def set(key, value, ttl=0)
66
- perform(:set, key, serialize(value), ttl)
63
+ def set(key, value, ttl=0, options=nil)
64
+ perform(:set, key, serialize(value, options), ttl)
67
65
  end
68
66
 
69
- def add(key, value, ttl=0)
70
- perform(:add, key, serialize(value), ttl, 0)
67
+ def add(key, value, ttl=0, options=nil)
68
+ perform(:add, key, serialize(value, options), ttl, 0)
71
69
  end
72
70
 
73
- def replace(key, value, ttl=0)
74
- perform(:replace, key, serialize(value), ttl)
71
+ def replace(key, value, ttl=0, options=nil)
72
+ perform(:replace, key, serialize(value, options), ttl)
75
73
  end
76
74
 
77
75
  def delete(key)
@@ -148,12 +146,14 @@ module Dalli
148
146
  )
149
147
  end
150
148
 
151
- def serialize(value)
152
- value.to_s
149
+ def serialize(value, options)
150
+ options && options[:raw] ? value.to_s : ::Marshal.dump(value)
153
151
  end
154
-
155
- def deserialize(value)
156
- value
152
+
153
+ def deserialize(value, options)
154
+ options && options[:raw] ? value : ::Marshal.load(value)
155
+ rescue TypeError
156
+ raise Dalli::DalliError, "Invalid marshalled data in memcached: #{value}"
157
157
  end
158
158
 
159
159
  def env_servers
@@ -163,6 +163,10 @@ module Dalli
163
163
  # Chokepoint method for instrumentation
164
164
  def perform(op, *args)
165
165
  key = args.first
166
+ if key.is_a?(Symbol)
167
+ key = key.to_s
168
+ args[0] = key
169
+ end
166
170
  validate_key(key)
167
171
  server = ring.server_for_key(key)
168
172
  server.request(op, *args)
data/lib/dalli/options.rb CHANGED
@@ -2,33 +2,6 @@ require 'thread'
2
2
 
3
3
  module Dalli
4
4
 
5
- # Auto-marshal all values in/out of memcached.
6
- # Otherwise, Dalli will just use to_s on all values.
7
- #
8
- # Dalli::Client.extend(Dalli::Marshal)
9
- #
10
- module Marshal
11
- def serialize(value)
12
- ::Marshal.dump(value)
13
- end
14
-
15
- def deserialize(value)
16
- begin
17
- ::Marshal.load(value)
18
- rescue TypeError
19
- raise Dalli::DalliError, "Invalid marshalled data in memcached, this happens if you switch the :marshal option and still have old data in memcached: #{value}"
20
- end
21
- end
22
-
23
- def append(key, value)
24
- raise Dalli::DalliError, "Marshalling and append do not work together"
25
- end
26
-
27
- def prepend(key, value)
28
- raise Dalli::DalliError, "Marshalling and prepend do not work together"
29
- end
30
- end
31
-
32
5
  # Make Dalli threadsafe by using a lock around all
33
6
  # public server methods.
34
7
  #
data/lib/dalli/server.rb CHANGED
@@ -167,7 +167,7 @@ module Dalli
167
167
  def cas_response
168
168
  header = read(24)
169
169
  raise Dalli::NetworkError, 'No response' if !header
170
- (extras, status, count, _, cas) = header.unpack(CAS_HEADER)
170
+ (extras, type, status, count, _, cas) = header.unpack(CAS_HEADER)
171
171
  data = read(count) if count > 0
172
172
  if status == 1
173
173
  nil
@@ -182,7 +182,7 @@ module Dalli
182
182
  def generic_response
183
183
  header = read(24)
184
184
  raise Dalli::NetworkError, 'No response' if !header
185
- (extras, status, count) = header.unpack(NORMAL_HEADER)
185
+ (extras, type, status, count) = header.unpack(NORMAL_HEADER)
186
186
  data = read(count) if count > 0
187
187
  if status == 1
188
188
  nil
@@ -287,15 +287,15 @@ module Dalli
287
287
  end
288
288
 
289
289
  def split(n)
290
- [0xFFFFFFFF & n, n >> 32]
290
+ [n >> 32, 0xFFFFFFFF & n]
291
291
  end
292
292
 
293
293
  def longlong(a, b)
294
- a | (b << 32)
294
+ (a << 32) | b
295
295
  end
296
296
 
297
- CAS_HEADER = '@4vnNNQ'
298
- NORMAL_HEADER = '@4vnN'
297
+ CAS_HEADER = '@4CCnNNQ'
298
+ NORMAL_HEADER = '@4CCnN'
299
299
  KV_HEADER = '@2n@6nN'
300
300
 
301
301
  REQUEST = 0x80
@@ -379,7 +379,7 @@ module Dalli
379
379
 
380
380
  def sasl_authentication(socket)
381
381
  init_sasl if !defined?(::SASL)
382
-
382
+
383
383
  Dalli.logger.info { "Dalli/SASL authenticating as #{username}" }
384
384
 
385
385
  # negotiate
@@ -387,7 +387,7 @@ module Dalli
387
387
  socket.write(req)
388
388
  header = read(24, socket)
389
389
  raise Dalli::NetworkError, 'No response' if !header
390
- (extras, status, count) = header.unpack(NORMAL_HEADER)
390
+ (extras, type, status, count) = header.unpack(NORMAL_HEADER)
391
391
  raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
392
392
  content = read(count, socket)
393
393
  return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
@@ -403,7 +403,7 @@ module Dalli
403
403
 
404
404
  header = read(24, socket)
405
405
  raise Dalli::NetworkError, 'No response' if !header
406
- (extras, status, count) = header.unpack(NORMAL_HEADER)
406
+ (extras, type, status, count) = header.unpack(NORMAL_HEADER)
407
407
  raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
408
408
  content = read(count, socket)
409
409
  return Dalli.logger.info("Dalli/SASL: #{content}") if status == 0
data/lib/dalli/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dalli
2
- VERSION = '0.9.4'
2
+ VERSION = '0.9.5'
3
3
  end
@@ -1,4 +1,3 @@
1
- require 'rails'
2
1
  # Used to test the full Rails stack.
3
2
  # Stolen from the Rails 3.0 source.
4
3
  # Needed for the session store tests.
@@ -18,6 +18,7 @@ class TestBenchmark < Test::Unit::TestCase
18
18
  # 5 and 6 are only used for multiget miss test
19
19
  @key5 = "Medium2"*8
20
20
  @key6 = "Long3"*40
21
+ @counter = 'counter'
21
22
  end
22
23
 
23
24
  def test_benchmark
@@ -27,15 +28,15 @@ class TestBenchmark < Test::Unit::TestCase
27
28
 
28
29
  n = 2500
29
30
 
30
- @m = Dalli::Client.new(@servers, :marshal => false)
31
+ @m = Dalli::Client.new(@servers)
31
32
  x.report("set:plain:dalli") do
32
33
  n.times do
33
- @m.set @key1, @marshalled
34
- @m.set @key2, @marshalled
35
- @m.set @key3, @marshalled
36
- @m.set @key1, @marshalled
37
- @m.set @key2, @marshalled
38
- @m.set @key3, @marshalled
34
+ @m.set @key1, @marshalled, 0, :raw => true
35
+ @m.set @key2, @marshalled, 0, :raw => true
36
+ @m.set @key3, @marshalled, 0, :raw => true
37
+ @m.set @key1, @marshalled, 0, :raw => true
38
+ @m.set @key2, @marshalled, 0, :raw => true
39
+ @m.set @key3, @marshalled, 0, :raw => true
39
40
  end
40
41
  end
41
42
 
@@ -51,15 +52,15 @@ class TestBenchmark < Test::Unit::TestCase
51
52
  end
52
53
  end
53
54
 
54
- @m = Dalli::Client.new(@servers, :marshal => false)
55
+ @m = Dalli::Client.new(@servers)
55
56
  x.report("get:plain:dalli") do
56
57
  n.times do
57
- @m.get @key1
58
- @m.get @key2
59
- @m.get @key3
60
- @m.get @key1
61
- @m.get @key2
62
- @m.get @key3
58
+ @m.get @key1, :raw => true
59
+ @m.get @key2, :raw => true
60
+ @m.get @key3, :raw => true
61
+ @m.get @key1, :raw => true
62
+ @m.get @key2, :raw => true
63
+ @m.get @key3, :raw => true
63
64
  end
64
65
  end
65
66
 
@@ -113,7 +114,18 @@ class TestBenchmark < Test::Unit::TestCase
113
114
  end
114
115
  end
115
116
 
116
- assert true
117
+ @m = Dalli::Client.new(@servers)
118
+ x.report("incr:ruby:dalli") do
119
+ n.times do
120
+ @m.incr @counter, 1, 0, 1
121
+ end
122
+ n.times do
123
+ @m.decr @counter, 1
124
+ end
125
+
126
+ assert_equal 0, @m.incr(@counter, 0)
127
+ end
128
+
117
129
  end
118
130
  end
119
131
 
data/test/helper.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'rubygems'
2
- # require 'simplecov-html'
2
+ # require 'simplecov'
3
3
  # SimpleCov.start
4
+ RAILS_VERSION = ENV['RAILS_VERSION'] || '~> 3.0.0'
5
+ #puts "Testing with Rails #{RAILS_VERSION}"
6
+ gem 'rails', RAILS_VERSION
4
7
 
5
8
  require 'test/unit'
6
9
  require 'shoulda'
@@ -12,25 +15,29 @@ require 'dalli'
12
15
  class Test::Unit::TestCase
13
16
  include MemcachedMock::Helper
14
17
 
18
+ def rails3?
19
+ RAILS_VERSION =~ /3\.0\./
20
+ end
21
+
15
22
  def assert_error(error, regexp=nil, &block)
16
23
  ex = assert_raise(error, &block)
17
24
  assert_match(regexp, ex.message, "#{ex.class.name}: #{ex.message}\n#{ex.backtrace.join("\n\t")}")
18
25
  end
19
26
 
20
27
  def with_activesupport
21
- case Rails.version
22
- when '3.0.0'
28
+ case
29
+ when rails3?
23
30
  require 'active_support/all'
24
- # when '2.3.0'
25
- # require 'active_support'
26
- # require 'active_support/cache/dalli_store23'
31
+ else
32
+ require 'active_support'
33
+ require 'active_support/cache/dalli_store23'
27
34
  end
28
35
  yield
29
36
  end
30
37
 
31
38
  def with_actionpack
32
- case Rails.version
33
- when '3.0.0'
39
+ case
40
+ when rails3?
34
41
  require 'action_dispatch'
35
42
  require 'action_controller'
36
43
  # when '2.3.0'
@@ -1,5 +1,4 @@
1
1
  require 'helper'
2
- require 'rails'
3
2
 
4
3
  class TestActiveSupport < Test::Unit::TestCase
5
4
  context 'active_support caching' do
@@ -27,13 +26,23 @@ class TestActiveSupport < Test::Unit::TestCase
27
26
  end
28
27
  end
29
28
 
30
- should 'support keys with spaces' do
29
+ should 'support keys with spaces on Rails3' do
31
30
  with_activesupport do
32
31
  memcached do
33
32
  connect
34
- dvalue = @mc.fetch('some key with spaces', :expires_in => 1.second) { 123 }
35
- mvalue = @dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
36
- assert_equal mvalue, dvalue
33
+ case
34
+ when rails3?
35
+ dvalue = @mc.fetch('some key with spaces', :expires_in => 1.second) { 123 }
36
+ mvalue = @dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
37
+ assert_equal mvalue, dvalue
38
+ else
39
+ assert_raises ArgumentError do
40
+ @mc.fetch('some key with spaces', :expires_in => 1.second) { 123 }
41
+ end
42
+ assert_raises ArgumentError do
43
+ @dalli.fetch('some other key with spaces', :expires_in => 1.second) { 123 }
44
+ end
45
+ end
37
46
  end
38
47
  end
39
48
  end
@@ -81,6 +90,25 @@ class TestActiveSupport < Test::Unit::TestCase
81
90
  end
82
91
  end
83
92
 
93
+ should 'support increment/decrement commands' do
94
+ with_activesupport do
95
+ memcached do
96
+ connect
97
+ assert_equal true, @mc.write('counter', 0, :raw => true)
98
+ assert_equal 1, @mc.increment('counter')
99
+ assert_equal 2, @mc.increment('counter')
100
+ assert_equal 1, @mc.decrement('counter')
101
+ assert_equal "1", @mc.read('counter', :raw => true)
102
+
103
+ assert_equal true, @dalli.write('counter', 0, :raw => true)
104
+ assert_equal 1, @dalli.increment('counter')
105
+ assert_equal 2, @dalli.increment('counter')
106
+ assert_equal 1, @dalli.decrement('counter')
107
+ assert_equal "1", @dalli.read('counter', :raw => true)
108
+ end
109
+ end
110
+ end
111
+
84
112
  should 'support other esoteric commands' do
85
113
  with_activesupport do
86
114
  memcached do
@@ -89,6 +117,8 @@ class TestActiveSupport < Test::Unit::TestCase
89
117
  ds = @dalli.stats
90
118
  assert_equal ms.keys.sort, ds.keys.sort
91
119
  assert_equal ms[ms.keys.first].keys.sort, ds[ds.keys.first].keys.sort
120
+
121
+ @dalli.reset
92
122
  end
93
123
  end
94
124
  end
data/test/test_dalli.rb CHANGED
@@ -86,6 +86,36 @@ class TestDalli < Test::Unit::TestCase
86
86
  end
87
87
  end
88
88
 
89
+ should 'support raw incr/decr' do
90
+ memcached do |client|
91
+ client.flush
92
+
93
+ assert_equal true, client.set('fakecounter', 0, 0, :raw => true)
94
+ assert_equal 1, client.incr('fakecounter', 1)
95
+ assert_equal 2, client.incr('fakecounter', 1)
96
+ assert_equal 3, client.incr('fakecounter', 1)
97
+ assert_equal 1, client.decr('fakecounter', 2)
98
+ assert_equal "1", client.get('fakecounter', :raw => true)
99
+
100
+ resp = client.incr('mycounter', 0)
101
+ assert_nil resp
102
+
103
+ resp = client.incr('mycounter', 1, 0, 2)
104
+ assert_equal 2, resp
105
+ resp = client.incr('mycounter', 1)
106
+ assert_equal 3, resp
107
+
108
+ resp = client.set('rawcounter', 10, 0, :raw => true)
109
+ assert_equal true, resp
110
+
111
+ resp = client.get('rawcounter', :raw => true)
112
+ assert_equal '10', resp
113
+
114
+ resp = client.incr('rawcounter', 1)
115
+ assert_equal 11, resp
116
+ end
117
+ end
118
+
89
119
  should "support incr/decr operations" do
90
120
  memcached do |dc|
91
121
  dc.flush
@@ -140,6 +170,9 @@ class TestDalli < Test::Unit::TestCase
140
170
  assert_not_nil resp
141
171
  assert_equal [true, true], resp
142
172
 
173
+ assert_equal true, dc.set(:foo, 'bar')
174
+ assert_equal 'bar', dc.get(:foo)
175
+
143
176
  resp = dc.get('123')
144
177
  assert_equal nil, resp
145
178
 
@@ -152,22 +185,19 @@ class TestDalli < Test::Unit::TestCase
152
185
  resp = dc.set('123', 'abc')
153
186
  assert_equal true, resp
154
187
 
155
- assert_raises Dalli::DalliError do
156
- dc.prepend('123', '0')
157
- end
188
+ dc.prepend('123', '0')
189
+ dc.append('123', '0')
158
190
 
159
191
  assert_raises Dalli::DalliError do
160
- dc.append('123', '0')
192
+ resp = dc.get('123')
161
193
  end
162
194
 
163
- resp = dc.get('123')
164
- assert_equal 'abc', resp
165
195
  dc.close
166
196
  dc = nil
167
197
 
168
- dc = Dalli::Client.new('localhost:19122', :marshal => false)
198
+ dc = Dalli::Client.new('localhost:19122')
169
199
 
170
- resp = dc.set('456', 'xyz')
200
+ resp = dc.set('456', 'xyz', 0, :raw => true)
171
201
  assert_equal true, resp
172
202
 
173
203
  resp = dc.prepend '456', '0'
@@ -176,7 +206,7 @@ class TestDalli < Test::Unit::TestCase
176
206
  resp = dc.append '456', '9'
177
207
  assert_equal true, resp
178
208
 
179
- resp = dc.get('456')
209
+ resp = dc.get('456', :raw => true)
180
210
  assert_equal '0xyz9', resp
181
211
 
182
212
  resp = dc.stats
@@ -251,6 +281,8 @@ class TestDalli < Test::Unit::TestCase
251
281
 
252
282
  should 'support SASL authentication' do
253
283
  memcached(19121, '-S') do |dc|
284
+ # I get "Dalli::NetworkError: Error authenticating: 32" in OSX
285
+ # but SASL works on Heroku servers. YMMV.
254
286
  assert_equal true, dc.set('abc', 123)
255
287
  assert_equal 123, dc.get('abc')
256
288
  assert_equal({"localhost:19121"=>{}}, dc.stats)
@@ -1,5 +1,9 @@
1
1
  require 'helper'
2
2
 
3
+ if RAILS_VERSION =~ /3\.0\./
4
+ # Session testing is complex enough that I can't test it in both Rails
5
+ # 3.0 and 2.3. Help is welcome.
6
+
3
7
  require 'abstract_unit'
4
8
  require 'action_dispatch/middleware/session/dalli_store'
5
9
 
@@ -197,4 +201,5 @@ class TestSessionStore < ActionController::IntegrationTest
197
201
  yield
198
202
  end
199
203
  end
204
+ end
200
205
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 4
9
- version: 0.9.4
8
+ - 5
9
+ version: 0.9.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Mike Perham
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-28 00:00:00 -07:00
17
+ date: 2010-09-15 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency