Tech Racho エンジニアの「?」を「!」に。
  • 開発

[Rails 4] active_support/core_extの変化を見る

Railsの便利さを支えるActiveSupport。特にcore_extは単体で使うことも多いと思います。

基本的な機能ながら、バージョンごとに地味に機能修正が行われているので、わかりやすいものを2個取り上げてみました。

3.2で挙動が変わったblank?

blank?はご存じ、「空ならtrueを返す」便利メソッドです。

blank?
オブジェクトが空ならtrueを、そうでなければfalseを返す
present?
!blank?を返す
presence
blank?ならnilを、そうでなければselfを返す

blank?は基本的に以下のような仕組みです。

  • empty?が定義されているとき: empty?を呼び出す
  • empty?が定義されていないとき: !selfを返す

たとえば[]にはempty?があるので、空配列はblank?がtrueになります。
nilにはempty?がないので、!selfを評価した結果blank?はtrueになります。
Object.newにはempty?がないので、!selfを評価した結果blank?はfalseになります。

実際にはArrayやNilClassなどには高速化のため別定義がしてあります。

ここで、Stringだけは違うルールでblank?が個別定義されているので注意が必要です。

Stringにはempty?が存在しますが、これは空文字であるかどうかを調べるメソッドです。
String#blank?はこれを使わず、「空白文字は空と見なす」として動作します。
この「空白文字」の定義がバージョンによって異なります。

# ActiveSupport 3.1.0
def blank?
  self !~ /\S/
end
# ActiveSupport 3.2.0
NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
def blank?
  # 1.8 does not takes [:space:] properly
  if encoding_aware?
    self !~ /[^[:space:]]/
  else
    self !~ NON_WHITESPACE_REGEXP
  end 
end
# ActiveSupport 4.0.0
def blank?
  self !~ /[^[:space:]]/
end

3.1までは/\S/が含まれていれば「空ではない」と見なされました。
つまり、「半角スペース」「タブ」「CR」「LF」の4文字以外は空白でないとされ、全角スペースは有効な文字とされていました。

3.2以降では[:space:]というPOSIX文字クラスに変更されています。
この文字クラスには全角スペースなども含まれるため、全角スペースのみの文字列にblank?を実行すると、

  • 3.1まで: false
  • 3.2から: true

という結果になります。

ちなみに[:space:]文字クラスの詳細は、RubyDocによると鬼車を参照しろと書いてあります。
http://www.geocities.jp/kosako3/oniguruma/doc/RE.txtによると、厳密には以下の文字になっています(Unicodeの場合)。

Space_Separator | Line_Separator | Paragraph_Separator |
0009 | 000A | 000B | 000C | 000D | 0085

Space_SeparatorはUnicodeのZsカテゴリ、Line_SeparatorはZlカテゴリU+2028だけ)、Paragraph_SeparatorはZpカテゴリ(U+2029だけ)です。

Unicodeで規定されている空白文字はすべてblank?になるようになっています。

4.0で挙動が変わったtry

基本メソッドtryも、4.0で挙動が変わりました。

# ActiveSupport 3.2.0
def try(*a, &b)
  if a.empty? && block_given?
    yield self
  else
    __send__(*a, &b)
  end
end
# ActiveSupport 4.0.0
def try(*a, &b) 
  if a.empty? && block_given?
    yield self
  else
    public_send(*a, &b) if respond_to?(a.first)
  end 
end 

def try!(*a, &b) 
  if a.empty? && block_given?
    yield self
  else
    public_send(*a, &b) 
  end 
end

主な違いは2点です。

sendがpublic_sendになった
直接呼び出すのと対称性が高まりました。privateメソッドを呼び出せることを期待していたコードは動かなくなります。
respond_to?チェックをするようになった
従来のtryはtry!になり、tryではrespond_to?チェックが事前に実行されます。4.0のtryでは、存在しないメソッドを指定してもNoMethodErrorは発生せず、何もしません。

まとめ

Ruby界隈は良くも悪くも進歩と仕様変更が早いので、常に最新ウォッチが大事ですね。
更新情報はCHANGELOGをブックマークに入れておくのがたぶん手っ取り早いと思います。幸いテストがしっかりしているので、挙動が分からなければそちらを見ればOKです。

あと、Rails以外でRubyを使うときも、必ずgemはbundleで管理し、Gemfile.lockをリポジトリに入れておく方が良いと思います。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。