May 25, 2016
Table of contents:
Over the last couple of weeks we’ve looked at getting started with Trailblazer and Ruby on Rails, and Operations and Contracts.
A Trailblazer Operation encapsulates the given action of your application. So far in this little exploration of Trailblazer we’ve created an Operation for creating new Users.
Encapsulating an action in an Operation class is a really nice way of defining the logic for that action. As we saw in Getting started with Operations in Trailblazer, it’s also really easy to test the business logic of the Operation for the given action.
But how do we now use that Operation in the Controller and the View?
In today’s tutorial we will be connecting the dots to finish off the implementation for creating a new user using Trailblazer and Ruby on Rails.
I’m definitely not a stickler for strict Test Driven Development. More often than not I tend to use the spike and stabalise method of writing code.
However sometimes I do find it beneficial to write out the tests first because it allows me to plan what I need to build. Just the act of writing each incremental test often allows me to get past the mental block of implementing something, even if it is relatively simple.
So with that being said, here are the controller tests that we will be looking to satisfy in today’s tutorial:
require 'test_helper'
class RegisterControllerTest < ActionController::TestCase
test 'should display register form' do
get :new
assert_response :success
end
test 'should fail to register with invalid data' do
post :create, user: { email: '', password: '' }
assert_response 400
end
test 'should register new user' do
post :create, user: { email: 'name@domain.com', password: 'password' }
assert_response 302
assert_redirected_to login_url
end
end
In the first test I’m asserting that a GET
request to the new
method of the RegisterController
returns a successful response. If this doesn’t return a successful response something has seriously went wrong.
Next I assert that passing invalid data to the create
method will return a HTTP response code of 400
.
And finally, I will assert that passing valid data to the create
method will return a HTTP response code of 302
and we should be redirected to the login URL.
You will notice that I’m not testing every validation rule or circumstance or scenario in these tests. I prefer to test those kinds of things at the Unit level.
With the tests in place, we can now write the code to make them pass.
The first thing I’m going to do is to add the routes that we require:
Rails
.application
.routes
.draw do
# Registration
get 'register', to: 'register#new'
post 'register', to: 'register#create'
# Authentication
get 'login', to: 'login#new'
end
We’re not going to implement logging in into the application in today’s tutorial, but we need the login route to redirect to on a successful registration attempt.
If you are familiar with routing in Rails this should look fairly straight forward to you. If you are not familiar with routing in Rails, take a look at Defining URL routes in Ruby on Rails.
Next up we can add the Controllers that we will need to make the tests pass.
First create a new file under the controllers
directory called register_controller.rb
:
class RegisterController < ApplicationController
end
If you remember back to my review of Trailblazer, a Controller in a Trailblazer application is simply a lean HTTP endpoint that does not contain any business logic.
So first up we can define the new
method, which will display the registration form:
class RegisterController < ApplicationController
def new
form User::Create
end
end
In this method we simply need to pass the Operation to the form
method that was added to the Controller by Trailblazer.
Next we can implement the create
method that will accept the POST
request to create a new user:
class RegisterController < ApplicationController
def new
form User::Create
end
def create
run User::Create do |op|
return redirect_to login_url
end
render :new, status: 400
end
end
Pretty nice huh? In this method we run the Operation and pass it a block. The block will only be executed if the request is run successfully. I absolutely love how simple Trailblazer makes my Controllers!
Inside the block I’m simply redirecting to the login page. But you could do whatever you want inside this block on a successful registration request.
If the Operation is not successful we can simply render the :new
method and set a status of 400
.
In this tutorial we’re simply using the login page as a destination for the redirect. So to make this work we will need a Controller and a View:
class LoginController < ApplicationController
def new; end
end
I’ve just left the new
method empty for now as we don’t actually have to implement anything in this Controller to make the tests pass.
Now that we have the Controller methods implemented, we need to add the Views so Rails doesn’t complain.
First I will create a new directory under views
called register
and a new file called new.html.slim
:
h1 Register - if @form.errors.any? ul - @form.errors.full_messages.each do |msg|
li = msg = form_for @form, url: register_url do |f| div = f.label :email =
f.text_field :email div = f.label :password = f.password_field :password div =
f.submit "Register"
This is just a simple form for the email and password and a submit button. If the form has any errors we can iterate through them and display them as a list.
We also need a view for the login route. Create a new directory under views
called login
and then create a new file called new.html.slim
:
h1 Login
Again, we don’t actually need to implement a login form to make the tests pass, so this will be fine for now.
Now with everything in place, you should be able to run the tests and see them all pass! You can also boot up Rails in the browser and go through the registration flow yourself to verify that everything is working correctly.
So as you can see, Trailblazer really does make your Controllers lean HTTP endpoints!
I personally love this aspect of Trailblazer. I hate it when you open a project and the Controllers are totally stuffed with methods and all sorts of weird code to deal with business logic edge cases.
The more I use Trailblazer, the more I appreciate this style of architecture for medium to larger sized applications.