Module: ActsAsCached::ClassMethods

Defined in:
lib/acts_as_cached/cache_methods.rb

Constant Summary collapse

@@nil_sentinel =
:_nil

Instance Method Summary collapse

Instance Method Details

#cache_configObject



5
6
7
# File 'lib/acts_as_cached/cache_methods.rb', line 5

def cache_config
  @cache_config ||= {}
end

#cache_key(cache_id) ⇒ Object



184
185
186
# File 'lib/acts_as_cached/cache_methods.rb', line 184

def cache_key(cache_id)
  [cache_name, cache_config[:version], cache_id].compact.join('/').gsub(' ', '_')[0..(max_key_length - 1)]
end

#cache_keys(*cache_ids) ⇒ Object



180
181
182
# File 'lib/acts_as_cached/cache_methods.rb', line 180

def cache_keys(*cache_ids)
  cache_ids.flatten.map { |cache_id| cache_key(cache_id) }
end

#cache_nameObject



176
177
178
# File 'lib/acts_as_cached/cache_methods.rb', line 176

def cache_name
  @cache_name ||= respond_to?(:model_name) ? model_name.cache_key : name
end

#cache_namespaceObject



162
163
164
# File 'lib/acts_as_cached/cache_methods.rb', line 162

def cache_namespace
  Rails.cache.respond_to?(:namespace) ? Rails.cache.namespace : ActsAsCached.config[:namespace]
end

#cache_optionsObject



9
10
11
# File 'lib/acts_as_cached/cache_methods.rb', line 9

def cache_options
  cache_config[:options] ||= {}
end

#cached?(cache_id = nil) ⇒ Boolean Also known as: is_cached?

Returns:

  • (Boolean)


144
145
146
# File 'lib/acts_as_cached/cache_methods.rb', line 144

def cached?(cache_id = nil)
  Rails.cache.exist?(cache_key(cache_id))
end

#caches(method, options = {}) ⇒ Object Also known as: cached

Encapsulates the pattern of writing custom cache methods which do nothing but wrap custom finders.

 => Story.caches(:find_popular)

 is the same as

 def self.cached_find_popular
   get_cache(:find_popular) { find_popular }
 end

The method also accepts both a :ttl and/or a :with key.
Obviously the :ttl value controls how long this method will
stay cached, while the :with key's value will be passed along
to the method.  The hash of the :with key will be stored with the key,
making two near-identical #caches calls with different :with values utilize
different caches.

=> Story.caches(:find_popular, :with => :today)

is the same as

 def self.cached_find_popular
   get_cache("find_popular:today") { find_popular(:today) }
 end

If your target method accepts multiple parameters, pass :withs an array.

> Story.caches(:find_popular, :withs => [ :one, :two ])

is the same as

def self.cached_find_popular
  get_cache("find_popular:onetwo") { find_popular(:one, :two) }
end


132
133
134
135
136
137
138
139
140
141
# File 'lib/acts_as_cached/cache_methods.rb', line 132

def caches(method, options = {})
  if options.keys.include?(:with)
    with = options.delete(:with)
    get_cache("#{method}:#{with}", options) { send(method, with) }
  elsif withs = options.delete(:withs)
    get_cache("#{method}:#{withs}", options) { send(method, *withs) }
  else
    get_cache(method, options) { send(method) }
  end
end

#expire_cache(cache_id = nil) ⇒ Object Also known as: clear_cache



86
87
88
89
# File 'lib/acts_as_cached/cache_methods.rb', line 86

def expire_cache(cache_id = nil)
  Rails.cache.delete(cache_key(cache_id))
  true
end

#fetch_cachable_data(cache_id = nil) ⇒ Object



153
154
155
156
157
158
159
160
# File 'lib/acts_as_cached/cache_methods.rb', line 153

def fetch_cachable_data(cache_id = nil)
  finder = cache_config[:finder] || :find
  return send(finder) unless cache_id

  args = [cache_id]
  args << cache_options.dup unless cache_options.blank?
  send(finder, *args)
end

#fetch_cache(cache_id) ⇒ Object



149
150
151
# File 'lib/acts_as_cached/cache_methods.rb', line 149

def fetch_cache(cache_id)
  Rails.cache.read(cache_key(cache_id))
end

#get_cache(*args) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/acts_as_cached/cache_methods.rb', line 13

def get_cache(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  args    = args.flatten

  ##
  # head off to get_caches if we were passed multiple cache_ids
  if args.size > 1
    return get_caches(args, options)
  else
    cache_id = args.first
  end

  if (item = fetch_cache(cache_id)).nil?
    set_cache(cache_id, block_given? ? yield : fetch_cachable_data(cache_id), options)
  else
    @@nil_sentinel == item ? nil : item
  end
end

#get_caches(*args) ⇒ Object

This method accepts an array of cache_ids which it will use to call get_multi on your cache store. Any misses will be fetched and saved to the cache, and a hash keyed by cache_id will ultimately be returned.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/acts_as_cached/cache_methods.rb', line 37

def get_caches(*args)
  options   = args.last.is_a?(Hash) ? args.pop : {}
  cache_ids = args.flatten.map(&:to_s)
  keys      = cache_keys(cache_ids)

  # Map memcache keys to object cache_ids in { memcache_key => object_id } format
  keys_map = Hash[*keys.zip(cache_ids).flatten]

  # Call get_multi and figure out which keys were missed based on what was a hit
  hits = Rails.cache.read_multi(*keys) || {}

  # Misses can take the form of key => nil
  hits.delete_if { |key, value| value.nil? }

  misses = keys - hits.keys
  hits.each { |k, v| hits[k] = nil if v == @@nil_sentinel }

  # Return our hash if there are no misses
  return hits.values.index_by(&:cache_id) if misses.empty?

  # Find any missed records
  needed_ids     = keys_map.values_at(*misses)
  missed_records = Array(fetch_cachable_data(needed_ids))

  # Cache the missed records
  missed_records.each { |missed_record| missed_record.set_cache(options) }

  # Return all records as a hash indexed by object cache_id
  (hits.values + missed_records).index_by(&:cache_id)
end

#get_caches_as_list(*args) ⇒ Object

simple wrapper for get_caches that returns the items as an ordered array



70
71
72
73
74
75
76
77
78
# File 'lib/acts_as_cached/cache_methods.rb', line 70

def get_caches_as_list(*args)
  cache_ids = args.last.is_a?(Hash) ? args.first : args
  cache_ids = [cache_ids].flatten
  hash      = get_caches(*args)

  cache_ids.map do |key|
    hash[key]
  end
end

#max_key_lengthObject

Memcache-client automatically prepends the namespace, plus a colon, onto keys, so we take that into account for the max key length. Rob Sanheim



168
169
170
171
172
173
174
# File 'lib/acts_as_cached/cache_methods.rb', line 168

def max_key_length
  unless @max_key_length
    key_size = cache_config[:key_size] || 250
    @max_key_length = cache_namespace ? (key_size - cache_namespace.length - 1) : key_size
  end
  @max_key_length
end

#reset_cache(cache_id = nil) ⇒ Object



92
93
94
# File 'lib/acts_as_cached/cache_methods.rb', line 92

def reset_cache(cache_id = nil)
  set_cache(cache_id, fetch_cachable_data(cache_id))
end

#set_cache(cache_id, value, options = nil) ⇒ Object



80
81
82
83
84
# File 'lib/acts_as_cached/cache_methods.rb', line 80

def set_cache(cache_id, value, options = nil)
  v = value.nil? ? @@nil_sentinel : value
  Rails.cache.write(cache_key(cache_id), v, options)
  value
end