Random thoughs about software development

Deleting records in Hanami

The last basic functionality we need to add is the ability to delete words. Thanks to this we will have full CRUD in our app and we will be able to move to more complicated features

Feature test for deleting words

As usually, we will start with adding a feature test. It’s going to be fairly easy - we will assume that a single word is on the list of words, click “Delete” link and then we will make sure that the word is no longer on the list. Create spec/web/features/delete_word_spec.rb file and type in this code:

require 'features_helper'

describe 'Delete words' do
  before do
    @word = "lew", translation: "lion")

  it 'deletes the word' do
    visit '/'
    within '.word .actions .delete_word' do
     click_button "Delete"

    assert page.has_content?('All words')
    assert page.has_content?('There are no words yet.')    

Delete record

Our next task is to generate destroy action in the words controller. To do this, type:

bundle exec hanami generate action web words#destroy --method=delete

As a result, we have an action that will be responsible for handling DELETE requests, which will be used for deleting words. Let’s change a little config/routes.rb file as well so we will have a named route:

delete '/words/:id', to: 'words#destroy', as: :destroy_word

Let’s implement the action now. It’s a simple action that will be very similar to the update action that we implemented in this blog post.

Open apps/web/controllers/words/destroy.rb and add this code:

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

    expose :word

    def call(params)
      @word =[:id])

      redirect_to '/'       

Send delete request

Now we have to adjust our index view - next to the Edit link we want to have another link that will trigger the delete action. Since link_to helper works only with GET requests we need to figure out a different way of sending DELETE request.

There are 2 possible solutions here:

  • we can add a class to our link and write some javascript so when we click this link we will send DELETE request using our javascript function
  • instead of using links we can add very simlpe form and use this for sending DELETE requests

We will use the second approach. For each word on our list we want to render a form with “Delete” submit button. To avoid duplication we will use partials.

Our index page should look like this:

<h1>All words</h1>

<% if words.any? %>
  <div id="words">
    <% words.each do |word| %>
      <div class="word row">
        <div class="col-xs-4"><%= %></div>
        <div class="col-xs-4"><%= word.translation %></div>
        <div class="col-xs-4 actions">
          <%= link_to 'Edit', routes.edit_word_path( %> 
          <%= render partial: 'delete_word_button', locals: {word: word} %>

    <% end %>
<% else %>
  <p class="placeholder">There are no words yet.</p>
<% end %>

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

As we can see we are rendering a partial called delete_word_button and we are passing to it a word.

The last step is to add this partial.

Let’s create apps/web/templates/words/_delete_word_button.html.erb file and add following code:

  form_for :word, routes.destroy_word_path(, method: :delete, class: "delete_word" do
    submit 'Delete'

It’s a very simple form - we specify the URL using previously added named route, specify that we want to send the form using DELETE method and we add “delete_word” CSS class so we can style form later on. The body of the form consists only of submit button.

After adding this our feature test should pass.


Thanks to what we have achieved today we have a full CRUD application. We can add, edit and delete words, everything with a pretty good test coverage. Code can be found here. Now we are in the good position for adding more advanced functionalities, so stay tuned.

Written on April 30, 2017