1
0
Fork 0
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:
Conrad Irwin 2011-10-15 01:51:52 -07:00
commit 231732616e
19 changed files with 881 additions and 357 deletions

View file

@ -1,5 +1,6 @@
# (C) John Mair (banisterfiend) 2011
# MIT License
#
require 'pp'
require 'pry/helpers/base_helpers'

View file

@ -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("")

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -1,3 +1,4 @@
require "pry/helpers/base_helpers"
require "pry/helpers/options_helpers"
require "pry/helpers/command_helpers"
require "pry/helpers/text"

View file

@ -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)

View 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

View file

@ -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]

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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/

View 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

View file

@ -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