セキュリティ系の勉強、その他開発メモとか雑談. GithubはUnity触っていた頃ものがメイン Twitterフォローもよろしくです

【Ruby】チュートリアルのform_forをform_withで書き換え (おまけ:capybaraでのテスト)

Ruby5.1前まではフォームを生成してくれるメソッドとして、form_forform_tagがあった。特にRubyチュートリアルでは2018/11/18現在、form_forを利用した方法を紹介しています。が、5.1以降はform_with推奨とのことなので、書き換えようと思いました。

form_with

APIドキュメントの翻訳版はこちらで読む事ができます。

techracho.bpsinc.jp


基準となるモデルがある場合

チュートリアルでは7.2.1で登場してます。Userモデルを作るフォームです。

<%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>


form_withを使用すると下のようにかけます。基本的に変わりませんが、model:でUserモデルを指定してあげます。チュートリアルの方を見ればわかりますが、コントローラの方で@user = User.newやってます。

<%=form_with model: @user do |f|%>
  <%= render 'shared/error_messages', object: @user%>

  <%=f.label :name%>
  <%=f.text_field :name,class:'form-control'%>

  <%=f.label :email%>
  <%= f.email_field :email,class:'form-control'%>

  <%=f.label :password%>
  <%= f.password_field :password,class:'form-control'%>

  <%=f.label :password_confirmation %>
  <%=f.password_field :password_confirmation,class:'form-control'%>

  <%=f.submit yield(:button_text),class:'btn btn-primary'%>
<%end%>


基準となるモデルがない場合

一方チュートリアル8.1.2では、上のような誰のフォームであるかをモデルで表わせず、sessionという言葉でまとめています。

<%= form_for(:session, url: login_path) do |f| %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.submit "Log in", class: "btn btn-primary" %>
    <% end %>


これは下のようにして書き換えられます。

    <%=form_with scope: :session, url:login_path do |f|%>

      <%=f.label :email%>
      <%=f.email_field :email, class:'form-control', id:'email'%>

      <%=f.label :password%>
      <%=f.password_field :password, class: 'form-control',id: 'password'%>

      <%=f.submit "Log in", class: "btn btn-primary"%>
    <%end%>


capybaraでのテスト

form_forでは、生成されるinputタグにid属性が付与されていました。一方form_withではid属性はオプションでつくようになっています。上の書き換えの例では、id属性にemailpasswordを指定しています。これはcapybaraでfill_inメソッドを使用してテストするためです。(他にもテスト方法はありますが、、)

#form_for
  <input class="form-control" id="session_email" name="session[email]" type="email" />
#form_with(id指定なし)
<input class="form-control" type="email" name="session[email]">
#form_with(id指定あり)
<input class="form-control" id="email" type="email" name="session[email]">


fill_inメソッドはidを参照するようで、id属性がないとエラーが起きます。変更前は以下のようにテストコードを書いていました。

#capybaraを使用しフォームに値を入れる
fill_in "Email",        with: "入力する値"

実はこれidがemailだと反応しません。_emailEmailが含まれていれば反応します。 なので、どっちも小文字でemailを指定するのが一番まるくおさまる気がします。もしくはid属性にEmailを指定してやればテストコードはそのままで大丈夫かもしれません。