instance_evalでインスタンスに機能を追加する

2 min read読了の目安(約1800字

概要

Rubyの「instance_eval」を使って、
インスタンスに対して動的に機能を付与してみる。

環境

  • macOS Catarina
  • ruby 3.0.0

instance_evalとは

引数で与えた文字列をRubyプログラムとして評価し、
その呼び出し元のインスタンスに評価した処理を実行できるメソッドである。

https://docs.ruby-lang.org/ja/latest/method/BasicObject/i/instance_eval.html

具体例として、以下のようになる。

class Sample
  def initialize
    @x = 10
  end
end

sample = Sample.new

p sample   #<Sample:0x00007fe6ed9e6868 @x=10>

sample.instance_eval "@y = 20"

p sample   #<Sample:0x00007fe6ed9e6868 @x=10, @y=20>

この具体例のように、1回目のsample(Sampleクラスのインスタンス)の出力では、
@xのみ保持しているが、そのあとにinstance_evalで@yを格納しており、
2回目のsampleの出力では、@yもインスタンス変数として保持できている。

instance_evalは、そのレシーバとなったインスタンスに対してのみ、
評価したプログラムを適用する。
そのため、インスタンスごとに固有の処理を追加できるイメージである。

利用例

例えば、ゲームなどでキャラクター固有の処理を動的に読み込ませて処理できるようにする、
というようなことができるのではと思う。

ゲームの各キャラクターの固有処理は、それぞれファイルに記述しておき、
ゲームの進行中にそのファイルを読み込みinstance_evalで評価&インスタンスに付与、
といったイメージである。

具体例として、以下のようになる。

character.rb

class Character
  def initialize
    ...
  end
  
  # ここに共通処理を実装するのもいい.
  ...
end

c1 = Character.new
c1.instance_eval File.read './characters/chara_1.rb'

p c1
c1.ability

c2 = Character.new
c2.instance_eval File.read './characters/chara_2.rb'

p c2
c2.ability

./characters/chara_1.rb

@name = "ブレイブリーファルコン"
@power = 5
@health = 6

def ability
  p "#{@name} executes own ability."
  p "ブレイブショット"
end

./characters/chara_2.rb

@name = "見習い剣士"
@power = 2
@health = 3

def ability
  p "#{@name} executes own ability."
  p "クリティカルバスター"
end

実行結果

❯❯❯ ruby character.rb
#<Character:0x00007fe02b905cc8 @name="ブレイブリーファルコン", @power=5, @health=6>
"ブレイブリーファルコン executes own ability."
"ブレイブショット"
#<Character:0x00007fe02b904288 @name="見習い剣士", @power=2, @health=3>
"見習い剣士 executes own ability."
"クリティカルバスター"

このように、それぞれのキャラクターの設定ファイル(ステータスやアビリティ内容)を読み込み、それぞれのインスタンスで固有の処理が実行できている。