class Pry::Command
The super-class of all commands, new commands should be created by calling {Pry::CommandSet#command} which creates a BlockCommand
or {Pry::CommandSet#create_command} which creates a ClassCommand
. Please don't use this class directly.
N.B. using a regular expresion here so that “raise-up 'foo'” does the right thing.
Constants
- VOID_VALUE
represents a void return value for a command
Attributes
The block we pass into a command so long as `:takes_block` is not equal to `false` @example
my-command | do puts "block content" end
Properties of one execution of a command (passed by {Pry#run_command} as a hash of context and expanded in `#initialize`
Public Class Methods
# File lib/pry/command.rb, line 66 def block @block || instance_method(:process) end
# File lib/pry/command.rb, line 109 def command_name options[:listing] end
Define or get the command's options
# File lib/pry/command.rb, line 51 def command_options(arg = nil) @command_options ||= default_options(match) @command_options.merge!(arg) if arg @command_options end
# File lib/pry/command.rb, line 165 def command_regex prefix = convert_to_regex(Pry.config.command_prefix) prefix = "(?:#{prefix})?" unless options[:use_prefix] /\A#{prefix}#{convert_to_regex(match)}(?!\S)/ end
# File lib/pry/command.rb, line 172 def convert_to_regex(obj) case obj when String Regexp.escape(obj) else obj end end
# File lib/pry/command.rb, line 89 def default_options(match) { keep_retval: false, argument_required: false, interpolate: true, shellwords: true, listing: (match.is_a?(String) ? match : match.inspect), use_prefix: true, takes_block: false } end
Define or get the command's description
# File lib/pry/command.rb, line 45 def description(arg = nil) @description = arg if arg @description ||= nil end
# File lib/pry/command.rb, line 75 def doc new.help end
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. Group should not be changed once it is initialized.
# File lib/pry/command.rb, line 185 def group(name = nil) @group ||= begin name || case Pry::Method(block).source_file when %r{/pry/.*_commands/(.*).rb} Regexp.last_match(1).capitalize.tr('_', " ") when /(pry-\w+)-([\d\.]+([\w\.]+)?)/ name = Regexp.last_match(1) version = Regexp.last_match(2) "#{name} (v#{version})" when /pryrc/ "pryrc" else "(other)" end end end
# File lib/pry/command.rb, line 105 def inspect name end
# File lib/pry/command.rb, line 35 def match(arg = nil) if arg @command_options ||= default_options(arg) @command_options[:listing] = arg.is_a?(String) ? arg : arg.inspect @match = arg end @match ||= nil end
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] val A line input at the REPL
@return [Fixnum]
# File lib/pry/command.rb, line 153 def match_score(val) if command_regex =~ val if Regexp.last_match.size > 1 Regexp.last_match.begin(1) else Regexp.last_match.end(0) end else -1 end end
Should this command be called for the given line? @param [String] val A line input at the REPL
@return [Boolean]
# File lib/pry/command.rb, line 133 def matches?(val) command_regex =~ val end
# File lib/pry/command.rb, line 101 def name super.to_s == "" ? "#<class(Pry::Command #{match.inspect})>" : super end
Instantiate a command, in preparation for calling it. @param [Hash] context The runtime context to use with this command.
# File lib/pry/command.rb, line 230 def initialize(context = {}) self.context = context self.target = context[:target] self.output = context[:output] self.eval_string = context[:eval_string] self.command_set = context[:command_set] self.hooks = context[:hooks] self.pry_instance = context[:pry_instance] end
# File lib/pry/command.rb, line 70 def source file, line = block.source_location strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line)) end
# File lib/pry/command.rb, line 79 def source_file Array(block.source_location).first end
# File lib/pry/command.rb, line 84 def source_line Array(block.source_location).last end
# File lib/pry/command.rb, line 202 def state Pry::CommandState.default.state_for(match) end
Create a new command with the given properties. @param [String, Regex] match The thing that triggers this command @param [String] description The description to appear in `help` @param [Hash] options Behavioral options (see {Pry::CommandSet#command}) @param [Module] helpers A module of helper functions to be included. @yield optional, used for BlockCommands @return [Class] (a subclass of {Pry::Command})
# File lib/pry/command.rb, line 120 def subclass(match, description, options, helpers, &block) klass = Class.new(self) klass.send(:include, helpers) klass.match = match klass.description = description klass.command_options = options klass.block = block klass end
Public Instance Methods
# File lib/pry/command.rb, line 292 def _pry_ Pry::Warning.warn('_pry_ is deprecated, use pry_instance instead') pry_instance end
# File lib/pry/command.rb, line 253 def block self.class.block end
Display a warning if a command collides with a local/method in the current scope.
# File lib/pry/command.rb, line 329 def check_for_command_collision(command_match, arg_string) collision_type = target.eval("defined?(#{command_match})") collision_type ||= 'local-variable' if arg_string =~ %r{\A\s*[-+*/%&|^]*=} if collision_type output.puts( "#{Helpers::Text.bold('WARNING:')} Calling Pry command '#{command_match}', " \ "which conflicts with a #{collision_type}.\n\n" ) end rescue Pry::RescuableException # rubocop:disable Lint/HandleExceptions end
# File lib/pry/command.rb, line 261 def command_name self.class.command_name end
# File lib/pry/command.rb, line 257 def command_options self.class.options end
# File lib/pry/command.rb, line 284 def commands command_set.to_hash end
Generate completions for this command
@param [String] _search The line typed so far @return [Array<String>] Completion words
# File lib/pry/command.rb, line 411 def complete(_search) [] end
# File lib/pry/command.rb, line 249 def description self.class.description end
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).
# File lib/pry/command.rb, line 318 def interpolate_string(str) dumped_str = str.dump if dumped_str.gsub!(/\\\#\{/, '#{') target.eval(dumped_str) else str end end
# File lib/pry/command.rb, line 245 def match self.class.match end
Make those properties accessible to instances
# File lib/pry/command.rb, line 241 def name self.class.name end
Process a line that Command.matches?
this command. @param [String] line The line to process @return [Object, Command::VOID_VALUE]
# File lib/pry/command.rb, line 394 def process_line(line) command_match, arg_string, captures, args = tokenize(line) if Pry.config.collision_warning check_for_command_collision(command_match, arg_string) end self.arg_string = arg_string self.captures = captures call_safely(*(captures + args)) end
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"'
# File lib/pry/command.rb, line 278 def run(command_string, *args) command_string = pry_instance.config.command_prefix.to_s + command_string complete_string = "#{command_string} #{args.join(' ')}".rstrip command_set.process_line(complete_string, context) end
# File lib/pry/command.rb, line 265 def source self.class.source end
@return [Hash] Pry
commands can store arbitrary state
here. This state persists between subsequent command invocations. All state saved here is unique to the command, it does not need to be namespaced.
@example
state.my_state = "my state" # this will not conflict with any # `state.my_state` used in another command.
# File lib/pry/command.rb, line 309 def state self.class.state end
@return [Object] The value of `self` inside the `target` binding.
# File lib/pry/command.rb, line 298 def target_self target.eval('self') end
Extract necessary information from a line that Command.matches?
this command.
Returns an array of four elements:
“`
[String] the portion of the line that matched with the Command match [String] a string of all the arguments (i.e. everything but the match) [Array] the captures caught by the command_regex [Array] the arguments obtained by splitting the arg_string
“`
@param [String] val The line of input @return [Array]
# File lib/pry/command.rb, line 356 def tokenize(val) val = interpolate_string(val) if command_options[:interpolate] self.class.command_regex =~ val # please call Command.matches? before Command#call_safely unless Regexp.last_match raise CommandError, "fatal: called a command which didn't match?!" end 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?(" ") # process and pass a block if one is found pass_block(arg_string) if command_options[:takes_block] args = if arg_string if command_options[:shellwords] Shellwords.shellwords(arg_string) else arg_string.split(" ") end else [] end [val[0..pos].rstrip, arg_string, captures, args] end
# File lib/pry/command.rb, line 288 def void VOID_VALUE end
Private Instance Methods
# File lib/pry/command.rb, line 485 def after_hooks find_hooks('after') end
# File lib/pry/command.rb, line 481 def before_hooks find_hooks('before') end
Run the command with the given `args`.
This is a public wrapper around `#call` which ensures all preconditions are met.
@param [Array<String>] args The arguments to pass to this command. @return [Object] The return value of the `#call` method, or
{Command::VOID_VALUE}.
# File lib/pry/command.rb, line 425 def call_safely(*args) if command_options[:argument_required] && args.empty? raise CommandError, "The command '#{command_name}' requires an argument." end ret = use_unpatched_symbol do call_with_hooks(*args) end command_options[:keep_retval] ? ret : void end
Run the `#call` method and all the registered hooks. @param [Array<String>] args The arguments to `#call` @return [Object] The return value from `#call`
# File lib/pry/command.rb, line 492 def call_with_hooks(*args) before_hooks.each { |block| instance_exec(*args, &block) } ret = call(*args) after_hooks.each do |block| ret = instance_exec(*args, &block) end ret end
# File lib/pry/command.rb, line 476 def find_hooks(event) event_name = "#{event}_#{command_name}" (hooks || Pry.hooks || self.class.hooks).get_hooks(event_name).values end
Normalize method arguments according to its arity.
@param [Integer] method @param [Array] args @return [Array] a (possibly shorter) array of the arguments to pass
# File lib/pry/command.rb, line 509 def normalize_method_args(method, args) case method.arity when -1 args when 0 [] else args.values_at(*(0..(method.arity - 1)).to_a) end end
Pass a block argument to a command. @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 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. Note that if we find the '| do' or '| {' we delete this and the elements following it from `arg_string`.
# File lib/pry/command.rb, line 451 def pass_block(arg_string) # Workaround for weird JRuby bug where rindex in this case can return nil # even when there's a match. arg_string.scan(/\| *(?:do|\{)/) block_index = $LAST_MATCH_INFO && $LAST_MATCH_INFO.offset(0)[0] return unless block_index block_init_string = arg_string.slice!(block_index..-1)[1..-1] prime_string = "proc #{block_init_string}\n" block_string = if !Pry::Code.complete_expression?(prime_string) pry_instance.r(target, prime_string) else prime_string end begin self.command_block = target.eval(block_string) rescue Pry::RescuableException raise CommandError, "Incomplete block definition." end end
# File lib/pry/command.rb, line 436 def use_unpatched_symbol call_method = Symbol.method_defined?(:call) && Symbol.instance_method(:call) Symbol.class_eval { undef :call } if call_method yield ensure Symbol.instance_eval { define_method(:call, call_method) } if call_method end