どうもこんにちは。
今回は、instance_eval
とclass_eval
の違いがわからなかったので、調べました。
そもそもこいつらいつ使う?
instance_eval
とclass_eval
は、動的にメソッドや属性を追加したり、クラスやインスタンスの内部状態に直接アクセスする時に使用されます。
Railsで使用することはある?
自分の感覚としては、Railsでアプリ開発を行う場合にinstance_eval
とclass_eval
は使用しないです。どちらかというと、Railsの内部動作に使用されているようです。
1. ActiveRecordでのclass_evalの使用
これが一番馴染み深いかもです。
Railsでデータを登録するときは、User.name = '試験太郎'
というように記述したりします。
このコードが動作している背景として、class_eval
が使用されているようです。
User.class_eval do
def name
# データベースから値を取得
end
def name=(value)
# データベースに値をセット
end
end
2. Rails ルーティング定義の時にinstance_evalを使用
Railsのルーティングを定義する場合、以下のように記述すると思います。
Rails.application.routes.draw do
resources :users
end
draw
メソッドの中でinstance_eval
が使われており、ブロックの中でメソッドを定義することで簡単にルーティングを定義することができます。
3. ActiveSupport::Concernでのclass_evalの使用
ActiveSupport::Concern
は、Railsでモジュールを簡単に拡張できる仕組みを提供しています。
このモジュールでは、includedブロック内でclass_evalが使われており、モジュールが含まれたクラスに対して追加のメソッドを動的に定義しています。
module MyModule
extend ActiveSupport::Concern
included do
# class_evalが内部で使われることで、含まれたクラスに対してコードが追加される
has_many :comments
end
def my_method
puts "This is a method from MyModule"
end
end
class MyClass < ApplicationRecord
include MyModule
end
4. Validationやコールバックの定義での使用
Railsのバリデーションやコールバックも、クラスやインスタンスに対して動的にメソッドを定義するためにclass_eval
やinstance_eval
を使っています。例えば、before_save
やafter_create
といったコールバックメソッドを定義する際、これらのメソッドはクラスに追加されます。
class User < ApplicationRecord
before_save :do_something
validate :validate_custom
private
def do_something
# コールバック用のロジック
end
def validate_custom
# カスタムバリデーションのロジック
end
end
具体的な動作
ここからは具体的な動作を見ていきます。
instance_eval
instance_eval
は、特定のインスタンスに対してブロックまたは文字列を評価し、そのインスタンスのコンテキストで動作します。用途は以下です。
-
インスタンスのプライベートメソッドや属性にアクセスするため
- インスタンスの外部からは通常アクセスできないプライベートメソッドや変数にアクセスできるようになります
-
特定のインスタンスにのみメソッドを追加する場合
- クラス全体ではなく、特定のオブジェクトに対してだけメソッドを動的に追加することができます
class MyClass
def initialize
@name = "Hello"
end
private
def greet
"Greetings!"
end
end
obj = MyClass.new
sub_obj = MyClass.new
obj.instance_eval do # インスタンスに対して`instance_eval`を使用している!
@name # => "Hello" (インスタンス変数に直接アクセス)
greet # => "Greetings!" (プライベートメソッドにアクセス)
def custom_method # インスタンスに対して`custom_method`を定義
@name = "こんにちは"
end
end
ポイントとして、obj = MyClass.new
を実行した後にインスタンスobj
に対してinstance_eval
メソッドを実行しています。
また、instance_eval
ブロックの中でcustom_method
メソッドを定義しています。
これによって、obj
インスタンスからはcustom_method
メソッドを呼び出すことができますが、sub_obj
インスタンスからはcustom_method
メソッドを呼び出すことができません。
class_eval
class_eval
は、クラスやモジュールのコンテキストでブロックや文字列を評価し、そのクラスやモジュールの定義を動的に変更する際に使用します。
-
クラスに対してメソッドを動的に追加する場合
- 既存のクラスにメソッドを追加したり、クラスの内部状態にアクセスしたりできます
-
プライベートメソッドやインスタンス変数にクラスレベルでアクセスする場合
- class_evalを使うことで、クラス内のプライベートメソッドや変数にアクセスできます
class MyClass
def initialize
@name = "Hello"
end
private
def greet
"Greetings!"
end
end
MyClass.class_eval do # クラスに対して`class_eval`を実行している!
def new_method
"This is a new method!"
end
end
obj = MyClass.new
obj.new_method # => "This is a new method!"
ポイントとして、obj = MyClass.new
を実行する前にMyClass
に対してclass_eval
を実行しています。こうすることで、全てのMyClass
のインスタンスからnew_method
メソッドにアクセスすることができます。
まとめ
Rails開発をしている自分としては、内部動作を勉強する良い機会となりました。
ただ、コーディング中にinstance_eval
やclass_eval
を使用することはあまりないかなぁと思います。
もしかしたら、Rubyのライブラリ開発をする時に使用するかもしれないですね!