普段仕事で使っている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?
を探してみます
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?
を探してみます
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?
を探してみます
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?
を探してみます
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
が存在しないか確認してみます
他の実装はありませんでした
読んでみて
安全なダックタイピングをするために acts_like_xxx
が実装されているのは知りませんでした
ダックタイピングをする場合には、このメソッドを活用していければと思います