cache-machine 0.1.10 → 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/.gitignore +3 -1
- data/.travis.yml +6 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +0 -2
- data/README.md +258 -0
- data/VERSION +1 -1
- data/cache-machine.gemspec +7 -22
- data/db/.gitignore +1 -0
- data/lib/cache-machine.rb +6 -2
- data/lib/cache_machine/adapter.rb +127 -0
- data/lib/cache_machine/adapters/rails.rb +58 -0
- data/lib/cache_machine/cache.rb +34 -85
- data/lib/cache_machine/cache/associations.rb +29 -0
- data/lib/cache_machine/cache/class_timestamp.rb +31 -0
- data/lib/cache_machine/cache/collection.rb +131 -0
- data/lib/cache_machine/cache/map.rb +97 -241
- data/lib/cache_machine/cache/mapper.rb +135 -0
- data/lib/cache_machine/cache/resource.rb +108 -0
- data/lib/cache_machine/cache/scope.rb +24 -0
- data/lib/cache_machine/cache/timestamp_builder.rb +58 -0
- data/lib/cache_machine/helpers/cache_helper.rb +3 -3
- data/lib/cache_machine/logger.rb +9 -8
- data/lib/cache_machine/railtie.rb +11 -0
- data/lib/cache_machine/tasks.rb +10 -0
- data/spec/fixtures.rb +11 -6
- data/spec/lib/cache_machine/adapters/rails_spec.rb +149 -0
- data/spec/lib/cache_machine/cache/associations_spec.rb +32 -0
- data/spec/lib/cache_machine/cache/class_timestam_spec.rb +29 -0
- data/spec/lib/cache_machine/cache/collection_spec.rb +106 -0
- data/spec/lib/cache_machine/cache/map_spec.rb +34 -0
- data/spec/lib/cache_machine/cache/mapper_spec.rb +61 -0
- data/spec/lib/cache_machine/cache/resource_spec.rb +86 -0
- data/spec/lib/cache_machine/cache/scope_spec.rb +50 -0
- data/spec/lib/cache_machine/cache/timestamp_builder_spec.rb +52 -0
- data/spec/spec_helper.rb +9 -3
- metadata +35 -61
- data/README.rdoc +0 -186
- data/spec/lib/cache_machine_spec.rb +0 -161
@@ -0,0 +1,58 @@
|
|
1
|
+
module CacheMachine
|
2
|
+
module Adapters
|
3
|
+
require "cache_machine/adapter"
|
4
|
+
|
5
|
+
class Rails < CacheMachine::Adapter
|
6
|
+
|
7
|
+
def initialize(*options)
|
8
|
+
CacheMachine::Logger.info "CACHE_MACHINE: initialized default Rails adapter"
|
9
|
+
end
|
10
|
+
|
11
|
+
def append_id_to_map(target, association, id)
|
12
|
+
key = get_map_key(target, association)
|
13
|
+
::Rails.cache.write(key, (::Rails.cache.read(key) || []) | [id])
|
14
|
+
end
|
15
|
+
|
16
|
+
def append_id_to_reverse_map(resource, association, target, id)
|
17
|
+
key = get_reverse_map_key(resource, association, target)
|
18
|
+
::Rails.cache.write(key, (::Rails.cache.read(key) || []) | [id])
|
19
|
+
end
|
20
|
+
|
21
|
+
def association_ids(target, association)
|
22
|
+
::Rails.cache.fetch(get_map_key(target, association)) do
|
23
|
+
target.association_ids(association)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch(key, options = {}, &block)
|
28
|
+
::Rails.cache.fetch(get_content_key(key), options, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_timestamp(name, options = {}, &block)
|
32
|
+
::Rails.cache.fetch(get_timestamp_key(name), options, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete(key)
|
36
|
+
::Rails.cache.delete(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_content(key)
|
40
|
+
delete(get_content_key(key))
|
41
|
+
end
|
42
|
+
|
43
|
+
def reset_timestamp(name)
|
44
|
+
::Rails.cache.delete(get_timestamp_key(name))
|
45
|
+
end
|
46
|
+
|
47
|
+
def reverse_association_ids(resource, association, target)
|
48
|
+
::Rails.cache.fetch(get_reverse_map_key(resource, association, target)) do
|
49
|
+
target.cache_map_ids(resource, association)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def write_timestamp(name, &block)
|
54
|
+
::Rails.cache.write(get_timestamp_key(name), &block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/cache_machine/cache.rb
CHANGED
@@ -1,29 +1,19 @@
|
|
1
|
+
require "cache_machine/adapters/rails"
|
1
2
|
require "cache_machine/cache/map"
|
2
3
|
|
3
4
|
module CacheMachine
|
4
5
|
module Cache
|
5
|
-
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
# Enable cache by default.
|
8
8
|
@enabled = true
|
9
9
|
|
10
|
-
# Supported by default cache formats.
|
11
|
-
@formats = [nil, :ehtml, :html, :json, :xml]
|
12
|
-
|
13
10
|
# Returns if cache is enabled.
|
14
11
|
#
|
15
12
|
# @return [ false, true ]
|
16
|
-
def self.enabled
|
13
|
+
def self.enabled
|
17
14
|
@enabled
|
18
15
|
end
|
19
16
|
|
20
|
-
# Returns currently set formats.
|
21
|
-
#
|
22
|
-
# @return [Array<Symbol>]
|
23
|
-
def self.formats
|
24
|
-
@formats
|
25
|
-
end
|
26
|
-
|
27
17
|
# Enables/disables cache.
|
28
18
|
#
|
29
19
|
# @param [ false, true ] is_enabled
|
@@ -31,87 +21,46 @@ module CacheMachine
|
|
31
21
|
@enabled = is_enabled
|
32
22
|
end
|
33
23
|
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# @note Empty format entry will always be present.
|
24
|
+
# Returns adapter used for storing maps of cache.
|
37
25
|
#
|
38
|
-
# @
|
39
|
-
def self.
|
40
|
-
@
|
26
|
+
# @return [CacheMachine::Adapter]
|
27
|
+
def self.map_adapter
|
28
|
+
@map_adapter ||= CacheMachine::Adapters::Rails.new
|
41
29
|
end
|
42
30
|
|
43
|
-
|
44
|
-
|
45
|
-
|
31
|
+
# Sets adapter used for storing maps of cache.
|
32
|
+
#
|
33
|
+
# @param [CacheMachine::Adapter]
|
34
|
+
def self.map_adapter=(adapter)
|
35
|
+
@map_adapter = adapter
|
46
36
|
end
|
47
37
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
# @example Cache result of method to be expired when collection changes.
|
55
|
-
# acts_as_cache_machine_for :cats => :cat_ids
|
56
|
-
# @example Cache and expire dependent collections (_mouse_ change invalidates all other collection caches by chain)
|
57
|
-
# acts_as_cache_machine_for :mouses => :cats, :cats => [:dogs, :bears], :dogs, :bears
|
58
|
-
#
|
59
|
-
# @param [ Hash<Symbol, Array> ] associations Cache Map
|
60
|
-
def acts_as_cache_machine_for *associations
|
61
|
-
Time.zone ||= ActiveSupport::TimeZone[0]
|
62
|
-
|
63
|
-
include CacheMachine::Cache::Map
|
64
|
-
cache_associated(associations)
|
65
|
-
end
|
66
|
-
alias :cache_map :acts_as_cache_machine_for
|
67
|
-
|
68
|
-
# Returns timestamp of class collection.
|
69
|
-
#
|
70
|
-
# @example Return timestamp of the class.
|
71
|
-
# MyActiveRecordClass.timestamp
|
72
|
-
#
|
73
|
-
# @param [ Symbol ] format
|
74
|
-
#
|
75
|
-
# @return [ String ]
|
76
|
-
def timestamp format = nil
|
77
|
-
Rails.cache.fetch(timestamp_key format) { Time.now.to_i.to_s }
|
78
|
-
end
|
79
|
-
|
80
|
-
# Returns cache key to fetch timestamp from memcached.
|
81
|
-
#
|
82
|
-
# @param [ Symbol ] format
|
83
|
-
#
|
84
|
-
# @return [ String ]
|
85
|
-
def timestamp_key format = nil
|
86
|
-
[self.name, format, 'timestamp'].join '_'
|
87
|
-
end
|
38
|
+
# Returns adapter used for storing content being cached.
|
39
|
+
#
|
40
|
+
# @return [CacheMachine::Adapter]
|
41
|
+
def self.storage_adapter
|
42
|
+
@storage_adapter ||= CacheMachine::Adapters::Rails.new
|
43
|
+
end
|
88
44
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
#
|
96
|
-
# @return [ String ]
|
97
|
-
def timestamped_key format = nil
|
98
|
-
[timestamp_key(format), timestamp(format)].join '_'
|
99
|
-
end
|
45
|
+
# Sets adapter used for storing content being cached.
|
46
|
+
#
|
47
|
+
# @param [CacheMachine::Adapter]
|
48
|
+
def self.storage_adapter=(adapter)
|
49
|
+
@storage_adapter = adapter
|
50
|
+
end
|
100
51
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
Rails.cache.delete(cache_key)
|
108
|
-
end
|
52
|
+
# Returns adapter used for storing content being cached.
|
53
|
+
#
|
54
|
+
# @return [CacheMachine::Adapter]
|
55
|
+
def self.timestamps_adapter
|
56
|
+
@timestamps_adapter ||= CacheMachine::Adapters::Rails.new
|
57
|
+
end
|
109
58
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
59
|
+
# Sets adapter used for storing content being cached.
|
60
|
+
#
|
61
|
+
# @param [CacheMachine::Adapter]
|
62
|
+
def self.timestamps_adapter=(adapter)
|
63
|
+
@timestamps_adapter = adapter
|
114
64
|
end
|
115
65
|
end
|
116
66
|
end
|
117
|
-
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CacheMachine
|
2
|
+
module Cache
|
3
|
+
module Associations
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
# Returns ids of an association.
|
8
|
+
#
|
9
|
+
# @param [ String, Symbol ] association
|
10
|
+
#
|
11
|
+
# @return [ Array ]
|
12
|
+
def association_ids(association)
|
13
|
+
pk = self.class.reflect_on_association(association).klass.primary_key.to_sym
|
14
|
+
send(association).map &pk
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns associated relation from cache.
|
18
|
+
#
|
19
|
+
# @param [ String, Symbol ] association_name
|
20
|
+
#
|
21
|
+
# @return [ ActiveRecord::Relation ]
|
22
|
+
def associated_from_cache(association_name)
|
23
|
+
klass = self.class.reflect_on_association(association_name).klass
|
24
|
+
klass.where(klass.primary_key => CacheMachine::Cache::map_adapter.association_ids(self, association_name))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CacheMachine
|
2
|
+
module Cache
|
3
|
+
module ClassTimestamp
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
after_destroy { self.class.reset_timestamp }
|
8
|
+
after_save { self.class.reset_timestamp }
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
# Returns timestamp of class collection.
|
14
|
+
#
|
15
|
+
# @example Return timestamp of the class.
|
16
|
+
# MyActiveRecordClass.timestamp
|
17
|
+
#
|
18
|
+
# @return [ String ]
|
19
|
+
def timestamp
|
20
|
+
CacheMachine::Cache::timestamps_adapter.fetch_timestamp(self.name) { Time.now.to_i.to_s }
|
21
|
+
end
|
22
|
+
|
23
|
+
# Resets timestamp of class collection.
|
24
|
+
#
|
25
|
+
def reset_timestamp
|
26
|
+
CacheMachine::Cache::timestamps_adapter.reset_timestamp(self.name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module CacheMachine
|
2
|
+
module Cache
|
3
|
+
module Collection
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
DEFAULT_DEPENDENCY_OPTIONS = { :on => :after_save, :scopes => :scoped, :members => [] }
|
7
|
+
|
8
|
+
included do
|
9
|
+
include CacheMachine::Cache::Associations
|
10
|
+
include CacheMachine::Cache::Scope
|
11
|
+
|
12
|
+
cattr_accessor :cache_map_members
|
13
|
+
self.cache_map_members = {}
|
14
|
+
|
15
|
+
# Updates cache map per collection update.
|
16
|
+
#
|
17
|
+
# @param resource_instance
|
18
|
+
# @param [ String, Symbol ] collection_name
|
19
|
+
def update_cache_map!(resource_instance, collection_name)
|
20
|
+
resource_id = resource_instance.send(resource_instance.class.primary_key)
|
21
|
+
collection_id = self.send(self.class.primary_key)
|
22
|
+
CacheMachine::Cache.map_adapter.append_id_to_map(resource_instance, collection_name, collection_id)
|
23
|
+
CacheMachine::Cache.map_adapter.append_id_to_reverse_map(resource_instance.class, collection_name, self, resource_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Updates cache of the related resources.
|
27
|
+
#
|
28
|
+
# @param [ Class ] cache_resource
|
29
|
+
def update_resource_collections_cache!(cache_resource)
|
30
|
+
self.class.cache_map_members[cache_resource].each do |collection_name, options|
|
31
|
+
cache_map_ids = CacheMachine::Cache::map_adapter.reverse_association_ids(cache_resource, collection_name, self)
|
32
|
+
unless cache_map_ids.empty?
|
33
|
+
(options[:members] + [collection_name]).each do |member|
|
34
|
+
CacheMachine::Cache::Map.reset_cache_on_map(cache_resource, cache_map_ids, member)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Updates cache of the related resource.
|
41
|
+
#
|
42
|
+
# @param [ Class ] cache_resource
|
43
|
+
def update_dependent_cache!(cache_resource = nil)
|
44
|
+
if cache_resource
|
45
|
+
update_resource_collections_cache!(cache_resource)
|
46
|
+
else
|
47
|
+
self.class.cache_map_members.keys.each &:update_resource_collections_cache!
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns all ids from resource associated with this collection member.
|
52
|
+
#
|
53
|
+
# @param [ Class ] cache_resource
|
54
|
+
# @param [ String, Symbol ] collection_name
|
55
|
+
#
|
56
|
+
# @return [ Array ]
|
57
|
+
def cache_map_ids(cache_resource, collection_name)
|
58
|
+
pk = self.class.primary_key.to_sym
|
59
|
+
resource_table_name = cache_resource.table_name
|
60
|
+
resource_pk = cache_resource.primary_key.to_sym
|
61
|
+
|
62
|
+
cache_resource.under_cache_scopes.joins(collection_name).
|
63
|
+
where(collection_name => { pk => self.send(pk) }).
|
64
|
+
select("#{resource_table_name}.#{resource_pk}").to_a.map &resource_pk
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
|
70
|
+
# Builds cache dependency.
|
71
|
+
#
|
72
|
+
# @param [ Class ] cache_resource
|
73
|
+
# @param [ String, Symbol ] collection_name
|
74
|
+
# @param [ Hash ] options
|
75
|
+
def register_cache_dependency(cache_resource, collection_name, options = {})
|
76
|
+
return if self.cache_map_members[cache_resource].try('[]', collection_name)
|
77
|
+
|
78
|
+
options.reverse_merge!(CacheMachine::Cache::Collection::DEFAULT_DEPENDENCY_OPTIONS)
|
79
|
+
|
80
|
+
# Register scopes.
|
81
|
+
self.cache_scopes = [*options[:scopes]]
|
82
|
+
|
83
|
+
# Save dependency options.
|
84
|
+
(self.cache_map_members[cache_resource] ||= {}).tap do |resource_collection|
|
85
|
+
resource_collection[collection_name] = options
|
86
|
+
end
|
87
|
+
|
88
|
+
# Prepare callbacks to be executed when it is time to expire the cache.
|
89
|
+
reset_cache_proc = Proc.new do
|
90
|
+
update_resource_collections_cache!(cache_resource)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Bind callbacks.
|
94
|
+
[*options[:on]].each { |callback| self.send(callback, &reset_cache_proc) }
|
95
|
+
|
96
|
+
# When new element appears - update maps.
|
97
|
+
cache_resource.send(:add_association_callbacks, collection_name,
|
98
|
+
:after_add => lambda { |resource_instance, collection_instance|
|
99
|
+
collection_instance.update_cache_map!(resource_instance, collection_name)
|
100
|
+
})
|
101
|
+
|
102
|
+
# Hook on '<<', 'concat' operations.
|
103
|
+
#cache_resource.send(:add_association_callbacks, collection_name,
|
104
|
+
# :after_add => lambda { |resource_instance, collection_instance|
|
105
|
+
# collection_instance.update_resource_collections_cache!(resource_instance.class)
|
106
|
+
# })
|
107
|
+
end
|
108
|
+
|
109
|
+
# Resets cache of associated resource instance.
|
110
|
+
#
|
111
|
+
# @param resource_instance
|
112
|
+
# @param [ String, Symbol ] association_name
|
113
|
+
def reset_resource_cache(resource_instance, association_name)
|
114
|
+
CacheMachine::Cache::Map.reset_cache_on_map(resource_instance.class,
|
115
|
+
resource_instance.send(resource_instance.class.primary_key),
|
116
|
+
association_name)
|
117
|
+
|
118
|
+
collection = self.cache_map_members[resource_instance.class][association_name]
|
119
|
+
|
120
|
+
if collection
|
121
|
+
collection[:members].each do |member|
|
122
|
+
CacheMachine::Cache::Map.reset_cache_on_map(resource_instance.class,
|
123
|
+
resource_instance.send(resource_instance.class.primary_key),
|
124
|
+
member)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -1,262 +1,118 @@
|
|
1
1
|
module CacheMachine
|
2
2
|
module Cache
|
3
|
+
require 'cache_machine/cache/mapper'
|
3
4
|
|
4
|
-
|
5
|
-
|
5
|
+
class Map
|
6
|
+
cattr_accessor :registered_models
|
7
|
+
self.registered_models = []
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
cattr_accessor :registered_maps
|
10
|
+
self.registered_maps = []
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
# Draws cache dependency graph.
|
13
|
+
#
|
14
|
+
# @return [ nil ]
|
15
|
+
def draw(&block)
|
16
|
+
self.class.registered_maps << block
|
17
|
+
nil
|
13
18
|
end
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# @example Define timestamp to be updated every hour.
|
29
|
-
# class MyModel < ActiveRecord::Base
|
30
|
-
# include CacheMachine::Cache
|
31
|
-
# define_timestamp(:my_timestamp, :expires_in => 1.hour) { my_optional_value }
|
32
|
-
# end
|
33
|
-
#
|
34
|
-
# @param [ String, Symbol ] timestamp_name
|
35
|
-
# @param [ Hash ] options
|
36
|
-
def define_timestamp timestamp_name, options = {}, &block
|
37
|
-
if block_given?
|
38
|
-
options[:timestamp] = block
|
39
|
-
end
|
40
|
-
|
41
|
-
define_method timestamp_name do
|
42
|
-
fetch_cache_of(timestamp_key_of(timestamp_name), options) do
|
43
|
-
CacheMachine::Logger.info "CACHE_MACHINE (define_timestamp): deleting old timestamp '#{timestamp_name}'."
|
44
|
-
delete_cache_of timestamp_name # Case when cache expired by time.
|
45
|
-
Time.zone.now.to_i.to_s
|
20
|
+
# Fills association map in cache.
|
21
|
+
#
|
22
|
+
# @param [ Class ] resource
|
23
|
+
def self.fill_associations_map(resource)
|
24
|
+
resource.under_cache_scopes.
|
25
|
+
select("#{resource.table_name}.#{resource.primary_key}").find_each do |instance|
|
26
|
+
resource.cached_collections.each do |collection_name|
|
27
|
+
CacheMachine::Cache::map_adapter.association_ids(instance, collection_name)
|
28
|
+
association_class = resource.reflect_on_association(collection_name).klass
|
29
|
+
association_class.under_cache_scopes.
|
30
|
+
select("#{association_class.table_name}.#{association_class.primary_key}").
|
31
|
+
find_each do |associated_instance|
|
32
|
+
CacheMachine::Cache::map_adapter.reverse_association_ids(resource, collection_name, associated_instance)
|
46
33
|
end
|
47
34
|
end
|
48
35
|
end
|
49
|
-
|
50
|
-
# Deletes cache of collection associated via many-to-many.
|
51
|
-
#
|
52
|
-
# @param [ ActiveRecord::Base ]
|
53
|
-
def delete_association_cache_on record, reflection
|
54
|
-
pk = record.class.primary_key
|
55
|
-
|
56
|
-
joining = unless reflection.options[:source_type]
|
57
|
-
reflection.through_reflection ? { reflection.through_reflection.name => reflection.source_reflection.name } : reflection.name
|
58
|
-
else
|
59
|
-
reflection.name
|
60
|
-
end
|
61
|
-
|
62
|
-
self.joins(joining).where(reflection.table_name => { pk => record.send(pk) }).find_each do |cache_source_record|
|
63
|
-
cache_source_record.delete_cache_of reflection.name
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Hooks association changes.
|
68
|
-
#
|
69
|
-
# @private
|
70
|
-
def has_many(association_id, options = {}) #:nodoc:
|
71
|
-
# Ensure what collection should be tracked.
|
72
|
-
if (should_be_on_hook = self.cache_map.keys.include?(association_id)) && options[:through]
|
73
|
-
# If relation is _many_to_many_ track collection changes.
|
74
|
-
options[:after_add] = \
|
75
|
-
options[:before_remove] = :delete_association_cache_on
|
76
|
-
end
|
77
|
-
super
|
78
|
-
hook_cache_machine_on association_id if should_be_on_hook
|
79
|
-
end
|
80
|
-
|
81
|
-
# Hooks association changes.
|
82
|
-
#
|
83
|
-
# @private
|
84
|
-
def has_and_belongs_to_many(association_id, options = {}) #:nodoc:
|
85
|
-
|
86
|
-
# Ensure what collection should be tracked.
|
87
|
-
if(should_be_on_hook = self.cache_map.keys.include?(association_id))
|
88
|
-
|
89
|
-
# If relation is many-to-many track collection changes.
|
90
|
-
options[:after_add] = \
|
91
|
-
options[:before_remove] = :delete_association_cache_on
|
92
|
-
end
|
93
|
-
super
|
94
|
-
hook_cache_machine_on association_id if should_be_on_hook
|
95
|
-
end
|
96
|
-
|
97
|
-
protected
|
98
|
-
|
99
|
-
# Hooks Cache Machine.
|
100
|
-
#
|
101
|
-
# @param [ Symbol ] association_id
|
102
|
-
def hook_cache_machine_on association_id
|
103
|
-
reset_cache_proc = Proc.new do |reflection, target_class, &block|
|
104
|
-
block ||= lambda { target_class.delete_association_cache_on self, reflection }
|
105
|
-
|
106
|
-
reflection.klass.after_save &block
|
107
|
-
reflection.klass.before_destroy &block
|
108
|
-
end
|
109
|
-
|
110
|
-
case (reflection = (target_class = self).reflect_on_association association_id)
|
111
|
-
when ActiveRecord::Reflection::ThroughReflection
|
112
|
-
# If association is _many_to_many_ it should reset its cache for all associated objects with class +target_class+.
|
113
|
-
reset_cache_proc.call(reflection, target_class)
|
114
|
-
when ActiveRecord::Reflection::AssociationReflection
|
115
|
-
if reflection.macro == :has_and_belongs_to_many
|
116
|
-
reset_cache_proc.call(reflection, target_class)
|
117
|
-
else
|
118
|
-
# If association is _has_many_ or _has_one_ it should reset its cache for associated object with class +target_class+.
|
119
|
-
reset_cache_proc.call(reflection) do
|
120
|
-
target_class.where((reflection.options[:primary_key] || :id) =>
|
121
|
-
send(reflection.options[:foreign_key] || reflection.primary_key_name)).first.try(:delete_cache_of, association_id)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
36
|
end
|
127
37
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
[ self.class.name,
|
140
|
-
self.to_param,
|
141
|
-
_member,
|
142
|
-
options[:format],
|
143
|
-
options[:page] || 1,
|
144
|
-
timestamp ].flatten.compact.join '/'
|
145
|
-
end
|
146
|
-
|
147
|
-
# Fetches cache of the member.
|
148
|
-
#
|
149
|
-
# @example Fetch cache of associated collection to be refreshed every hour.
|
150
|
-
# @instance.fetch_cache_of :association, :timestamp => lambda { custom_instance_method },
|
151
|
-
# :expires_in => 1.hour
|
152
|
-
#
|
153
|
-
# @param [ Symbol ] _member
|
154
|
-
# @param [ Hash ] options
|
155
|
-
#
|
156
|
-
# @return [ * ]
|
157
|
-
def fetch_cache_of _member, options = {}, &block
|
158
|
-
if CacheMachine::Cache::enabled?
|
159
|
-
expires_in = if expires_at = options[:expires_at]
|
160
|
-
if expires_at.kind_of? Proc
|
161
|
-
expires_at = expires_at.call
|
162
|
-
end
|
163
|
-
|
164
|
-
if expires_at.kind_of? Time
|
165
|
-
expires_at - Time.zone.now
|
166
|
-
else
|
167
|
-
raise ArgumentError, "expires_at is not a Time"
|
168
|
-
end
|
169
|
-
else
|
170
|
-
options[:expires_in]
|
171
|
-
end
|
172
|
-
|
173
|
-
CacheMachine::Logger.info "CACHE_MACHINE (fetch_cache_of): reading '#{cache_key}'."
|
174
|
-
Rails.cache.fetch(cache_key_of(_member, options), :expires_in => expires_in, &block)
|
175
|
-
else
|
176
|
-
yield
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
# Removes all caches using map.
|
181
|
-
def delete_all_caches
|
182
|
-
self.class.cache_map.to_a.flatten.uniq.each &method(:delete_cache_of)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Recursively deletes cache by map starting from the member.
|
186
|
-
#
|
187
|
-
# @param [ Symbol ] _member
|
188
|
-
def delete_cache_of _member
|
189
|
-
delete_cache_of_only _member
|
190
|
-
if chain = self.class.cache_map[_member]
|
191
|
-
[*chain].each &method(:delete_cache_of)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# Deletes cache of the only member ignoring cache map.
|
196
|
-
#
|
197
|
-
# @param [ Symbol ] _member
|
198
|
-
def delete_cache_of_only _member
|
199
|
-
CacheMachine::Cache.formats.each do |cache_format|
|
200
|
-
page_nr = 0; begin
|
201
|
-
cache_key = cache_key_of(_member, {:format => cache_format, :page => page_nr += 1})
|
202
|
-
CacheMachine::Logger.info "CACHE_MACHINE (delete_cache_of_only): deleting '#{cache_key}'"
|
203
|
-
end while Rails.cache.delete(cache_key)
|
204
|
-
end
|
205
|
-
reset_timestamp_of _member
|
206
|
-
end
|
207
|
-
|
208
|
-
# Returns timestamp cache key for anything.
|
209
|
-
#
|
210
|
-
# @param [ String, Symbol ] anything
|
211
|
-
#
|
212
|
-
# @return [ String ]
|
213
|
-
def timestamp_key_of anything
|
214
|
-
[self.class.name, self.to_param, anything, 'timestamp'].join '/'
|
215
|
-
end
|
38
|
+
# Returns cache key for cache resource.
|
39
|
+
#
|
40
|
+
# @param [ Class ] resource
|
41
|
+
# @param [ String, Numeric ] id
|
42
|
+
# @param [ String, Symbol ] member
|
43
|
+
#
|
44
|
+
# @return [ String ]
|
45
|
+
def self.resource_cache_key(resource, id, member)
|
46
|
+
"#{resource}##{id}:#{member}"
|
47
|
+
end
|
216
48
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
else
|
228
|
-
Time.zone.now.to_i.to_s
|
229
|
-
end
|
230
|
-
end
|
49
|
+
# Returns timestamp key for cache resource.
|
50
|
+
#
|
51
|
+
# @param [ Class ] resource
|
52
|
+
# @param [ String, Numeric ] id
|
53
|
+
# @param [ String, Symbol ] member
|
54
|
+
#
|
55
|
+
# @return [ String ]
|
56
|
+
def self.timestamp_key(resource, id, member)
|
57
|
+
"#{resource}##{id}:#{member}_timestamp"
|
58
|
+
end
|
231
59
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
60
|
+
# Returns cache key for cache resource.
|
61
|
+
#
|
62
|
+
# @param [ Class ] resource
|
63
|
+
# @param [ String, Numeric ] id
|
64
|
+
# @param [ String, Symbol ] member
|
65
|
+
#
|
66
|
+
# @return [ String ]
|
67
|
+
def self.timestamped_resource_member_key(resource, id, member, timestamp)
|
68
|
+
key = timestamp_key(resource, id, member)
|
69
|
+
collection_timestamp = CacheMachine::Cache::timestamps_adapter.fetch_timestamp(key) { Time.now.to_i.to_s }
|
70
|
+
"#{key}_#{collection_timestamp}_#{timestamp}"
|
71
|
+
end
|
238
72
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
73
|
+
# Returns cache key for cache resource.
|
74
|
+
#
|
75
|
+
# @param [ Class ] resource
|
76
|
+
# @param [ String, Numeric, Array ] id
|
77
|
+
# @param [ String, Symbol ] member
|
78
|
+
#
|
79
|
+
# @return [ String ]
|
80
|
+
def self.reset_cache_on_map(resource, ids, member)
|
81
|
+
[*ids].each do |id|
|
82
|
+
key = resource_cache_key(resource, id, member)
|
83
|
+
CacheMachine::Logger.info "Deleting cache by map: #{key}"
|
84
|
+
CacheMachine::Cache::storage_adapter.delete_content(key)
|
85
|
+
|
86
|
+
key = timestamp_key(resource, id, member)
|
87
|
+
CacheMachine::Logger.info "CACHE_MACHINE (reset_timestamp_of): deleting '#{key}'."
|
88
|
+
CacheMachine::Cache::timestamps_adapter.reset_timestamp(key)
|
244
89
|
end
|
90
|
+
end
|
245
91
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
92
|
+
# Returns cache key for cache resource.
|
93
|
+
#
|
94
|
+
# @param [ Class ] resource
|
95
|
+
# @param [ String, Numeric, Array ] id
|
96
|
+
# @param [ String, Symbol ] member
|
97
|
+
#
|
98
|
+
# @return [ String ]
|
99
|
+
def self.reset_resource_cache(resource, id, member)
|
100
|
+
key = resource_cache_key(resource, id, member)
|
101
|
+
CacheMachine::Logger.info "Deleting cache by map: #{key}"
|
102
|
+
CacheMachine::Cache::storage_adapter.delete_content(key)
|
103
|
+
end
|
256
104
|
|
257
|
-
|
258
|
-
|
259
|
-
|
105
|
+
# Returns cache key for cache resource.
|
106
|
+
#
|
107
|
+
# @param [ Class ] resource
|
108
|
+
# @param [ String, Numeric, Array ] id
|
109
|
+
# @param [ String, Symbol ] member
|
110
|
+
#
|
111
|
+
# @return [ String ]
|
112
|
+
def self.reset_resource_timestamp(resource, id, member)
|
113
|
+
key = timestamp_key(resource, id, member)
|
114
|
+
CacheMachine::Logger.info "CACHE_MACHINE (reset_timestamp_of): deleting '#{key}'."
|
115
|
+
CacheMachine::Cache::timestamps_adapter.reset_timestamp(key)
|
260
116
|
end
|
261
117
|
end
|
262
118
|
end
|