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
- workspace commands: irb_current_working_workspace, irb_workspaces, irb_pop_workspace, irb_change_workspace, irb_push_workspace
- miscellaneous commands: irb_context, install_alias_method, irb_help
- load commands: irb_load, irb_require, irb_source
- cloned commands: to_s, public, private, include
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.