ashleym1972-cache-money 0.2.6 → 0.2.7

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.
File without changes
data/lib/cache_money.rb CHANGED
@@ -14,7 +14,7 @@ require 'cash/config'
14
14
  require 'cash/accessor'
15
15
 
16
16
  require 'cash/request'
17
- require 'cash/mock'
17
+ require 'cash/fake'
18
18
  require 'cash/local'
19
19
 
20
20
  require 'cash/query/abstract'
@@ -28,8 +28,8 @@ require 'cash/util/marshal'
28
28
  class ActiveRecord::Base
29
29
  def self.is_cached(options = {})
30
30
  options.assert_valid_keys(:ttl, :repository, :version)
31
- include Cash
32
- ::Cash::Config::Config.create(self, options)
31
+ include Cash unless ancestors.include?(Cash)
32
+ Cash::Config::Config.create(self, options)
33
33
  end
34
34
  end
35
35
 
data/lib/cash/accessor.rb CHANGED
@@ -11,8 +11,10 @@ module Cash
11
11
  def fetch(keys, options = {}, &block)
12
12
  case keys
13
13
  when Array
14
+ return {} if keys.empty?
15
+
14
16
  keys = keys.collect { |key| cache_key(key) }
15
- hits = repository.get_multi(keys)
17
+ hits = repository.get_multi(*keys)
16
18
  if (missed_keys = keys - hits.keys).any?
17
19
  missed_values = block.call(missed_keys)
18
20
  hits.merge!(missed_keys.zip(Array(missed_values)).to_hash_without_nils)
data/lib/cash/buffered.rb CHANGED
@@ -62,7 +62,7 @@ module Cash
62
62
  buffer_command Command.new(:delete, key, *options)
63
63
  end
64
64
 
65
- def get_multi(keys)
65
+ def get_multi(*keys)
66
66
  values = keys.collect { |key| get(key) }
67
67
  keys.zip(values).to_hash_without_nils
68
68
  end
data/lib/cash/config.rb CHANGED
@@ -1,5 +1,10 @@
1
1
  module Cash
2
2
  module Config
3
+ def self.create(active_record, options, indices = [])
4
+ active_record.cache_config = Config.new(active_record, options)
5
+ indices.each { |i| active_record.index i.attributes, i.options }
6
+ end
7
+
3
8
  def self.included(a_module)
4
9
  a_module.module_eval do
5
10
  extend ClassMethods
@@ -38,11 +43,6 @@ module Cash
38
43
  class Config
39
44
  attr_reader :active_record, :options
40
45
 
41
- def self.create(active_record, options, indices = [])
42
- active_record.cache_config = new(active_record, options)
43
- indices.each { |i| active_record.index i.attributes, i.options }
44
- end
45
-
46
46
  def initialize(active_record, options = {})
47
47
  @active_record, @options = active_record, options
48
48
  end
@@ -64,7 +64,7 @@ module Cash
64
64
  end
65
65
 
66
66
  def inherit(active_record)
67
- self.class.create(active_record, @options, indices)
67
+ Cash::Config.create(active_record, @options, indices)
68
68
  end
69
69
  end
70
70
  end
