Skip to content

Commit

Permalink
Added input_type method to determine where to dispatch show-source.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
redgetan committed Jul 18, 2012
1 parent 98e32b1 commit 80bdcd4
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 59 deletions.
3 changes: 1 addition & 2 deletions lib/pry.rb
Expand Up @@ -144,8 +144,7 @@ def self.===(exception)
# 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
Expand Down
2 changes: 1 addition & 1 deletion lib/pry/default_commands/editing.rb
Expand Up @@ -189,7 +189,7 @@ def process

begin
@method = method_object
rescue NonMethodContextError => err
rescue MethodNotFound => err
end

if opts.present?(:patch) || (@method && @method.dynamically_defined?)
Expand Down
119 changes: 69 additions & 50 deletions lib/pry/default_commands/introspection.rb
Expand Up @@ -7,49 +7,70 @@ module DefaultCommands
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)
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
# @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
wrapped
end

def get_module_from_internal_binding
mod = target_self.is_a?(Module) ? target_self : target_self.class
end
def process(name)
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 '#{input}' could not be found or derived", false)
end

def proc?(name)
target.eval(name).is_a? Proc
rescue TypeError, NameError
false
render_output(code_or_doc, opts)
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
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 '#{name}' could not be found or derived", false)
command_error("method or module for '' could not be derived", false)
end
end

render_output(code_or_doc, opts)
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)
Expand Down Expand Up @@ -256,6 +277,22 @@ def options(opt)
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

Expand Down Expand Up @@ -313,24 +350,6 @@ def all_modules
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
Expand Down
6 changes: 3 additions & 3 deletions lib/pry/helpers/command_helpers.rb
Expand Up @@ -49,19 +49,19 @@ def get_method_or_raise(name, target, opts={}, omit_help=false)
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)
Expand Down
1 change: 1 addition & 0 deletions test/helper.rb
Expand Up @@ -126,6 +126,7 @@ def redirect_pry_io(new_in, new_out = StringIO.new)

def mock_pry(*args)

args.flatten!
binding = args.first.is_a?(Binding) ? args.shift : binding()

input = InputTester.new(*args)
Expand Down
42 changes: 39 additions & 3 deletions test/test_default_commands/test_show_source.rb
Expand Up @@ -149,7 +149,7 @@ def o.foo(*bars)
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
Expand Down Expand Up @@ -194,7 +194,7 @@ def @o.foo(*bars); end
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
Expand All @@ -206,11 +206,47 @@ def @o.foo(*bars); end
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
Expand Down

0 comments on commit 80bdcd4

Please sign in to comment.