-
Notifications
You must be signed in to change notification settings - Fork 0
Custom commands
- Block commands
- Class commands
- Complete example
- Regexp commands
- Command options
- Context methods
- Conventions
Pry ships with a large number of builtin commands to cover common use-cases. Unfortunately we can't hope to include every command that might be useful to anyone, so we also make it easy to define new commands for yourself. This page explains how to do so.
For further explanation of the concepts behind commands, and how to use them, see the page on the command system.
### Block commandsBlock commands are a quick way of definining simple commands, unlike Class commands they don't do any options parsing making them only suitable for quick hacks and personal utilities.
To create a new block command, you call Pry::CommandSet#block_command
, for example:
Pry::Commands.block_command "hello", "Say hello to three people" do |x, y, z|
output.puts "hello there #{x}, #{y}, and #{z}!"
end
This creates a command, hello
which you can call from within Pry:
pry(main)> hello tom dick harry
hello there tom, dick and harry!
It also sets up the commands description, so that it appears in the list of help
. For block commands the description is also used for the output of help <command>
:
pry(main)> help
...
hello Say hello to three people
...
pry(main)> help hello
Say hello to three people
As illustrated in the example, and explained further in Conventions, you should use output.puts
rather than puts
to make your command easily re-usable in situations where Pry is not using $stdout
.
Just as with normal commands, block commands are executed with a number of Context methods available.
The third argument to Pry::CommandSet#block_command
are the command options, these define extra behaviours for commands, for example setting keep_retval
allows you to return a value from your command:
Pry::Commands.block_command "add-numbers", "Add a list of numbers", :keep_retval => true do |*args|
args.map(&:to_i).inject(&:+)
end
This means that the output will be pretty-printed using the user's pretty printer and more importantly, it is made available in _
.
pry(main)> add-numbers 1 2 3 4
=> 10
pry(main)> _ + 5
=> 15
Creating a simple command is very similar to creating a block command, though a little more verbose:
Pry::Commands.create_command "echo" do
description "Echo the input: echo [ARGS]"
def process
output.puts args.join(" ")
end
end
Every time the command is invoked by the user, a new instance of the command class is created, it is setup as described in the Complete example, and then the process method is called.
Class commands were designed to help commands introspectable, they provide a declarative syntax for setting up the description
, and in addition to setting up help
like a Block command, class commands also respond to --help
:
pry(main)> help
...
echo Echo the input: echo [ARGS]
...
pry(main)> echo --help
Echo the input: echo [ARGS]
options:
-h, --help Show this message.
NOTE: it is possible to pass the description
and command_options
as arguments to create_command
, just like block_command
.
The main advantage of class commands is that you can easily support command-line style options or flags. This is done by overriding the options
method:
Pry::Commands.create_command "echo" do
description "Echo the input: echo [ARGS]"
# opt is a Slop object, see https://github.com/injekt/slop/blob/master/README.md
def options(opt)
opt.on :u, :upcase, "Upper-case the arguments before echoing"
opt.on :d, :downcase, "Lower-case the arguments before echoing"
end
def process
string = args.join(" ")
string.upcase! if opts.upcase?
string.downcase! if opts.downcase?
output.puts string
end
end
As you can see there are two methods defined on commands that make it easy to handle options:
-
opts
returns the Slop object that parsed the flags. -
args
returns any positional arguments that are left over.
Pry currently uses the Slop parser library internally, so please look at their README for exactly what kind of options you can support.
### Complete exampleThis section gives a summary of all the things you can do when defining a new Class Command using the echo
command from the previous section as an example.
Pry::Commands.create_command "echo" do
# The description is a short summary (aim for a maximum of about 80 characters).
# It is displayed after the command's name in the list of commands output by `help`.
#
# If you don't set a description, the String "No description." is used.
description "Echo the input."
# The banner is a longer summary (aim for several lines).
# It is displayed above the options when running `command --help` or `help command`
# Normally the first line is a summary of the usage.
#
# If you don't set a banner, the description is used.
banner <<-BANNER
Usage: echo [ -u | -d ] [ -j <joiner> ] <words>
Prints the words to the output, upcasing or downcasing as appropriate, separated
by the joiner string.
BANNER
# The command options are explained in the Command options section, they allow
# tweaking of the default behaviour of commands. You can normally omit them as the
# default options will usually work.
command_options(
:shellwords => false
)
# The {setup} method is called right at the start of command invocation, before
# {options} is called, and before {process} is called. You can use it to initialize
# state that the rest of your command will need, please don't override {initialize}
# itself. Every time setup is called will be on a fresh instance of the Command Class
# so you don't need to clean up old state left by previous invocations.
def setup
@joiner = " "
end
# The {options} method is called between {setup} and {process} in order to initialize
# the {opts} and {args}.
#
# It is also called (without {setup} or {process}) when displaying the help message, so
# it should not have any side-effects.
def options(opt)
opt.on :u, :upcase, "Upper-case the arguments before echoing"
opt.on :d, :downcase, "Lower-case the arguments before echoing"
opt.on :j, :joiner, "Join arguments with a string (default: ' ')", true do |j|
@joiner = j
end
end
# The {process} method is where all the magic of your command should happen.
# It isn't passed any options, but see the section on Context methods for an
# overview of what it can access in addition to {opts} and {args}
def process
# CommandErrors can be raised when the command has gone wrong in an
# expected way. They render nicely in the Pry REPL.
raise Pry::CommandError, "-u doesn't make sense with -d" if opts.u? && opts.d?
string = joined_args
string.upcase! if opts.upcase?
string.downcase! if opts.downcase?
output.puts string
end
# You can define your own private helper methods for this command. If you want
# to share helpers, then define them using Pry::Commands.helpers{ def foo; end }
# instead.
private
def joined_args
args.join(@joiner)
end
end
Both Block Commands and Class Commands can be defined as a Regexp instead of a String. This is most useful if you want to provide fun syntax for commands. If you want to do this, then you should also set the :listing command option to a String so that the output of "help" looks correct.
The capture groups of the regular expression are made available in two places, firstly by calling the captures
method, and secondly by being added to the front of the args
array.
Pry::Commands.create_command /wtf([?!]*)/ do
description "Show the exception backtrace, add more ?!s to see more lines."
command_options :listing => "wtf?!"
def process
raise Pry::CommandError, "No most-recent exception" unless _pry_.last_exception
number_of_lines = [captures[0].size, 0.5].max * 10
output.puts _pry_.last_exception
output.puts _pry_.last_exception.backtrace.first(number_of_lines)
end
end
This can then be used:
pry(main)> 1 / 0
ZeroDivisionError: divided by 0
pry(main)> wtf?!?
divided by 0
(pry):1:in `/'
(pry):1
Command options are parameters that tweak the behaviour of commands, they are provided by calling the command_options
method on Class Commands, or as the third parameter when defining Block Commands.
-
:keep_retval (Boolean) — Whether or not to use return value of the block for return of command or just to return
void
(the default). If your command returnsvoid
even if it has :keep_retval set to true, the command will have no return value and will show no eval output (e.g=> result
) - :requires_gem (Array) — Whether this command is an optional command contingent upon any gem dependencies. If it is and dependencies not met then command is disabled and a stub proc giving instructions to install command is provided. Note only use this option if you want your command to be optional; use of this option should be rare.
-
:interpolate (Boolean) — Whether string
#{}
based interpolation is applied to the command arguments before executing the command. Defaults to true. - :use_prefix (Boolean) — Whether or not the command responds to the command prefix config option. Defaults to true.
- :use_shellwords (Boolean) — Whether the commands arguments are tokenized using Shellwords. Defaults to true. If false, arguments are simply split on spaces instead. Setting it to false is most useful if you want to accept arbitrary ruby as an argument, as you can re-combine the arguments before eval'ing them.
-
:listing (String) — The listing name of the command. That is the name by which the command is looked up by
help
and byshow-source
. Necessary for regex based commands. -
:takes_block (Boolean) - Whether the command can take a block. A block is passed to a command by interposing a
|
character before providing the Ruby block syntax. e.gmy-command arg1 | { puts 'a block!' }
. The block is then made available inside the command via thecommand_block
method. This method will returnnil
if no block was passed or:takes_block
wasfalse
.
All commands can access a few special methods, in addition to those added to the helper module of their command sets.
-
opts
— The options as parsed by Slop. (Class Commands only) -
args
— The arguments that remained after Slop parsed the opts. (Class Commands only) -
arg_string
— The string that appeared after the command name, or after the end of the regexp match for regexp commands. -
captures
— The captures in the regexp (Regexp Commands only) -
command_block
- The passed block (only available if:takes_block => true
and a block was in fact passed).
-
_pry_
— The current instance of Pry that's running. -
target
— The binding that is currently being Pry'd. Most useful fortarget.eval
, which lets you eval code as though you were the user. -
target_self
— A short-cut fortarget.eval('self')
-
eval_string
— The cumulative lines of input for multi-line input. Modifying this modifies the input buffer (used by commands such asamend-line
) -
output
— The current output object.
-
command_options
— The current command's options. -
command_name
— The current command's name. -
description
— The current command's description. -
block
— The block in which the current command was defined.
-
command_set
— The command set through which the command was invoked. -
run
— Run another command in the current command set. -
void
— A value indicating that a command returned no value.
When you define your own commands and especially if you distribute them as plugins it is important you follow a few conventions.
Ensure all output goes to the output
object
Ensuring that you use output.puts
and not just the top-level puts
allows all Pry output to be redirected by the user.
Example:
Pry::Commands.block_command "greet" do |name|
output.puts "hello #{name}"
end
Provide non-color variation
If your command uses colored output please ensure that you provide a
non-color variation. Use the Pry.config.color
option to determine
whether color should be used.
Pry::Commands.block_command "greet" do |text|
output.puts Pry.config.color ? "\e[1m#{text}\e[0m" : text
end
Use hyphens not underscores for long command names
Commands are not methods, so to further distinguish them from methods (and avoid conflict) it is recommended to segment long names with hyphens rather than underscores.
Pry::Commands.block_command "long-command-name" do
output.puts "a long name"
end
Provide a listing
for Regexp commands
Regexp commands need a 'listing' name if they are to be looked up by
commands such as help
and show-command
.
Example:
Pry::Commands.block_command /\.(.*)/, "Invoke a shell command", :listing => "shell-command" do |cmd|
system(cmd)
end