3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby】instance_evalとclass_evalって何が違うんだヨォ...

Last updated at Posted at 2024-10-21

どうもこんにちは。

今回は、instance_evalclass_evalの違いがわからなかったので、調べました。

そもそもこいつらいつ使う?

instance_evalclass_evalは、動的にメソッドや属性を追加したり、クラスやインスタンスの内部状態に直接アクセスする時に使用されます。

Railsで使用することはある?

自分の感覚としては、Railsでアプリ開発を行う場合にinstance_evalclass_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_evalinstance_evalを使っています。例えば、before_saveafter_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_evalclass_evalを使用することはあまりないかなぁと思います。

もしかしたら、Rubyのライブラリ開発をする時に使用するかもしれないですね!

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?