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.
data/lib/cacheable.rb CHANGED
@@ -1,273 +1,24 @@
1
1
  require 'uri'
2
+ require "cacheable/caches"
3
+ require "cacheable/keys"
4
+ require "cacheable/expiry"
2
5
 
3
6
  module Cacheable
4
7
  def self.included(base)
5
- base.class_eval do
6
- class << self
7
- def model_cache(&block)
8
- class_attribute :cached_key,
9
- :cached_indices,
10
- :cached_methods,
11
- :cached_class_methods,
12
- :cached_associations
13
- instance_exec &block
14
- end
15
-
16
- def with_key
17
- self.cached_key = true
18
-
19
- class_eval <<-EOF
20
- after_commit :expire_key_cache, :on => :update
21
-
22
- def self.find_cached(id)
23
- Rails.cache.fetch "#{name.tableize}/" + id.to_i.to_s do
24
- self.find(id)
25
- end
26
- end
27
- EOF
28
- end
29
-
30
- def with_attribute(*attributes)
31
- self.cached_indices = attributes.inject({}) { |indices, attribute| indices[attribute] = {} }
32
-
33
- class_eval <<-EOF
34
- after_commit :expire_attribute_cache, :on => :update
35
- after_commit :expire_all_attribute_cache, :on => :update
36
- EOF
37
-
38
- attributes.each do |attribute|
39
- class_eval <<-EOF
40
- def self.find_cached_by_#{attribute}(value)
41
- self.cached_indices["#{attribute}"] ||= []
42
- self.cached_indices["#{attribute}"] << value
43
- Rails.cache.fetch attribute_cache_key("#{attribute}", value) do
44
- self.find_by_#{attribute}(value)
45
- end
46
- end
47
-
48
- def self.find_cached_all_by_#{attribute}(value)
49
- self.cached_indices["#{attribute}"] ||= []
50
- self.cached_indices["#{attribute}"] << value
51
- Rails.cache.fetch all_attribute_cache_key("#{attribute}", value) do
52
- self.find_all_by_#{attribute}(value)
53
- end
54
- end
55
- EOF
56
- end
57
- end
58
-
59
- def with_method(*methods)
60
- self.cached_methods = methods
61
-
62
- class_eval <<-EOF
63
- after_commit :expire_method_cache, :on => :update
64
- EOF
65
-
66
- methods.each do |meth|
67
- class_eval <<-EOF
68
- def cached_#{meth}
69
- Rails.cache.fetch method_cache_key("#{meth}") do
70
- #{meth}
71
- end
72
- end
73
- EOF
74
- end
75
- end
76
-
77
- # Cached class method
78
- # Should expire on any instance save
79
- def with_class_method(*methods)
80
- self.cached_class_methods = methods
81
-
82
- class_eval <<-EOF
83
- after_commit :expire_class_method_cache, on: :update
84
- EOF
85
-
86
- methods.each do |meth|
87
- class_eval <<-EOF
88
- def self.cached_#{meth}
89
- Rails.cache.fetch class_method_cache_key("#{meth}") do
90
- #{meth}
91
- end
92
- end
93
- EOF
94
- end
95
-
96
- end
97
-
98
- def with_association(*association_names)
99
- self.cached_associations = association_names
100
-
101
- association_names.each do |association_name|
102
- association = reflect_on_association(association_name)
103
-
104
- if :belongs_to == association.macro
105
- polymorphic = association.options[:polymorphic]
106
- polymorphic ||= false
107
- class_eval <<-EOF
108
- def cached_#{association_name}
109
- Rails.cache.fetch belong_association_cache_key("#{association_name}", #{polymorphic}) do
110
- #{association_name}
111
- end
112
- end
113
- EOF
114
- else
115
- if through_reflection_name = association.options[:through]
116
- through_association = self.reflect_on_association(through_reflection_name)
117
-
118
- # FIXME it should be the only reflection but I'm not 100% positive
119
- reverse_through_association = through_association.klass.reflect_on_all_associations(:belongs_to).first
120
-
121
- # In a through association it doesn't have to be a belongs_to
122
- reverse_association = association.klass.reflect_on_all_associations(:belongs_to).find { |reverse_association|
123
- reverse_association.options[:polymorphic] ? reverse_association.name == association.source_reflection.options[:as] : reverse_association.klass == self
124
- }
125
-
126
- if reverse_association
127
- association.klass.class_eval <<-EOF
128
- after_commit :expire_#{association_name}_cache
129
-
130
- def expire_#{association_name}_cache
131
- if respond_to? :cached_#{reverse_association.name}
132
- # cached_viewable.expire_association_cache
133
- cached_#{reverse_association.name}.expire_association_cache(:#{association_name})
134
- else
135
- #{reverse_association.name}.#{reverse_through_association.name}.expire_association_cache(:#{association_name})
136
- end
137
- end
138
- EOF
139
- end
140
- elsif :has_and_belongs_to_many == association.macro
141
- # No such thing as a polymorphic has_and_belongs_to_many
142
- reverse_association = association.klass.reflect_on_all_associations(:has_and_belongs_to_many).find { |reverse_association|
143
- reverse_association.klass == self
144
- }
145
-
146
- association.klass.class_eval <<-EOF
147
- after_commit :expire_#{association_name}_cache
148
-
149
- def expire_#{association_name}_cache
150
- if respond_to? :cached_#{reverse_association.name}
151
- # cached_viewable.expire_association_cache
152
- cached_#{reverse_association.name}.expire_association_cache(:#{association_name})
153
- else
154
- #{reverse_association.name}.each do |assoc|
155
- assoc.expire_association_cache(:#{association_name})
156
- end
157
- end
158
- end
159
- EOF
160
- else
161
- reverse_association = association.klass.reflect_on_all_associations(:belongs_to).find { |reverse_association|
162
- reverse_association.options[:polymorphic] ? reverse_association.name == association.options[:as] : reverse_association.klass == self
163
- }
164
-
165
- association.klass.class_eval <<-EOF
166
- after_commit :expire_#{association_name}_cache
167
-
168
- def expire_#{association_name}_cache
169
- if respond_to? :cached_#{reverse_association.name}
170
- cached_#{reverse_association.name}.expire_association_cache(:#{association_name})
171
- else
172
- #{reverse_association.name}.expire_association_cache(:#{association_name})
173
- end
174
- end
175
- EOF
176
- end
177
- class_eval <<-EOF
178
- def cached_#{association_name}
179
- Rails.cache.fetch have_association_cache_key("#{association_name}") do
180
- #{association_name}.respond_to?(:all) ? #{association_name}.all : #{association_name}
181
- end
182
- end
183
- EOF
184
- end
185
- end
186
- end
187
-
188
- def attribute_cache_key(attribute, value)
189
- "#{name.tableize}/attribute/#{attribute}/#{URI.escape(value.to_s)}"
190
- end
191
-
192
- def all_attribute_cache_key(attribute, value)
193
- "#{name.tableize}/attribute/#{attribute}/all/#{URI.escape(value.to_s)}"
194
- end
195
-
196
- def class_method_cache_key(meth)
197
- "#{name.tableize}/class_method/#{meth}"
198
- end
199
-
200
- end
8
+ base.extend(Cacheable::Caches)
9
+ base.send :include, Cacheable::Keys
10
+ base.send :include, Cacheable::Expiry
201
11
 
202
- end
203
-
204
- end
205
-
206
- def expire_model_cache
207
- expire_key_cache if self.class.cached_key
208
- expire_attribute_cache if self.class.cached_indices.present?
209
- expire_all_attribute_cache if self.class.cached_indices.present?
210
- expire_method_cache if self.class.cached_methods.present?
211
- expire_class_method_cache if self.class.cached_class_methods.present?
212
-
213
- if self.class.cached_associations.present?
214
- self.class.cached_associations.each do |assoc|
215
- expire_association_cache(assoc)
12
+ base.class_eval do
13
+ def self.model_cache(&block)
14
+ class_attribute :cached_key,
15
+ :cached_indices,
16
+ :cached_methods,
17
+ :cached_class_methods,
18
+ :cached_associations
19
+ instance_exec &block
216
20
  end
217
21
  end
218
22
  end
219
23
 
220
- def expire_key_cache
221
- Rails.cache.delete model_cache_key
222
- end
223
-
224
- def expire_attribute_cache
225
- self.class.cached_indices.each do |attribute, values|
226
- value = self.send(attribute)
227
- Rails.cache.delete self.class.attribute_cache_key(attribute, value)
228
- end
229
- end
230
-
231
- def expire_all_attribute_cache
232
- self.class.cached_indices.each do |attribute, values|
233
- value = self.send(attribute)
234
- Rails.cache.delete self.class.all_attribute_cache_key(attribute, value)
235
- end
236
- end
237
-
238
- def expire_method_cache
239
- self.class.cached_methods.each do |meth|
240
- Rails.cache.delete method_cache_key(meth)
241
- end
242
- end
243
-
244
- def expire_class_method_cache
245
- self.class.cached_class_methods.each do |meth|
246
- Rails.cache.delete self.class.class_method_cache_key(meth)
247
- end
248
- end
249
-
250
- def expire_association_cache(name)
251
- Rails.cache.delete have_association_cache_key(name)
252
- end
253
-
254
- def model_cache_key
255
- "#{self.class.name.tableize}/#{self.id.to_i}"
256
- end
257
-
258
- def method_cache_key(meth)
259
- "#{model_cache_key}/method/#{meth}"
260
- end
261
-
262
- def belong_association_cache_key(name, polymorphic=nil)
263
- if polymorphic
264
- "#{self.send("#{name}_type").tableize}/#{self.send("#{name}_id")}"
265
- else
266
- "#{name.tableize}/#{self.send(name + "_id")}"
267
- end
268
- end
269
-
270
- def have_association_cache_key(name)
271
- "#{model_cache_key}/association/#{name}"
272
- end
273
24
  end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cacheable do
4
+ let(:cache) { Rails.cache }
5
+ let(:user) { User.create(:login => 'flyerhzm') }
6
+
7
+ before :all do
8
+ @post1 = user.posts.create(:title => 'post1')
9
+ user2 = User.create(:login => 'PelegR')
10
+ user2.posts.create(:title => 'post3')
11
+ end
12
+
13
+ before :each do
14
+ cache.clear
15
+ user.reload
16
+ end
17
+
18
+ context "expire_model_cache" do
19
+ it "should delete with_key cache" do
20
+ User.find_cached(user.id)
21
+ Rails.cache.read("users/#{user.id}").should_not be_nil
22
+ user.expire_model_cache
23
+ Rails.cache.read("users/#{user.id}").should be_nil
24
+ end
25
+
26
+ it "should delete with_attribute cache" do
27
+ user = User.find_cached_by_login("flyerhzm")
28
+ Rails.cache.read("users/attribute/login/flyerhzm").should == user
29
+ user.expire_model_cache
30
+ Rails.cache.read("users/attribute/login/flyerhzm").should be_nil
31
+ end
32
+
33
+ it "should delete with_method cache" do
34
+ user.cached_last_post
35
+ Rails.cache.read("users/#{user.id}/method/last_post").should_not be_nil
36
+ user.expire_model_cache
37
+ Rails.cache.read("users/#{user.id}/method/last_post").should be_nil
38
+ end
39
+
40
+ it "should delete with_class_method cache (default_post)" do
41
+ Post.cached_default_post
42
+ Rails.cache.read("posts/class_method/default_post").should_not be_nil
43
+ @post1.expire_model_cache
44
+ Rails.cache.read("posts/class_method/default_post").should be_nil
45
+ end
46
+
47
+ it "should delete with_class_method cache (retrieve_with_user_id)" do
48
+ Post.cached_retrieve_with_user_id(1)
49
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/1").should_not be_nil
50
+ @post1.expire_model_cache
51
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/1").should be_nil
52
+ end
53
+
54
+ it "should delete with_class_method cache (retrieve_with_user_id) with different arguments" do
55
+ Post.cached_retrieve_with_user_id(1)
56
+ Post.cached_retrieve_with_user_id(2)
57
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/1").should_not be_nil
58
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/2").should_not be_nil
59
+ @post1.expire_model_cache
60
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/1").should be_nil
61
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/2").should be_nil
62
+ end
63
+
64
+ it "should delete with_class_method cache (retrieve_with_both)" do
65
+ Post.cached_retrieve_with_both(1, 1)
66
+ Rails.cache.read("posts/class_method/retrieve_with_both/1+1").should_not be_nil
67
+ @post1.expire_model_cache
68
+ Rails.cache.read("posts/class_method/retrieve_with_both/1+1").should be_nil
69
+ end
70
+
71
+ # TODO: should we cache empty arrays?
72
+ it "should delete associations cache" do
73
+ user.cached_images
74
+ Rails.cache.read("users/#{user.id}/association/images").should_not be_nil
75
+ user.expire_model_cache
76
+ Rails.cache.read("users/#{user.id}/association/images").should be_nil
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cacheable do
4
+ let(:cache) { Rails.cache }
5
+ let(:user) { User.create(:login => 'flyerhzm') }
6
+
7
+ before :all do
8
+ @post1 = user.posts.create(:title => 'post1')
9
+ @post2 = user.posts.create(:title => 'post2')
10
+ @image1 = @post1.images.create
11
+ @image2 = @post1.images.create
12
+ @comment1 = @post1.comments.create
13
+ @comment2 = @post1.comments.create
14
+ @tag1 = @post1.tags.create(title: "Rails")
15
+ @tag2 = @post1.tags.create(title: "Caching")
16
+ @group1 = Group.create(name: "Ruby On Rails")
17
+ @account = user.create_account(group: @group1)
18
+ end
19
+
20
+ before :each do
21
+ cache.clear
22
+ user.reload
23
+ end
24
+
25
+ context "with_association" do
26
+ context "belongs_to" do
27
+ it "should not cache association" do
28
+ Rails.cache.read("users/#{user.id}").should be_nil
29
+ end
30
+
31
+ it "should cache Post#user" do
32
+ @post1.cached_user.should == user
33
+ Rails.cache.read("users/#{user.id}").should == user
34
+ end
35
+
36
+ it "should cache Post#user multiple times" do
37
+ @post1.cached_user
38
+ @post1.cached_user.should == user
39
+ end
40
+
41
+ it "should cache Comment#commentable with polymorphic" do
42
+ Rails.cache.read("posts/#{@post1.id}").should be_nil
43
+ @comment1.cached_commentable.should == @post1
44
+ Rails.cache.read("posts/#{@post1.id}").should == @post1
45
+ end
46
+ end
47
+
48
+ context "has_many" do
49
+ it "should not cache associations" do
50
+ Rails.cache.read("users/#{user.id}/association/posts").should be_nil
51
+ end
52
+
53
+ it "should cache User#posts" do
54
+ user.cached_posts.should == [@post1, @post2]
55
+ Rails.cache.read("users/#{user.id}/association/posts").should == [@post1, @post2]
56
+ end
57
+
58
+ it "should cache User#posts multiple times" do
59
+ user.cached_posts
60
+ user.cached_posts.should == [@post1, @post2]
61
+ end
62
+ end
63
+
64
+ context "has_many with polymorphic" do
65
+ it "should not cache associations" do
66
+ Rails.cache.read("posts/#{@post1.id}/association/comments").should be_nil
67
+ end
68
+
69
+ it "should cache Post#comments" do
70
+ @post1.cached_comments.should == [@comment1, @comment2]
71
+ Rails.cache.read("posts/#{@post1.id}/association/comments").should == [@comment1, @comment2]
72
+ end
73
+
74
+ it "should cache Post#comments multiple times" do
75
+ @post1.cached_comments
76
+ @post1.cached_comments.should == [@comment1, @comment2]
77
+ end
78
+ end
79
+
80
+ context "has_one" do
81
+ it "should not cache associations" do
82
+ Rails.cache.read("users/#{user.id}/association/account").should be_nil
83
+ end
84
+
85
+ it "should cache User#posts" do
86
+ user.cached_account.should == @account
87
+ Rails.cache.read("users/#{user.id}/association/account").should == @account
88
+ end
89
+
90
+ it "should cache User#posts multiple times" do
91
+ user.cached_account
92
+ user.cached_account.should == @account
93
+ end
94
+ end
95
+
96
+ context "has_many through" do
97
+ it "should not cache associations" do
98
+ Rails.cache.read("users/#{user.id}/association/images").should be_nil
99
+ end
100
+
101
+ it "should cache User#images" do
102
+ user.cached_images.should == [@image1, @image2]
103
+ Rails.cache.read("users/#{user.id}/association/images").should == [@image1, @image2]
104
+ end
105
+
106
+ it "should cache User#images multiple times" do
107
+ user.cached_images
108
+ user.cached_images.should == [@image1, @image2]
109
+ end
110
+
111
+ context "expiry" do
112
+ it "should have the correct collection" do
113
+ @image3 = @post1.images.create
114
+ Rails.cache.read("users/#{user.id}/association/images").should be_nil
115
+ user.cached_images.should == [@image1, @image2, @image3]
116
+ Rails.cache.read("users/#{user.id}/association/images").should == [@image1, @image2, @image3]
117
+ end
118
+ end
119
+ end
120
+
121
+ context "has_one through belongs_to" do
122
+ it "should not cache associations" do
123
+ Rails.cache.read("users/#{user.id}/association/group").should be_nil
124
+ end
125
+
126
+ it "should cache User#group" do
127
+ user.cached_group.should == @group1
128
+ Rails.cache.read("users/#{user.id}/association/group").should == @group1
129
+ end
130
+
131
+ it "should cache User#group multiple times" do
132
+ user.cached_group
133
+ user.cached_group.should == @group1
134
+ end
135
+
136
+ end
137
+
138
+ context "has_and_belongs_to_many" do
139
+
140
+ it "should not cache associations off the bat" do
141
+ Rails.cache.read("posts/#{@post1.id}/association/tags").should be_nil
142
+ end
143
+
144
+ it "should cache Post#tags" do
145
+ @post1.cached_tags.should == [@tag1, @tag2]
146
+ Rails.cache.read("posts/#{@post1.id}/association/tags").should == [@tag1, @tag2]
147
+ end
148
+
149
+ it "should handle multiple requests" do
150
+ @post1.cached_tags
151
+ @post1.cached_tags.should == [@tag1, @tag2]
152
+ end
153
+
154
+ context "expiry" do
155
+ it "should have the correct collection" do
156
+ @tag3 = @post1.tags.create!(title: "Invalidation is hard")
157
+ Rails.cache.read("posts/#{@post1.id}/association/tags").should be_nil
158
+ @post1.cached_tags.should == [@tag1, @tag2, @tag3]
159
+ Rails.cache.read("posts/#{@post1.id}/association/tags").should == [@tag1, @tag2, @tag3]
160
+ end
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cacheable do
4
+ let(:cache) { Rails.cache }
5
+ let(:user) { User.create(:login => 'flyerhzm') }
6
+
7
+ before :all do
8
+ @post1 = user.posts.create(:title => 'post1')
9
+ @post2 = user.posts.create(:title => 'post2')
10
+ end
11
+
12
+ before :each do
13
+ cache.clear
14
+ user.reload
15
+ end
16
+
17
+ context "with_attribute" do
18
+ it "should not cache User.find_by_login" do
19
+ Rails.cache.read("users/attribute/login/flyerhzm").should be_nil
20
+ end
21
+
22
+ it "should cache by User.find_by_login" do
23
+ User.find_cached_by_login("flyerhzm").should == user
24
+ Rails.cache.read("users/attribute/login/flyerhzm").should == user
25
+ end
26
+
27
+ it "should get cached by User.find_by_login multiple times" do
28
+ User.find_cached_by_login("flyerhzm")
29
+ User.find_cached_by_login("flyerhzm").should == user
30
+ end
31
+
32
+ it "should escape whitespace" do
33
+ new_user = User.create(:login => "user space")
34
+ User.find_cached_by_login("user space").should == new_user
35
+ end
36
+
37
+ it "should handle fixed numbers" do
38
+ Post.find_cached_by_user_id(user.id).should == @post1
39
+ Rails.cache.read("posts/attribute/user_id/#{user.id}").should == @post1
40
+ end
41
+
42
+ context "find_all" do
43
+ it "should not cache Post.find_all_by_user_id" do
44
+ Rails.cache.read("posts/attribute/user_id/all/#{user.id}").should be_nil
45
+ end
46
+
47
+ it "should cache by Post.find_cached_all_by_user_id" do
48
+ Post.find_cached_all_by_user_id(user.id).should == [@post1, @post2]
49
+ Rails.cache.read("posts/attribute/user_id/all/#{user.id}").should == [@post1, @post2]
50
+ end
51
+
52
+ it "should get cached by Post.find_cached_all_by_user_id multiple times" do
53
+ Post.find_cached_all_by_user_id(user.id)
54
+ Post.find_cached_all_by_user_id(user.id).should == [@post1, @post2]
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cacheable do
4
+ let(:cache) { Rails.cache }
5
+ let(:user) { User.create(:login => 'flyerhzm') }
6
+
7
+ before :all do
8
+ @post1 = user.posts.create(:title => 'post1')
9
+ @post2 = user.posts.create(:title => 'post2')
10
+ end
11
+
12
+ before :each do
13
+ cache.clear
14
+ user.reload
15
+ end
16
+
17
+ context "with_class_method" do
18
+ it "should not cache Post.default_post" do
19
+ Rails.cache.read("posts/class_method/default_post").should be_nil
20
+ end
21
+
22
+ it "should cache Post.default_post" do
23
+ Post.cached_default_post.should == @post1
24
+ Rails.cache.read("posts/class_method/default_post").should == @post1
25
+ end
26
+
27
+ it "should cache Post.default_post multiple times" do
28
+ Post.cached_default_post
29
+ Post.cached_default_post.should == @post1
30
+ end
31
+
32
+ it "should cache Post.retrieve_with_user_id" do
33
+ Post.cached_retrieve_with_user_id(1).should == @post1
34
+ Rails.cache.read("posts/class_method/retrieve_with_user_id/1").should == @post1
35
+ end
36
+
37
+ it "should cache Post.retrieve_with_both with multiple arguments" do
38
+ Post.cached_retrieve_with_both(1, 1).should be_true
39
+ Rails.cache.read("posts/class_method/retrieve_with_both/1+1").should be_true
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cacheable do
4
+ let(:cache) { Rails.cache }
5
+ let(:user) { User.create(:login => 'flyerhzm') }
6
+
7
+ before :each do
8
+ cache.clear
9
+ user.reload
10
+ end
11
+
12
+ context "with_key" do
13
+ it "should not cache key" do
14
+ Rails.cache.read("users/#{user.id}").should be_nil
15
+ end
16
+
17
+ it "should cache by User#id" do
18
+ User.find_cached(user.id).should == user
19
+ Rails.cache.read("users/#{user.id}").should == user
20
+ end
21
+
22
+ it "should get cached by User#id multiple times" do
23
+ User.find_cached(user.id)
24
+ User.find_cached(user.id).should == user
25
+ end
26
+ end
27
+
28
+ end