短而甜
在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使用Something类中给出的FOO的定义。但是,如果我尝试从另一个类中以相同的方式调用它,它会为我提供 类的FOO的定义。
我认为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类,以打印“成功!”的方式。最后两次?这是测试案例,任何符合此要求的解决方案都是我接受的答案,其作者是本周的个人英雄。请!
答案 0 :(得分:3)
我使用sourcify gem(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!
编辑:哎呀,你也可以看到我在使用绑定的地方,删除了那段代码。