Recently I've been working on a Rails app to track work-out progress.
As you read in the title, this post is about using the Devise Gem, and I'll be working through using it at it's simplest form, to create a resource that I wished I'd had when I first tried it out.
The Devise Gem is users for User Authentication, It creates sign-up and sign-in forms, it also can be used to create user accounts for privacy. (I'm sure it can do more things, but this is how I'm using it. )
So as with all other gems, you'll need to install in the usual way by adding to your Gemfile, gem 'Devise'
Then run bundle install
in your terminal. After you've done that, you'll need to run the generator. rails generate devise:install
.
That command installs an initializer that will print a lot of instructions, the only one we need to focus on right now is adding a default URL for the Devise Mailer. You can use the suggested one for the sake of this application.
Next, we need to create our Devise model, you can name it anything, (e.g. User, Admin, Member or Staff) depending on your purpose. I'll be using "User", thus I'll run in my terminal, rails generate devise user
, following that up with rails db:migrate
.
The first action you should take is to ensure that your Rails routes are configured for Devise, which is done by adding the following to your config/routes.rb
file,
devise_for :stages
root to: "stages#index"
# "stages" can be replaced by whatever you name your controller, it's just what I needed to name my controller.
This will create all the necessary routes for authentication of your application, and then lead to the main index page.
In order to make your parts of your application unavailable to people without an account, go into their controllers and before your methods begin, add the following helper,
before_action :authenticate_user!
Something to be aware of, is to make sure that your helper matches your Devise model (User, Admin, etc.)
The three most important helpers for Devise are as follows,
user_signed_in?
# I used this to show the user’s email address in the top Navigation bar, and depending on their status to show “Log In” or “Log Out” options.
user_session
# Used to create the Log In occurrence, which remains until I’m logged out, in which case it destroys that session.
current_user
#This is used here to display the User's email in the navigation bar.
<% if user_signed_in? %>
<%= current_user.email %>
<ul class="dropdown-menu" role="menu">
<li><%= link_to 'Profile', edit_user_registration_path %></li>
<li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li>
<% else %>
<li><%= link_to 'Log In', new_user_session_path %></li>
<li><%= link_to 'Sign Up', new_user_registration_path %></li>
<% end %>
The current_user
helper is also used in my controllers, stopping the user from accessing and editing any other user's data.
def new
@stage = current_user.stages.new
end
def create
@stage = current_user.stages.new(allowed_params)
if @stage.save
redirect_to stages_path
else
render 'new'
end
end
def edit
@stage = current_user.stages.find(params[:id])
end
def update
@stage = current_user.stages.find(params[:id])
if @stage.update_attributes(allowed_params)
redirect_to stages_path
else
render 'edit'
end
end
def destroy
@stage = current_user.stages.find(params[:id])
@stage.destroy
redirect_to stages_path
end
As you can see, every method in my Stages controller has to be authenticated through the Devise Gem's current_user
helper.
Another useful aspect of Devise is that running $ rails generate devise:views users
will create all the necessary views for signing up, logging in, password changes and account changes. As an example, here's what Devise creates for a sign-up page,
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if @minimum_password_length %>
<em>(<%= @minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
As you can see, there's a lot of work already done for you, which means you only have to blend it into the style of whatever you're creating.
I believe that's all I'm going to cover in this post without running the risk of going against my opening statement of blogging about Devise in "it's simplest form". Hopefully this helps someone else understand this incredible useful gem!
If you have any suggestions for other simple ways to use it that are beginner-friendly, please comment them down below!
Top comments (2)
Hey Mosekwa, great question! I should have clarified, the stages controller is part of the app. It's basically a part of the app that I want to "protect" with Devise. It could be any controller. :) Let me know if that helps!
Hi Andrew, did you by any chance try to customize the error messages displayed when e.g. sign up fails (blank input, etc.)? For instance, one could move the error message into the label to keep the interface clear.
I don't know how to access those error messages. I've tried accessing the errors the way one would for a model, e.g. model.errors[:name] to get errors associated with the name of the model, but to no avail. Having searched online a bit, it isn't clear how to access those messages.
EDIT: Operator error. I hadn't tried targeting 'resource', I assumed it had to be 'resource_name' because of the bit right at the top of the form: "simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|". Always check assumptions!