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
Related
I have a Rails 2 app with an API. I versioned and optimised the controllers but there are duplicate methods. The goal is to have the common information in only one place. So I explored the following options:
redirect from routes the non-API controller, but each controller needs it's specific hooks
module inclusion. This is my favorite but there are like quite a lot of errors thrown out and very limited time to fix things up.
eval. Put all the code in one file and eval it in both places. Done this, it works but I am not pleased by this workaround.
What would be the best to go about this?
Might be some typos lurking in here but:
class GenericController < ApplicationController
def index
#objects = params[:controller].singularize.camelcase.constantize.all()
end
def show
#object = params[:controller].singularize.camelcase.constantize.find(params[:id])
end
def new
#object = params[:controller].singularize.camelcase.constantize.new
end
def edit
#object = params[:controller].singularize.camelcase.constantize.find(params[:id])
end
def create
model = params[:controller].singularize.downcase
#object = params[:controller].singularize.camelcase.constantize.new(params[model])
if #object.save
redirect_to '/'+params[:controller]
else
render :action => 'new'
end
end
def update
model = params[:controller].singularize.downcase
#object = params[:controller].singularize.camelcase.constantize.find(params[:id])
if #object.update_attributes(params[model])
redirect_to :controller => params[:controller], :action => 'index'
else
render :action => 'edit'
end
end
def destroy
if #object = params[:controller].singularize.camelcase.constantize.find(params[:id])
#object.destroy
end
redirect_to :controller => params[:controller], :action => 'index'
end
end
Specific controllers can override those implementations as needed, but:
class ProjectsController < GenericController
# done!
end
class ScenariosController < GenericController
# done!
end
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'
All,
I am trying to add caching to my Rails 3.1 apps. The sweeper lives in default namespace, and I have controller that lives in the Admin namespace.
For example, I have BooksController in the Admin namespace, and whenever the share method in this controller I want book cache to be sweeped. I tried to name this method after_books_share, but the method is not called.
class Admin::BooksController < ApplicationController
caches_action :show
cache_sweeper :book_sweeper
def share
# "Share" a book
end
end
class BookSweeper < ActionController::Caching::Sweeper
observe Book
def after_update(book)
expire_cache_for(book)
end
def after_books_share
book = Book.find params[:id]
expire_cache_for(book)
end
def expire_cache_for(book)
expire_action(:controller => '/books', :action => 'show', :id => book)
end
end
Use slashes in front of the controller name. To expire in the default namespace:
expire_action(:controller => '/users', :action => 'index')
To expire in the admin namespace:
expire_action(:controller => '/admin/users', :action => 'index')
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'
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!