Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails cache_digests touch has_many association

I have something like this:

class Suite < ActiveRecord::Base
  has_many :tests
end

class Test < ActiveRecord::Base
  belongs_to :suite
end

And I'm using the cache_digests gem to do fragment caching. I want that when I update a Suite object, the children tests caches expire. I tried to put a touch: true in the has_many association without success.

How can I do that?

Thanks in advance


EDIT

I was doing my cache like this:

<% cache test do %>
  <tr>
   etc...
    <% cache test.suite do %>
      etc..
    <% end %>
  </tr>
<% end %>

But it doesn't work, because when I edit a suite, their tests isn't touched. So, I changed my cache declaration to something like this:

<% cache [test, test.suite] do %>
   etc..
<% end %>

And it worked just as expected.

When I edit a test, or a suite, one of those is touched, so, the fragment got expired and I got the new version just as expected.

Thanks to @taryn-east for the help.

like image 898
caarlos0 Avatar asked Feb 25 '13 23:02

caarlos0


3 Answers

You're right, touch: true doesn't work on has_many relationships. You could use an after_save hook and update all the associated assets manually. For example...

class Post < ActiveRecord::Base
  has_many :assets
  after_save :touch_assets

  def touch_assets
    assets.update_all(updated_at: Time.now)
    # This does a single SQL call, but bypasses ActiveRecord in the process. See warning below.
    # SQL> UPDATE "assets" SET "updated_at" = '2014-03-25 22:37:55.208491' WHERE "assets"."post_id"  [["post_id", 2]]
  end
end

class Asset < ActiveRecord::Base
  belongs_to :post
end

WARNING: This will bypass ActiveRecord when updating the assets, so if the assets need to in turn touch another object, this won't work. You could however put some extra logic into the touch_assets method that updates the objects that the assets were supposed to update. But this starts to get messy.

like image 172
Ryan Avatar answered Oct 22 '22 08:10

Ryan


This page: https://github.com/rails/rails/issues/8759

Suggest using an after_save hook:

class Post < ActiveRecord::Base
  has_many :assets
  after_save -> { self.touch }
end

class Asset < ActiveRecord::Base
  belongs_to :post
end
like image 25
Taryn East Avatar answered Oct 22 '22 08:10

Taryn East


Change your fragment cache strategy, use something like:

<% cache [test, test.suite] do %>

I found this answer in the comments. I just want to make it obvious this is "the answer."

like image 27
echan00 Avatar answered Oct 22 '22 07:10

echan00