At some point or another, we all want to customize the default Devise registration process. The stereotypical example is creating an additional record on registration: for instance, let’s say you’re using Devise for
Users and you want to give each user an
Organization. A lot of developers want to add an
:organization_name field to the user registration form and create the organization during
class RegistrationsController < Devise::RegistrationsController # ... private def sign_up(resource_name, resource) user = resource # create an organization and associate it with the user organization_name = params[:user][:organization_name] Organization.create(name: organization_name, owner: user) # Sign in the user sign_in(resource_name, user) end end
But I believe that this approach should only be used as a last resort. If you want to run special logic during Devise’s registration process, you’ll open up a can of worms:
- Now, RegistrationsController (whose default purpose is to handle user creation) is now also handling
Organizationcreation inside one of its private methods. This is business-specific logic that might not be easy to remember when you need to revisit the controller in the future.
- What happens if there’s an error creating the organization? The
Userhas already been created, but there’s no
Organizationrecord to associate with them. You need to handle this edge case. Will you wipe the user? Redirect them to a different page? Display an error message?
- What if we want to add more to this method? For instance, to create an
Organizationand then a
Role? What happens if the
Organizationare created but the
Rolefails? (Ugh, this sucks.)
In Getting Real, Basecamp writes about letting software “push back”; that is, instead of fighting against software to make it bend to your whims, you bend your whims to fit the software’s design. Since customizing Devise’s
sign_up method is so tedious, why don’t we work within the Rails defaults to create a simpler, more beautiful solution?
Here’s what I recommend: don’t add any custom fields to Devise’s registration form, and don’t run any custom code on
sign_up. Redirect the user to
OrganizationsController#new immediately after they signup, and disguise that page as the second step in your registration process:
“Thanks for joining! Now tell us a bit about your organization.”
Then, you can handle creating the organization inside OrganizationsController – where it should have been done all along. Because we’re playing with the Rails defaults, it’s easy to handle errors, write tests, and modify whatever we need to inside the associated controller. This approach requires much less effort for a similar result.