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

Exploring How To Configure Irb

In my last post we discussed irb’s commands. This post goes further into irb’s innards, exploring all of its configuration options.

Gettin Ready

For starters, I refer you to the pickaxe which has an excellent section on configuring irb. Also, this post assumes ruby 1.8.6 and irb 0.9.5, though it should apply to 1.9ers. Yes, I know I should get on the 1.9 train.

This post will cover every irb configuration option. If I miss one, please let me know. Not all of irb’s features are enabled/used through configuration i.e. completion and xmp. I’ll save those for another blog post.

Let’s first cover some irb configuration basics:

  • By default, irb loads the first configuration file it finds among these files: ~/.irbrc, .irbrc, irb.rc, _irbrc, $irbrc and /etc/irbrc. For the curious, read the source. That file is also where most of irb’s configuration is setup.
  • To override the default configuration file, set the IRBRC environment variable:
    bash> IRBRC=‘/i/like/my/irbrc/elsewhere’ irb
  • Irb saves its configuration in a hash accessible through IRB.conf. By convention, the config keys are symbols in all caps. Simply set an option by setting a key’s value: IRB.conf[:SAVE_HISTORY] = 1000
  • Unlike irb commands in the last post, there isn’t much to getting most of the global configuration options:
  bash> irb -f --prompt simple
  >> IRB.conf.keys
  >> [:CONTEXT_MODE, :PROMPT_MODE, :PROMPT, :INSPECT_MODE, :VERBOSE, :RC, :SINGLE_IRB, :USE_TRACER, :EVAL_HISTORY, :LOAD_MODULES,
   :LC_MESSAGES, :USE_LOADER, :SAVE_HISTORY, :MATH_MODE, :IGNORE_SIGINT, :IRB_RC, :AP_NAME, :MAIN_CONTEXT, :AUTO_INDENT, :BACK_TRACE_LIMIT,
    :DEBUG_LEVEL, :IGNORE_EOF, :IRB_NAME, :ECHO, :IRB_LIB_PATH]
  • To get the full list of global irb configuration options, just grep it:
  # Cd to your ruby source's directory
  bash> cd /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8
  
  # You probably don't want to see this output
  bash> grep -r 'conf\[\|@CONF' irb.rb  irb/
  • Each irb subsession copies the global config options into its IRB::Context object. Since you have easy access to this object through irb_context() or conf(), here’s how you can figure out what configuration options you can change within irb:
  bash> irb
  >> (irb_context.methods - Object.methods).grep(/=$/).sort
  => ["ap_name=", "auto_indent_mode=", "back_trace_limit=", "debug_level=", "echo=", "eval_history=",
   "history_file=", "ignore_eof=", "ignore_sigint=", "inspect_mode=", "io=", "irb=", "irb_name=", "irb_path=",
  "load_modules=", "math_mode=", "prompt_c=", "prompt_i=", "prompt_mode=", "prompt_n=", "prompt_s=", "rc=",
  "return_format=", "save_history=", "use_loader=", "use_readline=", "use_tracer=", "verbose=", "workspace="]
  
  # To change the ap_name
  >> conf.ap_name = "Doh!"
  => "Doh!"

Note that all these modify configuration options except for io= and workspace= which set a context’s IRB::InputMethod and IRB::Workspace objects.

The Overview

Since we’re going to cover all the configuration options, let’s group them into understandable chunks:

History Options

  • :SAVE_HISTORY(number): Sets number of statements to keep in a history file.
  • :HISTORY_FILE (filename): Specify a history file.
  • :EVAL_HISTORY (number): Stores the outputs of the last number of statements in __ .

A sample config:

  IRB.conf[:SAVE_HISTORY] = 1000
  IRB.conf[:HISTORY_FILE] = "/i/like/my/history/here"
  IRB.conf[:EVAL_HISTORY] = 200

Enabling an irb history with :SAVE_HISTORY allows you to reevaluate statements in two ways. First, if your irb has a working readline library, you can look up and execute previous statements in reverse search mode. To enter this mode, type Control-R. Any keys you type will pull up the first statement that contains those letters. Familiarize yourself with what Control-A, Control-E, Control-R and Enter keys do in this mode. The second way is by using irb commands to manipulate and reevaluate several statements. Although irb doesn’t come with any such commands, utility_belt has some decent history commands. I also have my own version that I blogged about a long time ago.

