Edited at

Railsの拡張メソッド覚書

More than 1 year has passed since last update.


はじめに

この業界に入り研修開けて半年、アウトプットも大切なのでテスト代わりにまとめようと思い、ちょこちょこ更新していきます。

一個一個確かめながら書くので長いし時間かかりそう。

指摘や改善点がありましたらよろしくお願いします。


実行環境

macOS 10.13.2

ruby 2.5.0

activesupport 5.1.4

pry


Active Support


blank? present?

require 'active_support'

require 'active_support/core_ext'

test = nil
test.blank? #=> true
test.present? #=> false
test.empty? #=> ”NoMethodError: undefined method `empty?' for nil:NilClass”

test = ""
test.blank? #=> true
test.present? =#> false

test = 0
test.blank? #=> false
test.present? #=> true

blank?とpresent?です。nil、falseとそれ以外を判定します。

blank?とempty?を混ぜて覚えてましたが、empty?はNilClassには使えません。

1/25追記

コメントで指摘されましたが、blank?は空白文字も空と判定しますので

empty?とは動作が違います。

自分の言い回しだとNilClassで使えるか否かのみに読み取れますね。ありがとうございます。

せっかくなのでもう少し深く掘り下げて説明しようと思います。

RailsGuidesの説明だと以下の通りです。


Railsアプリケーションは以下の値を空白(blank)とみなします。


  • nilとfalse


  • 空白文字 (whitespace) だけで構成された文字列 (以下の注釈参照)


  • 空欄の配列とハッシュ


  • その他、empty?メソッドに応答するオブジェクトはすべて空白として扱われます。



配列とハッシュにも使えるそうです。

実際に定義元も見てみましょう。


rails/activesupport/lib/active_support/core_ext/object/blank.rb

class Object

# An object is blank if it's false, empty, or a whitespace string.
# For example, +false+, '', ' ', +nil+, [], and {} are all blank.
#
# This simplifies
#
# !address || address.empty?
#
# to
#
# address.blank?
#
# @return [true, false]
def blank?
respond_to?(:empty?) ? !!empty? : !self
end

# An object is present if it's not blank.
#
# @return [true, false]
def present?
!blank?
end


まずはObjectClassです。

present?は!blank?というのが読み取れます。ソースにコメントで例を書いてるのがありがたいですね。


rails/activesupport/lib/active_support/core_ext/object/blank.rb

class NilClass

# +nil+ is blank:
#
# nil.blank? # => true
#
# @return [true]
def blank?
true
end
end

class FalseClass
# +false+ is blank:
#
# false.blank? # => true
#
# @return [true]
def blank?
true
end
end

class TrueClass
# +true+ is not blank:
#
# true.blank? # => false
#
# @return [false]
def blank?
false
end
end


次はNilClass、FalseClass、TrueClassの定義です。しっかりNilClassにも定義されています。


rails/activesupport/lib/active_support/core_ext/object/blank.rb

class Array

# An array is blank if it's empty:
#
# [].blank? # => true
# [1,2,3].blank? # => false
#
# @return [true, false]
alias_method :blank?, :empty?
end

class Hash
# A hash is blank if it's empty:
#
# {}.blank? # => true
# { key: 'value' }.blank? # => false
#
# @return [true, false]
alias_method :blank?, :empty?
end


ArrayとHashはempty?が呼ばれていますね。


rails/activesupport/lib/active_support/core_ext/object/blank.rb

class String

BLANK_RE = /\A[[:space:]]*\z/
ENCODED_BLANKS = Concurrent::Map.new do |h, enc|
h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
end

# A string is blank if it's empty or contains whitespaces only:
#
# ''.blank? # => true
# ' '.blank? # => true
# "\t\n\r".blank? # => true
# ' blah '.blank? # => false
#
# Unicode whitespace is supported:
#
# "\u00a0".blank? # => true
#
# @return [true, false]
def blank?
# The regexp that matches blank strings is expensive. For the case of empty
# strings we can speed up this method (~3.5x) with an empty? call. The
# penalty for the rest of strings is marginal.
empty? ||
begin
BLANK_RE.match?(self)
rescue Encoding::CompatibilityError
ENCODED_BLANKS[self.encoding].match?(self)
end
end
end


指摘されたStringClassです。RailsGuidesにも注釈がありましたが、

文字列判定に[:space:]が使用されています。こちらで確認しましたらスペースやタブ、改行などが含まれるそうです。

それら以外がある場合はfalseになるようです。


rails/activesupport/lib/active_support/core_ext/object/blank.rb

class Numeric #:nodoc:

# No number is blank:
#
# 1.blank? # => false
# 0.blank? # => false
#
# @return [false]
def blank?
false
end
end

class Time #:nodoc:
# No Time is blank:
#
# Time.now.blank? # => false
#
# @return [false]
def blank?
false
end
end


最後はNumericClassとTimeClassです。数字と時間にはblankはないため必ずfalseが返ってくると書いてあります。


blank? present?のまとめ


  • blank?は[:space:]に含まれる文字とnilとfalseに対してtrueを返す。

  • present?はblank?の反対だよ。

  • String#empty?はlengthが0の場合にtrueを返すよ!


presence

require 'active_support'

require 'active_support/core_ext'

test = ""
foo = test.presence || "こっちが入るよ"
p foo #=> "こっちが入るよ"

test.presence #=> nil

test = "今回はこっち"
foo = test.presence || "こっちが入るよ"
p foo #=> "今回はこっち"

presenceメソッドはガイドみて知りました。

定義元も見ましょう。


rails/activesupport/lib/active_support/core_ext/object/blank.rb

class Object

# Returns the receiver if it's present otherwise returns +nil+.
# <tt>object.presence</tt> is equivalent to
#
# object.present? ? object : nil
#
# For example, something like
#
# state = params[:state] if params[:state].present?
# country = params[:country] if params[:country].present?
# region = state || country || 'US'
#
# becomes
#
# region = params[:state].presence || params[:country].presence || 'US'
#
# @return [Object]
def presence
self if present?
end
end

コメントを見ればわかりますが、

present?をして

trueならそのオブジェクト自身、

falseならnilを返します。

使い方としては値が入っていればその値を使い、nilなら初期値を入れるみたいな使い方ですね。

# この書き方を

if foo.present?
bar = foo
else
bar = "初期値"
end

# 一行で書けてわかりやすい!
bar = foo.presence || "初期値"

presenceを覚えれば簡潔に書けそうです。

例にもあるようにparamsの代入でよく使えそうです。


presenceのまとめ


  • 自身に対してpresent?をしてるよ

  • trueなら自信を返すよ

  • falseならnilを返すよ

  • 初期値の代入とかで使えるよ


参考

Rails Guides Active Support コア拡張機能

Rubyのリファレンスみたいに書いてないのかな?と思ったらしっかり書いてありました。

Rails Guidesの情報量すごい

ブラケット表現 (Bracket Expression)

blank?で使われる[:space:]の説明