bombshell
Ever wanted to give dudes the ability to explore your library interactively? Like, with a custom IRB-like shell/console?
Really, you did? Weird.
Simple example
pizza/bin/pizza:
#!/usr/bin/env ruby
$:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
require 'rubygems'
require 'pizza'
Bombshell.launch(Pizza::Shell)
pizza/lib/pizza/shell.rb:
require 'bombshell'
module Pizza
class Shell < Bombshell::Environment
include Bombshell::Shell
prompt_with 'pizzabot'
def order(size)
Pizza::Order.new(:size => size).place!
puts 'Your pizza has been ordered! Super!'
end
end
end
Let's try it out:
$ pizza
pizzabot> order 'large'
Your pizza has been ordered! Super!
pizzabot>
Prompts
You set your prompt like this:
prompt_with 'pizza_bot_loves_you'
Or like this:
prompt_with do
"pizza_bot / #{Time.now}" # binding is on your shell *class*
end
Or even like this:
prompt_with do |shell|
"pizza_bot / #{shell.size}" # the block gets the shell *instance* when it asks for it
end
Callbacks
You can set callbacks like this:
before_launch do
init # binding is on your shell *class*
end
before_launch do |size|
Pizza.default_size = size # the block gets as many command-line parameters as you ask for
end
having_launched do
puts size if size # binding is on your shell *instance*
end
Subshells
If you dump all of your functionality into one shell, things could get a little messy. That's why we have subshells:
pizza/lib/pizza/shell.rb:
require 'bombshell'
module Pizza
class Shell < Bombshell::Environment
include Bombshell::Shell
prompt_with 'pizzabot'
def pizza
Order.launch
end
end
end
pizza/lib/pizza/shell/order.rb:
require 'bombshell'
module Pizza
class Shell
class Order < Bombshell::Environment
include Bombshell::Shell
prompt_with 'new order'
def size(s)
@size = s
puts 'You got it!'
end
def topping(t)
@toppings ||= []
@toppings << t
puts "Added #{t}"
end
def order
Pizza::Order.new :size => @size, :toppings => @toppings
puts 'Coming right up!'
quit
end
end
end
end
Let's try it out:
pizzabot> pizza
new order> size 'large'
You got it!
new order> topping 'pepperoni'
Added pepperoni
new order> order
Coming right up!
pizzabot>
Tab completion
It's there. Give it a whirl with TAB.
To use:
Create a class for your shell and
include Bombshell::Shell. You should also set this class to inherit fromBombshell::Environmentas that will ensure your shell doesn't have any extraneous "commands" (i.e. methods) inherited from Object. (If you'd rather use a different basis--likeCleanSlate--orundefmethods yourself, go right ahead.)Define your commands as instance methods on this class. There's nothing too funny going on here, it's just Ruby.
Kick off the shell with
Bombshell.launch(YourShellClass). It's possible to do this from IRB but it's kind of messy (constant reassignment warnings). Instead, set up a "binary" for yourself likepizza/bin/pizzaat the top of this file.
Hints:
- Give your users a
helpcommand! - Use subshells for hierarchical interactivity!
- Provide as thin of a wrapper you can above your library! We want to see what's going on!
Copyright
Copyright (c) 2011 Andy Rossmeissl. See LICENSE.txt for further details.