:HISTORY_FILE

If :HISTORY_FILE isn’t set, irb uses the same directory and file format of the detected irbrc for the irb history file. For example, ~/.irbrc leads to a ~/.irb_history whereas a ./irbrc leads to ./irb_history. For related discussion on this read :RC_NAME_GENERATOR.

:EVAL_HISTORY

The :EVAL_HISTORY option saves output/eval history per irb subsession in an IRB::History object stored in __. The number it’s given represents the number of the latest outputs to save. An example of using this option:

  bash> irb
  >> Math.cos 0
  => 1.0
  >> Math.cos Math::PI/2
  => 6.12323399573677e-17
  >> Math.cos Math::PI
  => -1.0
  >> Math.cos Math::PI * 3/2
  => -1.83697019872103e-16

  # Print out the eval history
  >> __
  => 1 1.0
  2 6.12323399573677e-17
  3 -1.0
  4 -1.83697019872103e-16

  >> __.class
  => IRB::History

  # Creating and switching to another irb subsession
  >> irb Class
  >> :yo
  => :yo

  # New eval history
  >> __
  => 1 :yo

If you’re curious how/where irb sets the __, see here. In the same way, _, which saves the last output of any statement, is defined here.

Basic Options

These options either take true or false except for :USE_READLINE.

  • :SINGLE_IRB: Enables all irb subsessions to be evaluated under the same binding. Default is false.
  • :MATH_MODE: Enables math mode. Default is false. Enabling :MATH_MODE or irb -m, alters the default inspect mode and provides math commands by extending the Math module.
  • :RC: Enables loading an irbrc file. Default is true. Disabling :RC or irb -f, turns off any irbrc loading.
  • :USE_LOADER: Enabling :USE_LOADER overrides the default load() and require() with irb’s own commands, irb_load() and irb_require(). Default is false. Irb’s commands evaluate and display each line as if they had been input into irb. Read this to learn more about these irb commands. If you’re unsure what I mean by override, read the implementation.
  • :IGNORE_EOF: Ignores end of line input (Control-D in *nix) or exits current subsession. Default is false.
  • :IGNORE_SIGINT: If true, aborts current operation or returns to top level. If false, exits irb. Default is true.
  • :USE_READLINE: Enables using readline for input. This is a tri-state option: true, false or nil. Default is false or nil depending on readline’s existence. nil acts just like true except when irb is in inf-ruby-mode.

:SINGLE_IRB

If you enable :SINGLE_IRB, all irb subsessions get evaluated under the same global binding, TOPLEVEL_BINDING. This means that any local variables or methods you define are accessible across subsessions:

  # This flag isn't mentioned in irb -h. Whoops.
  bash> irb --single-irb
  >> it = 5
  => 5
  >> def say(num); puts "You can't escape this" + " binding ..." * num; end
  => nil
  >> say it
  You can't escape this binding ... binding ... binding ... binding ... binding ...
  => nil

  # Start a new subsession
  >> irb Date

  # Method and local variable still exist 
  >> say it
  You can't escape this binding ... binding ... binding ... binding ... binding ...
  => nil

In my previous post’s explanation of workspace commands, I mentioned that the one main difference they had from subsession commands was preservation of variables aka same binding. Well, since enabling this option removes that difference, it’s fair to ask: What do workspaces offer that subsessions can’t? The only answer I can come up with is that workspaces use the same context configuration.

Advanced Options

  • :IRB_RC (proc): This proc is called every time a subsession is created.
  • :SCRIPT (string): This is used implicitly when irb is passed arguments on the commandline. Sets irb in a file input mode in which it evaluates the file’s contents as input and prints out the interaction to stdout.
  • :LOAD_MODULES(paths): At startup, loads the given paths using require(). Since require() uses $LOAD_PATH to find files, gems as well as paths relative to the current directory are valid paths.
  • :CONTEXT_MODE (number): Sets the type of binding that irb uses when evaluating statements. Can be either 0, 1, 2 or 3 (3 is the default).
  • :RC_NAME_GENERATOR (proc): Generates an irb config file. Used by irb_history and irbrc. When not set defaults to this.

:IRB_RC

