Ruby instance_eval、class_eval、module_eval

2021年4月27日火曜日

Ruby

t f B! P L

instance_eval

レシーバとなるインスタンスのコンテキスト(特異クラス)でブロック(文字列)の処理を評価する。
レシーバをselfとして評価するので、privateメソッドやインスタンス変数にもアクセス可能。

class C
  def initialize
    @hoge = 'hoge'
  end
end

c = C.new

c.instance_eval do
  p self 
  # => #<C:0x00007f9fe61fda78 @hoge="hoge">
  def hoge
    @hoge
  end
end

c.hoge
=> "hoge"

引数がブロックか文字列かで定数やローカル変数のスコープが変わる。


top = 'トップレベルのローカル変数だよ'

class C
  c_top = 'Cのローカル変数だよ'
end

c = C.new
# ブロックの場合スコープはinstance_evalの外側になる。この場合トップレベル
c.instance_eval do
  p Module.nesting # ネストの状態はトップレベル
  # => []
  p top
  # => "トップレベルのローカル変数だよ"
  p c_top # クラスCはスコープ外
  # => NameError
end

# 特異メソッド定義
def c.hoge
  p 'cの特異メソッドだよ'
end

# 文字列の場合スコープは特異クラスにいる
c.instance_eval <<-eval
  p Module.nesting  # ネストの状態は特異クラスにいる
  # => [# <Class:# <C:0x00007f9fe61fda78>> ]
  p c_top # 特異クラス内のため、クラスCはスコープ外
  # => NameError
  hoge # 特異メソッドを呼び出せる
  # => "cの特異メソッドだよ"
eval

class_eval(module_eval)

クラスのコンテキストでブロック(文字列)を評価する。インスタンスメソッドやクラスメソッドを定義したりできる。
module_evalはclass_evalの別名。

class C; end

C.class_eval do
  def self.call_method
    p 'クラスメソッドだよ'
  end
end

C.call_method
=> "クラスメソッドだよ"

引数がブロックか文字列かで定数やクラス変数のスコープが変わる。

TOPCONST = 'トップレベルの定数だよ'

class C
  CCONST = 'Cの定数だよ'
end

# ブロックの場合スコープはclass_evalの外側になる。この場合トップレベル
C.class_eval do
  p Module.nesting # ネストの状態はトップレベル
  # => []
  p TOPCONST
  # => "トップレベルの定数だよ"
  p CCONST # クラスCはスコープ外
  # => NameError
end

# 文字列の場合スコープはclass_evalで評価したclass
C.class_eval <<-eval
  p Module.nesting # ネストの状態はクラスC
  # => [C]
  p CCONST
  # => "Cの定数だよ"
eval

eval

受け取った文字列をRubyコードとして実行する

eval('def huga; p 1 end')
huga
=> 1

自己紹介

Webエンジニアをやっています。日々思ったことや、読書レビュー、IT系の記事などを書き連ねています

広告

[書籍] 世界一楽しい決算書の読み方感想まとめ

  こういう人におすすめ 決算書を読もうとして挫折した人 企業分析したい投資家 会社で経営企画担当、管理職などのポジションの人 概要 著者はTwitterで会計クイズを行なっている 「大手町のランダムウォーカー」さん 。 「日本人全員が財務諸表を読める世界を創る」 を合言葉にして...

QooQ