memcache-client-activerecord 0.1.0 → 0.2.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.
data/Rakefile CHANGED
@@ -10,12 +10,16 @@ begin
10
10
  gem.email = "m.ishihara@gmail.com"
11
11
  gem.homepage = "http://github.com/m4i/memcache-client-activerecord"
12
12
  gem.authors = ["ISHIHARA Masaki"]
13
+ gem.rubyforge_project = "mc-activerecord"
13
14
  gem.add_runtime_dependency "activerecord", ">= 2.1"
14
15
  gem.add_development_dependency "rspec", ">= 1.2.9"
15
16
  gem.add_development_dependency "memcache-client", ">= 1.7.7"
16
17
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
18
  end
18
19
  Jeweler::GemcutterTasks.new
20
+ Jeweler::RubyforgeTasks.new do |rubyforge|
21
+ rubyforge.doc_task = "rdoc"
22
+ end
19
23
  rescue LoadError
20
24
  puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
25
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -4,19 +4,19 @@ class <%= migration_name %> < ActiveRecord::Migration
4
4
  when 'MySQL'
5
5
  execute(<<-SQL)
6
6
  CREATE TABLE `<%= table_name %>` (
7
- `key` VARBINARY(250) NOT NULL PRIMARY KEY,
8
- `value` MEDIUMBLOB NOT NULL,
9
- `cas` INT UNSIGNED NOT NULL,
10
- `expire_at` DATETIME
7
+ `key` VARBINARY(250) NOT NULL PRIMARY KEY,
8
+ `value` MEDIUMBLOB NOT NULL,
9
+ `cas` INT UNSIGNED NOT NULL,
10
+ `expiry` INT
11
11
  ) ENGINE=InnoDB
12
12
  SQL
13
13
 
14
14
  else
15
15
  create_table :<%= table_name %>, :id => false do |t|
16
- t.string :key, :null => false, :limit => 250
17
- t.binary :value, :null => false
18
- t.integer :cas, :null => false
19
- t.datetime :expire_at
16
+ t.string :key, :null => false, :limit => 250
17
+ t.binary :value, :null => false
18
+ t.integer :cas, :null => false
19
+ t.integer :expiry
20
20
  end
21
21
  add_index :<%= table_name %>, :key, :unique => true
22
22
  end
@@ -17,12 +17,13 @@ class MemCache
17
17
 
18
18
  MAX_KEY_SIZE = 250
19
19
  MAX_VALUE_SIZE = 2 ** 20
20
+ THIRTY_DAYS = 60 * 60 * 24 * 30
20
21
 
21
22
  COLUMN_NAMES = {
22
- :key => 'key',
23
- :value => 'value',
24
- :cas => 'cas',
25
- :expire_at => 'expire_at',
23
+ :key => 'key',
24
+ :value => 'value',
25
+ :cas => 'cas',
26
+ :expiry => 'expiry',
26
27
  }
27
28
 
28
29
  STORED = "STORED\r\n"
@@ -82,7 +83,7 @@ class MemCache
82
83
 
83
84
  def get(key, raw = false)
84
85
  cache_key = make_cache_key(key)
85
- if value = find(cache_key, :value, true, __method__)
86
+ if value = find(__method__, cache_key, :value, true)
86
87
  raw ? value : Marshal.load(value)
87
88
  end
88
89
  end
@@ -103,7 +104,7 @@ class MemCache
103
104
  cache_keys[make_cache_key(key)] = key
104
105
  cache_keys
105
106
  end
106
- rows = find_all(cache_keys.keys, [:key, :value], true, __method__)
107
+ rows = find_all(__method__, cache_keys.keys, [:key, :value], true)
107
108
  rows.inject({}) do |hash, (key, value)|
108
109
  hash[cache_keys[key]] = Marshal.load(value)
109
110
  hash
@@ -116,9 +117,9 @@ class MemCache
116
117
  cache_key = make_cache_key(key)
117
118
  value = value_to_storable(value, raw)
118
119
 
119
- unless update(cache_key, value, expiry, __method__)
120
+ unless update(__method__, cache_key, value, expiry)
120
121
  # rescue duplicate key error
121
- insert(cache_key, value, expiry, __method__) rescue nil
122
+ insert(__method__, cache_key, value, expiry) rescue nil
122
123
  end
123
124
 
124
125
  STORED unless @no_reply
@@ -128,7 +129,7 @@ class MemCache
128
129
  check_readonly!
129
130
  raise MemCacheError, 'A block is required' unless block_given?
130
131
 
131
- result = cas_with_reply(key, expiry, raw, __method__, &block)
132
+ result = cas_with_reply(__method__, key, expiry, raw, &block)
132
133
  result unless @no_reply
133
134
  end
134
135
 
@@ -138,18 +139,18 @@ class MemCache
138
139
  cache_key = make_cache_key(key)
139
140
  value = value_to_storable(value, raw)
140
141
 
141
- old_value, expire_at =
142
- find(cache_key, [:value, :expire_at], false, __method__)
142
+ old_value, old_expiry =
143
+ find(__method__, cache_key, [:value, :expiry], false)
143
144
 
144
- if old_value && available?(expire_at)
145
+ if old_value && available?(old_expiry)
145
146
  NOT_STORED unless @no_reply
146
147
 
147
148
  else
148
149
  if old_value
149
- update(cache_key, value, expiry, __method__)
150
+ update(__method__, cache_key, value, expiry)
150
151
  else
151
152
  # rescue duplicate key error
152
- insert(cache_key, value, expiry, __method__) rescue nil
153
+ insert(__method__, cache_key, value, expiry) rescue nil
153
154
  end
154
155
 
155
156
  STORED unless @no_reply
@@ -162,7 +163,7 @@ class MemCache
162
163
  cache_key = make_cache_key(key)
163
164
  value = value_to_storable(value, raw)
164
165
 
165
- if update(cache_key, value, expiry, __method__, true)
166
+ if update(__method__, cache_key, value, expiry, true)
166
167
  STORED unless @no_reply
167
168
  else
168
169
  NOT_STORED unless @no_reply
@@ -192,11 +193,11 @@ class MemCache
192
193
  conditions = { COLUMN_NAMES[:key] => cache_key }
193
194
 
194
195
  if @no_reply
195
- _delete(conditions, __method__)
196
+ _delete(__method__, conditions)
196
197
  nil
197
198
  else
198
- exists = !!find(cache_key, :key, true, __method__)
199
- _delete(conditions, __method__)
199
+ exists = !!find(__method__, cache_key, :key, true)
200
+ _delete(__method__, conditions)
200
201
  exists ? DELETED : NOT_FOUND
201
202
  end
202
203
  end
@@ -213,7 +214,7 @@ class MemCache
213
214
  end
214
215
 
215
216
  def garbage_collection!
216
- _delete(["#{quote_column_name(:expire_at)} <= ?", now], __method__)
217
+ _delete(__method__, ["#{quote_column_name(:expiry)} <= ?", now])
217
218
  end
218
219
 
219
220
  private
@@ -247,6 +248,14 @@ class MemCache
247
248
  value
248
249
  end
249
250
 
251
+ def expiry_to_storable(expiry)
252
+ expiry.zero? ?
253
+ nil :
254
+ expiry <= THIRTY_DAYS ?
255
+ now + expiry :
256
+ expiry
257
+ end
258
+
250
259
  def check_value_size!(value)
251
260
  if @check_size && value.size > MAX_VALUE_SIZE
252
261
  raise MemCacheError,
@@ -254,27 +263,27 @@ class MemCache
254
263
  end
255
264
  end
256
265
 
257
- def gets(key, raw, method)
266
+ def gets(_method_, key, raw)
258
267
  cache_key = make_cache_key(key)
259
- value, cas = find(cache_key, [:value, :cas], true, method)
268
+ value, cas = find(_method_, cache_key, [:value, :cas], true)
260
269
  if cas
261
270
  [raw ? value : Marshal.load(value), cas]
262
271
  end
263
272
  end
264
273
 
265
- def cas_with_reply(key, expiry, raw, method, &block)
266
- value, cas = gets(key, raw, method)
274
+ def cas_with_reply(_method_, key, expiry, raw, &block)
275
+ value, cas = gets(_method_, key, raw)
267
276
  if cas
268
277
  cache_key = make_cache_key(key)
269
278
  value = value_to_storable(yield(value), raw)
270
279
 
271
- update(cache_key, value, expiry, method, true, cas) ?
280
+ update(_method_, cache_key, value, expiry, true, cas) ?
272
281
  STORED : EXISTS
273
282
  end
274
283
  end
275
284
 
276
285
  # TODO: check value size
277
- def append_or_prepend(method, key, value)
286
+ def append_or_prepend(_method_, key, value)
278
287
  check_readonly!
279
288
 
