memcache 1.3.0 → 1.4.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 +7 -0
- data/.rbenv-version +1 -1
- data/VERSION +1 -1
- data/ext/native_server.c +23 -19
- data/lib/memcache.rb +33 -37
- data/lib/memcache/base.rb +6 -4
- data/lib/memcache/local_server.rb +5 -5
- data/lib/memcache/migration.rb +1 -0
- data/lib/memcache/pg_server.rb +22 -16
- data/lib/memcache/segmented.rb +15 -18
- data/lib/memcache/server.rb +6 -3
- data/memcache.gemspec +2 -0
- data/test/memcache_local_server_test.rb +1 -1
- data/test/memcache_native_server_test.rb +2 -2
- data/test/memcache_pg_server_test.rb +1 -1
- data/test/memcache_segmented_test_helper.rb +1 -1
- data/test/memcache_server_test.rb +1 -1
- data/test/memcache_server_test_helper.rb +48 -51
- data/test/memcache_test.rb +3 -2
- data/test/test_helper.rb +1 -2
- metadata +90 -110
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 825860f91b9b3291cc74c1f735661c2bc9031d43
|
4
|
+
data.tar.gz: 20397ed826b4973ff8662c00c4dac90ca528d7cd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c086c05cf5f57dbdd525558e38f859776c589fca4b25a01826a504c0cef6c3e525723aac4dbd5daa596c3c5ef4d2c00153e1d4a75b705d1a3817b32fb512cfe
|
7
|
+
data.tar.gz: 9c568fdbb4bdb0ca8e8e22be657fba9e56373dc926c547a9066ee2e0dc268d61a576c780f1221e8dde4d244c2bc3371cbce8af4dc9eb3b9c966aa7a73264c19b
|
data/.rbenv-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0-p247
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.4.0
|
data/ext/native_server.c
CHANGED
@@ -20,6 +20,9 @@ VALUE sym_binary;
|
|
20
20
|
VALUE sym_servers;
|
21
21
|
VALUE sym_ketama;
|
22
22
|
VALUE sym_ketama_weighted;
|
23
|
+
VALUE sym_value;
|
24
|
+
VALUE sym_flags;
|
25
|
+
VALUE sym_cas;
|
23
26
|
|
24
27
|
ID id_default;
|
25
28
|
ID id_md5;
|
@@ -36,8 +39,6 @@ ID id_consistent;
|
|
36
39
|
ID id_ketama;
|
37
40
|
ID id_ketama_spy;
|
38
41
|
|
39
|
-
static ID iv_memcache_flags, iv_memcache_cas;
|
40
|
-
|
41
42
|
static void mc_free(void *p) {
|
42
43
|
memcached_free(p);
|
43
44
|
}
|
@@ -83,7 +84,7 @@ static memcached_hash_t hash_behavior(VALUE sym) {
|
|
83
84
|
rb_raise(cMemcacheError, "Invalid hash behavior");
|
84
85
|
}
|
85
86
|
|
86
|
-
static
|
87
|
+
static memcached_server_distribution_t distribution_behavior(VALUE sym) {
|
87
88
|
ID id = SYM2ID(sym);
|
88
89
|
|
89
90
|
if (id == id_modula ) return MEMCACHED_DISTRIBUTION_MODULA;
|
@@ -241,7 +242,7 @@ static bool use_binary(memcached_st* mc) {
|
|
241
242
|
|
242
243
|
static VALUE mc_get(int argc, VALUE *argv, VALUE self) {
|
243
244
|
memcached_st *mc;
|
244
|
-
VALUE cas, keys, results, key,
|
245
|
+
VALUE cas, keys, results, key, result;
|
245
246
|
VALUE scalar_key = Qnil;
|
246
247
|
memcached_return status;
|
247
248
|
|
@@ -264,16 +265,17 @@ static VALUE mc_get(int argc, VALUE *argv, VALUE self) {
|
|
264
265
|
if (str == NULL) return Qnil;
|
265
266
|
|
266
267
|
if (status == MEMCACHED_SUCCESS) {
|
267
|
-
|
268
|
-
|
268
|
+
result = rb_hash_new();
|
269
|
+
rb_hash_aset(result, sym_value, rb_str_new(str, len));
|
270
|
+
rb_hash_aset(result, sym_flags, INT2NUM(flags));
|
269
271
|
free(str);
|
270
|
-
return
|
272
|
+
return result;
|
271
273
|
} else {
|
272
274
|
printf("Memcache read error: %s %u\n", memcached_strerror(mc, status), status);
|
273
275
|
return Qnil;
|
274
276
|
}
|
275
277
|
} else {
|
276
|
-
memcached_result_st*
|
278
|
+
memcached_result_st* mc_result;
|
277
279
|
size_t num_keys, i;
|
278
280
|
const char** key_strings;
|
279
281
|
size_t* key_lengths;
|
@@ -295,19 +297,21 @@ static VALUE mc_get(int argc, VALUE *argv, VALUE self) {
|
|
295
297
|
|
296
298
|
memcached_mget(mc, key_strings, key_lengths, num_keys);
|
297
299
|
|
298
|
-
while (
|
300
|
+
while ((mc_result = memcached_fetch_result(mc, NULL, &status))) {
|
299
301
|
if (escaped) {
|
300
|
-
key = unescape_key(memcached_result_key_value(
|
302
|
+
key = unescape_key(memcached_result_key_value(mc_result), memcached_result_key_length(mc_result));
|
301
303
|
} else {
|
302
|
-
key = rb_str_new(memcached_result_key_value(
|
304
|
+
key = rb_str_new(memcached_result_key_value(mc_result), memcached_result_key_length(mc_result));
|
303
305
|
}
|
304
306
|
|
305
307
|
if (status == MEMCACHED_SUCCESS) {
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
rb_hash_aset(
|
308
|
+
result = rb_hash_new();
|
309
|
+
rb_hash_aset(result, sym_value, rb_str_new(memcached_result_value(mc_result),
|
310
|
+
memcached_result_length(mc_result)));
|
311
|
+
rb_hash_aset(result, sym_flags, INT2NUM(memcached_result_flags(mc_result)));
|
312
|
+
if (RTEST(cas)) rb_hash_aset(result, sym_cas, ULL2NUM(memcached_result_cas(mc_result)));
|
313
|
+
memcached_result_free(mc_result);
|
314
|
+
rb_hash_aset(results, key, result);
|
311
315
|
} else {
|
312
316
|
printf("Memcache read error: %s %u\n", memcached_strerror(mc, status), status);
|
313
317
|
}
|
@@ -590,9 +594,9 @@ void Init_native_server() {
|
|
590
594
|
sym_servers = ID2SYM(rb_intern("servers"));
|
591
595
|
sym_ketama = ID2SYM(rb_intern("ketama"));
|
592
596
|
sym_ketama_weighted = ID2SYM(rb_intern("ketama_weighted"));
|
593
|
-
|
594
|
-
|
595
|
-
|
597
|
+
sym_value = ID2SYM(rb_intern("value"));
|
598
|
+
sym_flags = ID2SYM(rb_intern("flags"));
|
599
|
+
sym_cas = ID2SYM(rb_intern("cas"));
|
596
600
|
|
597
601
|
id_default = rb_intern("default");
|
598
602
|
id_md5 = rb_intern("md5");
|
data/lib/memcache.rb
CHANGED
@@ -106,21 +106,24 @@ class Memcache
|
|
106
106
|
|
107
107
|
def get(keys, opts = {})
|
108
108
|
raise 'opts must be hash' unless opts.instance_of?(Hash)
|
109
|
-
|
110
109
|
if keys.instance_of?(Array)
|
111
110
|
keys = keys.collect {|key| key.to_s}
|
112
111
|
multi_get(keys, opts)
|
113
112
|
else
|
114
113
|
key = keys.to_s
|
115
114
|
if opts[:expiry]
|
116
|
-
|
117
|
-
cas(key, value, :raw => true, :cas =>
|
115
|
+
result = server(key).gets(key)
|
116
|
+
cas(key, result[:value], :raw => true, :cas => result[:cas], :expiry => opts[:expiry]) if result
|
118
117
|
else
|
119
|
-
|
118
|
+
result = server(key).get(key, opts[:cas])
|
120
119
|
end
|
121
120
|
|
122
|
-
|
123
|
-
|
121
|
+
if result
|
122
|
+
result[:value] = unmarshal(result[:value], key) unless opts[:raw]
|
123
|
+
opts[:meta] ? result : result[:value]
|
124
|
+
elsif backup
|
125
|
+
backup.get(key, opts)
|
126
|
+
end
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
@@ -217,53 +220,54 @@ class Memcache
|
|
217
220
|
end
|
218
221
|
|
219
222
|
def update(key, opts = {})
|
220
|
-
key
|
221
|
-
|
222
|
-
if
|
223
|
-
cas(key, yield(value), opts.merge!(:cas =>
|
223
|
+
key = key.to_s
|
224
|
+
result = get(key, :cas => true, :meta => true)
|
225
|
+
if result
|
226
|
+
cas(key, yield(result[:value]), opts.merge!(:cas => result[:cas]))
|
224
227
|
else
|
225
|
-
add(key, yield(value), opts)
|
228
|
+
add(key, yield(result[:value]), opts)
|
226
229
|
end
|
227
230
|
end
|
228
231
|
|
229
|
-
def get_or_add(key, *args)
|
232
|
+
def get_or_add(key, *args, &block)
|
230
233
|
# Pseudo-atomic get and update.
|
231
234
|
key = key.to_s
|
232
|
-
if
|
235
|
+
if block
|
233
236
|
opts = args[0] || {}
|
234
|
-
get(key) || add(key, yield, opts) || get(key)
|
235
237
|
else
|
236
238
|
opts = args[1] || {}
|
237
|
-
|
239
|
+
block = lambda { args[0] }
|
238
240
|
end
|
241
|
+
get(key, opts) || add(key, block.call(), opts) || get(key, opts)
|
239
242
|
end
|
240
243
|
|
241
|
-
def get_or_set(key, *args)
|
244
|
+
def get_or_set(key, *args, &block)
|
242
245
|
key = key.to_s
|
243
246
|
if block_given?
|
244
247
|
opts = args[0] || {}
|
245
|
-
get(key) || set(key, yield, opts)
|
246
248
|
else
|
247
249
|
opts = args[1] || {}
|
248
|
-
|
250
|
+
block = lambda { args[0] }
|
249
251
|
end
|
252
|
+
get(key, opts) || set(key, block.call(), opts)
|
250
253
|
end
|
251
254
|
|
252
255
|
def add_or_get(key, value, opts = {})
|
253
256
|
# Try to add, but if that fails, get the existing value.
|
254
|
-
add(key, value, opts) || get(key)
|
257
|
+
add(key, value, opts) || get(key, opts)
|
255
258
|
end
|
256
259
|
|
257
260
|
def get_some(keys, opts = {})
|
258
|
-
keys
|
259
|
-
|
261
|
+
keys = keys.collect {|key| key.to_s}
|
262
|
+
results = opts[:disable] ? {} : self.multi_get(keys, opts)
|
260
263
|
if opts[:validation]
|
261
|
-
|
264
|
+
results.delete_if do |key, result|
|
265
|
+
value = opts[:meta] ? result[:value] : result
|
262
266
|
not opts[:validation].call(key, value)
|
263
267
|
end
|
264
268
|
end
|
265
269
|
|
266
|
-
keys_to_fetch = keys -
|
270
|
+
keys_to_fetch = keys - results.keys
|
267
271
|
if keys_to_fetch.any?
|
268
272
|
yield(keys_to_fetch).each do |key, value|
|
269
273
|
begin
|
@@ -272,10 +276,10 @@ class Memcache
|
|
272
276
|
raise if opts[:strict_write]
|
273
277
|
$stderr.puts "Memcache error in get_some: #{e.class} #{e.to_s} on key '#{key}' while storing value: #{value}"
|
274
278
|
end
|
275
|
-
|
279
|
+
results[key] = opts[:meta] ? {:value => value} : value
|
276
280
|
end
|
277
281
|
end
|
278
|
-
|
282
|
+
results
|
279
283
|
end
|
280
284
|
|
281
285
|
def lock(key, opts = {})
|
@@ -385,8 +389,9 @@ protected
|
|
385
389
|
|
386
390
|
results = {}
|
387
391
|
fetch_results = lambda do |server, keys|
|
388
|
-
server.get(keys, opts[:cas]).each do |key,
|
389
|
-
|
392
|
+
server.get(keys, opts[:cas]).each do |key, result|
|
393
|
+
result[:value] = unmarshal(result[:value], key) unless opts[:raw]
|
394
|
+
results[key] = opts[:meta] ? result : result[:value]
|
390
395
|
end
|
391
396
|
end
|
392
397
|
|
@@ -419,11 +424,7 @@ protected
|
|
419
424
|
|
420
425
|
def unmarshal(value, key = nil)
|
421
426
|
return value if value.nil?
|
422
|
-
|
423
|
-
object = Marshal.load(value)
|
424
|
-
object.memcache_flags = value.memcache_flags
|
425
|
-
object.memcache_cas = value.memcache_cas
|
426
|
-
object
|
427
|
+
Marshal.load(value)
|
427
428
|
rescue Exception => e
|
428
429
|
$stderr.puts "Memcache read error: #{e.class} #{e.to_s} on key '#{key}' while unmarshalling value: #{value}"
|
429
430
|
$stderr.puts caller
|
@@ -472,8 +473,3 @@ protected
|
|
472
473
|
@@cache_pool ||= Pool.new
|
473
474
|
end
|
474
475
|
end
|
475
|
-
|
476
|
-
# Add flags and cas
|
477
|
-
class Object
|
478
|
-
attr_accessor :memcache_flags, :memcache_cas
|
479
|
-
end
|
data/lib/memcache/base.rb
CHANGED
@@ -13,8 +13,10 @@ class Memcache
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def incr(key, amount = 1)
|
16
|
-
|
17
|
-
return unless
|
16
|
+
result = get(key)
|
17
|
+
return unless result
|
18
|
+
|
19
|
+
value = result[:value]
|
18
20
|
return unless value =~ /^\d+$/
|
19
21
|
|
20
22
|
value = value.to_i + amount
|
@@ -45,13 +47,13 @@ class Memcache
|
|
45
47
|
def append(key, value)
|
46
48
|
existing = get(key)
|
47
49
|
return false if existing.nil?
|
48
|
-
set(key, existing + value) && true
|
50
|
+
set(key, existing[:value] + value) && true
|
49
51
|
end
|
50
52
|
|
51
53
|
def prepend(key, value)
|
52
54
|
existing = get(key)
|
53
55
|
return false if existing.nil?
|
54
|
-
set(key, value + existing) && true
|
56
|
+
set(key, value + existing[:value]) && true
|
55
57
|
end
|
56
58
|
|
57
59
|
protected
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Memcache
|
2
2
|
class LocalServer < Base
|
3
3
|
def initialize
|
4
|
-
@data
|
4
|
+
@data = {}
|
5
5
|
@expiry = {}
|
6
6
|
end
|
7
7
|
|
@@ -26,9 +26,9 @@ class Memcache
|
|
26
26
|
if keys.kind_of?(Array)
|
27
27
|
hash = {}
|
28
28
|
keys.each do |key|
|
29
|
-
key
|
30
|
-
|
31
|
-
hash[key] =
|
29
|
+
key = key.to_s
|
30
|
+
result = get(key)
|
31
|
+
hash[key] = result if result
|
32
32
|
end
|
33
33
|
hash
|
34
34
|
else
|
@@ -43,7 +43,7 @@ class Memcache
|
|
43
43
|
|
44
44
|
def set(key, value, expiry = 0, flags = 0)
|
45
45
|
key = cache_key(key)
|
46
|
-
@data[key] = value.to_s
|
46
|
+
@data[key] = {:value => value.to_s, :flags => flags}
|
47
47
|
expiry = Time.at(expiry) if expiry > 60*60*24*30
|
48
48
|
if expiry.kind_of?(Time)
|
49
49
|
@expiry[key] = expiry
|
data/lib/memcache/migration.rb
CHANGED
data/lib/memcache/pg_server.rb
CHANGED
@@ -28,27 +28,30 @@ class Memcache
|
|
28
28
|
db.exec("TRUNCATE #{table}")
|
29
29
|
end
|
30
30
|
|
31
|
-
def get(keys)
|
31
|
+
def get(keys, cas = false)
|
32
|
+
# cas ignored for now
|
32
33
|
return get([keys])[keys.to_s] unless keys.kind_of?(Array)
|
33
34
|
return {} if keys.empty?
|
34
35
|
|
35
36
|
keys = keys.collect {|key| quote(key.to_s)}.join(',')
|
36
37
|
sql = %{
|
37
|
-
SELECT key, value FROM #{table}
|
38
|
+
SELECT key, value, flags FROM #{table}
|
38
39
|
WHERE key IN (#{keys}) AND #{prefix_clause} AND #{expiry_clause}
|
39
40
|
}
|
40
41
|
|
41
42
|
results = {}
|
42
43
|
db.query(sql).each do |row|
|
43
|
-
results[row['key']] = row['value']
|
44
|
+
results[row['key']] = {:value => row['value'], :flags => row['flags'].to_i}
|
44
45
|
end
|
45
46
|
results
|
46
47
|
end
|
47
48
|
|
48
49
|
def incr(key, amount = 1)
|
49
50
|
transaction do
|
50
|
-
|
51
|
-
return unless
|
51
|
+
result = get(key)
|
52
|
+
return unless result
|
53
|
+
|
54
|
+
value = result[:value]
|
52
55
|
return unless value =~ /^\d+$/
|
53
56
|
|
54
57
|
value = value.to_i + amount
|
@@ -73,25 +76,25 @@ class Memcache
|
|
73
76
|
result.cmdtuples == 1
|
74
77
|
end
|
75
78
|
|
76
|
-
def set(key, value, expiry = 0)
|
79
|
+
def set(key, value, expiry = 0, flags = 0)
|
77
80
|
transaction do
|
78
81
|
delete(key)
|
79
|
-
insert(key, value, expiry)
|
82
|
+
insert(key, value, expiry, flags)
|
80
83
|
end
|
81
84
|
value
|
82
85
|
end
|
83
86
|
|
84
|
-
def add(key, value, expiry = 0)
|
87
|
+
def add(key, value, expiry = 0, flags = 0)
|
85
88
|
delete_expired(key)
|
86
|
-
insert(key, value, expiry)
|
89
|
+
insert(key, value, expiry, flags)
|
87
90
|
value
|
88
91
|
rescue PGError => e
|
89
92
|
nil
|
90
93
|
end
|
91
94
|
|
92
|
-
def replace(key, value, expiry = 0)
|
95
|
+
def replace(key, value, expiry = 0, flags = 0)
|
93
96
|
delete_expired(key)
|
94
|
-
result = update(key, value, expiry)
|
97
|
+
result = update(key, value, expiry, flags)
|
95
98
|
result.cmdtuples == 1 ? value : nil
|
96
99
|
end
|
97
100
|
|
@@ -117,17 +120,20 @@ class Memcache
|
|
117
120
|
|
118
121
|
private
|
119
122
|
|
120
|
-
def insert(key, value, expiry
|
123
|
+
def insert(key, value, expiry, flags)
|
121
124
|
db.exec %{
|
122
|
-
INSERT INTO #{table} (prefix, key, value, updated_at, expires_at)
|
123
|
-
VALUES (#{quoted_prefix}, #{quote(key)}, #{quote(value)}, NOW(), #{expiry_sql(expiry)})
|
125
|
+
INSERT INTO #{table} (prefix, key, value, flags, updated_at, expires_at)
|
126
|
+
VALUES (#{quoted_prefix}, #{quote(key)}, #{quote(value)}, #{flags.to_i}, NOW(), #{expiry_sql(expiry)})
|
124
127
|
}
|
125
128
|
end
|
126
129
|
|
127
|
-
def update(key, value, expiry
|
130
|
+
def update(key, value, expiry, flags)
|
128
131
|
db.exec %{
|
129
132
|
UPDATE #{table}
|
130
|
-
SET value = #{quote(value)},
|
133
|
+
SET value = #{quote(value)},
|
134
|
+
flags = #{flags.to_i},
|
135
|
+
updated_at = NOW(),
|
136
|
+
expires_at = #{expiry_sql(expiry)}
|
131
137
|
WHERE key = #{quote(key)} AND #{prefix_clause}
|
132
138
|
}
|
133
139
|
end
|
data/lib/memcache/segmented.rb
CHANGED
@@ -12,9 +12,9 @@ class Memcache
|
|
12
12
|
results = super(keys, cas)
|
13
13
|
keys = {}
|
14
14
|
keys_to_fetch = []
|
15
|
-
results.each do |key,
|
16
|
-
next unless segmented?(
|
17
|
-
keys[key] = segment_keys(
|
15
|
+
results.each do |key, result|
|
16
|
+
next unless segmented?(result)
|
17
|
+
keys[key] = segment_keys(result)
|
18
18
|
keys_to_fetch.concat keys[key]
|
19
19
|
end
|
20
20
|
|
@@ -22,7 +22,7 @@ class Memcache
|
|
22
22
|
keys.each do |key, hashes|
|
23
23
|
value = ''
|
24
24
|
hashes.each do |hash_key|
|
25
|
-
if part = parts[hash_key]
|
25
|
+
if part = parts[hash_key][:value]
|
26
26
|
value << part
|
27
27
|
else
|
28
28
|
value = nil
|
@@ -30,11 +30,8 @@ class Memcache
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
value.memcache_flags = results[key].memcache_flags ^ PARTIAL_VALUE
|
36
|
-
end
|
37
|
-
results[key] = value
|
33
|
+
results[key][:value] = value
|
34
|
+
results[key][:flags] ^= PARTIAL_VALUE
|
38
35
|
end
|
39
36
|
results
|
40
37
|
end
|
@@ -66,18 +63,18 @@ class Memcache
|
|
66
63
|
end
|
67
64
|
|
68
65
|
def delete(key)
|
69
|
-
|
70
|
-
|
71
|
-
if result and segmented?(
|
72
|
-
segment_keys(
|
66
|
+
result = super_get(key)
|
67
|
+
enable = block_given? ? yield : super
|
68
|
+
if enable and result and segmented?(result)
|
69
|
+
segment_keys(result).each {|k| super(k)}
|
73
70
|
end
|
74
|
-
|
71
|
+
enable
|
75
72
|
end
|
76
73
|
|
77
74
|
private
|
78
75
|
|
79
|
-
def segmented?(
|
80
|
-
|
76
|
+
def segmented?(result)
|
77
|
+
result[:flags] & PARTIAL_VALUE == PARTIAL_VALUE
|
81
78
|
end
|
82
79
|
|
83
80
|
def segment(key, value)
|
@@ -105,8 +102,8 @@ class Memcache
|
|
105
102
|
end
|
106
103
|
end
|
107
104
|
|
108
|
-
def segment_keys(
|
109
|
-
hash, num = value.split(':')
|
105
|
+
def segment_keys(result)
|
106
|
+
hash, num = result[:value].split(':')
|
110
107
|
(0...num.to_i).collect {|i| "#{hash}:#{i}"}
|
111
108
|
end
|
112
109
|
|
data/lib/memcache/server.rb
CHANGED
@@ -94,11 +94,14 @@ class Memcache
|
|
94
94
|
value = socket.read(length.to_i)
|
95
95
|
match_response!(socket.read(2), "\r\n")
|
96
96
|
|
97
|
-
|
98
|
-
|
97
|
+
result = {
|
98
|
+
:value => value,
|
99
|
+
:flags => flags.to_i,
|
100
|
+
}
|
101
|
+
result[:cas] = cas if cas
|
99
102
|
|
100
103
|
key = input_key(key)
|
101
|
-
results[key] =
|
104
|
+
results[key] = result
|
102
105
|
end
|
103
106
|
results
|
104
107
|
end
|
data/memcache.gemspec
CHANGED
@@ -9,6 +9,7 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.description = %q{Ruby client for memcached supporting advanced protocol features and pluggable architecture.}
|
10
10
|
gem.summary = gem.description
|
11
11
|
gem.homepage = "https://github.com/ninjudd/memcache"
|
12
|
+
gem.license = 'MIT'
|
12
13
|
|
13
14
|
gem.add_development_dependency 'rake'
|
14
15
|
gem.add_development_dependency 'shoulda', '3.0.1'
|
@@ -20,4 +21,5 @@ Gem::Specification.new do |gem|
|
|
20
21
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
22
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
23
|
gem.require_paths = ["lib"]
|
24
|
+
gem.extensions = ["ext/extconf.rb"]
|
23
25
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'memcache_server_test_helper'
|
1
|
+
require File.dirname(__FILE__) + '/memcache_server_test_helper'
|
2
2
|
|
3
3
|
class MemcacheNativeServerTest < Test::Unit::TestCase
|
4
4
|
include MemcacheServerTestHelper
|
@@ -27,6 +27,6 @@ class MemcacheNativeServerTest < Test::Unit::TestCase
|
|
27
27
|
m.close
|
28
28
|
|
29
29
|
m.set('foo', 'foo')
|
30
|
-
assert_equal 'foo', m.get('foo')
|
30
|
+
assert_equal 'foo', m.get('foo')[:value]
|
31
31
|
end
|
32
32
|
end
|
@@ -8,7 +8,7 @@ module MemcacheSegmentedTestHelper
|
|
8
8
|
assert_not_equal '1,2,3,4,5,6,7,8,9,10', master_key
|
9
9
|
assert_equal 7, segment_keys.size
|
10
10
|
|
11
|
-
assert_equal '1,2,3,4,5,6,7,8,9,10', m.get('fav_numbers')
|
11
|
+
assert_equal '1,2,3,4,5,6,7,8,9,10', m.get('fav_numbers')[:value]
|
12
12
|
assert_equal true, m.delete('fav_numbers')
|
13
13
|
assert_equal nil, m.get('fav_numbers')
|
14
14
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'test_helper'
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
2
|
|
3
3
|
module MemcacheServerTestHelper
|
4
4
|
def test_prefix
|
@@ -14,8 +14,8 @@ module MemcacheServerTestHelper
|
|
14
14
|
|
15
15
|
m.set('2', 'foo', 0)
|
16
16
|
assert_equal nil, m.get('1')
|
17
|
-
assert_equal 'foo', m.get('2')
|
18
|
-
assert_equal({'2'=>'foo'}, m.get(['1', '2']))
|
17
|
+
assert_equal 'foo', m.get('2')[:value]
|
18
|
+
assert_equal({'2' => {:value => 'foo', :flags => 0}}, m.get(['1', '2']))
|
19
19
|
|
20
20
|
assert_equal 'bar:', m.prefix = 'bar:'
|
21
21
|
assert_equal 'bar:', m.prefix
|
@@ -23,57 +23,58 @@ module MemcacheServerTestHelper
|
|
23
23
|
assert_equal nil, m.prefix = nil
|
24
24
|
assert_equal nil, m.prefix
|
25
25
|
|
26
|
-
assert_equal 'baz', m.get('1')
|
27
|
-
assert_equal 'bar', m.get('2')
|
28
|
-
assert_equal({'1'=>'baz',
|
26
|
+
assert_equal 'baz', m.get('1')[:value]
|
27
|
+
assert_equal 'bar', m.get('2')[:value]
|
28
|
+
assert_equal({'1' => {:value => 'baz', :flags => 0},
|
29
|
+
'2' => {:value => 'bar', :flags => 0}}, m.get(['1', '2']))
|
29
30
|
end
|
30
31
|
|
31
32
|
def test_set_and_get
|
32
33
|
assert_equal 'foo', m.set('2', 'foo', 0)
|
33
34
|
|
34
|
-
assert_equal 'foo', m.get('2')
|
35
|
-
assert_equal 'foo', m.get('2')
|
35
|
+
assert_equal 'foo', m.get('2')[:value]
|
36
|
+
assert_equal 'foo', m.get('2')[:value]
|
36
37
|
|
37
38
|
assert_equal 'bar', m.set('2', 'bar', 0)
|
38
39
|
|
39
|
-
assert_equal 'bar', m.get('2')
|
40
|
-
assert_equal 'bar', m.get('2')
|
40
|
+
assert_equal 'bar', m.get('2')[:value]
|
41
|
+
assert_equal 'bar', m.get('2')[:value]
|
41
42
|
end
|
42
43
|
|
43
44
|
def test_spaces_in_keys
|
44
45
|
assert_equal '1', m.set('foo bar', '1', 0)
|
45
46
|
|
46
|
-
assert_equal '1', m.get('foo bar')
|
47
|
-
assert_equal '1', m.get('foo bar')
|
47
|
+
assert_equal '1', m.get('foo bar')[:value]
|
48
|
+
assert_equal '1', m.get('foo bar')[:value]
|
48
49
|
|
49
50
|
assert_equal '2', m.set('foo bar', '2', 0)
|
50
51
|
|
51
|
-
assert_equal '2', m.get('foo bar')
|
52
|
-
assert_equal '2', m.get('foo bar')
|
52
|
+
assert_equal '2', m.get('foo bar')[:value]
|
53
|
+
assert_equal '2', m.get('foo bar')[:value]
|
53
54
|
|
54
55
|
assert_equal '8', m.set('foo baz', '8', 0)
|
55
56
|
|
56
|
-
|
57
|
-
|
57
|
+
assert_equal({'foo bar' => {:value => '2', :flags => 0},
|
58
|
+
'foo baz' => {:value => '8', :flags => 0}}, m.get(['foo bar','foo baz']))
|
58
59
|
|
59
60
|
assert_equal 'foo', m.set(' ', 'foo', 0)
|
60
|
-
assert_equal 'foo', m.get(' ')
|
61
|
+
assert_equal 'foo', m.get(' ')[:value]
|
61
62
|
assert_equal true, m.delete(' ')
|
62
63
|
end
|
63
64
|
|
64
65
|
def test_expiry
|
65
66
|
assert_equal 'foo', m.set('foo', 'foo', 1)
|
66
|
-
assert_equal 'foo', m.get('foo')
|
67
|
+
assert_equal 'foo', m.get('foo')[:value]
|
67
68
|
|
68
69
|
assert_equal 'bar', m.add('bar', 'bar', 1)
|
69
|
-
assert_equal 'bar', m.get('bar')
|
70
|
+
assert_equal 'bar', m.get('bar')[:value]
|
70
71
|
|
71
72
|
assert_equal '', m.set('baz', '')
|
72
73
|
assert_equal 'baz', m.replace('baz', 'baz', 1)
|
73
|
-
assert_equal 'baz', m.get('baz')
|
74
|
+
assert_equal 'baz', m.get('baz')[:value]
|
74
75
|
|
75
76
|
assert_equal 'bap', m.set('bam', 'bap', (Time.now + 1).to_i)
|
76
|
-
assert_equal 'bap', m.get('bam')
|
77
|
+
assert_equal 'bap', m.get('bam')[:value]
|
77
78
|
|
78
79
|
sleep 2
|
79
80
|
|
@@ -90,13 +91,13 @@ module MemcacheServerTestHelper
|
|
90
91
|
|
91
92
|
# Add should only work if key doesn't exist.
|
92
93
|
assert_equal 'foo', m.add('foo', 'foo')
|
93
|
-
assert_equal 'foo', m.get('foo')
|
94
|
+
assert_equal 'foo', m.get('foo')[:value]
|
94
95
|
assert_equal nil, m.add('foo', 'bar')
|
95
|
-
assert_equal 'foo', m.get('foo')
|
96
|
+
assert_equal 'foo', m.get('foo')[:value]
|
96
97
|
|
97
98
|
# Replace should only work if key doesn't exist.
|
98
99
|
assert_equal 'bar', m.replace('foo', 'bar')
|
99
|
-
assert_equal 'bar', m.get('foo')
|
100
|
+
assert_equal 'bar', m.get('foo')[:value]
|
100
101
|
end
|
101
102
|
|
102
103
|
def test_append_and_prepend
|
@@ -105,10 +106,10 @@ module MemcacheServerTestHelper
|
|
105
106
|
|
106
107
|
m.set('foo', 'foo')
|
107
108
|
assert_equal true, m.append('foo', 'bar')
|
108
|
-
assert_equal 'foobar', m.get('foo')
|
109
|
+
assert_equal 'foobar', m.get('foo')[:value]
|
109
110
|
|
110
111
|
assert_equal true, m.prepend('foo', 'baz')
|
111
|
-
assert_equal 'bazfoobar', m.get('foo')
|
112
|
+
assert_equal 'bazfoobar', m.get('foo')[:value]
|
112
113
|
end
|
113
114
|
|
114
115
|
def test_incr
|
@@ -121,33 +122,33 @@ module MemcacheServerTestHelper
|
|
121
122
|
|
122
123
|
m.set('foo', '0')
|
123
124
|
assert_equal 1, m.incr('foo')
|
124
|
-
assert_equal '1', m.get('foo')
|
125
|
+
assert_equal '1', m.get('foo')[:value]
|
125
126
|
|
126
127
|
assert_equal 53, m.incr('foo', 52)
|
127
|
-
assert_equal '53', m.get('foo')
|
128
|
+
assert_equal '53', m.get('foo')[:value]
|
128
129
|
|
129
130
|
assert_equal 10, m.decr('foo', 43)
|
130
|
-
assert_equal '10', m.get('foo')
|
131
|
+
assert_equal '10', m.get('foo')[:value]
|
131
132
|
|
132
133
|
# Cannot go below zero.
|
133
134
|
assert_equal 0, m.decr('foo', 100)
|
134
|
-
assert_equal '0', m.get('foo').strip
|
135
|
+
assert_equal '0', m.get('foo')[:value].strip
|
135
136
|
end
|
136
137
|
|
137
138
|
def test_multi_get
|
138
139
|
m.set('2', '1,2,3')
|
139
140
|
m.set('3', '4,5')
|
140
141
|
|
141
|
-
expected = { '2' => '1,2,3', '3' => '4,5' }
|
142
|
+
expected = { '2' => {:value => '1,2,3', :flags => 0}, '3' => {:value => '4,5', :flags => 0} }
|
142
143
|
assert_equal expected, m.get(['2','3'])
|
143
|
-
assert_equal '4,5', m.get(['3'])['3']
|
144
|
+
assert_equal '4,5', m.get(['3'])['3'][:value]
|
144
145
|
assert_equal({}, m.get([]))
|
145
146
|
end
|
146
147
|
|
147
148
|
def test_delete
|
148
149
|
m.set('2', '1,2,3')
|
149
150
|
|
150
|
-
assert_equal '1,2,3', m.get('2')
|
151
|
+
assert_equal '1,2,3', m.get('2')[:value]
|
151
152
|
assert_equal true, m.delete('2')
|
152
153
|
assert_equal nil, m.get('2')
|
153
154
|
end
|
@@ -155,7 +156,7 @@ module MemcacheServerTestHelper
|
|
155
156
|
def test_flush_all
|
156
157
|
m.set('2', 'bar')
|
157
158
|
|
158
|
-
assert_equal 'bar', m.get('2')
|
159
|
+
assert_equal 'bar', m.get('2')[:value]
|
159
160
|
|
160
161
|
m.flush_all
|
161
162
|
|
@@ -165,39 +166,35 @@ module MemcacheServerTestHelper
|
|
165
166
|
module AdvancedMethods
|
166
167
|
def test_flags
|
167
168
|
m.set('thom', 'hartmann', 0)
|
168
|
-
|
169
|
-
assert_equal 0, value.memcache_flags
|
169
|
+
assert_equal 0, m.gets('thom')[:flags]
|
170
170
|
|
171
171
|
m.set('thom', 'hartmann', 0, 0b11110001)
|
172
|
-
|
173
|
-
assert_equal 0b11110001, value.memcache_flags
|
172
|
+
assert_equal 0b11110001, m.gets('thom')[:flags]
|
174
173
|
|
175
|
-
|
176
|
-
assert_equal 0b11110001, value.memcache_flags
|
174
|
+
assert_equal 0b11110001, m.get('thom')[:flags]
|
177
175
|
|
178
176
|
m.set('thom', 'hartmann', 0, 0b10101010)
|
179
|
-
|
180
|
-
assert_equal 0b10101010, value.memcache_flags
|
177
|
+
assert_equal 0b10101010, m.get('thom')[:flags]
|
181
178
|
end
|
182
179
|
|
183
180
|
def test_gets_and_cas
|
184
181
|
m.set('thom', 'hartmann')
|
185
182
|
|
186
|
-
|
187
|
-
assert_equal 'hartmann', value
|
188
|
-
assert_equal 'thompson', m.cas('thom', 'thompson',
|
189
|
-
assert_equal 'thompson', m.get('thom')
|
183
|
+
result = m.gets('thom')
|
184
|
+
assert_equal 'hartmann', result[:value]
|
185
|
+
assert_equal 'thompson', m.cas('thom', 'thompson', result[:cas])
|
186
|
+
assert_equal 'thompson', m.get('thom')[:value]
|
190
187
|
|
191
|
-
|
188
|
+
result = m.gets('thom')
|
192
189
|
m.delete('thom')
|
193
|
-
assert_nil m.cas('thom', 'hartson',
|
190
|
+
assert_nil m.cas('thom', 'hartson', result[:cas])
|
194
191
|
assert_equal nil, m.get('thom')
|
195
192
|
|
196
193
|
m.add('thom', 'hartmann')
|
197
|
-
|
194
|
+
result = m.gets('thom')
|
198
195
|
m.set('thom', 'foo')
|
199
|
-
assert_nil m.cas('thom', 'hartson',
|
200
|
-
assert_equal 'foo', m.get('thom')
|
196
|
+
assert_nil m.cas('thom', 'hartson', result[:cas])
|
197
|
+
assert_equal 'foo', m.get('thom')[:value]
|
201
198
|
end
|
202
199
|
end
|
203
200
|
end
|
data/test/memcache_test.rb
CHANGED
@@ -17,6 +17,7 @@ class MemcacheTest < Test::Unit::TestCase
|
|
17
17
|
|
18
18
|
keys = (0..200).collect {|key| key.to_s}
|
19
19
|
results = m.get(keys)
|
20
|
+
|
20
21
|
assert_equal 100, results.size
|
21
22
|
results.each do |key, value|
|
22
23
|
assert_equal key.to_i, value
|
@@ -242,10 +243,10 @@ class MemcacheTest < Test::Unit::TestCase
|
|
242
243
|
|
243
244
|
def test_flags
|
244
245
|
m.set('foo', :foo, :flags => 43)
|
245
|
-
assert_equal 43, m.get('foo')
|
246
|
+
assert_equal 43, m.get('foo', :meta => true)[:flags]
|
246
247
|
|
247
248
|
m.set('foo', 'foo', :raw => true, :flags => 43)
|
248
|
-
assert_equal 43, m.get('foo', :raw => true)
|
249
|
+
assert_equal 43, m.get('foo', :raw => true, :meta => true)[:flags]
|
249
250
|
end
|
250
251
|
|
251
252
|
def test_clone
|
data/test/test_helper.rb
CHANGED
@@ -4,7 +4,6 @@ require 'shoulda'
|
|
4
4
|
require 'mocha/setup'
|
5
5
|
require 'pp'
|
6
6
|
|
7
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
8
7
|
require 'memcache'
|
9
8
|
|
10
9
|
class Test::Unit::TestCase
|
@@ -41,7 +40,7 @@ class Test::Unit::TestCase
|
|
41
40
|
prefixes.each do |prefix|
|
42
41
|
assert_equal prefix, m.prefix = prefix
|
43
42
|
assert_equal prefix, m.prefix
|
44
|
-
super
|
43
|
+
super()
|
45
44
|
assert_equal nil, m.prefix = nil
|
46
45
|
assert_equal nil, m.prefix
|
47
46
|
end
|
metadata
CHANGED
@@ -1,105 +1,94 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: memcache
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 3
|
9
|
-
- 0
|
10
|
-
version: 1.3.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.4.0
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Justin Balthrop
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2013-08-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
22
14
|
name: rake
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 3
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
33
20
|
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: shoulda
|
37
21
|
prerelease: false
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: shoulda
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
48
33
|
version: 3.0.1
|
49
34
|
type: :development
|
50
|
-
version_requirements: *id002
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
name: mocha
|
53
35
|
prerelease: false
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.0.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mocha
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
63
48
|
type: :development
|
64
|
-
version_requirements: *id003
|
65
|
-
- !ruby/object:Gem::Dependency
|
66
|
-
name: activerecord
|
67
49
|
prerelease: false
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activerecord
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
77
62
|
type: :development
|
78
|
-
version_requirements: *id004
|
79
|
-
- !ruby/object:Gem::Dependency
|
80
|
-
name: activerecord-postgresql-adapter
|
81
63
|
prerelease: false
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activerecord-postgresql-adapter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
91
76
|
type: :development
|
92
|
-
|
93
|
-
|
94
|
-
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Ruby client for memcached supporting advanced protocol features and pluggable
|
84
|
+
architecture.
|
85
|
+
email:
|
95
86
|
- git@justinbalthrop.com
|
96
87
|
executables: []
|
97
|
-
|
98
|
-
|
99
|
-
|
88
|
+
extensions:
|
89
|
+
- ext/extconf.rb
|
100
90
|
extra_rdoc_files: []
|
101
|
-
|
102
|
-
files:
|
91
|
+
files:
|
103
92
|
- .gitignore
|
104
93
|
- .rbenv-gemsets
|
105
94
|
- .rbenv-version
|
@@ -134,41 +123,32 @@ files:
|
|
134
123
|
- test/memcache_server_test_helper.rb
|
135
124
|
- test/memcache_test.rb
|
136
125
|
- test/test_helper.rb
|
137
|
-
has_rdoc: true
|
138
126
|
homepage: https://github.com/ninjudd/memcache
|
139
|
-
licenses:
|
140
|
-
|
127
|
+
licenses:
|
128
|
+
- MIT
|
129
|
+
metadata: {}
|
141
130
|
post_install_message:
|
142
131
|
rdoc_options: []
|
143
|
-
|
144
|
-
require_paths:
|
132
|
+
require_paths:
|
145
133
|
- lib
|
146
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
none: false
|
157
|
-
requirements:
|
158
|
-
- - ">="
|
159
|
-
- !ruby/object:Gem::Version
|
160
|
-
hash: 3
|
161
|
-
segments:
|
162
|
-
- 0
|
163
|
-
version: "0"
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
164
144
|
requirements: []
|
165
|
-
|
166
145
|
rubyforge_project:
|
167
|
-
rubygems_version:
|
146
|
+
rubygems_version: 2.0.5
|
168
147
|
signing_key:
|
169
|
-
specification_version:
|
170
|
-
summary: Ruby client for memcached supporting advanced protocol features and pluggable
|
171
|
-
|
148
|
+
specification_version: 4
|
149
|
+
summary: Ruby client for memcached supporting advanced protocol features and pluggable
|
150
|
+
architecture.
|
151
|
+
test_files:
|
172
152
|
- test/memcache_local_server_test.rb
|
173
153
|
- test/memcache_native_server_test.rb
|
174
154
|
- test/memcache_null_server_test.rb
|