How to Override and Customize the Devise Controller in Rails

Judging by the number of different StackOverflow questions, there are a lot of people trying to do this, and a lot of confusion. Here is how I did it, and hopefully it helps you.

I have a User and a Verifier model.  What I want to do is create a new Verifier every time I create a new user, and pass in the user.id for the User into the verifier.user_id so that they are mapped together.

In order to do this I want to not really override but add additional functionality to the existing devise controller that handles when new users are created (and destroyed).  So I need to access the RegistrationsController#Create function in devise.

First thing is to create a new folder in the ‘app/controllers‘ folder where we can put my custom controller.  I called mine ‘app/controllers/my_devise‘.  Then create a new file in this folder called registrations_controller.rb

Screen Shot 2013-12-27 at 1.24.38 PM

It is called ‘registrations_controller‘ because that is what devise calls its controller that handles new user sign ups.  We want it to inherit from the original devise controller:

Screen Shot 2013-12-27 at 1.27.23 PM

Ok, so since I want to create some custom logic that happens when a new user is created, I want to override and customize the ‘create’ function:

Screen Shot 2013-12-27 at 1.36.31 PM

So you’ll notice the ‘super’ command is called at the beginning.  This is because I want to keep the original functionality of the devise create function, and just add my own additional logic.  If you want to completely customize and override the devise create function, then you don’t want this line.

Now at fist I was tempted to use ‘if user.save’, because I want to create a new Verifier when a User is created, however the devise registrations controller uses ‘resource’, so thats what I want to access here.  In my case, the model that I use with devise is User, so in my case ‘resource’ means ‘user’.  Yours will depend on how you set up devise at the beginning.

Now importantly don’t forget to also delete the linked Verifier when the User is deleted.  I used the same idea to override and customize the destroy function:

Screen Shot 2013-12-27 at 1.39.28 PM

Some things to note here.  I  used the find_all_by_user_id function to get all the Verifiers that are linked to the User that we are destroying in this function.  Since each User only has one Verifier,  I could have used find_by_user_id.  I decided to use find_all_by_user_id, just in case something weird happened and two Verifiers were linked to one User.  This would ensure they all get deleted.  Now note that find_all_by_user_id returns an array of values, even if the array only has a single element.  So thats why I need to use the each function to iterate through the array, even if it only has a single element.  This is because the destroy function cannot be called on an array, but only on a single instance.

So now when I create a new User, a Verifier is also created whose user_id value matches the User.id value.

Two things remain to do.  First, we need to copy the ‘views/devise/registrations‘ files into a custom folder that matches the custom folder we made for our registrations_controller.rb.  So I copied mine into ‘views/my_devise/registrations

Screen Shot 2013-12-27 at 1.54.47 PM

Lastly we need to update the config/routes.rb file to point to our custom controller

Screen Shot 2013-12-27 at 1.56.45 PM

If you get a routing error, try using { :registrations => "my_devise/registrations" } I got an error in production when running on heroku, and using this route worked.

That’s it!

Now I’ve been thinking that there might be a better way to do this linking a Verifier with a User with rails associations, so if you know of one, please let me know!

Here is a link to the devise registrations_controller.rb so you can see the default functions.  The devise documentation is also helpful, and I want to give credit to these StackOverflow questions which also helped me a lot. (one, two, three)

Want to learn more about Ruby on Rails?  Checkout OneMonthRails, what I used to get started in Rails. Best Rails course I’ve ever seen!

Advertisements

20 Responses to How to Override and Customize the Devise Controller in Rails

  1. Pingback: What I learned today | How to Override and Customize the Devise Controller in Rails

  2. Pingback: Customize Devise Flash Message Content in Rails | How I Learned Ruby on Rails

  3. Namrata says:

    my logout is not working. how do add routes for destroy??

    • Hi Namrata, I’m afraid I’ll need more information to be able to help you. Routes are added for destroy automatically when you create a model with Devise. You shouldn’t need to add them manually.

  4. Hi, i’ve been following all your step, but it’s not working. I get this error:

    Routing Error
    uninitialized constant Devise::MyDevise

  5. Pingback: Override Devise Controller with Custom Fields and Strong Parameters | How I Learned Ruby on Rails

  6. neanderslob says:

    Reblogged this on Neanderslob VS Technology and commented:
    Was an absolute lifesaver when I was trying to add additional registration information to my sign-up page.

  7. I can’t believe out of the tons and tons of articles about overriding the devise controllers this is the only one with real step by step instructions that doesn’t leave anything out for us noobs. Using this article, I was able to apply this concept to something different. For instance, I was able to add the stripe payment before ‘super’ and if the payment had no errors, do ‘super’ then create a new subscription in the database to a different model with the returned stripe info and then automatically create a profile for the user since the user can only have one profile with the subscription and profile both having the user id added too. If the payment fails then no account gets created at all. This is perfect! All other stripe tutorials are a mess and ultimately leaves the user with a free account if they click out of the registration before paying.

    • Very glad it was helpful! I hope to write more and share my journey of learning rails so others can benefit, just as I’ve benefitted from reading other’s blog posts, Stack Overflow questions, etc.

  8. John McCarthy says:

    Thank you so much for this! Immensely helpful!!

  9. Thank you soooo much for this post. This is exactly what I needed. I’m running a SaaS application and I needed to issue a refund when someone cancels their account. I needed to have the registration ‘destroy’ run a quick ‘refund’ module. Your post took all the confusion from the Stack Overflow questions and got me up & running quickly! Thank you!

  10. niall says:

    Hi Andrew

    Thanks for the great tutorial. I followed it and it is working fine locally but crashes on Heroku. I have adjusted the devise_for line as suggested but still crashes. I have narrowed it down to this area but don’t know what might be causing it.

    Did anyone else come across this error recently?

    Cheers

    • niall says:

      I sorted it, very strange that it would work in test but not in production. removed all unnecessary code and everything was fine. Thanks again Andrew

  11. Ben says:

    duuude, thanks for the info chrs mate

  12. Ben says:

    Ok this is definitely helpful, but it doesn’t answer my specific question. I want to add functionality to the registrations controller. Basically I want to add a new a paramater when signing up a new user. How can i add it to the registrations controller’s strong params? Any ideas much appreciated.

  13. How I can change the controller to edit a user from a user having as Administrator attribute?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: