simple_cacheable 1.3.2 → 1.3.3
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.
- checksums.yaml +7 -0
- data/.gitignore +0 -1
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/ChangeLog +7 -0
- data/Gemfile +2 -4
- data/Gemfile.lock +115 -0
- data/README.md +1 -1
- data/lib/cacheable/caches.rb +15 -0
- data/lib/cacheable/expiry.rb +53 -0
- data/lib/cacheable/keys.rb +54 -0
- data/lib/cacheable/types/association_cache.rb +92 -0
- data/lib/cacheable/types/attribute_cache.rb +30 -0
- data/lib/cacheable/types/class_method_cache.rb +23 -0
- data/lib/cacheable/types/key_cache.rb +17 -0
- data/lib/cacheable/types/method_cache.rb +19 -0
- data/lib/cacheable/version.rb +1 -1
- data/lib/cacheable.rb +14 -263
- data/spec/cacheable/expiry_cache_spec.rb +81 -0
- data/spec/cacheable/types/association_cache_spec.rb +166 -0
- data/spec/cacheable/types/attribute_cache_spec.rb +60 -0
- data/spec/cacheable/types/class_method_cache_spec.rb +43 -0
- data/spec/cacheable/types/key_cache_spec.rb +28 -0
- data/spec/cacheable/types/method_cache_spec.rb +33 -0
- data/spec/cacheable_spec.rb +18 -292
- data/spec/models/post.rb +10 -1
- data/spec/spec_helper.rb +0 -1
- metadata +37 -22
- data/.rvmrc +0 -2
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b46d1110354d90d106fe5abdfed1eaa6801dd7e4
|
4
|
+
data.tar.gz: 763dac9a0c34135095974d09bb2575fbc5106ed2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f0fd40d41856c6efb7ecdfa1d8263ab2178b98c4cc8656d5bde2f3e67d71029c8cdebacf72a9d127abd775867d1d452fe86e8b122ec8f8adb315b9fab444999b
|
7
|
+
data.tar.gz: 322aad0ed21db8769cc06557df6461f8e8a29ddad18597922a4410480dc71a3d639217ea07a88b3c861db899802b4cf05c615242dc5069a9aab55325bfb0983a
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
cacheable
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0
|
data/ChangeLog
ADDED
data/Gemfile
CHANGED
@@ -4,7 +4,7 @@ source "http://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
platforms :ruby do
|
7
|
-
gem "sqlite3
|
7
|
+
gem "sqlite3"
|
8
8
|
gem "memcached"
|
9
9
|
end
|
10
10
|
|
@@ -12,6 +12,4 @@ platforms :jruby do
|
|
12
12
|
gem "activerecord-jdbc-adapter"
|
13
13
|
gem "activerecord-jdbcsqlite3-adapter"
|
14
14
|
gem "jruby-memcached"
|
15
|
-
end
|
16
|
-
|
17
|
-
gem "debugger"
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
simple_cacheable (1.3.2)
|
5
|
+
rails (>= 3.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actionmailer (4.0.0)
|
11
|
+
actionpack (= 4.0.0)
|
12
|
+
mail (~> 2.5.3)
|
13
|
+
actionpack (4.0.0)
|
14
|
+
activesupport (= 4.0.0)
|
15
|
+
builder (~> 3.1.0)
|
16
|
+
erubis (~> 2.7.0)
|
17
|
+
rack (~> 1.5.2)
|
18
|
+
rack-test (~> 0.6.2)
|
19
|
+
activemodel (4.0.0)
|
20
|
+
activesupport (= 4.0.0)
|
21
|
+
builder (~> 3.1.0)
|
22
|
+
activerecord (4.0.0)
|
23
|
+
activemodel (= 4.0.0)
|
24
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
25
|
+
activesupport (= 4.0.0)
|
26
|
+
arel (~> 4.0.0)
|
27
|
+
activerecord-deprecated_finders (1.0.3)
|
28
|
+
activerecord-jdbc-adapter (1.2.2.1)
|
29
|
+
activerecord-jdbcsqlite3-adapter (1.2.2.1)
|
30
|
+
activerecord-jdbc-adapter (~> 1.2.2.1)
|
31
|
+
jdbc-sqlite3 (~> 3.7.2)
|
32
|
+
activesupport (4.0.0)
|
33
|
+
i18n (~> 0.6, >= 0.6.4)
|
34
|
+
minitest (~> 4.2)
|
35
|
+
multi_json (~> 1.3)
|
36
|
+
thread_safe (~> 0.1)
|
37
|
+
tzinfo (~> 0.3.37)
|
38
|
+
arel (4.0.0)
|
39
|
+
atomic (1.1.10)
|
40
|
+
atomic (1.1.10-java)
|
41
|
+
builder (3.1.4)
|
42
|
+
diff-lcs (1.1.3)
|
43
|
+
erubis (2.7.0)
|
44
|
+
hike (1.2.3)
|
45
|
+
i18n (0.6.4)
|
46
|
+
jdbc-sqlite3 (3.7.2)
|
47
|
+
jruby-memcached (0.5.1)
|
48
|
+
mail (2.5.4)
|
49
|
+
mime-types (~> 1.16)
|
50
|
+
treetop (~> 1.4.8)
|
51
|
+
memcached (1.4.6)
|
52
|
+
metaclass (0.0.1)
|
53
|
+
mime-types (1.23)
|
54
|
+
minitest (4.7.5)
|
55
|
+
mocha (0.10.5)
|
56
|
+
metaclass (~> 0.0.1)
|
57
|
+
multi_json (1.7.7)
|
58
|
+
polyglot (0.3.3)
|
59
|
+
rack (1.5.2)
|
60
|
+
rack-test (0.6.2)
|
61
|
+
rack (>= 1.0)
|
62
|
+
rails (4.0.0)
|
63
|
+
actionmailer (= 4.0.0)
|
64
|
+
actionpack (= 4.0.0)
|
65
|
+
activerecord (= 4.0.0)
|
66
|
+
activesupport (= 4.0.0)
|
67
|
+
bundler (>= 1.3.0, < 2.0)
|
68
|
+
railties (= 4.0.0)
|
69
|
+
sprockets-rails (~> 2.0.0)
|
70
|
+
railties (4.0.0)
|
71
|
+
actionpack (= 4.0.0)
|
72
|
+
activesupport (= 4.0.0)
|
73
|
+
rake (>= 0.8.7)
|
74
|
+
thor (>= 0.18.1, < 2.0)
|
75
|
+
rake (10.1.0)
|
76
|
+
rspec (2.8.0)
|
77
|
+
rspec-core (~> 2.8.0)
|
78
|
+
rspec-expectations (~> 2.8.0)
|
79
|
+
rspec-mocks (~> 2.8.0)
|
80
|
+
rspec-core (2.8.0)
|
81
|
+
rspec-expectations (2.8.0)
|
82
|
+
diff-lcs (~> 1.1.2)
|
83
|
+
rspec-mocks (2.8.0)
|
84
|
+
sprockets (2.10.0)
|
85
|
+
hike (~> 1.2)
|
86
|
+
multi_json (~> 1.0)
|
87
|
+
rack (~> 1.0)
|
88
|
+
tilt (~> 1.1, != 1.3.0)
|
89
|
+
sprockets-rails (2.0.0)
|
90
|
+
actionpack (>= 3.0)
|
91
|
+
activesupport (>= 3.0)
|
92
|
+
sprockets (~> 2.8)
|
93
|
+
sqlite3 (1.3.7)
|
94
|
+
thor (0.18.1)
|
95
|
+
thread_safe (0.1.0)
|
96
|
+
atomic
|
97
|
+
tilt (1.4.1)
|
98
|
+
treetop (1.4.14)
|
99
|
+
polyglot
|
100
|
+
polyglot (>= 0.3.1)
|
101
|
+
tzinfo (0.3.37)
|
102
|
+
|
103
|
+
PLATFORMS
|
104
|
+
java
|
105
|
+
ruby
|
106
|
+
|
107
|
+
DEPENDENCIES
|
108
|
+
activerecord-jdbc-adapter
|
109
|
+
activerecord-jdbcsqlite3-adapter
|
110
|
+
jruby-memcached
|
111
|
+
memcached
|
112
|
+
mocha
|
113
|
+
rspec
|
114
|
+
simple_cacheable!
|
115
|
+
sqlite3
|
data/README.md
CHANGED
@@ -76,7 +76,7 @@ Gotchas
|
|
76
76
|
|
77
77
|
Caching, and caching invalidation specifically, can be hard and confusing. Simple Cacheable methods should
|
78
78
|
expire correctly in most cases. Be careful using `with_method` and `with_class_method`, they should
|
79
|
-
specifically not be used to return collections. This is demonstrated well Tobias Lutke's presentation: [Rockstar Memcaching][2].
|
79
|
+
specifically not be used to return collections. This is demonstrated well in Tobias Lutke's presentation: [Rockstar Memcaching][2].
|
80
80
|
|
81
81
|
Copyright © 2011 Richard Huang (flyerhzm@gmail.com), released under the MIT license
|
82
82
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "cacheable/types/key_cache"
|
2
|
+
require "cacheable/types/attribute_cache"
|
3
|
+
require "cacheable/types/method_cache"
|
4
|
+
require "cacheable/types/class_method_cache"
|
5
|
+
require "cacheable/types/association_cache"
|
6
|
+
|
7
|
+
module Cacheable
|
8
|
+
module Caches
|
9
|
+
include Cacheable::KeyCache
|
10
|
+
include Cacheable::AttributeCache
|
11
|
+
include Cacheable::MethodCache
|
12
|
+
include Cacheable::ClassMethodCache
|
13
|
+
include Cacheable::AssocationCache
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module Expiry
|
3
|
+
def expire_model_cache
|
4
|
+
expire_key_cache if self.class.cached_key
|
5
|
+
expire_attribute_cache if self.class.cached_indices.present?
|
6
|
+
expire_all_attribute_cache if self.class.cached_indices.present?
|
7
|
+
expire_method_cache if self.class.cached_methods.present?
|
8
|
+
expire_class_method_cache if self.class.cached_class_methods.present?
|
9
|
+
|
10
|
+
if self.class.cached_associations.present?
|
11
|
+
self.class.cached_associations.each do |assoc|
|
12
|
+
expire_association_cache(assoc)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def expire_key_cache
|
18
|
+
Rails.cache.delete model_cache_key
|
19
|
+
end
|
20
|
+
|
21
|
+
def expire_attribute_cache
|
22
|
+
self.class.cached_indices.each do |attribute, values|
|
23
|
+
value = self.send(attribute)
|
24
|
+
Rails.cache.delete self.class.attribute_cache_key(attribute, value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def expire_all_attribute_cache
|
29
|
+
self.class.cached_indices.each do |attribute, values|
|
30
|
+
value = self.send(attribute)
|
31
|
+
Rails.cache.delete self.class.all_attribute_cache_key(attribute, value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def expire_method_cache
|
36
|
+
self.class.cached_methods.each do |meth|
|
37
|
+
Rails.cache.delete method_cache_key(meth)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def expire_class_method_cache
|
42
|
+
self.class.cached_class_methods.each do |meth, args|
|
43
|
+
args.each do |arg|
|
44
|
+
Rails.cache.delete self.class.class_method_cache_key(meth, arg)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def expire_association_cache(name)
|
50
|
+
Rails.cache.delete have_association_cache_key(name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module Keys
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(Cacheable::Keys::ClassKeys)
|
6
|
+
base.send :include, Cacheable::Keys::InstanceKeys
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassKeys
|
10
|
+
|
11
|
+
def attribute_cache_key(attribute, value)
|
12
|
+
"#{name.tableize}/attribute/#{attribute}/#{URI.escape(value.to_s)}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def all_attribute_cache_key(attribute, value)
|
16
|
+
"#{name.tableize}/attribute/#{attribute}/all/#{URI.escape(value.to_s)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def class_method_cache_key(meth, *args)
|
20
|
+
key = "#{name.tableize}/class_method/#{meth}"
|
21
|
+
args.flatten!
|
22
|
+
key += "/#{args.join('+')}" if args.any?
|
23
|
+
return key
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceKeys
|
29
|
+
|
30
|
+
def model_cache_key
|
31
|
+
"#{self.class.name.tableize}/#{self.id.to_i}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_cache_key(meth)
|
35
|
+
"#{model_cache_key}/method/#{meth}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def belong_association_cache_key(name, polymorphic=nil)
|
39
|
+
name = name.to_s if name.is_a?(Symbol)
|
40
|
+
if polymorphic
|
41
|
+
"#{self.send("#{name}_type").tableize}/#{self.send("#{name}_id")}"
|
42
|
+
else
|
43
|
+
"#{name.tableize}/#{self.send(name + "_id")}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def have_association_cache_key(name)
|
48
|
+
"#{model_cache_key}/association/#{name}"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module AssocationCache
|
3
|
+
|
4
|
+
def with_association(*association_names)
|
5
|
+
self.cached_associations = association_names
|
6
|
+
|
7
|
+
association_names.each do |association_name|
|
8
|
+
association = reflect_on_association(association_name)
|
9
|
+
|
10
|
+
if :belongs_to == association.macro
|
11
|
+
polymorphic = association.options[:polymorphic]
|
12
|
+
polymorphic ||= false
|
13
|
+
|
14
|
+
define_method("cached_#{association_name}") do
|
15
|
+
Rails.cache.fetch belong_association_cache_key(association_name, polymorphic) do
|
16
|
+
send(association_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
else
|
20
|
+
if through_reflection_name = association.options[:through]
|
21
|
+
through_association = self.reflect_on_association(through_reflection_name)
|
22
|
+
|
23
|
+
# FIXME it should be the only reflection but I'm not 100% positive
|
24
|
+
reverse_through_association = through_association.klass.reflect_on_all_associations(:belongs_to).first
|
25
|
+
|
26
|
+
# In a through association it doesn't have to be a belongs_to
|
27
|
+
reverse_association = association.klass.reflect_on_all_associations(:belongs_to).find { |reverse_association|
|
28
|
+
reverse_association.options[:polymorphic] ? reverse_association.name == association.source_reflection.options[:as] : reverse_association.klass == self
|
29
|
+
}
|
30
|
+
if reverse_association
|
31
|
+
association.klass.class_eval do
|
32
|
+
after_commit "expire_#{association_name}_cache".to_sym
|
33
|
+
|
34
|
+
define_method("expire_#{association_name}_cache") do
|
35
|
+
if respond_to? "expire_#{reverse_association.name}_cache".to_sym
|
36
|
+
# cached_viewable.expire_association_cache
|
37
|
+
send("cached_#{reverse_association.name}").expire_association_cache(association_name)
|
38
|
+
else
|
39
|
+
send(reverse_association.name).send(reverse_through_association.name).expire_association_cache(association_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
elsif :has_and_belongs_to_many == association.macro
|
45
|
+
# No such thing as a polymorphic has_and_belongs_to_many
|
46
|
+
reverse_association = association.klass.reflect_on_all_associations(:has_and_belongs_to_many).find { |reverse_association|
|
47
|
+
reverse_association.klass == self
|
48
|
+
}
|
49
|
+
|
50
|
+
association.klass.class_eval do
|
51
|
+
after_commit "expire_#{association_name}_cache".to_sym
|
52
|
+
|
53
|
+
define_method "expire_#{association_name}_cache" do
|
54
|
+
if respond_to? "cached_#{reverse_association.name}".to_sym
|
55
|
+
# cached_viewable.expire_association_cache
|
56
|
+
send("cached_#{reverse_association.name}").expire_association_cache(association_name)
|
57
|
+
else
|
58
|
+
send("#{reverse_association.name}").each do |assoc|
|
59
|
+
assoc.expire_association_cache(association_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
reverse_association = association.klass.reflect_on_all_associations(:belongs_to).find { |reverse_association|
|
66
|
+
reverse_association.options[:polymorphic] ? reverse_association.name == association.options[:as] : reverse_association.klass == self
|
67
|
+
}
|
68
|
+
|
69
|
+
association.klass.class_eval do
|
70
|
+
after_commit "expire_#{association_name}_cache".to_sym
|
71
|
+
|
72
|
+
define_method "expire_#{association_name}_cache" do
|
73
|
+
if respond_to? "cached_#{reverse_association.name}".to_sym
|
74
|
+
send("cached_#{reverse_association.name}").expire_association_cache(association_name)
|
75
|
+
else
|
76
|
+
send("#{reverse_association.name}").expire_association_cache(association_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
define_method("cached_#{association_name}") do
|
83
|
+
Rails.cache.fetch have_association_cache_key(association_name) do
|
84
|
+
send(association_name).respond_to?(:to_a) ? send(association_name).to_a : send(association_name)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module AttributeCache
|
3
|
+
def with_attribute(*attributes)
|
4
|
+
self.cached_indices = attributes.inject({}) { |indices, attribute| indices[attribute] = {} }
|
5
|
+
|
6
|
+
class_eval do
|
7
|
+
after_commit :expire_attribute_cache, :on => :update
|
8
|
+
after_commit :expire_all_attribute_cache, :on => :update
|
9
|
+
end
|
10
|
+
|
11
|
+
attributes.each do |attribute|
|
12
|
+
define_singleton_method("find_cached_by_#{attribute}") do |value|
|
13
|
+
self.cached_indices["#{attribute}"] ||= []
|
14
|
+
self.cached_indices["#{attribute}"] << value
|
15
|
+
Rails.cache.fetch attribute_cache_key("#{attribute}", value) do
|
16
|
+
self.send("find_by_#{attribute}", value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
define_singleton_method("find_cached_all_by_#{attribute}") do |value|
|
21
|
+
self.cached_indices["#{attribute}"] ||= []
|
22
|
+
self.cached_indices["#{attribute}"] << value
|
23
|
+
Rails.cache.fetch all_attribute_cache_key("#{attribute}", value) do
|
24
|
+
self.send("find_all_by_#{attribute}", value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module ClassMethodCache
|
3
|
+
# Cached class method
|
4
|
+
# Should expire on any instance save
|
5
|
+
def with_class_method(*methods)
|
6
|
+
self.cached_class_methods = methods.inject({}) { |indices, meth| indices[meth] = {} }
|
7
|
+
|
8
|
+
class_eval do
|
9
|
+
after_commit :expire_class_method_cache, on: :update
|
10
|
+
end
|
11
|
+
|
12
|
+
methods.each do |meth|
|
13
|
+
define_singleton_method("cached_#{meth}") do |*args|
|
14
|
+
self.cached_class_methods["#{meth}"] ||= []
|
15
|
+
self.cached_class_methods["#{meth}"] << args
|
16
|
+
Rails.cache.fetch class_method_cache_key(meth, args) do
|
17
|
+
self.method(meth).arity == 0 ? send(meth) : send(meth, *args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module KeyCache
|
3
|
+
def with_key
|
4
|
+
self.cached_key = true
|
5
|
+
|
6
|
+
class_eval do
|
7
|
+
after_commit :expire_key_cache, on: :update
|
8
|
+
end
|
9
|
+
|
10
|
+
define_singleton_method("find_cached") do |id|
|
11
|
+
Rails.cache.fetch "#{name.tableize}/" + id.to_i.to_s do
|
12
|
+
self.find(id)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Cacheable
|
2
|
+
module MethodCache
|
3
|
+
def with_method(*methods)
|
4
|
+
self.cached_methods = methods
|
5
|
+
|
6
|
+
class_eval do
|
7
|
+
after_commit :expire_method_cache, :on => :update
|
8
|
+
end
|
9
|
+
|
10
|
+
methods.each do |meth|
|
11
|
+
define_method("cached_#{meth}") do
|
12
|
+
Rails.cache.fetch method_cache_key(meth) do
|
13
|
+
send(meth)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/cacheable/version.rb
CHANGED