Проблема связана с тем, как Ruby работает с экземплярами и локальными переменными. Происходит то, что вы устанавливаете локальную переменную в своем блоке instance_eval, а не используете метод доступа ruby.
Это может помочь объяснить это:
class Foo
attr_accessor :bar
def input_local
bar = "local"
[bar, self.bar, @bar, bar()]
end
def input_instance
self.bar = "instance"
[bar, self.bar, @bar, bar()]
end
def input_both
bar = "local"
self.bar = "instance"
[bar, self.bar, @bar, bar()]
end
end
foo = Foo.new
foo.input_local #["local", nil, nil, nil]
foo.input_instance #["instance", "instance", "instance", "instance"]
foo.input_both #["local", "instance", "instance", "instance"]
Способ работы блоков заключается в том, что они различают локальные переменные и переменные экземпляра, но если локальная переменная не определена, когда вызывается ее читатель, класс по умолчанию использует переменную экземпляра (как в случае вызова input_instance в моем пример).
Есть три способа получить желаемое поведение.
Использовать переменные экземпляра:
class Foo
attr_accessor :bar
def evaluate(&block)
instance_eval &block
end
end
foo = Foo.new
foo.evaluate do
@bar = "instance"
end
foo.bar #"instance"
Использовать собственную переменную:
class Foo
attr_accessor :bar
def evaluate(&block)
block.call(self)
end
end
foo = Foo.new
foo.evaluate do |c|
c.bar = "instance"
end
foo.bar #"instance"
Использовать функции установки:
class Foo
attr_reader :bar
def set_bar value
@bar = value
end
def evaluate(&block)
instance_eval &block
end
end
foo = Foo.new
foo.evaluate do
set_bar "instance"
end
foo.bar #"instance"
Все эти примеры устанавливают для foo.bar значение "instance".