Rails Cache Sweeper - ruby-on-rails

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.

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

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!

Related

Authorize controller actions with cancan in rails

i have some actions in controller and put authorize for 3 actions,
class TestController
def test1
authorize! :view, :testcase1
#do things1
end
def test2
authorize! :view, :testcase2
#do things2
end
def test3
authorize! :view, :testcase3
#do things3
end
end
and in the corresponding action's view, i am checking like this
if can? :view, :test_case1
#do things
end
So the problem is i am calling the authorize in 3 functions, can i put it like a single, same as before_filter
I'm not quite sure what you're getting at, but load_and authorize_resource is typically the best way to go. If you need to skip a controller action, you can do something like this:
class TestController
load_and_authorize_resource
skip_authorize_resource :only => :new
end
Add this to the class
class TestController
load_and_authorize_resource
end
and you dont need to use authorize in every action
https://github.com/ryanb/cancan/wiki/authorizing-controller-actions

Ruby on Rails: Dynamic function name, based on database

How can I create a dynamic function name in rails using the data in the database? I don't know if this is even possible.
here is a sample of my goal
class PageController < ApplicationController
def (PageModel.find(1)) #def stay
#codes here #codes here
end #end
end
I know the syntax is wrong. please help, thanks
Update
this function will only be called via routes, and in my routes I have this line
match "/:action", :controller => "page", :via => "get"
the function will look like this if it is manually generated
def stay
#some query
render 'stay_page', :layout => 'stay_page_layout'
end
def pleasure
#some query
render 'pleasure _page', :layout => 'pleasure _page_layout'
end
In routes.rb:
# Either this...
get "pages/:page", to: "pages#page"
# Or this, but make sure this is the last route in the file
get "/:page", to: "pages#page"
Your controller:
class PageController < ApplicationController
def page
#page = PageModel.find(params[:page])
end
end
If you just need to render different templates depending on the page, you can do this:
def page
#page = PageModel.find(params[:page])
render #page.name
end
If you need custom logic you could do something like this:
VALID_PAGES = ["contact_us"]
def page
#page = PageModel.find(params[:page])
execute(#page.name)
end
def execute(name)
if VALID_PAGES.include?(name)
send(name)
end
end
def contact_us
# do stuff
end
Assuming your PageModel have page_id and page_name, your url get page_id as an parameter, and will render with page_name.
code for controller:
class PageController < ApplicationController
def dynamic_action
page_name = PageModel.find(params[:page_id]).page_name
render "#{page_name}_page", :layout => "#{page_name}_page_layout"
end
end
route:
match '/:page_id' => 'page#dynamic_action'

Rails: Do something once per page reload

How to invoke method once per page reload? defining before_filter in ApplicationController is failed because of multiple controllers used to perform action
When you say multiple controllers are used in one action, could you be more specific? Normally a before_filter in the controller responsible for the action would suffice.
If you want your before filter to affect specific methods in multiple controllers then place the method itself in the ApplicationController but the before_filter in each of the controllers that contain the action.
class ApplicationController
def foo
#bar = 'bar'
end
end
class UserController
before_filter :foo, :only => [:method1]
def method1
...
end
def method2
...
end
end
class StuffController
before_filter :foo, :only => [:method2]
def method1
...
end
def method2
...
end
end
class UnimportantController
# No before filter, neither of these methods will call :foo
def method1
...
end
def method2
...
end
end

Sweepers not working unless manually invoked, what is going on?

I have the following setup: A model called Categories. These categories are managed using ActiveAdmin. I want to cache the categories using page caching.
This is how I've setup my app/admin/categories.rb
ActiveAdmin.register Category do
controller do
cache_sweeper :category_sweeper
end
end
This is my sweeper:
class CategorySweeper < ActionController::Caching::Sweeper
observe Category
def after_save(category)
expire_cache(category)
end
def after_destroy(category)
expire_cache(category)
end
def expire_cache(category)
expire_page :controller => 'categories', :action => 'index', :format => 'json'
end
end
And here is my controller:
class CategoriesController < ApplicationController
caches_page :index
cache_sweeper :category_sweeper
respond_to :json
def index
#categories = Category.all
respond_with(#categories)
end
def show
CategorySweeper.instance.expire_cache(#category)
respond_with('manually sweeped!')
end
end
So the idea is when there is a change in the active admin the sweeper should get invoked. I set up debug.log and turns out it's working. But for some reason the cache does not expire!
But, if I do the show action (i.e. go to /categories/1.json then my manual sweeper kicks in and it works fine). So why is the sweeper only working when I invoke it and not when there is a change in the admin?
Thanks in advance,
-David
Your ActiveAdmin controller located in different namespace, see this SO question.
Shortly:
Add slash into cache expiration url code:
def expire_cache(category)
expire_page :controller => '/categories', :action => 'index', :format => 'json'
end

Rails 3: Expiring cached public/index.html page

I have the following line on my routes.rb file.
root :to => "portfolio#index"
I cached the index page as follow:
class PortfolioController < ApplicationController
caches_page :index
def index
#portfolio = Portfolio.where("featured = ? AND enabled = ?", false, true)
end
end
And the PortfolioSweeper.rb
class PortfolioSweeper < ActionController::Caching::Sweeper
observe Portfolio
def after_save(portfolio)
expire_cache(portfolio)
end
def after_destroy(portfolio)
expire_cache(portfolio)
end
private
def expire_cache(portfolio)
expire_page :controller => 'portfolio', :action => 'index'
end
end
What is happening is that the expire_page only removes the /public/portfolio.html page but NOT /public/index.html page. Can you guys think of a way to remove both files?
Try just passing it the path, like:
expire_page '/index.html'

Resources