<y>

Splatoonプレイヤー兼Webエンジニア

日報 2016/07/13 -Ruby on Railsのform_forとform_tagの違いで悩んだ-

今日ちょっと業務でハマったところがあったのでそれについて書く。

form_forとform_tagについて

作っているやつ

転職して二週間弱経ったが、自分の理解力の無さと知識吸収力の低さに辟易しつつ、Ruby on Railsを使って業務をしている。
Viewの部分をhtml.erbを使って書いているのだが、「アカウント新規登録の際のパスワード」と「確認用パスワード」の二つの項目をControllerにPOSTで送信する際に、どうしてもエラーが消えないという躓きに出会った。
値の受け渡しの流れは以下の通り。

  1. View(html.erb)で"新規パスワード"と"確認パスワード"の二つの項目をPOST形式で送信し、Controllerでパラメータとして受け取る
  2. strong parametersを利用して「新規パスワード」「確認パスワード」のみをpermitする
  3. 受け取った二つの値を比較し、一致していればモデルのインスタンスにパスワードを設定してsaveメソッドを呼び出す
  4. 値が異なっていれば、エラーメッセージを設定し、新規・確認パスワードの入力画面に戻る

具体的なコードはこんな感じ。

html.erb

<%= form_for(@model, action: :index, method: :post) do |f| %>
  <div class="new_password">
    <%= f.label(:new_password,"パスワード新規登録") %>
    <%= f.password_field(:new_password) %><br>
  </div>
  <div class="confirm_password">
    <%= f.label(:confirm_password,"登録パスワード確認用") %>
    <%= f.password_field(:confirm_password) %><br>
  </div>
  <div class="password_submit">
    <%= submit "登録" %>
<% end %>
controller.rb

  def create
    param = strong_params
    @model.password = param[:new_password]
    @model.onetime_password = ""
    if param[:new_password] != param[:confirm_password]
      @model.errors.add(:base, "パスワードが一致しません")
      render :new
    elsif @model.save
      session[:model_id] = @model.id
      redirect_to :index
    else
      @model.error.add(:base, "エラーが発生しました")
      render :new
    end
  end

  private
  def strong_params
    params.permit(
      :new_password,
      :confirm_password
    )
  end

いざパスワード登録!と登録ボタンを押したところ、いわゆる「ぬるぽ」が発生した。 発生箇所が

elsif @model.save

の行だったのと、DB内でパスワードが入るカラムにはNOT NULL制約がかかっていたのもあり、「パラメータで受け取った二つのパスワードの値がnilっぽい」ことが分かった。

form_tagを使わなくてはいけなかった理由

原因が分からず唸りながらググッていたところ、こんな記事を見かけた。

qiita.com

html.erbでフォームを作成する際、form_forとform_tagという二つの選択肢がある。 正直違いがあんまり分からないし、前までform_for使って上手くいってたからform_for使おう、という軽い気持ちでform_forを使っていた。 簡潔に書くと、

  • form_forは第一引数で指定したモデルに基づいたフォームを作成する時に使う
  • form_tagはモデルに基づかないフォームを作成するときに使う

ということになる。
今回はパスワード新規登録とパスワード確認用の二つの値だけで、特に基づくモデルがないため、本来ならform_tagを使うべきだった。
そのため、以下のようにコードを修正した。

html.erb

<%= form_tag(action: :index, method: :post) do %>
  <div class="new_password">
    <%= label(:new_password,"パスワード新規登録") %>
    <%= password_field_tag(:new_password) %><br>
  </div>
  <div class="confirm_password">
    <%= label(:confirm_password,"登録パスワード確認用") %>
    <%= password_field_tag(:confirm_password) %><br>
  </div>
  <div class="password_submit">
    <%= submit_tag "登録" %>
<% end %>

こうすることで、無事にパスワードを登録することができるようになった。

form_forでもぶっちゃけ問題なく動作する

