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

bunch of random refactorings to different commands

This commit is contained in:
John Mair 2013-01-02 02:06:51 +01:00
parent dcdea09e9e
commit 4d384737a7
4 changed files with 94 additions and 43 deletions

View file

@ -67,12 +67,12 @@ class Pry
# lookup variables and constants that are not modules
def default_lookup
if str !~ /\S#\S/ && target.eval("defined? #{str} ") =~ /variable|constant/
if variable_or_constant?(str)
obj = target.eval(str)
# restrict to only objects we KNOW for sure support the full API
# Do NOT support just any object that responds to source_location
if [::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) }
if sourcable_object?(obj)
Pry::Method(obj)
elsif !obj.is_a?(Module)
Pry::WrappedModule(obj.class)
@ -98,17 +98,39 @@ class Pry
Pry::Method.from_str(str,target) || Pry::WrappedModule.from_str(str, target)
end
sup = obj.super(super_level) if obj
if obj && !sup
lookup_super(obj, super_level)
end
private
def sourcable_object?(obj)
[::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) }
end
# Whether `str` represents a variable (or constant) when looked up
# in the context of the `target` binding. This is used to
# distinguish it from methods or expressions.
# @param [String] str The string to lookup
def variable_or_constant?(str)
str !~ /\S#\S/ && target.eval("defined? #{str} ") =~ /variable|constant/
end
def target_self
target.eval('self')
end
# grab the nth (`super_level`) super of `obj
# @param [Object] obj
# @param [Fixnum] super_level How far up the super chain to ascend.
def lookup_super(obj, super_level)
return nil if !obj
sup = obj.super(super_level)
if !sup
raise Pry::CommandError, "No superclass found for #{obj.wrapped}"
else
sup
end
end
private
def target_self
target.eval('self')
end
end
end

View file

@ -1,36 +1,37 @@
require 'forwardable'
class Pry
class Command::Edit
class MethodPatcher
attr_accessor :method_object
attr_accessor :target
attr_accessor :_pry_
extend Forwardable
def_delegators :@edit_context, :code_object, :target, :_pry_
def initialize(edit_context)
@method_object = edit_context.code_object
@target = edit_context.target
@_pry_ = edit_context._pry_
@edit_context = edit_context
end
# perform the patch
def perform_patch
source = wrap_for_nesting(wrap_for_owner(Pry::Editor.edit_tempfile_with_content(adjusted_lines)))
if method_object.alias?
if code_object.alias?
with_method_transaction do
_pry_.evaluate_ruby source
Pry.binding_for(method_object.owner).eval("alias #{method_object.name} #{method_object.original_name}")
_pry_.evaluate_ruby patched_code
end
else
_pry_.evaluate_ruby source
_pry_.evaluate_ruby patched_code
end
end
private
def patched_code
@patched_code ||= wrap(Pry::Editor.edit_tempfile_with_content(adjusted_lines))
end
# The method code adjusted so that the first line is rewritten
# so that def self.foo --> def foo
def adjusted_lines
lines = method_object.source.lines.to_a
lines = code_object.source.lines.to_a
lines[0] = definition_line_for_owner(lines.first)
lines
end
@ -45,12 +46,13 @@ class Pry
# @param [String] meth_name The method name before aliasing
# @param [Module] target The owner of the method
def with_method_transaction
target = Pry.binding_for(method_object.owner)
temp_name = "__pry_#{method_object.original_name}__"
target = owner_binding
temp_name = "__pry_#{code_object.original_name}__"
target.eval("alias #{temp_name} #{method_object.original_name}")
target.eval("alias #{temp_name} #{code_object.original_name}")
yield
target.eval("alias #{method_object.original_name} #{temp_name}")
target.eval("alias #{code_object.name} #{code_object.original_name}")
target.eval("alias #{code_object.original_name} #{temp_name}")
ensure
target.eval("undef #{temp_name}") rescue nil
end
@ -68,13 +70,26 @@ class Pry
# @param String The original definition line. e.g. def self.foo(bar, baz=1)
# @return String The new definition line. e.g. def foo(bar, baz=1)
def definition_line_for_owner(line)
if line =~ /^def (?:.*?\.)?#{Regexp.escape(method_object.original_name)}(?=[\(\s;]|$)/
"def #{method_object.original_name}#{$'}"
if line =~ /^def (?:.*?\.)?#{Regexp.escape(code_object.original_name)}(?=[\(\s;]|$)/
"def #{code_object.original_name}#{$'}"
else
raise CommandError, "Could not find original `def #{method_object.original_name}` line to patch."
raise CommandError, "Could not find original `def #{code_object.original_name}` line to patch."
end
end
# Provide a binding for the `code_object`'s owner context.
# @return [Binding]
def owner_binding
Pry.binding_for(code_object.owner)
end
# Apply wrap_for_owner and wrap_for_nesting successively to `source`
# @param [String] source
# @return [String] The wrapped source.
def wrap(source)
wrap_for_nesting(wrap_for_owner(source))
end
# Update the source code so that when it has the right owner when eval'd.
#
# This (combined with definition_line_for_owner) is backup for the case that
@ -84,7 +99,7 @@ class Pry
# @param [String] source The source to wrap
# @return [String]
def wrap_for_owner(source)
Thread.current[:__pry_owner__] = method_object.owner
Thread.current[:__pry_owner__] = code_object.owner
source = "Thread.current[:__pry_owner__].class_eval do\n#{source}\nend"
end
@ -101,7 +116,7 @@ class Pry
# @param [String] source The source to wrap.
# @return [String]
def wrap_for_nesting(source)
nesting = Pry::Code.from_file(method_object.source_file).nesting_at(method_object.source_line)
nesting = Pry::Code.from_file(code_object.source_file).nesting_at(code_object.source_line)
(nesting + [source] + nesting.map{ "end" } + [""]).join("\n")
rescue Pry::Indent::UnparseableNestingError => e

View file

@ -44,19 +44,11 @@ class Pry
"#{@file} @ line #{@line} #{@method && @method.name_with_owner}"
end
def nothing_to_do?
opts.quiet? && (internal_binding?(target) || !code?)
end
def process
if nothing_to_do?
return
elsif internal_binding?(target)
if target_self == TOPLEVEL_BINDING.eval("self")
output.puts "At the top level."
else
output.puts "Inside #{Pry.view_clip(target_self)}."
end
handle_internal_binding
return
end
@ -69,6 +61,22 @@ class Pry
private
def nothing_to_do?
opts.quiet? && (internal_binding?(target) || !code?)
end
def top_level?
target_self == TOPLEVEL_BINDING.eval("self")
end
def handle_internal_binding
if top_level?
output.puts "At the top level."
else
output.puts "Inside #{Pry.view_clip(target_self)}."
end
end
def show_method?
args.empty? && @method && @method.source? && @method.source_range.count < 20 &&
# These checks are needed in case of an eval with a binding and file/line

View file

@ -32,10 +32,16 @@ class Pry
if opts.verbose?
output.puts Pry::Code.new(_pry_.last_exception.backtrace, 0, :text).with_line_numbers.to_s
else
output.puts Pry::Code.new(_pry_.last_exception.backtrace.first([captures[0].size, 0.5].max * 10), 0, :text).with_line_numbers.to_s
end
output.puts Pry::Code.new(_pry_.last_exception.backtrace.first(size_of_backtrace), 0, :text).with_line_numbers.to_s
end
end
Pry::Commands.add_command(Pry::Command::Wtf)
private
def size_of_backtrace
[captures[0].size, 0.5].max * 10
end
end
Pry::Commands.add_command(Pry::Command::Wtf)
end