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系の記事などを書き連ねています

Rails Twitter認証で困ったこと

困った1:認証動作に差異 qiitaと実装したアプリで認証の動きが違った。 ちなみに実装したアプリはこれ ツイバト qiitaでは 毎回認証確認の画面になる 実装アプリでは 一度ログインしたことがあったら一瞬認証画面になって即リダイレクトして戻ってくる 原因 認証のメソッドが違っ...

QooQ