form_tagを利用してめでたしめでたしという感じだったのだが、ぬるぽが起きた具体的な理由は別のところにある。
順を追うと、

  1. ぬるぽが起きた原因としては、saveメソッドを呼んだ際に@model.passwordの中身がnilだったから
  2. @model.passwordの中身がnilなのは、strong_paramsでpermitされた値ではないから
  3. ではstrong_paramsでpermitに指定したnew_passwordとconfirm_passwordがpermitされていないのはなぜか

結論から言うと、strong parametersでネストを正しく表現していなかったからである。
form_forはモデルに基づいた値をPOSTするため、Controllerに送られる値は以下のようなネスト構造になる。

  • params
    • model
      • new_password
      • confirm_password

strong parametersでは、permitするパラメータがネストしている場合は、それを反映させなければならない。
つまり、

def strong_params
  params.permit(
    [:model][:new_password],
    [:model][:confirm_password]
  )

のように書かなくてはならない(もっとうまい書き方はあるだろうが…)。
パラメータがpermitされなかった理由は、ネストをしっかりと記述していなかったからである。
from_tagを使うと、

  • params
    • new_password
    • confirm_password

というネスト構造になるので、最初に書いていたstrong_paramsで問題ない。
結局、直し方としては

  • View(html.erb)をform_forからform_tagに修正する
  • strong_paramsのネスト構造を修正する

という二通りがある。
「html.erbでform_forをform_tagに直すよりstrong_paramsのネスト構造を直した方が楽じゃね?」と感じる方もいると思う。まったくもってその通りだ。
しかし、今回はあくまで「パスワードの登録」であり、「基づくモデルが存在しない」ことである。password.rbというモデルを作成して…とやっていたら、細分化しすぎて逆に分かりにくくなる。userというモデルにnameとmail_addressとageとpasswordいうプロパティがあり…という方が自然だ。大変分かりにくくて申し訳ないが、一画面でuserモデルの全てのプロパティを入力するフォームを作成するならform_forを利用すべきだが、今回は「新規登録パスワードと確認用パスワード」を入力するフォームだったため、モデルに基いていないということだ。
上記の点を考慮すると、form_forを利用してstrong_paramsを修正するのは、実質的には間違いと同義であるということになる、と考えられるのではないか。

まとめ

分からないことに対する知見が得られたときはとても嬉しい。ド素人なのもあって正直これで結構悩んだので、解決できたときはわりと嬉しかった。
今見返したら相当見にくい文章だということに気付いた。qiitaで読みやすい記事書いてる人、端的に言って尊敬する。

日報 2016/07/10

まだネット開通してないからテザリングで繋いでる。
ここ最近日報書けてない上にまともな進捗出せてなくてつらい。

今日までにちまちまやってたこと

Ruby on Rails チュートリアル

第1章 ゼロからデプロイまで

  • 1.1 はじめに
    • 1.1.1 前提知識
    • 1.1.2 この本における取り決め
  • 1.2 さっそく動かす
    • 1.2.1 開発環境
    • 1.2.2 Railsをインストールする
  • 1.3 最初のアプリケーション
    • 1.3.1 Bundler
    • 1.3.2 rails server
    • 1.3.3 MODEL-VIEW-CONTROLLER(MVC)
    • 1.3.4 Hello,world!
  • 1.4 Gitによるバージョン管理
    • 1.4.1 インストールとセットアップ
    • 1.4.2 Gitのメリット
    • 1.4.3 BitBucket

Progate

JavaScript 初級編

今日やったこと

Ruby on Rails チュートリアル

第1章 ゼロからデプロイまで

  • 1.4 Gitによるバージョン管理(途中から)
    • 1.4.4 ブランチ (branch)、変更 (edit)、 コミット (commit)、マージ (merge)
  • 1.5 デプロイする
    • 1.5.1 Herokuのセットアップ
    • 1.5.2 Herokuにデプロイする(1)
    • 1.5.3 Herokuにデプロイする(2)
    • 1.5.4 Herokuコマンド

第2章 Toyアプリケーション

  • 2.1 アプリケーションの計画
    • 2.1.1 ユーザーのモデル設計

