是';评估';在Ruby中与绑定对象交互的唯一方法是什么?

是';评估';在Ruby中与绑定对象交互的唯一方法是什么?,ruby,eval,scope,Ruby,Eval,Scope,我对Ruby还比较陌生,到目前为止,弄清楚如何使用Ruby是我最大的痛点之一。如果我正确阅读了文档,它们几乎完全是不透明的。要访问绑定对象内部的作用域,必须有一个Ruby代码字符串,并使用绑定来访问它 也许我只是一个来自不同学校的纯粹主义者,但一般来说,我对基于字符串的“eval”结构过敏。对于绑定对象,是否有任何方法可以安全地在一般情况下执行以下操作: 在绑定表示的上下文中列出作用域中的标识符,或检索内容的哈希 将绑定中局部变量的值设置为外部上下文中某个局部变量的值。理想情况下,即使该值是对象

我对Ruby还比较陌生,到目前为止,弄清楚如何使用Ruby是我最大的痛点之一。如果我正确阅读了文档,它们几乎完全是不透明的。要访问绑定对象内部的作用域,必须有一个Ruby代码字符串,并使用绑定来访问它

也许我只是一个来自不同学校的纯粹主义者,但一般来说,我对基于字符串的“eval”结构过敏。对于绑定对象,是否有任何方法可以安全地在一般情况下执行以下操作:

  • 在绑定表示的上下文中列出作用域中的标识符,或检索内容的哈希
  • 将绑定中局部变量的值设置为外部上下文中某个局部变量的值。理想情况下,即使该值是对象引用、文件句柄或其他复杂实体,也应能正常工作
  • (扩展名2:)给定散列,在绑定中为每个条目设置局部变量
  • 更好的是,给定一个hash,构建一个只包含基本语言构造和hash范围中的名称的绑定
  • 基本上,我想知道哪些是可能的,以及如何实现那些是可能的。我想每个问题的解决方案都会有相当密切的关系,这就是为什么我把所有这些都放在一个问题中


    或者,有没有类似于Perl的eval BLOCK语法的方法来评估已经在绑定上下文中解析过的代码?

    在搜索更多信息时,我找到了至少部分问题的答案:

    基于:

    剩下的是吉姆·舒伯特的有益建议之后的实验

  • 这可以通过
    eval
    -ing
    local\u variables
    instance\u variables
    global\u variables
    在绑定内部实现
  • 您可以按如下所述执行操作,给定
    var\u name
    new\u val
    my\u binding
    (语法可能不完善或可改进,请随时在注释中提出建议。此外,我无法在列表中使用代码格式,有关如何执行的建议也将得到实施。)
  • 您可以直接获取(2)并循环散列来执行此操作
  • 请参阅下面的第二个代码块。这个想法是从顶级绑定开始的,我相信它通常只包括全局变量
  • 这确实涉及到使用字符串
    eval
    。但是,没有变量值被扩展到所涉及的字符串中,因此,如果按照描述使用,它应该是相当安全的,并且应该能够“传入”复杂的变量值

    还要注意,总是可以执行
    eval var\u name、my\u binding
    来获取变量的值。请注意,在所有这些使用中,变量名称的安全性是至关重要的,因此理想情况下,它不应该来自任何类型的用户输入

    在给定的绑定内设置变量
    var\u name
    new\u val
    my\u绑定

    # the assignment to nil in the eval coerces the variable into existence at the outer scope
    setter = eval "#{var_name} = nil; lambda { |v| #{var_name} = v }", my_binding
    setter.call(new_val)
    
    构建“定制”装订:


    Walter,你应该可以直接和绑定交互。我以前很少使用绑定,但我在irb中运行了一些东西:

    jim@linux-g64g:~> irb
    irb(main):001:0> eval "self", TOPLEVEL_BINDING
    => main
    irb(main):002:0> eval "instance_variables", TOPLEVEL_BINDING
    => []
    irb(main):003:0> eval "methods", TOPLEVEL_BINDING
    => ["irb_kill", "inspect", "chws", "install_alias_method", ....
    
    我也有元编程Ruby,它没有太多关于绑定的内容。然而,如果你拿起这个,在144页的末尾,它说

    在某种意义上,你可以看到绑定 对象作为闭包的“更纯”形式 而不是块,因为这些对象 包含作用域但不包含 代码

    在另一页,它建议修改irb的代码(删除eval调用的最后两个参数),看看它如何使用绑定:

    //ctwc/irb/workspace.rb
    eval(语句,@binding)#,文件,行)


    而且。。。我本打算建议通过lambda考试,但我看到你刚刚回答了这个问题,所以我将把irb的修修补补作为进一步研究的建议。

    你能解释一下你到底想做什么吗?请提供一些代码,说明您希望它如何工作。也许有更好更安全的方法来实现你想要的

    我将尝试猜测您的典型用例。 给定哈希: {:a=>11,:b=>22}

    您需要一个最小的、相对隔离的执行环境,在这里您可以作为局部变量访问哈希值。(我不太清楚为什么您需要他们是本地人,除非您正在编写DSL,或者您已经编写了不想修改以访问哈希的代码。)

    这是我的尝试。为了简单起见,我假设您只使用符号作为散列键

    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 类MyBinding def初始化(varhash)@vars=varhash;结束 def方法_缺失(methname,*args) meth\u s=methname.to\u s 如果meth_s=~/=\z/ @变量[meth_s.sub(/=\z/,'').to_sym]=args.first 其他的 @变量[名称] 结束 结束 def eval(&block) 实例评估和块 结束 结束 示例用法:

    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 } 散列={:a=>11,:b=>22} mb=MyBinding.new散列 放置mb.eval{a+b} #设置值不太自然: mb.eval{self.a=33} 放置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 def初始化(varhash) @vars=Hash.new{| h,k | raise NameError,“未定义的局部变量或方法`{k}'}” @vars.update(varhash) 结束 2) 正常的作用域规则是这样的:如果存在名称与方法相同的局部,则局部优先,除非显式执行方法调用,如()。上面的类有相反的行为;方法优先。要获得“正常”行为,您需要隐藏所有(或大部分)标准方法,如#object_id;它只保留3种方法:

    __send__, instance_eval, __id__ __发送,实例,评估,id__ 。要使用它,只需使MyBinding从BlankSlate继承即可。 除了这3种方法之外,您还不能将本地名命名为“eval”或“m” __send__, instance_eval, __id__ proc { $SAFE = 1; eval "do_some_stuff" }.call # returns the value of eval call class ScopedHash def initialize(varhash) # You can use an OpenStruct instead of a Hash, but the you lose the NameError feature. # OpenStructs also don't have the ability to list their members unless you call a protected method @vars = Hash.new {|h,k| raise NameError, "undefined local variable or method `#{k}'" } @vars.update(varhash) end def eval yield @vars end end if __FILE__ == $0 # sample usage hash = {:a => 11, :b => 22} sh = ScopedHash.new hash puts sh.eval {|v| v[:a] + v[:b] } sh.eval {|v| v[:a] = 33 } puts sh.eval {|v| v[:a] + v[:b] } sh.eval{|v| v[:c] } # raises NameError end