cult3

Creating a Sign Up Form flow in Ruby on Rails Part 3

Apr 06, 2016

Table of contents:

  1. Adding the Root Route
  2. Displaying the Registration Form
  3. Dealing with failed registrations
  4. Dealing with successful registrations
  5. Conclusion

Over the last couple of weeks we’ve been putting the various components together to creating a registration flow in Ruby on Rails.

In Part 1 we looked at building out the Model layer, which included the models for users, roles and assignments.

In Part 2 we added the register Form Object for encapsulating the process of registering as a new user and automatically getting assigned to the correct role.

In this final part we are going to be looking at adding the Controller and the View to allow the user to register with the application via the browser, as well as the Functional Tests required to assert that this functionality is working correctly.

Adding the Root Route

In today’s tutorial we’re going to be flexing the application as a whole, rather than just concentrating on the individual “units” in isolation.

Something that we’re not going to test, but we’re going to need is a root route. So to prevent any weird errors later down the line, I’ll just add this now.

Open up the routes.rb file under the config directory and then delete the comments:

Rails.application.routes.draw {}

The first route we will be adding will be for the root of the application:

Rails.application.routes.draw { root 'home#index', as: :home }

This will pass traffic from the root of the application to the index method on the HomeController.

So to make sure that works we need to create a new file called home_controller.rb under the app/controllers directory:

class HomeController < ApplicationController
  def index; end
end

We also need to add a view for this route. Under app/views create a new directory called home.

Before I do this I’m first going to switch out the template engine for Slim. To do that, add the following line to your Gemfile:

gem 'slim-rails'

And then run the following command in Terminal:

bundle install

Finally you can create a new file called index.html.slim under the home directory:

h1 Welcome

Now if you run the following command in Terminal and then go to https://localhost:3000 in your browser you should see the welcome page:

rails s

Displaying the Registration Form

In order for a user to register with the application, first we need to present them with an HTML form that they can submit.

So the first thing we need to do is to add the route. Open up the routes.rb file again and add the following route:

Rails
  .application
  .routes
  .draw do
    root 'home#index', as: :home

    get 'register', to: 'register#new'
  end

This will direct GET requests to /register to the new method on the RegisterController controller.

So next up we need to create the RegisterController. Create a new file called register_controller.rb under the app/controllers directory:

class RegisterController < ApplicationController
  def new
    @form = RegisterForm.new(User.new)
  end
end

The implementation of the new requires that we instantiate a new instance of the RegisterForm Form Object from last week and pass a new instance of the User model.

We also need to create a new view file that will generate the HTML form for this page. Create a new directory called register under the app/views directory and then create a new file called new.html.slim:

- if @form.errors.any? ul
    - @form.errors.full_messages.each do |msg| li = msg =
        form_for @form, url: "register" do |f|
            div = f.label
                :email = f.text_field
                :email div = f.label :password = f.password_field :password div = f.submit
                    "Register"

We now have everything in place to display the form in the browser, but to assert that this is the case, we can add the first Functional Test.

Create a new file called register_controller_test.rb under the test/controllers directory:

class RegisterControllerTest < ActionController::TestCase
  test 'should get register form' do
    get :new

    assert_response :success
  end
end

In this test I’m making a GET request to the new method of the RegisterController and then asserting that the response is :success. If you run this test you should see it pass.

bin/rake test test/controllers/register_controller_test.rb

As a final step of verification you can boot up the Rails server and go to https://localhost:3000/register to see for yourself.

Dealing with failed registrations

When a user registers for the application, there are a lot of things that could go wrong. For example, they might enter an email address that is missing, invalid, or already registered.

When this happens we want the user to be redirected to the form so they can correct their mistakes. In this next section we will implement the code to make this happen.

First up I will add a test to assert that the request fails when the data that has been provided is invalid:

test 'should fail with invalid data' do
  post :create, register: { email: '', username: '', password: '' }

  assert_response 400
end

If you run the tests again you will see this fail because we have not created the route yet. I’m not going to step through each part of implementing this bit of functionality. If you want to see how the error messages guide you to what you should implement next I would encourage you to run the tests after each step of the following process.

First I’m going to add a new route to the routes.rb file:

Rails
  .application
  .routes
  .draw do
    root 'home#index', as: :home

    get 'register', to: 'register#new'
    post 'register', to: 'register#create'
  end

Next I’m going to add the create method to the RegisterController Controller:

class RegisterController < ApplicationController
  def new
    @form = RegisterForm.new(User.new)
  end

  def create; end
end

Next, I’m going to add a register_params method to get the parameters from the request:

private

def register_params
  params.require(:register).permit(:email, :username, :password)
end

Finally I will implement the create method. First I will instantiate a new instance of the RegisterForm Form Object:

@form = RegisterForm.new(User.new)

Next I will check to see if the request is valid by calling the validate method on the Form Object and pass it the return value of register_params:

if @form.validate(register_params)

else

end

If the request is not valid I will render the :new method and set the HTTP response code to 400:

if @form.validate(register_params)
  @form.save
  redirect_to '/'
else
  render :new, status: 400
end

The full controller will now look like this:

class RegisterController < ApplicationController
  def new
    @form = RegisterForm.new(User.new)
  end

  def create
    @form = RegisterForm.new(User.new)

    if @form.validate(register_params)

    else
      render :new, status: 400
    end
  end

  private

  def register_params
    params.require(:register).permit(:email, :username, :password)
  end
end

Now if you run the test again you should see it pass.

Dealing with successful registrations

Now that we have failing registrations taken care of, we can write the code to deal with valid registration requests.

First I will write a test to assert that this functionality is working correctly:

test 'should register new user via html request' do
  post :create, register: attributes_for(:user)

  assert_response 302
end

If you run this test you should see it fail because the request is not being redirected correctly when the form is saved.

To make this test pass we can implement the successful branch of the if clause inside of the create method of the RegisterControlller Controller:

class RegisterController < ApplicationController
  def new
    @form = RegisterForm.new(User.new)
  end

  def create
    @form = RegisterForm.new(User.new)

    if @form.validate(register_params)
      @form.save
      redirect_to '/'
    else
      render :new, status: 400
    end
  end

  private

  def register_params
    params.require(:register).permit(:email, :username, :password)
  end
end

If you run those tests again you should see them all pass!

Conclusion

Congratulations! You have now successfully implement the registration process of your Ruby on Rails application.

Over the last couple of weeks we’ve put into place everything we’ve learned so far including creating Models, Form Objects, Controllers and Views, as well as writing Unit and Functional tests where appropriate.

We’ve also covered a lot of the little details around building a Rails application and we’ve developed a workflow for building out the required functionality.

The registration process in this mini series has been purposely kept fairly simple. In a real world application you will probably have to deal with a whole load of extra requirements for any particular application.

But hopefully you have learned enough of the basics to confidently go on and build your own registration process for your own Ruby on Rails application.

You can see all of the code from today’s tutorial on Github.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.