Can you eval code in the context of a caller in Ruby?

后端 未结 5 527
说谎
说谎 2020-12-06 09:46

Essentially I\'m wondering if the following can be done in Ruby.

So for example:

def bar(symbol) 
  # magic code goes here, it outputs \"a = 100\"          


        
相关标签:
5条回答
  • 2020-12-06 09:56

    Here's a easier syntax hack, using a passed in block binding:

      def loginfo &block
        what = yield.to_s
        evaled = eval(what, block.binding)
        Rails.logger.info "#{what} = #{evaled.inspect}"
      end
    

    called like this:

      x = 1
      loginfo{ :x }
    

    will log out:

      x = 1
    
    0 讨论(0)
  • 2020-12-06 09:56

    Check article out Variable Bindings in Ruby

    class Reference
      def initialize(var_name, vars)
        @getter = eval "lambda { #{var_name} }", vars
        @setter = eval "lambda { |v| #{var_name} = v }", vars
      end
      def value
        @getter.call
      end
      def value=(new_value)
        @setter.call(new_value)
      end
    end
    
    def ref(&block)
      Reference.new(block.call, block.binding)
    end
    
    def bar(ref)
      # magic code goes here, it outputs "a = 100" 
      p ref.value
    end
    
    def foo
      a = 100 
      bar(ref{:a}) 
    end
    
    foo
    
    0 讨论(0)
  • 2020-12-06 09:59

    Just FYI, here's a "hacky way". This is my (re-)implementation of well-known ppp.rb:

    #!/usr/bin/ruby
    #
    # better ppp.rb
    #
    
    require 'continuation' if RUBY_VERSION >= '1.9.0'
    
    def ppp(*sym)
      cc = nil
      ok = false
    
      set_trace_func lambda {|event, file, lineno, id, binding, klass|
        if ok
          set_trace_func nil
          cc.call(binding)
        else
          ok = event == "return"
        end
      }
      return unless bb = callcc{|c| cc = c; nil }
    
      sym.map{|s| v = eval(s.to_s, bb); puts "#{s.inspect} = #{v}"; v }
    end
    
    a = 1
    s = "hello"
    ppp :a, :s
    
    exit 0
    

    This currently fails with 1.9.[012] due to a bug in ruby's set_trace_func.

    0 讨论(0)
  • 2020-12-06 10:11

    You have to pass foo's context to bar:

    def foo
      a = 100
      bar(:a, binding)
    end
    def bar(sym, b)
      puts "#{sym} is #{eval(sym.to_s, b)}"
    end
    
    0 讨论(0)
  • 2020-12-06 10:18

    There is no built-in way to get a callers binding in Ruby in 1.8.X or 1.9.X.

    You can use https://github.com/banister/binding_of_caller to work around.

    In MRI 2.0 you can use RubyVM::DebugInspector, see: https://github.com/banister/binding_of_caller/blob/master/lib/binding_of_caller/mri2.rb

    Working sample in MRI 2.0:

    require 'debug_inspector'
    
    def bar(symbol)
      RubyVM::DebugInspector.open do |inspector|
        val = eval(symbol.to_s, inspector.frame_binding(2))
        puts "#{symbol}: #{val}"
      end
    end
    
    def foo
      a = 100
      bar(:a)
    end
    
    foo
    # a: 100
    
    0 讨论(0)
提交回复
热议问题