I have the following three models (massively simplified):
class A < ActiveRecord::Base
has_many :bs
has_many :cs, :through => :bs
end
class B < ActiveRecord::Base
belongs_to :a
has_many :cs
end
class C < ActiveRecord::Base
belongs_to :b
end
It seems that A.cs gets cached the first time it is used (per object) when I'd really rather it not.
Here's a console session that highlights the problem (the fluff has been edited out)
rails console
001 > b = B.create
002 > c = C.new
003 > c.b = b
004 > c.save
005 > a = A.create
006 > a.bs << b
007 > a.cs
=> [#<C id: 1, b_id: 1>]
This is indeed as you would expect. The a.cs is going nicely through the a.bs relation.
008 > a2 = A.create
009 > a2.cs
=> []
010 > a2.bs << b
011 > a2.cs
=> []
So the first call to a2.cs (resulting in a db query) quite correctly returned no Cs. The second call, however, shows a distinct lack of Cs even though they jolly well should be there (no db queries occurred).
012 > A.find(a2.id).cs
=> [#<C id: 1, b_id: 1>]
Again, a db query was performed to get both the A record and the associated C's.
So, back to the question: How do I force rails to not use the cached result? I could of course resign myself to doing this workaround (as shown in console step 12), but since that would result in an extra two queries when only one is necessary, I'd rather not.
Query caching is a Rails feature that caches the result set returned by each query. If Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again.
clear will clear your app cache. In that case rake tmp:cache:clear will just try to remove files from "#{Rails. root}/tmp/cache" but probably won't actually do anything since nothing is probably being cached on the filesystem.
View caching in Ruby on Rails is taking the HTML that a view generates and storing it for later. Although Rails has support for writing these to the filesystem or keeping them in memory, for production use, you'll almost certainly want a standalone caching server, such as Memcached or Redis.
I did some more research into this issue. While using clear_association_cache
was convenient enough, adding it after every operation that invalidated the cache did not feel DRY. I thought Rails should be able to keep track of this. Thankfully, there is a way!
I will use your example models: A (has many B, has many C through B), B (belongs to A, has many C), and C (belongs to B).
We will need to use the touch: true
option for the belongs_to
method. This method updates the updated_at
attribute on the parent model, but more importantly it also triggers an after_touch
callback. This callback allows to us to automatically clear the association cache for any instance of A whenever a related instance of B or C is modified, created, or destroyed.
First modify the belongs_to
method calls for B and C, adding touch:true
class B < ActiveRecord::Base
belongs_to :a, touch: true
has_many :cs
end
class C < ActiveRecord::Base
belongs_to :b, touch: true
end
Then add an after_touch
callback to A
class A < ActiveRecord::Base
has_many :bs
has_many :cs, through: :bs
after_touch :clear_association_cache
end
Now we can safely hack away, creating all sorts of methods that modify/create/destroy instances of B and C, and the instance of A that they belong to will automatically have its cache up to date without us having to remember to call clear_association_cache
all over the place.
Depending on how you use model B, you may want to add an after_touch
callback there as well.
Documentation for belongs_to
options and ActiveRecord callbacks:
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With