simple_cacheable 1.3.3 → 1.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b46d1110354d90d106fe5abdfed1eaa6801dd7e4
4
- data.tar.gz: 763dac9a0c34135095974d09bb2575fbc5106ed2
3
+ metadata.gz: 84641f34cae9d9da455523484d0a802851c938ed
4
+ data.tar.gz: 951f43891133aa8e58d6235d686cc2427b407dcb
5
5
  SHA512:
6
- metadata.gz: f0fd40d41856c6efb7ecdfa1d8263ab2178b98c4cc8656d5bde2f3e67d71029c8cdebacf72a9d127abd775867d1d452fe86e8b122ec8f8adb315b9fab444999b
7
- data.tar.gz: 322aad0ed21db8769cc06557df6461f8e8a29ddad18597922a4410480dc71a3d639217ea07a88b3c861db899802b4cf05c615242dc5069a9aab55325bfb0983a
6
+ metadata.gz: 99d9989b5970e995a3212583ae721802d04e1acb6e5ca4384213083926e03235f97f663027c599cdc4bbaba28ba6d0fc832813e18df2e7439e6994ead01f2985
7
+ data.tar.gz: 8dd71f349161ed02e8f63482e9a0e9dd88f98c12cdbbd7e2555fe3a217a7099c29ff5ec414dd79f98e2835a88878503a75f5cc32640129d25d691a79e5d708c8
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+ - 2.0.0
4
5
  env: DB=sqlite
5
6
  service:
6
7
  - memcached
data/ChangeLog CHANGED
@@ -1,3 +1,9 @@
1
+ 1.4.0
2
+ * Fixed after_commit silent failure for assocations that didn't exist
3
+ * Fixed association for has_many through when there are several belongs_to relationships
4
+ * Fixed multiple calls to model_cache
5
+ * Fixed STI/inheritance use cases
6
+
1
7
  1.3.3
2
8
  * File Reorganization
3
9
  * Rails 4 Compatibility
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple_cacheable (1.3.2)
4
+ simple_cacheable (1.3.3)
5
5
  rails (>= 3.0.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -4,7 +4,7 @@ cacheable
4
4
  cacheable is a simple cache implementation based on activerecord, it is
5
5
  extracted from [rails-bestpractices.com][1].
6
6
 
7
- it supports activerecord >= 3.0.0, works on 1.9.2, 1.9.3 and jruby.
7
+ it supports activerecord >= 3.0.0, tested on 1.9.3 and 2.0.0 and works with jruby.
8
8
 
9
9
  Introduction
10
10
  ------------
@@ -68,7 +68,7 @@ Install
68
68
 
69
69
  add the following code to your Gemfile
70
70
 
71
- gem "simple_cacheable", :require => "cacheable"
71
+ gem "simple_cacheable"
72
72
 
73
73
 
74
74
  Gotchas
@@ -2,7 +2,8 @@ module Cacheable
2
2
  module AssocationCache
3
3
 
4
4
  def with_association(*association_names)
5
- self.cached_associations = association_names
5
+ self.cached_associations ||= []
6
+ self.cached_associations += association_names
6
7
 
7
8
  association_names.each do |association_name|
8
9
  association = reflect_on_association(association_name)
@@ -20,8 +21,9 @@ module Cacheable
20
21
  if through_reflection_name = association.options[:through]
21
22
  through_association = self.reflect_on_association(through_reflection_name)
22
23
 
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
24
+ reverse_through_association = through_association.klass.reflect_on_all_associations(:belongs_to).detect do |assoc|
25
+ assoc.klass.ancestors.include?(Cacheable) && assoc.klass.reflect_on_association(association.name)
26
+ end
25
27
 
26
28
  # In a through association it doesn't have to be a belongs_to
27
29
  reverse_association = association.klass.reflect_on_all_associations(:belongs_to).find { |reverse_association|
@@ -33,10 +35,11 @@ module Cacheable
33
35
 
34
36
  define_method("expire_#{association_name}_cache") do
35
37
  if respond_to? "expire_#{reverse_association.name}_cache".to_sym
36
- # cached_viewable.expire_association_cache
37
38
  send("cached_#{reverse_association.name}").expire_association_cache(association_name)
38
39
  else
39
- send(reverse_association.name).send(reverse_through_association.name).expire_association_cache(association_name)
40
+ if send(reverse_association.name) && send(reverse_association.name).respond_to?(reverse_through_association.name)
41
+ send(reverse_association.name).send(reverse_through_association.name).expire_association_cache(association_name)
42
+ end
40
43
  end
41
44
  end
42
45
  end
@@ -1,7 +1,10 @@
1
1
  module Cacheable
2
2
  module AttributeCache
3
3
  def with_attribute(*attributes)
4
- self.cached_indices = attributes.inject({}) { |indices, attribute| indices[attribute] = {} }
4
+ self.cached_indices ||= {}
5
+ self.cached_indices = self.cached_indices.merge(attributes.each_with_object({}) {
6
+ |attribute, indices| indices[attribute] = {}
7
+ })
5
8
 
6
9
  class_eval do
7
10
  after_commit :expire_attribute_cache, :on => :update
@@ -3,7 +3,7 @@ module Cacheable
3
3
  # Cached class method
4
4
  # Should expire on any instance save
5
5
  def with_class_method(*methods)
6
- self.cached_class_methods = methods.inject({}) { |indices, meth| indices[meth] = {} }
6
+ self.cached_class_methods = methods.each_with_object({}) { |meth, indices| indices[meth] = {} }
7
7
 
8
8
  class_eval do
9
9
  after_commit :expire_class_method_cache, on: :update
@@ -1,7 +1,8 @@
1
1
  module Cacheable
2
2
  module MethodCache
3
3
  def with_method(*methods)
4
- self.cached_methods = methods
4
+ self.cached_methods ||= []
5
+ self.cached_methods += methods
5
6
 
6
7
  class_eval do
7
8
  after_commit :expire_method_cache, :on => :update
@@ -1,3 +1,3 @@
1
1
  module Cacheable
2
- VERSION = "1.3.3"
2
+ VERSION = "1.4.0"
3
3
  end
data/lib/cacheable.rb CHANGED
@@ -8,16 +8,20 @@ module Cacheable
8
8
  base.extend(Cacheable::Caches)
9
9
  base.send :include, Cacheable::Keys
10
10
  base.send :include, Cacheable::Expiry
11
-
11
+ base.send :extend, ClassMethods
12
12
  base.class_eval do
13
- def self.model_cache(&block)
14
- class_attribute :cached_key,
13
+ class_attribute :cached_key,
15
14
  :cached_indices,
16
15
  :cached_methods,
17
16
  :cached_class_methods,
18
17
  :cached_associations
19
- instance_exec &block
20
- end
18
+ end
19
+
20
+ end
21
+
22
+ module ClassMethods
23
+ def model_cache(&block)
24
+ instance_exec &block
21
25
  end
22
26
  end
23
27
 
@@ -3,11 +3,13 @@ require 'spec_helper'
3
3
  describe Cacheable do
4
4
  let(:cache) { Rails.cache }
5
5
  let(:user) { User.create(:login => 'flyerhzm') }
6
+ let(:descendant) { Descendant.create(:login => "scotterc")}
6
7
 
7
8
  before :all do
8
9
  @post1 = user.posts.create(:title => 'post1')
9
10
  user2 = User.create(:login => 'PelegR')
10
11
  user2.posts.create(:title => 'post3')
12
+ @post3 = descendant.posts.create(:title => 'post3')
11
13
  end
12
14
 
13
15
  before :each do
@@ -68,7 +70,6 @@ describe Cacheable do
68
70
  Rails.cache.read("posts/class_method/retrieve_with_both/1+1").should be_nil
69
71
  end
70
72
 
71
- # TODO: should we cache empty arrays?
72
73
  it "should delete associations cache" do
73
74
  user.cached_images
74
75
  Rails.cache.read("users/#{user.id}/association/images").should_not be_nil
@@ -78,4 +79,89 @@ describe Cacheable do
78
79
 
79
80
  end
80
81
 
82
+ context "single table inheritance bug" do
83
+ context "user" do
84
+ it "has cached indices" do
85
+ User.cached_indices.should_not be_nil
86
+ end
87
+
88
+ it "has specific cached indices" do
89
+ User.cached_indices.keys.should include :login
90
+ User.cached_indices.keys.should_not include :email
91
+ end
92
+
93
+ it "should have cached_methods" do
94
+ User.cached_methods.should_not be_nil
95
+ User.cached_methods.should == [:last_post]
96
+ end
97
+ end
98
+
99
+ context "expiring class_method cache" do
100
+ it "expires correctly from inherited attributes" do
101
+ Rails.cache.read("users/class_method/default_name").should be_nil
102
+ User.cached_default_name
103
+ Rails.cache.read("users/class_method/default_name").should == "flyerhzm"
104
+ user.expire_model_cache
105
+ Rails.cache.read("users/class_method/default_name").should be_nil
106
+ end
107
+ end
108
+
109
+ context "descendant" do
110
+
111
+ it "should have cached indices hash" do
112
+ Descendant.cached_indices.should_not be_nil
113
+ end
114
+
115
+ it "has specific cached indices" do
116
+ Descendant.cached_indices.keys.should include :login
117
+ Descendant.cached_indices.keys.should include :email
118
+ end
119
+
120
+ it "should have cached_methods" do
121
+ Descendant.cached_methods.should_not be_nil
122
+ Descendant.cached_methods.should == [:last_post, :name]
123
+ end
124
+
125
+ context "expiring method cache" do
126
+ it "expires correctly from inherited attributes" do
127
+ Rails.cache.read("descendants/#{descendant.id}/method/last_post").should be_nil
128
+ descendant.cached_last_post.should == descendant.last_post
129
+ Rails.cache.read("descendants/#{descendant.id}/method/last_post").should == descendant.last_post
130
+ descendant.expire_model_cache
131
+ Rails.cache.read("descendants/#{descendant.id}/method/last_post").should be_nil
132
+ end
133
+ end
134
+
135
+ context "expiring attribute cache" do
136
+ it "expires correctly from inherited attributes" do
137
+ Rails.cache.read("descendants/attribute/login/scotterc").should be_nil
138
+ Descendant.find_cached_by_login("scotterc").should == descendant
139
+ Rails.cache.read("descendants/attribute/login/scotterc").should == descendant
140
+ descendant.expire_model_cache
141
+ Rails.cache.read("descendants/attribute/login/scotterc").should be_nil
142
+ end
143
+ end
144
+
145
+ context "expiring association cache" do
146
+ it "expires correctly from inherited attributes" do
147
+ Rails.cache.read("descendants/#{descendant.id}/association/posts").should be_nil
148
+ descendant.cached_posts.should == [@post3]
149
+ Rails.cache.read("descendants/#{descendant.id}/association/posts").should == [@post3]
150
+ descendant.expire_model_cache
151
+ Rails.cache.read("descendants/#{descendant.id}/association/posts").should be_nil
152
+ end
153
+ end
154
+
155
+ context "expiring class_method cache" do
156
+ it "expires correctly from inherited attributes" do
157
+ Rails.cache.read("descendants/class_method/default_name").should be_nil
158
+ Descendant.cached_default_name
159
+ Rails.cache.read("descendants/class_method/default_name").should == "ScotterC"
160
+ descendant.expire_model_cache
161
+ Rails.cache.read("descendants/class_method/default_name").should be_nil
162
+ end
163
+ end
164
+ end
165
+ end
166
+
81
167
  end
@@ -15,6 +15,7 @@ describe Cacheable do
15
15
  @tag2 = @post1.tags.create(title: "Caching")
16
16
  @group1 = Group.create(name: "Ruby On Rails")
17
17
  @account = user.create_account(group: @group1)
18
+ @location = @post1.create_location(city: "New York")
18
19
  end
19
20
 
20
21
  before :each do
@@ -163,4 +164,32 @@ describe Cacheable do
163
164
 
164
165
  end
165
166
 
167
+ describe "after_commit bug" do
168
+ it "normal" do
169
+ @image1.expects(:do_something).once
170
+ @image1.save
171
+ end
172
+
173
+ it "new image fails without association" do
174
+ image = Image.new
175
+ image.expects(:do_something).once
176
+ image.save
177
+ end
178
+
179
+ it "new image fails with missing association" do
180
+ image = @group1.images.new
181
+ image.expects(:do_something).once
182
+ image.save
183
+ end
184
+ end
185
+
186
+ describe "belongs_to bug" do
187
+
188
+ it "shouldn't hit location" do
189
+ @location.expects(:expire_association_cache).with(:images).never
190
+ user.save
191
+ end
192
+
193
+ end
194
+
166
195
  end
@@ -3,10 +3,12 @@ require 'spec_helper'
3
3
  describe Cacheable do
4
4
  let(:cache) { Rails.cache }
5
5
  let(:user) { User.create(:login => 'flyerhzm') }
6
+ let(:descendant) { Descendant.create(:login => "scotterc")}
6
7
 
7
8
  before :all do
8
9
  @post1 = user.posts.create(:title => 'post1')
9
10
  @post2 = user.posts.create(:title => 'post2')
11
+ @post3 = descendant.posts.create(:title => 'post3')
10
12
  end
11
13
 
12
14
  before :each do
@@ -57,4 +59,31 @@ describe Cacheable do
57
59
  end
58
60
  end
59
61
 
62
+ context "descendant" do
63
+ it "should not cache Descendant.find_by_login" do
64
+ Rails.cache.read("descendants/attribute/login/scotterc").should be_nil
65
+ end
66
+
67
+ it "should cache by Descendant.find_by_login" do
68
+ Descendant.find_cached_by_login("scotterc").should == descendant
69
+ Rails.cache.read("descendants/attribute/login/scotterc").should == descendant
70
+ end
71
+
72
+ it "should get cached by Descendant.find_by_login multiple times" do
73
+ Descendant.find_cached_by_login("scotterc")
74
+ Descendant.find_cached_by_login("scotterc").should == descendant
75
+ end
76
+
77
+ it "should escape whitespace" do
78
+ new_descendant = Descendant.create(:login => "descendant space")
79
+ Descendant.find_cached_by_login("descendant space").should == new_descendant
80
+ end
81
+
82
+ it "maintains cached methods" do
83
+ Rails.cache.read("descendants/#{descendant.id}/method/name").should be_nil
84
+ descendant.cached_name.should == descendant.name
85
+ Rails.cache.read("descendants/#{descendant.id}/method/name").should == descendant.name
86
+ end
87
+ end
88
+
60
89
  end
@@ -3,10 +3,12 @@ require 'spec_helper'
3
3
  describe Cacheable do
4
4
  let(:cache) { Rails.cache }
5
5
  let(:user) { User.create(:login => 'flyerhzm') }
6
+ let(:descendant) { Descendant.create(:login => "scotterc")}
6
7
 
7
8
  before :all do
8
9
  @post1 = user.posts.create(:title => 'post1')
9
10
  @post2 = user.posts.create(:title => 'post2')
11
+ @post3 = descendant.posts.create(:title => 'post3')
10
12
  end
11
13
 
12
14
  before :each do
@@ -28,6 +30,38 @@ describe Cacheable do
28
30
  user.cached_last_post
29
31
  user.cached_last_post.should == user.last_post
30
32
  end
33
+
34
+ context "descendant should inherit methods" do
35
+ it "should not cache Descendant.last_post" do
36
+ Rails.cache.read("descendants/#{descendant.id}/method/last_post").should be_nil
37
+ end
38
+
39
+ it "should cache Descendant#last_post" do
40
+ descendant.cached_last_post.should == descendant.last_post
41
+ Rails.cache.read("descendants/#{descendant.id}/method/last_post").should == descendant.last_post
42
+ end
43
+
44
+ it "should cache Descendant#last_post multiple times" do
45
+ descendant.cached_last_post
46
+ descendant.cached_last_post.should == descendant.last_post
47
+ end
48
+
49
+ context "as well as new methods" do
50
+ it "should not cache Descendant.name" do
51
+ Rails.cache.read("descendants/#{descendant.id}/method/name").should be_nil
52
+ end
53
+
54
+ it "should cache Descendant#name" do
55
+ descendant.cached_name.should == descendant.name
56
+ Rails.cache.read("descendants/#{descendant.id}/method/name").should == descendant.name
57
+ end
58
+
59
+ it "should cache Descendant#name multiple times" do
60
+ descendant.cached_name
61
+ descendant.cached_name.should == descendant.name
62
+ end
63
+ end
64
+ end
31
65
  end
32
66
 
33
67
  end
@@ -0,0 +1,20 @@
1
+ class Descendant < User
2
+
3
+ belongs_to :location
4
+
5
+ model_cache do
6
+ with_attribute :email
7
+ with_method :name
8
+ with_association :location
9
+ with_class_method :default_name
10
+ end
11
+
12
+ def name
13
+ "ScotterC"
14
+ end
15
+
16
+ def self.default_name
17
+ "ScotterC"
18
+ end
19
+
20
+ end
data/spec/models/group.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  class Group < ActiveRecord::Base
2
2
 
3
3
  has_many :accounts
4
+
5
+ has_many :images, as: :viewable
4
6
  end
data/spec/models/image.rb CHANGED
@@ -2,4 +2,9 @@ class Image < ActiveRecord::Base
2
2
 
3
3
  belongs_to :viewable, :polymorphic => true
4
4
 
5
+ after_commit :do_something
6
+
7
+ def do_something
8
+ e = 1337
9
+ end
5
10
  end
@@ -0,0 +1,3 @@
1
+ class Location < ActiveRecord::Base
2
+
3
+ end
data/spec/models/post.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  class Post < ActiveRecord::Base
2
2
  include Cacheable
3
3
 
4
+ belongs_to :location
4
5
  belongs_to :user
5
6
 
6
7
  has_many :comments, :as => :commentable
data/spec/models/user.rb CHANGED
@@ -12,9 +12,15 @@ class User < ActiveRecord::Base
12
12
  with_attribute :login
13
13
  with_method :last_post
14
14
  with_association :posts, :account, :images, :group
15
+ with_class_method :default_name
15
16
  end
16
17
 
17
18
  def last_post
18
19
  posts.last
19
20
  end
21
+
22
+ def self.default_name
23
+ "flyerhzm"
24
+ end
25
+
20
26
  end
data/spec/spec_helper.rb CHANGED
@@ -8,14 +8,11 @@ require 'mocha/api'
8
8
  require 'memcached'
9
9
  require 'cacheable'
10
10
 
11
- # MODELS = File.join(File.dirname(__FILE__), "models")
12
- # $LOAD_PATH.unshift(MODELS)
13
- # Dir[ File.join(MODELS, "*.rb") ].each { |f| require f }
14
-
15
11
  # It needs this order otherwise cacheable throws
16
12
  # errors when looking for reflection classes
17
13
  # Specifically, post can't be before tag
18
14
  # and user can't be before post
15
+ require 'models/location'
19
16
  require 'models/account'
20
17
  require 'models/group'
21
18
  require 'models/comment'
@@ -23,6 +20,7 @@ require 'models/image'
23
20
  require 'models/tag'
24
21
  require 'models/post'
25
22
  require 'models/user'
23
+ require 'models/descendant'
26
24
 
27
25
  ActiveRecord::Migration.verbose = false
28
26
  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
@@ -45,6 +43,7 @@ RSpec.configure do |config|
45
43
  ::ActiveRecord::Schema.define(:version => 1) do
46
44
  create_table :users do |t|
47
45
  t.string :login
46
+ t.string :email
48
47
  end
49
48
 
50
49
  create_table :accounts do |t|
@@ -55,6 +54,7 @@ RSpec.configure do |config|
55
54
  create_table :posts do |t|
56
55
  t.integer :user_id
57
56
  t.string :title
57
+ t.integer :location_id
58
58
  end
59
59
 
60
60
  create_table :comments do |t|
@@ -65,6 +65,7 @@ RSpec.configure do |config|
65
65
  create_table :images do |t|
66
66
  t.integer :viewable_id
67
67
  t.string :viewable_type
68
+ t.string :name
68
69
  end
69
70
 
70
71
  create_table :tags do |t|
@@ -79,6 +80,10 @@ RSpec.configure do |config|
79
80
  create_table :groups do |t|
80
81
  t.string :name
81
82
  end
83
+
84
+ create_table :locations do |t|
85
+ t.string :city
86
+ end
82
87
  end
83
88
 
84
89
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_cacheable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.3
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-10 00:00:00.000000000 Z
12
+ date: 2013-07-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -91,8 +91,10 @@ files:
91
91
  - spec/cacheable_spec.rb
92
92
  - spec/models/account.rb
93
93
  - spec/models/comment.rb
94
+ - spec/models/descendant.rb
94
95
  - spec/models/group.rb
95
96
  - spec/models/image.rb
97
+ - spec/models/location.rb
96
98
  - spec/models/post.rb
97
99
  - spec/models/tag.rb
98
100
  - spec/models/user.rb
@@ -130,8 +132,10 @@ test_files:
130
132
  - spec/cacheable_spec.rb
131
133
  - spec/models/account.rb
132
134
  - spec/models/comment.rb
135
+ - spec/models/descendant.rb
133
136
  - spec/models/group.rb
134
137
  - spec/models/image.rb
138
+ - spec/models/location.rb
135
139
  - spec/models/post.rb
136
140
  - spec/models/tag.rb
137
141
  - spec/models/user.rb