1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Added input_type method to determine where to dispatch show-source.

Enabled show-source to also work for any object that responds to source_location
Local variables now takes precedence over methods that they shadow for show-source
This commit is contained in:
Reginald Tan 2012-07-17 21:55:18 -04:00
parent 98e32b1e11
commit 80bdcd44b0
6 changed files with 117 additions and 62 deletions

View file

@ -144,8 +144,7 @@ class Pry
# CommandErrors are caught by the REPL loop and displayed to the user. They
# indicate an exceptional condition that's fatal to the current command.
class CommandError < StandardError; end
class NonMethodContextError < CommandError; end
class NonMethodError < CommandError; end
class MethodNotFound < CommandError; end
# indicates obsolete API
class ObsoleteError < StandardError; end

View file

@ -189,7 +189,7 @@ class Pry
begin
@method = method_object
rescue NonMethodContextError => err
rescue MethodNotFound => err
end
if opts.present?(:patch) || (@method && @method.dynamically_defined?)

View file

@ -7,51 +7,72 @@ class Pry
module ModuleIntrospectionHelpers
attr_accessor :module_object
# is our target binding in a method?
def method?
!!method_object
rescue NonMethodError,NonMethodContextError
false
def module_object
name = args.first
@module_object ||= WrappedModule.from_str(name, target)
end
def module?(name)
self.module_object = get_module(name)
# @param [String]
# @param [Binding] target The binding context of the input.
# @return [Symbol] type of input
def input_type(input,target)
if input == ""
:blank
elsif target.eval("defined? #{input} ") =~ /variable/ &&
target.eval(input).respond_to?(:source_location)
:sourcable_object
elsif Pry::Method.from_str(input,target)
:method
elsif Pry::WrappedModule.from_str(input, target)
:module
else
:unknown
end
def get_module(name)
wrapped = Pry::WrappedModule.from_str(name, target)
if !wrapped
if args.empty? && internal_binding?(target)
wrapped = Pry::WrappedModule(get_module_from_internal_binding)
end
end
wrapped
end
def get_module_from_internal_binding
mod = target_self.is_a?(Module) ? target_self : target_self.class
end
def proc?(name)
target.eval(name).is_a? Proc
rescue TypeError, NameError
false
end
def process(name)
if module?(name)
code_or_doc = process_module
elsif method?
code_or_doc = process_method
elsif proc?(name)
code_or_doc = process_proc
input = args.join(" ")
type = input_type(input, target)
code_or_doc = case type
when :blank
process_blank
when :sourcable_object
process_sourcable_object
when :method
process_method
when :module
process_module
else
command_error("method or module for '#{name}' could not be found or derived", false)
command_error("method or module for '#{input}' could not be found or derived", false)
end
render_output(code_or_doc, opts)
end
def process_blank
if mod = extract_module_from_internal_binding
@module_object = mod
process_module
elsif meth = extract_method_from_binding
@method_object = meth
process_method
else
command_error("method or module for '' could not be derived", false)
end
end
def extract_module_from_internal_binding
if args.empty? && internal_binding?(target)
mod = target_self.is_a?(Module) ? target_self : target_self.class
Pry::WrappedModule(mod)
end
end
def extract_method_from_binding
Pry::Method.from_binding(target)
end
def module_start_line(mod, candidate_rank=0)
if opts.present?(:'base-one')
1
@ -256,6 +277,22 @@ class Pry
opt.on :a, :all, "Show source for all definitions and monkeypatches of the module/class"
end
def process_sourcable_object
name = args.first
object = target.eval(name)
file_name, line = object.source_location
source = Pry::Code.from_file(file_name).expression_at(line)
code = Pry::Code.new(source).with_line_numbers(use_line_numbers?).to_s
result = ""
result << "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line}:\n"
result << "#{Pry::Helpers::Text.bold('Number of lines:')} #{code.lines.count}\n\n"
result << code
result << "\n"
end
def process_method
raise CommandError, "Could not find method source" unless method_object.source
@ -313,24 +350,6 @@ class Pry
result
end
def process_proc
name = args.first
target_proc = target.eval(name)
file_name, line = target_proc.source_location
source = Pry::Code.from_file(file_name).expression_at(line)
code = Pry::Code.new(source).with_line_numbers(use_line_numbers?).to_s
#code = Pry::Code.new(target_proc.source, line).with_line_numbers(use_line_numbers?).to_s
result = ""
result << "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line}:\n"
result << "#{Pry::Helpers::Text.bold('Number of lines:')} #{code.lines.count}\n\n"
result << code
result << "\n"
end
def use_line_numbers?
opts.present?(:b) || opts.present?(:l)
end

View file

@ -49,19 +49,19 @@ class Pry
meth = Pry::Method.from_str(name, target, opts)
if name && !meth
command_error("The method '#{name}' could not be found.", omit_help, NonMethodError)
command_error("The method '#{name}' could not be found.", omit_help, MethodNotFound)
end
(opts[:super] || 0).times do
if meth.super
meth = meth.super
else
command_error("'#{meth.name_with_owner}' has no super method.", omit_help, NonMethodError)
command_error("'#{meth.name_with_owner}' has no super method.", omit_help, MethodNotFound)
end
end
if !meth || (!name && internal_binding?(target))
command_error("No method name given, and context is not a method.", omit_help, NonMethodContextError)
command_error("No method name given, and context is not a method.", omit_help, MethodNotFound)
end
set_file_and_dir_locals(meth.source_file)

View file

@ -126,6 +126,7 @@ end
def mock_pry(*args)
args.flatten!
binding = args.first.is_a?(Binding) ? args.shift : binding()
input = InputTester.new(*args)

View file

@ -149,7 +149,7 @@ if !mri18_and_no_real_source_location?
it "should not raise an exception when a non-extant super method is requested" do
def @o.foo(*bars); end
mock_pry(binding, "show-source --super @o.foo").should =~ /'@o.foo' could not be found/
mock_pry(binding, "show-source --super @o.foo").should =~ /'self.foo' has no super method/
end
# dynamically defined method source retrieval is only supported in
@ -194,7 +194,7 @@ if !mri18_and_no_real_source_location?
end
end
describe "on procs/lambdas" do
describe "on sourcable objects" do
if RUBY_VERSION =~ /1.9/
it "should output source defined inside pry" do
@ -206,11 +206,47 @@ if !mri18_and_no_real_source_location?
end
end
it "should output source" do
it "should output source for procs/lambdas" do
hello = proc { puts 'hello world!' }
mock_pry(binding, "show-source hello").should =~ /proc { puts 'hello world!' }/
end
it "should output source for method objects" do
def @o.hi; puts 'hi world'; end
meth = @o.method(:hi)
mock_pry(binding, "show-source meth").should =~ /puts 'hi world'/
end
describe "on variables that shadow methods" do
before do
@method_shadow = [
"class TestHost ",
"def hello",
"hello = proc { ' smile ' }",
"binding.pry",
"end",
"end",
"TestHost.new.hello"
]
end
after do
Object.remove_const(:TestHost)
end
it "source of variable should take precedence over method that is being shadowed" do
string = mock_pry(@method_shadow,"show-source hello","exit-all")
string.include?("def hello").should == false
string =~ /proc { ' smile ' }/
end
it "source of method being shadowed should take precedence over variable
if given self.meth_name syntax" do
string = mock_pry(@method_shadow,"show-source self.hello","exit-all")
string.include?("def hello").should == true
end
end
end
describe "on modules" do