是';评估';在Ruby中与绑定对象交互的唯一方法是什么?
我对Ruby还比较陌生,到目前为止,弄清楚如何使用Ruby是我最大的痛点之一。如果我正确阅读了文档,它们几乎完全是不透明的。要访问绑定对象内部的作用域,必须有一个Ruby代码字符串,并使用绑定来访问它 也许我只是一个来自不同学校的纯粹主义者,但一般来说,我对基于字符串的“eval”结构过敏。对于绑定对象,是否有任何方法可以安全地在一般情况下执行以下操作:是';评估';在Ruby中与绑定对象交互的唯一方法是什么?,ruby,eval,scope,Ruby,Eval,Scope,我对Ruby还比较陌生,到目前为止,弄清楚如何使用Ruby是我最大的痛点之一。如果我正确阅读了文档,它们几乎完全是不透明的。要访问绑定对象内部的作用域,必须有一个Ruby代码字符串,并使用绑定来访问它 也许我只是一个来自不同学校的纯粹主义者,但一般来说,我对基于字符串的“eval”结构过敏。对于绑定对象,是否有任何方法可以安全地在一般情况下执行以下操作: 在绑定表示的上下文中列出作用域中的标识符,或检索内容的哈希 将绑定中局部变量的值设置为外部上下文中某个局部变量的值。理想情况下,即使该值是对象
或者,有没有类似于Perl的eval BLOCK语法的方法来评估已经在绑定上下文中解析过的代码?在搜索更多信息时,我找到了至少部分问题的答案: 基于: 剩下的是吉姆·舒伯特的有益建议之后的实验
eval
-inglocal\u variables
、instance\u variables
和global\u variables
在绑定内部实现var\u name
,new\u val
,my\u binding
(语法可能不完善或可改进,请随时在注释中提出建议。此外,我无法在列表中使用代码格式,有关如何执行的建议也将得到实施。)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.rbeval(语句,@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