data/lib/cash/fake.rb ADDED
@@ -0,0 +1,83 @@
1
+ module Cash
2
+ class Fake < HashWithIndifferentAccess
3
+ attr_accessor :servers
4
+
5
+ def get_multi(*keys)
6
+ slice(*keys).collect { |k,v| [k, Marshal.load(v)] }.to_hash
7
+ end
8
+
9
+ def set(key, value, ttl = 0, raw = false)
10
+ self[key] = marshal(value, raw)
11
+ end
12
+
13
+ def get(key, raw = false)
14
+ if raw
15
+ self[key]
16
+ else
17
+ if self.has_key?(key)
18
+ Marshal.load(self[key])
19
+ else
20
+ nil
21
+ end
22
+ end
23
+ end
24
+
25
+ def incr(key, amount = 1)
26
+ if self.has_key?(key)
27
+ self[key] = (self[key].to_i + amount).to_s
28
+ self[key].to_i
29
+ end
30
+ end
31
+
32
+ def decr(key, amount = 1)
33
+ if self.has_key?(key)
34
+ self[key] = (self[key].to_i - amount).to_s
35
+ self[key].to_i
36
+ end
37
+ end
38
+
39
+ def add(key, value, ttl = 0, raw = false)
40
+ return false if self.has_key?(key)
41
+
42
+ self[key] = marshal(value, raw)
43
+ true
44
+ end
45
+
46
+ def append(key, value)
47
+ set(key, get(key, true).to_s + value.to_s, nil, true)
48
+ end
49
+
50
+ def namespace
51
+ nil
52
+ end
53
+
54
+ def flush_all
55
+ clear
56
+ end
57
+
58
+ def stats
59
+ {}
60
+ end
61
+
62
+ def reset_runtime
63
+ [0, Hash.new(0)]
64
+ end
65
+
66
+ private
67
+ def marshal(value, raw)
68
+ if raw
69
+ value.to_s
70
+ else
71
+ Marshal.dump(value)
72
+ end
73
+ end
74
+
75
+ def unmarshal(marshaled_obj)
76
+ Marshal.load(marshaled_obj)
77
+ end
78
+
79
+ def deep_clone(obj)
80
+ unmarshal(marshal(obj))
81
+ end
82
+ end
83
+ end
data/lib/cash/index.rb CHANGED
@@ -22,15 +22,13 @@ module Cash
22
22
 
23
23
  module Commands
24
24
  def add(object)
25
- clone = object.shallow_clone
26
25
  _, new_attribute_value_pairs = old_and_new_attribute_value_pairs(object)
27
- add_to_index_with_minimal_network_operations(new_attribute_value_pairs, clone)
26
+ add_to_index_with_minimal_network_operations(new_attribute_value_pairs, object)
28
27
  end
29
28
 
30
29
  def update(object)
31
- clone = object.shallow_clone
32
30
  old_attribute_value_pairs, new_attribute_value_pairs = old_and_new_attribute_value_pairs(object)
33
- update_index_with_minimal_network_operations(old_attribute_value_pairs, new_attribute_value_pairs, clone)
31
+ update_index_with_minimal_network_operations(old_attribute_value_pairs, new_attribute_value_pairs, object)
34
32
  end
35
33
 
36
34
  def remove(object)
@@ -70,7 +68,7 @@ module Cash
70
68
  include Attributes
71
69
 
72
70
  def serialize_object(object)
73
- primary_key? ? object : object.id
71
+ primary_key? ? object.shallow_clone : object.id
74
72
  end
75
73
 
76
74
  def matches?(query)
@@ -105,7 +103,7 @@ module Cash
105
103
  end
106
104
 
107
105
  def add_object_to_primary_key_cache(attribute_value_pairs, object)
108
- set(cache_key(attribute_value_pairs), [object], :ttl => ttl)
106
+ set(cache_key(attribute_value_pairs), [serialize_object(object)], :ttl => ttl)
109
107
  end
110
108
 
111
109
  def cache_key(attribute_value_pairs)
data/lib/cash/local.rb CHANGED
@@ -14,7 +14,20 @@ module Cash
14
14
  end
15
15
 
16
16
  def method_missing(method, *args, &block)
17
- @remote_cache.send(method, *args, &block)
17
+ autoload_missing_constants do
18
+ @remote_cache.send(method, *args, &block)
19
+ end
20
+ end
21
+
22
+ def autoload_missing_constants
23
+ yield if block_given?
24
+ rescue ArgumentError, MemCache::MemCacheError => error
25
+ lazy_load ||= Hash.new { |hash, hash_key| hash[hash_key] = true; false }
26
+ if error.to_s[/undefined class|referred/] && !lazy_load[error.to_s.split.last.constantize]
27
+ retry
28
+ else
29
+ raise error
30
+ end
18
31
  end
19
32
  end
20
33
 
data/lib/cash/lock.rb CHANGED
@@ -2,19 +2,19 @@ module Cash
2
2
  class Lock
3
3
  class Error < RuntimeError; end
4
4
 
5
+ INITIAL_WAIT = 1
5
6
  DEFAULT_RETRY = 5
6
7
  DEFAULT_EXPIRY = 30
