2011-04-24 10:25:07 -04:00
class Pry
class NoCommandError < StandardError
def initialize ( name , owner )
super " Command ' #{ name } ' not found in command set #{ owner } "
end
end
2011-05-30 00:46:44 -04:00
# This class is used to create sets of commands. Commands can be imported from
2011-04-24 10:25:07 -04:00
# different sets, aliased, removed, etc.
class CommandSet
class Command < Struct . new ( :name , :description , :options , :block )
2011-05-03 22:43:35 -04:00
2011-04-24 10:25:07 -04:00
def call ( context , * args )
2011-04-25 15:31:38 -04:00
if stub_block = options [ :stub_info ]
context . instance_eval ( & stub_block )
else
2011-05-03 22:43:35 -04:00
ret = context . instance_exec ( * correct_arg_arity ( block . arity , args ) , & block )
2011-04-25 15:31:38 -04:00
ret if options [ :keep_retval ]
end
2011-04-24 10:25:07 -04:00
end
2011-05-03 22:43:35 -04:00
private
def correct_arg_arity ( arity , args )
case arity < = > 0
when - 1
args
2011-08-22 15:52:03 -04:00
when 0
[ ]
when 1
2011-08-22 21:41:27 -04:00
# another jruby hack
if Pry :: Helpers :: BaseHelpers . jruby?
args [ 0 .. ( arity - 1 ) ]
else
args . values_at 0 .. ( arity - 1 )
end
2011-05-03 22:43:35 -04:00
end
end
2011-04-24 10:25:07 -04:00
end
2011-05-30 06:56:50 -04:00
include Enumerable
2011-04-25 16:58:06 -04:00
include Pry :: Helpers :: BaseHelpers
2011-04-25 15:31:38 -04:00
2011-04-24 10:25:07 -04:00
attr_reader :commands
2011-04-30 06:28:58 -04:00
attr_reader :helper_module
2011-04-24 10:25:07 -04:00
# @param [Array<CommandSet>] imported_sets Sets which will be imported
# automatically
# @yield Optional block run to define commands
2011-05-07 01:32:05 -04:00
def initialize ( * imported_sets , & block )
2011-04-30 06:28:58 -04:00
@commands = { }
@helper_module = Module . new
2011-04-24 10:25:07 -04:00
2011-04-25 07:26:25 -04:00
define_default_commands
2011-04-24 10:25:07 -04:00
import ( * imported_sets )
instance_eval ( & block ) if block
end
# Defines a new Pry command.
2011-05-28 10:23:56 -04:00
# @param [String, Regexp] name The name of the command. Can be
# Regexp as well as String.
2011-04-24 10:25:07 -04:00
# @param [String] description A description of the command.
# @param [Hash] options The optional configuration parameters.
# @option options [Boolean] :keep_retval Whether or not to use return value
# of the block for return of `command` or just to return `nil`
# (the default).
2011-05-28 10:23:56 -04:00
# @option options [Array<String>] :requires_gem Whether the command has
# any gem dependencies, if it does and dependencies not met then
# command is disabled and a stub proc giving instructions to
# install command is provided.
# @option options [Boolean] :interpolate Whether string #{} based
# interpolation is applied to the command arguments before
# executing the command. Defaults to true.
# @option options [String] :listing The listing name of the
# command. That is the name by which the command is looked up by
# help and by show-command. Necessary for regex based commands.
2011-07-26 05:42:44 -04:00
# @option options [Boolean] :use_prefix Whether the command uses
# `Pry.config.command_prefix` prefix (if one is defined). Defaults
# to true.
2011-04-24 10:25:07 -04:00
# @yield The action to perform. The parameters in the block
# determines the parameters the command will receive. All
# parameters passed into the block will be strings. Successive
# command parameters are separated by whitespace at the Pry prompt.
# @example
2011-05-07 01:32:05 -04:00
# MyCommands = Pry::CommandSet.new do
2011-04-24 10:25:07 -04:00
# command "greet", "Greet somebody" do |name|
# puts "Good afternoon #{name.capitalize}!"
# end
# end
#
# # From pry:
# # pry(main)> _pry_.commands = MyCommands
# # pry(main)> greet john
# # Good afternoon John!
# # pry(main)> help greet
# # Greet somebody
2011-05-28 10:23:56 -04:00
# @example Regexp command
# MyCommands = Pry::CommandSet.new do
# command /number-(\d+)/, "number-N regex command", :listing => "number" do |num, name|
# puts "hello #{name}, nice number: #{num}"
# end
# end
#
# # From pry:
# # pry(main)> _pry_.commands = MyCommands
# # pry(main)> number-10 john
# # hello john, nice number: 10
# # pry(main)> help number
# # number-N regex command
2011-05-27 12:15:10 -04:00
def command ( name , description = " No description. " , options = { } , & block )
2011-04-25 15:31:38 -04:00
2011-05-22 19:57:50 -04:00
options = {
:requires_gem = > [ ] ,
:keep_retval = > false ,
:argument_required = > false ,
2011-05-27 12:15:10 -04:00
:interpolate = > true ,
2011-07-26 03:34:54 -04:00
:listing = > name ,
2011-07-26 05:42:44 -04:00
:use_prefix = > true
2011-05-22 19:57:50 -04:00
} . merge! ( options )
2011-04-25 15:31:38 -04:00
unless command_dependencies_met? options
gems_needed = Array ( options [ :requires_gem ] )
gems_not_installed = gems_needed . select { | g | ! gem_installed? ( g ) }
options [ :stub_info ] = proc do
2011-05-29 23:56:38 -04:00
output . puts " \n The command ' #{ name } ' is #{ Helpers :: Text . bold ( " unavailable " ) } because it requires the following gems to be installed: #{ ( gems_not_installed . join ( " , " ) ) } "
2011-05-29 11:40:18 -04:00
output . puts " - "
2011-07-27 07:06:54 -04:00
output . puts " Type `install-command #{ name } ` to install the required gems and activate this command. "
2011-04-25 15:31:38 -04:00
end
end
2011-05-27 12:15:10 -04:00
commands [ name ] = Command . new ( name , description , options , block )
2011-04-24 10:25:07 -04:00
end
2011-05-30 06:56:50 -04:00
def each & block
2011-07-26 05:42:44 -04:00
@commands . each ( & block )
2011-05-30 06:56:50 -04:00
end
2011-04-24 10:25:07 -04:00
# Removes some commands from the set
2011-05-03 11:49:09 -04:00
# @param [Array<String>] names name of the commands to remove
2011-04-24 10:25:07 -04:00
def delete ( * names )
names . each { | name | commands . delete name }
end
# Imports all the commands from one or more sets.
# @param [Array<CommandSet>] sets Command sets, all of the commands of which
# will be imported.
def import ( * sets )
2011-04-30 06:28:58 -04:00
sets . each do | set |
commands . merge! set . commands
helper_module . send :include , set . helper_module
end
2011-04-24 10:25:07 -04:00
end
# Imports some commands from a set
# @param [CommandSet] set Set to import commands from
# @param [Array<String>] names Commands to import
def import_from ( set , * names )
2011-05-01 02:19:50 -04:00
helper_module . send :include , set . helper_module
2011-04-24 10:25:07 -04:00
names . each { | name | commands [ name ] = set . commands [ name ] }
end
# Aliases a command
2011-04-25 07:26:25 -04:00
# @param [String] new_name New name of the command.
# @param [String] old_name Old name of the command.
2011-05-06 10:18:23 -04:00
# @param [String, nil] desc New description of the command.
2011-04-25 07:26:25 -04:00
def alias_command ( new_name , old_name , desc = nil )
2011-04-24 10:25:07 -04:00
commands [ new_name ] = commands [ old_name ] . dup
commands [ new_name ] . name = new_name
2011-04-25 12:09:04 -04:00
commands [ new_name ] . description = desc if desc
2011-04-24 10:25:07 -04:00
end
# Runs a command.
# @param [Object] context Object which will be used as self during the
# command.
# @param [String] name Name of the command to be run
# @param [Array<Object>] args Arguments passed to the command
# @raise [NoCommandError] If the command is not defined in this set
def run_command ( context , name , * args )
2011-04-30 06:28:58 -04:00
context . extend helper_module
2011-05-11 06:28:41 -04:00
command = commands [ name ]
2011-04-30 06:28:58 -04:00
2011-05-11 06:28:41 -04:00
if command . nil?
2011-04-24 10:25:07 -04:00
raise NoCommandError . new ( name , self )
end
2011-05-16 18:20:33 -04:00
if command . options [ :argument_required ] && args . empty?
2011-05-11 07:33:56 -04:00
puts " The command ' #{ command . name } ' requires an argument. "
2011-05-11 06:28:41 -04:00
else
2011-05-23 06:27:23 -04:00
command . call context , * args
2011-05-11 06:28:41 -04:00
end
2011-04-24 10:25:07 -04:00
end
2011-04-25 02:04:55 -04:00
# Sets the description for a command (replacing the old
# description.)
# @param [String] name The command name.
# @param [String] description The command description.
# @example
2011-05-07 01:32:05 -04:00
# MyCommands = Pry::CommandSet.new do
2011-04-25 02:04:55 -04:00
# desc "help", "help description"
# end
def desc ( name , description )
commands [ name ] . description = description
end
2011-04-25 07:26:25 -04:00
2011-04-30 06:28:58 -04:00
# Defines helpers methods for this command sets.
# Those helpers are only defined in this command set.
#
# @yield A block defining helper methods
# @example
# helpers do
# def hello
# puts "Hello!"
# end
#
# include OtherModule
# end
def helpers ( & block )
helper_module . class_eval ( & block )
end
2011-05-27 12:15:10 -04:00
2011-05-30 08:06:55 -04:00
# @return [Array] The list of commands provided by the command set.
def list_commands
commands . keys
end
2011-04-25 07:26:25 -04:00
private
def define_default_commands
2011-05-27 12:15:10 -04:00
2011-04-25 07:26:25 -04:00
command " help " , " This menu. " do | cmd |
if ! cmd
output . puts
help_text = heading ( " Command List: " ) + " \n "
2011-07-27 14:42:36 -04:00
help_text << commands . map do | key , command |
2011-04-27 11:24:52 -04:00
if command . description && ! command . description . empty?
2011-07-27 14:42:36 -04:00
" #{ command . options [ :listing ] } " . ljust ( 18 ) + command . description
2011-04-25 07:26:25 -04:00
end
2011-07-27 14:42:36 -04:00
end . compact . sort . join ( " \n " )
2011-04-25 07:26:25 -04:00
stagger_output ( help_text )
else
2011-05-27 12:15:10 -04:00
if command = find_command ( cmd )
2011-04-25 07:26:25 -04:00
output . puts command . description
else
output . puts " No info for command: #{ cmd } "
end
end
end
2011-04-25 15:31:38 -04:00
2011-07-26 11:14:24 -04:00
command " install-command " , " Install a disabled command. " do | name |
2011-05-27 12:15:10 -04:00
command = find_command ( name )
stub_info = command . options [ :stub_info ]
2011-04-25 15:31:38 -04:00
if ! stub_info
output . puts " Not a command stub. Nothing to do. "
next
end
output . puts " Attempting to install ` #{ name } ` command... "
2011-05-27 12:15:10 -04:00
gems_to_install = Array ( command . options [ :requires_gem ] )
2011-04-25 15:31:38 -04:00
gem_install_failed = false
gems_to_install . each do | g |
next if gem_installed? ( g )
output . puts " Installing ` #{ g } ` gem... "
begin
Gem :: DependencyInstaller . new . install ( g )
rescue Gem :: GemNotFoundException
output . puts " Required Gem: ` #{ g } ` not found. Aborting command installation. "
gem_install_failed = true
next
end
end
next if gem_install_failed
Gem . refresh
2011-05-29 11:40:18 -04:00
gems_to_install . each do | g |
begin
require g
rescue LoadError
output . puts " Required Gem: ` #{ g } ` installed but not found?!. Aborting command installation. "
gem_install_failed = true
end
end
next if gem_install_failed
2011-05-27 12:15:10 -04:00
command . options . delete :stub_info
2011-04-25 15:31:38 -04:00
output . puts " Installation of ` #{ name } ` successful! Type `help #{ name } ` for information "
end
2011-04-25 07:26:25 -04:00
end
2011-04-24 10:25:07 -04:00
end
end