コントローラの作成(3)

細かく書きすぎると肝心なところが抜けてしまうので、これまでと同じところは省略していきます。特に locales のところは記述しませんので、適当に補完して下さい。

タイトルの編集

引き続き、編集・更新のコントローラを作成します。いつものようにテストから記述します。

  シナリオ: 開発環境でタイトルを編集する
    前提 タイトルにダミーデータを登録する
    もし タイトル一覧を表示する
    かつ id が "あいうえお" である行の "[編集]" をクリックする
    ならば 画面に "タイトル編集(あいうえお)" と表示されていること
    もし "日本語""森高 千里" と入力する
    かつ "英語""Chisato Moritaka" と入力する
    かつ "読み""もりたか ちさと" と入力する
    かつ "更新する" ボタンをクリックする
    ならば 画面に "タイトル一覧" と表示されていること
    かつ 画面に "森高 千里" と表示されていること

ダミーデータの登録 step は spec/acceptance/steps/titles_page_steps.rb に記述します。

step 'タイトルにダミーデータを登録する' do
  Title.create(japanese:'あいうえお', english:'AIUEO', yomi:'あいうえお')
end

また、テーブルの特定行のリンククリックに関する step は spec/acceptance/steps/top_page_steps.rb に記述します。

step 'id が :name である行の :link をクリックする' do |name, link|
  within("//tr[@id='#{name}']") do
    click_link link
  end
end

view においてタイトルリストの id の設定ができていないこと、編集リンクがないことからエラーになっています。このため app/views/titles/index.html の該当部を以下のように修正します。

%table
  %tr
    -%w(link.titles).each do |k|
      %th=t k
    -if @is_dev
      %th=t 'head.control'
  -@titles.each do |title|
    -name = title.name(@is_ja)
    %tr{id:name}
      %td=name
      -if @is_dev
        %th=link_to lh(t 'link.edit'), edit_title_path(title)

編集リンクにより、edit が呼ばれました。app/controllers/titles_controller.rb に edit を記述します。今後作成する edit, update, destroy, show ではすべて id で指定されたタイトルオブジェクトを取得するため、app/controller/titles_controller.rb に before_action を追加します。

class TitlesController < ApplicationController
  before_action :reject_production, except:[ :index, :show ]
  before_action :get_title, only:[ :edit, :update, :destroy, :show ]

get_title は private メソッドとして実装します。

  def get_title
    @title, @ids = get_objects_and_ids [ Title ]
  end
  private :get_title

ここに記述した get_objects_and_ids は他のコントローラでも使用するため、app/controllers/application_controller.rb に記述します。

  def get_objects_and_ids(array, first_id = true)
    ans = []
    ids = {}
    array.each_with_index do |model, i|
      id = i == 0 && first_id ? 'id' : "#{model.to_s.underscore}_id"
      if pid = params[id]
        ans << model.find(pid)
        ids[id] = pid
      else    
        ans << nil
      end
    end
    ans << ids
  end

最後に、edit メソッドを app/controllers/titles_controller.rb に記述します。@title を取得するだけなので、中身は何もありません。

  def edit
  end

app/views/titles/edit.html.haml を記述します。

%h1="#{t '.title'}(#{@title.name(@is_ja)})"

=render partial:'form'

当初、app/views/titles/_form.html.haml に書いた時には f.submit にボタン名の引数を付けていましたが、以下のように省略すると文脈から適切なボタン名が付けられるのですね。

      %td{colspan:2}=f.submit

今回は edit なので自動的に「更新する」に変更されています。この結果、テストの方は無事に update メソッドが呼ばれていることがわかります。update メソッドも通常通り作成してしまいます。

  def update
    if @title.update(title_params)
      redirect_to titles_path(head:@title.head)
    else
      render action: :edit
    end
  end

正しく、テストが通過しました。編集に失敗した場合のテストもしておきます。登録自と違うのは前提とボタン名だけですね。

  シナリオ: パラメータ設定が間違えていたときのエラー確認(タイトル編集)
    前提 タイトルにダミーデータを登録する
    もし タイトル一覧を表示する
    かつ id が "あいうえお" である行の "[編集]" をクリックする
    もし "日本語""" と入力する 
    かつ "英語""Chisato Moritaka" と入力する
    かつ "読み""もりたか ちさと" と入力する
    かつ "更新する" ボタンをクリックする
    ならば 画面に "タイトル一覧" と表示されていないこと
    かつ 画面に "日本語を入力してください。" と表示されていること
    もし "日本語""森高 千里" と入力する
    かつ "英語""" と入力する
    かつ "更新する" ボタンをクリックする
    ならば 画面に "タイトル一覧" と表示されていないこと
    かつ 画面に "英語を入力してください。" と表示されていること
    もし "英語""Chisato Moritaka" と入力する
    かつ "読み""" と入力する
    かつ "更新する" ボタンをクリックする
    ならば 画面に "タイトル一覧" と表示されていないこと
    かつ 画面に "読みを入力してください。" と表示されていること
    かつ "読み""ABC" と入力する
    かつ "更新する" ボタンをクリックする
    ならば 画面に "タイトル一覧" と表示されていないこと
    かつ 画面に "読みは不正な値です。" と表示されていること

このテストはちゃんと通過しますね。

タイトル削除

タイトルが他のオブジェクトと関連していない場合、タイトルが削除できるようにします。現在は他のクラスがないため、すべて削除できる状況です。削除のテストを書いてみます。

  シナリオ: 開発環境でタイトルを削除する
    前提 タイトルにダミーデータを登録する
    もし タイトル一覧を表示する
    かつ id が "あいうえお" である行の "[削除]" をクリックする
    かつ ダイアログを承認する
    ならば 画面に "タイトル一覧" と表示されていること
    かつ 画面に "あいうえお" と表示されていないこと

削除リンクを app/views/titles/index.html.haml に追加します。

      -if @is_dev       
        %th             
          =link_to lh(t 'link.edit'), edit_title_path(title)
          =link_to lh(t 'link.destroy'), title_path(title), method: :delete, data:{ confirm:t('confirm.destroy') } if title.can_delete?

@title が削除できるかどうかは app/models/title.rb の can_delete? というメソッドで判断します。今は常に true を返します。

  def can_delete?
    true
  end

ダイアログの承認までが実行でき、destroy メソッドがないとエラーになっています。

  def destroy
    head = @title.head
    @title.destroy
    redirect_to titles_path(head:head)
  end  

これを記述して削除まで実装できました。show は今のところ表示するものがあまりないため、別のクラスを作成した後に作成してみます。