LoginSignup
2
2

More than 5 years have passed since last update.

instance_evalの第2引数でファイル名を与える

Posted at

RubyでBasicObject#instance_eval()には第2引数にファイル名を渡すことができる。

サンプルコード

例えばConfigクラスがあり、DSLで書かれたコンフィグファイルをロードするようなコードを書いたとする。

config.rb
class Config
  def initialize
    @config = {}
  end

  def load
    instance_eval File.read('./conf/setting.rb')
  end

  def set(key, value)
    @config[key] = value
  end

  def fetch(key)
    @config[key]
  end
end
conf/setting.rb

set :app, 'My App'
set :url, 'http://example.com'
set :log_leve, :info
bin/runner
require_relative '../config'
config = Config.new
config.load
p config.fetch(:url) # => "http://example.com"

サンプルコードの問題点

上のコードにはinstance_evalに関連して以下の問題がある

  1. conf/setting.rbの中でユーザ(DSLを利用するユーザ)がタイポするとエラーメッセージが分かりづらい
  2. conf/setting.rbの中でユーザが任意のコードを書き__FILE__ディレクティブを使うと動かない

1についてはconf/setting.rbの中でタイポしても、コンソールに表示されるエラーメッセージはconfig.rb:7:in `instance_eval': undefined method `settt' for #<Config:0x007f8c6a88af88 @config={}> (NoMethodError)こうなり一見どこでタイポしたか分からない。そうではなくタイポしたファイル名 + 行番号 conf/setting.rb:行番号 を表示してほしい

2についてはconf/setting.rbの中で__FILE__の値はsetting.rbではなくinstance_evalをcallしたconfig.rbになってしまう。

そこでinstance_evalの第2引数にファイル名を渡しておけば良い。

config.rb
class Config
  ..snip..

  def load
    path = File.expand_path('./conf/setting.rb')
    instance_eval File.read(path), path
  end

  ..snip..
end
2
2
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
2
2