【Rails】nil? empty? blank? present?の挙動と使い分けを確認

## 1. TL; DR
下記の基準で使い分ける
- nilかどうかの観点のみで判定したい：nil?
- nil + 空のオブジェクトの存在を判定したい：blank?
- 値の有無によって結果を判定したい：empty?
※例外が発生する可能性があるため取り扱い注意
- 空でないオブジェクトの存在を判定したい：present?

#### 早見表
|       |nil? |empty?|blank?|present?|
|---    |---  |---   |---   |---     |
|nil    |true |``NoMethodError``|true |false|
|true   |false|``NoMethodError``|false|true |
|false  |false|``NoMethodError``|true |false|
|""     |false|true             |true |false|
|" "    |false|false            |true |false|
|[]     |false|true             |true |false|
|{}     |false|true             |true |false|
|"Qiita"|false|false            |false|true |
|1      |false|``NoMethodError``|false|true |


## 2. nil?
- Rubyのメソッド
- nil以外はfalseを返す
    - ここではオブジェクトそのものがnilとされるものだけtrueとなるため、空配列や空ハッシュのようにオブジェクト自体は存在しているようなものは、全て結果がfalseとなる

```:コンソール
irb(main):029:0> nil.nil?
=> true
irb(main):030:0> false.nil?
=> false
irb(main):031:0> true.nil?
=> false
irb(main):032:0> "".nil?
=> false
irb(main):033:0> " ".nil?
=> false
irb(main):034:0> [].nil?
=> false
irb(main):035:0> {}.nil?
=> false
irb(main):036:0> "Qiita".nil?
=> false
irb(main):037:0> 1.nil?
=> false
```

## 3. empty?
- Rubyのメソッド
- 入れ物があって、そこに<font color="red">値が入っていないかどうか</font>が判断の基準
- ""はtrueだが、空文字を含む" "はfalseとなる
    - empty?はオブジェクトの長さ(length)が0になるものがtrueのため
- nil, true, false, 数値に使うと例外になる(入れ物そのものがない)

```:コンソール
irb(main):038:0> nil.empty?
Traceback (most recent call last):
        1: from (irb):38
NoMethodError (undefined method `empty?' for nil:NilClass)
irb(main):039:0> true.empty?
Traceback (most recent call last):
        2: from (irb):38
        1: from (irb):39:in `rescue in irb_binding'
NoMethodError (undefined method `empty?' for true:TrueClass)
irb(main):040:0> false.empty?
Traceback (most recent call last):
        2: from (irb):39
        1: from (irb):40:in `rescue in irb_binding'
NoMethodError (undefined method `empty?' for false:FalseClass)
irb(main):041:0> "".empty?
=> true
irb(main):042:0> " ".empty?
=> false
irb(main):043:0> [].empty?
=> true
irb(main):044:0> {}.empty?
=> true
irb(main):045:0> "Qiita".empty?
=> false
irb(main):046:0> 1.empty?
Traceback (most recent call last):
        1: from (irb):46
NoMethodError (undefined method `empty?' for 1:Integer)
```

lengthの結果が0の場合にtrueだから、""の結果はtrueで、" " の結果はfalseとなる
```
irb(main):006:0> " ".length
=> 1
irb(main):007:0> "".length
=> 0
```

## 4. blank?
- Railsのメソッド
- nilと空のオブジェクトの場合はtrue返す
    - nil, false, "", " ", [], {}の場合はtrueを返す
- empty?だとnilチェックでエラーが出てしまうが、blank?を使えば解決できる
- blank?の反対は、present?

```:コンソール
irb(main):047:0> nil.blank?
=> true
irb(main):048:0> true.blank?
=> false
irb(main):049:0> false.blank?
=> true
irb(main):050:0> "".blank?
=> true
irb(main):051:0> " ".blank?
=> true
irb(main):052:0> [].blank?
=> true
irb(main):053:0> {}.blank?
=> true
irb(main):054:0> "Qiita".blank?
=> false
irb(main):055:0> 1.blank?
=> false
```

## 5. present?
- Railsのメソッド
- オブジェクトがある場合はtrueを返す
- present?の反対は、blank?

```:コンソール
irb(main):056:0> nil.present?
=> false
irb(main):057:0> true.present?
=> true
irb(main):058:0> false.present?
=> false
irb(main):059:0> "".present?
=> false
irb(main):060:0> " ".present?
=> false
irb(main):061:0> [].present?
=> false
irb(main):062:0> {}.present?
=> false
irb(main):063:0> "Qiita".present?
=> true
irb(main):064:0> 1.present?
=> true
```

## 6. 使い分けの基準
- nilかどうかの観点のみで判定したい：nil?
- nil + 空のオブジェクトの存在を判定したい：blank?
- 値の有無によって結果を判定したい：empty?
※例外が発生する可能性があるため取り扱い注意
- 空でないオブジェクトの存在を判定したい：present?
※ただしActiveRecordで利用する場合は、exists?を使う方がパフォーマンスが上がる可能性が高い。詳しくは[下記の記事](https://qiita.com/ginger-yell/items/67fc358351c2eecaba4a)を参照

https://qiita.com/ginger-yell/items/67fc358351c2eecaba4a

## 7. おまけ　2重否定は避けた方が可読性が増すかも

present?とblank?は反対の関係にあるということは、下記は同じことを示す
``if 〜 blank?`` ``unless 〜 present?``

```:コンソール
irb(main):018:0> pp 'True!!!' unless Article.find(3).present?
  Article Load (0.1ms)  SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
=> nil
irb(main):019:0> pp 'True!!!' if Article.find(3).blank?
  Article Load (0.1ms)  SELECT "articles".* FROM "articles" WHERE "articles"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
=> nil
```

 ``unless 〜 present?``は、「空のオブジェクトはないものでない場合」という2重否定の判定になっている。

``if 〜 blank?``と ``unless 〜 present?``は同じことを意味すると言えど、``if 〜 blank?``の方が条件か明確で分かりやすい


## 参考
https://medium.com/le-wagon/how-to-use-nil-blank-present-exists-in-rails-5-fe03e78ab979