2012-01-26 74 views
3

我想運行字符串的eval來定義局部變量和常量。 我想在不同的命名空間中做到這一點。我可以用局部變量 而不是常量來做到這一點。有沒有辦法修改 以下的NameSpaces模塊,以便一個綁定/命名空間中定義的常量不會被另一個看到?Ruby綁定 - 局部變量的範圍與常量

# Example run under ruby 1.9.1 
module NameSpaces 
    def self.namespace(namespace) 
    return binding 
    end 
end 

b1 = NameSpaces.namespace("b1") 
b2 = NameSpaces.namespace("b2") 

# Set a 'x', then check to make sure its still set in the scope of 'b1' 
puts b1.eval("x = 1") # => 1 
puts b1.eval("x")  # => 1 

# Check to make sure 'x' is NOT set in the scope of 'b2' 
begin 
    puts b2.eval("x") # NameError exception expected here 
rescue Exception => e 
    puts e.to_s  # => undefined local variable or method `x' 
        # for NameSpaces:Module (THIS IS AS EXPECTED.) 
end 

# Set constant 'C' and do the same checks 
puts b1.eval("C = 1") # => 1 
puts b1.eval("C")  # => 1 

# Check to make sure 'C' is NOT set in the scope of 'b2' 
begin 
    puts b2.eval("C") # (I DON'T GET AN EXCEPTION. NOT AS I HAD HOPED FOR.) 
rescue Exception => e 
    puts e.to_s 
end 

非常感謝您的期待。我很困難。

回答

5

你正在觀察的行爲是正常的。當您在NameSpaces.namespace的呼叫範圍內執行C = 1時,在NameSpaces上定義了常量「C」。 (您可以通過嘗試NameSpaces::C來確認。)

要獲得您想要的效果,您需要使用匿名模塊的綁定。嘗試:

namespace1 = Module.new.class_eval("binding") 
namespace2 = Module.new.class_eval("binding") 
namespace1.eval("C = 1") 
namespace1.eval("C") 
=> 1 
namespace2.eval("C") 
NameError: uninitialized constant #<Module:0xf09180>::C 

注意,其在對象定義的任何常數(即,全球範圍內)將被傳遞到eval代碼中可用的,並且如果這樣的常數的值在所評估的代碼改變時,改變將在全球可見!

(即使你評估一個BasicObject的背景下,代碼不從Object繼承,評估的代碼可以仍然訪問常量的定義對象,用前綴名稱「::」)

+0

謝謝亞歷克斯。這是信息。我可能會錯過一些東西,但我認爲這種類型的評估失敗了我的第一個局部變量測試用例。我認爲,使用這種方法,我不得不解析傳遞給我的程序的表達式,以確定一種特定的eval形式。我希望的程序將獲取所有最終用戶提供的輸入,並將其解析爲表達式,但在用戶自己的名稱空間而不是程序中。 – dinman2022

+0

@ dinman2022,實際上,我用局部變量對它進行了測試,它可以工作。 –

+0

我不希望當地人在通話之間超出範圍。例如:\'MyClass類 DEF初始化 @spaces = {} self.namespace = '默認' 端 DEF命名空間=(ID) @namespace = @ spaces.has_key(ID)? @spaces [ID]:Module.new 端 DEF do_me(EXP) 如果EXP =〜/ ^命名空間/ self.namespace = exp.split [1] 別的 @ namespace.class_eval(EXP) 端 端 端 輸入=%q { X = 1 X#=> DIES該處 命名空間new_one X#應該失敗 } C = MyClass.new input.each_line {| L | 開始 提出c.do_me(升) 救援異常=>電子 提出Ë 端 }' – dinman2022