rink

gem install rink

Makes interactive consoles awesome. More specifically, it does this by automating as much as conceivably possible so that you can get right down to what you really care about: your application.

After the second copy-and-paste of an interactive console that I’d written for a few of my various gems, I decided to extract that code into a plug-and-play-friendly console gem. This library is the result.

Examples

To create a new interactive console:

require 'rink'
Rink::Console.new

The above example creates a console, but it doesn’t add much in the way of usefulness. To really get started on your application-specific interactive console, extend the Rink::Console class:

class MyConsole < Rink::Console
  # ...
end

#...
MyConsole.new

The Ruby Console

With the default set of options, any input that is not processed as a custom command will be executed as Ruby code, much like the Ruby IRB. Guess there’s not much more to say about that, so here’s an example to drive the point home:

>> Interactive Console <<

Rink::Console > class Person
Rink::Console >   attr_reader :first_name
Rink::Console >   def initialize
Rink::Console >     @first_name = "Colin"
Rink::Console >   end
Rink::Console > end
  => nil
Rink::Console > Person.new.first_name
  => "Colin"
Rink::Console >

The Rink Namespace

By default, Rink will execute code within the context of its namespace (see the Rink::Console class documentation for details). Basically, you can choose any Ruby object and run the console within the context of that object. For example:

class Person
  attr_reader :first_name

  def initialize
    @first_name = "Colin"
  end
end

class MyConsole < Rink::Console
  option :namespace => Person.new
end

MyConsole.new

>> Interactive Console <<

MyConsole > self
  => #<Person:0x10171c3a0 @first_name="Colin">

MyConsole > first_name
  => "Colin"

Custom Commands

Rink isn’t limited only to running Ruby code, however. You can also add specific commands to Rink like so:

class MyConsole < Rink::Console
  command :help do |args|
    if args.length == 0
      puts "What do you need help with?"
    else
      puts "Sorry, I don't know anything about #{args.inspect}."
    end
  end
end

MyConsole.new

# produces...

>> Interactive Console <<

MyConsole > help
What do you need help with?

MyConsole > help feed the poor
Sorry, I don't know anything about ["feed", "the", "poor"]

MyConsole >

In addition to commands, you can also easily add or override default options in your console:

class MyConsole < Rink::Console
  option :allow_ruby => false, :welcome => "Hello there!"
  command :greet_me do |args|
    puts options[:welcome]
  end
end

MyConsole.new

# produces...

>> Interactive Console <<

MyConsole > inspect
I don't know the word "inspect."

MyConsole > greet_me
Hello there!

See the class documentation for Rink::Console for much more detailed information, including how to disable Ruby code processing entirely.

Running From The Command Line

You’ve seen numerous examples of how to start Rink from within Ruby. The same thing works from within IRB or inside of a Rake task. Additionally, Rink ships with a script so that you can run it directly from the command line:

$ rink

If you’ve extended Rink, you can give it the name of the class you extended it with. Rink will look in both the current directory, and the “lib” directory (if present) beneath the current location:

$ rink My::Console

Loaded constant My::Console from /Users/colin/projects/gems/rink/my/console.rb

>> Interactive Console <<
My::Console >

If you need to load the console from a nonstandard directory, specify the path to that directory as a second argument. Rink will check both that directory and any ‘lib’ directory beneath it for your console.

Testing

Testing your console turns out to be really easy, since the Rink::Console initializer takes some optional arguments to override the input and output streams. To can the set of input, for instance, just use:

input_string = "help"
MyConsole.new(:input => input_string)

You’ll see the output of the above dumped to STDOUT. If you want to capture that output, the answer is pretty obvious:

output_string = ""
MyConsole.new(:input => input_string, :output => output_string)
#=> output_string now contains the contents of the result of running the commands found inside of input_string.

You can also pass IO objects in directly:

File.open("commands.txt", "r") do |cmd_file|
  File.open("output.log", "w") do |log_file|
    MyConsole.new(:input => cmd_file, :output => log_file)
  end
end

Dealing With Errors

Normally, if an error occurs, Rink will print a nicely-formatted message to its output stream. This is helpful if you’re using the console but not if you’re trying to write tests for one. So, you can disable error catching within Rink by passing :rescue_errors => false to the initializer:

MyConsole.new(:input => "raise", :rescue_errors => false)
#=> RuntimeError!

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010 Colin MacKenzie IV. See LICENSE for details.