info | twitter | github
J-_-L | Writings on Ruby Programming
18.12.10
4 comments

ripl: Why should you use an irb alternative?

What does it mean when the guy who blogged about irb’s details and wrote most of the successful irb gems (hirb, bond, boson) decides to implement his own irb alternative? There must be something wrong with irb!

OK, to be polite, nothing is wrong with irb. Except that it is old and big. irb consists of more than 5000 lines – is this really necessary? You can build a simple irb in less then 40 byte ;)

Listing 1
/44/minirb.rb ruby
 1
loop{$><<">> ";gets;$><<'=> ';p eval$_}

The purpose of most of irb’s code is to analyse the input for multi-line and auto-indent support. ripl (which stands for “ruby interpreter print loop”) does not care about these things and aims to be small and modern.

So, what does it do better than the “5000 lines” and “39 bytes” approaches? The github description tells it:

ripl is a light, modular alternative to irb. Like irb, it loads ~/.irbrc, has autocompletion and keeps history in ~/.irb_history. Unlike irb, it is highly customizable via plugins and supports commands i.e. ripl-play. This customizability makes it easy to build custom shells (i.e. for a gem or application) and complex shells (i.e. for the web).

The main advantage: It is easily extendable!

Furthermore, it is better documented than irb and comes with tests ;)

Usage

ripl can be installed with:

$ gem install ripl

As already mentioned, ripl even comes without multi-line support, but it is available as plugin:

$ gem install ripl-multi_line

To enable a plugin, you have to add it to your ~/.riplrc file (create one if it does not exit) like this:

require 'ripl/multi_line'

ripl loads your ~/.irbrc file, which typically contains some irb specific options (e.g. IRB.conf[:PROMPT]). To avoid errors, you can install ripl-irb, which catches calls to the IRB constant and prints messages to convert irb configuration to ripl equivalents.

Now you can start ripl and play around!

Hm… pretty boring. Just like irb… It needs more plugins!

Available plugins

You can install ripltools, a meta gem which downloads some useful general ripl plugins:

$ gem install ripltools

There are some plugins, which improve your ripl experience without requiring you to learn something (I would call them “passive” plugins): There is ripl-color_error, ripl-color_result, ripl-color_streams and ripl-auto_indent which are pretty self-descriptive. Another one is ripl-short_errors which modifies error messages to only show the first backtrace entry of an error. The complete backtrace can be shown manually by the _! method.

Furthermore, some “extra commands” plugins are included in ripltools.

ripl-commands adds some basic commands, similar to irb’s commands. Because there are no sub-sessions in ripl, it provides another way of getting “into” an object – jumps:

>> jump [1,2,3,4]
=> [1, 2, 3, 4]
>> size
=> 4
>> jumps
=> [main, [1, 2, 3, 4]]

ripl-play allows you to record (and playback) input sequences in ripl. It also allows playback of code snippets from the internet.

$ ripl play https://gist.github.com/725338 
>> a = 10 ** 2
=> 100
>> a + 10
=> 110
>>

It is also possible to create your specialized sub-shell (e.g. for your gem). Currently, there is a rails console, a rack console, a hijack shell using ripl, a web shell, my ruby/system hybrid shell and even a javascript (firefox tracemonkey) shell!

More plugins

The ripl github page has a large list of available plugins. You can also search through rubygems with:

$ gem list -r ripl-.*

It is also worth mentioning that a lot of irb gems do actually not use irb specific methods and are fully compatible with ripl.

Write your own plugins

It is super simple to write your own ripl plugins. Here is a short example plugin:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# by cldwalker
require 'ripl'

module Ripl::CustomErrors
  def print_eval_error(err)
    if handler = config[:custom_errors][err.class]
      handler.call(err)
    else
      super
    end
  end
end

Ripl::Shell.send :include, Ripl::CustomErrors
Ripl.config[:custom_errors] = {}

All it does: When an error is encountered, check if it is a one of an error class that should be treated differently.

A plugin basically consists of one big and two small parts:

  • Firstly, a module with all the logic – this is the main part (see below)
  • Secondly, the include statement to hook the code in, in this example: Ripl::Shell.send :include, Ripl::CustomErrors
  • And thirdly, some plugin default configuration for the Ripl.config hash. The settings a plugin uses should be prefixed with the plugin name.

The Ripl::Shell module is the most important one, because it contains the ruby interpreter print loop:

before_loop
loop
  loop_once
    get_input
      prompt

    eval_input
      loop_eval
        (print_eval_error)
          format_error

    print_result
      format_result

    (handle_interrupt)
after_loop

You can extend each of these methods, except loop. You can (and mostly should) use the super keyword to call the original ripl functionality (or the next plugin). This is possible, because the base methods are not directly declared in the Ripl::Shell class, but in an API module which gets included before any plugin. You should also read the main source file before starting to hack.

Besides Ripl::Shell, there is Ripl::Commands and Ripl::Runner, which work in a similar manner. You can include your custom module into Ripl::Commands and its methods become available in a ripl session (although, they are only available after a call to add_commands(eval("self", @binding))). You can extend Ripl::Runner to customize the starting process, e.g. by adding command line options.

To see some small example plugins, take a look at ripl-misc!

Creative Commons License

Comments

18.12.10

cldwalker

You may want to mention that unlike irb, ripl is well documented and tested. Also ripl has better autocompletion and 0.3.0 should have autocompletion hooks for plugins. As for ripl-irb, it's worth knowing it prints messages to convert irb configuration to ripl equivalents.
Thanks for the writeup. :)

18.12.10

J-_-L

Yep, added these points.

18.12.10

trans

Instead of using `Ripl::Shell..send :include, ...` why don't you just make `include` a public method for Shell? Either that or define a new method like `use` to do the same.

19.12.10

J-_-L

Hi trans. I don't know exactly, why he did not do that, but I guess the reason is to highlight that no more magic happens. Just the including Ruby programmers are used to.