Let’s see if the :IRB_RC proc is called every time an irb subsession is created:

  bash> irb
  # :IRB_RC is called at startup but it's not called this time since we haven't defined it yet.

  # The argument passed to lambda is the current context.
  >> IRB.conf[:IRB_RC] = lambda {|context| puts "Entered #{context.main}" }
  => #<Proc:0x00057648@(irb):1>

  # Create subsessions
  >> irb 'testin'
  Entered testin
  >> irb Array
  Entered Array

It sure does. What if we only want to have the proc run once i.e. prevent future subsessions from calling it?:

  >> IRB.conf[:IRB_RC] = lambda {|e| puts "Entered #{e.main}"; IRB.conf.delete(:IRB_RC) }
  => #<Proc:0x00091ac8@(irb#2):1>

  >> irb 'byebye'
  Entered byebye

  >> IRB.conf[:IRB_RC]
  => nil

  # Proc isn't called anymore since it doesn't exist
  >> irb 'testin'
  >>

Utility belt extends the functionality of this option by setting it to a hash of procs like this:

  Object.const_set("IRB_PROCS",{}) unless Object.const_defined?(:IRB_PROCS)
  IRB.conf[:IRB_RC] = lambda do
    IRB_PROCS.each {|key, proc| proc.call }
  end
  

With this version of :IRB_RC, multiple libraries/files can have their procs run if they define their entries in IRB_PROCS:

  # in a rails-specific file
  IRB_PROCS[:rails] = lambda { "set my final rails irb tweaks here"}

  # ... in some other file
  IRB_PROCS[:other] = lambda { "other tweaks"}

Personally, I like to have my procs run once and to pass the current IRB::Context object:

  Object.const_set("IRB_PROCS",{}) unless Object.const_defined?(:IRB_PROCS)
  IRB.conf[:IRB_RC] = lambda do |context|
    IRB_PROCS.each {|key, proc| proc.call(context); IRB_PROCS.delete(key)}
  end

:SCRIPT

This option is used quietly whenever you pass irb an argument. The argument, usually a file, is processed by Kernel#open and its result is fed to irb as input. For example:

  # Create a file message.rb:
  message = "irb from "
  message + __FILE__
  
  # Run from the commandline
  bash> irb -f --prompt simple message.rb
  >> message = "irb from "
  => "irb from "
  >> message + __FILE__
  => "irb from blah.rb"
  >> bash>

Since Kernel#open can open a subprocess and read its output:

  # Generates a mandelbrot set, from http://www.xcombinator.com/2008/02/22/ruby-inject-and-the-mandelbrot-set
  bash> irb -f "|curl -s http://gist.github.com/raw/120183/22aa68d0451ab2ac69a252f5d6a2acff2a04ea3b/gistfile1.rb"
  # Prints out irb processing the code and then generates:
                                                            **                   
                                                          ******                 
                                                        ********                 
                                                          ******                 
                                                       ******** **   *           
                                               ***   *****************           
                                               ************************  ***     
                                               ****************************      
                                            ******************************       
                                             ******************************      
                                          ************************************   
                                 *         **********************************    
                            ** ***** *     **********************************    
                            ***********   ************************************   
                          ************** ************************************    
                          ***************************************************    
                      *****************************************************      
  ***********************************************************************        
                      *****************************************************      
                          ***************************************************    
                          ************** ************************************    
                            ***********   ************************************   
                            ** ***** *     **********************************    
                                 *         **********************************    
                                          ************************************   
                                             ******************************      
                                            ******************************       
                                               ****************************      
                                               ************************  ***     
                                               ***   *****************           
                                                       ******** **   *           
                                                          ******                 
                                                        ********   
                                                         ******                 
                                                            **

Instead of opening a subprocess, you could just pipe a command into irb:

  bash> curl -s http://gist.github.com/raw/120183/22aa68d0451ab2ac69a252f5d6a2acff2a04ea3b/gistfile1.rb | irb -f
  # same output as above ...

:CONTEXT_MODE

This option is perhaps the only option whose use case I don’t understand. Depending on the option’s value, irb statements are evaluated with a different type of binding:

  • 0: Creates binding with eval(“proc{binding}.call”, TOPLEVEL_BINDING …).
  • 1: Creates binding in a temporary file.
  • 2: Create binding in a temporary file using threads.
  • 3: Creates binding with eval(“def irb_binding; binding; end; irb_binding”, TOPLEVEL_BINDING …)