7
8
 
8
9
  def initialize(cache)
9
10
  @cache = cache
10
- @runtime = Benchmark::Tms.new
11
11
  end
12
12
 
13
- def synchronize(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY)
13
+ def synchronize(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY, initial_wait = INITIAL_WAIT)
14
14
  if recursive_lock?(key)
15
15
  yield
16
16
  else
17
- acquire_lock(key, lock_expiry, retries)
17
+ acquire_lock(key, lock_expiry, retries, initial_wait)
18
18
  begin
19
19
  yield
20
20
  ensure
@@ -23,14 +23,12 @@ module Cash
23
23
  end
24
24
  end
25
25
 
26
- def acquire_lock(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY)
26
+ def acquire_lock(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY, initial_wait = INITIAL_WAIT)
27
27
  retries.times do |count|
28
- begin
29
- response = @cache.add("lock/#{key}", Process.pid, lock_expiry)
30
- return if response == "STORED\r\n"
31
- raise Error if count == retries - 1
32
- end
33
- exponential_sleep(count) unless count == retries - 1
28
+ response = @cache.add("lock/#{key}", Process.pid, lock_expiry)
29
+ return if response == "STORED\r\n"
30
+ raise Error if count == retries - 1
31
+ exponential_sleep(count, initial_wait) unless count == retries - 1
34
32
  end
35
33
  raise Error, "Couldn't acquire memcache lock for: #{key}"
36
34
  end
@@ -39,8 +37,8 @@ module Cash
39
37
  @cache.delete("lock/#{key}")
40
38
  end
41
39
 
42
- def exponential_sleep(count)
43
- @runtime += Benchmark::measure { sleep((2**count) / 2.0) }
40
+ def exponential_sleep(count, initial_wait)
41
+ sleep((2**count) / initial_wait)
44
42
  end
45
43
 
46
44
  private
@@ -33,7 +33,7 @@ module Cash
33
33
  def order
34
34
  @order ||= begin
35
35
  if order_sql = @options1[:order] || @options2[:order]
36
- matched, table_name, column_name, direction = *(ORDER.match(order_sql))
36
+ matched, table_name, column_name, direction = *(ORDER.match(order_sql.to_s))
37
37
  [column_name, direction =~ DESC ? :desc : :asc]
38
38
  else
39
39
  ['id', :asc]
data/rails/init.rb CHANGED
@@ -10,9 +10,9 @@ if memcache_config.nil? || memcache_config[:cache_money].nil?
10
10
  else
11
11
  require 'cache_money'
12
12
 
13
- ##$memcache = Rails.cache
14
13
  memcache_config[:logger] = Rails.logger
15
14
  $memcache = MemCache.new(memcache_config[:servers], memcache_config)
15
+ # $memcache = Memcached::Rails.new(memcache_config['servers'], memcache_config)
16
16
 
17
17
  ActionController::Base.cache_store = :cache_money_mem_cache_store
18
18
  ActionController::Base.session_options[:cache] = $memcache if memcache_options[:sessions]
@@ -19,6 +19,12 @@ module Cash
19
19
  end
20
20
 
21
21
  describe '#fetch([...])', :shared => true do
22
+ describe '#fetch([])' do
23
+ it 'returns the empty hash' do
24
+ Story.fetch([]).should == {}
25
+ end
26
+ end
27
+
22
28
  describe 'when there is a total cache miss' do
23
29
  it 'yields the keys to the block' do
