views:

55

answers:

2

I'm trying to implement a Cache Sweeper which would filter a specific controller action.

class ProductsController < ActionController  
    caches_action :index  
    cache_sweeper :product_sweeper  

    def index 
        @products = Product.all 
    end 

    def update_some_state
      #... do some stuff which doesn't trigger a product save, but invalidates cache
    end
end 

Sweeper Class:

class ProductSweeper < ActionController::Caching::Sweeper
    observe Product

    #expire fragment after model update
    def after_save
       expire_fragment('all_available_products')   
    end

    #expire different cache after controller method modifying state is called.
    def after_update_some_state
        expire_action(:controller => 'products', :action => 'index') 
    end
end

The ActiveRecord callback 'after_save' will work fine, but the callback on the controller action 'after_update_some_state' never seems to be called.

A: 

I think your sweeper should look like this:

class ProductSweeper < ActionController::Caching::Sweeper
  observe Product

  def after_save(product)
     expire_cache(product)
  end

  def after_destroy(product)
    expire_cache(product)
  end

  private

  def expire_cache(product)
    expire_fragment('all_available_products')
    expire_page(:controller => 'products', :action => 'index')
  end 

after_index isn't a callback unless you define it.

In the controller you should specify those actions in which the sweeper should be triggered, in a restful way those actions should be create, update, destroy, so your controller declaration should look like:

class ProductsController < ActionController  
  caches_action :index  
  cache_sweeper :product_sweeper, :only => [:create, :update, :destroy]  

  def index 
    @products = Product.all 
  end 

  def create
    @product = Product.new(params[:product])

     if @product.save # triggers the sweeper.
       # do something
     else
       # do something else
     end
   end

  # update and stuff ...
end 

I hope it helps you!

jpemberthy
The cache sweeper docs say: "Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change. They do this by being half-observers, half-filters and implementing callbacks for both roles. " Doesn't that mean that it would add after_index as a filter callback? http://api.rubyonrails.org/classes/ActionController/Caching/Sweeping.html
jrizza
No, Sweepers do not define filter callbacks, they work `almost` like observers, the great difference (from my point of view) is that you have to specify for what actions the observer will work and also, you'll get access to some controller specific objects and methods, e.g: `expire_cache`, `session`, `params` and so on ...
jpemberthy
After reviewing my original post, I realized that trying to display what I wanted to accomplish by using index as my example action which I was trying to wrap might be cofusing. I updated my original example to be a little more clear.
jrizza
I have updated my answer, I think you are a bit confused, The `after_save` callback on the sweeper works because you are observing a `Transport` and it has `ActiveRecord` callbacks e.g, `after_save`, `before_save`, `before_update`, all of them will be eventually triggered in the sweeper ... What you are trying to do with `after_update_some_status` is not a valid `ActiveRecord` callback and that's why it's never called, what you should do is; trigger a callback event on your controller action, e.g: `@product.save`, `@product.update_atributes` and then expire your cache on the sweeper.
jpemberthy
Looks like I was just missing the controller name when trying to implement the controller action callback. after|before_<controller name>_<action_name>
jrizza
A: 

Looks like I was just missing the controller name when trying to get the callbacks for controller actions working. My Sweeper should be:

class ProductSweeper < ActionController::Caching::Sweeper
    observe Product

    #expire fragment after model update
    def after_save
       expire_fragment('all_available_products')   
    end

    #expire different cache after controller method modifying state is called.
    def after_products_update_some_state
        expire_action(:controller => 'products', :action => 'index') 
    end

    #can also use before:
    def before_products_update_some_state
        #do something before. 
    end
end
jrizza