My guesses for using values 1 and 2 are for security. As for values 0 and 3, the difference is that the former has access to TOPLEVEL_BINDING while the latter doesn’t:

  >> eval("a = 2", TOPLEVEL_BINDING)
  => 2
  >> eval "a", eval("proc{binding}.call", TOPLEVEL_BINDING)
  => 2
  >> eval "a", eval("def irb_binding; binding; end; irb_binding", TOPLEVEL_BINDING)
  NameError: undefined local variable or method `a' for main:Object
          from (irb):4:in `eval'
          from (eval):1
          from (eval):1

:RC_NAME_GENERATOR

This option is used to generate irb config files i.e. irbrc and irb_history files in a standard way. Since irbrc is already loaded when your config is read, most irb users would only have this effect their irb_history. An example use case:

  # In your irbrc:
  # This looks for the first config file that exists in /etc/irb/ and then ~/.irb/
  IRB.conf[:RC_NAME_GENERATOR] = lambda {|name|
    ["/etc/irb/#{name}.rb", "#{ENV['HOME']}/.irb/#{name}.rb"].detect {|f|
      File.exists?(f)
    }
  }

  # In an irb lib for rails:
   load(config) if (config = IRB.rc_file("rails"))
  # Loads /etc/irb/rails.rb or ~/.irb/rails.rb

Visual Options

  • :AUTO_INDENT(boolean): Auto indents code when defining blocks, methods, classes, etc. Default is false.
  • :INSPECT_MODE (booleanish): When true or nil, formats output by calling inspect() on the statement result. When false just prints output. Default is nil. Note that setting this option from the commandline i.e. @context.inspect_mode= is buggy: it needs to be called twice to set it to false when initially nil.
  • :ECHO (booleanish): When true or nil, a statement’s output is printed. When false, no output is shown. Default is nil.
  • :PROMPT (hash): Hash of available prompts, mapping symbols to prompt hashes.
  • :PROMPT_MODE(symbol): Current prompt, pointing to a key in :PROMPT.

:ECHO

A common irb complaint is that its output is too verbose. Disabling this option makes irb print no output. Here’s a handy snippet that uses this option to toggle irb output:

  def irb_verbosity_toggle
    irb_context.echo ? irb_context.echo = false : irb_context.echo = true
  end

Note: Utility belt has another way to toggle irb’s verbosity, although not as terse.

:PROMPT

To create your own prompts, add an entry in :PROMPT. Your prompt should be a hash with the following keys:

  • :PROMPT_I: Normal prompt
  • :PROMPT_S: Prompt when continuing a string
  • :PROMPT_C: Prompt when continuing a statement
  • :PROMPT_N: Prompt when indenting code
  • :RETURN: String that prefixes output of a statement. Since this is passed to Kernel#printf(), the output could be displayed with any of its string flags, though most choose %s.

Updated: The different prompt types that start with :PROMPT* substitute string flags for certain values:

  • %N – Value from Irb.conf[:IRB_NAME]
  • %m – self.to_s
  • %M – self.inspect
  • %l – Character needed to finish continuing a string. Should be used with :PROMPT_S.
  • %zi – Indent level with optional number z for printf width.
  • %zn – Line number with optional number z for printf width.
  • %% – Literal percentage sign.

For more details on prompts I suggest the Configuring the Prompt section in the pickaxe or just check out the irb implementation code.

Here’s an example of creating a custom prompt:

  bash> irb --prompt simple

  # Create a basic prompt :DEMO
  >> IRB.conf[:PROMPT][:DEMO] = {:PROMPT_I => "normal> ", :PROMPT_S => "string_continue> ", 
  >> :PROMPT_C => "statement_continue> ", :PROMPT_N => "indent> ", :RETURN => "<< %s >>\n" }
  => {:PROMPT_I=>"normal> ", :PROMPT_S=>"string_continue> ", :PROMPT_C=>"statement_continue> ", 
  :PROMPT_N=>"indent> ", :RETURN=>"<< %s >>\n" }

  # Enable the created prompt
  >> conf.prompt_mode = :DEMO
  << :DEMO >>

  # PROMPT_I and PROMPT_C
  normal> 5 *
  statement_continue> 5
  << 25 >>

  # PROMPT_S
  normal> 'yoda
  string_continue> '
  << "yoda\n" >>

  # PROMPT_N
  normal> class Yoda
  indent>   end
  << nil >>

