Mini Irb and Mini Script/Console
In my last post, I introduced Bond‘s own version of irb’s completion. What I didn’t emphasize is that it doesn’t need irb. To prove it, I’ll show you a mini irb which has persistent readline history, error handling and Bond’s autocompletion … in 7 lines.
Mini Irb
A good starting point for a basic irb comes from this github snippet:
loop do
print '>> '
output = eval($stdin.gets)
print '=> '
puts output.inspect
end
It’s got the very basics of a repl: take a user’s input, eval it and print the result. Being as basic as it is, there’s no autocompletion, persistent readline history, etc.
But who says that needs to be much larger to get those features?:
1 %w{readline rubygems bond}.each {|e| require e }
2 Bond.start
3 history_file = File.join(ENV["HOME"], '.mini_irb_history')
4 IO.readlines(history_file).each {|e| Readline::HISTORY << e.chomp } if File.exists?(history_file)
5 while (input = Readline.readline('>> ', true)) != 'exit'
6 begin puts "=> #{eval(input).inspect}"; rescue Exception; puts "Error: #{$!}" end
7 end
8 File.open(history_file, 'w') {|f| f.write Readline::HISTORY.to_a.join("\n") }
A line by line play:
- Line 1-2: Loads readline, bond and bond’s improved version of irb-like completion.
- Line 3-4: Read in previous history from a history file.
- Line 5: Get the users input and start a loop that only ends with exit.
- Line 6: Evaluate the input and print an exception if it occurs.
- Line 8: Write all of the loaded history back to the history file.
If you’re not familiar with readline, you may be wondering where is the autocompletion being handled? Well, all of readline’s goodness is channeled through Readline.readline
. Bond simply sets Readline.completion_proc
and readline handles the rest. If you’d like to play with this, fork away!
Here’s what mini-irb looks like:
bash > ruby mini-irb.rb # Unlike irb, mini-irb correctly completes on chains of objects >> :some.to_s.split('').a ).all? ).any? ).assoc ).at # Completes require's arguments >> require 'a[TAB] >> require 'abbrev.rb' => true # Completes files >> File.read '~/[TAB] >> File.read '/Users/bozo/.ir[TAB] >> File.read '/Users/bozo/.irbrc' # Oh noz, wherz my irbz? >> IRB Error: (eval):1: uninitialized constant IRB # Try this one in irb >> raise Exception Error: Exception >> exit bash>
Mini script/console
Just for fun I figured I’d see how much more code it’d take to make a mini script/console for Rails. I’m sorry. It took one more line:
['readline', 'rubygems', 'bond', File.dirname(__FILE__) + '/../config/boot'].each {|e| require e }
Bond.start
["#{RAILS_ROOT}/config/environment", 'console_app', 'console_with_helpers'].each {|e| require e}
history_file = File.join(ENV["HOME"], '.myrb_history')
IO.readlines(history_file).each {|e| Readline::HISTORY << e.chomp } if File.exists?(history_file)
while (input = Readline.readline('>> ', true)) != 'exit'
begin puts "=> #{eval(input).inspect}"; rescue Exception; puts "Error: #{$!}" end
end
File.open(history_file, 'w') {|f| f.write Readline::HISTORY.to_a.join("\n") }
To be fair this is a stripped down version of script/console i.e. it doesn’t offer the debugger or sandbox. But those could be easily added. If you want to improve on this, fork away! If you want to see what this looks like, just give it a try. :)
Comparing Apples to Oranges
So how does mini-irb compare to irb? Well, it’s like comparing apples to oranges. Irb weighs in at 5000+ loc while mini-irb is 7 + 540 loc with Bond. Irb sports a number of features which I’ve documented extensively. But I’m finding less need for them every day. The only feature I’d keep is the ruby lexer to handle multiple lines of code. But even then I’d rather prototype multiple lines in an editor.
What are your two cents? What features from irb would you add to mini-irb?