In the continued saga that is the Rails 2.3.10 to Rails 3.0.1 upgrade, I found this little nugget today…you want to add a custom action to a controller that inherits from a Devise controller. Here are the steps that you need to follow:
1) Create a custom controller that overrides some out of the box Devise actions as well as adds a new method.
class RegistrationsController < Devise::RegistrationsController
def update
# do something different here
end
def deactivate_owner
# deactivate code here
end
end
2) You have to tell Devise to use the new controller in routes.rb as well as add the new route to the new action.
devise_for :owners, :controllers => { :registrations => "registrations" } do
post "deactivate_owner", :to => "registrations#deactivate_owner", :as => "deactivate_owner_registration"
end
When we initially implemented this, our route definition looked like this:
post "deactivate_owner", :to => "registrations#deactivate_owner", :as => "deactivate_owner_registration"
devise_for :owners, :controllers => { :registrations => "registrations" }
When it was implemented this way, we kept getting a AbstractController::ActionNotFound exception. Once we passed the block to devise_for seen in #2, everything worked fine.
Exactly! In Devise 1.0.x we used the URL to try to guess which scope was being accessed.
In Devise 1.1 use the constraints API in the router to tell which scope is being accessed. So you can either define your route inside devise_for or use “devise_scope” or “as” method.
This gives more flexibility to allow you to access whatever URL you want through Devise. :)
In Devise master we also improved the error message when AbstractController::ActionNotFound is raised, so people have a better idea of what happened.
November 7, 2010 at 2:36 pm
Thanks for the note Jose and thanks for making Devise great! I’m really enjoying using it, especially the new version for Rails3.
November 7, 2010 at 3:00 pm
This was an absolute life saver, thank you for this post! I’m performing this exact customization now and couldn’t figure out the abstract controller error.
November 9, 2010 at 12:13 pm
Hello Mike,
I am trying to follow your advise for my customization work and not having much luck with it.
So instead of RegistrationsController, I call my controller UsersController as shown below:
class UsersController < Devise::RegistrationsController
skip_before_filter :require_no_authentication <-- I override this
def index
# shows a list of users
end
def new
@user = User.new
@user.build_profile <-- for has one profile relationship
end
def change_status
# marks a user as active or inactive
end
...
end
To account for this and following your advise here, I have the following entries in the routes.rb file:
FileManager::Application::routes.draw do
devise_for :users, :controllers => {:registrations => “users”} do
get “index”, :to => “users#index”, :as => “index”
end
resources :users <- if I don’t do this I get the following error
# no route matches /users
end
If you can provide some further guidance, I will appreciate it.
Thanks.
Bharat
December 1, 2010 at 2:57 pm
Bharat,
I am confused about what further guidance you need. Is there a specific problem you are having or error you are seeing?
Mike
December 2, 2010 at 5:59 am
Hello Mike,
Thanks for your response and willingness to help out. I found a nice reference which is more inline with what I was trying to do and things are working now. Here is the reference:
https://github.com/fortuity/rails3-mongoid-devise
I will be posting my application on the github guite soon so that everyone can benefit from it. Currently, it uses authlogic which I am replacing with Devise and CanCan. Here is the URL:
http://github.com/bruparel/file_manager3
Regards,
Bharat
December 2, 2010 at 1:22 pm
Dang I wish I had seen this blog post before I dug into the Devise source code to figure out why it kept throwing UnknownAction exceptions.
One thing to note is that if you want to add a route that might interfere with the other Devise routes (ie, users#show at “users/:id”), you need to define them after the call to `devise_for`):
devise_for :users, :controllers => {:registrations => “users”}
devise_scope :user do
get “users/:id”, :to => “users#show”, :as => “user”
end
December 5, 2010 at 12:46 am
Yes! It works. I’m starting to think Devise needs a serious round of documentation. Who’s on that? if no one, i volunteer. I just have to learn the hell out of it first. I’m still not convinced that it’s so much better than Authlogic. But then Authlogic has no documentation either.
December 10, 2010 at 10:56 am