RottenSoftware

Random thoughs about software development

Creating records in Hanami

We’ve made good progress in the last post but we are still missing one of the key features - the ability to create and update words. In this post, we will create forms to do this for us.

Adding words to the system

Since we are adding a new feature to the system, we will start with feature test first. In our feature test we will specify that after filling certain fields in the form and clicking Create button we will end up having new word on the list.

Let’s create spec/web/features/add_word_spec.rb file and fill it with this code:

require 'features_helper'

describe 'Add a word' do
  after do
    WordRepository.new.clear
  end

  it 'can create a new word' do
    visit '/words/new'

    within 'form#word-form' do
      fill_in 'Name',  with: 'lew'
      fill_in 'Translation', with: 'lion'

      click_button 'Create'
    end

    current_path.must_equal('/')
    assert page.has_content?('All words')
    assert page.has_content?('lion')
  end
end

One thing worth mentioning is the after block before the definitions of the tests. It makes sure that we will delete content of our test database after each test, so next test will start with the clean plate.

Our next step is to generate action in the words controller. We have already done similar thing when we were working on the list of the words. All we need to do is to type:

bundle exec hanami generate action web words#new

Among others, this will add entry in the config/routes.rb file for handling new action.

Another generated file is apps/web/templates/words/new.html.erb. This is where our form will live, so add this code there:

<h2>Add word</h2>

<%=
  form_for :word, '/words' do
    div class: 'input form-group' do
      label      :name
      text_field :name, class: "form-control"
    end

    div class: 'input form-group' do
      label      :translation
      text_field :translation, class: "form-control"
    end

    div class: 'controls' do
      submit 'Create Word', class: "btn btn-success"
    end
  end
%>

As you see, we are using here some helper methods, like form_for, text_field or submit to create our form. Those methods are provided by Hanami and you can read about them more here.

Submit the form

We have the form to collect the data, but what we are still missing is the action that will accept submitted data and store them in the DB. Let’s start with creating a new action.

bundle exec hanami generate action web words#create --method=post

As you probably noticed - we passed additional option the generator, –method=post. It indicates that this action will handle HTTP POST requests and not GET requests as previous ones.

GET requests are used when we only fetch data from the server. When we create new resources, we should use POST action.

Our config/routes.rb file has been updated with this entry post '/words', to: 'words#create'.

Next step is to implement the create action. We want this action to do 2 things:

  • save the new word in the database
  • redirect to the list of words after saving record

Let’s express that with test. Type following code to the spec/web/controllers/words/create_spec.rb file:

require 'spec_helper'
require_relative '../../../../apps/web/controllers/words/create'

describe Web::Controllers::Words::Create do
  let(:action) { Web::Controllers::Words::Create.new }
  let(:params) { Hash[word: { name: 'lew', translation: 'lion' }] }

  before do
    WordRepository.new.clear
  end

  it 'creates a new word' do
    action.call(params)

    action.word.id.wont_be_nil
    action.word.name.must_equal params[:word][:name]
  end

  it 'redirects the user to the words listing' do
    response = action.call(params)

    response[0].must_equal 302
    response[1]['Location'].must_equal '/'
  end
end

Next thing we need to do is to implement the create action. This is pretty straightforward and should look like this:

module Web::Controllers::Words
  class Create
    include Web::Action

    expose :word

    def call(params)
      @word = WordRepository.new.create(params[:word])

      redirect_to '/'      
    end
  end
end

The very last thing we need to do is to update words template with the link to our newly created form. Open apps/web/templates/words/index.html.erb file and add this code at the bottom of the file:

<div class="buttons">
  <%= link_to 'New Word', routes.new_word_path, class: 'btn btn-success', title: 'New Word' %>
</div>

We can see some code that we are not familiar with yet:

  • link_to method
  • routes.new_word_path

link_to method is a helper provided by Hanami for generation links in our app. One of its parameters is the url that it should direct to and this is provided by the second helper - routes.new_word_path. To make this work we have to change a little config/routes.rb file:

get '/words/new', to: 'words#new', as: :new_word

Thanks to above line we have generated named route for a new_word and now we can use it in our application. You can learn more about routing in Hanami here.

Summary

In this post, we managed to display the form, collect input from the user, pass it to the create action and save the resource in the database. Pretty nice. In the next blog post, we will learn how to validate the form and display error messages.

Written on April 3, 2017
>