memcached_store 0.11.3 → 0.12.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0004cd75061b888d9a2f577b814ebbebb6641e0c
4
- data.tar.gz: 1094e612fcbbacea703db393fb4aa1d5188675a8
3
+ metadata.gz: 171a18683c77797d79f6c336d29904a50120607b
4
+ data.tar.gz: 3b2dc285bb4064f3b73dd620174934527a03cf5a
5
5
  SHA512:
6
- metadata.gz: e9fa84e1fb05038ae55dcb562c65c191f69aa716e9272cf4533414f116865254a7f3f7dd2b781bc68230b6755e6f00d51c6e617c722887728a60c28d24a0f098
7
- data.tar.gz: f60ddc1f132a2956d3810b3295c4ed8c07bc8a21b7393e10c59cd0a253d26210d69f3af824d0428047d756910b7d797a1a9984f1b6a515e2b5cb256b49ea14be
6
+ metadata.gz: 52711d0a3a25db301f523fb90b097392d653965876d93d24e80cedc542526b345f825a1e61a32d116a94a963024aebdf36264a076712f64841977836138cc467
7
+ data.tar.gz: d6dbb652bc7fc0b58ee187ca023e512723daa77095aa8333f78af6dc14345d8f120250d0fd88801763555cb2582aa05429f08bb7b1b4ac886dee04d0f2d5d013
@@ -64,6 +64,41 @@ module ActiveSupport
64
64
  {}
65
65
  end
66
66
 
67
+ def cas(name, options = nil)
68
+ options = merged_options(options)
69
+ options.merge!(:raw => true)
70
+ key = namespaced_key(name, options)
71
+
72
+ @data.cas(key, expiration(options), true) do |raw_value|
73
+ entry = deserialize_entry(raw_value)
74
+ value = yield entry.value
75
+ serialize_entry(Entry.new(value, options), options)
76
+ end
77
+ rescue *NONFATAL_EXCEPTIONS => e
78
+ @data.log_exception(e)
79
+ false
80
+ end
81
+
82
+ def cas_multi(*names)
83
+ options = names.extract_options!
84
+ options = merged_options(options)
85
+ options.merge!(:raw => true)
86
+ keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
87
+
88
+ @data.cas(keys_to_names.keys, expiration(options), true) do |raw_values|
89
+ values = {}
90
+ raw_values.each do |key, raw_value|
91
+ entry = deserialize_entry(raw_value)
92
+ values[keys_to_names[key]] = entry.value unless entry.expired?
93
+ end
94
+ values = yield values
95
+ Hash[values.map{|name, value| [escape_key(namespaced_key(name, options)), serialize_entry(Entry.new(value, options), options)]}]
96
+ end
97
+ rescue *NONFATAL_EXCEPTIONS => e
98
+ @data.log_exception(e)
99
+ false
100
+ end
101
+
67
102
  def increment(name, amount = 1, options = nil) # :nodoc:
68
103
  options = merged_options(options)
69
104
  instrument(:increment, name, :amount => amount) do
@@ -106,11 +141,7 @@ module ActiveSupport
106
141
 
107
142
  def write_entry(key, entry, options) # :nodoc:
108
143
  method = options && options[:unless_exist] ? :add : :set
109
- expires_in = options[:expires_in].to_i
110
- if expires_in > 0 && !options[:raw]
111
- # Set the memcache expire a few minutes in the future to support race condition ttls on read
112
- expires_in += 5.minutes
113
- end
144
+ expires_in = expiration(options)
114
145
  value = serialize_entry(entry, options)
115
146
  @data.send(method, escape_key(key), value, expires_in, options[:raw])
116
147
  rescue *NONFATAL_EXCEPTIONS => e
@@ -150,6 +181,15 @@ module ActiveSupport
150
181
  entry
151
182
  end
152
183
 
184
+ def expiration(options)
185
+ expires_in = options[:expires_in].to_i
186
+ if expires_in > 0 && !options[:raw]
187
+ # Set the memcache expire a few minutes in the future to support race condition ttls on read
188
+ expires_in += 5.minutes
189
+ end
190
+ expires_in
191
+ end
192
+
153
193
  end
154
194
  end
155
195
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module MemcachedStore
3
- VERSION = "0.11.3"
3
+ VERSION = "0.12.0"
4
4
  end
@@ -4,8 +4,8 @@ $:.unshift lib unless $:.include?(lib)
4
4
  require "memcached_store/version"
5
5
 
6
6
  Gem::Specification.new do |gem|
7
- gem.authors = ["Camilo Lopez", "Tom Burns", "Arthur Neves"]
8
- gem.email = ["camilo@camilolopez.com", "tom.burns@shopify.com", "arthurnn@gmail.com"]
7
+ gem.authors = ["Camilo Lopez", "Tom Burns", "Arthur Neves", "Francis Bogsanyi"]
8
+ gem.email = ["camilo@camilolopez.com", "tom.burns@shopify.com", "arthurnn@gmail.com", "francis.bogsanyi@shopify.com"]
9
9
  gem.summary = gem.description = %q{Plugin-able Memcached adapters to add features (compression, safety)}
