rubyのclass_evalのやっていること

2022.07.01

class_evalとは

モジュールのコンテキストで文字列 expr またはモジュール自身をブロックパラメータとするブロックを評価してその結果を返します。

Ruby 3.1 リファレンスマニュアル

クラスのインスタンスメソッドを定義できる。またクラスインスタンス変数を定義できる。

インスタンスメソッドとは・・インスタンスから使用できるメソッド。

特徴

クラスインスタンス変数を変更、追加できる。インスタンス変数は変わらないこと。

ちなみにクラスインスタンス変数はクラスメソッドで使用可能(例: self.fuga)。インスタンスメソッドでは動かない。

class Hoge
  @y = 10
  def initialize
    @x = 1
  end
end

Hoge.class_eval{@x = 2}
Hoge.class_eval{@y = 100}
p Hoge.instance_variables #=> [:@x, :@y]
p Hoge.instance_variable_get(:@x) #=> 2
p Hoge.instance_variable_get(:@y) #=> 100  クラスインスタンス変数は変わる
h = Hoge.new
p h.instance_variable_get(:@x) #=> 1    インスタンス変数は変わらない 
p h.instance_variable_get(:@y) #=> nil オブジェクトからクラスインスタンス変数は呼び出せない 

外側で定義した変数をクラスの中で呼び出すことができる。

# 通常は呼び出せない
x = 1
class A
 @x = x
end
p A.instance_variables
#=> undefined local variable or method `x' for A:Class (NameError)
#Aクラスのインスタンス変数として呼び出せる
x = 1
class A
end
A.class_eval do
 @x = x
end
p A.instance_variables
#=> [:@x]

プライベートメソッドは呼び出せない。

class Hoge
  private
  def p_m
    p "ハロー"
  end
end
Hoge.class_eval{p_m} #=> undefined local variable or method `p_m' for Hoge:Class (NameError)

instance_evalとの違い

  • instance_eval・・1つのオブジェクトに対してのみメソッドを定義できる。特異メソッド。プライベートメソッド呼び出し可。インスタンス変数を作る。
  • class_eval・・作られるオブジェクト全てに対してメソッドを定義できる。インスタンスメソッド。プライベートメソッド呼び出し不可。クラスインスタンス変数を作る。

instance_evalの場合オブジェクトに対してメソッドを定義する特異メソッドの形になる。他のオブジェクトから呼べない。

class Breakfast
 def initialize(main)
  @main = main
 end
end

b = Breakfast.new("a")
b2 = Breakfast.new("a")
b.instance_val do
 def eat
  p "#{@main}を食べる"
 end 
end

b.eat
#=> "aを食べる"

b2.eat
#=> undefined method `eat' for #<Breakfast:@main="a"> (NoMethodError)

class_evalの場合クラスのインスタンスメソッドとして定義できる。他のオブジェクトから呼べる。

class Breakfast
 def initialize(main)
  @main = main
 end
end

b = Breakfast.new("a")
b2 = Breakfast.new("a")
Breakfast.class_val do
 def eat
  p "#{@main}を食べる"
 end 
end

b.eat
#=> "aを食べる"

b2.eat
#=> "aを食べる"

まとめ

class_evalはクラスにインスタンスメソッドを定義する。外側の変数をクラスの中で使うようにすることも可能。

class_evalがクラスメソッドで使用できるクラスインスタンス変数を作成していることに注意。インスタンス変数ではない。

クラスの外にある変数を使ってそのクラスのインスタンスメソッドを使用したいときに使えそう。

プライベートメソッドの呼び出しはclass_evalはしない。

instance_evalとは別物。