User registration
What we will build
Now that our User
model is in place, we need a way to allow users to create an account on our application. To do this, we are going to create a new page with a sign-up form:
When users enter a valid email and password, they will be redirected to the dashboard page with a success message:
If the user enters invalid information, they will be redirected back to the registration form with a minimalistic error message:
Now that we have a clear idea of what we want, it's time to start writing some code!
Signing up users
The first step is to create the routes that we will use:
# config/routes.rb
Rails.application.routes.draw do
root "pages#home"
resource :dashboard, only: :show
resource :registration, only: %i[new create]
get "up" => "rails/health#show", as: :rails_health_check
end
We use the singular resource
method instead of the plural resources
method. This is because our users are only expected to register once, so it makes more sense to use a singular route in that case. If we open a terminal and run bin/rails routes -g registration
, we should see the following output:
Prefix Verb URI Pattern Controller#Action
registration_index POST /registration(.:format) registration#create
new_registration GET /registration/new(.:format) registration#new
The next step is to add the corresponding controller:
# app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
redirect_to dashboard_path, notice: "You have successfully registered!"
else
render :new, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:email, :password)
end
end
This is the most idiomatic Ruby on Rails controller. We see this code repeated all the time in Rails controllers.
The #new
action instantiates a new User
and assigns it to a @user
instance variable so that we can access it in the view.
The #create
action instantiates a new User
with the parameters submitted by the user in the registration form. If the parameters are valid, the user is saved to the database and redirected to the dashboard with a success message. If the parameters are invalid, the user is redirected back to the registration form with an error message.
Note that even if the route name is singular, the controller is plural. With Rails conventions, controller names should always be plural.
Now that our controller is ready, we can create the corresponding view:
<%# app/views/registrations/new.html.erb %>
<h1>Sign up</h1>
<%= form_with model: @user, url: registration_path do |f| %>
<%= tag.div(@user.errors.full_messages.to_sentence) if @user.errors.any? %>
<div>
<%= f.label :email %>
<%= f.email_field :email %>
</div>
<div>
<%= f.label :password %>
<%= f.password_field :password %>
</div>
<%= f.submit "Sign up" %>
<% end %>
This view uses the form_with
helper to create a form that will submit the user's registration information to the RegistrationsController#create
action. If there are any errors, they will be displayed at the top of the form.
Let's also add a link in the navbar to the registration form:
<%# app/views/layouts/application.html.erb %>
<!DOCTYPE html>
<html>
<head>
<%# ... %>
</head>
<body>
<header>
<nav>
<ul>
<li><%= link_to "Home", root_path %></li>
<li><%= link_to "Dashboard", dashboard_path %></li>
<li><%= link_to "Sign up", new_registration_path %></li>
</ul>
</nav>
</header>
<main>
<%# ... %>
</main>
</body>
</html>
Let's go ahead and try to create a new user with invalid parameters. You should be redirected to the registration form with an error message. If you try to create a new user with valid parameters, you should be redirected to the dashboard with a success message.
User registration tests
Let's add some automated tests to make sure this behavior will continue to work as expected. Let's write a first test to make sure there are no errors when trying to access the registration form:
# test/controllers/registrations_controller_test.rb
require "test_helper"
class RegistrationsControllerTest < ActionDispatch::IntegrationTest
test "#new" do
get new_registration_path
assert_response :success
end
end
It might seem like these types of tests are not very useful, but they are. They make sure that the route exists and that the view is rendered without any errors. In fact, I write these types of tests for every route in my applications.
Next, we can add a test for the #create
action. We are only going to test the happy path here where valid parameters are submitted.
# test/controllers/registrations_controller_test.rb
require "test_helper"
class RegistrationsControllerTest < ActionDispatch::IntegrationTest
# ...
test "#create" do
assert_difference "User.count", 1 do
post registration_path, params: { user: { email: "[email protected]", password: "password" } }
end
assert_redirected_to dashboard_path
end
end
In this test, we check that when submitting valid parameters, the user is saved to the database and is redirected to the dashboard.
Let's run all our tests to make sure everything is working as expected:
bin/rails test
If they are green, we can move on to the next chapter where we will add the ability to log in.