後述するが、Ruby on Rails使って開発しているので、ActiveRecord便利だなとか、routes.rbにnamespace記述するとか、session保持してbefore_actionでログイン確認するんや〜とか、MVC命名規則とか、CRUDの考え方とか、くらいは理解できてきている。

近況

新しいところに入社して一週間が経過した。
所謂ベンチャーに近いところで、未経験のぺーぺーである自分を採ってくれたのは将来性と意欲だと思われる。
その代わり、給与はちょっと低い。ただこれはトレードオフであり、自分にとっても納得がいく。
入社してからは一応プロジェクトに入り、Ruby on Railsを使って開発している。正直全然わかんなかったけど、とにかく人に聞いたりググりまくったりして、超トロいけどゆっくりと理解しつつ開発しているのが現状。Ruby on Railsチュートリアルもっと早めにやっておくんだったと後悔している。
現状の基礎知識が皆無な状態だとマズいので、今はHTML&CSSレッスンブックよりも優先してrailsチュートリアルをやっている。一応業務で学んだこともそれなりにあるので、チュートリアルをやる前に知識がある箇所が一部あるため少しは進めやすくなっているだろうと思う。順番が逆だが。
正直自分の吸収速度が非常に遅く、もっとトントンと理解して業務をこなしていきたいという思いが強いのだが、いかんせんRubyの文法も結構あやふやである。そのため、先にチュートリアルやって基礎を身につけてから業務に臨んだ方が生産性が少しでも上がるだろうという考えの元にチュートリアルをやっている。
で、とにかく今は金がない。先日ゴキブリの駆除業者を呼んだ記事を書いたが、この支払いで結構な額が飛んで無一文になったため、泣く泣く親に借りるという失態をここで晒そう。親、ありがとう。あと業者呼んで以降ゴキブリ全く見てません。業者、ありがとう。
正直言って真剣に金銭的にヤバいので、なにか稼げる術を模索している。なにかいい話が転がり込まないかと思っているのだが、世の中にいい話などなく、もしいい話が転がり込んできたとしてもどうせ怪しんで断ってしまうだろうという雑な思考に辿り着いて一人で寂寥感を覚えた。
現状、なるべく金を使わず篭ってゲームと開発の勉強をする以外にないなあと考えている。

まとめ

回線工事の業者早く来てくれ

ネットないとつらいっすね

勤務先も代わりしれっと一人暮らしも始めたものの、Gによる空間妨害を受けていたため駆除業者を頼んでいた。本日業者の方に施工してもらったのだが、プロ意識が高く、精神的にも安心させてくれてとても助かった。

http://tkc-no1.com

こちらが業者のHPだが、自営業らしい。ネット上での評判も良く、こちらに頼んだ。お困りの際はおススメ。

で、6/27にNURO光の回線工事をしてもらったのだが、工事は2回行い、うち1回しか終わっておらず、次の工事の連絡がまだないためインターネット回線が家に引かれていない。

つらい。。。

進捗は頑張って生やすので待ってて。。。

日報 2016/06/30

昨日6/29(水)は少しだけ進捗が生えていたのだが、家に出てきた多めのGと戦っていたため日報が書けなかった。 Gについては、7/4(月)に駆除業者を呼んだので、ブラックキャップを撒いて一旦実家に退避した。昨日に比べて出た数もかなり減った(目視できたのは1匹だけ)ので、一応効果は出ているはず。

昨日やったこと

HTML&CSSレッスンブック

CHAPTER4

  • STEP4-1 リンクを設定する
  • STEP4-2 関連記事へのリンクをリストアップする
  • STEP4-3 ナビゲーションメニューを作成する
  • STEP4-4 ナビゲーションメニューのデザインを指定する

今日やったこと

HTML&CSSレッスンブック

CHAPTER4

  • STEP4-5 ロゴ画像とサイト名にリンクを設定する
  • STEP4-6 各ページのヘッダーを統一する

