Ruby, Railsにはnilやブランク、その他ブロック条件の真偽値判定を行うメソッドがかなりあります。ついつい無思考に present?やblank?を使ってしまいがちですが、使い分けることでより明示的になるかなと思ってます。
真偽判定いろいろ
- present?(Railsのみ)
- blank?(Railsのみ)
- empty?
- nil?
- any?
- all?
- zero?
- ActiveRecord::Type::Boolean.new.cast
present? と blank?
present?
は!blank?
と同じ。
https://github.com/rails/rails/blob/d66e7835bea9505f7003e5038aa19b6ea95ceea1/activesupport/lib/active_support/core_ext/object/blank.rb#L23
"".present? #=> false
[].present? #=> false
{}.present? #=> false
nil.present? #=> false
# 配列の要素はnilでも存在してると判定される
[nil].present? #=> true
present?
が true の時はレシーバを返し、falseの時はnilを返したい時はpresence
が使える。
a = "hoge"
b = a.presence
# これと同じ
b = a.present? ? a : nil
empty? と nil?
訂正。blank?のtrue判定はより広く、これはよくある誤解なので注意。詳しくはコメント参照。@scivolaさんありがとうございますmblank? = empty? + nil?
のこと。
empty?
はnilには呼べない。nilのハンドリングはちゃんとやれってことか。それが面倒なら blank?を使えと。ちなみに、nil?
は Object#nil?なので、全クラスのインスタンスで使える。
"".empty? #=> true
[].empty? #=> true
{}.empty? #=> true
nil.empty? #=> NoMethodError
nil.nil? #=> true
any? と all?
any?
は配列やハッシュに対して、条件一致する要素が少なくとも一つはあるか判定する。基本的にはブロックを渡して使う(正規表現も渡せる)。all?
は全部条件を満たすか判定する。
[1, 2, 3].any? { |i| i > 3 } #=> false
[1, 2, 3, 4].any? { |i| i > 3 } #=> true
[1, 2, 3, 4].all? { |i| i > 3 } #=> false
[1, 2, 3, 4].all? { |i| i < 5 } #=> true
{ name: "taro", age: 20 }.any? { |k, v| k == :name } #=> true
zero?
0 かどうか判定するメソッドまであったりする。Numeric#zero?なので、IntegerやFloatなどのNumeric以外には使えない。
0.zero? #=> true
ActiveRecord::Type::Boolean.new.cast
true / false 判定は、"true" / "false"
のように文字列になると正しく行われない。ActiveRecord::Type::Boolean
を使うとこの辺もよしなにやってくれる。もちろん Rails だけ。
ActiveRecord::Type::Boolean.new.cast("false") #=> false
全部 boolean になるわけではなく、戻り値は true/false/nil。
# false判定されるもの
false, 0, "0", :"0", "f", :f, "F", :F, "false", :false, "FALSE", :FALSE, "off", :off, "OFF", :OFF
# nil判定されるもの
nil, ""
おまけ
||= と &.
||=
は nil ガードと呼ばれ、左辺が falsy な値だった場合だけ右辺が評価される。
a = nil # falseでも同じ
a ||= "hoge"
p a #=> "hoge"
a ||= "foo"
p a #=> "hoge" 再代入されない
&.
はぼっち演算子と呼ばれ、続けてメソッドを呼び出して使う。レシーバが nil だった場合、強制的に nil を返す。
nil.upcase #=> NoMethodError
nil&.upcase #=> nil
感想
個人的にはこれらのメソッドはあんまり好きじゃなくて、==
とかで明示的に書く方が好きだけど、確かにシンプルに書けるので、使い所は選んでいきたいですね!