【问题标题】:How to run IRB.start in context of current class如何在当前类的上下文中运行 IRB.start
【发布时间】:2010-11-15 23:17:00
【问题描述】:

我刚刚经历了 PragProg Continuous Testing With Ruby,他们谈到在当前类的上下文中调用 IRB 来手动检查代码。

然而,他们引用了如果你在一个类中调用IRB.startself 是预定义的,它指的是我们在调用 start 时所在的对象,它是对我来说不是这样。

即使是非常简单的例子,比如

a = "hello"
require 'irb'
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start

当我尝试访问 a 变量时,我明白了

NameError: undefined local variable or method `a' for main:Object

仅当我将a 更改为全局变量时才有效

$a = "hello"
require 'irb'
ARGV.clear # otherwise all script parameters get passed to IRB
IRB.start

然后我可以访问它

irb(main):001:0> $a
=> 1

有什么办法可以访问当前类中的局部变量和实例变量吗?

【问题讨论】:

    标签: ruby irb


    【解决方案1】:

    正如您已经发现的,self 不是指启动 IRB 的对象,而是指TOPLEVEL_BINDING,它似乎是Object 类本身的一个实例。

    您仍然可以使用特定的类或对象作为上下文来运行 IRB 会话,但这并不像启动 IRB 那样简单。

    如果您关心的是使用特定上下文启动 IRB,那么手动启动 IRB 非常容易。只需正常启动 IRB,然后调用 irb 方法,将所需的对象/类作为上下文传递给它。

    $ irb
    irb(main):002:0> require 'myclass'
    => true
    irb(main):003:0> irb MyClass
    irb#1(MyClass):001:0> self
    => MyClass
    

    您也可以通过编程方式启动 IRB 会话并指定上下文,但这并不像应有的那么简单,因为您必须重现一些 IRB 的启动代码。在 IRB 源代码中进行了大量的试验和挖掘之后,我想出了一些可行的方法:

    require 'irb'
    IRB.setup nil
    IRB.conf[:MAIN_CONTEXT] = IRB::Irb.new.context
    require 'irb/ext/multi-irb'
    IRB.irb nil, self
    

    【讨论】:

    • 恕我直言,这是回答这个问题的唯一答案。
    • 赞成,为我工作并回答原始问题(而不是建议替代方案),谢谢!
    • 史诗般的答案。谢谢 - 第一次尝试完全适合我(ruby 1.9.3)。感谢您做这项工作,所以我不必这样做。
    • 该死,不在这里工作。也许是因为我们使用的是 JRuby。 :(
    【解决方案2】:

    我建议在 ripl 中尝试这个,这是一个 irb 替代方案。上面的例子有效:

    a = 'hello'
    require 'ripl'
    Ripl.start :binding => binding
    

    请注意,局部变量有效,因为您使用 :binding 选项传递当前绑定。

    你可以在 irb 中做同样的事情,但由于它的文档记录和未经测试,你干净地做到这一点的机会微乎其微。

    【讨论】:

    • 不幸的是,这在 JRuby 下是不行的。
    • 你应该打开一个问题:github.com/cldwalker/ripl/issues。 ripl 已被验证可以在 jruby 上工作
    • 第二个建议避免 IRB 为此。撬是另一种选择。
    【解决方案3】:

    你可以使用实例变量而不是全局变量,例如:

    require 'irb'
    @a = "hello"
    ARGV.clear
    IRB.start
    
    >> @a
    => "hello"
    

    【讨论】:

    • 使用实例变量会有什么变化?
    • @ulvund 好吧,与局部变量不同,它们在IRB.start 之后仍然可见(即,用a 替换此代码中的@a 是行不通的),但与全局变量不同,它们不会污染全局命名空间(只是主对象的命名空间)。无论如何,其他答案已经提到让当地人也能工作的方法,所以如果使用外部库或做更多的工作不是问题,那么这些是更好的选择。 (我更喜欢自己撬。)
    【解决方案4】:

    从 Ruby 2.4.0 开始,您可以这样做:

    require 'irb'
    binding.irb
    

    这将启动一个 IBR REPL,您将在其中获得 self 的正确值,并且您将能够访问范围内的所有局部变量和实例变量。键入 Ctrl+D 或 quit 以恢复您的 Ruby 程序。

    【讨论】:

    • 是的!刚找到here,想写答案...
    【解决方案5】:

    使用Pry:

    a = 'hello'
    require 'pry'
    binding.pry
    

    【讨论】:

    • 我对 Ripl(这是公认的解决方案)有一​​些问题,它给了我比我预期的另一个上下文。因此,经过仔细检查,我也将投票给 Pry。
    • 我无法正常工作,可能有些不兼容/usr/local/share/gems/gems/term-ansicolor-1.2.2/lib/term/ansicolor.rb:188:in 'color': wrong number of arguments (0 for 1..2) (ArgumentError) from /usr/local/share/gems/gems/pry-0.10.0/lib/pry/output.rb:35:in 'decolorize_maybe'
    【解决方案6】:

    以下是在调用 IRB.start 的上下文中从脚本调用 IRB 的方法。

    require 'irb'
    class C
        def my_method
            @var = 'hi'
            $my_binding = binding
            IRB.start(__FILE__)
        end
    end
    
    C.new.my_method
    

    执行您的脚本将调用 IRB。当您收到提示时,您还有一件事要做...

    % ./my_script.rb
    irb(main):001:0> @var.nil?
    => true
    irb(main):002:0> cb $my_binding
    => #<C:0x000000009da300 @var="hi">
    irb(#<C:0x000000009da300>):003:0> @var.nil?
    => false
    irb(#<C:0x000000009da300>):004:0> @var
    => "hi"
    

    享受吧!

    【讨论】:

    • 可能是这样。但问题是,“如何在当前类的上下文中运行 IRB.start”。我的答案是唯一真正回答问题的答案。
    • 您无法访问局部变量,因此无法回答问题。
    【解决方案7】:

    我的 Ruby 2.2.3 解决方案。和科比的很像

    def to_s
      "Sample"
    end
    
    def interactive
      banana = "Hello"
      @banana = "There"
      require 'irb'
      IRB.setup(nil)
      workspace = IRB::WorkSpace.new(binding)
      irb = IRB::Irb.new(workspace)
      IRB.conf[:MAIN_CONTEXT] = irb.context
      irb.eval_input
    end
    
    irb(Sample):001:0> puts banana
    Hello
    => nil
    irb(Sample):002:0> puts @banana
    There
    => nil
    irb(Sample):003:0>
    

    我可以访问局部变量和实例变量。 require 'irb' 当然可以在文件的顶部。 banana@banana 的设置只是为了证明我可以从 irb 提示符访问它们。 to_s 是获得漂亮提示的一种方法,但还有其他选择。而且没有真正的理由制作单独的方法,但就目前而言,您可以将它放在任何地方,它应该可以工作。

    【讨论】:

      【解决方案8】:

      ruby-debug-base gem 将 binding_n 方法添加到 Kernel 模块中,这将使您可以访问绑定对象,该对象可以在 eval 中使用以提供对调用的访问权限-堆栈变量。记得发出 Debugger.start 来开启调用堆栈追踪。

      这是一个示例,展示了它用于内省 irb(一个 Ruby 程序)内部发生的事情。也可以将 requireDebugger.start 放入您自己的 Ruby 代码中。

      $ irb
      ruby-1.8.7-p302 > require 'rubygems'
       => true 
      ruby-1.8.7-p302 > require 'ruby-debug-base'
       => true 
      ruby-1.8.7-p302 > Debugger.start
       => true 
      ruby-1.8.7-p302 > puts caller
      /tmp/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/irb/workspace.rb:52  :i  n `irb_binding' #`
      /tmp/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/irb/workspace.rb:52
      => nil 
      ruby-1.8.7-p302 > eval "main", binding_n(2)
       => #<Object:0xb7762958 @prompt={:PROMPT_I=>"ruby-1.8.7-p302 > ", :PROMPT_N=>"  ruby-1.8.7-p302 ?> ", :PROMPT_S=>"ruby-1.8.7-p302%l> ", :PROMPT_C=>"ruby-1.8.7-p302 > ", :AUTO_INDENT=>true, :RETURN=>" => %s \n"}> 
      ruby-1.8.7-p302 > 
      

      如果您愿意为 1.9.2 运行 Ruby 的修补版本,请参阅http://gitnub.com/rocky/rb-threadframe,了解我认为更好地访问调用堆栈的方法。 Rubinius 通过 Rubinius::VM.backtrace 提供了这种内置功能。

      【讨论】:

        【解决方案9】:

        这个是这个线程中所有答案的混搭。是产生提问者所要求行为的最小代码段:

        require 'irb'
        
        module Craft
          class Console
            def start
              IRB.setup(__FILE__)
              workspace = IRB::WorkSpace.new(self)
              IRB::Irb.new(workspace).run(IRB.conf)
            end
        
            def hello
              puts 'hello world!'
            end
          end
        end
        

        无需清除ARGV,也无需显式调用eval_input

        这是输出的样子:

        bundle exec craft console
        irb(#<Craft::Console:0x00007fb0135592c0>):001:0>hello
        hello world!
        => nil
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-11-08
          • 1970-01-01
          • 2016-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-10-04
          • 1970-01-01
          相关资源
          最近更新 更多