24
30
  Story.fetch(["yabba", "dabba"]) { |*missing_ids| ["doo", "doo"] }.should == {
@@ -104,9 +110,18 @@ module Cash
104
110
 
105
111
  describe '#add' do
106
112
  describe 'when the value already exists' do
107
- it 'yields to the block' do
108
- Story.set("count", 1)
109
- Story.add("count", 1) { "yield me" }.should == "yield me"
113
+ describe 'when a block is given' do
114
+ it 'yields to the block' do
115
+ Story.set("count", 1)
116
+ Story.add("count", 1) { "yield me" }.should == "yield me"
117
+ end
118
+ end
119
+
120
+ describe 'when no block is given' do
121
+ it 'does not error' do
122
+ Story.set("count", 1)
123
+ lambda { Story.add("count", 1) }.should_not raise_error
124
+ end
110
125
  end
111
126
  end
112
127
 
@@ -109,27 +109,28 @@ module Cash
109
109
  end
110
110
  end
111
111
 
112
- describe '#find(:first, ..., :offset => ...)' do
113
- it "#finds the object in the correct order" do
114
- story1 = Story.create!(:title => 'title1')
115
- story2 = Story.create!(:title => story1.title)
116
- Story.find(:first, :conditions => { :title => story1.title }, :offset => 1).should == story2
112
+ describe '#find(:first, ...)' do
113
+ describe '#find(:first, ..., :offset => ...)' do
114
+ it "#finds the object in the correct order" do
115
+ story1 = Story.create!(:title => 'title1')
116
+ story2 = Story.create!(:title => story1.title)
117
+ Story.find(:first, :conditions => { :title => story1.title }, :offset => 1).should == story2
118
+ end
117
119
  end
118
- end
119
120
 
120
- describe '#find(:first, :conditions => [])' do
121
- it 'works' do
122
- story = Story.create!
123
- Story.find(:first, :conditions => []).should == story
121
+ describe '#find(:first, :conditions => [])' do
122
+ it 'finds the object in the correct order' do
123
+ story = Story.create!
124
+ Story.find(:first, :conditions => []).should == story
125
+ end
124
126
  end
125
- end
126
127
 
127
- describe "#find(:first, :conditions => '...')" do
128
- it "uses the active record instance to typecast values extracted from the conditions" do
129
- story1 = Story.create! :title => 'a story', :published => true
130
- story2 = Story.create! :title => 'another story', :published => false
131
- Story.get('published/false').should == [story2.id]
132
- Story.find(:first, :conditions => 'published = 0').should == story2
128
+ describe "#find(:first, :conditions => '...')" do
129
+ it "coerces ruby values to the appropriate database values" do
130
+ story1 = Story.create! :title => 'a story', :published => true
131
+ story2 = Story.create! :title => 'another story', :published => false
132
+ Story.find(:first, :conditions => 'published = 0').should == story2
133
+ end
133
134
  end
134
135
  end
135
136
  end
@@ -22,7 +22,7 @@ module Cash
22
22
  end
23
23
 
24
24
  describe '#find(:first, ...)' do
25
- describe '#find(:first, :conditions => { :id => ?})' do
25
+ describe '#find(:first, :conditions => { :id => ? })' do
26
26
  it "does not use the database" do
27
27
  story = Story.create!
28
28
  mock(Story.connection).execute.never
@@ -99,6 +99,15 @@ module Cash
99
99
  .should == story
100
100
  end
101
101
  end
102
+
103
+ describe 'when the attributes must be coerced to sql values' do
104
+ it 'does not use the database' do
105
+ story1 = Story.create!(:published => true)
106
+ story2 = Story.create!(:published => false)
107
+ mock(Story.connection).execute.never
108
+ Story.find(:first, :conditions => 'published = 0').should == story2
109
+ end
110
+ end
102
111
  end
103
112
 
104
113
  describe '#find(:first, :conditions => [...])' do
@@ -31,7 +31,7 @@ module Cash
31
31
  $memcache.get("lock/lock_key").should == nil
32
32
  end
33
33
 
34
- specify "does not block on recursive lock acquisition" do
34
+ it "does not block on recursive lock acquisition" do
35
35
  $lock.synchronize('lock_key') do
36
36
  lambda { $lock.synchronize('lock_key') {} }.should_not raise_error
37
37
  end
@@ -39,43 +39,64 @@ module Cash
39
39
  end
40
40
 
41
41
  describe '#acquire_lock' do
42
- specify "creates a lock at a given cache key" do
42
+ it "creates a lock at a given cache key" do
43
43
  $memcache.get("lock/lock_key").should == nil
44
44
  $lock.acquire_lock("lock_key")
45
45
  $memcache.get("lock/lock_key").should_not == nil
46
46
  end
47
47
 
48
- specify "retries specified number of times" do
49
- $lock.acquire_lock('lock_key')
50
- as_another_process do
51
- mock($memcache).add("lock/lock_key", Process.pid, timeout = 10) { "NOT_STORED\r\n" }.times(3)
52
- stub($lock).exponential_sleep
53
- lambda { $lock.acquire_lock('lock_key', timeout, 3) }.should raise_error
48
+ describe 'when given a timeout for the lock' do
49
+ it "correctly sets timeout on memcache entries" do
50
+ mock($memcache).add('lock/lock_key', Process.pid, timeout = 10) { true }
51
+ $lock.acquire_lock('lock_key', timeout)
54
52
  end
55
53
  end
56
54
 
57
- specify "correctly sets timeout on memcache entries" do
58
- mock($memcache).add('lock/lock_key', Process.pid, timeout = 10) { "STORED\r\n" }
59
- $lock.acquire_lock('lock_key', timeout)
60
- end
61
-
62
- specify "prevents two processes from acquiring the same lock at the same time" do
63
- $lock.acquire_lock('lock_key')
64
- as_another_process do
65
- lambda { $lock.acquire_lock('lock_key') }.should raise_error
55
+ describe 'when to processes contend for a lock' do
56
+ it "prevents two processes from acquiring the same lock at the same time" do
57
+ $lock.acquire_lock('lock_key')
58
+ as_another_process do
59
+ stub($lock).exponential_sleep
60
+ lambda { $lock.acquire_lock('lock_key') }.should raise_error
61
+ end
62
+ end
63
+
64
+ describe 'when given a number of times to retry' do
65
+ it "retries specified number of times" do
66
+ $lock.acquire_lock('lock_key')
67
+ as_another_process do
68
+ mock($memcache).add("lock/lock_key", Process.pid, timeout = 10) { false }.times(retries = 3)
69
+ stub($lock).exponential_sleep
70
+ lambda { $lock.acquire_lock('lock_key', timeout, retries) }.should raise_error
71
+ end
72
+ end
73
+ end
74
+
75
+ describe 'when given an initial wait' do
76
+ it 'sleeps exponentially starting with the initial wait' do
77
+ mock($lock).sleep(initial_wait = 0.123)
78
+ mock($lock).sleep(2 * initial_wait)
79
+ mock($lock).sleep(4 * initial_wait)
80
+ mock($lock).sleep(8 * initial_wait)
81
+ $lock.acquire_lock('lock_key')
82
+ as_another_process do
83
+ lambda { $lock.acquire_lock('lock_key', Lock::DEFAULT_EXPIRY, Lock::DEFAULT_RETRY, initial_wait) }.should raise_error
84
+ end
85
+ end
66
86
  end
67
- end
68
87
 
69
- def as_another_process
70
- current_pid = Process.pid
71
- stub(Process).pid { current_pid + 1 }
72
- yield
88
+ def as_another_process
89
+ current_pid = Process.pid
90
+ stub(Process).pid { current_pid + 1 }
91
+ yield
92
+ end
93
+
73
94
  end
74
95
 
75
96
  end
76
97
 
77
98
  describe '#release_lock' do
78
- specify "deletes the lock for a given cache key" do
99
+ it "deletes the lock for a given cache key" do
79
100
  $memcache.get("lock/lock_key").should == nil
80
101
  $lock.acquire_lock("lock_key")
81
102
  $memcache.get("lock/lock_key").should_not == nil
@@ -87,6 +87,12 @@ module Cash
87
87
  FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.id').should == @fairy_tales
88
88
  FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.`id`').should == @fairy_tales
89
89
  end
90
+
91
+ describe 'when the order is passed as a symbol' do
92
+ it 'works' do
93
+ FairyTale.find(:all, :conditions => { :title => @title }, :order => :id)
94
+ end
95
+ end
90
96
  end
91
97
 
92
98
  describe 'when the cache is not populated' do
@@ -73,14 +73,14 @@ module Cash
73
73
  it 'returns a hash' do
74
74
  @cache.set('key1', @value)
75
75
  @cache.set('key2', @value)
76
- @cache.get_multi(['key1', 'key2']).should == { 'key1' => @value, 'key2' => @value }
76
+ @cache.get_multi('key1', 'key2').should == { 'key1' => @value, 'key2' => @value }
77
77
  end
78
78
  end
79
79
 
80
80
  describe 'when there are misses' do
81
81
  it 'only returns results for hits' do
82
82
  @cache.set('key1', @value)
83
- @cache.get_multi(['key1', 'key2']).should == { 'key1' => @value }
83
+ @cache.get_multi('key1', 'key2').should == { 'key1' => @value }
84
84
  end
85
85
  end
86
86
  end
@@ -203,7 +203,7 @@ module Cash
203
203
  @cache.transaction do
204
204
  @cache.set('key1', @value)
205
205
  @cache.set('key2', [])
206
- @cache.get_multi(['key1', 'key2']).should == { 'key1' => @value, 'key2' => [] }
206
+ @cache.get_multi('key1', 'key2').should == { 'key1' => @value, 'key2' => [] }
207
207
  end
208
208
  end
209
209
  end
@@ -213,7 +213,7 @@ module Cash
213
213
  @cache.transaction do
214
214
  @cache.set('key1', @value)
215
215
  @cache.set('key2', @value)
216
- @cache.get_multi(['key1', 'key2']).should == { 'key1' => @value, 'key2' => @value }
216
+ @cache.get_multi('key1', 'key2').should == { 'key1' => @value, 'key2' => @value }
217
217
  end
218
218
  end
219
219
  end
@@ -222,7 +222,7 @@ module Cash
222
222
  it 'only returns results for hits' do
223
223
  @cache.transaction do
224
224
  @cache.set('key1', @value)
225
- @cache.get_multi(['key1', 'key2']).should == { 'key1' => @value }
225
+ @cache.get_multi('key1', 'key2').should == { 'key1' => @value }
226
226
  end
227
227
  end
228
228
  end
@@ -286,8 +286,8 @@ module Cash
286
286
  @cache.transaction do
287
287
  @cache.set('key1', @value)
288
288
  @cache.set('key2', @value)
289
- @cache.get_multi(['key1', 'key2']).should == { 'key1' => @value, 'key2' => @value }
290
- $memcache.get_multi(['key1', 'key2']).should == {}
289
+ @cache.get_multi('key1', 'key2').should == { 'key1' => @value, 'key2' => @value }
290
+ $memcache.get_multi('key1', 'key2').should == {}
291
291
  end
292
292
  end
293
293
 
data/spec/spec_helper.rb CHANGED
@@ -6,6 +6,7 @@ require 'spec'
6
6
  require 'pp'
7
7
  require 'cache_money'
8
8
  require 'memcache'
9
+ # require 'memcached'
9
10
  require File.join(dir, '../config/environment')
10
11
 
11
12
  Spec::Runner.configure do |config|
@@ -13,9 +14,9 @@ Spec::Runner.configure do |config|
13
14
  config.before :suite do
14
15
  load File.join(dir, "../db/schema.rb")
15
16
 
16
- config = YAML.load(IO.read((File.expand_path(File.dirname(__FILE__) + "/../config/memcache.yml"))))['test']
17
- $memcache = MemCache.new(config)
18
- $memcache.servers = config['servers']
17
+ config = YAML.load(IO.read((File.expand_path(File.dirname(__FILE__) + "/../config/memcached.yml"))))['test']
18
+ $memcache = MemCache.new(config['servers'], config)
19
+ # $memcache = Memcached::Rails.new(config['servers'], config)
19
20
  $lock = Cash::Lock.new($memcache)
20
21
  end
21
22
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ashleym1972-cache-money
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Kallen
@@ -61,7 +61,7 @@ files:
61
61
  - lib/cash/index.rb
62
62
  - lib/cash/local.rb
63
63
  - lib/cash/lock.rb
64
- - lib/cash/mock.rb
64
+ - lib/cash/fake.rb
65
65
  - lib/cash/query/abstract.rb
66
66
  - lib/cash/query/calculation.rb
67
67
  - lib/cash/query/primary_key.rb
@@ -103,7 +103,7 @@ specification_version: 2
103
103
  summary: Write-through and Read-through Cacheing for ActiveRecord
104
104
  test_files:
105
105
  - config/environment.rb
106
- - config/memcache.yml
106
+ - config/memcached.yml
107
107
  - db/schema.rb
108
108
  - spec/cash/accessor_spec.rb
109
109
  - spec/cash/active_record_spec.rb
data/lib/cash/mock.rb DELETED
@@ -1,154 +0,0 @@
1
- module Cash
2
- class Mock < HashWithIndifferentAccess
3
- attr_accessor :servers
4
-
5
- class CacheEntry
6
- attr_reader :value
7
-
8
- def self.default_ttl
9
- 1_000_000
10
- end
11
-
12
- def self.now
13
- Time.now
14
- end
15
-
16
- def initialize(value, raw, ttl)
17
- if raw
18
- @value = value.to_s
19
- else
20
- @value = Marshal.dump(value)
21
- end
22
-
23
- if ttl.zero?
24
- @ttl = self.class.default_ttl
25
- else
26
- @ttl = ttl
27
- end
28
-
29
- @expires_at = self.class.now + @ttl
30
- end
31
-
32
-
33
- def expired?
34
- self.class.now > @expires_at
35
- end
36
-
37
- def increment(amount = 1)
38
- @value = (@value.to_i + amount).to_s
39
- end
40
-
41
- def decrement(amount = 1)
42
- @value = (@value.to_i - amount).to_s
43
- end
44
-
45
- def unmarshal
46
- Marshal.load(@value)
47
- end
48
-
49
- def to_i
50
- @value.to_i
51
- end
52
- end
53
-
54
- attr_accessor :logging
55
-
56
- def initialize
57
- @logging = false
58
- end
59
-
60
- def get_multi(keys)
61
- slice(*keys).collect { |k,v| [k, v.unmarshal] }.to_hash_without_nils
62
- end
63
-
64
- def set(key, value, ttl = CacheEntry.default_ttl, raw = false)
65
- log "< set #{key} #{ttl}"
66
- self[key] = CacheEntry.new(value, raw, ttl)
67
- log('> STORED')
68
- end
69
-
70
- def get(key, raw = false)
71
- log "< get #{key}"
72
- unless self.has_unexpired_key?(key)
73
- log('> END')
74
- return nil
75
- end
76
-
77
- log("> sending key #{key}")
78
- log('> END')
79
- if raw
80
- self[key].value
81
- else
82
- self[key].unmarshal
83
- end
84
- end
85
-
86
- def delete(key, options = {})
87
- log "< delete #{key}"
88
- if self.has_unexpired_key?(key)
89
- log "> DELETED"
90
- super(key)
91
- else
92
- log "> NOT FOUND"
93
- end
94
- end
95
-
96
- def incr(key, amount = 1)
97
- if self.has_unexpired_key?(key)
98
- self[key].increment(amount)
99
- self[key].to_i
100
- end
101
- end
102
-
103
- def decr(key, amount = 1)
104
- if self.has_unexpired_key?(key)
105
- self[key].decrement(amount)
106
- self[key].to_i
107
- end
108
- end
109
-
110
- def add(key, value, ttl = CacheEntry.default_ttl, raw = false)
111
- if self.has_unexpired_key?(key)
112
- "NOT_STORED\r\n"
113
- else
114
- set(key, value, ttl, raw)
115
- "STORED\r\n"
116
- end
117
- end
118
-
119
- def append(key, value)
120
- set(key, get(key, true).to_s + value.to_s, nil, true)
121
- end
122
-
123
- def namespace
124
- nil
125
- end
126
-
127
- def flush_all
128
- log('< flush_all')
129
- clear
130
- end
131
-
132
- def stats
133
- {}
134
- end
135
-
136
- def reset_runtime
137
- [0, Hash.new(0)]
138
- end
139
-
140
- def has_unexpired_key?(key)
141
- self.has_key?(key) && !self[key].expired?
142
- end
143
-
144
- def log(message)
145
- return unless logging
146
- logger.debug(message)
147
- end
148
-
149
- def logger
150
- @logger ||= ActiveSupport::BufferedLogger.new(Rails.root.join('log/cash_mock.log'))
151
- end
152
-
153
- end
154
- end