Постоянный поиск с instance_eval в Ruby 1.9 - PullRequest
3 голосов
/ 04 февраля 2011

Короткие и сладкие

Запуск этого кода в Ruby 1.9:

FOO = "global constant"

class Something
  FOO = "success!"

  def self.handle &block
    self.new.instance_eval &block
  end
end

class Other
  FOO = "wrong constant"

  def self.handle
    Something.handle{FOO}
  end
end

puts Something.handle{FOO}
puts Other.handle

Я получаю "успех!" и "неправильная константа". Как я могу получить оба звонка, чтобы напечатать «успех!»? Это не надуманное упражнение для развлечения - я бы не стал тратить на это время людей. У меня есть реальная проблема, и я сократил ее до простейшего возможного примера, который демонстрирует проблему. Продолжайте читать «почему».

Более подробное объяснение

Calling Something.handle {FOO} работает правильно. Ruby использует определение FOO, данное в классе Something. Однако, если я попытаюсь назвать его таким же образом из другого класса, это даст мне определение FOO для , что class.

Я думал, что идея более жестких постоянных поисков в Ruby 1.9 состояла в том, чтобы избежать подобных проблем. instance_eval должен использовать область своего получателя (в данном случае self.new), а не область вызывающего блока. Это работает для таких вещей, как переменные экземпляра, но не для констант. Это не проблема приоритета - удалите «глобальные» и «неправильные» константы, и ruby ​​по-прежнему не сможет найти оставшуюся правильную константу.

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

Это больно:

ThirdPartyApis::MyAnswerSite.connection question.id, answer.id do |question_id, answer_id|
  question = ThirdPartyApis::MyAnswerSite::Question.find question_id
  answer = ThirdPartyApis::MyAnswerSite::Answer.find answer_id

  ThirdPartyApis::MyAnswerSite::Solution.new question, answer
end

Это приятно:

ThirdPartyApis::MyAnswerSite.connection question.id, answer.id do |question_id, answer_id|
  question = Question.find question_id
  answer = Answer.find answer_id

  Solution.new question, answer
end

Краткое описание

Так что это многословное объяснение. Пожалуйста, не предлагайте обходные пути, которые не решают мой вопрос. Я ценю желание исследовать другие возможности, но мой вопрос прост: можно ТОЛЬКО изменить класс Something таким образом, чтобы это выглядело «успех!» дважды в конце? Это тестовый пример, и любое решение, которое подходит для этого, является моим принятым ответом, а его автор - мой личный герой на неделю. Пожалуйста!

1 Ответ

3 голосов
/ 04 февраля 2011

Я заставил его работать, используя драгоценный камень sourcify (gem install sourcify):

require 'sourcify'
FOO = "global constant"

class Something
  FOO = "success!"

  def self.handle &block
    (self.new.instance_eval(block.to_source)).call
  end

end

class Other
  FOO = "wrong constant"

  def self.handle
    Something.handle{FOO}
  end
end

puts Something.handle{FOO}
=> success!
puts Other.handle
=> success!

edit: К сожалению, вы могли видеть, где я тоже работал, используя привязки, удалили этот код.

...