10
10
  gem.homepage = "https://github.com/Shopify/memcached_store/"
11
11
 
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  class TestMemcachedStore < ActiveSupport::TestCase
4
4
  setup do
5
- @cache = ActiveSupport::Cache.lookup_store(:memcached_store, expires_in: 60)
5
+ @cache = ActiveSupport::Cache.lookup_store(:memcached_store, expires_in: 60, support_cas: true)
6
6
  @cache.clear
7
7
  end
8
8
 
@@ -51,6 +51,72 @@ class TestMemcachedStore < ActiveSupport::TestCase
51
51
  assert_nil @cache.fetch('foo') { 'baz' }
52
52
  end
53
53
 
54
+ def test_cas
55
+ @cache.write('foo', nil)
56
+ assert @cache.cas('foo') {|value| assert_nil value; 'bar' }
57
+ assert_equal 'bar', @cache.read('foo')
58
+ end
59
+
60
+ def test_cas_with_cache_miss
61
+ refute @cache.cas('not_exist') {|value| flunk }
62
+ end
63
+
64
+ def test_cas_with_conflict
65
+ @cache.write('foo', 'bar')
66
+ refute @cache.cas('foo') {|value|
67
+ @cache.write('foo', 'baz')
68
+ 'biz'
69
+ }
70
+ assert_equal 'baz', @cache.read('foo')
71
+ end
72
+
73
+ def test_cas_multi_with_empty_set
74
+ refute @cache.cas_multi() {|hash| flunk }
75
+ end
76
+
77
+ def test_cas_multi
78
+ @cache.write('foo', 'bar')
79
+ @cache.write('fud', 'biz')
80
+ assert @cache.cas_multi('foo', 'fud') {|hash| assert_equal({"foo" => "bar", "fud" => "biz"}, hash); {"foo" => "baz", "fud" => "buz"} }
81
+ assert_equal({"foo" => "baz", "fud" => "buz"}, @cache.read_multi('foo', 'fud'))
82
+ end
83
+
84
+ def test_cas_multi_with_altered_key
85
+ @cache.write('foo', 'baz')
86
+ assert @cache.cas_multi('foo') {|hash| {'fu' => 'baz'}}
87
+ assert_nil @cache.read('fu')
88
+ assert_equal 'baz', @cache.read('foo')
89
+ end
90
+
91
+ def test_cas_multi_with_cache_miss
92
+ assert @cache.cas_multi('not_exist') {|hash| assert hash.empty?; {} }
93
+ end
94
+
95
+ def test_cas_multi_with_partial_miss
96
+ @cache.write('foo', 'baz')
97
+ assert @cache.cas_multi('foo', 'bar') {|hash| assert_equal({"foo" => "baz"}, hash); {} }
98
+ assert_equal 'baz', @cache.read('foo')
99
+ end
100
+
101
+ def test_cas_multi_with_partial_update
102
+ @cache.write('foo', 'bar')
103
+ @cache.write('fud', 'biz')
104
+ assert @cache.cas_multi('foo', 'fud') {|hash| assert_equal({"foo" => "bar", "fud" => "biz"}, hash); {"foo" => "baz"} }
105
+ assert_equal({"foo" => "baz", "fud" => "biz"}, @cache.read_multi('foo', 'fud'))
106
+ end
107
+
108
+ def test_cas_multi_with_partial_conflict
109
+ @cache.write('foo', 'bar')
110
+ @cache.write('fud', 'biz')
111
+ result = @cache.cas_multi('foo', 'fud') do |hash|
112
+ assert_equal({"foo" => "bar", "fud" => "biz"}, hash)
113
+ @cache.write('foo', 'bad')
114
+ {"foo" => "baz", "fud" => "buz"}
115
+ end
116
+ assert result
117
+ assert_equal({"foo" => "bad", "fud" => "buz"}, @cache.read_multi('foo', 'fud'))
118
+ end
119
+
54
120
  def test_should_read_and_write_hash
55
121
  assert @cache.write('foo', {:a => "b"})
56
122
  assert_equal({:a => "b"}, @cache.read('foo'))
metadata CHANGED
@@ -1,16 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memcached_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.3
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Camilo Lopez
8
8
  - Tom Burns
9
9
  - Arthur Neves
10
+ - Francis Bogsanyi
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2014-05-08 00:00:00.000000000 Z
14
+ date: 2014-05-09 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
17
  name: activesupport
@@ -115,6 +116,7 @@ email:
115
116
  - camilo@camilolopez.com
116
117
  - tom.burns@shopify.com
117
118
  - arthurnn@gmail.com
119
+ - francis.bogsanyi@shopify.com
118
120
  executables: []
119
121
  extensions: []
120
122
  extra_rdoc_files: []