2011-12-31 06:50:04 -05:00
class Pry
2011-12-31 07:40:01 -05:00
# The super-class of all commands, new commands should be created by calling
2012-01-21 18:17:43 -05:00
# {Pry::CommandSet#command} which creates a BlockCommand or {Pry::CommandSet#create_command}
2011-12-31 07:40:01 -05:00
# which creates a ClassCommand. Please don't use this class directly.
2011-12-31 06:50:04 -05:00
class Command
# represents a void return value for a command
VOID_VALUE = Object . new
# give it a nice inspect
def VOID_VALUE . inspect ( ) " void " end
2011-12-31 07:40:01 -05:00
# Properties of the command itself (as passed as arguments to
2012-01-21 18:17:43 -05:00
# {CommandSet#command} or {CommandSet#create_command}).
2011-12-31 06:50:04 -05:00
class << self
2012-01-08 15:48:54 -05:00
attr_accessor :block
2011-12-31 06:50:04 -05:00
attr_accessor :name
2012-01-15 15:21:28 -05:00
attr_writer :description
attr_writer :command_options
2012-01-08 15:48:54 -05:00
# Define or get the command's description
def description ( arg = nil )
@description = arg if arg
@description
end
# Define or get the command's options
def command_options ( arg = nil )
2012-01-14 23:54:30 -05:00
@command_options || = { }
@command_options . merge! ( arg ) if arg
2012-01-08 15:48:54 -05:00
@command_options
end
# backward compatibility
alias_method :options , :command_options
alias_method :options = , :command_options =
# Define or get the command's banner
def banner ( arg = nil )
@banner = arg if arg
@banner || description
end
2011-12-31 06:50:04 -05:00
end
2011-12-31 07:40:01 -05:00
# Make those properties accessible to instances
def name ; self . class . name ; end
def description ; self . class . description ; end
def block ; self . class . block ; end
def command_options ; self . class . options ; end
2011-12-31 09:13:59 -05:00
def command_name ; command_options [ :listing ] ; end
2011-12-31 07:40:01 -05:00
class << self
def inspect
" # <class(Pry::Command #{ name . inspect } )> "
end
# Create a new command with the given properties.
#
# @param String name the name of the command
# @param String description the description to appear in {help}
# @param Hash options behavioural options (@see {Pry::CommandSet#command})
# @param Module helpers a module of helper functions to be included.
# @param Proc &block (optional, a block, used for BlockCommands)
#
# @return Class (a subclass of Pry::Command)
#
def subclass ( name , description , options , helpers , & block )
klass = Class . new ( self )
klass . send ( :include , helpers )
klass . name = name
klass . description = description
2012-01-08 15:48:54 -05:00
klass . command_options = options
2011-12-31 07:40:01 -05:00
klass . block = block
klass
end
2012-01-02 19:04:47 -05:00
# Should this command be called for the given line?
#
# @param String a line input at the REPL
# @return Boolean
def matches? ( val )
command_regex =~ val
end
2012-02-11 18:15:22 -05:00
# How well does this command match the given line?
#
# Higher scores are better because they imply that this command matches
# the line more closely.
#
# The score is calculated by taking the number of characters at the start
# of the string that are used only to identify the command, not as part of
# the arguments.
#
# @example
# /\.(.*)/.match_score(".foo") #=> 1
# /\.*(.*)/.match_score("...foo") #=> 3
# 'hi'.match_score("hi there") #=> 2
#
# @param String a line input at the REPL
# @return Fixnum
def match_score ( val )
if command_regex =~ val
Regexp . last_match . size > 1 ? Regexp . last_match . begin ( 1 ) : Regexp . last_match . end ( 0 )
else
- 1
end
end
2011-12-31 07:40:01 -05:00
# Store hooks to be run before or after the command body.
# @see {Pry::CommandSet#before_command}
# @see {Pry::CommandSet#after_command}
def hooks
@hooks || = { :before = > [ ] , :after = > [ ] }
end
2012-01-02 19:04:47 -05:00
def command_regex
prefix = convert_to_regex ( Pry . config . command_prefix )
prefix = " (?: #{ prefix } )? " unless options [ :use_prefix ]
/ ^ #{ prefix } #{ convert_to_regex ( name ) } (?! \ S) /
end
def convert_to_regex ( obj )
case obj
when String
Regexp . escape ( obj )
else
obj
end
end
2012-02-20 02:08:16 -05:00
# The group in which the command should be displayed in "help" output.
# This is usually auto-generated from directory naming, but it can be
# manually overridden if necessary.
def group ( name = nil )
@group = name if name
@group || = (
case Pry :: Method ( block ) . source_file
when %r{ /pry/.*_commands/(.*).rb }
2012-02-22 22:28:00 -05:00
$1 . capitalize . gsub ( / _ / , " " )
2012-02-23 02:04:28 -05:00
when %r{ (pry-[ \ w_]+)-([ \ d \ .]+) }
2012-02-23 00:21:31 -05:00
name , version = $1 , $2
2012-02-23 02:04:28 -05:00
" #{ name . to_s } (v #{ version . to_s } ) "
2012-02-20 02:08:16 -05:00
when / pryrc /
" ~/.pryrc "
else
2012-03-27 23:09:57 -04:00
" (other) "
2012-02-20 02:08:16 -05:00
end
)
end
2011-12-31 07:40:01 -05:00
end
2012-01-02 19:04:47 -05:00
# Properties of one execution of a command (passed by {Pry#run_command} as a hash of
2011-12-31 07:40:01 -05:00
# context and expanded in {#initialize}
2011-12-31 06:50:04 -05:00
attr_accessor :output
attr_accessor :target
attr_accessor :captures
attr_accessor :eval_string
attr_accessor :arg_string
2011-12-31 07:40:01 -05:00
attr_accessor :context
2011-12-31 06:50:04 -05:00
attr_accessor :command_set
attr_accessor :_pry_
2012-02-26 07:05:19 -05:00
# The block we pass *into* a command so long as `:takes_block` is
# not equal to `false`
2012-02-23 09:30:48 -05:00
# @example
2012-02-26 07:05:19 -05:00
# my-command | do
2012-02-23 09:30:48 -05:00
# puts "block content"
# end
attr_accessor :command_block
2011-12-31 06:50:04 -05:00
# Run a command from another command.
# @param [String] command_string The string that invokes the command
# @param [Array] args Further arguments to pass to the command
# @example
# run "show-input"
# @example
# run ".ls"
# @example
# run "amend-line", "5", 'puts "hello world"'
def run ( command_string , * args )
2012-03-07 21:38:12 -05:00
complete_string = " #{ command_string } #{ args . join ( " " ) } " . rstrip
2012-01-02 19:04:47 -05:00
command_set . process_line ( complete_string , context )
2011-12-31 06:50:04 -05:00
end
def commands
command_set . commands
end
def text
Pry :: Helpers :: Text
end
def void
VOID_VALUE
end
include Pry :: Helpers :: BaseHelpers
include Pry :: Helpers :: CommandHelpers
2011-12-31 07:40:01 -05:00
# Instantiate a command, in preparation for calling it.
#
# @param Hash context The runtime context to use with this command.
2011-12-31 09:13:59 -05:00
def initialize ( context = { } )
2011-12-31 06:50:04 -05:00
self . context = context
self . target = context [ :target ]
self . output = context [ :output ]
self . eval_string = context [ :eval_string ]
self . command_set = context [ :command_set ]
self . _pry_ = context [ :pry_instance ]
end
2011-12-31 09:13:59 -05:00
# The value of {self} inside the {target} binding.
def target_self ; target . eval ( 'self' ) ; end
2012-01-02 19:04:47 -05:00
# Revaluate the string (str) and perform interpolation.
# @param [String] str The string to reevaluate with interpolation.
#
# @return [String] The reevaluated string with interpolations
# applied (if any).
def interpolate_string ( str )
dumped_str = str . dump
if dumped_str . gsub! ( / \\ \ # \ { / , '#{' )
target . eval ( dumped_str )
else
str
end
end
# Display a warning if a command collides with a local/method in
# the current scope.
# @param [String] command_name_match The name of the colliding command.
# @param [Binding] target The current binding context.
2012-01-28 22:39:31 -05:00
def check_for_command_name_collision ( command_name_match , arg_string )
collision_type = target . eval ( " defined?( #{ command_name_match } ) " )
collision_type || = 'local-variable' if arg_string . match ( %r{ \ A \ s*[-+*/%&|^]*= } )
if collision_type
output . puts " #{ Pry :: Helpers :: Text . bold ( 'WARNING:' ) } Calling Pry command ' #{ command_name_match } ', " +
" which conflicts with a #{ collision_type } . \n \n "
2012-01-02 19:04:47 -05:00
end
rescue Pry :: RescuableException
end
# Extract necessary information from a line that Command.matches? this command.
#
# @param String the line of input
# @return [
# String the command name used, or portion of line that matched the command_regex
# String a string of all the arguments (i.e. everything but the name)
# Array the captures caught by the command_regex
# Array args the arguments got by splitting the arg_string
# ]
def tokenize ( val )
val . replace ( interpolate_string ( val ) ) if command_options [ :interpolate ]
self . class . command_regex =~ val
# please call Command.matches? before Command#call_safely
raise CommandError , " fatal: called a command which didn't match?! " unless Regexp . last_match
captures = Regexp . last_match . captures
pos = Regexp . last_match . end ( 0 )
arg_string = val [ pos .. - 1 ]
# remove the one leading space if it exists
arg_string . slice! ( 0 ) if arg_string . start_with? ( " " )
2012-02-26 07:05:19 -05:00
# process and pass a block if one is found
pass_block ( arg_string ) if command_options [ :takes_block ]
2012-01-02 19:04:47 -05:00
if arg_string
args = command_options [ :shellwords ] ? Shellwords . shellwords ( arg_string ) : arg_string . split ( " " )
else
args = [ ]
end
[ val [ 0 .. pos ] . rstrip , arg_string , captures , args ]
end
# Process a line that Command.matches? this command.
#
# @param String the line to process
# @return Object or Command::VOID_VALUE
def process_line ( line )
command_name , arg_string , captures , args = tokenize ( line )
2012-01-28 22:39:31 -05:00
check_for_command_name_collision ( command_name , arg_string ) if Pry . config . collision_warning
2012-01-02 19:04:47 -05:00
self . arg_string = arg_string
self . captures = captures
call_safely ( * ( captures + args ) )
end
2012-02-23 09:30:48 -05:00
# Pass a block argument to a command.
2012-02-26 07:05:19 -05:00
# @param [String] arg_string The arguments (as a string) passed to the command.
# We inspect these for a '| do' or a '| {' and if we find it we use it
2012-02-23 09:30:48 -05:00
# to start a block input sequence. Once we have a complete
# block, we save it to an accessor that can be retrieved from the command context.
2012-02-26 07:05:19 -05:00
# Note that if we find the '| do' or '| {' we delete this and the
# elements following it from `arg_string`.
def pass_block ( arg_string )
2012-02-26 07:15:07 -05:00
block_index = arg_string . rindex / \ | *(?:do| \ {) /
2012-02-23 09:30:48 -05:00
2012-02-26 07:05:19 -05:00
return if ! block_index
2012-02-23 09:30:48 -05:00
2012-02-26 07:05:19 -05:00
block_init_string = arg_string . slice! ( block_index .. - 1 ) [ 1 .. - 1 ]
prime_string = " proc #{ block_init_string } \n "
if ! _pry_ . complete_expression? ( prime_string )
block_string = _pry_ . r ( target , prime_string )
else
block_string = prime_string
end
2012-02-23 09:30:48 -05:00
2012-02-26 07:05:19 -05:00
begin
self . command_block = target . eval ( block_string )
rescue Pry :: RescuableException
raise CommandError , " Incomplete block definition. "
2012-02-23 09:30:48 -05:00
end
end
private :pass_block
2011-12-31 07:40:01 -05:00
# Run the command with the given {args}.
#
# This is a public wrapper around {#call} which ensures all preconditions are met.
#
# @param *[String] the arguments to pass to this command.
# @return Object the return value of the {#call} method, or Command::VOID_VALUE
2011-12-31 06:50:04 -05:00
def call_safely ( * args )
2011-12-31 07:40:01 -05:00
unless dependencies_met?
2011-12-31 06:50:04 -05:00
gems_needed = Array ( command_options [ :requires_gem ] )
gems_not_installed = gems_needed . select { | g | ! gem_installed? ( g ) }
output . puts " \n The command ' #{ name } ' is #{ Helpers :: Text . bold ( " unavailable " ) } because it requires the following gems to be installed: #{ ( gems_not_installed . join ( " , " ) ) } "
output . puts " - "
output . puts " Type `install-command #{ name } ` to install the required gems and activate this command. "
2011-12-31 07:40:01 -05:00
return void
end
if command_options [ :argument_required ] && args . empty?
raise CommandError , " The command ' #{ name } ' requires an argument. "
2011-12-31 06:50:04 -05:00
end
2011-12-31 07:40:01 -05:00
ret = call_with_hooks ( * args )
command_options [ :keep_retval ] ? ret : void
end
# Are all the gems required to use this command installed?
#
# @return Boolean
def dependencies_met?
@dependencies_met || = command_dependencies_met? ( command_options )
2011-12-31 06:50:04 -05:00
end
2011-12-31 07:40:01 -05:00
private
# Run the {#call} method and all the registered hooks.
#
# @param *String the arguments to #{call}
# @return Object the return value from #{call}
2011-12-31 06:50:04 -05:00
def call_with_hooks ( * args )
self . class . hooks [ :before ] . each do | block |
instance_exec ( * args , & block )
end
2012-01-15 15:21:28 -05:00
ret = call ( * args )
2011-12-31 06:50:04 -05:00
self . class . hooks [ :after ] . each do | block |
ret = instance_exec ( * args , & block )
end
2011-12-31 07:40:01 -05:00
ret
2011-12-31 06:50:04 -05:00
end
2012-01-15 18:51:04 -05:00
# Fix the number of arguments we pass to a block to avoid arity warnings.
#
# @param Number the arity of the block
# @param Array the arguments to pass
# @return Array a (possibly shorter) array of the arguments to pass
def correct_arg_arity ( arity , args )
case
when arity < 0
args
when arity == 0
[ ]
when arity > 0
args . values_at ( * ( 0 .. ( arity - 1 ) ) . to_a )
end
end
2011-12-31 06:50:04 -05:00
end
2011-12-31 07:40:01 -05:00
# A super-class for Commands that are created with a single block.
#
# This class ensures that the block is called with the correct number of arguments
# and the right context.
#
# Create subclasses using {Pry::CommandSet#command}.
2011-12-31 06:50:04 -05:00
class BlockCommand < Command
# backwards compatibility
alias_method :opts , :context
2012-01-08 15:48:54 -05:00
# Call the block that was registered with this command.
#
# @param *String the arguments passed
# @return Object the return value of the block
2011-12-31 06:50:04 -05:00
def call ( * args )
2012-02-26 20:34:05 -05:00
instance_exec ( * correct_arg_arity ( block . arity , args ) , & block )
2011-12-31 06:50:04 -05:00
end
2012-02-26 04:00:50 -05:00
def help
" #{ command_options [ :listing ] . to_s . ljust ( 18 ) } #{ description } "
end
2011-12-31 06:50:04 -05:00
end
2011-12-31 07:40:01 -05:00
# A super-class ofr Commands with structure.
#
# This class implements the bare-minimum functionality that a command should have,
# namely a --help switch, and then delegates actual processing to its subclasses.
#
2012-01-21 18:17:43 -05:00
# Create subclasses using {Pry::CommandSet#create_command}, and override the {options(opt)} method
2011-12-31 07:40:01 -05:00
# to set up an instance of Slop, and the {process} method to actually run the command. If
# necessary, you can also override {setup} which will be called before {options}, for example to
# require any gems your command needs to run, or to set up state.
2011-12-31 06:50:04 -05:00
class ClassCommand < Command
2011-12-31 10:57:50 -05:00
2011-12-31 06:50:04 -05:00
attr_accessor :opts
attr_accessor :args
2011-12-31 07:40:01 -05:00
# Set up {opts} and {args}, and then call {process}
#
# This function will display help if necessary.
#
# @param *String the arguments passed
# @return Object the return value of {process} or VOID_VALUE
2011-12-31 06:50:04 -05:00
def call ( * args )
setup
self . opts = slop
self . args = self . opts . parse! ( args )
if opts . present? ( :help )
output . puts slop . help
2011-12-31 07:40:01 -05:00
void
2011-12-31 06:50:04 -05:00
else
2012-02-26 20:34:05 -05:00
process ( * correct_arg_arity ( method ( :process ) . arity , args ) )
2011-12-31 06:50:04 -05:00
end
end
2011-12-31 09:40:44 -05:00
# Return the help generated by Slop for this command.
def help
slop . help
end
2011-12-31 07:40:01 -05:00
# Return an instance of Slop that can parse the options that this command accepts.
2011-12-31 06:50:04 -05:00
def slop
Slop . new do | opt |
2011-12-31 10:57:50 -05:00
opt . banner ( unindent ( self . class . banner ) )
2011-12-31 06:50:04 -05:00
options ( opt )
opt . on ( :h , :help , " Show this message. " )
end
end
2011-12-31 07:40:01 -05:00
# A function called just before {options(opt)} as part of {call}.
#
# This function can be used to set up any context your command needs to run, for example
# requiring gems, or setting default values for options.
#
# @example
# def setup;
# require 'gist'
# @action = :method
# end
def setup ; end
# A function to setup Slop so it can parse the options your command expects.
#
# NOTE: please don't do anything side-effecty in the main part of this method,
# as it may be called by Pry at any time for introspection reasons. If you need
# to set up default values, use {setup} instead.
#
# @example
# def options(opt)
# opt.banner "Gists methods or classes"
# opt.on(:c, :class, "gist a class") do
# @action = :class
# end
# end
def options ( opt ) ; end
# The actual body of your command should go here.
#
# The {opts} mehod can be called to get the options that Slop has passed,
# and {args} gives the remaining, unparsed arguments.
#
# The return value of this method is discarded unless the command was created
# with :keep_retval => true, in which case it is returned to the repl.
#
# @example
# def process
# if opts.present?(:class)
# gist_class
# else
# gist_method
# end
# end
def process ; raise CommandError , " command ' #{ name } ' not implemented " end
2011-12-31 06:50:04 -05:00
end
end