Как мне обойти eval в Ruby и «уже инициализированную константу»? - PullRequest
1 голос
/ 23 декабря 2011

Я унаследовал обслуживание в приложении, которое использует eval() как способ оценки правил, написанных в коде Ruby, в механизме правил.Я знаю, что есть много других способов сделать это, но кодовая база на данный момент довольно большая, и изменение ее на что-то другое было бы непосильным по времени;поэтому предположим, что я застрял, используя eval() на данный момент.

Как написано, правила обычно вызывают некоторые из тех же объектов из базы данных, что и друг другу, и автор правил дал переменным в правилаходинаковые имена друг с другом.Это приводит к появлению страниц и страниц предупреждений «уже инициализированной константы» в консоли во время разработки.

Мне интересно несколько вещей:

Во-первых, если чувствует, что как будто это замедляет выполнение программы в среде разработчика, и поэтому яинтересно, сильно ли это сказывается на производительности в производственной среде, в частности, когда всплывают эти предупреждения, а не сам eval(), который, как я знаю, является хитом.

Во-вторых, есть ли способ «пространства имен» выполнениякаждого правила, чтобы он не определял свои переменные в той же области, что и все другие ошибки в запросе, чтобы избежать появления этого предупреждения повсеместно?Я знаю, что могу переписать все правила, чтобы использовать синтаксис ||= или проверить, было ли уже определено имя, но их довольно много, поэтому я бы предпочел сделать это из кода, который запускает eval()., если возможно.

** обновление с примером правила ** У вопроса есть правило о том, когда оно должно отображаться пользователю.Например, если пользователь заявил, что он живет в квартире, может потребоваться показать другой вопрос, чтобы спросить, каков размер жилого дома.Таким образом, rule_text второго вопроса может выглядеть так:

UserLivesInApartment = Question.find_by_name "UserLivesInApartment"
UserLivesInApartment.answer_for(current_user)

Код, который вызывает eval, гарантирует, что в области видимости есть переменная current_user до оценки.

1 Ответ

1 голос
/ 23 декабря 2011

э-э, возможно, не самый золотой стандарт.Возможно, вы могли бы запустить экземпляр drb и ​​запустить его в этом, а не в eval, таким образом, вы бы имели хоть какой-то контроль над происходящим и не загрязняли ваше собственное пространство имен.

http://segment7.net/projects/ruby/drb/introduction.html


Редактировать: добавлен еще один ответ для запуска кода в том же процессе:

Я не знаю, как выглядит ваш код правила, но может быть возможно обернуть модуль вокруг него:

# create a module
module RuleEngineRun1;end
# run code in module
RuleEngineRun1.module_eval("class Foo;end")

# get results
#....

# cleanup
Object.send(:remove_const, :RuleEngineRun1)

Вы также можете создать анонимный модуль с Module.new {#block, чтобы быть модулем eval'd}, если вам нужно запустить код параллельно.


В последующих рубинахвы можете добавить -W0 для запуска вашего кода без вывода предупреждений, но это делает возможные ошибки незамеченными:

$ cat foo.rb 
FOO = :bar
FOO = :bar

$ ruby foo.rb 
foo.rb:2: warning: already initialized constant FOO

$ ruby -W0 foo.rb 

Вы также можете запустить eval внутри блока Kernel.silence_warnings, но это может привести к разрушительным последствиям.если вы действительно столкнулись с некоторыми реальными проблемами с кодом eval'd, см. Подавлять предупреждения Ruby при запуске specs

...