CHAPTER5

  • STEP5-1 記事の一覧ページを作成する
  • STEP5-2 特定ページのデザインを指定する
  • STEP5-3 投稿日時を表示する
  • STEP5-4 概要全体にリンクを設定する
  • STEP5-5 トップページを作成する
  • STEP5-6 大きな画像を表示してトップページをデザインする
  • STEP5-7 既存の設定を流用してデザインを追加する
  • STEP5-8 グラデーションのリンクボタンを追加する

気になったこと

正直あまり時間がないので詳しく書けないが、特にCHAPTER5の理解が薄い。 参考書に書いてあることをそのまま書けばいいため、完全に理解できていない状態でも先に進めてしまう。 今回は個人的にCHAPTER5の理解が薄いまま進めてしまった自覚があるので、しっかりと復習したい。

日報 2016/06/28

今日やったこと

HTML&CSSレッスンブック

CHAPTER3

  • STEP3-6 画像にキャプションをつける
  • STEP3-7 記事ページを増やす(1)
  • STEP3-8 記事ページを増やす(2)

気になったこと

今回の進捗では特に気になったことはない。強いて言えばあまり時間がなく、もうちょっと進めたかったことくらい。

その他

@dolpenさんから、

というアドバイスをいただいたので、現在進めているレッスンブック、Ruby on Railsのtutorialの進捗を生やしながら、こっちの方面でも何か考えていきたい。

日報 2016/06/27

6/23〜26の間は、引っ越しとかそういうのが諸々あったため進捗ありません。

今日やったこと

HTML&CSSレッスンブック

CHAPTER3

  • STEP3-1 画像を表示する
  • STEP3-2 画像を中央に配置する
  • STEP3-3 画像を左または右に配置する
  • STEP3-4 ロゴ画像を表示する
  • STEP3-5 背景画像を表示する

気になったこと

paddingとmarginが自分の考えてるのと違った…

一応実際に触ってみてわかってきたとはいえ、まだまだ理解が浅いpaddingやmargin。
STEP3-3.2でデフォルトスタイルシートによって挿入されたmarginをなくす作業があるのだが、その際の自分が考えていたCSSボックスモデルがこちら↓ f:id:yklitter:20160628021115p:plain

で、@r7kamuraさんにchromeのdeveloper toolを使うと図で表示してくれるよ、という有益な情報を教えてもらったので、確認してみることに。
article
f:id:yklitter:20160628021444p:plainf:id:yklitter:20160628021451p:plain

figure
f:id:yklitter:20160628021536p:plainf:id:yklitter:20160628021542p:plain

という感じだった。 ココらへんはもう何周かして抑えこむ必要がある。

今回は主に画像周りの配置で、このテキストを始める前から知っていたfloatとかtext-alignとか出てきたけど、今まではなんとなく理解が薄かったというか、"あ、なるほどな"という理解の芽生えがあってから進められているのかなと思う。
我が家のネット環境が整うまでまだしばらく時間がかかるが、実家に戻ったり無料Wi-Fi使ったりしてなんとか日報書いたりしていきたい。 当分は大量のダンボールを荷解く作業と並行しなくてはならないが、進捗は毎日ちょっとずつ出していくつもり。

一人暮らしが始まった

6/26に友人に手伝ってもらって引っ越し作業を終えた。
免許取って1年半くらいで初めてハイエースを運転したがもう二度と運転したくない。

足りないものリスト

食周り

  • コップ
  • フライパン
  • まな板
  • 水切り
  • 包丁
  • 台所用洗剤
  • 各種調味料
  • 炊飯器

風呂周り

  • バスマット
  • 風呂用洗剤

トイレ周り

  • トイレットペーパーの替え
  • トイレ用ブラシ

掃除周り

  • コロコロローラー
  • 掃除機

その他

  • ドライバー
  • ドライヤー

ぱっと思いつく感じこれくらい。
あとはまあ必要に応じて買うくらいのスタンスでいく。
これから先が不安だらけだが、とりあえず荷解きを終えなくてはならずつらい気持ちが生え続けている。