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')
Related
I am trying to create an action that checks if a user is permitted to perform a certain action and if the user isn't then I want to redirect the user to an "Access Denied" view
This is how my current setup is
class PermissionController < ApplicationController
def authorize(permission_id)
is_permitted = is_user_permitted(permission_id)
respond_to do |format|
format.js { render :json => {:is_permitted => is_permitted, :redirect => url_for(:controller => 'welcome', :action => 'index' , notice: "No access")}}
format.all { redirect_to :controller => 'welcome', :action => 'index' , notice: "No access" unless is_permitted == true }
end
end
end
I want to call the authorize action in the :before_filter of another controller.
How do I do that?
I can't put the authorize action in the ApplicationController since I want to define a route to this action in routes.rb
#NickM has covered this in his comment... have OtherController inherit from PermissionController
class PermissionController < ApplicationController
def authorize
...
end
end
class OtherController < PermissionController
before_filter :authorize
end
However I note your authorize method has a parameter?
You'll need to handle that in the before_filter clause. Assuming you can store permission_id in a session variable...
class PermissionController < ApplicationController
def authorize(permission_id)
...
end
end
class OtherController < PermissionController
before_filter { |controller| controller.authorize(session[:permission_id] }
end
Why don't you try something like this, assuming you have the user's id stored in session:
class ApplicationController < ActionController::Base
def current_user
return unless session[:user_id]
#current_user ||= User.find(session[:user_id])
end
def authorize
# If user is NOT permitted
if !is_user_permitted(current_user)
# Response for you ajax here
respond_to do |format|
format.js { render :json => {:is_permitted => false, :redirect => url_for(:controller => 'welcome', :action => 'index' , notice: "No access")}}
format.all { redirect_to :controller => 'welcome', :action => 'index' , notice: "No access" }
end
end
end
end
class SomeOtherChildController < ApplicationController
before_filter :authorize, :only => [:show, :new, :edit]
# or you can use :except => [:index, :create, :destroy, :update] instead of :only.
# No authorization required
def index
end
# Authorization required
def show
end
# Authorization required
def new
end
# No authorization required
def create
end
# Authorization required (Ajax response for your "Edit" button)
def edit
# authorize method in ApplicationController will be called first
# If user is authorized, then the rest of this action will be executed
respond_to do |format|
format.js { render :json => {:is_permitted => true} }
end
end
# No authorization required
def update
end
# No authorization required
def destroy
end
end
Take this as an outline and general concept to what you might want to implement.
This is typically a concept I implement with permissions in my apps. You probably wouldn't want to put the Permissions logic in a separate child class controller, because in order to check permissions, you would either have to create a reference object of the PermissionsController (that is ugly and very un-Rails like) and use it within whatever controller you're trying to check permissions for, or you will have all other controller classes inherit from the PermissionsController, which isn't terrible, but certainly not ideal.
If users can have multiple types of permissions, you are probably better off creating a Permission model and controller, with a User has_many Permissions relationship, where the logic in the authorize method would become a bit easier to implement.
it seems devise do not create any controller file in app/controller folder, now I am looking to show some custom info from other models in users/edit view but not able to figure out how to do it since there is no controller to add
#num_of_cars = current_user.num_of_cars.all
I am looking to show num_of_cars in users/edit page but not able to figure out how to do it
EDIT - the below mentioned code giving me error, I am putting this code in my devise/registration/edit file
<%= render partial: 'myinfo/cars_list' %>
where myinfo is another resource with a partial _cars_list.html.erb, the /myinfo scaffold works perfectly fine but when I am trying to display that partial into users/edit, it is giving me
undefined method `each_with_index' for nil:NilClass
(I am using each_wit_index to display list but I don't think that is the problem)
Override the Devise sessions controller actions:
# app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
def edit
# add custom logic here
#num_of_cars = current_user.num_of_cars.all
super
end
end
Register the controller:
# app/config/routes.rb
devise_for :users, :controllers => {:sessions => "sessions"}
You can create new controller for handel devise/registration/edit
here's is controller/passwords_controller.rb
class PasswordsController < Devise::RegistrationsController
before_filter :authenticate_user!
def edit
#num_of_cars = current_user.num_of_cars.all
super
end
def update
#user = current_user
# raise params.inspect
if #user.update_with_password(params[:user])
sign_in(#user, :bypass => true)
redirect_to user_path, :notice => "Password has been change!"
else
render :edit,:locals => { :resource => #user, :resource_name => "user" }
end
end
end
here's is routes.rb
devise_scope :user do
get '/edit_password' => 'passwords#edit', :as => :change_password
put '/change' => 'passwords#update'
end
and finaly you can copy devise/registrations/edit.html.erb to passwords/edit.html.erb
In your case, you dont have to override the devise controller. You can replace
<%= render partial: 'myinfo/cars_list' %>
with
<%= render partial: 'myinfo/cars_list', :locals => { :num_of_cars => current_user.num_of_cars.all } %>
in your devise/registration/edit page. Then using "num_of_cars" variable instead of "#num_of_cars", inside your 'myinfo/cars_list' partial, should fix your issue.
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
I just created my first engine. It adds a couple of new routes like so:
Rails.application.routes.draw do
scope :module => 'contact' do
get "contact", :to => 'contacts#new'
get "contact/send_email", :to => 'contacts#send_email', :as => 'send_email'
end
end
Then, in /websites/Engines/contact/app/controllers/contacts_controller.rb, I have:
module Contact
class ContactsController < ApplicationController
# Unloadable marks your class for reloading between requests
unloadable
def new
#contact_form = Contact::Form.new
end
def send_email
#contact_form = Contact::Form.new(params[:contact_form])
if #contact_form.valid?
Notifications.contact(#contact_form).deliver
redirect_to :back, :notice => 'Thank you! Your email has been sent.'
else
render :new
end
end
end
end
I loaded it up in the client app's console to prove to myself some basics were working and quickly got this load error (which I then confirmed by reproducing the issue in a browser):
ruby-1.8.7-p302 > Contact::Form.new
=> #<Contact::Form:0x2195b70>
ruby-1.8.7-p302 > app.contact_path
=> "/contact"
ruby-1.8.7-p302 > r = Rails.application.routes; r.recognize_path(app.contact_path)
LoadError: Expected /websites/Engines/contact/app/controllers/contacts_controller.rb to define ContactsController
And there you have it; /contact gets to the engine's contacts_controller.rb but the fact the controller is inside the module Contact makes it unrecognizable.
What am I doing wrong?
Your app/controllers/contacts_controller.rb is actually defining the Contact::ContactsController, not the ContactsController that Rails expects.
The problem is with your routes, they should be defined like this:
Rails.application.routes.draw do
scope :module => 'contact' do
get "contact", :to => 'contact/contacts#new'
get "contact/send_email", :to => 'contact/contacts#send_email', :as => 'send_email'
end
end
Thanks to #ryan-bigg and #nathanvda their answers in conjunction fixed this issue for me. In short, I ended up using the following routes:
Rails.application.routes.draw do
scope :module => 'contact' do
get "contact", :to => 'contacts#new'
post "contact/send_email", :to => 'contacts#send_email', :as => 'send_email'
end
end
with the following controller:
module Contact
class ContactsController < ApplicationController
def new
#contact_form = Contact::Form.new
end
def send_email
#contact_form = Contact::Form.new(params[:contact_form])
if #contact_form.valid?
Contact::Mailer.contact_us(#contact_form).deliver
redirect_to :back, :notice => 'Thank you! Your email has been sent.'
else
render :new
end
end
end
end
but what seemed to be the final piece was #nathanvda's suggestions to move the contacts_controller from:
/app/controllers/contacts_controller.rb
to
/app/controllers/contact/contacts_controller.rb
Thank you both for your help!
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'