mguymon-cache-money 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module Cash
2
+ Request = {}
3
+ end
@@ -0,0 +1,43 @@
1
+ module Cash
2
+ class Transactional
3
+ attr_reader :memcache
4
+
5
+ def initialize(memcache, lock)
6
+ @memcache, @cache = [memcache, memcache]
7
+ @lock = lock
8
+ end
9
+
10
+ def transaction
11
+ exception_was_raised = false
12
+ begin_transaction
13
+ result = yield
14
+ rescue Object => e
15
+ exception_was_raised = true
16
+ raise
17
+ ensure
18
+ begin
19
+ @cache.flush unless exception_was_raised
20
+ ensure
21
+ end_transaction
22
+ end
23
+ end
24
+
25
+ def respond_to?(method)
26
+ @cache.respond_to?(method)
27
+ end
28
+
29
+ private
30
+
31
+ def method_missing(method, *args, &block)
32
+ @cache.send(method, *args, &block)
33
+ end
34
+
35
+ def begin_transaction
36
+ @cache = Buffered.push(@cache, @lock)
37
+ end
38
+
39
+ def end_transaction
40
+ @cache = @cache.pop
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ class Array
2
+ alias_method :count, :size
3
+
4
+ def to_hash_without_nils
5
+ keys_and_values_without_nils = reject { |key, value| value.nil? }
6
+ shallow_flattened_keys_and_values_without_nils = keys_and_values_without_nils.inject([]) { |result, pair| result += pair }
7
+ Hash[*shallow_flattened_keys_and_values_without_nils]
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Marshal
2
+ class << self
3
+ def constantize(name)
4
+ name.constantize
5
+ end
6
+
7
+ def load_with_constantize(value)
8
+ begin
9
+ Marshal.load_without_constantize value
10
+ rescue ArgumentError => e
11
+ _, class_name = *(/undefined class\/module ([\w:]*\w)/.match(e.message))
12
+ raise if !class_name
13
+ constantize(class_name)
14
+ Marshal.load value
15
+ end
16
+ end
17
+ alias_method_chain :load, :constantize
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ module Cash
2
+ module WriteThrough
3
+ DEFAULT_TTL = 12.hours
4
+
5
+ def self.included(active_record_class)
6
+ active_record_class.class_eval do
7
+ include InstanceMethods
8
+ extend ClassMethods
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def self.included(active_record_class)
14
+ active_record_class.class_eval do
15
+ after_create :add_to_caches
16
+ after_update :update_caches
17
+ after_destroy :remove_from_caches
18
+ end
19
+ end
20
+
21
+ def add_to_caches
22
+ InstanceMethods.unfold(self.class, :add_to_caches, self)
23
+ end
24
+
25
+ def update_caches
26
+ InstanceMethods.unfold(self.class, :update_caches, self)
27
+ end
28
+
29
+ def remove_from_caches
30
+ return if new_record?
31
+ InstanceMethods.unfold(self.class, :remove_from_caches, self)
32
+ end
33
+
34
+ def expire_caches
35
+ InstanceMethods.unfold(self.class, :expire_caches, self)
36
+ end
37
+
38
+ def shallow_clone
39
+ self.class.send(:instantiate, instance_variable_get(:@attributes))
40
+ end
41
+
42
+ private
43
+ def self.unfold(klass, operation, object)
44
+ while klass < ActiveRecord::Base && klass.ancestors.include?(WriteThrough)
45
+ klass.send(operation, object)
46
+ klass = klass.superclass
47
+ end
48
+ end
49
+ end
50
+
51
+ module ClassMethods
52
+ def add_to_caches(object)
53
+ indices.each { |index| index.add(object) } if cache_config
54
+ end
55
+
56
+ def update_caches(object)
57
+ indices.each { |index| index.update(object) } if cache_config
58
+ end
59
+
60
+ def remove_from_caches(object)
61
+ indices.each { |index| index.remove(object) } if cache_config
62
+ end
63
+
64
+ def expire_caches(object)
65
+ indices.each { |index| index.delete(object) } if cache_config
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,263 @@
1
+ # require 'memcache'
2
+ # require 'memcached'
3
+
4
+ #typically MemCache/Memcached can be used via the following in rails/init.rb:
5
+ #$memcache = MemCache.new(memcache_config[:servers].gsub(' ', '').split(','), memcache_config)
6
+ #$memcache = Memcached::Rails.new(memcache_config[:servers].gsub(' ', '').split(','), memcache_config)
7
+
8
+ #this wrapper lets both work.
9
+
10
+ ####### they have MemCache installed (don't need the wrapper)
11
+ if defined? MemCache
12
+
13
+ Rails.logger.info("cache-money: MemCache installed") if defined? Rails
14
+ #TODO add logging?
15
+ class MemcachedWrapper < ::MemCache
16
+ end
17
+
18
+ ########## they have Memcached installed (do need the wrapper)
19
+ elsif defined? Memcached
20
+ Rails.logger.info("cache-money: Memcached installed") if defined? Rails
21
+
22
+ class Memcached
23
+ alias :get_multi :get #:nodoc:
24
+ end
25
+
26
+ class MemcachedWrapper < ::Memcached
27
+ DEFAULTS = { :servers => '127.0.0.1:11211' }
28
+
29
+ attr_reader :logger, :default_ttl
30
+
31
+ # See Memcached#new for details.
32
+ def initialize(*args)
33
+ opts = DEFAULTS.merge(args.last.is_a?(Hash) ? args.pop : {})
34
+
35
+ if opts.respond_to?(:symbolize_keys!)
36
+ opts.symbolize_keys!
37
+ else
38
+ opts = symbolize_keys(opts)
39
+ end
40
+
41
+ servers = Array(
42
+ args.any? ? args.unshift : opts.delete(:servers)
43
+ ).flatten.compact
44
+
45
+ opts[:prefix_key] ||= "#{opts[:namespace]}:"
46
+
47
+ @logger = opts[:logger]
48
+ @debug = opts[:debug]
49
+
50
+ super(servers, opts)
51
+ end
52
+
53
+ def symbolize_keys(opts)
54
+ # Destructively convert all keys to symbols.
55
+ if opts.kind_of?(Hash) && !opts.kind_of?(HashWithIndifferentAccess)
56
+ opts.keys.each do |key|
57
+ unless key.is_a?(Symbol)
58
+ opts[key.to_sym] = opts[key]
59
+ opts.delete(key)
60
+ end
61
+ end
62
+ end
63
+ opts
64
+ end
65
+
66
+ def namespace
67
+ options[:prefix_key]
68
+ end
69
+
70
+ # Wraps Memcached::Rails#add to return a text string - for cache money
71
+ def add(key, value, ttl=@default_ttl, raw=false)
72
+ logger.debug("Memcached add: #{key.inspect}") if logger && @debug
73
+ super(key, value, ttl, !raw)
74
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
75
+ stored
76
+ rescue Memcached::NotStored
77
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
78
+ not_stored
79
+ rescue Memcached::Error
80
+ log_error($!) if logger
81
+ not_stored
82
+ end
83
+
84
+ def replace(key, value, ttl = @default_ttl, raw = false)
85
+ logger.debug("Memcached replace: #{key.inspect}") if logger && @debug
86
+ super(key, value, ttl, !raw)
87
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
88
+ stored
89
+ rescue Memcached::NotStored
90
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
91
+ not_stored
92
+ rescue Memcached::Error
93
+ log_error($!) if logger
94
+ not_stored
95
+ end
96
+
97
+ # Wraps Memcached#get so that it doesn't raise. This has the side-effect of preventing you from
98
+ # storing <tt>nil</tt> values.
99
+ def get(key, raw=false)
100
+ logger.debug("Memcached get: #{key.inspect}") if logger && @debug
101
+ value = super(key, !raw)
102
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
103
+ value
104
+ rescue Memcached::NotFound
105
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
106
+ nil
107
+ rescue TypeError
108
+ log_error($!) if logger
109
+ delete(key)
110
+ logger.debug("Memcached deleted: #{key.inspect}") if logger && @debug
111
+ nil
112
+ rescue Memcached::Error
113
+ log_error($!) if logger
114
+ nil
115
+ end
116
+
117
+ def fetch(key, expiry = 0, raw = false)
118
+ value = get(key, !raw)
119
+
120
+ if value.nil? && block_given?
121
+ value = yield
122
+ add(key, value, expiry, !raw)
123
+ end
124
+
125
+ value
126
+ end
127
+
128
+ # Wraps Memcached#cas so that it doesn't raise. Doesn't set anything if no value is present.
129
+ def cas(key, ttl=@default_ttl, raw=false, &block)
130
+ logger.debug("Memcached cas: #{key.inspect}") if logger && @debug
131
+ super(key, ttl, !raw, &block)
132
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
133
+ stored
134
+ rescue Memcached::NotFound
135
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
136
+ rescue TypeError
137
+ log_error($!) if logger
138
+ delete(key)
139
+ logger.debug("Memcached deleted: #{key.inspect}") if logger && @debug
140
+ rescue Memcached::Error
141
+ if $!.is_a?(Memcached::ClientError)
142
+ raise $!
143
+ end
144
+ log_error($!) if logger
145
+ end
146
+
147
+ def get_multi(*keys)
148
+ keys.flatten!
149
+ logger.debug("Memcached get_multi: #{keys.inspect}") if logger && @debug
150
+ values = super(keys, true)
151
+ logger.debug("Memcached hit: #{keys.inspect}") if logger && @debug
152
+ values
153
+ rescue Memcached::NotFound
154
+ logger.debug("Memcached miss: #{keys.inspect}") if logger && @debug
155
+ {}
156
+ rescue TypeError
157
+ log_error($!) if logger
158
+ keys.each { |key| delete(key) }
159
+ logger.debug("Memcached deleted: #{keys.inspect}") if logger && @debug
160
+ {}
161
+ rescue Memcached::Error
162
+ log_error($!) if logger
163
+ {}
164
+ end
165
+
166
+ def set(key, value, ttl=@default_ttl, raw=false)
167
+ logger.debug("Memcached set: #{key.inspect}") if logger && @debug
168
+ super(key, value, ttl, !raw)
169
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
170
+ stored
171
+ rescue Memcached::Error
172
+ log_error($!) if logger
173
+ not_stored
174
+ end
175
+
176
+ def append(key, value)
177
+ logger.debug("Memcached append: #{key.inspect}") if logger && @debug
178
+ super(key, value)
179
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
180
+ stored
181
+ rescue Memcached::NotStored
182
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
183
+ not_stored
184
+ rescue Memcached::Error
185
+ log_error($!) if logger
186
+ end
187
+
188
+ def prepend(key, value)
189
+ logger.debug("Memcached prepend: #{key.inspect}") if logger && @debug
190
+ super(key, value)
191
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
192
+ stored
193
+ rescue Memcached::NotStored
194
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
195
+ not_stored
196
+ rescue Memcached::Error
197
+ log_error($!) if logger
198
+ end
199
+
200
+ def delete(key)
201
+ logger.debug("Memcached delete: #{key.inspect}") if logger && @debug
202
+ super(key)
203
+ logger.debug("Memcached hit: #{key.inspect}") if logger && @debug
204
+ deleted
205
+ rescue Memcached::NotFound
206
+ logger.debug("Memcached miss: #{key.inspect}") if logger && @debug
207
+ not_found
208
+ rescue Memcached::Error
209
+ log_error($!) if logger
210
+ end
211
+
212
+ def incr(*args)
213
+ super
214
+ rescue Memcached::NotFound
215
+ rescue Memcached::Error
216
+ log_error($!) if logger
217
+ end
218
+
219
+ def decr(*args)
220
+ super
221
+ rescue Memcached::NotFound
222
+ rescue Memcached::Error
223
+ log_error($!) if logger
224
+ end
225
+
226
+ def get_server_for_key(key, options = {})
227
+ server_by_key(key)
228
+ end
229
+
230
+ alias :reset :quit
231
+ alias :close :quit #nodoc
232
+ alias :flush_all :flush
233
+ alias :compare_and_swap :cas
234
+ alias :"[]" :get
235
+ alias :"[]=" :set
236
+
237
+ private
238
+
239
+ def stored
240
+ "STORED\r\n"
241
+ end
242
+
243
+ def deleted
244
+ "DELETED\r\n"
245
+ end
246
+
247
+ def not_stored
248
+ "NOT_STORED\r\n"
249
+ end
250
+
251
+ def not_found
252
+ "NOT_FOUND\r\n"
253
+ end
254
+
255
+ def log_error(err)
256
+ #logger.error("#{err}: \n\t#{err.backtrace.join("\n\t")}") if logger
257
+ logger.error("Memcached ERROR, #{err.class}: #{err}") if logger
258
+ end
259
+
260
+ end
261
+ else
262
+ Rails.logger.warn 'unable to determine memcache implementation' if defined? Rails
263
+ end #include the wraper
@@ -0,0 +1,40 @@
1
+ yml = YAML.load(IO.read(File.join(RAILS_ROOT, "config", "memcached.yml")))
2
+ memcache_config = yml[RAILS_ENV]
3
+ memcache_config.symbolize_keys! if memcache_config.respond_to?(:symbolize_keys!)
4
+
5
+ if defined?(DISABLE_CACHE_MONEY) || ENV['DISABLE_CACHE_MONEY'] == 'true' || memcache_config.nil? || memcache_config[:cache_money] != true
6
+ Rails.logger.info 'cache-money disabled'
7
+ class ActiveRecord::Base
8
+ def self.index(*args)
9
+ end
10
+ end
11
+ else
12
+ Rails.logger.info 'cache-money enabled'
13
+ require 'cache_money'
14
+
15
+ memcache_config[:logger] = Rails.logger
16
+ memcache_servers =
17
+ case memcache_config[:servers].class.to_s
18
+ when "String"; memcache_config[:servers].gsub(' ', '').split(',')
19
+ when "Array"; memcache_config[:servers]
20
+ end
21
+ $memcache = MemcachedWrapper.new(memcache_servers, memcache_config)
22
+
23
+ #ActionController::Base.cache_store = :cache_money_mem_cache_store
24
+ ActionController::Base.session_options[:cache] = $memcache if memcache_config[:sessions]
25
+ #silence_warnings {
26
+ # Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(:cache_money_mem_cache_store)
27
+ #}
28
+
29
+ $local = Cash::Local.new($memcache)
30
+ $lock = Cash::Lock.new($memcache)
31
+ $cache = Cash::Transactional.new($local, $lock)
32
+
33
+ # allow setting up caching on a per-model basis
34
+ unless memcache_config[:automatic_caching].to_s == 'false'
35
+ Rails.logger.info "cache-money: global model caching enabled"
36
+ class ActiveRecord::Base
37
+ is_cached(:repository => $cache)
38
+ end
39
+ end
40
+ end