Railsのソースコード読んでみる | ActiveSupport class_eval編

f:id:sktktk1230:20190921180106p:plain

普段仕事で使っている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 を探してみます

f:id:sktktk1230:20180110160708p:plain

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) を呼び出しているだけなので、このメソッドいるのかな?と思えてしまいます
なので、コミットログで現在に至るまでの経緯を見てみます

こちらがコミットログです
f:id:sktktk1230:20180110160723p:plain

ruby1.9.2でsingleton_classが実装されたようです
その為、それ以前のバージョンにも対応出来るようにsingleton_class メソッドが記述されていたようです
f:id:sktktk1230:20180110160736p:plain

一応singleton_classの実装がRuby1.9.2で行われているかリポジトリのチェンジログ等で確認してみます
RubyのGithubのdoc配下にNEWS-1.9.2があったので見てみると
f:id:sktktk1230:20180110160752p:plain

読んでみて

class_evalは使い方だけ覚えて使っていたけど、しっかり実装を追ってみたからこそわかることがあったので、引き続き続けていきます