普段仕事で使っているRuby on Railsですが、ソースコードを読む機会もなかなかないので、試しにやってみることにしました
読めるようにするまで
以前書いた記事に読めるようにするまでの設定を画像キャプチャ付きで解説しましたので、よろしければこちらをご参照下さい
shitake4.hatenablog.com
読んだ箇所
仕事でもたまに使われてたりする class_eval
を今日は読んでみようと思います
どんな使い方だっけ?
読んでみる前にまずは使い方を調べてみます
Ruby on RailsのAPIドキュメントを見てみると
class_eval on an object acts like singleton_class.class_eval.
引用:Ruby on Rails API:class_eval
調べてみてもどんな動きをするのか分かりませんでした
singleton_class.class_eval
のように振る舞うそうなので、調べてみます
まずはsingleton_classです
singleton_classメソッドは、オブジェクトの特異クラスを返します。
小さい整数(Fixnum)およびシンボルに対してsingleton_classを呼び出すと、例外TypeErrorが発生します。true、false、nilに対して呼び出すと、特異クラスではなくTrueClass、FalseClass、NilClassを返します。
次の例では、オブジェクトcatの特異クラスをsingletonに取り出し、define_methodで特異メソッドを定義しています。
引用:Rubyリファレンス:singleton_class
次にclass_evalです
class_evalメソッドは、ブロックをクラス定義やモジュール定義の中のコードであるように実行します。ブロックの戻り値がメソッドの戻り値になります。
引用:Rubyリファレンス:class_eval
使い方はこんなかんじです
class User attr_accessor :name def initialize(name) @name = name end [:downcase, :upcase].each do |method| class_eval <<-EOS def #{method} @name.#{method} end EOS end end user = User.new("taro") puts user.upcase ### 引用:[Rubyリファレンス:class_eval](https://ref.xaio.jp/ruby/classes/module/class_eval)
eachでdowncase,upcaseを回し、downcase,upcaseを定義しています
[:downcase, :upcase].each do |method| class_eval <<-EOS def #{method} @name.#{method} end EOS end
#{}
で変数の値を埋め込んでいるので、downcaseの場合はこんな感じで定義されているということです
class_eval <<-EOS def downcase @name.downcase end EOS
つまり、特異クラスに対して、与えられたブロックをクラス定義の中のコードであるように実行することが出来るということです
ソースコードを読んでみる
1. railsプロジェクトのactivesupportにある機能なので、activesupportディレクトリのlib配下で def class_eval
を探してみます
2. 該当箇所が1箇所だったので、それを見てみます
1. activesupport > lib > active_support > core_ext > kernel > singleton_class.rb
# frozen_string_literal: true module Kernel # class_eval on an object acts like singleton_class.class_eval. def class_eval(*args, &block) singleton_class.class_eval(*args, &block) end end
これを見てみるとsingleton_class.class_eval(*args, &block)
を呼び出しているだけなので、このメソッドいるのかな?と思えてしまいます
なので、コミットログで現在に至るまでの経緯を見てみます
こちらがコミットログです
ruby1.9.2でsingleton_classが実装されたようです
その為、それ以前のバージョンにも対応出来るようにsingleton_class
メソッドが記述されていたようです
一応singleton_classの実装がRuby1.9.2で行われているかリポジトリのチェンジログ等で確認してみます
RubyのGithubのdoc配下にNEWS-1.9.2があったので見てみると
読んでみて
class_evalは使い方だけ覚えて使っていたけど、しっかり実装を追ってみたからこそわかることがあったので、引き続き続けていきます