Debug Options

  • :BACK_TRACE_LIMIT (number): Number of messages to display from beginning and end of a caught error’s backtrace. Default is 16.
  • :USE_TRACER (boolean): Enables using the standard library Tracer to trace execution in irb.
  • :VERBOSE (boolean): Sets the verbosity of a few context-related events. Seeing that verbose? is used by context objects, which doesn’t respect this option, my guess is this option is broken.
  • :DEBUG_LEVEL(number): Enables debugging for irb developers i.e. debugging the ruby lexical analyzer.

:BACK_TRACE_LIMIT

To better understand :BACK_TRACE_LIMIT, let’s play ping pong:

  # Define the players
  >> def ping(num,max); raise "Ping wins" if num > max; pong(num+1,max); end
  => nil
  >> def pong(num,max); raise "Pong wins" if num > max; ping(num+1,max); end
  => nil

  >> conf.back_trace_limit
  => 16

  # The stacktrace is much less than the limit so we should see the full stacktrace.
  >> ping 1,10
  RuntimeError: Ping wins
          from (irb):1:in `ping'
          from (irb):2:in `pong'
          from (irb):1:in `ping'
          from (irb):2:in `pong'
          from (irb):1:in `ping'
          from (irb):2:in `pong'
          from (irb):1:in `ping'
          from (irb):2:in `pong'
          from (irb):1:in `ping'
          from (irb):2:in `pong'
          from (irb):1:in `ping'
          from (irb):4

  >> conf.back_trace_limit = 3
  => 3

  # Now that the limit is 3, we only see the first and last 3 messages of the trace.
  >> ping 1,10
  RuntimeError: Ping wins
          from (irb):1:in `ping'
          from (irb):2:in `pong'
          from (irb):1:in `ping'
  ... 6 levels...
          from (irb):2:in `pong'
          from (irb):1:in `ping'
          from (irb):6>>

:USE_TRACER

(#use_tracer):USE_TRACER uses the Tracer standard library to trace statements. Since Tracer isn’t that well documented, I should at least explain that it’s basically a wrapper around Kernel#set_trace_func. Here’s what it looks like:

  bash> irb -f --tracer --prompt simple
  /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tracer.rb:150: warning: tried to create Proc object without a block
  /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tracer.rb:146: warning: tried to create Proc object without a block
  >> :dude.to_s
  #0:(irb):1:Object:-: -
  #0:(irb):1:Symbol:>: -
  #0:(irb):1:Symbol:<: -
  => "dude"

  # To turn it off
  >> conf.use_tracer = false
  #0:(irb):5:Object:-: -
  => false

Probably the most confusing aspect of Tracer is not knowing what the different fields delimited by ‘:’ represent. Looking into the source reveals the fields in order are: thread number, file, line, klass, event type (defined by Tracer::EVENT_SYMBOL) and the actual line of code ( – if line not found).

Questionable Options

The main reason to describe these options isn’t to use them but to know there is little need to use them.

  • :AP_NAME: Give your irb app a name.
  • :IRB_LIB_PATH: The directory of the currently used irb.rb. Used when tracing with :USE_TRACER
  • :VERSION: Version of irb specified by the version file.
  • :IRB_NAME: A name for the current irb subsession, which is available as %N in an irb prompt string. Used internally to set a context’s irb_path().
  • :__*: There are at least two options that start with underscores that don’t have an apparent reason for being in IRB.conf. Just ignore ’em.
  • :LC_MESSAGES: Stores the current IRB::Locale object. If you want to change languages, you can modify this object directly or you can set environment variables such as ENV['IRB_LANG'].
  • :MAIN_CONTEXT: This stores the IRB::Context object belonging to the first subsession. Used internally as a global setting in a couple of useful places. Remembering that irb_context saves the current IRB::Context object, let’s relate it to this option:
bash> irb
>> irb_context == IRB.conf[:MAIN_CONTEXT]
=> true
>> irb ‘another subsession’
>> irb_context == IRB.conf[:MAIN_CONTEXT]
=> false

Wrap Up

If you survived this in one sitting, wow! For further irb exploration, here’s my irbrc. Some future irb topics I’m thinking of writing up include customizing irb completion, a guide to hacking irb, strategies for creating/managing many irb commands and using irb’s xmp. If you have suggestions on irb topics you’d like to learn about, please comment them.

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