rails f.check_boxの妖艶な動きに騙されるな!
checkboxを生成してくれるrailsのヘルパーがあるのだが、けっこうハマったのでメモ。
見積もりを1時間ほどとしていた軽めのバグタスクがあったのだが、手を付け始めてから結構長い間プルリクをオープン状態のまま放置していた。というのも、バグっている箇所とほぼ同じコードを書いているはずの箇所がちゃんと動いてしまっており、「これもしかしたらハマるかも」と思ったからだ(実際結構ハマった)。何でハマったか、ちゃんと残しておこうb
結論から言うと、「f.check_boxのchecked_value、unchecked_valueにはシンボルではなくstringを入れておこう」ということ。
バグってたのは以下のコード。
# == Schema Information
#
# Table name: hoges
#
# id :integer not null, primary key
# foo :integer default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Hoge < ActiveRecord::Base
enum foo: %i(foo_ng foo_ok)
end
〜〜〜
<%= f.label :foo do %>
<%= f.check_box :foo, {}, :foo_ok, :foo_ng %>
<%= label_tag 'hoge_foo', '' %>
<%= f.label :foo, 'fooはokです' %>
<% end %>
〜〜〜
f.check_boxのドキュメントを見ると、引数は以下の順番。
1. プロパティ名
2. オプション
3. checked_value (default: "1")
4. unchecked_value (default: "0")
上記のコードでは、チェックを入れればfoo_ok(つまり0)がsubmitされる。だから、チェックしてsubmit後そのページに戻ってきたときは、デフォでチェックされてるはず...なのだが、チェックされてなかった、というのが直すべきバグ内容。なんでだろ??
同じようなコードを書いているところとの違いは、enumを使ってなんかゴチャゴチャやってるかどうかくらい。でもそれしか違いがないんだから、多分それがバグの要因なんだろう。ここまでは分かった。でもその先どう解決すべきかがよく分からなくて結構時間をとられた。以下、解決までの流れ。
- 「rails check box enum」でググってみた
- が、それらしきものが見当たらず...
- とりあえずあまり得策ではないものの、オプションのcheckedをvalue見て変える形で実装
- まぁそりゃあ...動くっちゃあ動く
- でも、やっぱりコレジャナイ感がすごい
- そもそもf.check_boxならformの要素渡ってるはず
- checked渡さずとも、正しい挙動ならデフォでチェックが入るはず
- ということは...?
- formを作るところで間違っている(ヘルパーに渡すものが違う)のかも
- なんか怪しいのは、やはり引数の3番目と4番目のchecked_value & unchecked_value
- 試しにフォーム要素
@hoge
から@hoge.foos.key[0]
みたいな感じで取ってくると、動いた - 動かなかったやつと動いたやつで、生成されたhtmlを確認してみる
- checked="checked" 以外同じやんけ!!!!!!
- ということは...?
- 謎
- 別に
@hoge.foos.key[0]
でなくてもいいよね、これって別にフォーム要素関係ないし(どちらかというとクラスの話) - 試しに 'foo_ok' とか文字列ブチ込んだろ
- 動いた
- 別に
-
動いた
- ということは...?
- これ文字列じゃないとだめなやつじゃね...?
- 多分そうなんだ、そうなんだけど、どういうわけで文字列じゃないとだめなんだべ?
- f.check_boxのソースコードを辿ってみる
- ほう、check_boxはTag::CheckBox.newでつくってるらしい
- Tag::CheckBoxのソースコードを辿ってみる
- ほう、Tag::CheckBoxはここかぁ〜
- この辺
- なるほど、文字列とシンボルの比較をやってたからここがfalseになってたんや...!
ということでした。つまるところ、
[1] pry(main)> 'aaa' == :aaa
=> false
やで!ということです。
今回の学び
-
「どうなってるのかよく分からんけど、動いた」は危ない
- この場合はうまくいくかもしれないけど、次にもし、同じようで同じじゃない、少し同じバグが出てきたときに対処できない可能性がある
- なんで動かなかったのか?なんで動いたのか?を理解することを心がける
-
ソースコードともっと仲良くなる
- なんか謎な動き方してると思ったら、ソースコードをあたってみること。大抵書いてある