280
289
  cache_key = make_cache_key(key)
@@ -283,33 +292,33 @@ class MemCache
283
292
  old = quote_column_name(:value)
284
293
  new = quote_value(:value, value)
285
294
  pairs = {
286
- :value => concat_sql(*(method == :append ? [old, new] : [new, old]))
295
+ :value => concat_sql(*(_method_ == :append ? [old, new] : [new, old]))
287
296
  }
288
297
 
289
298
  affected_rows = @ar.connection.update(
290
299
  update_sql(cache_key, pairs, true, nil),
291
- sql_name(method)
300
+ sql_name(_method_)
292
301
  )
293
302
 
294
303
  affected_rows > 0 ? STORED : NOT_STORED unless @no_reply
295
304
  end
296
305
 
297
- def incr_or_decl(method, key, amount)
306
+ def incr_or_decl(_method_, key, amount)
298
307
  check_readonly!
299
308
 
300
309
  unless /\A\s*\d+\s*\z/ =~ amount.to_s
301
310
  raise MemCacheError, 'invalid numeric delta argument'
302
311
  end
303
- amount = method == :incr ? amount.to_i : - amount.to_i
312
+ amount = _method_ == :incr ? amount.to_i : - amount.to_i
304
313
 
305
314
  value = nil
306
315
 
307
316
  count = 0
308
317
  begin
309
318
  count += 1
310
- raise MemCacheError, "cannot #{method}" if count > 10
319
+ raise MemCacheError, "cannot #{_method_}" if count > 10
311
320
 
312
- result = cas_with_reply(key, nil, true, method) do |old_value|
321
+ result = cas_with_reply(_method_, key, nil, true) do |old_value|
313
322
  unless /\A\s*\d+\s*\z/ =~old_value
314
323
  raise MemCacheError,
315
324
  'cannot increment or decrement non-numeric value'
@@ -324,7 +333,7 @@ class MemCache
324
333
  raise unless @no_reply
325
334
  end
326
335
 
327
- def find(cache_key, column_keys, only_available, method)
336
+ def find(_method_, cache_key, column_keys, only_available)
328
337
  result = @ar.connection.send(
329
338
  column_keys.is_a?(Array) ? :select_one : :select_value,
330
339
  select_sql(
@@ -332,7 +341,7 @@ class MemCache
332
341
  quote_column_name(*Array(column_keys)),
333
342
  only_available
334
343
  ),
335
- sql_name(method)
344
+ sql_name(_method_)
336
345
  )
337
346
 
338
347
  (result && column_keys.is_a?(Array)) ?
@@ -340,7 +349,7 @@ class MemCache
340
349
  result
341
350
  end
342
351
 
343
- def find_all(cache_keys, column_keys, only_available, method)
352
+ def find_all(_method_, cache_keys, column_keys, only_available)
344
353
  return [] if cache_keys.empty?
345
354
 
346
355
  result = @ar.connection.send(
@@ -350,7 +359,7 @@ class MemCache
350
359
  quote_column_name(*Array(column_keys)),
351
360
  only_available
352
361
  ),
353
- sql_name(method)
362
+ sql_name(_method_)
354
363
  )
355
364
 
356
365
  column_keys.is_a?(Array) ?
@@ -358,7 +367,7 @@ class MemCache
358
367
  result
359
368
  end
360
369
 
361
- def insert(cache_key, value, expiry, method)
370
+ def insert(_method_, cache_key, value, expiry)
362
371
  attributes = attributes_for_update(value, expiry).merge(
363
372
  :key => cache_key,
364
373
  :cas => 0
@@ -374,11 +383,11 @@ class MemCache
374
383
  "INSERT INTO #{@ar.quoted_table_name}" +
375
384
  " (#{quote_column_name(*column_keys)})" +
376
385
  " VALUES(#{quoted_values.join(', ')})",
377
- sql_name(method)
386
+ sql_name(_method_)
378
387
  )
379
388
  end
380
389
 
381
- def update(cache_key, value, expiry, method, only_available = false, cas = nil)
390
+ def update(_method_, cache_key, value, expiry, only_available = false, cas = nil)
382
391
  attributes = attributes_for_update(value, expiry)
383
392
 
384
393
  pairs = attributes.keys.inject({}) do |pairs, column_key|
@@ -388,32 +397,32 @@ class MemCache
388
397
 
389
398
  @ar.connection.update(
390
399
  update_sql(cache_key, pairs, only_available, cas),
391
- sql_name(method)
400
+ sql_name(_method_)
392
401
  ) > 0
