From b031df2f2f5850ee6e9018f33d35f3485a9b0423 Mon Sep 17 00:00:00 2001 From: Christopher Sexton Date: Fri, 22 Nov 2013 19:36:18 -0500 Subject: [PATCH 01/10] Add Watch Expression Command Usage: watch [EXPRESSION] watch watch --delete [INDEX] Evaluate an expression after every command and display it when its value changes. -d, --delete Delete the watch expression with the given index. If no index is given; clear all watch expressions. -l, --list Show all current watch expressions and their values. Calling watch with no expressions or options will also show the watch expressions. The watch command will use Pry's command `state` to keep track of watched expressions. --- lib/pry/commands/watch_expression.rb | 97 +++++++++++++++++++ .../commands/watch_expression/expression.rb | 41 ++++++++ spec/commands/watch_expression_spec.rb | 61 ++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 lib/pry/commands/watch_expression.rb create mode 100644 lib/pry/commands/watch_expression/expression.rb create mode 100644 spec/commands/watch_expression_spec.rb diff --git a/lib/pry/commands/watch_expression.rb b/lib/pry/commands/watch_expression.rb new file mode 100644 index 00000000..4fe1746e --- /dev/null +++ b/lib/pry/commands/watch_expression.rb @@ -0,0 +1,97 @@ +class Pry + class Command::WatchExpression < Pry::ClassCommand + require 'pry/commands/watch_expression/expression.rb' + extend Pry::Helpers::BaseHelpers + + match 'watch' + group 'Context' + description 'Evaluate an expression after every command and display it when its value changes.' + command_options :use_prefix => false + + banner <<-'BANNER' + Usage: watch [EXPRESSION] + watch + watch --delete [INDEX] + + Evaluate an expression after every command and display it when its value changes. + BANNER + + def options(opt) + opt.on :d, :delete, + "Delete the watch expression with the given index. If no index is given; clear all watch expressions.", + :optional_argument => true, :as => Integer + opt.on :l, :list, + "Show all current watch expressions and their values. Calling watch with no expressions or options will also show the watch expressions." + end + + def process + ret = case + when opts.present?(:delete) + delete opts[:delete] + when opts.present?(:list) || args.empty? + list + else + add_hook + add_expression(args) + end + end + + private + + def expressions + state.expressions ||= [] + state.expressions + end + + def delete(index) + if index + output.puts "Deleting watch expression ##{index}: #{expressions[index-1]}" + expressions.delete_at(index-1) + else + output.puts "Deleting all watched expressions" + expressions.clear + end + end + + def list + if expressions.empty? + output.puts "No watched expressions" + else + Pry::Pager.with_pager(output) do |pager| + pager.puts "Listing all watched expressions:" + pager.puts "" + expressions.each_with_index do |expr, index| + pager.print text.with_line_numbers(expr.to_s, index+1) + end + pager.puts "" + end + end + end + + def eval_and_print_changed + expressions.each do |expr| + expr.eval! + if expr.changed? + output.puts "#{text.blue "watch"}: #{expr.to_s}" + end + end + end + + def add_expression(arguments) + e = expressions + e << Expression.new(target, arg_string) + output.puts "Watching #{Code.new(arg_string)}" + end + + def add_hook + hook = [:after_eval, :watch_expression] + unless Pry.config.hooks.hook_exists? *hook + _pry_.hooks.add_hook(*hook) do + eval_and_print_changed + end + end + end + end + + Pry::Commands.add_command(Pry::Command::WatchExpression) +end diff --git a/lib/pry/commands/watch_expression/expression.rb b/lib/pry/commands/watch_expression/expression.rb new file mode 100644 index 00000000..1a3c1de9 --- /dev/null +++ b/lib/pry/commands/watch_expression/expression.rb @@ -0,0 +1,41 @@ +class Pry + class Command::WatchExpression + class Expression + attr_reader :target, :source, :value, :previous_value + + def initialize(target, source) + @target = target + @source = source + end + + def eval! + @previous_value = value + @value = target_eval(target, source) + end + + def to_s + "#{print_source} => #{print_value}" + end + + def changed? + (value != previous_value) + end + + def print_value + Pry::ColorPrinter.pp(value, "") + end + + def print_source + Code.new(source).strip + end + + private + + def target_eval(target, source) + target.eval(source) + rescue => e + e + end + end + end +end diff --git a/spec/commands/watch_expression_spec.rb b/spec/commands/watch_expression_spec.rb new file mode 100644 index 00000000..71984e58 --- /dev/null +++ b/spec/commands/watch_expression_spec.rb @@ -0,0 +1,61 @@ +require 'helper' + +describe "watch expression" do + + # Custom eval that will: + # 1) Create an instance of pry that can use for multiple calls + # 2) Exercise the after_eval hook + # 3) Return the output + def eval(expr) + output = @tester.eval expr + @tester.pry.hooks.exec_hook :after_eval + output + end + + before do + @tester = pry_tester + @tester.pry.hooks.clear :after_eval + eval "watch --delete" + end + + it "registers the before_session hook" do + eval 'watch 1+1' + @tester.pry.hooks.hook_exists?(:after_eval, :watch_expression).should == true + end + + it "prints no watched expressions" do + eval('watch').should =~ /No watched expressions/ + end + + it "watches an expression" do + eval "watch 1+1" + eval('watch').should =~ /=> 2/ + end + + it "watches a local variable" do + eval 'foo = :bar' + eval 'watch foo' + eval('watch').should =~ /=> :bar/ + end + + #it "prints only when an expression changes" do + # # TODO: This is one of the main features, but I am not sure how to test the + # # output from a hook. + #end + + describe "deleting expressions" do + before do + eval 'watch :keeper' + eval 'watch :delete' + eval 'watch -d 2' + end + + it "keeps keeper" do + eval('watch').should =~ /keeper/ + end + + it "deletes delete" do + eval('watch').should.not =~ /delete/ + end + end +end From bb1418c623fdb9cdaea3a0dbff3d01247439a4e3 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 23 Jan 2014 22:13:53 -0800 Subject: [PATCH 02/10] Minor space changes per @rf- --- lib/pry/commands/watch_expression.rb | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/pry/commands/watch_expression.rb b/lib/pry/commands/watch_expression.rb index 4fe1746e..0ced7ece 100644 --- a/lib/pry/commands/watch_expression.rb +++ b/lib/pry/commands/watch_expression.rb @@ -1,7 +1,6 @@ class Pry class Command::WatchExpression < Pry::ClassCommand require 'pry/commands/watch_expression/expression.rb' - extend Pry::Helpers::BaseHelpers match 'watch' group 'Context' @@ -25,22 +24,21 @@ class Pry end def process - ret = case - when opts.present?(:delete) - delete opts[:delete] - when opts.present?(:list) || args.empty? - list - else - add_hook - add_expression(args) - end + case + when opts.present?(:delete) + delete opts[:delete] + when opts.present?(:list) || args.empty? + list + else + add_hook + add_expression(args) + end end private def expressions - state.expressions ||= [] - state.expressions + state.expressions ||= [] end def delete(index) @@ -85,7 +83,7 @@ class Pry def add_hook hook = [:after_eval, :watch_expression] - unless Pry.config.hooks.hook_exists? *hook + unless Pry.config.hooks.hook_exists?(*hook) _pry_.hooks.add_hook(*hook) do eval_and_print_changed end From 483728fcc78deb478c3fe8db4629b3b63fca18a2 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 23 Jan 2014 23:00:12 -0800 Subject: [PATCH 03/10] Fix hook name in test --- spec/commands/watch_expression_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/commands/watch_expression_spec.rb b/spec/commands/watch_expression_spec.rb index 71984e58..ea10ae87 100644 --- a/spec/commands/watch_expression_spec.rb +++ b/spec/commands/watch_expression_spec.rb @@ -18,7 +18,7 @@ describe "watch expression" do eval "watch --delete" end - it "registers the before_session hook" do + it "registers the after_eval hook" do eval 'watch 1+1' @tester.pry.hooks.hook_exists?(:after_eval, :watch_expression).should == true end From 7ff98c60a43cfb50b57e769965fdb3455e49a9f7 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 23 Jan 2014 23:00:20 -0800 Subject: [PATCH 04/10] Implement expression change test (per @rf-) --- spec/commands/watch_expression_spec.rb | 29 ++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/spec/commands/watch_expression_spec.rb b/spec/commands/watch_expression_spec.rb index ea10ae87..4801bdb3 100644 --- a/spec/commands/watch_expression_spec.rb +++ b/spec/commands/watch_expression_spec.rb @@ -38,10 +38,31 @@ describe "watch expression" do eval('watch').should =~ /=> :bar/ end - #it "prints only when an expression changes" do - # # TODO: This is one of the main features, but I am not sure how to test the - # # output from a hook. - #end + it "prints when an expression changes" do + ReplTester.start do + input 'a = 1' + output '=> 1' + + input 'watch a' + output "Watching a\nwatch: a => 1" + + input "a = 2" + output "watch: a => 2\n=> 2" + end + end + + it "doesn't print when an expresison remains the same" do + ReplTester.start do + input 'a = 1' + output '=> 1' + + input 'watch a' + output "Watching a\nwatch: a => 1" + + input "a = 1" + output "=> 1" + end + end describe "deleting expressions" do before do From b4ad243570d51802bf9a886264375b0e379f32aa Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 23 Jan 2014 23:01:53 -0800 Subject: [PATCH 05/10] remove unnecessary temporary --- lib/pry/commands/watch_expression.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pry/commands/watch_expression.rb b/lib/pry/commands/watch_expression.rb index 0ced7ece..ee28a1b1 100644 --- a/lib/pry/commands/watch_expression.rb +++ b/lib/pry/commands/watch_expression.rb @@ -76,8 +76,7 @@ class Pry end def add_expression(arguments) - e = expressions - e << Expression.new(target, arg_string) + expressions << Expression.new(target, arg_string) output.puts "Watching #{Code.new(arg_string)}" end From 0320ae8471cbd02c04fd7c1f329cd2844bd6d545 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Thu, 23 Jan 2014 23:36:46 -0800 Subject: [PATCH 06/10] Make watch expressions fully global At the moment Pry::Hooks are not local to each instance, so the hook was only being added once. This caused problems when you opened two binding.pry's in one program, as the second one's watch expressions appeared to be ignored. --- lib/pry/commands/watch_expression.rb | 10 +++++----- spec/commands/watch_expression_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/pry/commands/watch_expression.rb b/lib/pry/commands/watch_expression.rb index ee28a1b1..05d8e36e 100644 --- a/lib/pry/commands/watch_expression.rb +++ b/lib/pry/commands/watch_expression.rb @@ -38,7 +38,7 @@ class Pry private def expressions - state.expressions ||= [] + Pry.config.watch_expressions ||= [] end def delete(index) @@ -66,7 +66,7 @@ class Pry end end - def eval_and_print_changed + def eval_and_print_changed(output) expressions.each do |expr| expr.eval! if expr.changed? @@ -82,9 +82,9 @@ class Pry def add_hook hook = [:after_eval, :watch_expression] - unless Pry.config.hooks.hook_exists?(*hook) - _pry_.hooks.add_hook(*hook) do - eval_and_print_changed + unless Pry.hooks.hook_exists?(*hook) + Pry.hooks.add_hook(*hook) do |_, _pry_| + eval_and_print_changed _pry_.output end end end diff --git a/spec/commands/watch_expression_spec.rb b/spec/commands/watch_expression_spec.rb index 4801bdb3..e87d8be4 100644 --- a/spec/commands/watch_expression_spec.rb +++ b/spec/commands/watch_expression_spec.rb @@ -64,6 +64,30 @@ describe "watch expression" do end end + it "continues to work if you start a second pry instance" do + ReplTester.start do + input 'a = 1' + output '=> 1' + + input 'watch a' + output "Watching a\nwatch: a => 1" + + input "a = 2" + output "watch: a => 2\n=> 2" + end + + ReplTester.start do + input 'b = 1' + output '=> 1' + + input 'watch b' + output "Watching b\nwatch: b => 1" + + input "b = 2" + output "watch: b => 2\n=> 2" + end + end + describe "deleting expressions" do before do eval 'watch :keeper' From 3e0d76458feadd4e565d13269eb9703df008f1f0 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 24 Jan 2014 00:16:16 -0800 Subject: [PATCH 07/10] Fix watch tests --- spec/commands/watch_expression_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/commands/watch_expression_spec.rb b/spec/commands/watch_expression_spec.rb index e87d8be4..ea62cedd 100644 --- a/spec/commands/watch_expression_spec.rb +++ b/spec/commands/watch_expression_spec.rb @@ -8,7 +8,7 @@ describe "watch expression" do # 3) Return the output def eval(expr) output = @tester.eval expr - @tester.pry.hooks.exec_hook :after_eval + @tester.pry.hooks.exec_hook :after_eval, nil, @tester.pry output end From c7386aebc2bd2977a1234cffbb001e9a22a0b357 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Fri, 24 Jan 2014 00:24:42 -0800 Subject: [PATCH 08/10] Remove level of indirection --- lib/pry/commands/ls/ls_entity.rb | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/pry/commands/ls/ls_entity.rb b/lib/pry/commands/ls/ls_entity.rb index 7d0b4883..a8958d19 100644 --- a/lib/pry/commands/ls/ls_entity.rb +++ b/lib/pry/commands/ls/ls_entity.rb @@ -29,34 +29,32 @@ class Pry private - def greppable - proc do |entity| - entity.tap { |o| o.grep = @grep } - end + def grep(entity) + entity.tap { |o| o.grep = @grep } end def globals - greppable.call(Globals.new(@target, @opts)) + grep(Globals.new(@target, @opts)) end def constants - greppable.call(Constants.new(@interrogatee, @target, @no_user_opts, @opts)) + grep(Constants.new(@interrogatee, @target, @no_user_opts, @opts)) end def methods - greppable.call(Methods.new(@interrogatee, @no_user_opts, @opts)) + grep(Methods.new(@interrogatee, @no_user_opts, @opts)) end def self_methods - greppable.call(SelfMethods.new(@interrogatee, @no_user_opts, @opts)) + grep(SelfMethods.new(@interrogatee, @no_user_opts, @opts)) end def instance_vars - greppable.call(InstanceVars.new(@interrogatee, @no_user_opts, @opts)) + grep(InstanceVars.new(@interrogatee, @no_user_opts, @opts)) end def local_names - greppable.call(LocalNames.new(@target, @no_user_opts, @sticky_locals, @args)) + grep(LocalNames.new(@target, @no_user_opts, @sticky_locals, @args)) end def local_vars From a987a8e7c250568264e49b4b90a0a1760b15c67e Mon Sep 17 00:00:00 2001 From: Jonas Arvidsson Date: Sat, 25 Jan 2014 22:55:47 +0100 Subject: [PATCH 09/10] Fix bug in edit regarding file names with path but no suffix There was a bug in Command::Edit#probably_a_file? due to missing parentheses in a method call followed by an operator. --- CHANGELOG.md | 1 + lib/pry/commands/edit.rb | 2 +- spec/commands/edit_spec.rb | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec1ea1c..b28c9195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ being inferred from context (#877) * Rename `--installed-plugins` flag to `--plugins` * Strip ANSI codes from prompt before measuring length for indentation (#493) +* Fix bug in `edit` regarding recognition of file names without suffix. #### Dev-facing changes * `rake pry` now accepts switches prefixed with `_` (e.g., `rake pry _v`) diff --git a/lib/pry/commands/edit.rb b/lib/pry/commands/edit.rb index a3b55999..dd00c5ec 100644 --- a/lib/pry/commands/edit.rb +++ b/lib/pry/commands/edit.rb @@ -186,7 +186,7 @@ class Pry end def probably_a_file?(str) - [".rb", ".c", ".py", ".yml", ".gemspec"].include? File.extname(str) || + [".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) || str =~ /\/|\\/ end diff --git a/spec/commands/edit_spec.rb b/spec/commands/edit_spec.rb index 5150aec4..fb7fee2f 100644 --- a/spec/commands/edit_spec.rb +++ b/spec/commands/edit_spec.rb @@ -35,6 +35,16 @@ describe "edit" do FileUtils.rm(@tf_path) if File.exists?(@tf_path) end + it "should not allow patching any known kind of file" do + ["file.rb", "file.c", "file.py", "file.yml", "file.gemspec", + "/tmp/file", "\\\\Temp\\\\file"].each do |file| + proc { + pry_eval "edit -p #{file}" + }.should.raise(NotImplementedError). + message.should =~ /Cannot yet patch false objects!/ + end + end + it "should invoke Pry.config.editor with absolutified filenames" do pry_eval 'edit lib/pry.rb' @file.should == File.expand_path('lib/pry.rb') From 7d0a8e351341f3438b7c8018584f43e31aa27df9 Mon Sep 17 00:00:00 2001 From: Robert Gleeson Date: Sun, 26 Jan 2014 21:31:58 +0100 Subject: [PATCH 10/10] use 'platform' on rubinius (over RbConfig) in Gemfile. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 11553a13..bc55a05d 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ group :development do gem 'rb-fsevent', :require => 'false' end -if RbConfig::CONFIG['ruby_install_name'] == 'rbx' +platform :rbx do gem 'rubysl-singleton' gem 'rubysl-prettyprint' gem 'rb-readline'