Tagged With   lib:name=irb , post:type=tutorial , post:lang=ruby

Demystifying Irb's Commands

Irb is one of the most commonly used standard ruby libraries, yet most don’t seem to know much about it. I’ve lost count of the number of blog posts that explain some irb “tricks” or “secret” config options. This is the first in a series that aims to make irb less magical and more accessible.

First off, I’m assuming you know your way around irb some. If you don’t then I recommend reading the pragmatic section on irb or defunkt’s mix tape. Second, I’ll be referring to ruby 1.8.6 and irb 0.9.5 in this post. For better or worse, most everything here should also apply to ruby 1.9.1.

Snooping Around

Having played with irb, you know that you have access to everything in Kernel and Object. A quick double check confirms this:

  bash> irb
  >> self.instance_eval("class<< self;self;end").ancestors
  => [IRB::ExtendCommandBundle, Object, Kernel]

Ok, but what methods/commands are we getting from IRB::ExtendCommandBundle? A whole lot:

  >> self.instance_eval('class<< self;self;end').instance_methods(nil)
  => ["irb_pop_workspace", "irb_source", "irb_help", "conf", "exit", "irb_pwb", "irb_context", "irb_load", "pwws", "irb_change_binding", 
  "pushws", "irb_jobs", "to_s", "irb_print_working_workspace", "irb_chws", "bindings", "popws", "irb_workspaces", "irb", "public", "quit", 
  "irb_cwb", "pushb", "irb_exit", "irb_fg", "cwws", "cws", "irb_pushws", "popb", "context", "irb_bindings", "irb_popws", "help", 
  "irb_require", "irb_kill", "include", "irb_quit", "irb_print_working_binding", "cb", "irb_pushb", "irb_pwws", "chws", "irb_popb", "jobs", 
  "install_alias_method", "irb_current_working_workspace", "irb_change_workspace", "private", "workspaces", "irb_current_working_binding", 
  "irb_cb", "irb_push_binding", "fg", "irb_push_workspace", "irb_cwws", "irb_cws", "irb_pop_binding", "source", "kill"]

  # If you try to get the above list with self.methods(nil), you'll get a different answer. This has to do with
  # the on-the-fly methods generated by IRB::ExtendCommandBundle.

Well, that’s a lot of commands. 59 to be exact. But are they really 59 distinct commands? If you dig around in irb/extend_command_bundle.rb you’ll find that most of them are just aliases:

  >> IRB::ExtendCommandBundle.instance_eval("@ALIASES").size
  => 39

  # If 39 of the 59 commands are just aliases, how do we get the original commands?
  >> commands = self.instance_eval("class<< self;self;end").instance_methods(nil) - IRB::ExtendCommandBundle.instance_eval("@ALIASES").map {|e| e[0].to_s}
  => ["irb_pop_workspace", "irb_source", "irb_help", "irb_context", "irb_load", "irb_jobs", "to_s", "irb_workspaces", "irb", "public", 
  "irb_exit", "irb_fg", "irb_require", "irb_kill", "include", "install_alias_method", "irb_current_working_workspace", 
  "irb_change_workspace", "private", "irb_push_workspace"]

Can we get a visual?

  # import hirb's table command
  >> require 'hirb'; extend Hirb::Console
  => main

  # create a default commands hash
  >> defaults = commands.inject({}) {|h,e| h[e] = [];h }                                                                                                                                             
  => {"irb_pop_workspace"=>[], "irb_require"=>[], "public"=>[], "private"=>[], "irb"=>[], "irb_push_workspace"=>[], "to_s"=>[], 
  "irb_change_workspace"=>[], "irb_load"=>[], "irb_source"=>[], "irb_jobs"=>[], "irb_fg"=>[], "irb_help"=>[], "include"=>[], "irb_kill"=>[], 
  "irb_exit"=>[], "irb_workspaces"=>[], "irb_current_working_workspace"=>[], "install_alias_method"=>[], "irb_context"=>[]}

  # create a table from an array of command/aliases pairs
   >> table IRB::ExtendCommandBundle.instance_eval("@ALIASES").inject(defaults) {|h,e| h[e[1].to_s] ||= []; h[e[1].to_s] << e[0]; h }.to_a.map {|e| [e[0],e[1].join(",")] }, :headers=>%w{commands aliases}, :max_width=>200
  +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------+
  | commands                      | aliases                                                                                                                       |
  +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------+
  | irb_pop_workspace             | irb_popws,popws,irb_pop_binding,irb_popb,popb                                                                                 |
  | irb_require                   |                                                                                                                               |
  | public                        |                                                                                                                               |
  | private                       |                                                                                                                               |
  | irb                           |                                                                                                                               |
  | irb_push_workspace            | irb_pushws,pushws,irb_push_binding,irb_pushb,pushb                                                                            |
  | to_s                          |                                                                                                                               |
  | irb_change_workspace          | irb_chws,irb_cws,chws,cws,irb_change_binding,irb_cb,cb                                                                        |
  | irb_load                      |                                                                                                                               |
  | irb_source                    | source                                                                                                                        |
  | irb_jobs                      | jobs                                                                                                                          |
  | irb_fg                        | fg                                                                                                                            |
  | irb_help                      | help                                                                                                                          |
  | include                       |                                                                                                                               |
  | irb_kill                      | kill                                                                                                                          |
  | irb_exit                      | irb_quit,exit,quit                                                                                                            |
  | irb_workspaces                | workspaces,irb_bindings,bindings                                                                                              |
  | irb_current_working_workspace | irb_print_working_workspace,irb_cwws,irb_pwws,cwws,pwws,irb_current_working_binding,irb_print_working_binding,irb_cwb,irb_pwb |
  | install_alias_method          |                                                                                                                               |
  | irb_context                   | context,conf                                                                                                                  |
  +-------------------------------+-------------------------------------------------------------------------------------------------------------------------------+
  20 rows in set
  => true  

Irb Commands – The Big Picture

Now that we know what the original commands are, let’s group them by similar functionality:

Subsession Commands: irb_jobs, irb, irb_fg, irb_exit, irb_kill

  • irb_jobs (jobs): Lists subsessions with their number and current state.
  • irb: Creates a subsession in the context of an object. If no object is given, defaults to main object.
  • irb_fg (fg): Jump to a subsession by number.
  • irb_exit (irb_quit, exit, quit): Terminates the current subsession.
  • irb_kill (kill): Kills a subsession by number.

These commands manage irb’s different subsessions or jobs. By default, you start with a subsession in the context of an object named main. Let’s verify this by listing the current subsessions with irb_jobs:

  bash> irb
  >> irb_jobs
  => #0->irb on main (#<Thread:0x35700>: running)

You can create subsessions in the context of any object with irb. This is handy for giving you quick access to anything in that object:

  >> irb IRB::ExtendCommandBundle
  >> @ALIASES.size
  => 39

  >> @EXTEND_COMMANDS.size
  => 13

Jump to a subsession with irb_fg and a subsession’s number:

  >> irb_jobs
  => #0->irb on main (#<Thread:0x35700>: stop)
  #1->irb#1 on IRB::ExtendCommandBundle (#<Thread:0x1114238>: running)

  >> irb_fg 0
  => #<IRB::Irb: @scanner=#<RubyLex:0x1363c>, @context=#<IRB::Context:0x15090>, @signal_status=:IN_EVAL>

  >> self
  => main

You can terminate the current subsession with irb_exit. Kill other subsessions you’re not in with irb_kill and their number:

  >> irb 'another'
  >> jobs
  => #0->irb on main (#<Thread:0x35700>: stop)
  #1->irb#1 on IRB::ExtendCommandBundle (#<Thread:0x3c2d0>: stop)
  #2->irb#2 on another (#<Thread:0x459d4>: running)

  >> irb_kill 1
  => [1]

  >> jobs
  => #0->irb on main (#<Thread:0x35700>: stop)
  #2->irb#2 on another (#<Thread:0x459d4>: running)

  >> irb_exit
  => #<IRB::Irb: @scanner=#<RubyLex:0x1363c>, @context=#<IRB::Context:0x15090>, @signal_status=:IN_EVAL>

  >> jobs
  => #0->irb on main (#<Thread:0x35700>: running)

To further explore these commands see irb/ext/multi-irb.rb.

