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

edit command: add --method option

* --method doesn't accept any parameters, it only edits the 'current' method associated
with the binding. It's equivalent to 'edit-method' (with no args).
* This option is necessary as 'edit' by itself edits the last expression.
* Note that --method doesn't just edit the current method, if no method exists (i.e binding.pry was not put
inside a method context) then it edits the current OBJECT instead.

TODO:
* explicitly restrict --method to *just* methods and not allow it to fall back to objects?
* allow --method to take a parameter that must be a method object? e.g --method Pry#repl works
but --method Pry would fail ?
This commit is contained in:
John Mair 2013-01-11 20:38:12 +01:00
parent 7ae55b3a8a
commit 503e6bb63f
2 changed files with 105 additions and 28 deletions

View file

@ -1,11 +1,4 @@
class Pry
# Uses the following state variables:
# - dynamical_ex_file [Array<String>]
# Utilised in `edit --ex --patch` operations. Contains the source code
# of a monkey patched file, in which an exception was raised. We store
# the entire source code because an exception may happen anywhere in the
# code and there is no way to predict that. So we simply superimpose
# everything (admittedly, doing extra job).
class Command::Edit < Pry::ClassCommand
require 'pry/commands/edit/method_patcher'
require 'pry/commands/edit/exception_patcher'
@ -24,6 +17,7 @@ class Pry
edit sample.rb
edit sample.rb --line 105
edit MyClass#my_method
edit --method
edit -p MyClass#my_method
edit YourClass
edit --ex`
@ -43,11 +37,12 @@ class Pry
opt.on :c, :current, "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)"
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
opt.on :p, :patch, "Instead of editing the object's file, try to edit in a tempfile and apply as a monkey patch"
opt.on :m, :method, "Explicitly edit the _current_ method (when inside a method context)."
end
def process
if bad_option_combination?
raise CommandError, "Only one of --ex, --temp, --in and FILE may be specified."
raise CommandError, "Only one of --ex, --temp, --in, --method and FILE may be specified."
end
if repl_edit?
@ -62,6 +57,11 @@ class Pry
end
end
def repl_edit?
!opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) &&
filename_argument.empty?
end
def repl_edit
content = Pry::Editor.edit_tempfile_with_content(initial_temp_file_content,
initial_temp_file_content.lines.count)
@ -72,10 +72,13 @@ class Pry
end
end
def runtime_patch?
opts.present?(:patch) || pry_method?(code_object)
end
def apply_runtime_patch
if patch_exception?
ex_file_and_line = FileAndLineLocator.from_exception(_pry_.last_exception, opts[:ex].to_i)
ExceptionPatcher.new(_pry_, state, ex_file_and_line).perform_patch
ExceptionPatcher.new(_pry_, state, file_and_line_for_current_exception).perform_patch
else
if code_object.is_a?(Pry::Method)
MethodPatcher.new(_pry_, code_object).perform_patch
@ -86,20 +89,24 @@ class Pry
end
def ensure_file_name_is_valid(file_name)
raise CommandError, "Cannot find a valid file for #{args.first}" if !file_name
raise CommandError, "Cannot find a valid file for #{filename_argument}" if !file_name
raise CommandError, "#{file_name} is not a valid file name, cannot edit!" if not_a_real_file?(file_name)
end
def file_and_line_for_current_exception
FileAndLineLocator.from_exception(_pry_.last_exception, opts[:ex].to_i)
end
def file_and_line
file_name, line = if opts.present?(:current)
FileAndLineLocator.from_binding(target)
elsif opts.present?(:ex)
FileAndLineLocator.from_exception(_pry_.last_exception, opts[:ex].to_i)
file_and_line_for_current_exception
elsif code_object
FileAndLineLocator.from_code_object(code_object, args.first)
FileAndLineLocator.from_code_object(code_object, filename_argument)
else
# when file and line are passed as a single arg, e.g my_file.rb:30
FileAndLineLocator.from_filename_argument(args.first)
FileAndLineLocator.from_filename_argument(filename_argument)
end
[file_name, opts.present?(:line) ? opts[:l].to_i : line]
@ -107,6 +114,7 @@ class Pry
def file_edit
file_name, line = file_and_line
ensure_file_name_is_valid(file_name)
# Sanitize blanks.
@ -122,22 +130,18 @@ class Pry
end
end
def filename_argument
args.first || ""
end
def code_object
@code_object ||= args.first && !probably_a_file?(args.first) &&
Pry::CodeObject.lookup(args.first, target, _pry_)
@code_object ||= !probably_a_file?(filename_argument) &&
Pry::CodeObject.lookup(filename_argument, _pry_)
end
def repl_edit?
!opts.present?(:ex) && !opts.present?(:current) && args.empty?
end
def runtime_patch?
opts.present?(:patch) || dynamically_defined_method?
end
def dynamically_defined_method?
def pry_method?(code_object)
code_object.is_a?(Pry::Method) &&
code_object.dynamically_defined?
code_object.pry_method?
end
def patch_exception?
@ -146,7 +150,7 @@ class Pry
def bad_option_combination?
[opts.present?(:ex), opts.present?(:temp),
opts.present?(:in), !args.empty?].count(true) > 1
opts.present?(:in), opts.present?(:method), !filename_argument.empty?].count(true) > 1
end
def input_expression

View file

@ -307,7 +307,7 @@ describe "edit" do
proc {
pry_eval 'edit ruby.rb -i'
}.should.raise(Pry::CommandError).
message.should =~ /Only one of --ex, --temp, --in and FILE/
message.should =~ /Only one of --ex, --temp, --in, --method and FILE/
end
it "should not work with nonsense" do
@ -623,4 +623,77 @@ describe "edit" do
end
end
end
describe "--method flag" do
before do
@t = pry_tester
class BinkyWink
eval %{
def tits_macgee
binding
end
}
def tots_macgee
:jeremy_jones
binding
end
end
end
after do
Object.remove_const(:BinkyWink)
end
it 'should edit method context' do
old_editor = Pry.editor
Pry.editor = lambda do |file, line|
[file, line].should == BinkyWink.instance_method(:tots_macgee).source_location
nil
end
t = pry_tester(BinkyWink.new.tots_macgee)
t.process_command "edit -m -n"
Pry.editor = old_editor
end
it 'errors when cannot find method context' do
old_editor = Pry.editor
Pry.editor = lambda do |file, line|
[file, line].should == BinkyWink.instance_method(:tits_macgee).source_location
nil
end
t = pry_tester(BinkyWink.new.tits_macgee)
lambda { t.process_command "edit -m -n" }.should.
raise(Pry::CommandError).message.should.match(/Cannot find a file for/)
Pry.editor = old_editor
end
it 'errors when a filename arg is passed with --method' do
lambda { @t.process_command "edit -m Pry#repl" }.should.
raise(Pry::CommandError).message.should.match(/Only one of/)
end
end
describe "pretty error messages" do
before do
@t = pry_tester
class TrinkyDink
eval %{
def claudia_linklater
end
}
end
end
after do
Object.remove_const(:TrinkyDink)
end
it 'should display a nice error message when cannot open a file' do
lambda { @t.process_command "edit TrinkyDink#claudia_linklater" }.should.
raise(Pry::CommandError).message.should.match(/Cannot find a file for/)
end
end
end