393
402
  end
394
403
 
395
- def _delete(conditions, method)
404
+ def _delete(_method_, conditions)
396
405
  @ar.connection.execute(
397
406
  "DELETE FROM #{@ar.quoted_table_name}" +
398
407
  " WHERE #{@ar.send(:sanitize_sql, conditions)}",
399
- sql_name(method)
408
+ sql_name(_method_)
400
409
  )
401
410
  end
402
411
 
403
- def truncate(method)
412
+ def truncate(_method_)
404
413
  sql = case @ar.connection.adapter_name
405
414
  when 'SQLite'
406
415
  "DELETE FROM #{@ar.quoted_table_name}"
407
416
  else
408
417
  "TRUNCATE TABLE #{@ar.quoted_table_name}"
409
418
  end
410
- @ar.connection.execute(sql, sql_name(method))
419
+ @ar.connection.execute(sql, sql_name(_method_))
411
420
  end
412
421
 
413
422
  def attributes_for_update(value, expiry)
414
423
  attributes = { :value => value }
415
424
  unless expiry.nil?
416
- attributes.update(:expire_at => expiry.zero? ? nil : now(expiry))
425
+ attributes.update(:expiry => expiry_to_storable(expiry))
417
426
  end
418
427
  attributes
419
428
  end
@@ -449,8 +458,8 @@ class MemCache
449
458
 
450
459
  if only_available
451
460
  conditions.first << ' AND (' +
452
- "#{quote_column_name(:expire_at)} IS NULL" +
453
- " OR #{quote_column_name(:expire_at)} > ?" +
461
+ "#{quote_column_name(:expiry)} IS NULL" +
462
+ " OR #{quote_column_name(:expiry)} > ?" +
454
463
  ')'
455
464
  conditions << now
456
465
  end
@@ -467,12 +476,12 @@ class MemCache
467
476
  end
468
477
  end
469
478
 
470
- def sql_name(method)
471
- "#{self.class.name}##{method}"
479
+ def sql_name(_method_)
480
+ "#{self.class.name}##{_method_}"
472
481
  end
473
482
 
474
- def now(delay = 0)
475
- Time.now + delay
483
+ def now
484
+ Time.now.to_i
476
485
  end
477
486
 
478
487
  def quote_column_name(*column_keys)
@@ -486,12 +495,8 @@ class MemCache
486
495
  @ar.connection.quote(value, @ar.columns_hash[COLUMN_NAMES[column_key]])
487
496
  end
488
497
 
489
- def available?(expire_at)
490
- expire_at.nil? || now < to_time(expire_at)
491
- end
492
-
493
- def to_time(expire_at)
494
- @ar.columns_hash[COLUMN_NAMES[:expire_at]].type_cast(expire_at)
498
+ def available?(expiry)
499
+ expiry.nil? || now < expiry.to_i
495
500
  end
496
501
  end
497
502
 
@@ -138,6 +138,27 @@ describe "#{adapter}:" do
138
138
  caches.same(:get, 'FOO').should == 2
139
139
  end
140
140
 
141
+ it 'should support a number of seconds starting from current time' do
142
+ [
143
+ 1,
144
+ MemCache::ActiveRecord::THIRTY_DAYS,
145
+ ].each do |expiry|
146
+ dbcache.set('foo', 1, expiry)
147
+ cache_class.find_by_key('foo').expiry.should ==
148
+ Time.now.to_i + expiry
149
+ end
150
+ end
151
+
152
+ it 'should support an unix time expiry' do
153
+ [
154
+ MemCache::ActiveRecord::THIRTY_DAYS + 1,
155
+ Time.now.to_i,
156
+ ].each do |expiry|
157
+ dbcache.set('foo', 1, expiry)
158
+ cache_class.find_by_key('foo').expiry.should == expiry
159
+ end
160
+ end
161
+
141
162
  unless ENV['LOG']
142
163
  it 'should behave like MemCache with over 64KB value' do
143
164
  value = 'a' * (2 ** 16 + 1)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memcache-client-activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ISHIHARA Masaki
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-30 00:00:00 +09:00
12
+ date: 2009-12-31 00:00:00 +09:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  version:
91
91
  requirements: []
92
92
 
93
- rubyforge_project:
93
+ rubyforge_project: mc-activerecord
94
94
  rubygems_version: 1.3.5
95
95
  signing_key:
96
96
  specification_version: 3