From f19e6a9073f60287f5989837a33c67252961e690 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Fri, 9 Sep 2011 00:31:56 -0700 Subject: [PATCH] implement edit-method -p --- lib/pry/default_commands/introspection.rb | 35 +++++++++++++++++---- lib/pry/helpers/command_helpers.rb | 38 +++++++++++++---------- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/lib/pry/default_commands/introspection.rb b/lib/pry/default_commands/introspection.rb index 9456f2da..1f931d0a 100644 --- a/lib/pry/default_commands/introspection.rb +++ b/lib/pry/default_commands/introspection.rb @@ -203,6 +203,7 @@ class Pry 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 @@ -213,20 +214,42 @@ class Pry next if opts.help? + if !Pry.config.editor + output.puts "Error: No editor set!" + output.puts "Ensure that #{text.bold("Pry.config.editor")} is set to your editor of choice." + next + end + meth_name = args.shift - if (meth = get_method_object(meth_name, target, opts.to_hash(true))).nil? + meth_name, target, type = get_method_attributes(meth_name, target, opts.to_hash(true)) + meth = get_method_object_from_target(meth_name, target, type) + + if meth.nil? output.puts "Invalid method name: #{meth_name}." next end - next output.puts "Error: No editor set!\nEnsure that #{text.bold("Pry.config.editor")} is set to your editor of choice." if !Pry.config.editor + if opts.p? || is_a_dynamically_defined_method?(meth) + code, _ = code_and_code_type_for(meth) + + lines = code.lines.to_a + if lines[0] =~ /^def [^( \n]+/ + lines[0] = "def #{meth_name}#{$'}" + else + next output.puts "Error: Pry can only patch methods created with the `def` keyword." + end + + temp_file do |f| + f.puts lines.join + f.flush + invoke_editor(f.path, 0) + Pry.new(:input => StringIO.new(File.read(f.path))).rep(meth.owner) + end + next + end if is_a_c_method?(meth) output.puts "Error: Can't edit a C method." - elsif is_a_dynamically_defined_method?(meth) - output.puts "Error: Can't edit an eval method." - - # editor is invoked here else file, line = path_line_for(meth) set_file_and_dir_locals(file) diff --git a/lib/pry/helpers/command_helpers.rb b/lib/pry/helpers/command_helpers.rb index 5957c045..9dab5e2e 100644 --- a/lib/pry/helpers/command_helpers.rb +++ b/lib/pry/helpers/command_helpers.rb @@ -178,37 +178,43 @@ class Pry [doc, code_type] end - def get_method_object(meth_name, target, options) + def get_method_object(meth_name, target=nil, options={}) + get_method_object_from_target(*get_method_attributes(meth_name, target, options)) rescue nil + end + + def get_method_attributes(meth_name, target=nil, options={}) if meth_name if meth_name =~ /(\S+)\#(\S+)\Z/ context, meth_name = $1, $2 target = Pry.binding_for(target.eval(context)) - options["instance-methods"] = true - options[:methods] = false + type = :instance elsif meth_name =~ /(\S+)\.(\S+)\Z/ context, meth_name = $1, $2 target = Pry.binding_for(target.eval(context)) - options["instance-methods"] = false - options[:methods] = true + type = :singleton + elsif options["instance_methods"] + type = :instance + elsif options[:methods] + type = :singleton + else + type = nil end else meth_name = meth_name_from_binding(target) + type = nil end + [meth_name, target, type] + end - if !meth_name - return nil - end - - if options["instance-methods"] + def get_method_object_from_target(meth_name, target, type=nil) + case type + when :instance target.eval("instance_method(:#{meth_name})") rescue nil - elsif options[:methods] + when :singleton target.eval("method(:#{meth_name})") rescue nil else - begin - target.eval("instance_method(:#{meth_name})") - rescue - target.eval("method(:#{meth_name})") rescue nil - end + get_method_object_from_target(meth_name, target, :instance) || + get_method_object_from_target(meth_name, target, :singleton) end end