【Rails】present?メソッドの使い方やリファクタリングの方法を具体的なコードを使って丁寧に解説

Rails

present?メソッドとは

present?メソッドを使用したオブジェクトが存在すればtrueを返し、存在しなければfalseを返すメソッドです。

present?メソッドの使い方
リンクをコピーしました

present?メソッドの構文
1
オブジェクト.present?
使い方の例
1
2
3
4
5
site_name = 'pikawaka'
=> ""

site_name.present?
=> true

present?メソッドはActiveSupportで定義されているメソッドです。

ActiveSupport | present?メソッドの定義
1
2
3
def present?
  !blank?
end

present?メソッドは上記の様に定義されております。!blank?ということはつまりblank?メソッドの返り値の反対がpresent?メソッドの返り値になります。blank?メソッドは真偽値(trueかfalse)を返すメソッドなので、blank?メソッドがtrueならpresent?メソッドはfalse、blank?メソッドがfalseならpresent?メソッドはtrueを返すということですね。
blank?メソッドの挙動の詳細については後述するので、ここではpresent?メソッドの返り値は必ずblank?メソッドの反対になるんだな〜と理解しておきましょう。

present?メソッドがtrueになる場合・falseになる場合のオブジェクト
リンクをコピーしました

present?メソッドがblank?メソッドの返り値の反対の結果になるということは分かりましたが、具体的にどんなオブジェクトがtrue・falseを返すか見ていきましょう

コンソール | 返り値がfalseの場合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
str = ''
str.present?
=> false

str = '   '
str.present?
=> false

arr = []
arr.present?
=> false

key_value = {}
key_value.present?
=> false

false.present?
=> false

nil.present?
=> false

present?メソッドがfalseの場合を見てみると、空文字、空白文字、配列の器、hashの器、false、nilの場合はpresent?メソッドの返り値はfalseになってます。つまりstringや配列、ハッシュなど型の器はあっても中身がない場合はfalseになっていることがわかります。

コンソール | 返り値がtrueの場合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
str = 'str'
str.present?
=> true

arr = ['arr']
arr.present?
=> true

key_value = {key: 'value'}
key_value.present?
=> true

true.present?
=> true

そして、それぞれの型で中身がある場合はtrueを返します。つまり、present?メソッドがtrueであればレシーバーのオブジェクトがどんな型でも何かしら値が入っているのだなと考えてください。逆にpresent?メソッドがfalseならばレシーバーのオブジェクトがarrayなど器はあっても中身はないと考えると分かりやすいので、まずはざっくりそのように覚えましょう。

present?メソッドを三項演算子で書いてみよう
リンクをコピーしました

present?メソッドの返り値は真偽値(true or false)を返すので、三項演算子を使って処理をかけます。
三項演算子とは、下記のように「条件式 ? 条件が正しい場合 : 条件が正しくない場合」と一行でif elseの様な条件分岐を簡潔に書く方法です。

コンソール | 三項演算子
1
2
3
4
5
6
7
site_name = 'pikawaka'
site_name == 'pikawaka' ? 'このサイトはpikawakaです。' : 'このサイトはpikawakaではありません'
=> "このサイトはpikawakaです。"

site_name = 'hoge'
site_name == 'pikawaka' ? 'このサイトはpikawakaです。' : 'このサイトはpikawakaではありません'
=> "このサイトはpikawakaではありません"

詳しく知りたい方は三項演算子を徹底解説を参照してください。

この三項演算子を応用するとpresent?メソッドが真偽値を返すことから、「条件式 ? もし値があった場合:もし値がなかった場合」という様に一行で簡潔に書くことができます。例題を見てみましょう。

コンソール | present?と三項演算子を一緒に使う方法
1
2
3
4
5
6
7
arr = ['arr']
arr.present? ? '配列の中に値が入っています' : '配列の中身は空です'
=> "配列の中に値が入っています"

arr = []
arr.present? ? '配列の中に値が入っています' : '配列の中身は空です'
=> "配列の中身は空です"

この様にpresent?メソッドと三項演算子を一緒に使うとif文で5行くらい書かなければいけないコードが

コンソール | present?と三項演算子を一緒に使う方法
1
2
3
4
5
6
7
8
arr = ['arr']
if arr.present?
  '配列の中に値が入っています'
else
  '配列の中身は空です'
end

=> "配列の中に値が入っています"

一行で簡潔に書ける様になるので、if elseの条件分岐を使ってる人は三項演算子で簡潔に書けないか考えてみましょう。

コンソール | present?と三項演算子を一緒に使う方法
1
2
3
arr = ['arr']
arr.present? ? '配列の中に値が入っています' : '配列の中身は空です'
=> "配列の中に値が入っています"

present?メソッドをpresenceメソッドでリファクタリングしてみよう
リンクをコピーしました

present?メソッドと三項演算子を使って値がある場合・値がない場合を一行で書ける簡潔な方法を紹介しましたが、presenceメソッドを使うとより簡潔に書くことができます。

presenceメソッドとは、オブジェクト.present?がtrueならばオブジェクトを返し、falseならばnilを返すメソッドです。

presenceメソッドがどの様なメソッドか具体例を見てみましょう。

コンソール | presenceメソッドとは
1
2
3
4
5
6
7
arr = ['arr']
arr.presence
=> ["arr"]

arr = []
arr.presence
=> nil

上記の例で行くと['arr'].present?はtrueになるので['arr']を返し、[].present?はfalseなのでnilを返しました。このnilを返す特徴と || を利用するとオブジェクトが存在するならばオブジェクトを返し、存在しないならば || 以降の処理を返すということが簡単に書ける様になります。

コンソール | presenceメソッドと || 演算子を一緒に使った場合
1
2
3
4
5
6
7
arr = ['arr']
arr.presence || '配列の中身は空です'
=> ['arr']

arr = []
arr.presence || '配列の中身は空です'
=> "配列の中身は空です"

|| 演算子の使い方について知らない方は || 演算子の使い方を詳しく解説を参照してください。

presence || ~~を使うとこの様に短く書けます。presenceメソッドと三項演算子を見比べてみましょう。

コンソール | presenceメソッドと三項演算子を見比べてみる
1
2
3
4
# 下の2つのコードは同じ意味になります。
['arr'].present? ? ['arr'] : '配列の中身は空です'
['arr'].presence || '配列の中身は空です'
=> ['arr']

presenceメソッドを使えば、この様に三項演算子よりも処理を簡潔に書くことができます。上の例はコード量が少ないのでどれだけリファクタリングができたか分かりにくいですが、数が多くなるとpresenceメソッドでより簡潔にかけていることを実感できます。下記の例を見てみましょう。

コンソール | present?メソッドとif文を使用した例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def get_blood_type(a_type, b_type, ab_type, o_type)
  if a_type.present?
    '血液型はA型です。'
  elsif b_type.present?
    '血液型はB型です。'
  elsif ab_type.present?
    '血液型はAB型です。'
  elsif o_type.present?
    '血液型はO型です。'
  end
end

a_type = ''
b_type = ''
ab_type = ''
o_type = '血液型はO型です。'

blood_type = get_blood_type(a_type, b_type, ab_type, o_type)
p blood_type
=> "血液型はO型です。"

このコードを下記にリファクタリングできます。

コンソール | presenceメソッドと || 演算子でリファクタリングした例
1
2
3
4
5
6
7
8
9
10
11
12
def get_blood_type(a_type, b_type, ab_type, o_type)
  a_type.presence || b_type.presence || ab_type.presence || o_type.presence
end

a_type = ''
b_type = ''
ab_type = ''
o_type = '血液型はO型です。'

blood_type = get_blood_type(a_type, b_type, ab_type, o_type)
p blood_type
=> "血液型はO型です。"

この様にpresenceメソッドで冗長なif文をリファクタリングできます。
presenceメソッドは大変便利なメソッドなので、よりpresenceメソッドの詳細な使い方を知りたい方はpresenceメソッドを使ってpresent?メソッドのコードをリファクタリングしよう!を参照してください。

present?メソッド以外の真偽判定メソッド
リンクをコピーしました

ここまでpresent?メソッドの使い方やリファクタリングの方法について説明してきました。この章ではpresent?メソッドの様に存在チェック系等の真偽値を返すメソッドについて説明していきます。

blank?メソッド
リンクをコピーしました

記事の冒頭で説明したblank?メソッドです。present?メソッドはblank?メソッドで実行した真偽値の反対の真偽値が出ると説明しましたが、blank?メソッドが実際にどの様なオブジェクトに対してtrueやfalseを実行するか見ていきましょう。

コンソール | blank?メソッド(trueの場合)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
str = ''
str.blank?
=> true

str = '   '
str.blank?
=> true

arr = []
arr.blank?
=> true

key_value = {}
key_value.blank?
=> true

false.blank?
=> true

nil.blank?
=> true

present?メソッドが!blank?で定義されていると説明した通り、present?メソッドがfalseになるパターンはblank?メソッドでは全てtrueになります。

コンソール | blank?メソッド(falseの場合)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
str = 'str'
str.blank?
=> false

arr = ['arr']
arr.blank?
=> false

key_value = {key: 'value'}
key_value.blank?
=> false

true.blank?
=> false

逆にpresent?メソッドがtrueになる場合はblank?メソッドは全てfalseになります。
blank?メソッドとpresent?メソッドは常に真偽値の結果が反対になると覚えておきましょう。

nil?メソッド
リンクをコピーしました

nil?メソッドの動作は簡単です。オブジェクトがnilであればtrueを返し、nilでなければfalseを返します。
空文字や配列の器、ハッシュの器だけでも全てfalseを返します。

コンソール | nil?メソッド
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
nil.nil?
=> true

''.nil?
=> false

'   '.nil?
=> false

[].nil?
=> false

{}.nil?
=> false

false.nil?
=> false

nil?はnilに対してはtrueを返し、それ以外に関しては全てfalseを返すと覚えておきましょう。

empty?メソッド
リンクをコピーしました

empty?メソッドについて説明します。empty?メソッドはString、Array、Hash等に定義されているメソッドで、nilや真偽値オブジェクトに対して実行するとエラーを出力します。具体的にどのオブジェクトではtrueになり、どのオブジェクトではfalseになるか見ていきましょう。

コンソール | empty?メソッド(文字列の場合)
1
2
3
4
5
"".empty?
=> true

" ".empty?
=> false

空文字ならばtrueを返し、空白文字でも入っていたらfalseを返します。

コンソール | empty?メソッド(配列の場合)
1
2
3
4
5
[].empty?
=> true

['   '].empty?
=> false

配列も要素が0ならばtrueを返しますが、空白文字でも要素が存在するならばfalseを返します。

コンソール | empty?メソッド(hashの場合)
1
2
3
4
5
{}.empty?
=> true

{hoge: 'hogehoge'}.empty?
=> false

hashも配列と同じで、中身がない器だけの場合はtrueを返し、key valueのペアが存在すればfalseを返します。

コンソール | empty?メソッド(エラーが出る場合)
1
2
3
4
5
6
7
8
9
10
11
nil.empty?
NoMethodError: undefined method `empty?' for nil:NilClass

true.empty?
NoMethodError: undefined method `empty?' for true:TrueClass

false.empty?
NoMethodError: undefined method `empty?' for false:FalseClass

10.empty?
NoMethodError: undefined method `empty?' for 10:Integer

この様にNilClass、Integerクラス、TrueClass、FalseClassには定義されておらずエラーが出てしまうので注意が必要です。

exists?メソッド
リンクをコピーしました

exists?メソッドは指定したレコードがDBにあるかどうかを真偽値で返すメソッドです。

コンソール | exists?メソッドの使い方
1
2
3
4
5
User.exists?(age: 20)
=> true

User.exists?(age: 19)
=> false

上記の例で言うと、User.exists?(age: 20)でtrueが返ってきているので、usersテーブルに20歳のユーザーがいることを表しています。そしてUser.exists?(age: 19)がfalseなので、usersテーブルに19歳のユーザーがいないことを表しています。

この様に指定したレコードが存在するかしないかを真偽値で返してくれるので、レコードのデータを使わずにデータの存在チェックだけをしたい場合にはかなり便利なメソッドになります。

データの存在チェックで真偽値を返すメソッドを表でおさらいしよう
リンクをコピーしました

present?メソッド以外にもオブジェクトの存在チェックをするメソッドを説明しました。それぞれのメソッドで似ているが微妙に挙動が違うメソッドなどもあったので、最後にそれぞれのメソッドが返す真偽値に対して簡単に表で見直しましょう。

present? blank? nil? empty?
''(空文字) false true false true
' '(空白文字) false true false false
[](空配列) false true false true
['  '](配列要素あり) true false false false
{}(空ハッシュ) false true false true
{hoge: 'hogehoge'}(ハッシュ要素あり) true false false false
1000 true false false NoMethodError
nil false true true NoMethodError
true true false false NoMethodError
false false true false NoMethodError

存在チェックのメソッドでどのメソッドを使うか悩んだときはこの表を見て適切なメソッドを使う様にしましょう!

まとめ
  • present?メソッドは中身があればtrueを返し、器だけならfalseを返す
  • if文の条件分岐や三項演算子でpresent?メソッドを使っていればpresence || ~~の形でリファクタリングできる
  • 存在チェックで似ているメソッドはたくさんあるので、適切な存在チェックのメソッドを使う様にしよう