Thoughts on login using username or e-mail address with Devise
Devise is one of the more polarising libraries out there in the Rails ecosystem — you either love it or hate it. I'm a fan; having used it for multiple projects throughout it's evolution and giving a few alternatives a try (including rolling my own), I can't really fault it.
One thing about it that does irk me, however, is the use of the #devise_controller?
method and the pattern of customising it's behaviour by putting code in ApplicationController
.
If you follow the official how-to on allowing users to login using either username or password, you'll end up with something like this in your ApplicationController
:
before_filter :configure_permitted_parameters,
:if => :devise_controller?
protected
def configure_permitted_parameters
# ...
end
This logic is needed for only a handful of actions on one controller (DeviseController
), yet we end up with a before_filter
that checks #devise_controller?
before every action in your application. Admittedly the consequences of this are minimal, but it's not a good pattern to repeat and is generally bad object-oriented design.
Thanks to the power of Ruby, and the syntactic sugar provided by ActiveSupport::Concern
, we have a nice alternative:
# config/initializers/devise.rb
module MyPermittedParameters
extend ActiveSupport::Concern
included do
before_filter :configure_permitted_parameters
end
protected
def configure_permitted_parameters
...
end
end
DeviseController.send :include, MyPermittedParameters
While we're picking on the above-linked guide, why not use Arel instead of raw SQL when overriding #find_first_by_auth_conditions
:
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions). \
where(arel_table[:username].lower.eq(login.downcase).or(
arel_table[:email].lower.eq(login.downcase))).first
else
where(conditions).first
end
end
Written by Ben Caldwell
Related protips
1 Response
Nice direction. I've introduced username's for accounts on a side-project but haven't switched over to allowing login using them because it was a hassle I didn't want to deal with yet. Your approach seems cleaner and I'll probably refer back here when I'm ready to implement username login. Thanks. :)