Railsのソースコード読んでみる | Active Support acts_like?編

f:id:sktktk1230:20180726124729p:plain

普段仕事で使っているRuby on Railsですが、ソースコードを読む機会もなかなかないので、試しにやってみることにしました

読めるようにするまで

以前書いた記事に読めるようにするまでの設定を画像キャプチャ付きで解説しましたので、よろしければこちらをご参照ください
shitake4.hatenablog.com

読んだ箇所

acts_like? を今日は読んでみようと思います

どんな使い方だっけ?

読んでみる前にまずは使い方を調べてみます
Railsの日本語ドキュメントを見てみると

2.7 acts_like?(duck)
acts_like?メソッドは、一部のクラスがその他のクラスと同様に振る舞うかどうかのチェックを、ある慣例に則って実行します。Stringクラスと同じインターフェイスを提供するクラスがあり、その中で以下のメソッドを定義しておくとします。
def acts_like_string?
end
このメソッドは単なる目印であり、メソッドの本体と戻り値の間には関連はありません。これにより、クライアントコードで以下のようなダックタイピングチェックを行なうことができます。
some_klass.acts_like?(:string)
RailsにはDateクラスやTimeクラスと同様に振る舞うクラスがいくつかあり、この手法を使用できます。
引用:ActiveSupport コア機能:acts_like?

レシーバのクラスが引数に入れたクラスと同じ振る舞いをするか確認するメソッドです
安全にダックタイピングする為、レシーバを確認したい場合などに利用します

ソースコードを読んでみる

1. railsプロジェクトのactivesupportにある機能ですので、activesupportディレクトリのlib配下で def acts_like? を探してみます

f:id:sktktk1230:20180111152611p:plain

2. 該当箇所が1箇所あったので、それぞれみてみます

activesupport > lib > active_support > core_ext > object > acts_like.rb
# frozen_string_literal: true

class Object
  # A duck-type assistant method. For example, Active Support extends Date
  # to define an <tt>acts_like_date?</tt> method, and extends Time to define
  # <tt>acts_like_time?</tt>. As a result, we can do <tt>x.acts_like?(:time)</tt> and
  # <tt>x.acts_like?(:date)</tt> to do duck-type-safe comparisons, since classes that
  # we want to act like Time simply need to define an <tt>acts_like_time?</tt> method.
  def acts_like?(duck)
    case duck
    when :time
      respond_to? :acts_like_time?
    when :date
      respond_to? :acts_like_date?
    when :string
      respond_to? :acts_like_string?
    else
      respond_to? :"acts_like_#{duck}?"
    end
  end
end

レシーバに acts_like_引数 メソッドが実装されているか確認しています
acts_like_引数 メソッドの内、time, date, stringはactivesupport内ですでに実装されているということがここらか分かりました
それ以外にも拡張することもできるようです

それでは acts_like_引数 はどう実装すればいいのか見てみます

3. まずはdef acts_like_time? を探してみます

f:id:sktktk1230:20180111152627p:plain

4. 該当箇所が4箇所あったので、それぞれ見てみます

そのままtrueで返しています

1. activesupport > lib > active_support > core_ext > date_time > acts_like.rb
# frozen_string_literal: true

require "date"
require "active_support/core_ext/object/acts_like"

class DateTime
  # Duck-types as a Date-like class. See Object#acts_like?.
  def acts_like_date?
    true
  end

  # Duck-types as a Time-like class. See Object#acts_like?.
  def acts_like_time?
    true
  end
end
2. activesupport > lib > active_support > time_with_zone.rb
# So that +self+ <tt>acts_like?(:time)</tt>.
def acts_like_time?
  true
end
3. activesupport > lib > active_support > core_ext > time > acts_like.rb
# frozen_string_literal: true

require "active_support/core_ext/object/acts_like"

class Time
  # Duck-types as a Time-like class. See Object#acts_like?.
  def acts_like_time?
    true
  end
end
4. activesupport > test > core_ext > object > acts_like_test.rb

テスト用に定義されたものの為、省略します

4. 次にdef acts_like_date? を探してみます

f:id:sktktk1230:20180111152644p:plain

5. 該当箇所が2箇所あったので、それぞれ見てみます

さきほどと重複になりますが、そのままtrueを返しています

1. activesupport > lib > active_support > core_ext > date_time > acts_like.rb
# frozen_string_literal: true

require "date"
require "active_support/core_ext/object/acts_like"

class DateTime
  # Duck-types as a Date-like class. See Object#acts_like?.
  def acts_like_date?
    true
  end

  # Duck-types as a Time-like class. See Object#acts_like?.
  def acts_like_time?
    true
  end
end
2. activesupport > lib > active_support > core_ext > date > acts_like.rb
# frozen_string_literal: true

require "active_support/core_ext/object/acts_like"

class Date
  # Duck-types as a Date-like class. See Object#acts_like?.
  def acts_like_date?
    true
  end
end

5. 最後にdef acts_like_string? を探してみます

f:id:sktktk1230:20180111152706p:plain

5. 該当箇所が2箇所あったので、それぞれ見てみます

さきほどと重複になりますが、そのままtrueを返しています

1. activesupport > lib > active_support > core_ext > string > behavior.rb
# frozen_string_literal: true

class String
  # Enables more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
  def acts_like_string?
    true
  end
end
2. guides > source > active_support_core_extensions.md

ソースコードではなくactivesupportのcore_extensionのドキュメントだったのでここでは省略します

6. 一応さきほど調べた以外の def acts_like_xxx が存在しないか確認してみます

他の実装はありませんでした
f:id:sktktk1230:20180111152718p:plain

読んでみて

安全なダックタイピングをするために acts_like_xxx が実装されているのは知りませんでした
ダックタイピングをする場合には、このメソッドを活用していければと思います