Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Could you explain what exactly you're trying to do? Please provide some code showing how you want it to work. There might be a better and safer way to accomplish what you want.</p> <p>I'm going to take a shot at guessing your typical use-case. Given a Hash: {:a => 11, :b => 22}</p> <p>You want a minimal, relatively-isolated execution environment where you can access the values of the hash as local-variables. (I'm not exactly sure why you'd need them to be locals, except maybe if you're writing a DSL, or if you have already-written code that you don't want to adapt to access the Hash.)</p> <p>Here's my attempt. For simplicity, I'm assuming you only use symbols as Hash keys.</p> <pre> 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 </pre> <p>Sample Usage:</p> <pre> 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 } </pre> <p>Some caveats:</p> <p>1) I didn't raise a NameError if the variable didn't exist, but a simple replacement fixes that:</p> <pre> def initialize(varhash) @vars = Hash.new {|h,k| raise NameError, "undefined local variable or method `#{k}'" } @vars.update(varhash) end </pre> <p>2) The normal scoping rules are such that if a local exists whose name is the same as a method, the local takes precedence, unless you explicitly do a method call, like a(). The class above has the opposite behavior; the method takes precedence. To get "normal" behavior, you'll need to hide all (or most) of the standard methods, like #object_id. ActiveSupport provides a BlankSlate class for this purpose; it only keeps 3 methods:</p> <pre> __send__, instance_eval, __id__</pre> <p>. To use it, just make MyBinding inherit from BlankSlate. Besides these 3 methods, you also won't be able to have locals named "eval" or "method_missing".</p> <p>3) Setting a local is not as natural, because it needs "self" to receive the method call. Not sure if there's a workaround for this.</p> <p>4) The eval block can mess with the @vars hash.</p> <p>5) If you have a real local var in the scope where you call mb.eval, with the same name as one of the hash keys, the real local will take precedence... this is probably the biggest downside because subtle bugs can creep in.</p> <p>After realizing the downsides to my technique, I'm recommending using a Hash directly to keep a set of variables, unless I see a reason otherwise. But if you still want to use the native eval, you can improve safety by using Regexps to avoid code injection, and "locally" setting $SAFE to be higher for the eval call by using a Proc, like so:</p> <pre> proc { $SAFE = 1; eval "do_some_stuff" }.call # returns the value of eval call </pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload