Не могли бы вы объяснить, что именно вы пытаетесь сделать? Пожалуйста, предоставьте код, показывающий, как вы хотите, чтобы он работал. Там может быть лучший и безопасный способ выполнить то, что вы хотите.
Я собираюсь попытаться угадать ваш типичный вариант использования.
Учитывая хэш:
{: a => 11,: b => 22}
Вам нужна минимальная, относительно изолированная среда выполнения, в которой вы можете получить доступ к значениям хеш-функции как локальным переменным. (Я не совсем уверен, почему вам нужно, чтобы они были местными, за исключением, может быть, если вы пишете DSL или у вас уже есть написанный код, который вы не хотите адаптировать для доступа к Hash.)
Вот моя попытка. Для простоты я предполагаю, что вы используете только символы в качестве хэш-ключей.
class MyBinding
def initialize(varhash); @vars=varhash; end
def method_missing(methname, *args)
meth_s = methname.to_s
if meth_s =~ /=\z/
@vars[meth_s.sub(/=\z/, '').to_sym] = args.first
else
@vars[methname]
end
end
def eval(&block)
instance_eval &block
end
end
Пример использования:
hash = {:a => 11, :b => 22}
mb = MyBinding.new hash
puts mb.eval { a + b }
# setting values is not as natural:
mb.eval { self.a = 33 }
puts mb.eval { a + b }
Некоторые предостережения:
1) Я не вызывал NameError, если переменная не существовала, но простая замена исправляет это:
def initialize(varhash)
@vars = Hash.new {|h,k| raise NameError, "undefined local variable or method `#{k}'" }
@vars.update(varhash)
end
2) Обычные правила области видимости таковы, что если существует локальный объект, имя которого совпадает с именем метода, он имеет приоритет, если вы явно не выполняете вызов метода, например a (). Класс выше имеет противоположное поведение; метод имеет приоритет. Чтобы получить «нормальное» поведение, вам нужно скрыть все (или большинство) стандартных методов, таких как #object_id. ActiveSupport предоставляет класс BlankSlate для этой цели; он сохраняет только 3 метода:
__send__, instance_eval, __id__
. Чтобы использовать его, просто сделайте MyBinding наследоваться от BlankSlate.
Помимо этих 3 методов, вы также не сможете иметь локальные имена с именем "eval" или "method_missing".
3) Установка локального значения не так естественна, потому что для получения вызова метода требуется "self". Не уверен, что есть обходной путь для этого.
4) Блок eval может связываться с хэшем @vars.
5) Если у вас есть реальная локальная переменная в области действия, в которой вы вызываете mb.eval, с тем же именем, что и у одного из хеш-ключей, реальный локальный будет иметь приоритет ... это, вероятно, самый большой недостаток, потому что тонкий ошибки могут закрасться.
После осознания недостатков моей техники, я рекомендую использовать Hash напрямую, чтобы сохранить набор переменных, если только я не увижу причину. Но если вы все еще хотите использовать нативный eval, вы можете повысить безопасность, используя Regexps, чтобы избежать внедрения кода, и «локально» установив значение $ SAFE для вызова eval выше, используя Proc, например:
proc { $SAFE = 1; eval "do_some_stuff" }.call # returns the value of eval call