Workspace Commands: irb_workspaces, irb_push_workspace, irb_pop_workspace, irb_current_working_workspace, irb_change_workspace

  • irb_workspaces (workspaces, irb_bindings, bindings): Array of workspaces in the stack.
  • irb_push_workspace (irb_pushws, pushws, irb_push_binding, irb_pushb, pushb): Create/push a new workspace with given object or binding. Like irb with subsessions.
  • irb_pop_workspace (irb_popws, popws, irb_pop_binding, irb_popb, popb): Exit/pop the current workspace and return to the previous one. Like irb_exit with subsessions.
  • irb_current_working_workspace (irb_print_working_workspace, irb_cwws, irb_pwws, cwws, pwws, irb_current_working_binding, irb_print_working_binding, irb_cwb, irb_pwb): Main object of current workspace.
  • irb_change_workspace (irb_chws, irb_cws, chws, cws, irb_change_binding, irb_cb, cb): Change the current workspace to the given object.

Disclaimer: I don’t use workspaces much and know few people that do. If you have a good use case for them, feel free to comment in order to improve this section.

These commands manage workspaces, which exist within a subsession. Like subsessions, workspaces allow you to change the current context. The one major difference is that local variables are preserved between workspaces. This can be helpful if you have defined some local variables you’d like to reuse in different contexts. Note that you can’t jump between workspaces like you can with subsessions. You can only push and pop them onto a stack. Let’s look at some examples:

  bash> irb
  >> self
  => main

  >> irb_current_working_workspace
  => main

  >> irb_workspaces
  => []

  >> useful_local_variable = 'amazing value'
  => "amazing value"
  
  >> irb_push_workspace IRB::ExtendCommandBundle
  => [main]

  >> irb_workspaces
  => [main]

  # Local variable preserved between workspaces
  >> useful_local_variable
  => "amazing value"

  >> another_var = 'value'
  => 'value'

  # Exit current workspace
  >> irb_pop_workspace
  => []

  >> irb_current_working_workspace
  => main

  # Local variable from deleted workspace preserved
  >> another_var
  => 'value'

  # Change/replace the current workspace
  >> irb_change_workspace 'the end'
  => 'the end'

  >> irb_current_working_workspace
  => 'the end'

To further explore these commands see
irb/workspace.rb, irb/ext/workspaces.rb and irb/ext/change-ws.rb.

Miscellaneous Commands: irb_context, install_alias_method, irb_help

  • irb_context (context, conf): The current IRB::Context object.
  • install_alias_method: Alias methods with safety checks.
  • irb_help (help): Prints out ri help.

irb_context is one of the most important commands as it gives runtime access to all the IRB.conf options you see in .irbrc’s. It’s important to note that each irb subsession has it’s own context object and option values. If you enter just the context object you should get a list of its current options:

  >> irb_context
  conf.auto_indent_mode=false
  conf.back_trace_limit=16
  conf.debug_level=1
  conf.echo=true
  conf.ignore_eof=false
  conf.ignore_sigint=true
  conf.inspect_mode=nil
  conf.io=#<IRB::ReadlineInputMethod:0x139c0>
  conf.irb=#<IRB::Irb:0x150e0>
  conf.irb_name="irb"
  conf.irb_path="(irb)"
  conf.last_value=...
  conf.line_no=1
  conf.load_modules=[]
  conf.output_method=#<IRB::StdioOutputMethod:0x1377c>
  conf.prompt_c="%N(%m):%03n:%i* "
  conf.prompt_i="%N(%m):%03n:%i> "
  conf.prompt_mode=:DEFAULT
  conf.prompt_n="%N(%m):%03n:%i> "
  conf.prompt_s="%N(%m):%03n:%i%l "
  conf.rc=false
  conf.return_format="=> %s\n"
  conf.thread=#<Thread:0x35700 run>
  conf.use_readline=nil
  conf.verbose=nil
  conf.workspace=#<IRB::WorkSpace:0x1502c @binding=#<Binding:0x14bcc>, @main=main>

Going into the usefulness of all these options could be its own blog post. Fortunately, the Commands section of the pragmatic irb chapter has covered the most important options. To change an option, call it as a method on the context object:

  # change the verbosity level
  >> irb_context.verbose = true
  => true

install_alias_method is the aliasing system used internally for all irb commands. Unlike alias_method, install_alias_method makes sure existing methods don’t get overridden:

  >> def ping; system("ping google.com"); end
  => nil

  # Suppose I want to alias this command to p. It warns me if the method already exists.
  >> install_alias_method :p, :ping
  irb: warn: can't alias p from ping.
  => nil

  # I can override this warning by passing it a numerical flag
  >> install_alias_method :p, :ping, 1
  => #<Class:#<Object:0x389a0>>

  >> p
  PING google.com (74.125.67.100): 56 data bytes
  64 bytes from 74.125.67.100: icmp_seq=0 ttl=53 time=29.363 ms
  ...

  # install_alias_method saves the original method in "irb_#{method}_org"
  >> irb_p_org 'ping'
  "ping"
  => nil

  # The 1 flag only overrides private methods. To override any method, pass it a 2 flag.

irb_help is a simple wrapper around ri. Unfortunately for my rdoc version (2.3.0) and ruby version (1.8.6) this command is broken:

  >> irb_help 'Array'

  unrecognized option `--no-home'

  For help on options, try 'ri --help'

  NoMethodError: undefined method `execute' for IRB::ExtendCommand::Help:Module
          from (eval):3:in `irb_help'
          from (eval):9:in `send'
          from (eval):9:in `irb_help'
          from (irb):1
          from /Library/Ruby/Site/1.8/rubygems.rb:459

  # This command's output probably looks similar to the output of system("ri Array").

To further explore these commands see irb/extend-command.rb and irb/cmd/help.rb.

Load Commands: irb_load, irb_require, irb_source

  • irb_load: Loads a file like Kernel#load.
  • irb_require: Loads a file like Kernel#require.
  • irb_source: Loads a file given its full path.

These load commands load a given file by evaluating each line separately in irb and printing the result. For these examples, create a file named “calculations.rb” in the current directory with the text:

  [4,3,2,1].inject {|t,e| t * e }
  [4,3,2,1].inject {|t,e| t ** e }

irb_load, like Kernel#load, loads a full path or one relative to $:.

  >> irb_load "calcuations.rb"
  >> [4,3,2,1].inject {|t,e| t * e }
  => 24
  >> [4,3,2,1].inject {|t,e| t ** e }
  => 4096
  >> => nil

  # Like Kernel#load you can load the same file as many times as you'd like.
  >> irb_load "calcuations.rb"
  >> [4,3,2,1].inject {|t,e| t * e }
  => 24
  >> [4,3,2,1].inject {|t,e| t ** e }
  => 4096
  >> => nil

irb_require, like Kernel#require, loads a path relative to $: if it hasn’t already been registered in $".

  >> irb_require "calcuations"
  >> [4,3,2,1].inject {|t,e| t * e }
  => 24
  >> [4,3,2,1].inject {|t,e| t ** e }
  => 4096
  >> => nil

  >> irb_require "calcuations"
  => false

irb_source is like a picky version of irb_load, only able to load full paths:

  >> irb_source File.join(Dir.pwd, "calcuations.rb")
  >> [4,3,2,1].inject {|t,e| t * e }
  => 24
  >> [4,3,2,1].inject {|t,e| t ** e }
  => 4096
  >> => nil

Note that you can toggle irb_load and irb_require replacing their respective Kernel methods by toggling the context object’s use_loader flag:

  >> context.use_loader = true
  => true

  >> load "calcuations.rb"
  >> [4,3,2,1].inject {|t,e| t * e }
  => 24
  >> [4,3,2,1].inject {|t,e| t ** e }
  => 4096
  >> => nil

  # Turn off the overridding
  >> context.use_loader = false
  => false

To further explore these commands see irb/ext/loader.rb and irb/cmd/load.rb.

Cloned Commands: to_s, public, private, include

These commands have been cloned from other classes (i.e. Module) and should behave like the originals. Feel free to correct me if they have additional functionality.

That’s All Folks

Hope you enjoyed this venture into irb. Feel free to share additional insights into these commands.

Enjoyed this post? Tell others! hacker newsHacker News | twitterTwitter | DeliciousDelicious | redditReddit
blog comments powered by Disqus