mguymon-cache-money 0.2.12

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.
@@ -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