Do cache sweepers observe the controller and not the model?

I'm (finally) looking into caching, and it seems kind of strange to
me. If I cache a page, and then create a sweeper to expire that page,
the sweeper appears to "observe" controller actions rather than the
model.

For example, say I've got the following sweeper:

class StylesheetSweeper < ActionController::Caching::Sweeper
  observe Stylesheet

  def after_save(stylesheet)
    expire_stylesheet_page stylesheet.id
  end

  def after_destroy(stylesheet)
    expire_stylesheet_page stylesheet.id
  end

  private
  def expire_stylesheet_page(stylesheet_id)
    expire_page hash_for_stylesheet_path(stylesheet_id)
  end
end

If I open the console and make a change to a Stylesheet object, the
cache doesn't get expired. That seems wrong to me. Editing records
through the console would lead to a stale cache.

What am I missing?

Pat

Cache sweepers are invoked from the controller, not the model. It'd
be nice if my_model.update automatically invoked the sweeper, but
afaik that doesn't currently happen. You set up the cache expiration
when my_controller#update gets called, etc.

Pat

You must put your sweeper inside the controller that'd trigger the
sweeper.
The sweeper is triggered on change in models.
but when you step into any expire_* method in a debugger, you'd see
that a controller is needed!

http://trac.extendviget.com/tyrant/browser/branches/edge-conversion/vendor/rails/actionpack/lib/action_controller/caching/sweeping.rb?rev=160

          def method_missing(method, *arguments)
            return if @controller.nil?
            @controller.send(method, *arguments)
          end

Gone Sail wrote:

OK on your logic Yaxm. So now what do we do if the controller (for
reasons which I do not know) is nil ?? the model sweeper is trying to
expire the fragment as it was intended but now rails is preventing it !!
what a pain. help.

Yaxm Yaxm wrote:

I don't get it. How is your model being "updated" if not through a
controller. The whole point is that models are updated through
controller actions. You then add sweepers to those actions which are
invoked when the model is altered through those actions. Why would the
controller "be nil"? There should always be a controller that is
responsible for the actions which modify the state of your models...

Are you modifying your model via script/console or runner? How often will you be doing this? You could prolly instantiate a controller here or something but if it’s a really rare instance, you might just want to do something cheap and easy like sweep the whole cache [via FileUtils.rm_rf or something]. The problem with instantiating a controller is that it’s gonna need a CGI request to generate the urls when it is trying to expire_page IIRC. And that may be more trouble than it’s worth.

RSL

Nathan Esquenazi wrote:

I don't get it. How is your model being "updated" if not through a
controller. The whole point is that models are updated through
controller actions. You then add sweepers to those actions which are
invoked when the model is altered through those actions. Why would the
controller "be nil"? There should always be a controller that is
responsible for the actions which modify the state of your models...

i didn't say it was not going thru a controller. the thing is that the
model is being instantiated via classify.constantize. the model name is
not the same as the controller name but i am still doing a model.save
therefore this should clear the cache correctly but it fails in
caching.rb Sweeping module in the method_missing method because
@controller is nil. i did not write this code i am just trying to fix
it. the method that is missing is expire_fragment.

def method_missing(method, *arguments)
  return if @controller.nil?
  @controller.send!(method, *arguments)
end

the controller shouldn’t be nil if you are editing the model via a rails action instigated by a controller. in your controller you should have

cache_sweeper :foo_sweeper

and in yr sweeper do you have

observe Foo

right? if so, then controller should not be nil. if not, try adding those things [obviously taking into account the actual names of the sweeper and model] and see if it solves yr problem. if not, please include the code you use in your reply so we can see what is happening exactly. :slight_smile:

RSL