mirror of
https://github.com/pry/pry.git
synced 2022-11-09 12:35:05 -05:00
Merge branch 'feature/ls2'
This commit is contained in:
commit
231732616e
19 changed files with 881 additions and 357 deletions
|
@ -1,5 +1,6 @@
|
|||
# (C) John Mair (banisterfiend) 2011
|
||||
# MIT License
|
||||
#
|
||||
|
||||
require 'pp'
|
||||
require 'pry/helpers/base_helpers'
|
||||
|
|
|
@ -126,7 +126,11 @@ class Pry
|
|||
# remove the one leading space if it exists
|
||||
arg_string.slice!(0) if arg_string.start_with?(" ")
|
||||
|
||||
args = arg_string ? Shellwords.shellwords(arg_string) : []
|
||||
if arg_string
|
||||
args = command.options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
|
||||
else
|
||||
args = []
|
||||
end
|
||||
|
||||
options = {
|
||||
:val => val,
|
||||
|
@ -164,7 +168,10 @@ class Pry
|
|||
|
||||
context.command_processor = self
|
||||
|
||||
ret = commands.run_command(context, command, *args)
|
||||
ret = nil
|
||||
catch(:command_done) do
|
||||
ret = commands.run_command(context, command, *args)
|
||||
end
|
||||
|
||||
options[:val].replace("")
|
||||
|
||||
|
|
|
@ -83,6 +83,9 @@ class Pry
|
|||
# @option options [Boolean] :use_prefix Whether the command uses
|
||||
# `Pry.config.command_prefix` prefix (if one is defined). Defaults
|
||||
# to true.
|
||||
# @option options [Boolean] :shellwords Whether the command's arguments
|
||||
# should be split using Shellwords instead of just split on spaces.
|
||||
# Defaults to true.
|
||||
# @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
|
||||
|
@ -120,6 +123,7 @@ class Pry
|
|||
:keep_retval => false,
|
||||
:argument_required => false,
|
||||
:interpolate => true,
|
||||
:shellwords => true,
|
||||
:listing => name,
|
||||
:use_prefix => true
|
||||
}.merge!(options)
|
||||
|
|
|
@ -7,76 +7,44 @@ class Pry
|
|||
run ".ri", *args
|
||||
end
|
||||
|
||||
command "show-doc", "Show the comments above METH. Type `show-doc --help` for more info. Aliases: \?" do |*args|
|
||||
command "show-doc", "Show the comments above METH. Type `show-doc --help` for more info. Aliases: \?", :shellwords => false do |*args|
|
||||
target = target()
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
opts = parse_options!(args, :method_object => true) do |opt|
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: show-doc [OPTIONS] [METH 1] [METH 2] [METH N]
|
||||
Usage: show-doc [OPTIONS] [METH]
|
||||
Show the comments above method METH. Tries instance methods first and then methods by default.
|
||||
e.g show-doc hello_method
|
||||
USAGE
|
||||
|
||||
opt.on :M, "instance-methods", "Operate on instance methods."
|
||||
opt.on :m, :methods, "Operate on methods."
|
||||
opt.on :c, :context, "Select object context to run under.", true do |context|
|
||||
target = Pry.binding_for(target.eval(context))
|
||||
end
|
||||
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
||||
opt.on :h, :help, "This message." do
|
||||
output.puts opt
|
||||
end
|
||||
end
|
||||
|
||||
next if opts.help?
|
||||
meth = opts[:method_object]
|
||||
raise Pry::CommandError, "No documentation found." if meth.doc.nil? || meth.doc.empty?
|
||||
|
||||
args = [nil] if args.empty?
|
||||
args.each do |method_name|
|
||||
begin
|
||||
meth = get_method_or_raise(method_name, target, opts.to_hash(true))
|
||||
rescue CommandError => e
|
||||
puts "\nError: #{e.message}"
|
||||
next
|
||||
end
|
||||
|
||||
next output.puts("No documentation found.") if meth.doc.nil? || meth.doc.empty?
|
||||
|
||||
doc = process_comment_markup(meth.doc, meth.source_type)
|
||||
output.puts make_header(meth, doc)
|
||||
output.puts "#{text.bold("visibility: ")} #{meth.visibility}"
|
||||
output.puts "#{text.bold("signature: ")} #{meth.signature}"
|
||||
output.puts
|
||||
render_output(opts.flood?, false, doc)
|
||||
doc
|
||||
end
|
||||
doc = process_comment_markup(meth.doc, meth.source_type)
|
||||
output.puts make_header(meth, doc)
|
||||
output.puts "#{text.bold("visibility: ")} #{meth.visibility}"
|
||||
output.puts "#{text.bold("signature: ")} #{meth.signature}"
|
||||
output.puts
|
||||
render_output(opts.flood?, false, doc)
|
||||
end
|
||||
|
||||
alias_command "?", "show-doc"
|
||||
|
||||
command "stat", "View method information and set _file_ and _dir_ locals. Type `stat --help` for more info." do |*args|
|
||||
command "stat", "View method information and set _file_ and _dir_ locals. Type `stat --help` for more info.", :shellwords => false do |*args|
|
||||
target = target()
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
opts = parse_options!(args, :method_object => true) do |opt|
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: stat [OPTIONS] [METH]
|
||||
Show method information for method METH and set _file_ and _dir_ locals.
|
||||
e.g: stat hello_method
|
||||
USAGE
|
||||
|
||||
opt.on :M, "instance-methods", "Operate on instance methods."
|
||||
opt.on :m, :methods, "Operate on methods."
|
||||
opt.on :c, :context, "Select object context to run under.", true do |context|
|
||||
target = Pry.binding_for(target.eval(context))
|
||||
end
|
||||
opt.on :h, :help, "This message" do
|
||||
output.puts opt
|
||||
end
|
||||
end
|
||||
|
||||
next if opts.help?
|
||||
|
||||
meth = get_method_or_raise(args.shift, target, opts.to_hash(true))
|
||||
|
||||
meth = opts[:method_object]
|
||||
output.puts unindent <<-EOS
|
||||
Method Information:
|
||||
--
|
||||
|
@ -90,12 +58,12 @@ class Pry
|
|||
EOS
|
||||
end
|
||||
|
||||
command "gist-method", "Gist a method to github. Type `gist-method --help` for more info.", :requires_gem => "gist" do |*args|
|
||||
command "gist-method", "Gist a method to github. Type `gist-method --help` for more info.", :requires_gem => "gist", :shellwords => false do |*args|
|
||||
require 'gist'
|
||||
|
||||
target = target()
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
opts = parse_options!(args, :method_object => true) do |opt|
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: gist-method [OPTIONS] [METH]
|
||||
Gist the method (doc or source) to github.
|
||||
|
@ -104,18 +72,11 @@ class Pry
|
|||
e.g: gist -d my_method
|
||||
USAGE
|
||||
|
||||
opt.on :m, :method, "Gist a method's source."
|
||||
opt.on :d, :doc, "Gist a method's documentation."
|
||||
opt.on :p, :private, "Create a private gist (default: true)", :default => true
|
||||
opt.on :h, :help, "This message" do
|
||||
output.puts opt
|
||||
end
|
||||
end
|
||||
|
||||
next if opts.help?
|
||||
|
||||
meth = get_method_or_raise(args.shift, target, opts.to_hash(true))
|
||||
|
||||
meth = opts[:method_object]
|
||||
type_map = { :ruby => "rb", :c => "c", :plain => "plain" }
|
||||
if !opts.doc?
|
||||
content = meth.source
|
||||
|
|
|
@ -46,7 +46,7 @@ class Pry
|
|||
index == 0 ? text.bright_green(spec.version.to_s) : text.green(spec.version.to_s)
|
||||
end
|
||||
|
||||
output.puts "#{text.white gem} (#{versions.join ', '})"
|
||||
output.puts "#{text.default gem} (#{versions.join ', '})"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,59 +5,39 @@ class Pry
|
|||
|
||||
Introspection = Pry::CommandSet.new do
|
||||
|
||||
command "show-method", "Show the source for METH. Type `show-method --help` for more info. Aliases: $, show-source" do |*args|
|
||||
target = target()
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
command "show-method", "Show the source for METH. Type `show-method --help` for more info. Aliases: $, show-source", :shellwords => false do |*args|
|
||||
opts = parse_options!(args, :method_object => true) do |opt|
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: show-method [OPTIONS] [METH 1] [METH 2] [METH N]
|
||||
Usage: show-method [OPTIONS] [METH]
|
||||
Show the source for method METH. Tries instance methods first and then methods by default.
|
||||
e.g: show-method hello_method
|
||||
USAGE
|
||||
|
||||
opt.on :l, "line-numbers", "Show line numbers."
|
||||
opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)."
|
||||
|
||||
opt.on :M, "instance-methods", "Operate on instance methods."
|
||||
opt.on :m, :methods, "Operate on methods."
|
||||
opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
|
||||
opt.on :c, :context, "Select object context to run under.", true do |context|
|
||||
target = Pry.binding_for(target.eval(context))
|
||||
end
|
||||
opt.on :h, :help, "This message." do
|
||||
output.puts opt
|
||||
end
|
||||
end
|
||||
next if opts.help?
|
||||
opts[:instance] = opts['instance-methods'] if opts.m?
|
||||
|
||||
args = [nil] if args.empty?
|
||||
args.each do |method_name|
|
||||
begin
|
||||
meth = get_method_or_raise(method_name, target, opts.to_hash(true))
|
||||
rescue CommandError => e
|
||||
puts "\nError: #{e.message}"
|
||||
next
|
||||
end
|
||||
next unless meth.source
|
||||
meth = opts[:method_object]
|
||||
|
||||
output.puts make_header(meth)
|
||||
if Pry.color
|
||||
code = CodeRay.scan(meth.source, meth.source_type).term
|
||||
else
|
||||
code = meth.source
|
||||
end
|
||||
raise CommandError, "Could not find method source" unless meth.source
|
||||
|
||||
start_line = false
|
||||
if opts.b?
|
||||
start_line = 1
|
||||
elsif opts.l?
|
||||
start_line = meth.source_line || 1
|
||||
end
|
||||
output.puts make_header(meth)
|
||||
|
||||
render_output(opts.flood?, start_line, code)
|
||||
code
|
||||
if Pry.color
|
||||
code = CodeRay.scan(meth.source, meth.source_type).term
|
||||
else
|
||||
code = meth.source
|
||||
end
|
||||
|
||||
start_line = false
|
||||
if opts.b?
|
||||
start_line = 1
|
||||
elsif opts.l?
|
||||
start_line = meth.source_line || 1
|
||||
end
|
||||
|
||||
render_output(opts.flood?, start_line, code)
|
||||
end
|
||||
|
||||
alias_command "show-source", "show-method"
|
||||
|
@ -217,10 +197,10 @@ class Pry
|
|||
end
|
||||
end
|
||||
|
||||
command "edit-method", "Edit a method. Type `edit-method --help` for more info." do |*args|
|
||||
command "edit-method", "Edit a method. Type `edit-method --help` for more info.", :shellwords => false do |*args|
|
||||
target = target()
|
||||
|
||||
opts = Slop.parse!(args) do |opt|
|
||||
opts = parse_options!(args, :method_object => true) do |opt|
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: edit-method [OPTIONS] [METH]
|
||||
Edit the method METH in an editor.
|
||||
|
@ -228,25 +208,19 @@ class Pry
|
|||
e.g: edit-method hello_method
|
||||
USAGE
|
||||
|
||||
opt.on :M, "instance-methods", "Operate on instance methods."
|
||||
opt.on :m, :methods, "Operate on methods."
|
||||
opt.on :n, "no-reload", "Do not automatically reload the method's file after editing."
|
||||
opt.on "no-jump", "Do not fast forward editor to first line of method."
|
||||
opt.on :p, :patch, "Instead of editing the method's file, try to edit in a tempfile and apply as a monkey patch."
|
||||
opt.on :c, :context, "Select object context to run under.", true do |context|
|
||||
target = Pry.binding_for(target.eval(context))
|
||||
end
|
||||
opt.on :h, :help, "This message." do
|
||||
output.puts opt
|
||||
end
|
||||
end
|
||||
next if opts.help?
|
||||
|
||||
if !Pry.config.editor
|
||||
raise CommandError, "No editor set!\nEnsure that #{text.bold("Pry.config.editor")} is set to your editor of choice."
|
||||
end
|
||||
|
||||
meth = get_method_or_raise(args.shift, target, opts.to_hash(true))
|
||||
meth = opts[:method_object]
|
||||
|
||||
if opts.p? || meth.dynamically_defined?
|
||||
lines = meth.source.lines.to_a
|
||||
|
|
|
@ -4,229 +4,219 @@ class Pry
|
|||
Ls = Pry::CommandSet.new do
|
||||
|
||||
helpers do
|
||||
def should_trim?(target, options)
|
||||
if target.eval('self').is_a? Module
|
||||
options[:e] || target.eval('self') >= Object
|
||||
|
||||
# http://ruby.runpaint.org/globals, and running "puts global_variables.inspect".
|
||||
BUILTIN_GLOBALS = %w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\
|
||||
$: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT
|
||||
$FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER
|
||||
$INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS
|
||||
$ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID
|
||||
$PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout)
|
||||
|
||||
# $SAFE and $? are thread-local, the exception stuff only works in a rescue clause,
|
||||
# everything else is basically a local variable with a $ in its name.
|
||||
PSEUDO_GLOBALS = %w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9
|
||||
$CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO
|
||||
$LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH)
|
||||
|
||||
# Get all the methods that we'll want to output
|
||||
def all_methods(obj, opts)
|
||||
opts.M? ? Pry::Method.all_from_class(obj) : Pry::Method.all_from_obj(obj)
|
||||
end
|
||||
|
||||
def resolution_order(obj, opts)
|
||||
opts.M? ? Pry::Method.instance_resolution_order(obj) : Pry::Method.resolution_order(obj)
|
||||
end
|
||||
|
||||
# Get the name of the klass for pretty display in the title column of ls -m
|
||||
# as there can only ever be one singleton class of a non-class, we just call
|
||||
# that "self".
|
||||
def class_name(klass)
|
||||
if klass == klass.ancestors.first
|
||||
(klass.name || "") == "" ? klass.to_s : klass.name
|
||||
elsif klass.ancestors.include?(Module)
|
||||
begin
|
||||
"#{class_name(ObjectSpace.each_object(klass).detect{ |x| class << x; self; end == klass })}.self"
|
||||
rescue # ObjectSpace is not enabled by default in jruby
|
||||
klass.to_s.sub(/#<Class:(.*)>/, '\1.self')
|
||||
end
|
||||
else
|
||||
options[:e]
|
||||
"self"
|
||||
end
|
||||
end
|
||||
|
||||
def trim_methods(target, options, visibility)
|
||||
if should_trim?(target, options)
|
||||
[]
|
||||
else
|
||||
Object.send("#{visibility}_instance_methods")
|
||||
# Get a lambda that can be used with .take_while to prevent over-eager
|
||||
# traversal of the Object's ancestry graph.
|
||||
def below_ceiling(obj, opts)
|
||||
ceiling = if opts.q?
|
||||
[opts.M? ? obj.ancestors[1] : obj.class.ancestors[1]] + Pry.config.ls.ceiling
|
||||
elsif opts.v?
|
||||
[]
|
||||
else
|
||||
Pry.config.ls.ceiling.dup
|
||||
end
|
||||
|
||||
# We always want to show *something*, so if this object is actually a base type,
|
||||
# then we'll show the class itself, but none of its ancestors nor modules.
|
||||
ceiling.map!{ |klass| (obj.class == klass || obj == klass) ? klass.ancestors[1] : klass }
|
||||
|
||||
lambda { |klass| !ceiling.include?(klass) }
|
||||
end
|
||||
|
||||
# Format and colourise a list of methods.
|
||||
def format_methods(methods)
|
||||
methods.sort_by(&:name).map do |method|
|
||||
if method.name == 'method_missing'
|
||||
color(:method_missing, 'method_missing')
|
||||
elsif method.visibility == :private
|
||||
color(:private_method, method.name)
|
||||
elsif method.visibility == :protected
|
||||
color(:protected_method, method.name)
|
||||
else
|
||||
color(:public_method, method.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def ls_color_map
|
||||
{
|
||||
"local variables" => Pry.config.ls.local_var_color,
|
||||
"instance variables" => Pry.config.ls.instance_var_color,
|
||||
"class variables" => Pry.config.ls.class_var_color,
|
||||
"global variables" => Pry.config.ls.global_var_color,
|
||||
"just singleton methods" => Pry.config.ls.method_color,
|
||||
"public methods" => Pry.config.ls.method_color,
|
||||
"private methods" => Pry.config.ls.method_color,
|
||||
"protected methods" => Pry.config.ls.method_color,
|
||||
"public instance methods" => Pry.config.ls.instance_method_color,
|
||||
"private instance methods" => Pry.config.ls.instance_method_color,
|
||||
"protected instance methods" => Pry.config.ls.instance_method_color,
|
||||
"constants" => Pry.config.ls.constant_color
|
||||
}
|
||||
def format_variables(type, vars)
|
||||
vars.sort_by(&:downcase).map{ |var| color(type, var) }
|
||||
end
|
||||
|
||||
def format_constants(mod, constants)
|
||||
constants.sort_by(&:downcase).map do |name|
|
||||
if const = (!mod.autoload?(name) && mod.const_get(name) rescue nil)
|
||||
if (const < Exception rescue false)
|
||||
color(:exception_constant, name)
|
||||
elsif (Module === mod.const_get(name) rescue false)
|
||||
color(:class_constant, name)
|
||||
else
|
||||
color(:constant, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def format_globals(globals)
|
||||
globals.sort_by(&:downcase).map do |name|
|
||||
if PSEUDO_GLOBALS.include?(name)
|
||||
color(:pseudo_global, name)
|
||||
elsif BUILTIN_GLOBALS.include?(name)
|
||||
color(:builtin_global, name)
|
||||
else
|
||||
color(:global_var, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def format_locals(locals)
|
||||
locals.sort_by(&:downcase).map do |name|
|
||||
if _pry_.special_locals.include?(name.to_sym)
|
||||
color(:pry_var, name)
|
||||
else
|
||||
color(:local_var, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add a new section to the output. Outputs nothing if the section would be empty.
|
||||
def output_section(heading, body)
|
||||
return if body.compact.empty?
|
||||
output.puts "#{text.bold(color(:heading, heading))}: #{body.compact.join(Pry.config.ls.separator)}"
|
||||
end
|
||||
|
||||
# Color output based on config.ls.*_color
|
||||
def color(type, str)
|
||||
text.send(Pry.config.ls.send(:"#{type}_color"), str)
|
||||
end
|
||||
end
|
||||
|
||||
command "ls", "Show the list of vars and methods in the current scope. Type `ls --help` for more info." do |*args|
|
||||
options = {}
|
||||
# Set target local to the default -- note that we can set a different target for
|
||||
# ls if we like: e.g ls my_var
|
||||
target = target()
|
||||
command "ls", "Show the list of vars and methods in the current scope. Type `ls --help` for more info.",
|
||||
:shellwords => false, :interpolate => false do |*args|
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = unindent <<-EOS
|
||||
Usage: ls [OPTIONS] [VAR]
|
||||
opts = Slop.parse!(args, :strict => true) do |opt|
|
||||
opt.banner unindent <<-USAGE
|
||||
Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
|
||||
ls [-g] [-l]
|
||||
|
||||
List information about VAR (the current context by default).
|
||||
Shows local and instance variables by default.
|
||||
--
|
||||
EOS
|
||||
ls shows you which methods, constants and variables are accessible to Pry. By default it shows you the local variables defined in the current shell, and any public methods or instance variables defined on the current object.
|
||||
|
||||
opts.on("-g", "--globals", "Display global variables.") do
|
||||
options[:g] = true
|
||||
end
|
||||
The colours used are configurable using Pry.config.ls.*_color, and the separator is Pry.config.ls.separator.
|
||||
|
||||
opts.on("-c", "--constants", "Display constants.") do
|
||||
options[:c] = true
|
||||
end
|
||||
Pry.config.ls.ceiling is used to hide methods defined higher up in the inheritance chain, this is by default set to [Object, Module, Class] so that methods defined on all Objects are omitted. The -v flag can be used to ignore this setting and show all methods, while the -q can be used to set the ceiling much lower and show only methods defined on the object or its direct class.
|
||||
USAGE
|
||||
|
||||
opts.on("-l", "--locals", "Display locals.") do
|
||||
options[:l] = true
|
||||
end
|
||||
opt.on :m, "methods", "Show public methods defined on the Object (default)"
|
||||
opt.on :M, "module", "Show methods defined in a Module or Class"
|
||||
|
||||
opts.on("-i", "--ivars", "Display instance variables.") do
|
||||
options[:i] = true
|
||||
end
|
||||
opt.on :p, "ppp", "Show public, protected (in yellow) and private (in green) methods"
|
||||
opt.on :q, "quiet", "Show only methods defined on object.singleton_class and object.class"
|
||||
opt.on :v, "verbose", "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)"
|
||||
|
||||
opts.on("-k", "--class-vars", "Display class variables.") do
|
||||
options[:k] = true
|
||||
end
|
||||
opt.on :g, "globals", "Show global variables, including those builtin to Ruby (in cyan)"
|
||||
opt.on :l, "locals", "Show locals, including those provided by Pry (in red)"
|
||||
|
||||
opts.on("-m", "--methods", "Display methods (public methods by default).") do
|
||||
options[:m] = true
|
||||
end
|
||||
opt.on :c, "constants", "Show constants, highlighting classes (in blue), and exceptions (in purple)"
|
||||
|
||||
opts.on("-M", "--instance-methods", "Display instance methods (only relevant to classes and modules).") do
|
||||
options[:M] = true
|
||||
end
|
||||
opt.on :i, "ivars", "Show instance variables (in blue) and class variables (in bright blue)"
|
||||
|
||||
opts.on("-P", "--public", "Display public methods (with -m).") do
|
||||
options[:P] = true
|
||||
end
|
||||
opt.on :G, "grep", "Filter output by regular expression", :optional => false
|
||||
|
||||
opts.on("-r", "--protected", "Display protected methods (with -m).") do
|
||||
options[:r] = true
|
||||
end
|
||||
|
||||
opts.on("-p", "--private", "Display private methods (with -m).") do
|
||||
options[:p] = true
|
||||
end
|
||||
|
||||
opts.on("-j", "--just-singletons", "Display just the singleton methods (with -m).") do
|
||||
options[:j] = true
|
||||
end
|
||||
|
||||
opts.on("-s", "--super", "Include superclass entries excluding Object (relevant to constant and methods options).") do
|
||||
options[:s] = true
|
||||
end
|
||||
|
||||
opts.on("-e", "--everything", "Include superclass entries including Object (must be combined with -s switch).") do
|
||||
options[:e] = true
|
||||
end
|
||||
|
||||
opts.on("-a", "--all", "Display all types of entries.") do
|
||||
options[:a] = true
|
||||
end
|
||||
|
||||
opts.on("-v", "--verbose", "Verbose ouput.") do
|
||||
options[:v] = true
|
||||
end
|
||||
|
||||
opts.on("-f", "--flood", "Do not use a pager to view text longer than one screen.") do
|
||||
options[:f] = true
|
||||
end
|
||||
|
||||
opts.on("--grep REG", "Regular expression to be used.") do |reg|
|
||||
options[:grep] = Regexp.new(reg)
|
||||
end
|
||||
|
||||
opts.on_tail("-h", "--help", "Show this message.") do
|
||||
output.puts opts
|
||||
options[:h] = true
|
||||
end
|
||||
end.order(args) do |new_target|
|
||||
target = Pry.binding_for(target.eval("#{new_target}")) if !options[:h]
|
||||
opt.on :h, "help", "Show help"
|
||||
end
|
||||
|
||||
# exit if we've displayed help
|
||||
next if options[:h]
|
||||
next output.puts(opts) if opts.h?
|
||||
|
||||
# default is locals/ivars/class vars.
|
||||
# Only occurs when no options or when only option is verbose
|
||||
options.merge!({
|
||||
:l => true,
|
||||
:i => true,
|
||||
:k => true
|
||||
}) if options.empty? || (options.size == 1 && options[:v]) || (options.size == 1 && options[:grep])
|
||||
obj = args.empty? ? target_self : target.eval(args.join(" "))
|
||||
|
||||
options[:grep] = // if !options[:grep]
|
||||
# exclude -q, -v and --grep because they don't specify what the user wants to see.
|
||||
has_opts = (opts.m? || opts.M? || opts.p? || opts.g? || opts.l? || opts.c? || opts.i?)
|
||||
|
||||
show_methods = opts.m? || opts.M? || opts.p? || !has_opts
|
||||
show_constants = opts.c? || (!has_opts && Module === obj)
|
||||
show_ivars = opts.i? || !has_opts
|
||||
show_locals = opts.l? || (!has_opts && args.empty?)
|
||||
|
||||
grep_regex, grep = [Regexp.new(opts[:G] || "."), lambda{ |x| x.grep(grep_regex) }]
|
||||
|
||||
raise Pry::CommandError, "-l does not make sense with a specified Object" if opts.l? && !args.empty?
|
||||
raise Pry::CommandError, "-g does not make sense with a specified Object" if opts.g? && !args.empty?
|
||||
raise Pry::CommandError, "-q does not make sense with -v" if opts.q? && opts.v?
|
||||
raise Pry::CommandError, "-M only makes sense with a Module or a Class" if opts.M? && !(Module === obj)
|
||||
raise Pry::CommandError, "-c only makes sense with a Module or a Class" if opts.c? && !args.empty? && !(Module === obj)
|
||||
|
||||
|
||||
# Display public methods by default if -m or -M switch is used.
|
||||
options[:P] = true if (options[:m] || options[:M]) && !(options[:p] || options[:r] || options[:j])
|
||||
if opts.g?
|
||||
output_section("global variables", grep[format_globals(target.eval("global_variables"))])
|
||||
end
|
||||
|
||||
info = {}
|
||||
target_self = target.eval('self')
|
||||
if show_constants
|
||||
mod = Module === obj ? obj : Object
|
||||
constants = mod.constants
|
||||
constants -= (mod.ancestors - [mod]).map(&:constants).flatten unless opts.v?
|
||||
output_section("constants", grep[format_constants(mod, constants)])
|
||||
end
|
||||
|
||||
# ensure we have a real boolean and not a `nil` (important when
|
||||
# interpolating in the string)
|
||||
options[:s] = !!options[:s]
|
||||
if show_methods
|
||||
# methods is a hash {Module/Class => [Pry::Methods]}
|
||||
methods = all_methods(obj, opts).select{ |method| opts.p? || method.visibility == :public }.group_by(&:owner)
|
||||
|
||||
# Numbers (e.g 0, 1, 2) are for ordering the hash values in Ruby 1.8
|
||||
i = -1
|
||||
|
||||
# Start collecting the entries selected by the user
|
||||
info["local variables"] = [Array(target.eval("local_variables")).sort, i += 1] if options[:l] || options[:a]
|
||||
info["instance variables"] = [Array(target.eval("instance_variables")).sort, i += 1] if options[:i] || options[:a]
|
||||
|
||||
info["class variables"] = [if target_self.is_a?(Module)
|
||||
Array(target.eval("class_variables")).sort
|
||||
else
|
||||
Array(target.eval("self.class.class_variables")).sort
|
||||
end, i += 1] if options[:k] || options[:a]
|
||||
|
||||
info["global variables"] = [Array(target.eval("global_variables")).sort, i += 1] if options[:g] || options[:a]
|
||||
|
||||
info["public methods"] = [Array(target.eval("public_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :public), i += 1] if (options[:m] && options[:P]) || options[:a]
|
||||
|
||||
info["protected methods"] = [Array(target.eval("protected_methods(#{options[:s]})")).sort - trim_methods(target, options, :protected), i += 1] if (options[:m] && options[:r]) || options[:a]
|
||||
|
||||
info["private methods"] = [Array(target.eval("private_methods(#{options[:s]})")).sort - trim_methods(target, options, :private), i += 1] if (options[:m] && options[:p]) || options[:a]
|
||||
|
||||
info["just singleton methods"] = [Array(target.eval("methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:j]) && !options[:a]
|
||||
|
||||
info["public instance methods"] = [Array(target.eval("public_instance_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :public), i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:P]) || options[:a])
|
||||
|
||||
info["protected instance methods"] = [Array(target.eval("protected_instance_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :protected), i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:r]) || options[:a])
|
||||
|
||||
info["private instance methods"] = [Array(target.eval("private_instance_methods(#{options[:s]})")).uniq.sort - trim_methods(target, options, :private), i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:p]) || options[:a])
|
||||
|
||||
# dealing with 1.8/1.9 compatibility issues :/
|
||||
csuper = options[:s]
|
||||
if Module.method(:constants).arity == 0
|
||||
csuper = nil
|
||||
end
|
||||
|
||||
info["constants"] = [Array(target_self.is_a?(Module) ? target.eval("constants(#{csuper})") :
|
||||
target.eval("self.class.constants(#{csuper})")).uniq.sort, i += 1] if options[:c] || options[:a]
|
||||
|
||||
text = ""
|
||||
|
||||
# verbose output?
|
||||
if options[:v]
|
||||
# verbose
|
||||
|
||||
info.sort_by { |k, v| v.last }.each do |k, v|
|
||||
if !v.first.empty?
|
||||
text << "#{k}:\n--\n"
|
||||
filtered_list = v.first.grep options[:grep]
|
||||
text << text().send(ls_color_map[k], (filtered_list.join(Pry.config.ls.separator)))
|
||||
text << "\n\n"
|
||||
end
|
||||
end
|
||||
|
||||
if !options[:f]
|
||||
stagger_output(text)
|
||||
else
|
||||
output.puts text
|
||||
end
|
||||
|
||||
# plain
|
||||
else
|
||||
list = info.sort_by { |k, v| v.last }.map { |k, v| [k, [v.first.grep(options[:grep])], v.last] }
|
||||
list = list.each { |k, v| text << text().send(ls_color_map[k], v.first.join(Pry.config.ls.separator)); text << Pry.config.ls.separator }
|
||||
|
||||
if !options[:f]
|
||||
stagger_output(text)
|
||||
else
|
||||
output.puts text
|
||||
end
|
||||
list
|
||||
end
|
||||
end
|
||||
# reverse the resolution order so that the most useful information appears right by the prompt
|
||||
resolution_order(obj, opts).take_while(&below_ceiling(obj, opts)).reverse.each do |klass|
|
||||
methods_here = format_methods((methods[klass] || []).select{ |m| m.name =~ grep_regex })
|
||||
output_section "#{class_name(klass)} methods", methods_here
|
||||
end
|
||||
end
|
||||
|
||||
if show_ivars
|
||||
klass = (Module === obj ? obj : obj.class)
|
||||
output_section("instance variables", format_variables(:instance_var, grep[obj.__send__(:instance_variables)]))
|
||||
output_section("class variables", format_variables(:class_var, grep[klass.__send__(:class_variables)]))
|
||||
end
|
||||
|
||||
if show_locals
|
||||
output_section("locals", format_locals(grep[target.eval("local_variables")]))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
require "pry/helpers/base_helpers"
|
||||
require "pry/helpers/options_helpers"
|
||||
require "pry/helpers/command_helpers"
|
||||
require "pry/helpers/text"
|
||||
|
|
|
@ -2,6 +2,7 @@ class Pry
|
|||
module Helpers
|
||||
|
||||
module CommandHelpers
|
||||
include OptionsHelpers
|
||||
|
||||
module_function
|
||||
|
||||
|
@ -28,15 +29,29 @@ class Pry
|
|||
end
|
||||
|
||||
def get_method_or_raise(name, target, opts={}, omit_help=false)
|
||||
if (meth = Pry::Method.from_str(name, target, opts))
|
||||
set_file_and_dir_locals(meth.source_file)
|
||||
meth
|
||||
else
|
||||
# FIXME: better/more accurate error handling
|
||||
message = "The method '#{name}' could not be found."
|
||||
message << " Type `#{command_name} --help` for help." unless omit_help
|
||||
raise CommandError, message
|
||||
meth = Pry::Method.from_str(name, target, opts)
|
||||
|
||||
if name && !meth
|
||||
command_error("The method '#{name}' could not be found.", omit_help)
|
||||
elsif !meth
|
||||
command_error("No method name given, and context is not a method.", omit_help)
|
||||
end
|
||||
|
||||
(opts[:super] || 0).times do
|
||||
if meth.super
|
||||
meth = meth.super
|
||||
else
|
||||
command_error("The method '#{meth.name}' is not defined in a superclass of '#{class_name(meth.owner)}'.", omit_help)
|
||||
end
|
||||
end
|
||||
|
||||
set_file_and_dir_locals(meth.source_file)
|
||||
meth
|
||||
end
|
||||
|
||||
def command_error(message, omit_help)
|
||||
message += " Type `#{command_name} --help` for help." unless omit_help
|
||||
raise CommandError, message
|
||||
end
|
||||
|
||||
def make_header(meth, content=meth.source)
|
||||
|
|
53
lib/pry/helpers/options_helpers.rb
Normal file
53
lib/pry/helpers/options_helpers.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
class Pry
|
||||
module Helpers
|
||||
module OptionsHelpers
|
||||
module_function
|
||||
|
||||
# Use Slop to parse the arguments given.
|
||||
#
|
||||
# @param [Array] mutable list of arguments
|
||||
# @param [Hash] predefined option types
|
||||
# @param [&Block] used to add custom arguments.
|
||||
#
|
||||
# @return Slop::Options
|
||||
#
|
||||
# @option [Boolean] :method_object
|
||||
# Set to true if you want to get a method object from the user.
|
||||
#
|
||||
def parse_options!(args, predefined={}, &block)
|
||||
Slop.parse!(args) do |opt|
|
||||
add_method_object_options(opt) if predefined[:method_object]
|
||||
|
||||
yield opt
|
||||
opt.on :h, :help, "This message" do
|
||||
output.puts opt
|
||||
throw :command_done
|
||||
end
|
||||
|
||||
end.tap do |opts|
|
||||
process_method_object_options(args, opts) if predefined[:method_object]
|
||||
end
|
||||
end
|
||||
|
||||
# Add the method object options to an unused Slop instance.
|
||||
def add_method_object_options(opt)
|
||||
@method_target = target
|
||||
opt.on :M, "instance-methods", "Operate on instance methods."
|
||||
opt.on :m, :methods, "Operate on methods."
|
||||
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors."
|
||||
opt.on :c, :context, "Select object context to run under.", true do |context|
|
||||
@method_target = Pry.binding_for(target.eval(context))
|
||||
end
|
||||
end
|
||||
|
||||
# Add the derived :method_object option to a used Slop instance.
|
||||
def process_method_object_options(args, opts)
|
||||
opts[:instance] = opts['instance-methods'] if opts.m?
|
||||
# TODO: de-hack when we upgrade Slop: https://github.com/injekt/slop/pull/30
|
||||
opts.options[:super].force_argument_value opts.options[:super].count if opts.super?
|
||||
method_obj = get_method_or_raise(args.empty? ? nil : args.join(" "), @method_target, opts.to_hash(true))
|
||||
opts.on(:method_object, :default => method_obj)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -50,6 +50,16 @@ class Pry
|
|||
Pry.color ? "\e[1m#{text}\e[0m" : text.to_s
|
||||
end
|
||||
|
||||
# Returns _text_ in the default foreground colour.
|
||||
# Use this instead of "black" or "white" when you mean absence of colour.
|
||||
#
|
||||
# @param [String, #to_s]
|
||||
# @return [String] _text_
|
||||
def default(text)
|
||||
text.to_s
|
||||
end
|
||||
alias_method :bright_default, :bold
|
||||
|
||||
# Executes _block_ with _Pry.color_ set to false.
|
||||
#
|
||||
# @param [Proc]
|
||||
|
|
|
@ -20,10 +20,10 @@ class Pry
|
|||
def from_str(name, target=TOPLEVEL_BINDING, options={})
|
||||
if name.nil?
|
||||
from_binding(target)
|
||||
elsif name.to_s =~ /(\S+)\#(\S+)\Z/
|
||||
elsif name.to_s =~ /(.+)\#(\S+)\Z/
|
||||
context, meth_name = $1, $2
|
||||
from_module(target.eval(context), meth_name)
|
||||
elsif name.to_s =~ /(\S+)\.(\S+)\Z/
|
||||
elsif name.to_s =~ /(.+)\.(\S+)\Z/
|
||||
context, meth_name = $1, $2
|
||||
from_obj(target.eval(context), meth_name)
|
||||
elsif options[:instance]
|
||||
|
@ -74,14 +74,88 @@ class Pry
|
|||
def from_obj(obj, name)
|
||||
new(obj.method(name)) rescue nil
|
||||
end
|
||||
|
||||
# Get all of the instance methods of a `Class` or `Module`
|
||||
# @param [Class,Module] klass
|
||||
# @return [Array[Pry::Method]]
|
||||
def all_from_class(klass)
|
||||
all_from_common(klass, :instance_method)
|
||||
end
|
||||
|
||||
# Get all of the methods on an `Object`
|
||||
# @param [Object] obj
|
||||
# @return [Array[Pry::Method]]
|
||||
def all_from_obj(obj)
|
||||
all_from_common(obj, :method)
|
||||
end
|
||||
|
||||
# Get every `Class` and `Module`, in order, that will be checked when looking
|
||||
# for an instance method to call on this object.
|
||||
# @param [Object] obj
|
||||
# @return [Array[Class, Module]]
|
||||
def resolution_order(obj)
|
||||
if obj.is_a?(Class)
|
||||
singleton_class_resolution_order(obj) + instance_resolution_order(Class)
|
||||
else
|
||||
klass = singleton_class(obj) rescue obj.class
|
||||
instance_resolution_order(klass)
|
||||
end
|
||||
end
|
||||
|
||||
# Get every `Class` and `Module`, in order, that will be checked when looking
|
||||
# for methods on instances of the given `Class` or `Module`.
|
||||
# This does not treat singleton classes of classes specially.
|
||||
# @param [Class, Module] klass
|
||||
# @return [Array[Class, Module]]
|
||||
def instance_resolution_order(klass)
|
||||
# include klass in case it is a singleton class,
|
||||
([klass] + klass.ancestors).uniq
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# See all_from_class and all_from_obj.
|
||||
# If method_type is :instance_method, obj must be a `Class` or a `Module`
|
||||
# If method_type is :method, obj can be any `Object`
|
||||
#
|
||||
# N.B. we pre-cache the visibility here to avoid O(N²) behaviour in "ls".
|
||||
def all_from_common(obj, method_type)
|
||||
%w(public protected private).map do |visibility|
|
||||
obj.__send__(:"#{visibility}_#{method_type}s").map do |method_name|
|
||||
new(obj.__send__(method_type, method_name), :visibility => visibility.to_sym)
|
||||
end
|
||||
end.flatten(1)
|
||||
end
|
||||
|
||||
# Get the singleton classes of superclasses that could define methods on
|
||||
# the given class object, and any modules they include.
|
||||
# If a module is included at multiple points in the ancestry, only
|
||||
# the lowest copy will be returned.
|
||||
def singleton_class_resolution_order(klass)
|
||||
resolution_order = klass.ancestors.map do |anc|
|
||||
[singleton_class(anc)] + singleton_class(anc).included_modules if anc.is_a?(Class)
|
||||
end.compact.flatten(1)
|
||||
|
||||
resolution_order.reverse.uniq.reverse - Class.included_modules
|
||||
end
|
||||
|
||||
def singleton_class(obj); class << obj; self; end end
|
||||
end
|
||||
|
||||
# A new instance of `Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.
|
||||
#
|
||||
# @param [::Method, UnboundMethod, Proc] method
|
||||
# @param [Hash] known_info, can be used to pre-cache expensive to compute stuff.
|
||||
# @return [Pry::Method]
|
||||
def initialize(method)
|
||||
def initialize(method, known_info={})
|
||||
@method = method
|
||||
@visibility = known_info[:visibility]
|
||||
end
|
||||
|
||||
# Get the name of the method as a String, regardless of the underlying Method#name type.
|
||||
# @return [String]
|
||||
def name
|
||||
@method.name.to_s
|
||||
end
|
||||
|
||||
# @return [String, nil] The source code of the method, or `nil` if it's unavailable.
|
||||
|
@ -156,15 +230,15 @@ class Pry
|
|||
# @return [Symbol] The visibility of the method. May be `:public`,
|
||||
# `:protected`, or `:private`.
|
||||
def visibility
|
||||
if owner.public_instance_methods.include?(name)
|
||||
:public
|
||||
elsif owner.protected_instance_methods.include?(name)
|
||||
:protected
|
||||
elsif owner.private_instance_methods.include?(name)
|
||||
:private
|
||||
else
|
||||
:none
|
||||
end
|
||||
@visibility ||= if owner.public_instance_methods.include?(name)
|
||||
:public
|
||||
elsif owner.protected_instance_methods.include?(name)
|
||||
:protected
|
||||
elsif owner.private_instance_methods.include?(name)
|
||||
:private
|
||||
else
|
||||
:none
|
||||
end
|
||||
end
|
||||
|
||||
# @return [String] A representation of the method's signature, including its
|
||||
|
@ -191,6 +265,18 @@ class Pry
|
|||
"#{name}(#{args.join(', ')})"
|
||||
end
|
||||
|
||||
# @return [Pry::Method, nil] The wrapped method that get's called when you
|
||||
# use "super" in the body of this method.
|
||||
def super(times=1)
|
||||
if respond_to?(:receiver)
|
||||
sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times)
|
||||
sup &&= sup.bind(receiver)
|
||||
else
|
||||
sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times)
|
||||
end
|
||||
Pry::Method.new(sup) if sup
|
||||
end
|
||||
|
||||
# @return [Boolean] Was the method defined outside a source file?
|
||||
def dynamically_defined?
|
||||
!!(source_file and source_file =~ /(\(.*\))|<.*>/)
|
||||
|
@ -259,5 +345,16 @@ class Pry
|
|||
def strip_leading_whitespace(text)
|
||||
Pry::Helpers::CommandHelpers.unindent(text)
|
||||
end
|
||||
|
||||
# @param [Class,Module] the ancestors to investigate
|
||||
# @return [Method] the unwrapped super-method
|
||||
def super_using_ancestors(ancestors, times=1)
|
||||
next_owner = self.owner
|
||||
times.times do
|
||||
next_owner = ancestors[ancestors.index(next_owner) + 1]
|
||||
return nil unless next_owner
|
||||
end
|
||||
next_owner.instance_method(name) rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -208,16 +208,38 @@ class Pry
|
|||
|
||||
config.memory_size = 100
|
||||
|
||||
config.ls ||= OpenStruct.new
|
||||
config.ls.local_var_color = :bright_red
|
||||
config.ls.instance_var_color = :bright_blue
|
||||
config.ls.class_var_color = :blue
|
||||
config.ls.global_var_color = :bright_magenta
|
||||
config.ls.method_color = :green
|
||||
config.ls.instance_method_color = :bright_green
|
||||
config.ls.constant_color = :yellow
|
||||
config.ls ||= OpenStruct.new({
|
||||
:heading_color => :grey,
|
||||
|
||||
config.ls.separator = " "
|
||||
:public_method_color => :default,
|
||||
:private_method_color => :green,
|
||||
:protected_method_color => :yellow,
|
||||
:method_missing_color => :bright_red,
|
||||
|
||||
:local_var_color => :default,
|
||||
:pry_var_color => :red, # e.g. _, _pry_, _file_
|
||||
|
||||
:instance_var_color => :blue, # e.g. @foo
|
||||
:class_var_color => :bright_blue, # e.g. @@foo
|
||||
|
||||
:global_var_color => :default, # e.g. $CODERAY_DEBUG, $eventmachine_library
|
||||
:builtin_global_color => :cyan, # e.g. $stdin, $-w, $PID
|
||||
:pseudo_global_color => :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO
|
||||
|
||||
:constant_color => :default, # e.g. VERSION, ARGF
|
||||
:class_constant_color => :blue, # e.g. Object, Kernel
|
||||
:exception_constant_color => :magenta, # e.g. Exception, RuntimeError
|
||||
|
||||
# What should separate items listed by ls? (TODO: we should allow a columnar layout)
|
||||
:separator => " ",
|
||||
|
||||
# Any methods defined on these classes, or modules included into these classes, will not
|
||||
# be shown by ls unless the -v flag is used.
|
||||
# A user of Rails may wih to add ActiveRecord::Base to the list.
|
||||
# add the following to your .pryrc:
|
||||
# Pry.config.ls.ceiling << ActiveRecord::Base if defined? ActiveRecordBase
|
||||
:ceiling => [Object, Module, Class]
|
||||
})
|
||||
end
|
||||
|
||||
# Set all the configurable options back to their default values
|
||||
|
|
|
@ -129,13 +129,21 @@ class Pry
|
|||
private :initialize_special_locals
|
||||
|
||||
def inject_special_locals(target)
|
||||
inject_local("_in_", @input_array, target)
|
||||
inject_local("_out_", @output_array, target)
|
||||
inject_local("_pry_", self, target)
|
||||
inject_local("_ex_", self.last_exception, target)
|
||||
inject_local("_file_", self.last_file, target)
|
||||
inject_local("_dir_", self.last_dir, target)
|
||||
inject_local("_", self.last_result, target)
|
||||
special_locals.each_pair do |name, value|
|
||||
inject_local(name, value, target)
|
||||
end
|
||||
end
|
||||
|
||||
def special_locals
|
||||
{
|
||||
:_in_ => @input_array,
|
||||
:_out_ => @output_array,
|
||||
:_pry_ => self,
|
||||
:_ex_ => last_exception,
|
||||
:_file_ => last_file,
|
||||
:_dir_ => last_dir,
|
||||
:_ => last_result
|
||||
}
|
||||
end
|
||||
|
||||
# Initialize the repl session.
|
||||
|
|
|
@ -94,11 +94,14 @@ def redirect_pry_io(new_in, new_out = StringIO.new)
|
|||
end
|
||||
|
||||
def mock_pry(*args)
|
||||
|
||||
binding = args.first.is_a?(Binding) ? args.shift : binding()
|
||||
|
||||
input = InputTester.new(*args)
|
||||
output = StringIO.new
|
||||
|
||||
redirect_pry_io(input, output) do
|
||||
Pry.start
|
||||
binding.pry
|
||||
end
|
||||
|
||||
output.string
|
||||
|
|
|
@ -10,16 +10,6 @@ describe "Pry::DefaultCommands::Documentation" do
|
|||
|
||||
str_output.string.should =~ /sample doc/
|
||||
end
|
||||
|
||||
it 'should output multiple methods\' documentation' do
|
||||
str_output = StringIO.new
|
||||
redirect_pry_io(InputTester.new("show-doc sample_method another_sample_method", "exit-all"), str_output) do
|
||||
pry
|
||||
end
|
||||
|
||||
str_output.string.should =~ /sample doc/
|
||||
str_output.string.should =~ /another sample doc/
|
||||
end
|
||||
|
||||
it 'should output a method\'s documentation if inside method without needing to use method name' do
|
||||
$str_output = StringIO.new
|
||||
|
@ -37,5 +27,28 @@ describe "Pry::DefaultCommands::Documentation" do
|
|||
$str_output.string.should =~ /sample comment/
|
||||
$str_output = nil
|
||||
end
|
||||
|
||||
it "should be able to find super methods" do
|
||||
|
||||
c = Class.new{
|
||||
# classy initialize!
|
||||
def initialize(*args); end
|
||||
}
|
||||
|
||||
d = Class.new(c){
|
||||
# grungy initialize??
|
||||
def initialize(*args, &block); end
|
||||
}
|
||||
|
||||
o = d.new
|
||||
|
||||
# instancey initialize!
|
||||
def o.initialize; end
|
||||
|
||||
mock_pry(binding, "show-doc o.initialize").should =~ /instancey initialize/
|
||||
mock_pry(binding, "show-doc --super o.initialize").should =~ /grungy initialize/
|
||||
mock_pry(binding, "show-doc o.initialize -ss").should =~ /classy initialize/
|
||||
mock_pry(binding, "show-doc --super o.initialize -ss").should == mock_pry("show-doc Object#initialize")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -254,16 +254,6 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
str_output.string.should =~ /def sample/
|
||||
end
|
||||
|
||||
it 'should output multiple methods\' sources' do
|
||||
str_output = StringIO.new
|
||||
redirect_pry_io(InputTester.new("show-method sample_method another_sample_method", "exit-all"), str_output) do
|
||||
pry
|
||||
end
|
||||
|
||||
str_output.string.should =~ /def sample/
|
||||
str_output.string.should =~ /def another_sample/
|
||||
end
|
||||
|
||||
it 'should output a method\'s source with line numbers' do
|
||||
str_output = StringIO.new
|
||||
redirect_pry_io(InputTester.new("show-method -l sample_method", "exit-all"), str_output) do
|
||||
|
@ -312,6 +302,20 @@ describe "Pry::DefaultCommands::Introspection" do
|
|||
$str_output = nil
|
||||
end
|
||||
|
||||
it "should find methods even if there are spaces in the arguments" do
|
||||
o = Object.new
|
||||
def o.foo(*bars);
|
||||
"Mr flibble"
|
||||
self;
|
||||
end
|
||||
|
||||
str_output = StringIO.new
|
||||
redirect_pry_io(InputTester.new("show-method o.foo('bar', 'baz bam').foo", "exit-all"), str_output) do
|
||||
binding.pry
|
||||
end
|
||||
str_output.string.should =~ /Mr flibble/
|
||||
end
|
||||
|
||||
# dynamically defined method source retrieval is only supported in
|
||||
# 1.9 - where Method#source_location is native
|
||||
if RUBY_VERSION =~ /1.9/
|
||||
|
|
142
test/test_default_commands/test_ls.rb
Normal file
142
test/test_default_commands/test_ls.rb
Normal file
|
@ -0,0 +1,142 @@
|
|||
require 'helper'
|
||||
describe "ls" do
|
||||
describe "class_name" do
|
||||
extend Pry::DefaultCommands::Ls.helper_module
|
||||
it "should use the name of the class if it exists" do
|
||||
class_name(Object).should == "Object"
|
||||
end
|
||||
|
||||
it "should use the name of the module if it exists" do
|
||||
class_name(Kernel).should == "Kernel"
|
||||
end
|
||||
|
||||
it "should use Foo.self for singleton classes of classes" do
|
||||
class_name(class << Object; self; end).should == "Object.self"
|
||||
end
|
||||
|
||||
it "should use Foo.self for singleton methods of modules" do
|
||||
class_name(class << Kernel; self; end).should == "Kernel.self"
|
||||
end
|
||||
|
||||
it "should default to the .to_s if that's not possible" do
|
||||
cls = Class.new
|
||||
class_name(cls).should == cls.to_s
|
||||
end
|
||||
|
||||
it "should default to .to_s.self" do
|
||||
cls = Class.new
|
||||
class_name(class << cls; self; end).should == "#{cls.to_s}.self"
|
||||
end
|
||||
|
||||
it "should just be self for singleton classes of normal objects" do
|
||||
class_name(class << "hi"; self; end).should == "self"
|
||||
end
|
||||
end
|
||||
|
||||
describe "below ceiling" do
|
||||
it "should stop before Object by default" do
|
||||
mock_pry("cd Class.new{ def goo; end }.new", "ls").should.not =~ /Object/
|
||||
mock_pry("cd Class.new{ def goo; end }", "ls -M").should.not =~ /Object/
|
||||
end
|
||||
|
||||
it "should include object if -v is given" do
|
||||
mock_pry("cd Class.new{ def goo; end }.new", "ls -m -v").should =~ /Object/
|
||||
mock_pry("cd Class.new{ def goo; end }", "ls -vM").should =~ /Object/
|
||||
end
|
||||
|
||||
it "should include super-classes by default" do
|
||||
mock_pry("cd Class.new(Class.new{ def goo; end }).new", "ls").should =~ /goo/
|
||||
mock_pry("cd Class.new(Class.new{ def goo; end })", "ls -M").should =~ /goo/
|
||||
end
|
||||
|
||||
it "should not include super-classes when -q is given" do
|
||||
mock_pry("cd Class.new(Class.new{ def goo; end }).new", "ls -q").should.not =~ /goo/
|
||||
mock_pry("cd Class.new(Class.new{ def goo; end })", "ls -M -q").should.not =~ /goo/
|
||||
end
|
||||
end
|
||||
|
||||
describe "methods" do
|
||||
it "should show public methods by default" do
|
||||
mock_pry("ls Class.new{ def goo; end }.new").should =~ /goo/
|
||||
end
|
||||
|
||||
it "should not show protected/private by default" do
|
||||
mock_pry("ls -M Class.new{ def goo; end; private :goo }").should.not =~ /goo/
|
||||
mock_pry("ls Class.new{ def goo; end; protected :goo }.new").should.not =~ /goo/
|
||||
end
|
||||
|
||||
it "should show public methods with -p" do
|
||||
mock_pry("ls -p Class.new{ def goo; end }.new").should =~ /goo/
|
||||
end
|
||||
|
||||
it "should show protected/private methods with -p" do
|
||||
mock_pry("ls -pM Class.new{ def goo; end; protected :goo }").should =~ /goo/
|
||||
mock_pry("ls -p Class.new{ def goo; end; private :goo }.new").should =~ /goo/
|
||||
end
|
||||
end
|
||||
|
||||
describe "constants" do
|
||||
it "should show constants defined on the current module" do
|
||||
mock_pry("class TempFoo1; BARGHL = 1; end", "ls TempFoo1").should =~ /BARGHL/
|
||||
end
|
||||
|
||||
it "should not show constants defined on parent modules by default" do
|
||||
mock_pry("class TempFoo2; LHGRAB = 1; end; class TempFoo3 < TempFoo2; BARGHL = 1; end", "ls TempFoo3").should.not =~ /LHGRAB/
|
||||
end
|
||||
|
||||
it "should show constants defined on ancestors with -v" do
|
||||
mock_pry("class TempFoo4; LHGRAB = 1; end; class TempFoo5 < TempFoo4; BARGHL = 1; end", "ls -v TempFoo5").should =~ /LHGRAB/
|
||||
end
|
||||
|
||||
it "should not autoload constants!" do
|
||||
autoload :McflurgleTheThird, "/tmp/this-file-d000esnat-exist.rb"
|
||||
lambda{ mock_pry("ls -c") }.should.not.raise
|
||||
end
|
||||
end
|
||||
|
||||
describe "grep" do
|
||||
it "should reduce the number of outputted things" do
|
||||
mock_pry("ls -c").should =~ /ArgumentError/
|
||||
mock_pry("ls -c --grep Run").should.not =~ /ArgumentError/
|
||||
end
|
||||
it "should still output matching things" do
|
||||
mock_pry("ls -c --grep Run").should =~ /RuntimeError/
|
||||
end
|
||||
end
|
||||
|
||||
describe "when no arguments given" do
|
||||
describe "when at the top-level" do
|
||||
# rubinius has a bug that means local_variables of "main" aren't reported inside eval()
|
||||
unless defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/
|
||||
it "should show local variables" do
|
||||
mock_pry("ls").should =~ /_pry_/
|
||||
mock_pry("arbitrar = 1", "ls").should =~ /arbitrar/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "when in a class" do
|
||||
it "should show constants" do
|
||||
mock_pry("class GeFromulate1; FOOTIFICATE=1.3; end", "cd GeFromulate1", "ls").should =~ /FOOTIFICATE/
|
||||
end
|
||||
|
||||
it "should show class variables" do
|
||||
mock_pry("class GeFromulate2; @@flurb=1.3; end", "cd GeFromulate2", "ls").should =~ /@@flurb/
|
||||
end
|
||||
|
||||
it "should show methods" do
|
||||
mock_pry("class GeFromulate3; def self.mooflight; end ; end", "cd GeFromulate3", "ls").should =~ /mooflight/
|
||||
end
|
||||
end
|
||||
|
||||
describe "when in an object" do
|
||||
it "should show methods" do
|
||||
mock_pry("cd Class.new", "ls").should =~ /allocate/
|
||||
end
|
||||
|
||||
it "should show instance variables" do
|
||||
mock_pry("cd Class.new", "@alphooent = 1", "ls").should =~ /@alphooent/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,11 @@
|
|||
require 'helper'
|
||||
|
||||
describe Pry::Method do
|
||||
it "should use String names for compatibility" do
|
||||
klass = Class.new { def hello; end }
|
||||
Pry::Method.new(klass.instance_method(:hello)).name.should == "hello"
|
||||
end
|
||||
|
||||
describe ".from_str" do
|
||||
it 'should look up instance methods if no methods available and no options provided' do
|
||||
klass = Class.new { def hello; end }
|
||||
|
@ -59,13 +64,227 @@ describe Pry::Method do
|
|||
it 'should look up methods using klass.new.method syntax' do
|
||||
klass = Class.new { def hello; :hello; end }
|
||||
meth = Pry::Method.from_str("klass.new.hello", Pry.binding_for(binding))
|
||||
meth.name.to_sym.should == :hello
|
||||
meth.name.should == "hello"
|
||||
end
|
||||
|
||||
it 'should look up instance methods using klass.meth#method syntax' do
|
||||
klass = Class.new { def self.meth; Class.new; end }
|
||||
meth = Pry::Method.from_str("klass.meth#initialize", Pry.binding_for(binding))
|
||||
meth.name.to_sym.should == :initialize
|
||||
meth.name.should == "initialize"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'all_from_class' do
|
||||
def should_find_method(name)
|
||||
Pry::Method.all_from_class(@class).map(&:name).should.include(name)
|
||||
end
|
||||
|
||||
it 'should be able to find public instance methods defined in a class' do
|
||||
@class = Class.new{ def meth; 1; end }
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it 'should be able to find private and protected instance methods defined in a class' do
|
||||
@class = Class.new { protected; def prot; 1; end; private; def priv; 1; end }
|
||||
should_find_method('priv')
|
||||
should_find_method('prot')
|
||||
end
|
||||
|
||||
it 'should find methods all the way up to Kernel' do
|
||||
@class = Class.new
|
||||
should_find_method('exit!')
|
||||
end
|
||||
|
||||
it 'should be able to find instance methods defined in a super-class' do
|
||||
@class = Class.new(Class.new{ def meth; 1; end }) {}
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it 'should be able to find instance methods defined in modules included into this class' do
|
||||
@class = Class.new{ include Module.new{ def meth; 1; end; } }
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it 'should be able to find instance methods defined in modules included into super-classes' do
|
||||
@class = Class.new(Class.new{ include Module.new{ def meth; 1; end; } })
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it 'should attribute overridden methods to the sub-class' do
|
||||
@class = Class.new(Class.new{ include Module.new{ def meth; 1; end; } }) { def meth; 2; end }
|
||||
Pry::Method.all_from_class(@class).detect{ |x| x.name == 'meth' }.owner.should == @class
|
||||
end
|
||||
|
||||
it 'should be able to find methods defined on a singleton class' do
|
||||
@class = (class << Object.new; def meth; 1; end; self; end)
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it 'should be able to find methods on super-classes when given a singleton class' do
|
||||
@class = (class << Class.new{ def meth; 1; end}.new; self; end)
|
||||
should_find_method('meth')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'all_from_obj' do
|
||||
describe 'on normal objects' do
|
||||
def should_find_method(name)
|
||||
Pry::Method.all_from_obj(@obj).map(&:name).should.include(name)
|
||||
end
|
||||
|
||||
it "should find methods defined in the object's class" do
|
||||
@obj = Class.new{ def meth; 1; end }.new
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should find methods defined in modules included into the object's class" do
|
||||
@obj = Class.new{ include Module.new{ def meth; 1; end } }.new
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should find methods defined in the object's singleton class" do
|
||||
@obj = Object.new
|
||||
class << @obj; def meth; 1; end; end
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should find methods in modules included into the object's singleton class" do
|
||||
@obj = Object.new
|
||||
@obj.extend Module.new{ def meth; 1; end }
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should find methods all the way up to Kernel" do
|
||||
@obj = Object.new
|
||||
should_find_method('exit!')
|
||||
end
|
||||
|
||||
it "should not find methods defined on the classes singleton class" do
|
||||
@obj = Class.new{ class << self; def meth; 1; end; end }.new
|
||||
Pry::Method.all_from_obj(@obj).map(&:name).should.not.include('meth')
|
||||
end
|
||||
|
||||
it "should work in the face of an overridden send" do
|
||||
@obj = Class.new{ def meth; 1; end; def send; raise EOFError; end }.new
|
||||
should_find_method('meth')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'on classes' do
|
||||
def should_find_method(name)
|
||||
Pry::Method.all_from_obj(@class).map(&:name).should.include(name)
|
||||
end
|
||||
|
||||
it "should find methods defined in the class' singleton class" do
|
||||
@class = Class.new{ class << self; def meth; 1; end; end }
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should find methods defined on modules extended into the class" do
|
||||
@class = Class.new{ extend Module.new{ def meth; 1; end; } }
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should find methods defined on the singleton class of super-classes" do
|
||||
@class = Class.new(Class.new{ class << self; def meth; 1; end; end })
|
||||
should_find_method('meth')
|
||||
end
|
||||
|
||||
it "should not find methods defined within the class" do
|
||||
@class = Class.new{ def meth; 1; end }
|
||||
Pry::Method.all_from_obj(@obj).map(&:name).should.not.include('meth')
|
||||
end
|
||||
|
||||
it "should find methods defined on Class" do
|
||||
@class = Class.new
|
||||
should_find_method('allocate')
|
||||
end
|
||||
|
||||
it "should find methods defined on Kernel" do
|
||||
@class = Class.new
|
||||
should_find_method('exit!')
|
||||
end
|
||||
|
||||
it "should attribute overridden methods to the sub-class' singleton class" do
|
||||
@class = Class.new(Class.new{ class << self; def meth; 1; end; end }) { class << self; def meth; 1; end; end }
|
||||
Pry::Method.all_from_obj(@class).detect{ |x| x.name == 'meth' }.owner.should == (class << @class; self; end)
|
||||
end
|
||||
|
||||
it "should attrbute overridden methods to the class not the module" do
|
||||
@class = Class.new { class << self; def meth; 1; end; end; extend Module.new{ def meth; 1; end; } }
|
||||
Pry::Method.all_from_obj(@class).detect{ |x| x.name == 'meth' }.owner.should == (class << @class; self; end)
|
||||
end
|
||||
|
||||
it "should attribute overridden methods to the relevant singleton class in preference to Class" do
|
||||
@class = Class.new { class << self; def allocate; 1; end; end }
|
||||
Pry::Method.all_from_obj(@class).detect{ |x| x.name == 'allocate' }.owner.should == (class << @class; self; end)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'method resolution order' do
|
||||
module LS
|
||||
class Top; end
|
||||
|
||||
class Next < Top; end
|
||||
|
||||
module M; end
|
||||
module N; include M; end
|
||||
module O; include M; end
|
||||
module P; end
|
||||
|
||||
class Low < Next; include N; include P; end
|
||||
class Lower < Low; extend N; end
|
||||
class Bottom < Lower; extend O; end
|
||||
end
|
||||
|
||||
def singleton_class(obj); class << obj; self; end; end
|
||||
|
||||
it "should look at a class and then its superclass" do
|
||||
Pry::Method.instance_resolution_order(LS::Next).should == [LS::Next] + Pry::Method.instance_resolution_order(LS::Top)
|
||||
end
|
||||
|
||||
it "should include the included modules between a class and its superclass" do
|
||||
Pry::Method.instance_resolution_order(LS::Low).should == [LS::Low, LS::P, LS::N, LS::M] + Pry::Method.instance_resolution_order(LS::Next)
|
||||
end
|
||||
|
||||
it "should not include modules extended into the class" do
|
||||
Pry::Method.instance_resolution_order(LS::Bottom).should == [LS::Bottom] + Pry::Method.instance_resolution_order(LS::Lower)
|
||||
end
|
||||
|
||||
it "should include included modules for Modules" do
|
||||
Pry::Method.instance_resolution_order(LS::O).should == [LS::O, LS::M]
|
||||
end
|
||||
|
||||
it "should include the singleton class of objects" do
|
||||
obj = LS::Low.new
|
||||
Pry::Method.resolution_order(obj).should == [singleton_class(obj)] + Pry::Method.instance_resolution_order(LS::Low)
|
||||
end
|
||||
|
||||
it "should not include singleton classes of numbers" do
|
||||
Pry::Method.resolution_order(4).should == Pry::Method.instance_resolution_order(Fixnum)
|
||||
end
|
||||
|
||||
it "should include singleton classes for classes" do
|
||||
Pry::Method.resolution_order(LS::Low).should == [singleton_class(LS::Low)] + Pry::Method.resolution_order(LS::Next)
|
||||
end
|
||||
|
||||
it "should include modules included into singleton classes" do
|
||||
Pry::Method.resolution_order(LS::Lower).should == [singleton_class(LS::Lower), LS::N, LS::M] + Pry::Method.resolution_order(LS::Low)
|
||||
end
|
||||
|
||||
it "should include modules at most once" do
|
||||
Pry::Method.resolution_order(LS::Bottom).count(LS::M).should == 1
|
||||
end
|
||||
|
||||
it "should include modules at the point which they would be reached" do
|
||||
Pry::Method.resolution_order(LS::Bottom).should == [singleton_class(LS::Bottom), LS::O] + (Pry::Method.resolution_order(LS::Lower))
|
||||
end
|
||||
|
||||
it "should include the Pry::Method.instance_resolution_order of Class after the singleton classes" do
|
||||
Pry::Method.resolution_order(LS::Top).should ==
|
||||
[singleton_class(LS::Top), singleton_class(Object), (defined? BasicObject) && singleton_class(BasicObject)].compact +
|
||||
Pry::Method.instance_resolution_order(Class)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue