diff options
author | Kyrylo Silin <silin@kyrylo.org> | 2019-02-27 02:22:32 +0200 |
---|---|---|
committer | Kyrylo Silin <silin@kyrylo.org> | 2019-02-27 02:23:52 +0200 |
commit | b98cff1b85aeacebcf7bc8c5d13c524814672173 (patch) | |
tree | 869f42b628cf6467045ea42b6410093a01de6698 | |
parent | 4068d96ee8c859d3af5000a39e351b320d74e941 (diff) | |
download | pry-b98cff1b85aeacebcf7bc8c5d13c524814672173.tar.gz |
rubocop: fix offences of the Style/ClassAndModuleChildren cop
95 files changed, 5081 insertions, 4868 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index b9d27765..0de0a936 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -23,3 +23,7 @@ Layout/CommentIndentation: Layout/MultilineMethodCallIndentation: EnforcedStyle: indented + +Style/ClassAndModuleChildren: + Exclude: + - 'spec/fixtures/example_nesting.rb' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 3baaead6..2ec6971a 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -203,13 +203,6 @@ Style/AsciiComments: Style/CaseEquality: Enabled: false -# Offense count: 95 -# Cop supports --auto-correct. -# Configuration parameters: AutoCorrect, EnforcedStyle. -# SupportedStyles: nested, compact -Style/ClassAndModuleChildren: - Enabled: false - # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. @@ -96,6 +96,7 @@ require 'tempfile' require 'pathname' require 'pry/version' +require 'pry/last_exception' require 'pry/input_completer' require 'pry/repl' require 'pry/code' @@ -125,7 +126,6 @@ require 'pry/terminal' require 'pry/editor' require 'pry/rubygem' require "pry/indent" -require "pry/last_exception" require "pry/inspector" require 'pry/object_path' require 'pry/output' diff --git a/lib/pry/basic_object.rb b/lib/pry/basic_object.rb index ab4b03e2..72e6d99e 100644 --- a/lib/pry/basic_object.rb +++ b/lib/pry/basic_object.rb @@ -1,6 +1,8 @@ -class Pry::BasicObject < BasicObject - [:Kernel, :File, :Dir, :LoadError, :ENV, :Pry].each do |constant| - const_set constant, ::Object.const_get(constant) +class Pry + class BasicObject < BasicObject + [:Kernel, :File, :Dir, :LoadError, :ENV, :Pry].each do |constant| + const_set constant, ::Object.const_get(constant) + end + include Kernel end - include Kernel end diff --git a/lib/pry/commands/amend_line.rb b/lib/pry/commands/amend_line.rb index d00b9a31..4cee1272 100644 --- a/lib/pry/commands/amend_line.rb +++ b/lib/pry/commands/amend_line.rb @@ -1,98 +1,100 @@ class Pry - class Command::AmendLine < Pry::ClassCommand - match(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/) - group 'Editing' - description 'Amend a line of input in multi-line mode.' - command_options interpolate: false, listing: 'amend-line' - - banner <<-'BANNER' - Amend a line of input in multi-line mode. `amend-line N`, where the N represents - line to replace. Can also specify a range of lines using `amend-line N..M` - syntax. Passing "!" as replacement content deletes the line(s) instead. - - amend-line 1 puts 'new' # replace line 1 - amend-line 1..4 ! # delete lines 1..4 - amend-line 3 >puts 'bye' # insert before line 3 - amend-line puts 'appended' # no line number modifies immediately preceding line - BANNER - - def process - raise CommandError, "No input to amend." if eval_string.empty? - - eval_string.replace(amend_input) - run "fix-indent" - run "show-input" - end + class Command + class AmendLine < Pry::ClassCommand + match(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/) + group 'Editing' + description 'Amend a line of input in multi-line mode.' + command_options interpolate: false, listing: 'amend-line' + + banner <<-'BANNER' + Amend a line of input in multi-line mode. `amend-line N`, where the N represents + line to replace. Can also specify a range of lines using `amend-line N..M` + syntax. Passing "!" as replacement content deletes the line(s) instead. + + amend-line 1 puts 'new' # replace line 1 + amend-line 1..4 ! # delete lines 1..4 + amend-line 3 >puts 'bye' # insert before line 3 + amend-line puts 'appended' # no line number modifies immediately preceding line + BANNER + + def process + raise CommandError, "No input to amend." if eval_string.empty? + + eval_string.replace(amend_input) + run "fix-indent" + run "show-input" + end - private + private - # @return [String] A new string with the amendments applied to it. - def amend_input - input_array = eval_string.each_line.to_a + # @return [String] A new string with the amendments applied to it. + def amend_input + input_array = eval_string.each_line.to_a - if arg_string == "!" - delete_from_array(input_array, line_range) - elsif arg_string.start_with?(">") - insert_into_array(input_array, line_range) - else - replace_in_array(input_array, line_range) - end + if arg_string == "!" + delete_from_array(input_array, line_range) + elsif arg_string.start_with?(">") + insert_into_array(input_array, line_range) + else + replace_in_array(input_array, line_range) + end - input_array.join - end + input_array.join + end - def delete_from_array(array, range) - array.slice!(range) - end + def delete_from_array(array, range) + array.slice!(range) + end - def insert_into_array(array, range) - insert_slot = Array(range).first - array.insert(insert_slot, arg_string[1..-1] << "\n") - end + def insert_into_array(array, range) + insert_slot = Array(range).first + array.insert(insert_slot, arg_string[1..-1] << "\n") + end - def replace_in_array(array, range) - array[range] = arg_string + "\n" - end + def replace_in_array(array, range) + array[range] = arg_string + "\n" + end - # @return [Fixnum] The number of lines currently in `eval_string` (the input buffer). - def line_count - eval_string.lines.count - end + # @return [Fixnum] The number of lines currently in `eval_string` (the input buffer). + def line_count + eval_string.lines.count + end - # Returns the (one-indexed) start and end lines given by the user. - # The lines in this range will be affected by the `amend-line`. - # Returns `nil` if no lines were specified by the user. - # @return [Array<Fixnum>, nil] - def start_and_end_line_number - start_line_number, end_line_number = args - end_line_number ||= start_line_number.to_i + # Returns the (one-indexed) start and end lines given by the user. + # The lines in this range will be affected by the `amend-line`. + # Returns `nil` if no lines were specified by the user. + # @return [Array<Fixnum>, nil] + def start_and_end_line_number + start_line_number, end_line_number = args + end_line_number ||= start_line_number.to_i - [start_line_number.to_i, end_line_number.to_i] if start_line_number - end + [start_line_number.to_i, end_line_number.to_i] if start_line_number + end - # Takes two numbers that are 1-indexed, and returns a range (or - # number) that is 0-indexed. 1-indexed means the first element is - # indentified by 1 rather than by 0 (as is the case for Ruby arrays). - # @param [Fixnum] start_line_number One-indexed number. - # @param [Fixnum] end_line_number One-indexed number. - # @return [Range] The zero-indexed range. - def zero_indexed_range_from_one_indexed_numbers(start_line_number, end_line_number) - # FIXME: one_index_number is a horrible name for this method - one_index_number(start_line_number)..one_index_number(end_line_number) - end + # Takes two numbers that are 1-indexed, and returns a range (or + # number) that is 0-indexed. 1-indexed means the first element is + # indentified by 1 rather than by 0 (as is the case for Ruby arrays). + # @param [Fixnum] start_line_number One-indexed number. + # @param [Fixnum] end_line_number One-indexed number. + # @return [Range] The zero-indexed range. + def zero_indexed_range_from_one_indexed_numbers(start_line_number, end_line_number) + # FIXME: one_index_number is a horrible name for this method + one_index_number(start_line_number)..one_index_number(end_line_number) + end - # The lines (or line) that will be modified by the `amend-line`. - # @return [Range, Fixnum] The lines or line. - def line_range - start_line_number, end_line_number = start_and_end_line_number - if start_line_number - zero_indexed_range_from_one_indexed_numbers(start_line_number, - end_line_number) - else - line_count - 1 + # The lines (or line) that will be modified by the `amend-line`. + # @return [Range, Fixnum] The lines or line. + def line_range + start_line_number, end_line_number = start_and_end_line_number + if start_line_number + zero_indexed_range_from_one_indexed_numbers(start_line_number, + end_line_number) + else + line_count - 1 + end end end - end - Pry::Commands.add_command(Pry::Command::AmendLine) + Pry::Commands.add_command(Pry::Command::AmendLine) + end end diff --git a/lib/pry/commands/bang.rb b/lib/pry/commands/bang.rb index 7dc6984a..58bea56d 100644 --- a/lib/pry/commands/bang.rb +++ b/lib/pry/commands/bang.rb @@ -1,20 +1,22 @@ class Pry - class Command::Bang < Pry::ClassCommand - match(/^\s*!\s*$/) - group 'Editing' - description 'Clear the input buffer.' - command_options use_prefix: false + class Command + class Bang < Pry::ClassCommand + match(/^\s*!\s*$/) + group 'Editing' + description 'Clear the input buffer.' + command_options use_prefix: false - banner <<-'BANNER' - Clear the input buffer. Useful if the parsing process goes wrong and you get - stuck in the read loop. - BANNER + banner <<-'BANNER' + Clear the input buffer. Useful if the parsing process goes wrong and you get + stuck in the read loop. + BANNER - def process - output.puts 'Input buffer cleared!' - eval_string.replace('') + def process + output.puts 'Input buffer cleared!' + eval_string.replace('') + end end - end - Pry::Commands.add_command(Pry::Command::Bang) + Pry::Commands.add_command(Pry::Command::Bang) + end end diff --git a/lib/pry/commands/bang_pry.rb b/lib/pry/commands/bang_pry.rb index 71f81ef1..728fe92a 100644 --- a/lib/pry/commands/bang_pry.rb +++ b/lib/pry/commands/bang_pry.rb @@ -1,17 +1,19 @@ class Pry - class Command::BangPry < Pry::ClassCommand - match '!pry' - group 'Navigating Pry' - description 'Start a Pry session on current self.' + class Command + class BangPry < Pry::ClassCommand + match '!pry' + group 'Navigating Pry' + description 'Start a Pry session on current self.' - banner <<-'BANNER' - Start a Pry session on current self. Also works mid multi-line expression. - BANNER + banner <<-'BANNER' + Start a Pry session on current self. Also works mid multi-line expression. + BANNER - def process - target.pry + def process + target.pry + end end - end - Pry::Commands.add_command(Pry::Command::BangPry) + Pry::Commands.add_command(Pry::Command::BangPry) + end end diff --git a/lib/pry/commands/cat.rb b/lib/pry/commands/cat.rb index 5d53ace7..4bc445f5 100644 --- a/lib/pry/commands/cat.rb +++ b/lib/pry/commands/cat.rb @@ -1,63 +1,65 @@ class Pry - class Command::Cat < Pry::ClassCommand - require 'pry/commands/cat/abstract_formatter.rb' - require 'pry/commands/cat/input_expression_formatter.rb' - require 'pry/commands/cat/exception_formatter.rb' - require 'pry/commands/cat/file_formatter.rb' - - match 'cat' - group 'Input and Output' - description "Show code from a file, Pry's input buffer, or the last exception." - - banner <<-'BANNER' - Usage: cat FILE - cat --ex [STACK_INDEX] - cat --in [INPUT_INDEX_OR_RANGE] - - `cat` is capable of showing part or all of a source file, the context of the - last exception, or an expression from Pry's input history. - - `cat --ex` defaults to showing the lines surrounding the location of the last - exception. Invoking it more than once travels up the exception's backtrace, and - providing a number shows the context of the given index of the backtrace. - BANNER - - def options(opt) - opt.on :ex, "Show the context of the last exception", optional_argument: true, as: Integer - opt.on :i, :in, "Show one or more entries from Pry's expression history", optional_argument: true, as: Range, default: -5..-1 - opt.on :s, :start, "Starting line (defaults to the first line)", optional_argument: true, as: Integer - opt.on :e, :end, "Ending line (defaults to the last line)", optional_argument: true, as: Integer - opt.on :l, :'line-numbers', "Show line numbers" - opt.on :t, :type, "The file type for syntax highlighting (e.g., 'ruby' or 'python')", argument: true, as: Symbol - end + class Command + class Cat < Pry::ClassCommand + require 'pry/commands/cat/abstract_formatter.rb' + require 'pry/commands/cat/input_expression_formatter.rb' + require 'pry/commands/cat/exception_formatter.rb' + require 'pry/commands/cat/file_formatter.rb' - def process - output = case - when opts.present?(:ex) - ExceptionFormatter.new(_pry_.last_exception, _pry_, opts).format - when opts.present?(:in) - InputExpressionFormatter.new(_pry_.input_ring, opts).format - else - FileFormatter.new(args.first, _pry_, opts).format - end - - _pry_.pager.page output - end + match 'cat' + group 'Input and Output' + description "Show code from a file, Pry's input buffer, or the last exception." - def complete(search) - super | load_path_completions - end + banner <<-'BANNER' + Usage: cat FILE + cat --ex [STACK_INDEX] + cat --in [INPUT_INDEX_OR_RANGE] + + `cat` is capable of showing part or all of a source file, the context of the + last exception, or an expression from Pry's input history. + + `cat --ex` defaults to showing the lines surrounding the location of the last + exception. Invoking it more than once travels up the exception's backtrace, and + providing a number shows the context of the given index of the backtrace. + BANNER + + def options(opt) + opt.on :ex, "Show the context of the last exception", optional_argument: true, as: Integer + opt.on :i, :in, "Show one or more entries from Pry's expression history", optional_argument: true, as: Range, default: -5..-1 + opt.on :s, :start, "Starting line (defaults to the first line)", optional_argument: true, as: Integer + opt.on :e, :end, "Ending line (defaults to the last line)", optional_argument: true, as: Integer + opt.on :l, :'line-numbers', "Show line numbers" + opt.on :t, :type, "The file type for syntax highlighting (e.g., 'ruby' or 'python')", argument: true, as: Symbol + end - def load_path_completions - $LOAD_PATH.flat_map do |path| - Dir[path + '/**/*'].map do |f| - next if File.directory?(f) + def process + output = case + when opts.present?(:ex) + ExceptionFormatter.new(_pry_.last_exception, _pry_, opts).format + when opts.present?(:in) + InputExpressionFormatter.new(_pry_.input_ring, opts).format + else + FileFormatter.new(args.first, _pry_, opts).format + end - f.sub!(path + '/', '') + _pry_.pager.page output + end + + def complete(search) + super | load_path_completions + end + + def load_path_completions + $LOAD_PATH.flat_map do |path| + Dir[path + '/**/*'].map do |f| + next if File.directory?(f) + + f.sub!(path + '/', '') + end end end end - end - Pry::Commands.add_command(Pry::Command::Cat) + Pry::Commands.add_command(Pry::Command::Cat) + end end diff --git a/lib/pry/commands/cat/abstract_formatter.rb b/lib/pry/commands/cat/abstract_formatter.rb index c390bf1b..8e39bd4b 100644 --- a/lib/pry/commands/cat/abstract_formatter.rb +++ b/lib/pry/commands/cat/abstract_formatter.rb @@ -1,27 +1,29 @@ class Pry - class Command::Cat - class AbstractFormatter - include Pry::Helpers::CommandHelpers - include Pry::Helpers::BaseHelpers + class Command + class Cat + class AbstractFormatter + include Pry::Helpers::CommandHelpers + include Pry::Helpers::BaseHelpers - private + private - def decorate(content) - content.code_type = code_type - content.between(*between_lines) - .with_line_numbers(use_line_numbers?).highlighted - end + def decorate(content) + content.code_type = code_type + content.between(*between_lines) + .with_line_numbers(use_line_numbers?).highlighted + end - def code_type - opts[:type] || :ruby - end + def code_type + opts[:type] || :ruby + end - def use_line_numbers? - opts.present?(:'line-numbers') || opts.present?(:ex) - end + def use_line_numbers? + opts.present?(:'line-numbers') || opts.present?(:ex) + end - def between_lines - [opts[:start] || 1, opts[:end] || -1] + def between_lines + [opts[:start] || 1, opts[:end] || -1] + end end end end diff --git a/lib/pry/commands/cat/exception_formatter.rb b/lib/pry/commands/cat/exception_formatter.rb index 9928716e..3da96f52 100644 --- a/lib/pry/commands/cat/exception_formatter.rb +++ b/lib/pry/commands/cat/exception_formatter.rb @@ -1,80 +1,82 @@ class Pry - class Command::Cat - class ExceptionFormatter < AbstractFormatter - attr_reader :ex - attr_reader :opts - attr_reader :_pry_ - include Pry::Helpers::Text + class Command + class Cat + class ExceptionFormatter < AbstractFormatter + attr_reader :ex + attr_reader :opts + attr_reader :_pry_ + include Pry::Helpers::Text - def initialize(exception, _pry_, opts) - @ex = exception - @opts = opts - @_pry_ = _pry_ - end + def initialize(exception, _pry_, opts) + @ex = exception + @opts = opts + @_pry_ = _pry_ + end - def format - check_for_errors - set_file_and_dir_locals(backtrace_file, _pry_, _pry_.current_context) - code = decorate( - Pry::Code.from_file(backtrace_file) - .between(*start_and_end_line_for_code_window) - .with_marker(backtrace_line) - ) - "#{header}#{code}" - end + def format + check_for_errors + set_file_and_dir_locals(backtrace_file, _pry_, _pry_.current_context) + code = decorate( + Pry::Code.from_file(backtrace_file) + .between(*start_and_end_line_for_code_window) + .with_marker(backtrace_line) + ) + "#{header}#{code}" + end - private + private - def code_window_size - _pry_.config.default_window_size || 5 - end + def code_window_size + _pry_.config.default_window_size || 5 + end - def backtrace_level - @backtrace_level ||= - begin - bl = - if opts[:ex].nil? - ex.bt_index - else - ex.bt_index = absolute_index_number(opts[:ex], ex.backtrace.size) - end + def backtrace_level + @backtrace_level ||= + begin + bl = + if opts[:ex].nil? + ex.bt_index + else + ex.bt_index = absolute_index_number(opts[:ex], ex.backtrace.size) + end - increment_backtrace_level - bl - end - end + increment_backtrace_level + bl + end + end - def increment_backtrace_level - ex.inc_bt_index - end + def increment_backtrace_level + ex.inc_bt_index + end - def backtrace_file - Array(ex.bt_source_location_for(backtrace_level)).first - end + def backtrace_file + Array(ex.bt_source_location_for(backtrace_level)).first + end - def backtrace_line - Array(ex.bt_source_location_for(backtrace_level)).last - end + def backtrace_line + Array(ex.bt_source_location_for(backtrace_level)).last + end - def check_for_errors - raise CommandError, "No exception found." unless ex - raise CommandError, "The given backtrace level is out of bounds." unless backtrace_file - end + def check_for_errors + raise CommandError, "No exception found." unless ex + raise CommandError, "The given backtrace level is out of bounds." unless backtrace_file + end - def start_and_end_line_for_code_window - start_line = backtrace_line - code_window_size - start_line = 1 if start_line < 1 + def start_and_end_line_for_code_window + start_line = backtrace_line - code_window_size + start_line = 1 if start_line < 1 - [start_line, backtrace_line + code_window_size] - end + [start_line, backtrace_line + code_window_size] + end - def header - unindent %{ - #{bold 'Exception:'} #{ex.class}: #{ex.message} - -- - #{bold('From:')} #{backtrace_file}:#{backtrace_line} @ #{bold("level: #{backtrace_level}")} of backtrace (of #{ex.backtrace.size - 1}). + def header + unindent %{ + #{bold 'Exception:'} #{ex.class}: #{ex.message} + -- + #{bold('From:')} #{backtrace_file}:#{backtrace_line} @ #{bold("level: #{backtrace_level}")} of backtrace (of #{ex.backtrace.size - 1}). - } + } + end end end end diff --git a/lib/pry/commands/cat/file_formatter.rb b/lib/pry/commands/cat/file_formatter.rb index 04fb599f..d91c30fa 100644 --- a/lib/pry/commands/cat/file_formatter.rb +++ b/lib/pry/commands/cat/file_formatter.rb @@ -1,69 +1,71 @@ class Pry - class Command::Cat - class FileFormatter < AbstractFormatter - attr_reader :file_with_embedded_line - attr_reader :opts - attr_reader :_pry_ + class Command + class Cat + class FileFormatter < AbstractFormatter + attr_reader :file_with_embedded_line + attr_reader :opts + attr_reader :_pry_ - def initialize(file_with_embedded_line, _pry_, opts) - raise CommandError, "Must provide a filename, --in, or --ex." if !file_with_embedded_line + def initialize(file_with_embedded_line, _pry_, opts) + raise CommandError, "Must provide a filename, --in, or --ex." if !file_with_embedded_line - @file_with_embedded_line = file_with_embedded_line - @opts = opts - @_pry_ = _pry_ - @code_from_file = Pry::Code.from_file(file_name) - end + @file_with_embedded_line = file_with_embedded_line + @opts = opts + @_pry_ = _pry_ + @code_from_file = Pry::Code.from_file(file_name) + end - def format - set_file_and_dir_locals(file_name, _pry_, _pry_.current_context) - decorate(@code_from_file) - end + def format + set_file_and_dir_locals(file_name, _pry_, _pry_.current_context) + decorate(@code_from_file) + end - def file_and_line - file_name, line_num = file_with_embedded_line.split(/:(?!\/|\\)/) + def file_and_line + file_name, line_num = file_with_embedded_line.split(/:(?!\/|\\)/) - [file_name, line_num ? line_num.to_i : nil] - end + [file_name, line_num ? line_num.to_i : nil] + end - private + private - def file_name - file_and_line.first - end + def file_name + file_and_line.first + end - def line_number - file_and_line.last - end + def line_number + file_and_line.last + end - def code_window_size - _pry_.config.default_window_size || 7 - end + def code_window_size + _pry_.config.default_window_size || 7 + end - def decorate(content) - if line_number - super(content.around(line_number, code_window_size)) - else - super + def decorate(content) + if line_number + super(content.around(line_number, code_window_size)) + else + super + end end - end - def code_type - opts[:type] || detect_code_type_from_file(file_name) - end + def code_type + opts[:type] || detect_code_type_from_file(file_name) + end - def detect_code_type_from_file(file_name) - code_type = @code_from_file.code_type + def detect_code_type_from_file(file_name) + code_type = @code_from_file.code_type - if code_type == :unknown - name = File.basename(file_name).split('.', 2).first - case name - when "Rakefile", "Gemfile" - :ruby + if code_type == :unknown + name = File.basename(file_name).split('.', 2).first + case name + when "Rakefile", "Gemfile" + :ruby + else + :text + end else - :text + code_type end - else - code_type end end end diff --git a/lib/pry/commands/cat/input_expression_formatter.rb b/lib/pry/commands/cat/input_expression_formatter.rb index c8a5ee76..e7c5ae16 100644 --- a/lib/pry/commands/cat/input_expression_formatter.rb +++ b/lib/pry/commands/cat/input_expression_formatter.rb @@ -1,42 +1,44 @@ class Pry - class Command::Cat - class InputExpressionFormatter < AbstractFormatter - attr_accessor :input_expressions - attr_accessor :opts - - def initialize(input_expressions, opts) - @input_expressions = input_expressions - @opts = opts - end + class Command + class Cat + class InputExpressionFormatter < AbstractFormatter + attr_accessor :input_expressions + attr_accessor :opts + + def initialize(input_expressions, opts) + @input_expressions = input_expressions + @opts = opts + end - def format - raise CommandError, "No input expressions!" if numbered_input_items.length < 1 + def format + raise CommandError, "No input expressions!" if numbered_input_items.length < 1 - if numbered_input_items.length > 1 - content = "" - numbered_input_items.each do |i, s| - content << "#{Helpers::Text.bold(i.to_s)}:\n" << decorate(Pry::Code(s).with_indentation(2)).to_s - end + if numbered_input_items.length > 1 + content = "" + numbered_input_items.each do |i, s| + content << "#{Helpers::Text.bold(i.to_s)}:\n" << decorate(Pry::Code(s).with_indentation(2)).to_s + end - content - else - decorate(Pry::Code(selected_input_items.first)) + content + else + decorate(Pry::Code(selected_input_items.first)) + end end - end - private + private - def selected_input_items - input_expressions[normalized_expression_range] || [] - end + def selected_input_items + input_expressions[normalized_expression_range] || [] + end - def numbered_input_items - @numbered_input_items ||= normalized_expression_range.zip(selected_input_items) - .reject { |_, s| s.nil? || s == "" } - end + def numbered_input_items + @numbered_input_items ||= normalized_expression_range.zip(selected_input_items) + .reject { |_, s| s.nil? || s == "" } + end - def normalized_expression_range - absolute_index_range(opts[:i], input_expressions.count) + def normalized_expression_range + absolute_index_range(opts[:i], input_expressions.count) + end end end end diff --git a/lib/pry/commands/cd.rb b/lib/pry/commands/cd.rb index 667453a0..6b85586e 100644 --- a/lib/pry/commands/cd.rb +++ b/lib/pry/commands/cd.rb @@ -1,41 +1,43 @@ class Pry - class Command::Cd < Pry::ClassCommand - match 'cd' - group 'Context' - description 'Move into a new context (object or scope).' - - banner <<-'BANNER' - Usage: cd [OPTIONS] [--help] - - Move into new context (object or scope). As in UNIX shells use `cd ..` to go - back, `cd /` to return to Pry top-level and `cd -` to toggle between last two - scopes. Complex syntax (e.g `cd ../@x/@y`) also supported. - - cd @x - cd .. - cd / - cd - - - https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope - BANNER - - def process - state.old_stack ||= [] - - if arg_string.strip == "-" - unless state.old_stack.empty? - _pry_.binding_stack, state.old_stack = state.old_stack, _pry_.binding_stack - end - else - stack = ObjectPath.new(arg_string, _pry_.binding_stack).resolve - - if stack && stack != _pry_.binding_stack - state.old_stack = _pry_.binding_stack - _pry_.binding_stack = stack + class Command + class Cd < Pry::ClassCommand + match 'cd' + group 'Context' + description 'Move into a new context (object or scope).' + + banner <<-'BANNER' + Usage: cd [OPTIONS] [--help] + + Move into new context (object or scope). As in UNIX shells use `cd ..` to go + back, `cd /` to return to Pry top-level and `cd -` to toggle between last two + scopes. Complex syntax (e.g `cd ../@x/@y`) also supported. + + cd @x + cd .. + cd / + cd - + + https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope + BANNER + + def process + state.old_stack ||= [] + + if arg_string.strip == "-" + unless state.old_stack.empty? + _pry_.binding_stack, state.old_stack = state.old_stack, _pry_.binding_stack + end + else + stack = ObjectPath.new(arg_string, _pry_.binding_stack).resolve + + if stack && stack != _pry_.binding_stack + state.old_stack = _pry_.binding_stack + _pry_.binding_stack = stack + end end end end - end - Pry::Commands.add_command(Pry::Command::Cd) + Pry::Commands.add_command(Pry::Command::Cd) + end end diff --git a/lib/pry/commands/change_inspector.rb b/lib/pry/commands/change_inspector.rb index 84ce835c..ac2e57ad 100644 --- a/lib/pry/commands/change_inspector.rb +++ b/lib/pry/commands/change_inspector.rb @@ -1,28 +1,32 @@ -class Pry::Command::ChangeInspector < Pry::ClassCommand - match 'change-inspector' - group 'Input and Output' - description 'Change the current inspector proc.' - command_options argument_required: true - banner <<-BANNER - Usage: change-inspector NAME +class Pry + class Command + class ChangeInspector < Pry::ClassCommand + match 'change-inspector' + group 'Input and Output' + description 'Change the current inspector proc.' + command_options argument_required: true + banner <<-BANNER + Usage: change-inspector NAME - Change the proc used to print return values. See list-inspectors for a list - of available procs and a short description of what each one does. - BANNER + Change the proc used to print return values. See list-inspectors for a list + of available procs and a short description of what each one does. + BANNER - def process(inspector) - if inspector_map.key?(inspector) - _pry_.print = inspector_map[inspector][:value] - output.puts "Switched to the '#{inspector}' inspector!" - else - raise Pry::CommandError, "'#{inspector}' isn't a known inspector!" - end - end + def process(inspector) + if inspector_map.key?(inspector) + _pry_.print = inspector_map[inspector][:value] + output.puts "Switched to the '#{inspector}' inspector!" + else + raise Pry::CommandError, "'#{inspector}' isn't a known inspector!" + end + end - private + private - def inspector_map - Pry::Inspector::MAP + def inspector_map + Pry::Inspector::MAP + end + Pry::Commands.add_command(self) + end end - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/change_prompt.rb b/lib/pry/commands/change_prompt.rb index 37d81da2..a0f77070 100644 --- a/lib/pry/commands/change_prompt.rb +++ b/lib/pry/commands/change_prompt.rb @@ -1,44 +1,48 @@ -class Pry::Command::ChangePrompt < Pry::ClassCommand - match 'change-prompt' - group 'Input and Output' - description 'Change the current prompt.' - command_options argument_required: true - banner <<-BANNER - Usage: change-prompt [OPTIONS] [NAME] +class Pry + class Command + class ChangePrompt < Pry::ClassCommand + match 'change-prompt' + group 'Input and Output' + description 'Change the current prompt.' + command_options argument_required: true + banner <<-BANNER + Usage: change-prompt [OPTIONS] [NAME] - Change the current prompt. - BANNER + Change the current prompt. + BANNER - def options(opt) - opt.on(:l, :list, 'List the available prompts') - end + def options(opt) + opt.on(:l, :list, 'List the available prompts') + end - def process(prompt) - if opts.present?(:l) - list_prompts - else - change_prompt(prompt) - end - end + def process(prompt) + if opts.present?(:l) + list_prompts + else + change_prompt(prompt) + end + end - private + private - def list_prompts - prompts = Pry::Prompt.all.map do |name, prompt| - "#{bold(name)}#{red(' (selected)') if _pry_.prompt == prompt}\n" + - prompt.description - end - output.puts(prompts.join("\n" * 2)) - end + def list_prompts + prompts = Pry::Prompt.all.map do |name, prompt| + "#{bold(name)}#{red(' (selected)') if _pry_.prompt == prompt}\n" + + prompt.description + end + output.puts(prompts.join("\n" * 2)) + end + + def change_prompt(prompt) + if Pry::Prompt[prompt] + _pry_.prompt = Pry::Prompt[prompt] + else + raise Pry::CommandError, "'#{prompt}' isn't a known prompt. " \ + "Run `change-prompt --list` to see the list of known prompts." + end + end - def change_prompt(prompt) - if Pry::Prompt[prompt] - _pry_.prompt = Pry::Prompt[prompt] - else - raise Pry::CommandError, "'#{prompt}' isn't a known prompt. " \ - "Run `change-prompt --list` to see the list of known prompts." + Pry::Commands.add_command(self) end end - - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/clear_screen.rb b/lib/pry/commands/clear_screen.rb index f070e55b..0a162efc 100644 --- a/lib/pry/commands/clear_screen.rb +++ b/lib/pry/commands/clear_screen.rb @@ -1,14 +1,18 @@ -class Pry::Command::ClearScreen < Pry::ClassCommand - match 'clear-screen' - group 'Input and Output' - description 'Clear the contents of the screen/window Pry is running in.' +class Pry + class Command + class ClearScreen < Pry::ClassCommand + match 'clear-screen' + group 'Input and Output' + description 'Clear the contents of the screen/window Pry is running in.' - def process - if Pry::Helpers::Platform.windows? - _pry_.config.system.call(_pry_.output, 'cls', _pry_) - else - _pry_.config.system.call(_pry_.output, 'clear', _pry_) + def process + if Pry::Helpers::Platform.windows? + _pry_.config.system.call(_pry_.output, 'cls', _pry_) + else + _pry_.config.system.call(_pry_.output, 'clear', _pry_) + end + end + Pry::Commands.add_command(self) end end - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/code_collector.rb b/lib/pry/commands/code_collector.rb index cd6d767b..d8344309 100644 --- a/lib/pry/commands/code_collector.rb +++ b/lib/pry/commands/code_collector.rb @@ -1,166 +1,168 @@ class Pry - class Command::CodeCollector - include Helpers::CommandHelpers + class Command + class CodeCollector + include Helpers::CommandHelpers - attr_reader :args - attr_reader :opts - attr_reader :_pry_ + attr_reader :args + attr_reader :opts + attr_reader :_pry_ - # The name of the explicitly given file (if any). - attr_accessor :file + # The name of the explicitly given file (if any). + attr_accessor :file - class << self - attr_accessor :input_expression_ranges - attr_accessor :output_result_ranges - end - - @input_expression_ranges = [] - @output_result_ranges = [] - - def initialize(args, opts, _pry_) - @args = args - @opts = opts - @_pry_ = _pry_ - end + class << self + attr_accessor :input_expression_ranges + attr_accessor :output_result_ranges + end - # Add the `--lines`, `-o`, `-i`, `-s`, `-d` options. - def self.inject_options(opt) @input_expression_ranges = [] @output_result_ranges = [] - opt.on :l, :lines, "Restrict to a subset of lines. Takes a line number or range", - optional_argument: true, as: Range, default: 1..-1 - opt.on :o, :out, "Select lines from Pry's output result history. Takes an index or range", - optional_argument: true, as: Range, default: -5..-1 do |r| - output_result_ranges << (r || (-5..-1)) + def initialize(args, opts, _pry_) + @args = args + @opts = opts + @_pry_ = _pry_ end - opt.on :i, :in, "Select lines from Pry's input expression history. Takes an index or range", - optional_argument: true, as: Range, default: -5..-1 do |r| - input_expression_ranges << (r || (-5..-1)) - end - opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors", - as: :count - opt.on :d, :doc, "Select lines from the code object's documentation" - end - # The content (i.e code/docs) for the selected object. - # If the user provided a bare code object, it returns the source. - # If the user provided the `-i` or `-o` switches, it returns the - # selected input/output lines joined as a string. If the user used - # `-d CODE_OBJECT` it returns the docs for that code object. - # - # @return [String] - def content - @content ||= - begin - raise CommandError, "Only one of --out, --in, --doc and CODE_OBJECT may be specified." if bad_option_combination? - - content = case - when opts.present?(:o) - pry_output_content - when opts.present?(:i) - pry_input_content - when opts.present?(:d) - code_object_doc - else - code_object_source_or_file - end - - restrict_to_lines(content, line_range) - end - end + # Add the `--lines`, `-o`, `-i`, `-s`, `-d` options. + def self.inject_options(opt) + @input_expression_ranges = [] + @output_result_ranges = [] - # The code object - # - # @return [Pry::WrappedModule, Pry::Method, Pry::Command] - def code_object - Pry::CodeObject.lookup(obj_name, _pry_, super: opts[:super]) - end + opt.on :l, :lines, "Restrict to a subset of lines. Takes a line number or range", + optional_argument: true, as: Range, default: 1..-1 + opt.on :o, :out, "Select lines from Pry's output result history. Takes an index or range", + optional_argument: true, as: Range, default: -5..-1 do |r| + output_result_ranges << (r || (-5..-1)) + end + opt.on :i, :in, "Select lines from Pry's input expression history. Takes an index or range", + optional_argument: true, as: Range, default: -5..-1 do |r| + input_expression_ranges << (r || (-5..-1)) + end + opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors", + as: :count + opt.on :d, :doc, "Select lines from the code object's documentation" + end - # Given a string and a range, return the `range` lines of that - # string. - # - # @param [String] content - # @param [Range, Fixnum] range - # @return [String] The string restricted to the given range - def restrict_to_lines(content, range) - Array(content.lines.to_a[range]).join - end + # The content (i.e code/docs) for the selected object. + # If the user provided a bare code object, it returns the source. + # If the user provided the `-i` or `-o` switches, it returns the + # selected input/output lines joined as a string. If the user used + # `-d CODE_OBJECT` it returns the docs for that code object. + # + # @return [String] + def content + @content ||= + begin + raise CommandError, "Only one of --out, --in, --doc and CODE_OBJECT may be specified." if bad_option_combination? + + content = case + when opts.present?(:o) + pry_output_content + when opts.present?(:i) + pry_input_content + when opts.present?(:d) + code_object_doc + else + code_object_source_or_file + end + + restrict_to_lines(content, line_range) + end + end - # The selected `_pry_.output_ring` as a string, as specified by - # the `-o` switch. - # - # @return [String] - def pry_output_content - pry_array_content_as_string(_pry_.output_ring, self.class.output_result_ranges) do |v| - _pry_.config.gist.inspecter.call(v) + # The code object + # + # @return [Pry::WrappedModule, Pry::Method, Pry::Command] + def code_object + Pry::CodeObject.lookup(obj_name, _pry_, super: opts[:super]) end - end - # The selected `_pry_.input_ring` as a string, as specified by - # the `-i` switch. - # - # @return [String] - def pry_input_content - pry_array_content_as_string(_pry_.input_ring, self.class.input_expression_ranges) { |v| v } - end + # Given a string and a range, return the `range` lines of that + # string. + # + # @param [String] content + # @param [Range, Fixnum] range + # @return [String] The string restricted to the given range + def restrict_to_lines(content, range) + Array(content.lines.to_a[range]).join + end - # The line range passed to `--lines`, converted to a 0-indexed range. - def line_range - opts.present?(:lines) ? one_index_range_or_number(opts[:lines]) : 0..-1 - end + # The selected `_pry_.output_ring` as a string, as specified by + # the `-o` switch. + # + # @return [String] + def pry_output_content + pry_array_content_as_string(_pry_.output_ring, self.class.output_result_ranges) do |v| + _pry_.config.gist.inspecter.call(v) + end + end - # Name of the object argument - def obj_name - @obj_name ||= args.empty? ? "" : args.join(" ") - end + # The selected `_pry_.input_ring` as a string, as specified by + # the `-i` switch. + # + # @return [String] + def pry_input_content + pry_array_content_as_string(_pry_.input_ring, self.class.input_expression_ranges) { |v| v } + end - private + # The line range passed to `--lines`, converted to a 0-indexed range. + def line_range + opts.present?(:lines) ? one_index_range_or_number(opts[:lines]) : 0..-1 + end - def bad_option_combination? - [opts.present?(:in), opts.present?(:out), - !args.empty?].count(true) > 1 - end + # Name of the object argument + def obj_name + @obj_name ||= args.empty? ? "" : args.join(" ") + end - def pry_array_content_as_string(array, ranges) - all = '' - ranges.each do |range| - raise CommandError, "Minimum value for range is 1, not 0." if convert_to_range(range).first == 0 + private - ranged_array = Array(array[range]) || [] - ranged_array.compact.each { |v| all << yield(v) } + def bad_option_combination? + [opts.present?(:in), opts.present?(:out), + !args.empty?].count(true) > 1 end - all - end + def pry_array_content_as_string(array, ranges) + all = '' + ranges.each do |range| + raise CommandError, "Minimum value for range is 1, not 0." if convert_to_range(range).first == 0 - def code_object_doc - (code_object && code_object.doc) || could_not_locate(obj_name) - end + ranged_array = Array(array[range]) || [] + ranged_array.compact.each { |v| all << yield(v) } + end - def code_object_source_or_file - (code_object && code_object.source) || file_content - end + all + end - def file_content - if File.exist?(obj_name) - # Set the file accessor. - self.file = obj_name - File.read(obj_name) - else - could_not_locate(obj_name) + def code_object_doc + (code_object && code_object.doc) || could_not_locate(obj_name) end - end - def could_not_locate(name) - raise CommandError, "Cannot locate: #{name}!" - end + def code_object_source_or_file + (code_object && code_object.source) || file_content + end + + def file_content + if File.exist?(obj_name) + # Set the file accessor. + self.file = obj_name + File.read(obj_name) + else + could_not_locate(obj_name) + end + end - def convert_to_range(n) - if !n.is_a?(Range) - (n..n) - else - n + def could_not_locate(name) + raise CommandError, "Cannot locate: #{name}!" + end + + def convert_to_range(n) + if !n.is_a?(Range) + (n..n) + else + n + end end end end diff --git a/lib/pry/commands/disable_pry.rb b/lib/pry/commands/disable_pry.rb index 69bfc4e8..a4019cc1 100644 --- a/lib/pry/commands/disable_pry.rb +++ b/lib/pry/commands/disable_pry.rb @@ -1,27 +1,29 @@ class Pry - class Command::DisablePry < Pry::ClassCommand - match 'disable-pry' - group 'Navigating Pry' - description 'Stops all future calls to pry and exits the current session.' + class Command + class DisablePry < Pry::ClassCommand + match 'disable-pry' + group 'Navigating Pry' + description 'Stops all future calls to pry and exits the current session.' - banner <<-'BANNER' - Usage: disable-pry + banner <<-'BANNER' + Usage: disable-pry - After this command is run any further calls to pry will immediately return `nil` - without interrupting the flow of your program. This is particularly useful when - you've debugged the problem you were having, and now wish the program to run to - the end. + After this command is run any further calls to pry will immediately return `nil` + without interrupting the flow of your program. This is particularly useful when + you've debugged the problem you were having, and now wish the program to run to + the end. - As alternatives, consider using `exit!` to force the current Ruby process - to quit immediately; or using `edit-method -p` to remove the `binding.pry` - from the code. - BANNER + As alternatives, consider using `exit!` to force the current Ruby process + to quit immediately; or using `edit-method -p` to remove the `binding.pry` + from the code. + BANNER - def process - ENV['DISABLE_PRY'] = 'true' - _pry_.run_command "exit" + def process + ENV['DISABLE_PRY'] = 'true' + _pry_.run_command "exit" + end end - end - Pry::Commands.add_command(Pry::Command::DisablePry) + Pry::Commands.add_command(Pry::Command::DisablePry) + end end diff --git a/lib/pry/commands/edit.rb b/lib/pry/commands/edit.rb index 0e138526..8861f325 100644 --- a/lib/pry/commands/edit.rb +++ b/lib/pry/commands/edit.rb @@ -1,204 +1,206 @@ class Pry - class Command::Edit < Pry::ClassCommand - require 'pry/commands/edit/exception_patcher' - require 'pry/commands/edit/file_and_line_locator' - - match 'edit' - group 'Editing' - description 'Invoke the default editor on a file.' - - banner <<-'BANNER' - Usage: edit [--no-reload|--reload|--patch] [--line LINE] [--temp|--ex|FILE[:LINE]|OBJECT|--in N] - - Open a text editor. When no FILE is given, edits the pry input buffer. - When a method/module/command is given, the code is opened in an editor. - Ensure `Pry.config.editor` or `_pry_.config.editor` is set to your editor of choice. - - edit sample.rb edit -p MyClass#my_method - edit sample.rb --line 105 edit MyClass - edit MyClass#my_method edit --ex - edit --method edit --ex -p - - https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command - BANNER - - def options(opt) - opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)", - optional_argument: true, as: Integer - opt.on :i, :in, "Open a temporary file containing the Nth input expression. N may be a range", - optional_argument: true, as: Range, default: -1..-1 - opt.on :t, :temp, "Open an empty temporary file" - opt.on :l, :line, "Jump to this line in the opened file", - argument: true, as: Integer - opt.on :n, :"no-reload", "Don't automatically reload the edited file" - 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, --method and FILE may be specified." + class Command + class Edit < Pry::ClassCommand + require 'pry/commands/edit/exception_patcher' + require 'pry/commands/edit/file_and_line_locator' + + match 'edit' + group 'Editing' + description 'Invoke the default editor on a file.' + + banner <<-'BANNER' + Usage: edit [--no-reload|--reload|--patch] [--line LINE] [--temp|--ex|FILE[:LINE]|OBJECT|--in N] + + Open a text editor. When no FILE is given, edits the pry input buffer. + When a method/module/command is given, the code is opened in an editor. + Ensure `Pry.config.editor` or `_pry_.config.editor` is set to your editor of choice. + + edit sample.rb edit -p MyClass#my_method + edit sample.rb --line 105 edit MyClass + edit MyClass#my_method edit --ex + edit --method edit --ex -p + + https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command + BANNER + + def options(opt) + opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)", + optional_argument: true, as: Integer + opt.on :i, :in, "Open a temporary file containing the Nth input expression. N may be a range", + optional_argument: true, as: Range, default: -1..-1 + opt.on :t, :temp, "Open an empty temporary file" + opt.on :l, :line, "Jump to this line in the opened file", + argument: true, as: Integer + opt.on :n, :"no-reload", "Don't automatically reload the edited file" + 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 - if repl_edit? - # code defined in pry, eval'd within pry. - repl_edit - elsif runtime_patch? - # patch code without persisting changes, implies future changes are patches - apply_runtime_patch - else - # code stored in actual files, eval'd at top-level - file_edit + def process + if bad_option_combination? + raise CommandError, "Only one of --ex, --temp, --in, --method and FILE may be specified." + end + + if repl_edit? + # code defined in pry, eval'd within pry. + repl_edit + elsif runtime_patch? + # patch code without persisting changes, implies future changes are patches + apply_runtime_patch + else + # code stored in actual files, eval'd at top-level + file_edit + end end - end - def repl_edit? - !opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) && - filename_argument.empty? - end + def repl_edit? + !opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) && + filename_argument.empty? + end - def repl_edit - content = Pry::Editor.new(_pry_).edit_tempfile_with_content(initial_temp_file_content, - initial_temp_file_content.lines.count) - silence_warnings do - eval_string.replace content + def repl_edit + content = Pry::Editor.new(_pry_).edit_tempfile_with_content(initial_temp_file_content, + initial_temp_file_content.lines.count) + silence_warnings do + eval_string.replace content + end + Pry.history.push(content) end - Pry.history.push(content) - end - def file_based_exception? - opts.present?(:ex) && !opts.present?(:patch) - end + def file_based_exception? + opts.present?(:ex) && !opts.present?(:patch) + end - def runtime_patch? - !file_based_exception? && - (opts.present?(:patch) || - previously_patched?(code_object) || - pry_method?(code_object)) - end + def runtime_patch? + !file_based_exception? && + (opts.present?(:patch) || + previously_patched?(code_object) || + pry_method?(code_object)) + end - def apply_runtime_patch - if patch_exception? - ExceptionPatcher.new(_pry_, state, file_and_line_for_current_exception).perform_patch - else - if code_object.is_a?(Pry::Method) - code_object.redefine Pry::Editor.new(_pry_).edit_tempfile_with_content(code_object.source) + def apply_runtime_patch + if patch_exception? + ExceptionPatcher.new(_pry_, state, file_and_line_for_current_exception).perform_patch else - raise NotImplementedError, "Cannot yet patch #{code_object} objects!" + if code_object.is_a?(Pry::Method) + code_object.redefine Pry::Editor.new(_pry_).edit_tempfile_with_content(code_object.source) + else + raise NotImplementedError, "Cannot yet patch #{code_object} objects!" + end end end - end - def ensure_file_name_is_valid(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 ensure_file_name_is_valid(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_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) - file_and_line_for_current_exception - elsif code_object - 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(filename_argument) - end - - [file_name, opts.present?(:line) ? opts[:l].to_i : line] - end + def file_and_line + file_name, line = if opts.present?(:current) + FileAndLineLocator.from_binding(target) + elsif opts.present?(:ex) + file_and_line_for_current_exception + elsif code_object + 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(filename_argument) + end + + [file_name, opts.present?(:line) ? opts[:l].to_i : line] + end - def file_edit - file_name, line = file_and_line + def file_edit + file_name, line = file_and_line - ensure_file_name_is_valid(file_name) + ensure_file_name_is_valid(file_name) - Pry::Editor.new(_pry_).invoke_editor(file_name, line, reload?(file_name)) - set_file_and_dir_locals(file_name) + Pry::Editor.new(_pry_).invoke_editor(file_name, line, reload?(file_name)) + set_file_and_dir_locals(file_name) - if reload?(file_name) - silence_warnings do - load file_name + if reload?(file_name) + silence_warnings do + load file_name + end end end - end - def filename_argument - args.join(' ') - end + def filename_argument + args.join(' ') + end - def code_object - @code_object ||= - !probably_a_file?(filename_argument) && - Pry::CodeObject.lookup(filename_argument, _pry_) - end + def code_object + @code_object ||= + !probably_a_file?(filename_argument) && + Pry::CodeObject.lookup(filename_argument, _pry_) + end - def pry_method?(code_object) - code_object.is_a?(Pry::Method) && - code_object.pry_method? - end + def pry_method?(code_object) + code_object.is_a?(Pry::Method) && + code_object.pry_method? + end - def previously_patched?(code_object) - code_object.is_a?(Pry::Method) && Pry::Method::Patcher.code_for(code_object.source_location.first) - end + def previously_patched?(code_object) + code_object.is_a?(Pry::Method) && Pry::Method::Patcher.code_for(code_object.source_location.first) + end - def patch_exception? - opts.present?(:ex) && opts.present?(:patch) - end + def patch_exception? + opts.present?(:ex) && opts.present?(:patch) + end - def bad_option_combination? - [opts.present?(:ex), opts.present?(:temp), - opts.present?(:in), opts.present?(:method), !filename_argument.empty?].count(true) > 1 - end + def bad_option_combination? + [opts.present?(:ex), opts.present?(:temp), + opts.present?(:in), opts.present?(:method), !filename_argument.empty?].count(true) > 1 + end + + def input_expression + case opts[:i] + when Range + (_pry_.input_ring[opts[:i]] || []).join + when Integer + _pry_.input_ring[opts[:i]] || "" + else + raise Pry::CommandError, "Not a valid range: #{opts[:i]}" + end + end - def input_expression - case opts[:i] - when Range - (_pry_.input_ring[opts[:i]] || []).join - when Integer - _pry_.input_ring[opts[:i]] || "" - else - raise Pry::CommandError, "Not a valid range: #{opts[:i]}" + def reloadable? + opts.present?(:reload) || opts.present?(:ex) end - end - def reloadable? - opts.present?(:reload) || opts.present?(:ex) - end + def never_reload? + opts.present?(:'no-reload') || _pry_.config.disable_auto_reload + end - def never_reload? - opts.present?(:'no-reload') || _pry_.config.disable_auto_reload - end + def reload?(file_name = "") + (reloadable? || file_name.end_with?(".rb")) && !never_reload? + end - def reload?(file_name = "") - (reloadable? || file_name.end_with?(".rb")) && !never_reload? - end + def initial_temp_file_content + case + when opts.present?(:temp) + "" + when opts.present?(:in) + input_expression + when eval_string.strip != "" + eval_string + else + _pry_.input_ring.to_a.reverse_each.find { |x| x && x.strip != "" } || "" + end + end - def initial_temp_file_content - case - when opts.present?(:temp) - "" - when opts.present?(:in) - input_expression - when eval_string.strip != "" - eval_string - else - _pry_.input_ring.to_a.reverse_each.find { |x| x && x.strip != "" } || "" + def probably_a_file?(str) + [".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) || + str =~ /\/|\\/ end end - def probably_a_file?(str) - [".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) || - str =~ /\/|\\/ - end + Pry::Commands.add_command(Pry::Command::Edit) end - - Pry::Commands.add_command(Pry::Command::Edit) end diff --git a/lib/pry/commands/edit/exception_patcher.rb b/lib/pry/commands/edit/exception_patcher.rb index 07e13bc9..8939b1b7 100644 --- a/lib/pry/commands/edit/exception_patcher.rb +++ b/lib/pry/commands/edit/exception_patcher.rb @@ -1,24 +1,26 @@ class Pry - class Command::Edit - class ExceptionPatcher - attr_accessor :_pry_ - attr_accessor :state - attr_accessor :file_and_line + class Command + class Edit + class ExceptionPatcher + attr_accessor :_pry_ + attr_accessor :state + attr_accessor :file_and_line - def initialize(_pry_, state, exception_file_and_line) - @_pry_ = _pry_ - @state = state - @file_and_line = exception_file_and_line - end + def initialize(_pry_, state, exception_file_and_line) + @_pry_ = _pry_ + @state = state + @file_and_line = exception_file_and_line + end - # perform the patch - def perform_patch - file_name, _ = file_and_line - lines = state.dynamical_ex_file || File.read(file_name) + # perform the patch + def perform_patch + file_name, _ = file_and_line + lines = state.dynamical_ex_file || File.read(file_name) - source = Pry::Editor.new(_pry_).edit_tempfile_with_content(lines) - _pry_.evaluate_ruby source - state.dynamical_ex_file = source.split("\n") + source = Pry::Editor.new(_pry_).edit_tempfile_with_content(lines) + _pry_.evaluate_ruby source + state.dynamical_ex_file = source.split("\n") + end end end end diff --git a/lib/pry/commands/edit/file_and_line_locator.rb b/lib/pry/commands/edit/file_and_line_locator.rb index 68a8bfaf..7dea02ed 100644 --- a/lib/pry/commands/edit/file_and_line_locator.rb +++ b/lib/pry/commands/edit/file_and_line_locator.rb @@ -1,38 +1,40 @@ class Pry - class Command::Edit - module FileAndLineLocator - class << self - def from_binding(target) - if target.respond_to?(:source_location) - target.source_location - else - [target.eval("__FILE__"), target.eval("__LINE__")] + class Command + class Edit + module FileAndLineLocator + class << self + def from_binding(target) + if target.respond_to?(:source_location) + target.source_location + else + [target.eval("__FILE__"), target.eval("__LINE__")] + end end - end - def from_code_object(code_object, filename_argument) - if File.exist?(code_object.source_file.to_s) - [code_object.source_file, code_object.source_line] - else - raise CommandError, "Cannot find a file for #{filename_argument}!" + def from_code_object(code_object, filename_argument) + if File.exist?(code_object.source_file.to_s) + [code_object.source_file, code_object.source_line] + else + raise CommandError, "Cannot find a file for #{filename_argument}!" + end end - end - def from_exception(exception, backtrace_level) - raise CommandError, "No exception found." if exception.nil? + def from_exception(exception, backtrace_level) + raise CommandError, "No exception found." if exception.nil? - file_name, line = exception.bt_source_location_for(backtrace_level) - raise CommandError, "Exception has no associated file." if file_name.nil? - raise CommandError, "Cannot edit exceptions raised in REPL." if Pry.eval_path == file_name + file_name, line = exception.bt_source_location_for(backtrace_level) + raise CommandError, "Exception has no associated file." if file_name.nil? + raise CommandError, "Cannot edit exceptions raised in REPL." if Pry.eval_path == file_name - [file_name, line] - end + [file_name, line] + end - # when file and line are passed as a single arg, e.g my_file.rb:30 - def from_filename_argument(filename_argument) - f = File.expand_path(filename_argument) - l = f.sub!(/:(\d+)$/, "") ? Regexp.last_match(1).to_i : 1 - [f, l] + # when file and line are passed as a single arg, e.g my_file.rb:30 + def from_filename_argument(filename_argument) + f = File.expand_path(filename_argument) + l = f.sub!(/:(\d+)$/, "") ? Regexp.last_match(1).to_i : 1 + [f, l] + end end end end diff --git a/lib/pry/commands/exit.rb b/lib/pry/commands/exit.rb index 4d5efba4..b1809380 100644 --- a/lib/pry/commands/exit.rb +++ b/lib/pry/commands/exit.rb @@ -1,43 +1,45 @@ class Pry - class Command::Exit < Pry::ClassCommand - match 'exit' - group 'Navigating Pry' - description 'Pop the previous binding.' - command_options keep_retval: true - - banner <<-'BANNER' - Usage: exit [OPTIONS] [--help] - Aliases: quit - - Pop the previous binding (does NOT exit program). It can be useful to exit a - context with a user-provided value. For instance an exit value can be used to - determine program flow. - - exit "pry this" - exit - - https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value - BANNER - - def process - if _pry_.binding_stack.one? - _pry_.run_command "exit-all #{arg_string}" - else - # otherwise just pop a binding and return user supplied value - process_pop_and_return + class Command + class Exit < Pry::ClassCommand + match 'exit' + group 'Navigating Pry' + description 'Pop the previous binding.' + command_options keep_retval: true + + banner <<-'BANNER' + Usage: exit [OPTIONS] [--help] + Aliases: quit + + Pop the previous binding (does NOT exit program). It can be useful to exit a + context with a user-provided value. For instance an exit value can be used to + determine program flow. + + exit "pry this" + exit + + https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value + BANNER + + def process + if _pry_.binding_stack.one? + _pry_.run_command "exit-all #{arg_string}" + else + # otherwise just pop a binding and return user supplied value + process_pop_and_return + end end - end - def process_pop_and_return - popped_object = _pry_.binding_stack.pop.eval('self') + def process_pop_and_return + popped_object = _pry_.binding_stack.pop.eval('self') - # return a user-specified value if given otherwise return the object - return target.eval(arg_string) unless arg_string.empty? + # return a user-specified value if given otherwise return the object + return target.eval(arg_string) unless arg_string.empty? - popped_object + popped_object + end end - end - Pry::Commands.add_command(Pry::Command::Exit) - Pry::Commands.alias_command 'quit', 'exit' + Pry::Commands.add_command(Pry::Command::Exit) + Pry::Commands.alias_command 'quit', 'exit' + end end diff --git a/lib/pry/commands/exit_all.rb b/lib/pry/commands/exit_all.rb index 1136de31..a650bdfa 100644 --- a/lib/pry/commands/exit_all.rb +++ b/lib/pry/commands/exit_all.rb @@ -1,29 +1,31 @@ class Pry - class Command::ExitAll < Pry::ClassCommand - match 'exit-all' - group 'Navigating Pry' - description 'End the current Pry session.' + class Command + class ExitAll < Pry::ClassCommand + match 'exit-all' + group 'Navigating Pry' + description 'End the current Pry session.' - banner <<-'BANNER' - Usage: exit-all [--help] - Aliases: !!@ + banner <<-'BANNER' + Usage: exit-all [--help] + Aliases: !!@ - End the current Pry session (popping all bindings and returning to caller). - Accepts optional return value. - BANNER + End the current Pry session (popping all bindings and returning to caller). + Accepts optional return value. + BANNER - def process - # calculate user-given value - exit_value = target.eval(arg_string) + def process + # calculate user-given value + exit_value = target.eval(arg_string) - # clear the binding stack - _pry_.binding_stack.clear + # clear the binding stack + _pry_.binding_stack.clear - # break out of the repl loop - throw(:breakout, exit_value) + # break out of the repl loop + throw(:breakout, exit_value) + end end - end - Pry::Commands.add_command(Pry::Command::ExitAll) - Pry::Commands.alias_command '!!@', 'exit-all' + Pry::Commands.add_command(Pry::Command::ExitAll) + Pry::Commands.alias_command '!!@', 'exit-all' + end end diff --git a/lib/pry/commands/exit_program.rb b/lib/pry/commands/exit_program.rb index a4d9168b..67291549 100644 --- a/lib/pry/commands/exit_program.rb +++ b/lib/pry/commands/exit_program.rb @@ -1,23 +1,25 @@ class Pry - class Command::ExitProgram < Pry::ClassCommand - match 'exit-program' - group 'Navigating Pry' - description 'End the current program.' + class Command + class ExitProgram < Pry::ClassCommand + match 'exit-program' + group 'Navigating Pry' + description 'End the current program.' - banner <<-'BANNER' - Usage: exit-program [--help] - Aliases: quit-program - !!! + banner <<-'BANNER' + Usage: exit-program [--help] + Aliases: quit-program + !!! - End the current program. - BANNER + End the current program. + BANNER - def process - Kernel.exit target.eval(arg_string).to_i + def process + Kernel.exit target.eval(arg_string).to_i + end end - end - Pry::Commands.add_command(Pry::Command::ExitProgram) - Pry::Commands.alias_command 'quit-program', 'exit-program' - Pry::Commands.alias_command '!!!', 'exit-program' + Pry::Commands.add_command(Pry::Command::ExitProgram) + Pry::Commands.alias_command 'quit-program', 'exit-program' + Pry::Commands.alias_command '!!!', 'exit-program' + end end diff --git a/lib/pry/commands/find_method.rb b/lib/pry/commands/find_method.rb index 51862992..f7adcac4 100644 --- a/lib/pry/commands/find_method.rb +++ b/lib/pry/commands/find_method.rb @@ -1,191 +1,193 @@ class Pry - class Command::FindMethod < Pry::ClassCommand - extend Pry::Helpers::BaseHelpers - - match 'find-method' - group 'Context' - description 'Recursively search for a method within a Class/Module or the current namespace.' - command_options shellwords: false - - banner <<-'BANNER' - Usage: find-method [-n|-c] METHOD [NAMESPACE] - - Recursively search for a method within a Class/Module or the current namespace. - Use the `-n` switch (the default) to search for methods whose name matches the - given regex. Use the `-c` switch to search for methods that contain the given - code. - - # Find all methods whose name match /re/ inside - # the Pry namespace. Matches Pry#repl, etc. - find-method re Pry - - # Find all methods that contain the code: - # output.puts inside the Pry namespace. - find-method -c 'output.puts' Pry - BANNER - - def options(opt) - opt.on :n, :name, "Search for a method by name" - opt.on :c, :content, "Search for a method based on content in Regex form" - end - - def process - return if args.size < 1 - - klass = search_class - - matches = opts.content? ? content_search(klass) : name_search(klass) - show_search_results(matches) - end + class Command + class FindMethod < Pry::ClassCommand + extend Pry::Helpers::BaseHelpers + + match 'find-method' + group 'Context' + description 'Recursively search for a method within a Class/Module or the current namespace.' + command_options shellwords: false + + banner <<-'BANNER' + Usage: find-method [-n|-c] METHOD [NAMESPACE] + + Recursively search for a method within a Class/Module or the current namespace. + Use the `-n` switch (the default) to search for methods whose name matches the + given regex. Use the `-c` switch to search for methods that contain the given + code. + + # Find all methods whose name match /re/ inside + # the Pry namespace. Matches Pry#repl, etc. + find-method re Pry + + # Find all methods that contain the code: + # output.puts inside the Pry namespace. + find-method -c 'output.puts' Pry + BANNER + + def options(opt) + opt.on :n, :name, "Search for a method by name" + opt.on :c, :content, "Search for a method based on content in Regex form" + end - private + def process + return if args.size < 1 - # @return [Regexp] The pattern to search for. - def pattern - @pattern ||= ::Regexp.new args[0] - end + klass = search_class - # Output the result of the search. - # - # @param [Array] matches - def show_search_results(matches) - if matches.empty? - output.puts bold("No Methods Matched") - else - print_matches(matches) + matches = opts.content? ? content_search(klass) : name_search(klass) + show_search_results(matches) end - end - # The class to search for methods. - # We only search classes, so if the search object is an - # instance, return its class. If no search object is given - # search `target_self`. - def search_class - klass = if args[1] - target.eval(args[1]) - else - target_self - end - - klass.is_a?(Module) ? klass : klass.class - end - - # pretty-print a list of matching methods. - # - # @param [Array<Method>] matches - def print_matches(matches) - grouped = matches.group_by(&:owner) - order = grouped.keys.sort_by { |x| x.name || x.to_s } + private - order.each do |klass| - print_matches_for_class(klass, grouped) + # @return [Regexp] The pattern to search for. + def pattern + @pattern ||= ::Regexp.new args[0] end - end - # Print matched methods for a class - def print_matches_for_class(klass, grouped) - output.puts bold(klass.name) - grouped[klass].each do |method| - header = method.name_with_owner - output.puts header + additional_info(header, method) + # Output the result of the search. + # + # @param [Array] matches + def show_search_results(matches) + if matches.empty? + output.puts bold("No Methods Matched") + else + print_matches(matches) + end end - end - # Return the matched lines of method source if `-c` is given or "" - # if `-c` was not given - def additional_info(header, method) - if opts.content? - ": " << colorize_code(matched_method_lines(header, method)) - else - "" + # The class to search for methods. + # We only search classes, so if the search object is an + # instance, return its class. If no search object is given + # search `target_self`. + def search_class + klass = if args[1] + target.eval(args[1]) + else + target_self + end + + klass.is_a?(Module) ? klass : klass.class end - end - def matched_method_lines(header, method) - method.source.split(/\n/).select { |x| x =~ pattern }.join("\n#{' ' * header.length}") - end + # pretty-print a list of matching methods. + # + # @param [Array<Method>] matches + def print_matches(matches) + grouped = matches.group_by(&:owner) + order = grouped.keys.sort_by { |x| x.name || x.to_s } - # Run the given block against every constant in the provided namespace. - # - # @param [Module] klass The namespace in which to start the search. - # @param [Hash<Module,Boolean>] done The namespaces we've already visited (private) - # @yieldparam klass Each class/module in the namespace. - # - def recurse_namespace(klass, done = {}, &block) - return if !(Module === klass) || done[klass] - - done[klass] = true - - yield klass - - klass.constants.each do |name| - next if klass.autoload?(name) - - begin - const = klass.const_get(name) - rescue RescuableException - # constant loading is an inexact science at the best of times, - # this often happens when a constant was .autoload? but someone - # tried to load it. It's now not .autoload? but will still raise - # a NameError when you access it. - else - recurse_namespace(const, done, &block) + order.each do |klass| + print_matches_for_class(klass, grouped) end end - end - # Gather all the methods in a namespace that pass the given block. - # - # @param [Module] namespace The namespace in which to search. - # @yieldparam [Method] method The method to test - # @yieldreturn [Boolean] - # @return [Array<Method>] - # - def search_all_methods(namespace) - done = Hash.new { |h, k| h[k] = {} } - matches = [] + # Print matched methods for a class + def print_matches_for_class(klass, grouped) + output.puts bold(klass.name) + grouped[klass].each do |method| + header = method.name_with_owner + output.puts header + additional_info(header, method) + end + end - recurse_namespace(namespace) do |klass| - (Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method| - next if done[method.owner][method.name] + # Return the matched lines of method source if `-c` is given or "" + # if `-c` was not given + def additional_info(header, method) + if opts.content? + ": " << colorize_code(matched_method_lines(header, method)) + else + "" + end + end - done[method.owner][method.name] = true + def matched_method_lines(header, method) + method.source.split(/\n/).select { |x| x =~ pattern }.join("\n#{' ' * header.length}") + end - matches << method if yield method + # Run the given block against every constant in the provided namespace. + # + # @param [Module] klass The namespace in which to start the search. + # @param [Hash<Module,Boolean>] done The namespaces we've already visited (private) + # @yieldparam klass Each class/module in the namespace. + # + def recurse_namespace(klass, done = {}, &block) + return if !(Module === klass) || done[klass] + + done[klass] = true + + yield klass + + klass.constants.each do |name| + next if klass.autoload?(name) + + begin + const = klass.const_get(name) + rescue RescuableException + # constant loading is an inexact science at the best of times, + # this often happens when a constant was .autoload? but someone + # tried to load it. It's now not .autoload? but will still raise + # a NameError when you access it. + else + recurse_namespace(const, done, &block) + end end end - matches - end + # Gather all the methods in a namespace that pass the given block. + # + # @param [Module] namespace The namespace in which to search. + # @yieldparam [Method] method The method to test + # @yieldreturn [Boolean] + # @return [Array<Method>] + # + def search_all_methods(namespace) + done = Hash.new { |h, k| h[k] = {} } + matches = [] + + recurse_namespace(namespace) do |klass| + (Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method| + next if done[method.owner][method.name] + + done[method.owner][method.name] = true + + matches << method if yield method + end + end - # Search for all methods with a name that matches the given regex - # within a namespace. - # - # @param [Module] namespace The namespace to search - # @return [Array<Method>] - # - def name_search(namespace) - search_all_methods(namespace) do |meth| - meth.name =~ pattern + matches + end + + # Search for all methods with a name that matches the given regex + # within a namespace. + # + # @param [Module] namespace The namespace to search + # @return [Array<Method>] + # + def name_search(namespace) + search_all_methods(namespace) do |meth| + meth.name =~ pattern + end end - end - # Search for all methods who's implementation matches the given regex - # within a namespace. - # - # @param [Module] namespace The namespace to search - # @return [Array<Method>] - # - def content_search(namespace) - search_all_methods(namespace) do |meth| - begin - meth.source =~ pattern - rescue RescuableException - false + # Search for all methods who's implementation matches the given regex + # within a namespace. + # + # @param [Module] namespace The namespace to search + # @return [Array<Method>] + # + def content_search(namespace) + search_all_methods(namespace) do |meth| + begin + meth.source =~ pattern + rescue RescuableException + false + end end end end - end - Pry::Commands.add_command(Pry::Command::FindMethod) + Pry::Commands.add_command(Pry::Command::FindMethod) + end end diff --git a/lib/pry/commands/fix_indent.rb b/lib/pry/commands/fix_indent.rb index 75c48c74..0a62fbe1 100644 --- a/lib/pry/commands/fix_indent.rb +++ b/lib/pry/commands/fix_indent.rb @@ -1,19 +1,21 @@ class Pry - class Command::FixIndent < Pry::ClassCommand - match 'fix-indent' - group 'Input and Output' + class Command + class FixIndent < Pry::ClassCommand + match 'fix-indent' + group 'Input and Output' - description "Correct the indentation for contents of the input buffer" + description "Correct the indentation for contents of the input buffer" - banner <<-USAGE - Usage: fix-indent - USAGE + banner <<-USAGE + Usage: fix-indent + USAGE - def process - indented_str = Pry::Indent.indent(eval_string) - eval_string.replace indented_str + def process + indented_str = Pry::Indent.indent(eval_string) + eval_string.replace indented_str + end end - end - Pry::Commands.add_command(Pry::Command::FixIndent) + Pry::Commands.add_command(Pry::Command::FixIndent) + end end diff --git a/lib/pry/commands/gem_cd.rb b/lib/pry/commands/gem_cd.rb index fb4d9f1e..5a247b50 100644 --- a/lib/pry/commands/gem_cd.rb +++ b/lib/pry/commands/gem_cd.rb @@ -1,26 +1,28 @@ class Pry - class Command::GemCd < Pry::ClassCommand - match 'gem-cd' - group 'Gems' - description "Change working directory to specified gem's directory." - command_options argument_required: true + class Command + class GemCd < Pry::ClassCommand + match 'gem-cd' + group 'Gems' + description "Change working directory to specified gem's directory." + command_options argument_required: true - banner <<-'BANNER' - Usage: gem-cd GEM_NAME + banner <<-'BANNER' + Usage: gem-cd GEM_NAME - Change the current working directory to that in which the given gem is - installed. - BANNER + Change the current working directory to that in which the given gem is + installed. + BANNER - def process(gem) - Dir.chdir(Rubygem.spec(gem).full_gem_path) - output.puts(Dir.pwd) - end + def process(gem) + Dir.chdir(Rubygem.spec(gem).full_gem_path) + output.puts(Dir.pwd) + end - def complete(str) - Rubygem.complete(str) + def complete(str) + Rubygem.complete(str) + end end - end - Pry::Commands.add_command(Pry::Command::GemCd) + Pry::Commands.add_command(Pry::Command::GemCd) + end end diff --git a/lib/pry/commands/gem_install.rb b/lib/pry/commands/gem_install.rb index b19498c7..7753e388 100644 --- a/lib/pry/commands/gem_install.rb +++ b/lib/pry/commands/gem_install.rb @@ -1,32 +1,34 @@ class Pry - class Command::GemInstall < Pry::ClassCommand - match 'gem-install' - group 'Gems' - description 'Install a gem and refresh the gem cache.' - command_options argument_required: true + class Command + class GemInstall < Pry::ClassCommand + match 'gem-install' + group 'Gems' + description 'Install a gem and refresh the gem cache.' + command_options argument_required: true - banner <<-'BANNER' - Usage: gem-install GEM_NAME + banner <<-'BANNER' + Usage: gem-install GEM_NAME - Installs the given gem, refreshes the gem cache, and requires the gem for you - based on a best guess from the gem name. + Installs the given gem, refreshes the gem cache, and requires the gem for you + based on a best guess from the gem name. - gem-install pry-stack_explorer - BANNER + gem-install pry-stack_explorer + BANNER - def setup - require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller - end + def setup + require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller + end - def process(gem) - Rubygem.install(gem) - output.puts "Gem `#{green(gem)}` installed." - require gem - rescue LoadError - require_path = gem.split('-').join('/') - require require_path + def process(gem) + Rubygem.install(gem) + output.puts "Gem `#{green(gem)}` installed." + require gem + rescue LoadError + require_path = gem.split('-').join('/') + require require_path + end end - end - Pry::Commands.add_command(Pry::Command::GemInstall) + Pry::Commands.add_command(Pry::Command::GemInstall) + end end diff --git a/lib/pry/commands/gem_list.rb b/lib/pry/commands/gem_list.rb index f7d9bae1..97df9110 100644 --- a/lib/pry/commands/gem_list.rb +++ b/lib/pry/commands/gem_list.rb @@ -1,33 +1,35 @@ class Pry - class Command::GemList < Pry::ClassCommand - match 'gem-list' - group 'Gems' - description 'List and search installed gems.' + class Command + class GemList < Pry::ClassCommand + match 'gem-list' + group 'Gems' + description 'List and search installed gems.' - banner <<-'BANNER' - Usage: gem-list [REGEX] + banner <<-'BANNER' + Usage: gem-list [REGEX] - List all installed gems, when a regex is provided, limit the output to those - that match the regex. - BANNER + List all installed gems, when a regex is provided, limit the output to those + that match the regex. + BANNER - def process(pattern = nil) - pattern = Regexp.compile(pattern || '') - gems = Rubygem.list(pattern).group_by(&:name) + def process(pattern = nil) + pattern = Regexp.compile(pattern || '') + gems = Rubygem.list(pattern).group_by(&:name) - gems.each do |gem, specs| - specs.sort! do |a, b| - Gem::Version.new(b.version) <=> Gem::Version.new(a.version) - end + gems.each do |gem, specs| + specs.sort! do |a, b| + Gem::Version.new(b.version) <=> Gem::Version.new(a.version) + end - versions = specs.each_with_index.map do |spec, index| - index == 0 ? bright_green(spec.version.to_s) : green(spec.version.to_s) - end + versions = specs.each_with_index.map do |spec, index| + index == 0 ? bright_green(spec.version.to_s) : green(spec.version.to_s) + end - output.puts "#{default gem} (#{versions.join ', '})" + output.puts "#{default gem} (#{versions.join ', '})" + end end end - end - Pry::Commands.add_command(Pry::Command::GemList) + Pry::Commands.add_command(Pry::Command::GemList) + end end diff --git a/lib/pry/commands/gem_open.rb b/lib/pry/commands/gem_open.rb index 79b0d9db..c1837039 100644 --- a/lib/pry/commands/gem_open.rb +++ b/lib/pry/commands/gem_open.rb @@ -1,29 +1,31 @@ class Pry - class Command::GemOpen < Pry::ClassCommand - match 'gem-open' - group 'Gems' - description 'Opens the working directory of the gem in your editor.' - command_options argument_required: true + class Command + class GemOpen < Pry::ClassCommand + match 'gem-open' + group 'Gems' + description 'Opens the working directory of the gem in your editor.' + command_options argument_required: true - banner <<-'BANNER' - Usage: gem-open GEM_NAME + banner <<-'BANNER' + Usage: gem-open GEM_NAME - Change the current working directory to that in which the given gem is - installed, and then opens your text editor. + Change the current working directory to that in which the given gem is + installed, and then opens your text editor. - gem-open pry-exception_explorer - BANNER + gem-open pry-exception_explorer + BANNER - def process(gem) - Dir.chdir(Rubygem.spec(gem).full_gem_path) do - Pry::Editor.new(_pry_).invoke_editor(".", 0, false) + def process(gem) + Dir.chdir(Rubygem.spec(gem).full_gem_path) do + Pry::Editor.new(_pry_).invoke_editor(".", 0, false) + end end - end - def complete(str) - Rubygem.complete(str) + def complete(str) + Rubygem.complete(str) + end end - end - Pry::Commands.add_command(Pry::Command::GemOpen) + Pry::Commands.add_command(Pry::Command::GemOpen) + end end diff --git a/lib/pry/commands/gem_readme.rb b/lib/pry/commands/gem_readme.rb index 5e1ade40..c113e5cc 100644 --- a/lib/pry/commands/gem_readme.rb +++ b/lib/pry/commands/gem_readme.rb @@ -1,25 +1,29 @@ -class Pry::Command::GemReadme < Pry::ClassCommand - match 'gem-readme' - description 'Show the readme bundled with a rubygem' - group 'Gems' - command_options argument_required: true - banner <<-BANNER - gem-readme gem - Show the readme bundled with a rubygem - BANNER +class Pry + class Command + class GemReadme < Pry::ClassCommand + match 'gem-readme' + description 'Show the readme bundled with a rubygem' + group 'Gems' + command_options argument_required: true + banner <<-BANNER + gem-readme gem + Show the readme bundled with a rubygem + BANNER - def process(name) - spec = Gem::Specification.find_by_name(name) - glob = File.join(spec.full_gem_path, 'README*') - readme = Dir[glob][0] - if File.exist?(readme.to_s) - _pry_.pager.page File.read(readme) - else - raise Pry::CommandError, "Gem '#{name}' doesn't appear to have a README" + def process(name) + spec = Gem::Specification.find_by_name(name) + glob = File.join(spec.full_gem_path, 'README*') + readme = Dir[glob][0] + if File.exist?(readme.to_s) + _pry_.pager.page File.read(readme) + else + raise Pry::CommandError, "Gem '#{name}' doesn't appear to have a README" + end + rescue Gem::LoadError + raise Pry::CommandError, "Gem '#{name}' wasn't found. Are you sure it is installed?" + end + + Pry::Commands.add_command(self) end - rescue Gem::LoadError - raise Pry::CommandError, "Gem '#{name}' wasn't found. Are you sure it is installed?" end - - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/gem_search.rb b/lib/pry/commands/gem_search.rb index 127781e2..4151d9d2 100644 --- a/lib/pry/commands/gem_search.rb +++ b/lib/pry/commands/gem_search.rb @@ -1,41 +1,45 @@ -class Pry::Command::GemSearch < Pry::ClassCommand - match 'gem-search' - description 'Search for a gem with the rubygems.org JSON API' - group 'Gems' - command_options argument_required: true - banner <<-BANNER - gem-search [options] gem - Search for a gem with the rubygems.org HTTP API - BANNER +class Pry + class Command + class GemSearch < Pry::ClassCommand + match 'gem-search' + description 'Search for a gem with the rubygems.org JSON API' + group 'Gems' + command_options argument_required: true + banner <<-BANNER + gem-search [options] gem + Search for a gem with the rubygems.org HTTP API + BANNER - API_ENDPOINT = 'https://rubygems.org/api/v1/search.json' + API_ENDPOINT = 'https://rubygems.org/api/v1/search.json' - def setup - require 'json' unless defined?(JSON) - require 'net/http' unless defined?(Net::HTTP) - end + def setup + require 'json' unless defined?(JSON) + require 'net/http' unless defined?(Net::HTTP) + end - def options(opt) - opt.on :l, :limit, 'Limit the number of results (max: 30)', - default: 10, - as: Integer, - argument: true - end + def options(opt) + opt.on :l, :limit, 'Limit the number of results (max: 30)', + default: 10, + as: Integer, + argument: true + end - def process(str) - uri = URI.parse(API_ENDPOINT) - uri.query = URI.encode_www_form(query: str) - gems = JSON.parse(Net::HTTP.get(uri)) - _pry_.pager.page list_as_string(gems, opts[:limit]) - end + def process(str) + uri = URI.parse(API_ENDPOINT) + uri.query = URI.encode_www_form(query: str) + gems = JSON.parse(Net::HTTP.get(uri)) + _pry_.pager.page list_as_string(gems, opts[:limit]) + end - private + private - def list_as_string(gems, limit = 10) - gems[0..limit - 1].map do |gem| - name, version, info = gem.values_at 'name', 'version', 'info' - "#{bold(name)} #{bold('v' + version)} \n#{info}\n\n" - end.join + def list_as_string(gems, limit = 10) + gems[0..limit - 1].map do |gem| + name, version, info = gem.values_at 'name', 'version', 'info' + "#{bold(name)} #{bold('v' + version)} \n#{info}\n\n" + end.join + end + Pry::Commands.add_command(self) + end end - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/gem_stats.rb b/lib/pry/commands/gem_stats.rb index ac8fdd0c..7d91be68 100644 --- a/lib/pry/commands/gem_stats.rb +++ b/lib/pry/commands/gem_stats.rb @@ -1,10 +1,12 @@ -class Pry::Command::GemStat < Pry::ClassCommand - require 'json' - require 'net/http' - STAT_HOST = "rubygems.org" - STAT_PORT = 443 - STAT_PATH = "/api/v1/gems/%s.json" - FAIL_WHALE = <<-FAILWHALE +class Pry + class Command + class GemStat < Pry::ClassCommand + require 'json' + require 'net/http' + STAT_HOST = "rubygems.org" + STAT_PORT = 443 + STAT_PATH = "/api/v1/gems/%s.json" + FAIL_WHALE = <<-FAILWHALE W W W W W W W '. W @@ -14,73 +16,75 @@ W W W W \'-.__, .__.,' `'----'._\--' VVVVVVVVVVVVVVVVVVVVV - FAILWHALE + FAILWHALE - match 'gem-stat' - description 'Show the statistics of a gem (requires internet connection)' - group 'Gems' - command_options argument_required: true - banner <<-BANNER - gem-stats name + match 'gem-stat' + description 'Show the statistics of a gem (requires internet connection)' + group 'Gems' + command_options argument_required: true + banner <<-BANNER + gem-stats name - Show the statistics of a gem. - Requires an internet connection. - BANNER + Show the statistics of a gem. + Requires an internet connection. + BANNER - def process(name) - client = Net::HTTP.start STAT_HOST, STAT_PORT, use_ssl: true - res = client.get STAT_PATH % URI.encode_www_form_component(name) - case res - when Net::HTTPOK - _pry_.pager.page format_gem(JSON.parse(res.body)) - when Net::HTTPServiceUnavailable - _pry_.pager.page <<-FAILURE + def process(name) + client = Net::HTTP.start STAT_HOST, STAT_PORT, use_ssl: true + res = client.get STAT_PATH % URI.encode_www_form_component(name) + case res + when Net::HTTPOK + _pry_.pager.page format_gem(JSON.parse(res.body)) + when Net::HTTPServiceUnavailable + _pry_.pager.page <<-FAILURE #{bright_blue(FAIL_WHALE)} #{bright_red('Ruby On Rails')} #{bright_red('Net::HTTPServiceUnavailable')} - FAILURE - else - raise Pry::CommandError, "Bad response (#{res.class})" - end - ensure - client.finish if client - end + FAILURE + else + raise Pry::CommandError, "Bad response (#{res.class})" + end + ensure + client.finish if client + end - private + private - def format_gem(h) - h = Pry::Config.from_hash(h) - format_str = unindent <<-FORMAT - %{name} %{version} - -- - Total Downloads : %{downloads} - Version Downloads : %{version_downloads} + def format_gem(h) + h = Pry::Config.from_hash(h) + format_str = unindent <<-FORMAT + %{name} %{version} + -- + Total Downloads : %{downloads} + Version Downloads : %{version_downloads} - #{red('Dependencies')} (runtime) - -- - %{rdependencies} + #{red('Dependencies')} (runtime) + -- + %{rdependencies} - #{red('Dependencies')} (development) - %{ddependencies} - FORMAT - format_str % { - name: green(h.name), - version: bold("v#{h.version}"), - downloads: h.downloads, - version_downloads: h.version_downloads, - rdependencies: format_dependencies(h.dependencies.runtime), - ddependencies: format_dependencies(h.dependencies.development) - } - end + #{red('Dependencies')} (development) + %{ddependencies} + FORMAT + format_str % { + name: green(h.name), + version: bold("v#{h.version}"), + downloads: h.downloads, + version_downloads: h.version_downloads, + rdependencies: format_dependencies(h.dependencies.runtime), + ddependencies: format_dependencies(h.dependencies.development) + } + end - def format_dependencies(rdeps) - return bold('None') if rdeps.empty? + def format_dependencies(rdeps) + return bold('None') if rdeps.empty? - with_line_numbers( - rdeps.map { |h| "#{h['name']} (#{h['requirements']})" }.join("\n"), - 1, - :bold - ) + with_line_numbers( + rdeps.map { |h| "#{h['name']} (#{h['requirements']})" }.join("\n"), + 1, + :bold + ) + end + Pry::Commands.add_command(self) + end end - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/gist.rb b/lib/pry/commands/gist.rb index ef2b5e0a..7ce590cf 100644 --- a/lib/pry/commands/gist.rb +++ b/lib/pry/commands/gist.rb @@ -1,102 +1,104 @@ class Pry - class Command::Gist < Pry::ClassCommand - match 'gist' - group 'Misc' - description 'Upload code, docs, history to https://gist.github.com/.' - command_options requires_gem: "gist" + class Command + class Gist < Pry::ClassCommand + match 'gist' + group 'Misc' + description 'Upload code, docs, history to https://gist.github.com/.' + command_options requires_gem: "gist" - banner <<-'BANNER' - Usage: gist [OPTIONS] [--help] + banner <<-'BANNER' + Usage: gist [OPTIONS] [--help] - The gist command enables you to gist code from files and methods to github. + The gist command enables you to gist code from files and methods to github. - gist -i 20 --lines 1..3 - gist Pry#repl --lines 1..-1 - gist Rakefile --lines 5 - BANNER + gist -i 20 --lines 1..3 + gist Pry#repl --lines 1..-1 + gist Rakefile --lines 5 + BANNER - def setup - require 'gist' - end + def setup + require 'gist' + end - def options(opt) - CodeCollector.inject_options(opt) - opt.on :login, "Authenticate the gist gem with GitHub" - opt.on :p, :public, "Create a public gist (default: false)", default: false - opt.on :clip, "Copy the selected content to clipboard instead, do NOT gist it", default: false - end + def options(opt) + CodeCollector.inject_options(opt) + opt.on :login, "Authenticate the gist gem with GitHub" + opt.on :p, :public, "Create a public gist (default: false)", default: false + opt.on :clip, "Copy the selected content to clipboard instead, do NOT gist it", default: false + end - def process - return ::Gist.login! if opts.present?(:login) + def process + return ::Gist.login! if opts.present?(:login) - cc = CodeCollector.new(args, opts, _pry_) + cc = CodeCollector.new(args, opts, _pry_) - if cc.content =~ /\A\s*\z/ - raise CommandError, "Found no code to gist." - end + if cc.content =~ /\A\s*\z/ + raise CommandError, "Found no code to gist." + end - if opts.present?(:clip) - clipboard_content(cc.content) - else - # we're overriding the default behavior of the 'in' option (as - # defined on CodeCollector) with our local behaviour. - content = opts.present?(:in) ? input_content : cc.content - gist_content content, cc.file + if opts.present?(:clip) + clipboard_content(cc.content) + else + # we're overriding the default behavior of the 'in' option (as + # defined on CodeCollector) with our local behaviour. + content = opts.present?(:in) ? input_content : cc.content + gist_content content, cc.file + end end - end - def clipboard_content(content) - ::Gist.copy(content) - output.puts "Copied content to clipboard!" - end + def clipboard_content(content) + ::Gist.copy(content) + output.puts "Copied content to clipboard!" + end - def input_content - content = "" - CodeCollector.input_expression_ranges.each do |range| - input_expressions = _pry_.input_ring[range] || [] - Array(input_expressions).each_with_index do |code, index| - corrected_index = index + range.first - if code && code != "" - content << code - if code !~ /;\Z/ - content << "#{comment_expression_result_for_gist(_pry_.config.gist.inspecter.call(_pry_.output_ring[corrected_index]))}" + def input_content + content = "" + CodeCollector.input_expression_ranges.each do |range| + input_expressions = _pry_.input_ring[range] || [] + Array(input_expressions).each_with_index do |code, index| + corrected_index = index + range.first + if code && code != "" + content << code + if code !~ /;\Z/ + content << "#{comment_expression_result_for_gist(_pry_.config.gist.inspecter.call(_pry_.output_ring[corrected_index]))}" + end end end end - end - content - end + content + end - def comment_expression_result_for_gist(result) - content = "" - result.lines.each_with_index do |line, index| - if index == 0 - content << "# => #{line}" - else - content << "# #{line}" + def comment_expression_result_for_gist(result) + content = "" + result.lines.each_with_index do |line, index| + if index == 0 + content << "# => #{line}" + else + content << "# #{line}" + end end + + content end - content - end + def gist_content(content, filename) + response = ::Gist.gist(content, filename: filename || "pry_gist.rb", public: !!opts[:p]) + if response + url = response['html_url'] + message = "Gist created at URL #{url}" + begin + ::Gist.copy(url) + message << ", which is now in the clipboard." + rescue ::Gist::ClipboardError + end - def gist_content(content, filename) - response = ::Gist.gist(content, filename: filename || "pry_gist.rb", public: !!opts[:p]) - if response - url = response['html_url'] - message = "Gist created at URL #{url}" - begin - ::Gist.copy(url) - message << ", which is now in the clipboard." - rescue ::Gist::ClipboardError + output.puts message end - - output.puts message end end - end - Pry::Commands.add_command(Pry::Command::Gist) - Pry::Commands.alias_command 'clipit', 'gist --clip' + Pry::Commands.add_command(Pry::Command::Gist) + Pry::Commands.alias_command 'clipit', 'gist --clip' + end end diff --git a/lib/pry/commands/help.rb b/lib/pry/commands/help.rb index 7be69b19..512ef3e6 100644 --- a/lib/pry/commands/help.rb +++ b/lib/pry/commands/help.rb @@ -1,164 +1,166 @@ class Pry - class Command::Help < Pry::ClassCommand - match 'help' - group 'Help' - description 'Show a list of commands or information about a specific command.' - - banner <<-'BANNER' - Usage: help [COMMAND] - - With no arguments, help lists all the available commands along with their - descriptions. When given a command name as an argument, shows the help - for that command. - BANNER - - # We only want to show commands that have descriptions, so that the - # easter eggs don't show up. - def visible_commands - visible = {} - commands.each do |key, command| - visible[key] = command if command.description && !command.description.empty? + class Command + class Help < Pry::ClassCommand + match 'help' + group 'Help' + description 'Show a list of commands or information about a specific command.' + + banner <<-'BANNER' + Usage: help [COMMAND] + + With no arguments, help lists all the available commands along with their + descriptions. When given a command name as an argument, shows the help + for that command. + BANNER + + # We only want to show commands that have descriptions, so that the + # easter eggs don't show up. + def visible_commands + visible = {} + commands.each do |key, command| + visible[key] = command if command.description && !command.description.empty? + end + visible end - visible - end - # Get a hash of available commands grouped by the "group" name. - def command_groups - visible_commands.values.group_by(&:group) - end + # Get a hash of available commands grouped by the "group" name. + def command_groups + visible_commands.values.group_by(&:group) + end - def process - if args.empty? - display_index(command_groups) - else - display_search(args.first) + def process + if args.empty? + display_index(command_groups) + else + display_search(args.first) + end end - end - # Display the index view, with headings and short descriptions per command. - # - # @param [Hash<String, Array<Commands>>] groups - def display_index(groups) - help_text = [] + # Display the index view, with headings and short descriptions per command. + # + # @param [Hash<String, Array<Commands>>] groups + def display_index(groups) + help_text = [] - sorted_group_names(groups).each do |group_name| - commands = sorted_commands(groups[group_name]) + sorted_group_names(groups).each do |group_name| + commands = sorted_commands(groups[group_name]) - if commands.any? - help_text << help_text_for_commands(group_name, commands) + if commands.any? + help_text << help_text_for_commands(group_name, commands) + end end - end - _pry_.pager.page help_text.join("\n\n") - end + _pry_.pager.page help_text.join("\n\n") + end - # Given a group name and an array of commands, - # return the help string for those commands. - # - # @param [String] name The group name. - # @param [Array<Pry::Command>] commands - # @return [String] The generated help string. - def help_text_for_commands(name, commands) - "#{bold(name.capitalize)}\n" << commands.map do |command| - " #{command.options[:listing].to_s.ljust(18)} #{command.description.capitalize}" - end.join("\n") - end + # Given a group name and an array of commands, + # return the help string for those commands. + # + # @param [String] name The group name. + # @param [Array<Pry::Command>] commands + # @return [String] The generated help string. + def help_text_for_commands(name, commands) + "#{bold(name.capitalize)}\n" << commands.map do |command| + " #{command.options[:listing].to_s.ljust(18)} #{command.description.capitalize}" + end.join("\n") + end - # @param [Hash] groups - # @return [Array<String>] An array of sorted group names. - def sorted_group_names(groups) - groups.keys.sort_by(&method(:group_sort_key)) - end + # @param [Hash] groups + # @return [Array<String>] An array of sorted group names. + def sorted_group_names(groups) + groups.keys.sort_by(&method(:group_sort_key)) + end - # Sort an array of commands by their `listing` name. - # - # @param [Array<Pry::Command>] commands The commands to sort - # @return [Array<Pry::Command>] commands sorted by listing name. - def sorted_commands(commands) - commands.sort_by { |command| command.options[:listing].to_s } - end + # Sort an array of commands by their `listing` name. + # + # @param [Array<Pry::Command>] commands The commands to sort + # @return [Array<Pry::Command>] commands sorted by listing name. + def sorted_commands(commands) + commands.sort_by { |command| command.options[:listing].to_s } + end - # Display help for an individual command or group. - # - # @param [String] search The string to search for. - def display_search(search) - if (command = command_set.find_command_for_help(search)) - display_command(command) - else - display_filtered_search_results(search) + # Display help for an individual command or group. + # + # @param [String] search The string to search for. + def display_search(search) + if (command = command_set.find_command_for_help(search)) + display_command(command) + else + display_filtered_search_results(search) + end end - end - # Display help for a searched item, filtered first by group - # and if that fails, filtered by command name. - # - # @param [String] search The string to search for. - def display_filtered_search_results(search) - groups = search_hash(search, command_groups) - - if groups.size > 0 - display_index(groups) - else - display_filtered_commands(search) + # Display help for a searched item, filtered first by group + # and if that fails, filtered by command name. + # + # @param [String] search The string to search for. + def display_filtered_search_results(search) + groups = search_hash(search, command_groups) + + if groups.size > 0 + display_index(groups) + else + display_filtered_commands(search) + end end - end - # Display help for a searched item, filtered by group - # - # @param [String] search The string to search for. - def display_filtered_commands(search) - filtered = search_hash(search, visible_commands) - raise CommandError, "No help found for '#{args.first}'" if filtered.empty? - - if filtered.size == 1 - display_command(filtered.values.first) - else - display_index("'#{search}' commands" => filtered.values) + # Display help for a searched item, filtered by group + # + # @param [String] search The string to search for. + def display_filtered_commands(search) + filtered = search_hash(search, visible_commands) + raise CommandError, "No help found for '#{args.first}'" if filtered.empty? + + if filtered.size == 1 + display_command(filtered.values.first) + else + display_index("'#{search}' commands" => filtered.values) + end end - end - # Display help for an individual command. - # - # @param [Pry::Command] command - def display_command(command) - _pry_.pager.page command.new.help - end + # Display help for an individual command. + # + # @param [Pry::Command] command + def display_command(command) + _pry_.pager.page command.new.help + end - # Find a subset of a hash that matches the user's search term. - # - # If there's an exact match a Hash of one element will be returned, - # otherwise a sub-Hash with every key that matches the search will - # be returned. - # - # @param [String] search the search term - # @param [Hash] hash the hash to search - def search_hash(search, hash) - matching = {} - - hash.each_pair do |key, value| - next unless key.is_a?(String) - if normalize(key) == normalize(search) - return { key => value } - elsif normalize(key).start_with?(normalize(search)) - matching[key] = value + # Find a subset of a hash that matches the user's search term. + # + # If there's an exact match a Hash of one element will be returned, + # otherwise a sub-Hash with every key that matches the search will + # be returned. + # + # @param [String] search the search term + # @param [Hash] hash the hash to search + def search_hash(search, hash) + matching = {} + + hash.each_pair do |key, value| + next unless key.is_a?(String) + if normalize(key) == normalize(search) + return { key => value } + elsif normalize(key).start_with?(normalize(search)) + matching[key] = value + end end + + matching end - matching - end + # Clean search terms to make it easier to search group names + # + # @param [String] key + # @return [String] + def normalize(key) + key.downcase.gsub(/pry\W+/, '') + end - # Clean search terms to make it easier to search group names - # - # @param [String] key - # @return [String] - def normalize(key) - key.downcase.gsub(/pry\W+/, '') + def group_sort_key(group_name) + [%w(Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands).index(group_name.tr(' ', '_')) || 99, group_name] + end end - def group_sort_key(group_name) - [%w(Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands).index(group_name.tr(' ', '_')) || 99, group_name] - end + Pry::Commands.add_command(Pry::Command::Help) end - - Pry::Commands.add_command(Pry::Command::Help) end diff --git a/lib/pry/commands/hist.rb b/lib/pry/commands/hist.rb index 49aa7774..b37fd97e 100644 --- a/lib/pry/commands/hist.rb +++ b/lib/pry/commands/hist.rb @@ -1,181 +1,183 @@ class Pry - class Command::Hist < Pry::ClassCommand - match 'hist' - group 'Editing' - description 'Show and replay Readline history.' - - banner <<-'BANNER' - Usage: hist [--head|--tail] - hist --all - hist --head N - hist --tail N - hist --show START..END - hist --grep PATTERN - hist --clear - hist --replay START..END - hist --save [START..END] FILE - Aliases: history - - Show and replay Readline history. - BANNER - - def options(opt) - opt.on :a, :all, "Display all history" - opt.on :H, :head, "Display the first N items", optional_argument: true, as: Integer - opt.on :T, :tail, "Display the last N items", optional_argument: true, as: Integer - opt.on :s, :show, "Show the given range of lines", optional_argument: true, as: Range - opt.on :G, :grep, "Show lines matching the given pattern", argument: true, as: String - opt.on :c, :clear, "Clear the current session's history" - opt.on :r, :replay, "Replay a line or range of lines", argument: true, as: Range - opt.on :save, "Save history to a file", argument: true, as: Range - opt.on :e, :'exclude-pry', "Exclude Pry commands from the history" - opt.on :n, :'no-numbers', "Omit line numbers" - end - - def process - @history = find_history - - if opts.present?(:show) - @history = @history.between(opts[:show]) + class Command + class Hist < Pry::ClassCommand + match 'hist' + group 'Editing' + description 'Show and replay Readline history.' + + banner <<-'BANNER' + Usage: hist [--head|--tail] + hist --all + hist --head N + hist --tail N + hist --show START..END + hist --grep PATTERN + hist --clear + hist --replay START..END + hist --save [START..END] FILE + Aliases: history + + Show and replay Readline history. + BANNER + + def options(opt) + opt.on :a, :all, "Display all history" + opt.on :H, :head, "Display the first N items", optional_argument: true, as: Integer + opt.on :T, :tail, "Display the last N items", optional_argument: true, as: Integer + opt.on :s, :show, "Show the given range of lines", optional_argument: true, as: Range + opt.on :G, :grep, "Show lines matching the given pattern", argument: true, as: String + opt.on :c, :clear, "Clear the current session's history" + opt.on :r, :replay, "Replay a line or range of lines", argument: true, as: Range + opt.on :save, "Save history to a file", argument: true, as: Range + opt.on :e, :'exclude-pry', "Exclude Pry commands from the history" + opt.on :n, :'no-numbers', "Omit line numbers" end - if opts.present?(:grep) - @history = @history.grep(opts[:grep]) - end + def process + @history = find_history - @history = - case - when opts.present?(:head) - @history.take_lines(1, opts[:head] || 10) - when opts.present?(:tail) - @history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10) - when opts.present?(:show) - @history.between(opts[:show]) - else - @history + if opts.present?(:show) + @history = @history.between(opts[:show]) end - if opts.present?(:'exclude-pry') - @history = @history.select do |loc| - !command_set.valid_command?(loc.line) + if opts.present?(:grep) + @history = @history.grep(opts[:grep]) end - end - - if opts.present?(:save) - process_save - elsif opts.present?(:clear) - process_clear - elsif opts.present?(:replay) - process_replay - else - process_display - end - end - private + @history = + case + when opts.present?(:head) + @history.take_lines(1, opts[:head] || 10) + when opts.present?(:tail) + @history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10) + when opts.present?(:show) + @history.between(opts[:show]) + else + @history + end - def process_display - unless opts.present?(:'no-numbers') - @history = @history.with_line_numbers - end + if opts.present?(:'exclude-pry') + @history = @history.select do |loc| + !command_set.valid_command?(loc.line) + end + end - _pry_.pager.open do |pager| - @history.print_to_output(pager, true) + if opts.present?(:save) + process_save + elsif opts.present?(:clear) + process_clear + elsif opts.present?(:replay) + process_replay + else + process_display + end end - end - def process_save - case opts[:save] - when Range - @history = @history.between(opts[:save]) + private - unless args.first - raise CommandError, "Must provide a file name." + def process_display + unless opts.present?(:'no-numbers') + @history = @history.with_line_numbers end - file_name = File.expand_path(args.first) - when String - file_name = File.expand_path(opts[:save]) + _pry_.pager.open do |pager| + @history.print_to_output(pager, true) + end end - output.puts "Saving history in #{file_name}..." + def process_save + case opts[:save] + when Range + @history = @history.between(opts[:save]) - File.open(file_name, 'w') { |f| f.write(@history.raw) } + unless args.first + raise CommandError, "Must provide a file name." + end - output.puts "History saved." - end + file_name = File.expand_path(args.first) + when String + file_name = File.expand_path(opts[:save]) + end - def process_clear - Pry.history.clear - output.puts "History cleared." - end + output.puts "Saving history in #{file_name}..." - def process_replay - @history = @history.between(opts[:r]) - replay_sequence = @history.raw + File.open(file_name, 'w') { |f| f.write(@history.raw) } - # If we met follow-up "hist" call, check for the "--replay" option - # presence. If "hist" command is called with other options, proceed - # further. - check_for_juxtaposed_replay(replay_sequence) + output.puts "History saved." + end - replay_sequence.lines.each do |line| - _pry_.eval line, generated: true + def process_clear + Pry.history.clear + output.puts "History cleared." end - end - # Checks +replay_sequence+ for the presence of neighboring replay calls. - # @example - # [1] pry(main)> hist --show 46894 - # 46894: hist --replay 46675..46677 - # [2] pry(main)> hist --show 46675..46677 - # 46675: 1+1 - # 46676: a = 100 - # 46677: hist --tail - # [3] pry(main)> hist --replay 46894 - # Error: Replay index 46894 points out to another replay call: `hist -r 46675..46677` - # [4] pry(main)> - # - # @raise [Pry::CommandError] If +replay_sequence+ contains another - # "hist --replay" call - # @param [String] replay_sequence The sequence of commands to be replayed - # (per saltum) - # @return [Boolean] `false` if +replay_sequence+ does not contain another - # "hist --replay" call - def check_for_juxtaposed_replay(replay_sequence) - if replay_sequence =~ /\Ahist(?:ory)?\b/ - # Create *fresh* instance of Options for parsing of "hist" command. - _slop = self.slop - _slop.parse replay_sequence.split(' ')[1..-1] - - if _slop.present?(:r) - replay_sequence = replay_sequence.split("\n").join('; ') - index = opts[:r] - index = index.min if index.min == index.max || index.max.nil? - - raise CommandError, "Replay index #{index} points out to another replay call: `#{replay_sequence}`" + def process_replay + @history = @history.between(opts[:r]) + replay_sequence = @history.raw + + # If we met follow-up "hist" call, check for the "--replay" option + # presence. If "hist" command is called with other options, proceed + # further. + check_for_juxtaposed_replay(replay_sequence) + + replay_sequence.lines.each do |line| + _pry_.eval line, generated: true end - else - false end - end - # Finds history depending on the given switch. - # - # @return [Pry::Code] if it finds `--all` (or `-a`) switch, returns all - # entries in history. Without the switch returns only the entries from the - # current Pry session. - def find_history - h = if opts.present?(:all) - Pry.history.to_a - else - Pry.history.to_a.last(Pry.history.session_line_count) + # Checks +replay_sequence+ for the presence of neighboring replay calls. + # @example + # [1] pry(main)> hist --show 46894 + # 46894: hist --replay 46675..46677 + # [2] pry(main)> hist --show 46675..46677 + # 46675: 1+1 + # 46676: a = 100 + # 46677: hist --tail + # [3] pry(main)> hist --replay 46894 + # Error: Replay index 46894 points out to another replay call: `hist -r 46675..46677` + # [4] pry(main)> + # + # @raise [Pry::CommandError] If +replay_sequence+ contains another + # "hist --replay" call + # @param [String] replay_sequence The sequence of commands to be replayed + # (per saltum) + # @return [Boolean] `false` if +replay_sequence+ does not contain another + # "hist --replay" call + def check_for_juxtaposed_replay(replay_sequence) + if replay_sequence =~ /\Ahist(?:ory)?\b/ + # Create *fresh* instance of Options for parsing of "hist" command. + _slop = self.slop + _slop.parse replay_sequence.split(' ')[1..-1] + + if _slop.present?(:r) + replay_sequence = replay_sequence.split("\n").join('; ') + index = opts[:r] + index = index.min if index.min == index.max || index.max.nil? + + raise CommandError, "Replay index #{index} points out to another replay call: `#{replay_sequence}`" end + else + false + end + end - Pry::Code(Pry.history.filter(h[0..-2])) + # Finds history depending on the given switch. + # + # @return [Pry::Code] if it finds `--all` (or `-a`) switch, returns all + # entries in history. Without the switch returns only the entries from the + # current Pry session. + def find_history + h = if opts.present?(:all) + Pry.history.to_a + else + Pry.history.to_a.last(Pry.history.session_line_count) + end + + Pry::Code(Pry.history.filter(h[0..-2])) + end end - end - Pry::Commands.add_command(Pry::Command::Hist) - Pry::Commands.alias_command 'history', 'hist' + Pry::Commands.add_command(Pry::Command::Hist) + Pry::Commands.alias_command 'history', 'hist' + end end diff --git a/lib/pry/commands/import_set.rb b/lib/pry/commands/import_set.rb index 97b53543..381c6df2 100644 --- a/lib/pry/commands/import_set.rb +++ b/lib/pry/commands/import_set.rb @@ -1,23 +1,25 @@ class Pry - class Command::ImportSet < Pry::ClassCommand - match 'import-set' - group 'Commands' - # TODO: Provide a better description with examples and a general conception - # of this command. - description 'Import a Pry command set.' + class Command + class ImportSet < Pry::ClassCommand + match 'import-set' + group 'Commands' + # TODO: Provide a better description with examples and a general conception + # of this command. + description 'Import a Pry command set.' - banner <<-'BANNER' - Import a Pry command set. - BANNER + banner <<-'BANNER' + Import a Pry command set. + BANNER - # TODO: resolve unused parameter. - def process(_command_set_name) - raise CommandError, "Provide a command set name" if command_set.nil? + # TODO: resolve unused parameter. + def process(_command_set_name) + raise CommandError, "Provide a command set name" if command_set.nil? - set = target.eval(arg_string) - _pry_.commands.import set + set = target.eval(arg_string) + _pry_.commands.import set + end end - end - Pry::Commands.add_command(Pry::Command::ImportSet) + Pry::Commands.add_command(Pry::Command::ImportSet) + end end diff --git a/lib/pry/commands/install_command.rb b/lib/pry/commands/install_command.rb index 82c0d530..eeaca3a6 100644 --- a/lib/pry/commands/install_command.rb +++ b/lib/pry/commands/install_command.rb @@ -1,54 +1,56 @@ class Pry - class Command::InstallCommand < Pry::ClassCommand - match 'install-command' - group 'Commands' - description 'Install a disabled command.' - - banner <<-'BANNER' - Usage: install-command COMMAND - - Installs the gems necessary to run the given COMMAND. You will generally not - need to run this unless told to by an error message. - BANNER - - def process(name) - require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller - command = find_command(name) - - unless command - output.puts "Command #{green(name)} is not found" - return - end + class Command + class InstallCommand < Pry::ClassCommand + match 'install-command' + group 'Commands' + description 'Install a disabled command.' + + banner <<-'BANNER' + Usage: install-command COMMAND + + Installs the gems necessary to run the given COMMAND. You will generally not + need to run this unless told to by an error message. + BANNER + + def process(name) + require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller + command = find_command(name) + + unless command + output.puts "Command #{green(name)} is not found" + return + end - if command_dependencies_met?(command.options) - output.puts "Dependencies for #{green(name)} are met. Nothing to do" - return - end + if command_dependencies_met?(command.options) + output.puts "Dependencies for #{green(name)} are met. Nothing to do" + return + end - output.puts "Attempting to install #{green(name)} command..." - gems_to_install = Array(command.options[:requires_gem]) + output.puts "Attempting to install #{green(name)} command..." + gems_to_install = Array(command.options[:requires_gem]) - gems_to_install.each do |g| - next if Rubygem.installed?(g) + gems_to_install.each do |g| + next if Rubygem.installed?(g) - output.puts "Installing #{green(g)} gem..." - Rubygem.install(g) - end + output.puts "Installing #{green(g)} gem..." + Rubygem.install(g) + end - gems_to_install.each do |g| - begin - require g - rescue LoadError - fail_msg = "Required gem #{green(g)} installed but not found." - fail_msg += " Aborting command installation\n" - fail_msg += 'Tips: 1. Check your PATH; 2. Run `bundle update`' - raise CommandError, fail_msg + gems_to_install.each do |g| + begin + require g + rescue LoadError + fail_msg = "Required gem #{green(g)} installed but not found." + fail_msg += " Aborting command installation\n" + fail_msg += 'Tips: 1. Check your PATH; 2. Run `bundle update`' + raise CommandError, fail_msg + end end - end - output.puts "Installation of #{green(name)} successful! Type `help #{name}` for information" + output.puts "Installation of #{green(name)} successful! Type `help #{name}` for information" + end end - end - Pry::Commands.add_command(Pry::Command::InstallCommand) + Pry::Commands.add_command(Pry::Command::InstallCommand) + end end diff --git a/lib/pry/commands/jump_to.rb b/lib/pry/commands/jump_to.rb index b4719aa3..3b14085a 100644 --- a/lib/pry/commands/jump_to.rb +++ b/lib/pry/commands/jump_to.rb @@ -1,29 +1,31 @@ class Pry - class Command::JumpTo < Pry::ClassCommand - match 'jump-to' - group 'Navigating Pry' - description 'Jump to a binding further up the stack.' + class Command + class JumpTo < Pry::ClassCommand + match 'jump-to' + group 'Navigating Pry' + description 'Jump to a binding further up the stack.' - banner <<-'BANNER' - Jump to a binding further up the stack, popping all bindings below. - BANNER + banner <<-'BANNER' + Jump to a binding further up the stack, popping all bindings below. + BANNER - def process(break_level) - break_level = break_level.to_i - nesting_level = _pry_.binding_stack.size - 1 - max_nest_level = nesting_level - 1 + def process(break_level) + break_level = break_level.to_i + nesting_level = _pry_.binding_stack.size - 1 + max_nest_level = nesting_level - 1 - case break_level - when nesting_level - output.puts "Already at nesting level #{nesting_level}" - when 0..max_nest_level - _pry_.binding_stack = _pry_.binding_stack[0..break_level] - else - output.puts "Invalid nest level. Must be between 0 and " \ - "#{max_nest_level}. Got #{break_level}." + case break_level + when nesting_level + output.puts "Already at nesting level #{nesting_level}" + when 0..max_nest_level + _pry_.binding_stack = _pry_.binding_stack[0..break_level] + else + output.puts "Invalid nest level. Must be between 0 and " \ + "#{max_nest_level}. Got #{break_level}." + end end end - end - Pry::Commands.add_command(Pry::Command::JumpTo) + Pry::Commands.add_command(Pry::Command::JumpTo) + end end diff --git a/lib/pry/commands/list_inspectors.rb b/lib/pry/commands/list_inspectors.rb index 760c0486..96f8e701 100644 --- a/lib/pry/commands/list_inspectors.rb +++ b/lib/pry/commands/list_inspectors.rb @@ -1,36 +1,40 @@ -class Pry::Command::ListInspectors < Pry::ClassCommand - match 'list-inspectors' - group 'Input and Output' - description 'List the inspector procs available for use.' - banner <<-BANNER - Usage: list-inspectors +class Pry + class Command + class ListInspectors < Pry::ClassCommand + match 'list-inspectors' + group 'Input and Output' + description 'List the inspector procs available for use.' + banner <<-BANNER + Usage: list-inspectors - List the inspector procs available to print return values. You can use - change-inspector to switch between them. - BANNER + List the inspector procs available to print return values. You can use + change-inspector to switch between them. + BANNER - def process - output.puts heading("Available inspectors") + "\n" - inspector_map.each do |name, inspector| - output.write "Name: #{bold(name)}" - output.puts selected_inspector?(inspector) ? selected_text : "" - output.puts inspector[:description] - output.puts - end - end + def process + output.puts heading("Available inspectors") + "\n" + inspector_map.each do |name, inspector| + output.write "Name: #{bold(name)}" + output.puts selected_inspector?(inspector) ? selected_text : "" + output.puts inspector[:description] + output.puts + end + end - private + private - def inspector_map - Pry::Inspector::MAP - end + def inspector_map + Pry::Inspector::MAP + end - def selected_text - red " (selected) " - end + def selected_text + red " (selected) " + end - def selected_inspector?(inspector) - _pry_.print == inspector[:value] + def selected_inspector?(inspector) + _pry_.print == inspector[:value] + end + Pry::Commands.add_command(self) + end end - Pry::Commands.add_command(self) end diff --git a/lib/pry/commands/ls.rb b/lib/pry/commands/ls.rb index 515dfd8e..43dd8e7a 100644 --- a/lib/pry/commands/ls.rb +++ b/lib/pry/commands/ls.rb @@ -1,113 +1,115 @@ require 'pry/commands/ls/ls_entity' class Pry - class Command::Ls < Pry::ClassCommand - DEFAULT_OPTIONS = { - heading_color: :bright_blue, - public_method_color: :default, - private_method_color: :blue, - protected_method_color: :blue, - method_missing_color: :bright_red, - local_var_color: :yellow, - pry_var_color: :default, # e.g. _, _pry_, _file_ - instance_var_color: :blue, # e.g. @foo - class_var_color: :bright_blue, # e.g. @@foo - global_var_color: :default, # e.g. $CODERAY_DEBUG, $eventmachine_library - builtin_global_color: :cyan, # e.g. $stdin, $-w, $PID - pseudo_global_color: :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO - constant_color: :default, # e.g. VERSION, ARGF - class_constant_color: :blue, # e.g. Object, Kernel - exception_constant_color: :magenta, # e.g. Exception, RuntimeError - unloaded_constant_color: :yellow, # Any constant that is still in .autoload? state - separator: " ", - ceiling: [Object, Module, Class] - } + class Command + class Ls < Pry::ClassCommand + DEFAULT_OPTIONS = { + heading_color: :bright_blue, + public_method_color: :default, + private_method_color: :blue, + protected_method_color: :blue, + method_missing_color: :bright_red, + local_var_color: :yellow, + pry_var_color: :default, # e.g. _, _pry_, _file_ + instance_var_color: :blue, # e.g. @foo + class_var_color: :bright_blue, # e.g. @@foo + global_var_color: :default, # e.g. $CODERAY_DEBUG, $eventmachine_library + builtin_global_color: :cyan, # e.g. $stdin, $-w, $PID + pseudo_global_color: :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO + constant_color: :default, # e.g. VERSION, ARGF + class_constant_color: :blue, # e.g. Object, Kernel + exception_constant_color: :magenta, # e.g. Exception, RuntimeError + unloaded_constant_color: :yellow, # Any constant that is still in .autoload? state + separator: " ", + ceiling: [Object, Module, Class] + } - match 'ls' - group 'Context' - description 'Show the list of vars and methods in the current scope.' - command_options shellwords: false, interpolate: false + match 'ls' + group 'Context' + description 'Show the list of vars and methods in the current scope.' + command_options shellwords: false, interpolate: false - banner <<-'BANNER' - Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object] - ls [-g] [-l] + banner <<-'BANNER' + Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object] + ls [-g] [-l] - ls shows you which methods, constants and variables are accessible to Pry. By - default it shows you the local variables defined in the current shell, and any - public methods or instance variables defined on the current object. + ls shows you which methods, constants and variables are accessible to Pry. By + default it shows you the local variables defined in the current shell, and any + public methods or instance variables defined on the current object. - The colours used are configurable using Pry.config.ls.*_color, and the separator - is Pry.config.ls.separator. + The colours used are configurable using Pry.config.ls.*_color, and the separator + is Pry.config.ls.separator. - Pry.config.ls.ceiling is used to hide methods defined higher up in the - inheritance chain, this is by default set to [Object, Module, Class] so that - methods defined on all Objects are omitted. The -v flag can be used to ignore - this setting and show all methods, while the -q can be used to set the ceiling - much lower and show only methods defined on the object or its direct class. + Pry.config.ls.ceiling is used to hide methods defined higher up in the + inheritance chain, this is by default set to [Object, Module, Class] so that + methods defined on all Objects are omitted. The -v flag can be used to ignore + this setting and show all methods, while the -q can be used to set the ceiling + much lower and show only methods defined on the object or its direct class. - Also check out `find-method` command (run `help find-method`). - BANNER + Also check out `find-method` command (run `help find-method`). + BANNER - def options(opt) - opt.on :m, :methods, "Show public methods defined on the Object" - opt.on :M, "instance-methods", "Show public methods defined in a Module or Class" - opt.on :p, :ppp, "Show public, protected (in yellow) and private (in green) methods" - opt.on :q, :quiet, "Show only methods defined on object.singleton_class and object.class" - opt.on :v, :verbose, "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)" - opt.on :g, :globals, "Show global variables, including those builtin to Ruby (in cyan)" - opt.on :l, :locals, "Show hash of local vars, sorted by descending size" - opt.on :c, :constants, "Show constants, highlighting classes (in blue), and exceptions (in purple).\n" \ - "#{' ' * 32}Constants that are pending autoload? are also shown (in yellow)" - opt.on :i, :ivars, "Show instance variables (in blue) and class variables (in bright blue)" - opt.on :G, :grep, "Filter output by regular expression", argument: true - if Object.respond_to?(:deprecate_constant) - opt.on :d, :dconstants, "Show deprecated constants" + def options(opt) + opt.on :m, :methods, "Show public methods defined on the Object" + opt.on :M, "instance-methods", "Show public methods defined in a Module or Class" + opt.on :p, :ppp, "Show public, protected (in yellow) and private (in green) methods" + opt.on :q, :quiet, "Show only methods defined on object.singleton_class and object.class" + opt.on :v, :verbose, "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)" + opt.on :g, :globals, "Show global variables, including those builtin to Ruby (in cyan)" + opt.on :l, :locals, "Show hash of local vars, sorted by descending size" + opt.on :c, :constants, "Show constants, highlighting classes (in blue), and exceptions (in purple).\n" \ + "#{' ' * 32}Constants that are pending autoload? are also shown (in yellow)" + opt.on :i, :ivars, "Show instance variables (in blue) and class variables (in bright blue)" + opt.on :G, :grep, "Filter output by regular expression", argument: true + if Object.respond_to?(:deprecate_constant) + opt.on :d, :dconstants, "Show deprecated constants" + end + if Helpers::Platform.jruby? + opt.on :J, "all-java", "Show all the aliases for methods from java (default is to show only prettiest)" + end end - if Helpers::Platform.jruby? - opt.on :J, "all-java", "Show all the aliases for methods from java (default is to show only prettiest)" - end - end - # Exclude -q, -v and --grep because they, - # don't specify what the user wants to see. - def no_user_opts? - !(opts[:methods] || opts['instance-methods'] || opts[:ppp] || - opts[:globals] || opts[:locals] || opts[:constants] || opts[:ivars]) - end + # Exclude -q, -v and --grep because they, + # don't specify what the user wants to see. + def no_user_opts? + !(opts[:methods] || opts['instance-methods'] || opts[:ppp] || + opts[:globals] || opts[:locals] || opts[:constants] || opts[:ivars]) + end - def process - @interrogatee = args.empty? ? target_self : target.eval(args.join(' ')) - raise_errors_if_arguments_are_weird - ls_entity = LsEntity.new( - interrogatee: @interrogatee, - no_user_opts: no_user_opts?, - opts: opts, - args: args, - _pry_: _pry_ - ) + def process + @interrogatee = args.empty? ? target_self : target.eval(args.join(' ')) + raise_errors_if_arguments_are_weird + ls_entity = LsEntity.new( + interrogatee: @interrogatee, + no_user_opts: no_user_opts?, + opts: opts, + args: args, + _pry_: _pry_ + ) - _pry_.pager.page ls_entity.entities_table - end + _pry_.pager.page ls_entity.entities_table + end - private + private - def error_list - any_args = args.any? - non_mod_interrogatee = !(Module === @interrogatee) - [ - ['-l does not make sense with a specified Object', :locals, any_args], - ['-g does not make sense with a specified Object', :globals, any_args], - ['-q does not make sense with -v', :quiet, opts.present?(:verbose)], - ['-M only makes sense with a Module or a Class', 'instance-methods', non_mod_interrogatee], - ['-c only makes sense with a Module or a Class', :constants, any_args && non_mod_interrogatee] - ] - end + def error_list + any_args = args.any? + non_mod_interrogatee = !(Module === @interrogatee) + [ + ['-l does not make sense with a specified Object', :locals, any_args], + ['-g does not make sense with a specified Object', :globals, any_args], + ['-q does not make sense with -v', :quiet, opts.present?(:verbose)], + ['-M only makes sense with a Module or a Class', 'instance-methods', non_mod_interrogatee], + ['-c only makes sense with a Module or a Class', :constants, any_args && non_mod_interrogatee] + ] + end - def raise_errors_if_arguments_are_weird - error_list.each do |message, option, invalid_expr| - raise Pry::CommandError, message if opts.present?(option) && invalid_expr + def raise_errors_if_arguments_are_weird + error_list.each do |message, option, invalid_expr| + raise Pry::CommandError, message if opts.present?(option) && invalid_expr + end end end - end - Pry::Commands.add_command(Pry::Command::Ls) + Pry::Commands.add_command(Pry::Command::Ls) + end end diff --git a/lib/pry/commands/ls/constants.rb b/lib/pry/commands/ls/constants.rb index d969eefa..e18362da 100644 --- a/lib/pry/commands/ls/constants.rb +++ b/lib/pry/commands/ls/constants.rb @@ -1,55 +1,57 @@ require 'pry/commands/ls/interrogatable' class Pry - class Command::Ls < Pry::ClassCommand - class Constants < Pry::Command::Ls::Formatter - DEPRECATED_CONSTANTS = [:Data, :Fixnum, :Bignum, :TimeoutError, :NIL, :FALSE, :TRUE] - DEPRECATED_CONSTANTS << :JavaPackageModuleTemplate if Helpers::Platform.jruby? - include Pry::Command::Ls::Interrogatable - - def initialize(interrogatee, no_user_opts, opts, _pry_) - super(_pry_) - @interrogatee = interrogatee - @no_user_opts = no_user_opts - @default_switch = opts[:constants] - @verbose_switch = opts[:verbose] - @dconstants = opts.dconstants? - end + class Command + class Ls < Pry::ClassCommand + class Constants < Pry::Command::Ls::Formatter + DEPRECATED_CONSTANTS = [:Data, :Fixnum, :Bignum, :TimeoutError, :NIL, :FALSE, :TRUE] + DEPRECATED_CONSTANTS << :JavaPackageModuleTemplate if Helpers::Platform.jruby? + include Pry::Command::Ls::Interrogatable + + def initialize(interrogatee, no_user_opts, opts, _pry_) + super(_pry_) + @interrogatee = interrogatee + @no_user_opts = no_user_opts + @default_switch = opts[:constants] + @verbose_switch = opts[:verbose] + @dconstants = opts.dconstants? + end - def correct_opts? - super || (@no_user_opts && interrogating_a_module?) - end + def correct_opts? + super || (@no_user_opts && interrogating_a_module?) + end - def output_self - mod = interrogatee_mod - constants = WrappedModule.new(mod).constants(@verbose_switch) - output_section('constants', grep.regexp[format(mod, constants)]) - end + def output_self + mod = interrogatee_mod + constants = WrappedModule.new(mod).constants(@verbose_switch) + output_section('constants', grep.regexp[format(mod, constants)]) + end - private + private - def show_deprecated_constants? - @dconstants == true - end + def show_deprecated_constants? + @dconstants == true + end - def format(mod, constants) - constants.sort_by(&:downcase).map do |name| - if Object.respond_to?(:deprecate_constant) && - DEPRECATED_CONSTANTS.include?(name) && - !show_deprecated_constants? - next - end + def format(mod, constants) + constants.sort_by(&:downcase).map do |name| + if Object.respond_to?(:deprecate_constant) && + DEPRECATED_CONSTANTS.include?(name) && + !show_deprecated_constants? + next + end - if (const = (!mod.autoload?(name) && (mod.const_get(name) || true) rescue nil)) - if (const < Exception rescue false) - color(:exception_constant, name) - elsif (Module === mod.const_get(name) rescue false) - color(:class_constant, name) + if (const = (!mod.autoload?(name) && (mod.const_get(name) || true) rescue nil)) + if (const < Exception rescue false) + color(:exception_constant, name) + elsif (Module === mod.const_get(name) rescue false) + color(:class_constant, name) + else + color(:constant, name) + end else - color(:constant, name) + color(:unloaded_constant, name) end - else - color(:unloaded_constant, name) end end end diff --git a/lib/pry/commands/ls/formatter.rb b/lib/pry/commands/ls/formatter.rb index 72999976..bf0762c1 100644 --- a/lib/pry/commands/ls/formatter.rb +++ b/lib/pry/commands/ls/formatter.rb @@ -1,50 +1,52 @@ class Pry - class Command::Ls < Pry::ClassCommand - class Formatter - attr_writer :grep - attr_reader :_pry_ - - def initialize(_pry_) - @_pry_ = _pry_ - @target = _pry_.current_context - @default_switch = nil - end - - def write_out - return false unless correct_opts? - - output_self - end - - private - - def color(type, str) - Pry::Helpers::Text.send _pry_.config.ls["#{type}_color"], str - end - - # Add a new section to the output. - # Outputs nothing if the section would be empty. - def output_section(heading, body) - return '' if body.compact.empty? - - fancy_heading = Pry::Helpers::Text.bold(color(:heading, heading)) - Pry::Helpers.tablify_or_one_line(fancy_heading, body, @_pry_.config) - end - - def format_value(value) - Pry::ColorPrinter.pp(value, '') - end - - def correct_opts? - @default_switch - end - - def output_self - raise NotImplementedError - end - - def grep - @grep || proc { |x| x } + class Command + class Ls < Pry::ClassCommand + class Formatter + attr_writer :grep + attr_reader :_pry_ + + def initialize(_pry_) + @_pry_ = _pry_ + @target = _pry_.current_context + @default_switch = nil + end + + def write_out + return false unless correct_opts? + + output_self + end + + private + + def color(type, str) + Pry::Helpers::Text.send _pry_.config.ls["#{type}_color"], str + end + + # Add a new section to the output. + # Outputs nothing if the section would be empty. + def output_section(heading, body) + return '' if body.compact.empty? + + fancy_heading = Pry::Helpers::Text.bold(color(:heading, heading)) + Pry::Helpers.tablify_or_one_line(fancy_heading, body, @_pry_.config) + end + + def format_value(value) + Pry::ColorPrinter.pp(value, '') + end + + def correct_opts? + @default_switch + end + + def output_self + raise NotImplementedError + end + + def grep + @grep || proc { |x| x } + end end end end diff --git a/lib/pry/commands/ls/globals.rb b/lib/pry/commands/ls/globals.rb index 103d4e87..2be92aa2 100644 --- a/lib/pry/commands/ls/globals.rb +++ b/lib/pry/commands/ls/globals.rb @@ -1,43 +1,45 @@ class Pry - class Command::Ls < Pry::ClassCommand - class Globals < Pry::Command::Ls::Formatter - # Taken from "puts global_variables.inspect". - BUILTIN_GLOBALS = - %w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\ - $: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT - $FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER - $INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS - $ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID - $PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout) + class Command + class Ls < Pry::ClassCommand + class Globals < Pry::Command::Ls::Formatter + # Taken from "puts global_variables.inspect". + BUILTIN_GLOBALS = + %w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\ + $: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT + $FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER + $INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS + $ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID + $PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout) - # `$SAFE` and `$?` are thread-local, the exception stuff only works in a - # rescue clause, everything else is basically a local variable with a `$` - # in its name. - PSEUDO_GLOBALS = - %w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9 - $CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO - $LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH) + # `$SAFE` and `$?` are thread-local, the exception stuff only works in a + # rescue clause, everything else is basically a local variable with a `$` + # in its name. + PSEUDO_GLOBALS = + %w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9 + $CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO + $LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH) - def initialize(opts, _pry_) - super(_pry_) - @default_switch = opts[:globals] - end + def initialize(opts, _pry_) + super(_pry_) + @default_switch = opts[:globals] + end - def output_self - variables = format(@target.eval('global_variables')) - output_section('global variables', grep.regexp[variables]) - end + def output_self + variables = format(@target.eval('global_variables')) + output_section('global variables', grep.regexp[variables]) + end - private + private - def format(globals) - globals.map(&:to_s).sort_by(&:downcase).map do |name| - if PSEUDO_GLOBALS.include?(name) - color(:pseudo_global, name) - elsif BUILTIN_GLOBALS.include?(name) - color(:builtin_global, name) - else - color(:global_var, name) + def format(globals) + globals.map(&:to_s).sort_by(&:downcase).map do |name| + if PSEUDO_GLOBALS.include?(name) + color(:pseudo_global, name) + elsif BUILTIN_GLOBALS.include?(name) + color(:builtin_global, name) + else + color(:global_var, name) + end end end end diff --git a/lib/pry/commands/ls/grep.rb b/lib/pry/commands/ls/grep.rb index cbdf9154..e13aea68 100644 --- a/lib/pry/commands/ls/grep.rb +++ b/lib/pry/commands/ls/grep.rb @@ -1,18 +1,20 @@ class Pry - class Command::Ls < Pry::ClassCommand - class Grep - def initialize(grep_regexp) - @grep_regexp = grep_regexp - end + class Command + class Ls < Pry::ClassCommand + class Grep + def initialize(grep_regexp) + @grep_regexp = grep_regexp + end - def regexp - proc { |x| - if x.instance_of?(Array) - x.grep(@grep_regexp) - else - x =~ @grep_regexp - end - } + def regexp + proc { |x| + if x.instance_of?(Array) + x.grep(@grep_regexp) + else + x =~ @grep_regexp + end + } + end end end end diff --git a/lib/pry/commands/ls/instance_vars.rb b/lib/pry/commands/ls/instance_vars.rb index 8717f3a9..062f8473 100644 --- a/lib/pry/commands/ls/instance_vars.rb +++ b/lib/pry/commands/ls/instance_vars.rb @@ -1,37 +1,39 @@ require 'pry/commands/ls/interrogatable' class Pry - class Command::Ls < Pry::ClassCommand - class InstanceVars < Pry::Command::Ls::Formatter - include Pry::Command::Ls::Interrogatable + class Command + class Ls < Pry::ClassCommand + class InstanceVars < Pry::Command::Ls::Formatter + include Pry::Command::Ls::Interrogatable - def initialize(interrogatee, no_user_opts, opts, _pry_) - super(_pry_) - @interrogatee = interrogatee - @no_user_opts = no_user_opts - @default_switch = opts[:ivars] - end + def initialize(interrogatee, no_user_opts, opts, _pry_) + super(_pry_) + @interrogatee = interrogatee + @no_user_opts = no_user_opts + @default_switch = opts[:ivars] + end - def correct_opts? - super || @no_user_opts - end + def correct_opts? + super || @no_user_opts + end - def output_self - ivars = if Object === @interrogatee - Pry::Method.safe_send(@interrogatee, :instance_variables) - else - [] # TODO: BasicObject support - end - kvars = Pry::Method.safe_send(interrogatee_mod, :class_variables) - ivars_out = output_section('instance variables', format(:instance_var, ivars)) - kvars_out = output_section('class variables', format(:class_var, kvars)) - ivars_out + kvars_out - end + def output_self + ivars = if Object === @interrogatee + Pry::Method.safe_send(@interrogatee, :instance_variables) + else + [] # TODO: BasicObject support + end + kvars = Pry::Method.safe_send(interrogatee_mod, :class_variables) + ivars_out = output_section('instance variables', format(:instance_var, ivars)) + kvars_out = output_section('class variables', format(:class_var, kvars)) + ivars_out + kvars_out + end - private + private - def format(type, vars) - vars.sort_by { |var| var.to_s.downcase }.map { |var| color(type, var) } + def format(type, vars) + vars.sort_by { |var| var.to_s.downcase }.map { |var| color(type, var) } + end end end end diff --git a/lib/pry/commands/ls/interrogatable.rb b/lib/pry/commands/ls/interrogatable.rb index 66a4aa45..3fa0592a 100644 --- a/lib/pry/commands/ls/interrogatable.rb +++ b/lib/pry/commands/ls/interrogatable.rb @@ -1,16 +1,22 @@ -module Pry::Command::Ls::Interrogatable - private +class Pry + class Command + class Ls + module Interrogatable + private - def interrogating_a_module? - Module === @interrogatee - end + def interrogating_a_module? + Module === @interrogatee + end - def interrogatee_mod - if interrogating_a_module? - @interrogatee - else - singleton = Pry::Method.singleton_class_of(@interrogatee) - singleton.ancestors.grep(::Class).reject { |c| c == singleton }.first + def interrogatee_mod + if interrogating_a_module? + @interrogatee + else + singleton = Pry::Method.singleton_class_of(@interrogatee) + singleton.ancestors.grep(::Class).reject { |c| c == singleton }.first + end + end + end end end end diff --git a/lib/pry/commands/ls/jruby_hacks.rb b/lib/pry/commands/ls/jruby_hacks.rb index ef471735..1e393baf 100644 --- a/lib/pry/commands/ls/jruby_hacks.rb +++ b/lib/pry/commands/ls/jruby_hacks.rb @@ -1,47 +1,53 @@ -module Pry::Command::Ls::JRubyHacks - private +class Pry + class Command + class Ls + module JRubyHacks + private - # JRuby creates lots of aliases for methods imported from java in an attempt - # to make life easier for ruby programmers. (e.g. getFooBar becomes - # get_foo_bar and foo_bar, and maybe foo_bar? if it returns a Boolean). The - # full transformations are in the assignAliases method of: - # https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java - # - # This has the unfortunate side-effect of making the output of ls even more - # incredibly verbose than it normally would be for these objects; and so we - # filter out all but the nicest of these aliases here. - # - # TODO: This is a little bit vague, better heuristics could be used. - # JRuby also has a lot of scala-specific logic, which we don't copy. - def trim_jruby_aliases(methods) - grouped = methods.group_by do |m| - m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase - end + # JRuby creates lots of aliases for methods imported from java in an attempt + # to make life easier for ruby programmers. (e.g. getFooBar becomes + # get_foo_bar and foo_bar, and maybe foo_bar? if it returns a Boolean). The + # full transformations are in the assignAliases method of: + # https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java + # + # This has the unfortunate side-effect of making the output of ls even more + # incredibly verbose than it normally would be for these objects; and so we + # filter out all but the nicest of these aliases here. + # + # TODO: This is a little bit vague, better heuristics could be used. + # JRuby also has a lot of scala-specific logic, which we don't copy. + def trim_jruby_aliases(methods) + grouped = methods.group_by do |m| + m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase + end - grouped.flat_map do |_key, values| - values = values.sort_by do |m| - rubbishness(m.name) - end + grouped.flat_map do |_key, values| + values = values.sort_by do |m| + rubbishness(m.name) + end - found = [] - values.select do |x| - (!found.any? { |y| x == y }) && found << x - end - end - end + found = [] + values.select do |x| + (!found.any? { |y| x == y }) && found << x + end + end + end - # When removing jruby aliases, we want to keep the alias that is - # "least rubbish" according to this metric. - def rubbishness(name) - name.each_char.map do |x| - case x - when /[A-Z]/ - 1 - when '?', '=', '!' - -2 - else - 0 + # When removing jruby aliases, we want to keep the alias that is + # "least rubbish" according to this metric. + def rubbishness(name) + name.each_char.map do |x| + case x + when /[A-Z]/ + 1 + when '?', '=', '!' + -2 + else + 0 + end + end.inject(&:+) + (name.size / 100.0) + end end - end.inject(&:+) + (name.size / 100.0) + end end end diff --git a/lib/pry/commands/ls/local_names.rb b/lib/pry/commands/ls/local_names.rb index cec747ad..577b2feb 100644 --- a/lib/pry/commands/ls/local_names.rb +++ b/lib/pry/commands/ls/local_names.rb @@ -1,30 +1,32 @@ class Pry - class Command::Ls < Pry::ClassCommand - class LocalNames < Pry::Command::Ls::Formatter - def initialize(no_user_opts, args, _pry_) - super(_pry_) - @no_user_opts = no_user_opts - @args = args - @sticky_locals = _pry_.sticky_locals - end + class Command + class Ls < Pry::ClassCommand + class LocalNames < Pry::Command::Ls::Formatter + def initialize(no_user_opts, args, _pry_) + super(_pry_) + @no_user_opts = no_user_opts + @args = args + @sticky_locals = _pry_.sticky_locals + end - def correct_opts? - super || (@no_user_opts && @args.empty?) - end + def correct_opts? + super || (@no_user_opts && @args.empty?) + end - def output_self - local_vars = grep.regexp[@target.eval('local_variables')] - output_section('locals', format(local_vars)) - end + def output_self + local_vars = grep.regexp[@target.eval('local_variables')] + output_section('locals', format(local_vars)) + end - private + private - def format(locals) - locals.sort_by(&:downcase).map do |name| - if @sticky_locals.include?(name.to_sym) - color(:pry_var, name) - else - color(:local_var, name) + def format(locals) + locals.sort_by(&:downcase).map do |name| + if @sticky_locals.include?(name.to_sym) + color(:pry_var, name) + else + color(:local_var, name) + end end end end diff --git a/lib/pry/commands/ls/local_vars.rb b/lib/pry/commands/ls/local_vars.rb index 5ebb73e1..9b1e14e0 100644 --- a/lib/pry/commands/ls/local_vars.rb +++ b/lib/pry/commands/ls/local_vars.rb @@ -1,36 +1,38 @@ class Pry - class Command::Ls < Pry::ClassCommand - class LocalVars < Pry::Command::Ls::Formatter - def initialize(opts, _pry_) - super(_pry_) - @default_switch = opts[:locals] - @sticky_locals = _pry_.sticky_locals - end + class Command + class Ls < Pry::ClassCommand + class LocalVars < Pry::Command::Ls::Formatter + def initialize(opts, _pry_) + super(_pry_) + @default_switch = opts[:locals] + @sticky_locals = _pry_.sticky_locals + end - def output_self - name_value_pairs = @target.eval('local_variables').reject do |e| - @sticky_locals.key?(e.to_sym) - end.map do |name| - [name, (@target.eval(name.to_s))] + def output_self + name_value_pairs = @target.eval('local_variables').reject do |e| + @sticky_locals.key?(e.to_sym) + end.map do |name| + [name, (@target.eval(name.to_s))] + end + format(name_value_pairs).join('') end - format(name_value_pairs).join('') - end - private + private - def format(name_value_pairs) - name_value_pairs.sort_by do |_name, value| - value.to_s.size - end.reverse.map do |name, value| - colorized_assignment_style(name, format_value(value)) + def format(name_value_pairs) + name_value_pairs.sort_by do |_name, value| + value.to_s.size + end.reverse.map do |name, value| + colorized_assignment_style(name, format_value(value)) + end end - end - def colorized_assignment_style(lhs, rhs, desired_width = 7) - colorized_lhs = color(:local_var, lhs) - color_escape_padding = colorized_lhs.size - lhs.size - pad = desired_width + color_escape_padding - "%-#{pad}s = %s" % [color(:local_var, colorized_lhs), rhs] + def colorized_assignment_style(lhs, rhs, desired_width = 7) + colorized_lhs = color(:local_var, lhs) + color_escape_padding = colorized_lhs.size - lhs.size + pad = desired_width + color_escape_padding + "%-#{pad}s = %s" % [color(:local_var, colorized_lhs), rhs] + end end end end diff --git a/lib/pry/commands/ls/ls_entity.rb b/lib/pry/commands/ls/ls_entity.rb index 918e2d54..f600cd99 100644 --- a/lib/pry/commands/ls/ls_entity.rb +++ b/lib/pry/commands/ls/ls_entity.rb @@ -9,62 +9,64 @@ require 'pry/commands/ls/local_names' require 'pry/commands/ls/local_vars' class Pry - class Command::Ls < Pry::ClassCommand - class LsEntity - attr_reader :_pry_ + class Command + class Ls < Pry::ClassCommand + class LsEntity + attr_reader :_pry_ - def initialize(opts) - @interrogatee = opts[:interrogatee] - @no_user_opts = opts[:no_user_opts] - @opts = opts[:opts] - @args = opts[:args] - @grep = Grep.new(Regexp.new(opts[:opts][:G] || '.')) - @_pry_ = opts.delete(:_pry_) - end + def initialize(opts) + @interrogatee = opts[:interrogatee] + @no_user_opts = opts[:no_user_opts] + @opts = opts[:opts] + @args = opts[:args] + @grep = Grep.new(Regexp.new(opts[:opts][:G] || '.')) + @_pry_ = opts.delete(:_pry_) + end - def entities_table - entities.map(&:write_out).reject { |o| !o }.join('') - end + def entities_table + entities.map(&:write_out).reject { |o| !o }.join('') + end - private + private - def grep(entity) - entity.tap { |o| o.grep = @grep } - end + def grep(entity) + entity.tap { |o| o.grep = @grep } + end - def globals - grep Globals.new(@opts, _pry_) - end + def globals + grep Globals.new(@opts, _pry_) + end - def constants - grep Constants.new(@interrogatee, @no_user_opts, @opts, _pry_) - end + def constants + grep Constants.new(@interrogatee, @no_user_opts, @opts, _pry_) + end - def methods - grep(Methods.new(@interrogatee, @no_user_opts, @opts, _pry_)) - end + def methods + grep(Methods.new(@interrogatee, @no_user_opts, @opts, _pry_)) + end - def self_methods - grep SelfMethods.new(@interrogatee, @no_user_opts, @opts, _pry_) - end + def self_methods + grep SelfMethods.new(@interrogatee, @no_user_opts, @opts, _pry_) + end - def instance_vars - grep InstanceVars.new(@interrogatee, @no_user_opts, @opts, _pry_) - end + def instance_vars + grep InstanceVars.new(@interrogatee, @no_user_opts, @opts, _pry_) + end - def local_names - grep LocalNames.new(@no_user_opts, @args, _pry_) - end + def local_names + grep LocalNames.new(@no_user_opts, @args, _pry_) + end - def local_vars - LocalVars.new(@opts, _pry_) - end + def local_vars + LocalVars.new(@opts, _pry_) + end - def entities - [ - globals, constants, methods, self_methods, instance_vars, local_names, - local_vars - ] + def entities + [ + globals, constants, methods, self_methods, instance_vars, local_names, + local_vars + ] + end end end end diff --git a/lib/pry/commands/ls/methods.rb b/lib/pry/commands/ls/methods.rb index 3b3862ef..fa7d54fb 100644 --- a/lib/pry/commands/ls/methods.rb +++ b/lib/pry/commands/ls/methods.rb @@ -2,52 +2,54 @@ require 'pry/commands/ls/methods_helper' require 'pry/commands/ls/interrogatable' class Pry - class Command::Ls < Pry::ClassCommand - class Methods < Pry::Command::Ls::Formatter - include Pry::Command::Ls::Interrogatable - include Pry::Command::Ls::MethodsHelper + class Command + class Ls < Pry::ClassCommand + class Methods < Pry::Command::Ls::Formatter + include Pry::Command::Ls::Interrogatable + include Pry::Command::Ls::MethodsHelper - def initialize(interrogatee, no_user_opts, opts, _pry_) - super(_pry_) - @interrogatee = interrogatee - @no_user_opts = no_user_opts - @default_switch = opts[:methods] - @instance_methods_switch = opts['instance-methods'] - @ppp_switch = opts[:ppp] - @jruby_switch = opts['all-java'] - @quiet_switch = opts[:quiet] - @verbose_switch = opts[:verbose] - end + def initialize(interrogatee, no_user_opts, opts, _pry_) + super(_pry_) + @interrogatee = interrogatee + @no_user_opts = no_user_opts + @default_switch = opts[:methods] + @instance_methods_switch = opts['instance-methods'] + @ppp_switch = opts[:ppp] + @jruby_switch = opts['all-java'] + @quiet_switch = opts[:quiet] + @verbose_switch = opts[:verbose] + end - def output_self - methods = all_methods.group_by(&:owner) - # Reverse the resolution order so that the most useful information - # appears right by the prompt. - resolution_order.take_while(&below_ceiling).reverse.map do |klass| - methods_here = (methods[klass] || []).select { |m| grep.regexp[m.name] } - heading = "#{Pry::WrappedModule.new(klass).method_prefix}methods" - output_section(heading, format(methods_here)) - end.join('') - end + def output_self + methods = all_methods.group_by(&:owner) + # Reverse the resolution order so that the most useful information + # appears right by the prompt. + resolution_order.take_while(&below_ceiling).reverse.map do |klass| + methods_here = (methods[klass] || []).select { |m| grep.regexp[m.name] } + heading = "#{Pry::WrappedModule.new(klass).method_prefix}methods" + output_section(heading, format(methods_here)) + end.join('') + end - private + private - def correct_opts? - super || @instance_methods_switch || @ppp_switch || @no_user_opts - end + def correct_opts? + super || @instance_methods_switch || @ppp_switch || @no_user_opts + end - # Get a lambda that can be used with `take_while` to prevent over-eager - # traversal of the Object's ancestry graph. - def below_ceiling - ceiling = if @quiet_switch - [Pry::Method.safe_send(interrogatee_mod, :ancestors)[1]] + - _pry_.config.ls.ceiling - elsif @verbose_switch - [] - else - _pry_.config.ls.ceiling.dup - end - lambda { |klass| !ceiling.include?(klass) } + # Get a lambda that can be used with `take_while` to prevent over-eager + # traversal of the Object's ancestry graph. + def below_ceiling + ceiling = if @quiet_switch + [Pry::Method.safe_send(interrogatee_mod, :ancestors)[1]] + + _pry_.config.ls.ceiling + elsif @verbose_switch + [] + else + _pry_.config.ls.ceiling.dup + end + lambda { |klass| !ceiling.include?(klass) } + end end end end diff --git a/lib/pry/commands/ls/methods_helper.rb b/lib/pry/commands/ls/methods_helper.rb index c819ab03..14ec0919 100644 --- a/lib/pry/commands/ls/methods_helper.rb +++ b/lib/pry/commands/ls/methods_helper.rb @@ -1,43 +1,49 @@ require 'pry/commands/ls/jruby_hacks' -module Pry::Command::Ls::MethodsHelper - include Pry::Command::Ls::JRubyHacks +class Pry + class Command + class Ls + module MethodsHelper + include Pry::Command::Ls::JRubyHacks - private + private - # Get all the methods that we'll want to output. - def all_methods(instance_methods = false) - methods = if instance_methods || @instance_methods_switch - Pry::Method.all_from_class(@interrogatee) - else - Pry::Method.all_from_obj(@interrogatee) - end + # Get all the methods that we'll want to output. + def all_methods(instance_methods = false) + methods = if instance_methods || @instance_methods_switch + Pry::Method.all_from_class(@interrogatee) + else + Pry::Method.all_from_obj(@interrogatee) + end - if Pry::Helpers::Platform.jruby? && !@jruby_switch - methods = trim_jruby_aliases(methods) - end + if Pry::Helpers::Platform.jruby? && !@jruby_switch + methods = trim_jruby_aliases(methods) + end - methods.select { |method| @ppp_switch || method.visibility == :public } - end + methods.select { |method| @ppp_switch || method.visibility == :public } + end - def resolution_order - if @instance_methods_switch - Pry::Method.instance_resolution_order(@interrogatee) - else - Pry::Method.resolution_order(@interrogatee) - end - end + def resolution_order + if @instance_methods_switch + Pry::Method.instance_resolution_order(@interrogatee) + else + Pry::Method.resolution_order(@interrogatee) + end + end - def format(methods) - methods.sort_by(&:name).map do |method| - if method.name == 'method_missing' - color(:method_missing, 'method_missing') - elsif method.visibility == :private - color(:private_method, method.name) - elsif method.visibility == :protected - color(:protected_method, method.name) - else - color(:public_method, method.name) + def format(methods) + methods.sort_by(&:name).map do |method| + if method.name == 'method_missing' + color(:method_missing, 'method_missing') + elsif method.visibility == :private + color(:private_method, method.name) + elsif method.visibility == :protected + color(:protected_method, method.name) + else + color(:public_method, method.name) + end + end + end end end end diff --git a/lib/pry/commands/ls/self_methods.rb b/lib/pry/commands/ls/self_methods.rb index 95f49987..97536739 100644 --- a/lib/pry/commands/ls/self_methods.rb +++ b/lib/pry/commands/ls/self_methods.rb @@ -2,31 +2,33 @@ require 'pry/commands/ls/interrogatable' require 'pry/commands/ls/methods_helper' class Pry - class Command::Ls < Pry::ClassCommand - class SelfMethods < Pry::Command::Ls::Formatter - include Pry::Command::Ls::Interrogatable - include Pry::Command::Ls::MethodsHelper + class Command + class Ls < Pry::ClassCommand + class SelfMethods < Pry::Command::Ls::Formatter + include Pry::Command::Ls::Interrogatable + include Pry::Command::Ls::MethodsHelper - def initialize(interrogatee, no_user_opts, opts, _pry_) - super(_pry_) - @interrogatee = interrogatee - @no_user_opts = no_user_opts - @ppp_switch = opts[:ppp] - @jruby_switch = opts['all-java'] - end + def initialize(interrogatee, no_user_opts, opts, _pry_) + super(_pry_) + @interrogatee = interrogatee + @no_user_opts = no_user_opts + @ppp_switch = opts[:ppp] + @jruby_switch = opts['all-java'] + end - def output_self - methods = all_methods(true).select do |m| - m.owner == @interrogatee && grep.regexp[m.name] + def output_self + methods = all_methods(true).select do |m| + m.owner == @interrogatee && grep.regexp[m.name] + end + heading = "#{Pry::WrappedModule.new(@interrogatee).method_prefix}methods" + output_section(heading, format(methods)) end - heading = "#{Pry::WrappedModule.new(@interrogatee).method_prefix}methods" - output_section(heading, format(methods)) - end - private + private - def correct_opts? - @no_user_opts && interrogating_a_module? + def correct_opts? + @no_user_opts && interrogating_a_module? + end end end end diff --git a/lib/pry/commands/nesting.rb b/lib/pry/commands/nesting.rb index f9c0297c..9bc3b630 100644 --- a/lib/pry/commands/nesting.rb +++ b/lib/pry/commands/nesting.rb @@ -1,25 +1,27 @@ class Pry - class Command::Nesting < Pry::ClassCommand - match 'nesting' - group 'Navigating Pry' - description 'Show nesting information.' + class Command + class Nesting < Pry::ClassCommand + match 'nesting' + group 'Navigating Pry' + description 'Show nesting information.' - banner <<-'BANNER' - Show nesting information. - BANNER + banner <<-'BANNER' + Show nesting information. + BANNER - def process - output.puts 'Nesting status:' - output.puts '--' - _pry_.binding_stack.each_with_index do |obj, level| - if level == 0 - output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)" - else - output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}" + def process + output.puts 'Nesting status:' + output.puts '--' + _pry_.binding_stack.each_with_index do |obj, level| + if level == 0 + output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)" + else + output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}" + end end end end - end - Pry::Commands.add_command(Pry::Command::Nesting) + Pry::Commands.add_command(Pry::Command::Nesting) + end end diff --git a/lib/pry/commands/play.rb b/lib/pry/commands/play.rb index d16244ea..c74906ef 100644 --- a/lib/pry/commands/play.rb +++ b/lib/pry/commands/play.rb @@ -1,108 +1,110 @@ class Pry - class Command::Play < Pry::ClassCommand - match 'play' - group 'Editing' - description 'Playback a string variable, method, line, or file as input.' - - banner <<-'BANNER' - Usage: play [OPTIONS] [--help] - - The play command enables you to replay code from files and methods as if they - were entered directly in the Pry REPL. - - play --lines 149..153 # assumes current context - play -i 20 --lines 1..3 # assumes lines of the input expression at 20 - play -o 4 # the output of an expression at 4 - play Pry#repl -l 1..-1 # play the contents of Pry#repl method - play -e 2 # play from specified line until end of valid expression - play hello.rb # play a file - play Rakefile -l 5 # play line 5 of a file - play -d hi # play documentation of hi method - play hi --open # play hi method and leave it open - - https://github.com/pry/pry/wiki/User-Input#wiki-Play - BANNER - - def options(opt) - CodeCollector.inject_options(opt) - - opt.on :open, 'Plays the selected content except the last line. Useful' \ - ' for replaying methods and leaving the method definition' \ - ' "open". `amend-line` can then be used to' \ - ' modify the method.' - - opt.on :e, :expression=, 'Executes until end of valid expression', as: Integer - opt.on :p, :print, 'Prints executed code' - end - - def process - @cc = CodeCollector.new(args, opts, _pry_) + class Command + class Play < Pry::ClassCommand + match 'play' + group 'Editing' + description 'Playback a string variable, method, line, or file as input.' + + banner <<-'BANNER' + Usage: play [OPTIONS] [--help] + + The play command enables you to replay code from files and methods as if they + were entered directly in the Pry REPL. + + play --lines 149..153 # assumes current context + play -i 20 --lines 1..3 # assumes lines of the input expression at 20 + play -o 4 # the output of an expression at 4 + play Pry#repl -l 1..-1 # play the contents of Pry#repl method + play -e 2 # play from specified line until end of valid expression + play hello.rb # play a file + play Rakefile -l 5 # play line 5 of a file + play -d hi # play documentation of hi method + play hi --open # play hi method and leave it open + + https://github.com/pry/pry/wiki/User-Input#wiki-Play + BANNER + + def options(opt) + CodeCollector.inject_options(opt) + + opt.on :open, 'Plays the selected content except the last line. Useful' \ + ' for replaying methods and leaving the method definition' \ + ' "open". `amend-line` can then be used to' \ + ' modify the method.' + + opt.on :e, :expression=, 'Executes until end of valid expression', as: Integer + opt.on :p, :print, 'Prints executed code' + end - perform_play - show_input - end + def process + @cc = CodeCollector.new(args, opts, _pry_) - def perform_play - eval_string << content_after_options - run "fix-indent" - end + perform_play + show_input + end - def show_input - if opts.present?(:print) || !Pry::Code.complete_expression?(eval_string) - run "show-input" + def perform_play + eval_string << content_after_options + run "fix-indent" end - end - def content_after_options - if opts.present?(:open) - restrict_to_lines(content, (0..-2)) - elsif opts.present?(:expression) - content_at_expression - else - content + def show_input + if opts.present?(:print) || !Pry::Code.complete_expression?(eval_string) + run "show-input" + end end - end - def content_at_expression - code_object.expression_at(opts[:expression]) - end + def content_after_options + if opts.present?(:open) + restrict_to_lines(content, (0..-2)) + elsif opts.present?(:expression) + content_at_expression + else + content + end + end - def code_object - Pry::Code.new(content) - end + def content_at_expression + code_object.expression_at(opts[:expression]) + end - def should_use_default_file? - !args.first && !opts.present?(:in) && !opts.present?(:out) - end + def code_object + Pry::Code.new(content) + end - def content - if should_use_default_file? - file_content - else - @cc.content + def should_use_default_file? + !args.first && !opts.present?(:in) && !opts.present?(:out) end - end - # The file to play from when no code object is specified. - # e.g `play --lines 4..10` - def default_file - file = - if target.respond_to?(:source_location) - target.source_location.first + def content + if should_use_default_file? + file_content else - target.eval("__FILE__") + @cc.content end - file && File.expand_path(file) - end + end - def file_content - if default_file && File.exist?(default_file) - @cc.restrict_to_lines(File.read(default_file), @cc.line_range) - else - raise CommandError, "File does not exist! File was: #{default_file.inspect}" + # The file to play from when no code object is specified. + # e.g `play --lines 4..10` + def default_file + file = + if target.respond_to?(:source_location) + target.source_location.first + else + target.eval("__FILE__") + end + file && File.expand_path(file) + end + + def file_content + if default_file && File.exist?(default_file) + @cc.restrict_to_lines(File.read(default_file), @cc.line_range) + else + raise CommandError, "File does not exist! File was: #{default_file.inspect}" + end end end - end - Pry::Commands.add_command(Pry::Command::Play) + Pry::Commands.add_command(Pry::Command::Play) + end end diff --git a/lib/pry/commands/pry_backtrace.rb b/lib/pry/commands/pry_backtrace.rb index 8d7f59cb..696ec282 100644 --- a/lib/pry/commands/pry_backtrace.rb +++ b/lib/pry/commands/pry_backtrace.rb @@ -1,25 +1,27 @@ class Pry - class Command::PryBacktrace < Pry::ClassCommand - match 'pry-backtrace' - group 'Context' - description 'Show the backtrace for the Pry session.' + class Command + class PryBacktrace < Pry::ClassCommand + match 'pry-backtrace' + group 'Context' + description 'Show the backtrace for the Pry session.' - banner <<-BANNER - Usage: pry-backtrace [OPTIONS] [--help] + banner <<-BANNER + Usage: pry-backtrace [OPTIONS] [--help] - Show the backtrace for the position in the code where Pry was started. This can - be used to infer the behavior of the program immediately before it entered Pry, - just like the backtrace property of an exception. + Show the backtrace for the position in the code where Pry was started. This can + be used to infer the behavior of the program immediately before it entered Pry, + just like the backtrace property of an exception. - NOTE: if you are looking for the backtrace of the most recent exception raised, - just type: `_ex_.backtrace` instead. - See: https://github.com/pry/pry/wiki/Special-Locals - BANNER + NOTE: if you are looking for the backtrace of the most recent exception raised, + just type: `_ex_.backtrace` instead. + See: https://github.com/pry/pry/wiki/Special-Locals + BANNER - def process - _pry_.pager.page bold('Backtrace:') << "\n--\n" << _pry_.backtrace.join("\n") + def process + _pry_.pager.page bold('Backtrace:') << "\n--\n" << _pry_.backtrace.join("\n") + end end - end - Pry::Commands.add_command(Pry::Command::PryBacktrace) + Pry::Commands.add_command(Pry::Command::PryBacktrace) + end end diff --git a/lib/pry/commands/pry_version.rb b/lib/pry/commands/pry_version.rb index 8cb8fd85..3784a4c8 100644 --- a/lib/pry/commands/pry_version.rb +++ b/lib/pry/commands/pry_version.rb @@ -1,17 +1,19 @@ class Pry - class Command::Version < Pry::ClassCommand - match 'pry-version' - group 'Misc' - description 'Show Pry version.' + class Command + class Version < Pry::ClassCommand + match 'pry-version' + group 'Misc' + description 'Show Pry version.' - banner <<-'BANNER' - Show Pry version. - BANNER + banner <<-'BANNER' + Show Pry version. + BANNER - def process - output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}." + def process + output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}." + end end - end - Pry::Commands.add_command(Pry::Command::Version) + Pry::Commands.add_command(Pry::Command::Version) + end end diff --git a/lib/pry/commands/raise_up.rb b/lib/pry/commands/raise_up.rb index 3dea3270..96bb9989 100644 --- a/lib/pry/commands/raise_up.rb +++ b/lib/pry/commands/raise_up.rb @@ -1,33 +1,35 @@ class Pry # N.B. using a regular expresion here so that "raise-up 'foo'" does the right thing. - class Command::RaiseUp < Pry::ClassCommand - match(/raise-up(!?\b.*)/) - group 'Context' - description 'Raise an exception out of the current pry instance.' - command_options listing: 'raise-up' + class Command + class RaiseUp < Pry::ClassCommand + match(/raise-up(!?\b.*)/) + group 'Context' + description 'Raise an exception out of the current pry instance.' + command_options listing: 'raise-up' - banner <<-BANNER - Raise up, like exit, allows you to quit pry. Instead of returning a value - however, it raises an exception. If you don't provide the exception to be - raised, it will use the most recent exception (in pry `_ex_`). + banner <<-BANNER + Raise up, like exit, allows you to quit pry. Instead of returning a value + however, it raises an exception. If you don't provide the exception to be + raised, it will use the most recent exception (in pry `_ex_`). - When called as raise-up! (with an exclamation mark), this command raises the - exception through any nested prys you have created by "cd"ing into objects. + When called as raise-up! (with an exclamation mark), this command raises the + exception through any nested prys you have created by "cd"ing into objects. - raise-up "get-me-out-of-here" + raise-up "get-me-out-of-here" - # This is equivalent to the command above. - raise "get-me-out-of-here" - raise-up - BANNER + # This is equivalent to the command above. + raise "get-me-out-of-here" + raise-up + BANNER - def process - return _pry.pager.page help if captures[0] =~ /(-h|--help)\b/ + def process + return _pry.pager.page help if captures[0] =~ /(-h|--help)\b/ - # Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' in a rubyesque manner - target.eval("_pry_.raise_up#{captures[0]}") + # Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' in a rubyesque manner + target.eval("_pry_.raise_up#{captures[0]}") + end end - end - Pry::Commands.add_command(Pry::Command::RaiseUp) + Pry::Commands.add_command(Pry::Command::RaiseUp) + end end diff --git a/lib/pry/commands/reload_code.rb b/lib/pry/commands/reload_code.rb index 334be097..1dba1a22 100644 --- a/lib/pry/commands/reload_code.rb +++ b/lib/pry/commands/reload_code.rb @@ -1,68 +1,70 @@ class Pry - class Command::ReloadCode < Pry::ClassCommand - match 'reload-code' - group 'Misc' - description 'Reload the source file that contains the specified code object.' + class Command + class ReloadCode < Pry::ClassCommand + match 'reload-code' + group 'Misc' + description 'Reload the source file that contains the specified code object.' - banner <<-'BANNER' - Reload the source file that contains the specified code object. + banner <<-'BANNER' + Reload the source file that contains the specified code object. - e.g reload-code MyClass#my_method #=> reload a method - reload-code MyClass #=> reload a class - reload-code my-command #=> reload a pry command - reload-code self #=> reload the current object - reload-code #=> reload the current file or object - BANNER + e.g reload-code MyClass#my_method #=> reload a method + reload-code MyClass #=> reload a class + reload-code my-command #=> reload a pry command + reload-code self #=> reload the current object + reload-code #=> reload the current file or object + BANNER - def process - if !args.empty? - reload_object(args.join(" ")) - elsif internal_binding?(target) - reload_object("self") - else - reload_current_file + def process + if !args.empty? + reload_object(args.join(" ")) + elsif internal_binding?(target) + reload_object("self") + else + reload_current_file + end end - end - private + private - def current_file - file = - if target.respond_to?(:source_location) - target.source_location.first - else - target.eval("__FILE__") + def current_file + file = + if target.respond_to?(:source_location) + target.source_location.first + else + target.eval("__FILE__") + end + File.expand_path file + end + + def reload_current_file + if !File.exist?(current_file) + raise CommandError, "Current file: #{current_file} cannot be found on disk!" end - File.expand_path file - end - def reload_current_file - if !File.exist?(current_file) - raise CommandError, "Current file: #{current_file} cannot be found on disk!" + load current_file + output.puts "The current file: #{current_file} was reloaded!" end - load current_file - output.puts "The current file: #{current_file} was reloaded!" - end - - def reload_object(identifier) - code_object = Pry::CodeObject.lookup(identifier, _pry_) - check_for_reloadability(code_object, identifier) - load code_object.source_file - output.puts "#{identifier} was reloaded!" - end + def reload_object(identifier) + code_object = Pry::CodeObject.lookup(identifier, _pry_) + check_for_reloadability(code_object, identifier) + load code_object.source_file + output.puts "#{identifier} was reloaded!" + end - def check_for_reloadability(code_object, identifier) - if !code_object || !code_object.source_file - raise CommandError, "Cannot locate #{identifier}!" - elsif !File.exist?(code_object.source_file) - raise CommandError, - "Cannot reload #{identifier} as it has no associated file on disk. " \ - "File found was: #{code_object.source_file}" + def check_for_reloadability(code_object, identifier) + if !code_object || !code_object.source_file + raise CommandError, "Cannot locate #{identifier}!" + elsif !File.exist?(code_object.source_file) + raise CommandError, + "Cannot reload #{identifier} as it has no associated file on disk. " \ + "File found was: #{code_object.source_file}" + end end end - end - Pry::Commands.add_command(Pry::Command::ReloadCode) - Pry::Commands.alias_command 'reload-method', 'reload-code' + Pry::Commands.add_command(Pry::Command::ReloadCode) + Pry::Commands.alias_command 'reload-method', 'reload-code' + end end diff --git a/lib/pry/commands/reset.rb b/lib/pry/commands/reset.rb index 4414cbb5..91599b64 100644 --- a/lib/pry/commands/reset.rb +++ b/lib/pry/commands/reset.rb @@ -1,18 +1,20 @@ class Pry - class Command::Reset < Pry::ClassCommand - match 'reset' - group 'Context' - description 'Reset the REPL to a clean state.' + class Command + class Reset < Pry::ClassCommand + match 'reset' + group 'Context' + description 'Reset the REPL to a clean state.' - banner <<-'BANNER' - Reset the REPL to a clean state. - BANNER + banner <<-'BANNER' + Reset the REPL to a clean state. + BANNER - def process - output.puts 'Pry reset.' - exec 'pry' + def process + output.puts 'Pry reset.' + exec 'pry' + end end - end - Pry::Commands.add_command(Pry::Command::Reset) + Pry::Commands.add_command(Pry::Command::Reset) + end end diff --git a/lib/pry/commands/ri.rb b/lib/pry/commands/ri.rb index 46e85c80..c2abd046 100644 --- a/lib/pry/commands/ri.rb +++ b/lib/pry/commands/ri.rb @@ -1,65 +1,67 @@ class Pry - class Command::Ri < Pry::ClassCommand - match 'ri' - group 'Introspection' - description 'View ri documentation.' + class Command + class Ri < Pry::ClassCommand + match 'ri' + group 'Introspection' + description 'View ri documentation.' - banner <<-'BANNER' - Usage: ri [spec] + banner <<-'BANNER' + Usage: ri [spec] - View ri documentation. Relies on the "rdoc" gem being installed. - See also "show-doc" command. + View ri documentation. Relies on the "rdoc" gem being installed. + See also "show-doc" command. - ri Array#each - BANNER + ri Array#each + BANNER - def process(spec) - unless spec - return output.puts "Please provide a class, module, or method name (e.g: ri Array#push)" - end + def process(spec) + unless spec + return output.puts "Please provide a class, module, or method name (e.g: ri Array#push)" + end - # Lazily load RI - require 'rdoc/ri/driver' + # Lazily load RI + require 'rdoc/ri/driver' - unless defined? RDoc::RI::PryDriver + unless defined? RDoc::RI::PryDriver - # Subclass RI so that it formats its output nicely, and uses `lesspipe`. - subclass = Class.new(RDoc::RI::Driver) # the hard way. + # Subclass RI so that it formats its output nicely, and uses `lesspipe`. + subclass = Class.new(RDoc::RI::Driver) # the hard way. - subclass.class_eval do - def initialize(pager, opts) - @pager = pager - super opts - end + subclass.class_eval do + def initialize(pager, opts) + @pager = pager + super opts + end - def page - paging_text = StringIO.new - yield paging_text - @pager.page(paging_text.string) - end + def page + paging_text = StringIO.new + yield paging_text + @pager.page(paging_text.string) + end - def formatter(_io) - if @formatter_klass - @formatter_klass.new - else - RDoc::Markup::ToAnsi.new + def formatter(_io) + if @formatter_klass + @formatter_klass.new + else + RDoc::Markup::ToAnsi.new + end end end - end - RDoc::RI.const_set :PryDriver, subclass # hook it up! - end + RDoc::RI.const_set :PryDriver, subclass # hook it up! + end - # Spin-up an RI insance. - ri = RDoc::RI::PryDriver.new _pry_.pager, use_stdout: true, interactive: false + # Spin-up an RI insance. + ri = RDoc::RI::PryDriver.new _pry_.pager, use_stdout: true, interactive: false - begin - ri.display_names [spec] # Get the documentation (finally!) - rescue RDoc::RI::Driver::NotFoundError => e - output.puts "error: '#{e.name}' not found" + begin + ri.display_names [spec] # Get the documentation (finally!) + rescue RDoc::RI::Driver::NotFoundError => e + output.puts "error: '#{e.name}' not found" + end end end - end - Pry::Commands.add_command(Pry::Command::Ri) + Pry::Commands.add_command(Pry::Command::Ri) + end end diff --git a/lib/pry/commands/save_file.rb b/lib/pry/commands/save_file.rb index d22a6924..6d3ab689 100644 --- a/lib/pry/commands/save_file.rb +++ b/lib/pry/commands/save_file.rb @@ -1,61 +1,63 @@ require 'pry/commands/code_collector' class Pry - class Command::SaveFile < Pry::ClassCommand - match 'save-file' - group 'Input and Output' - description 'Export to a file using content from the REPL.' + class Command + class SaveFile < Pry::ClassCommand + match 'save-file' + group 'Input and Output' + description 'Export to a file using content from the REPL.' - banner <<-'BANNER' - Usage: save-file [OPTIONS] --to [FILE] + banner <<-'BANNER' + Usage: save-file [OPTIONS] --to [FILE] - Export to a file using content from the REPL. + Export to a file using content from the REPL. - save-file my_method --to hello.rb - save-file -i 1..10 --to hello.rb --append - save-file show-method --to my_command.rb - save-file sample_file.rb --lines 2..10 --to output_file.rb - BANNER + save-file my_method --to hello.rb + save-file -i 1..10 --to hello.rb --append + save-file show-method --to my_command.rb + save-file sample_file.rb --lines 2..10 --to output_file.rb + BANNER - def options(opt) - CodeCollector.inject_options(opt) + def options(opt) + CodeCollector.inject_options(opt) - opt.on :to=, "Specify the output file path" - opt.on :a, :append, "Append output to file" - end + opt.on :to=, "Specify the output file path" + opt.on :a, :append, "Append output to file" + end - def process - @cc = CodeCollector.new(args, opts, _pry_) - raise CommandError, "Found no code to save." if @cc.content.empty? + def process + @cc = CodeCollector.new(args, opts, _pry_) + raise CommandError, "Found no code to save." if @cc.content.empty? - if !file_name - display_content - else - save_file + if !file_name + display_content + else + save_file + end end - end - def file_name - opts[:to] || nil - end + def file_name + opts[:to] || nil + end - def save_file - File.open(file_name, mode) do |f| - f.puts @cc.content + def save_file + File.open(file_name, mode) do |f| + f.puts @cc.content + end + output.puts "#{file_name} successfully saved" end - output.puts "#{file_name} successfully saved" - end - def display_content - output.puts @cc.content - output.puts "\n\n--\nPlease use `--to FILE` to export to a file." - output.puts "No file saved!\n--" - end + def display_content + output.puts @cc.content + output.puts "\n\n--\nPlease use `--to FILE` to export to a file." + output.puts "No file saved!\n--" + end - def mode - opts.present?(:append) ? "a" : "w" + def mode + opts.present?(:append) ? "a" : "w" + end end - end - Pry::Commands.add_command(Pry::Command::SaveFile) + Pry::Commands.add_command(Pry::Command::SaveFile) + end end diff --git a/lib/pry/commands/shell_command.rb b/lib/pry/commands/shell_command.rb index c7355908..c06efab2 100644 --- a/lib/pry/commands/shell_command.rb +++ b/lib/pry/commands/shell_command.rb @@ -1,73 +1,75 @@ class Pry - class Command::ShellCommand < Pry::ClassCommand - match(/\.(.*)/) - group 'Input and Output' - description "All text following a '.' is forwarded to the shell." - command_options listing: '.<shell command>', use_prefix: false, - takes_block: true + class Command + class ShellCommand < Pry::ClassCommand + match(/\.(.*)/) + group 'Input and Output' + description "All text following a '.' is forwarded to the shell." + command_options listing: '.<shell command>', use_prefix: false, + takes_block: true - banner <<-'BANNER' - Usage: .COMMAND_NAME + banner <<-'BANNER' + Usage: .COMMAND_NAME - All text following a "." is forwarded to the shell. + All text following a "." is forwarded to the shell. - .ls -aF - .uname - BANNER + .ls -aF + .uname + BANNER - def process(cmd) - if cmd =~ /^cd\s*(.*)/i - process_cd parse_destination(Regexp.last_match(1)) - else - pass_block(cmd) - if command_block - command_block.call `#{cmd}` + def process(cmd) + if cmd =~ /^cd\s*(.*)/i + process_cd parse_destination(Regexp.last_match(1)) else - _pry_.config.system.call(output, cmd, _pry_) + pass_block(cmd) + if command_block + command_block.call `#{cmd}` + else + _pry_.config.system.call(output, cmd, _pry_) + end end end - end - private + private - def parse_destination(dest) - return "~" if dest.empty? - return dest unless dest == "-" + def parse_destination(dest) + return "~" if dest.empty? + return dest unless dest == "-" - state.old_pwd || raise(CommandError, "No prior directory available") - end + state.old_pwd || raise(CommandError, "No prior directory available") + end - def process_cd(dest) - state.old_pwd = Dir.pwd - Dir.chdir(File.expand_path(path_from_cd_path(dest) || dest)) - rescue Errno::ENOENT - raise CommandError, "No such directory: #{dest}" - end + def process_cd(dest) + state.old_pwd = Dir.pwd + Dir.chdir(File.expand_path(path_from_cd_path(dest) || dest)) + rescue Errno::ENOENT + raise CommandError, "No such directory: #{dest}" + end - def cd_path_env - ENV['CDPATH'] - end + def cd_path_env + ENV['CDPATH'] + end - def cd_path_exists? - cd_path_env && cd_path_env.length.nonzero? - end + def cd_path_exists? + cd_path_env && cd_path_env.length.nonzero? + end - def path_from_cd_path(dest) - return if !(dest && cd_path_exists?) || special_case_path?(dest) + def path_from_cd_path(dest) + return if !(dest && cd_path_exists?) || special_case_path?(dest) - cd_path_env.split(File::PATH_SEPARATOR).each do |path| - if File.directory?(path) && path.split(File::SEPARATOR).last == dest - return path + cd_path_env.split(File::PATH_SEPARATOR).each do |path| + if File.directory?(path) && path.split(File::SEPARATOR).last == dest + return path + end end + + return nil end - return nil + def special_case_path?(dest) + ['.', '..', '-'].include?(dest) || dest =~ /\A[#{File::PATH_SEPARATOR}~]/ + end end - def special_case_path?(dest) - ['.', '..', '-'].include?(dest) || dest =~ /\A[#{File::PATH_SEPARATOR}~]/ - end + Pry::Commands.add_command(Pry::Command::ShellCommand) end - - Pry::Commands.add_command(Pry::Command::ShellCommand) end diff --git a/lib/pry/commands/shell_mode.rb b/lib/pry/commands/shell_mode.rb index 8cb8fe52..037993a0 100644 --- a/lib/pry/commands/shell_mode.rb +++ b/lib/pry/commands/shell_mode.rb @@ -1,25 +1,27 @@ class Pry - class Command::ShellMode < Pry::ClassCommand - match 'shell-mode' - group 'Input and Output' - description 'Toggle shell mode. Bring in pwd prompt and file completion.' + class Command + class ShellMode < Pry::ClassCommand + match 'shell-mode' + group 'Input and Output' + description 'Toggle shell mode. Bring in pwd prompt and file completion.' - banner <<-'BANNER' - Toggle shell mode. Bring in pwd prompt and file completion. - BANNER + banner <<-'BANNER' + Toggle shell mode. Bring in pwd prompt and file completion. + BANNER - def process - state.disabled ^= true + def process + state.disabled ^= true - if state.disabled - state.prev_prompt = _pry_.prompt - _pry_.prompt = Pry::Prompt[:shell] - else - _pry_.prompt = state.prev_prompt + if state.disabled + state.prev_prompt = _pry_.prompt + _pry_.prompt = Pry::Prompt[:shell] + else + _pry_.prompt = state.prev_prompt + end end end - end - Pry::Commands.add_command(Pry::Command::ShellMode) - Pry::Commands.alias_command 'file-mode', 'shell-mode' + Pry::Commands.add_command(Pry::Command::ShellMode) + Pry::Commands.alias_command 'file-mode', 'shell-mode' + end end diff --git a/lib/pry/commands/show_doc.rb b/lib/pry/commands/show_doc.rb index b3f6d883..8fa97024 100644 --- a/lib/pry/commands/show_doc.rb +++ b/lib/pry/commands/show_doc.rb @@ -1,92 +1,94 @@ require 'pry/commands/show_info' class Pry - class Command::ShowDoc < Command::ShowInfo - include Pry::Helpers::DocumentationHelpers - - match 'show-doc' - group 'Introspection' - description 'Show the documentation for a method or class.' - - banner <<-BANNER - Usage: show-doc [OPTIONS] [METH] - Aliases: ? - - Show the documentation for a method or class. Tries instance methods first and - then methods by default. - - show-doc hi_method # docs for hi_method - show-doc Pry # for Pry class - show-doc Pry -a # for all definitions of Pry class (all monkey patches) - BANNER - - def process - super + class Command + class ShowDoc < Command::ShowInfo + include Pry::Helpers::DocumentationHelpers + + match 'show-doc' + group 'Introspection' + description 'Show the documentation for a method or class.' + + banner <<-BANNER + Usage: show-doc [OPTIONS] [METH] + Aliases: ? + + Show the documentation for a method or class. Tries instance methods first and + then methods by default. + + show-doc hi_method # docs for hi_method + show-doc Pry # for Pry class + show-doc Pry -a # for all definitions of Pry class (all monkey patches) + BANNER + + def process + super + + output.puts( + "\nWARNING: the show-doc command is deprecated. It will be removed " \ + "from future Pry versions.\nPlease use 'show-source' with the -d " \ + "(or --doc) switch instead\nExample: show-source #{obj_name} -d" + ) + end - output.puts( - "\nWARNING: the show-doc command is deprecated. It will be removed " \ - "from future Pry versions.\nPlease use 'show-source' with the -d " \ - "(or --doc) switch instead\nExample: show-source #{obj_name} -d" - ) - end + # The docs for code_object prepared for display. + def content_for(code_object) + Code.new( + render_doc_markup_for(code_object), + start_line_for(code_object), + :text + ).with_line_numbers(use_line_numbers?).to_s + end - # The docs for code_object prepared for display. - def content_for(code_object) - Code.new( - render_doc_markup_for(code_object), - start_line_for(code_object), - :text - ).with_line_numbers(use_line_numbers?).to_s - end + # process the markup (if necessary) and apply colors + def render_doc_markup_for(code_object) + docs = docs_for(code_object) - # process the markup (if necessary) and apply colors - def render_doc_markup_for(code_object) - docs = docs_for(code_object) + if code_object.command? + # command '--help' shouldn't use markup highlighting + docs + else + if docs.empty? + raise CommandError, "No docs found for: #{obj_name ? obj_name : 'current context'}" + end - if code_object.command? - # command '--help' shouldn't use markup highlighting - docs - else - if docs.empty? - raise CommandError, "No docs found for: #{obj_name ? obj_name : 'current context'}" + process_comment_markup(docs) end - - process_comment_markup(docs) end - end - # Return docs for the code_object, adjusting for whether the code_object - # has yard docs available, in which case it returns those. - # (note we only have to check yard docs for modules since they can - # have multiple docs, but methods can only be doc'd once so we - # dont need to check them) - def docs_for(code_object) - if code_object.module_with_yard_docs? - # yard docs - code_object.yard_doc - else - # normal docs (i.e comments above method/module/command) - code_object.doc + # Return docs for the code_object, adjusting for whether the code_object + # has yard docs available, in which case it returns those. + # (note we only have to check yard docs for modules since they can + # have multiple docs, but methods can only be doc'd once so we + # dont need to check them) + def docs_for(code_object) + if code_object.module_with_yard_docs? + # yard docs + code_object.yard_doc + else + # normal docs (i.e comments above method/module/command) + code_object.doc + end end - end - # Which sections to include in the 'header', can toggle: :owner, - # :signature and visibility. - def header_options - super.merge signature: true - end + # Which sections to include in the 'header', can toggle: :owner, + # :signature and visibility. + def header_options + super.merge signature: true + end - # figure out start line of docs by back-calculating based on - # number of lines in the comment and the start line of the code_object - # @return [Fixnum] start line of docs - def start_line_for(code_object) - return 1 if code_object.command? || opts.present?(:'base-one') - return 1 unless code_object.source_line + # figure out start line of docs by back-calculating based on + # number of lines in the comment and the start line of the code_object + # @return [Fixnum] start line of docs + def start_line_for(code_object) + return 1 if code_object.command? || opts.present?(:'base-one') + return 1 unless code_object.source_line - code_object.source_line - code_object.doc.lines.count + code_object.source_line - code_object.doc.lines.count + end end - end - Pry::Commands.add_command(Pry::Command::ShowDoc) - Pry::Commands.alias_command '?', 'show-doc' + Pry::Commands.add_command(Pry::Command::ShowDoc) + Pry::Commands.alias_command '?', 'show-doc' + end end diff --git a/lib/pry/commands/show_info.rb b/lib/pry/commands/show_info.rb index 3bcb9a7e..beca4d97 100644 --- a/lib/pry/commands/show_info.rb +++ b/lib/pry/commands/show_info.rb @@ -1,216 +1,218 @@ class Pry - class Command::ShowInfo < Pry::ClassCommand - extend Pry::Helpers::BaseHelpers + class Command + class ShowInfo < Pry::ClassCommand + extend Pry::Helpers::BaseHelpers - command_options shellwords: false, interpolate: false + command_options shellwords: false, interpolate: false - def initialize(*) - super + def initialize(*) + super - @used_super = nil - end + @used_super = nil + end - def options(opt) - opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors", as: :count - opt.on :l, "line-numbers", "Show line numbers" - opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)" - opt.on :a, :all, "Show all definitions and monkeypatches of the module/class" - end + def options(opt) + opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors", as: :count + opt.on :l, "line-numbers", "Show line numbers" + opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)" + opt.on :a, :all, "Show all definitions and monkeypatches of the module/class" + end + + def process + code_object = Pry::CodeObject.lookup(obj_name, _pry_, super: opts[:super]) + raise CommandError, no_definition_message if !code_object - def process - code_object = Pry::CodeObject.lookup(obj_name, _pry_, super: opts[:super]) - raise CommandError, no_definition_message if !code_object + @original_code_object = code_object - @original_code_object = code_object + if !obj_name && code_object.c_module? && !opts[:all] + result = "Warning: You're inside an object, whose class is defined by means\n" + + " of the C Ruby API. Pry cannot display the information for\n" + + " this class." + if code_object.candidates.any? + result += "\n However, you can view monkey-patches applied to this class.\n" + + " Just execute the same command with the '--all' switch." + end + elsif show_all_modules?(code_object) + # show all monkey patches for a module - if !obj_name && code_object.c_module? && !opts[:all] - result = "Warning: You're inside an object, whose class is defined by means\n" + - " of the C Ruby API. Pry cannot display the information for\n" + - " this class." - if code_object.candidates.any? - result += "\n However, you can view monkey-patches applied to this class.\n" + - " Just execute the same command with the '--all' switch." + result = content_and_headers_for_all_module_candidates(code_object) + else + # show a specific code object + co = code_object_with_accessible_source(code_object) + result = content_and_header_for_code_object(co) end - elsif show_all_modules?(code_object) - # show all monkey patches for a module - result = content_and_headers_for_all_module_candidates(code_object) - else - # show a specific code object - co = code_object_with_accessible_source(code_object) - result = content_and_header_for_code_object(co) + set_file_and_dir_locals(code_object.source_file) + _pry_.pager.page result end - set_file_and_dir_locals(code_object.source_file) - _pry_.pager.page result - end - - # This method checks whether the `code_object` is a WrappedModule, - # if it is, then it returns the first candidate (monkeypatch) with - # accessible source (or docs). If `code_object` is not a WrappedModule (i.e a - # method or a command) then the `code_object` itself is just - # returned. - # - # @return [Pry::WrappedModule, Pry::Method, Pry::Command] - def code_object_with_accessible_source(code_object) - if code_object.is_a?(WrappedModule) - candidate = code_object.candidates.find(&:source) - if candidate - return candidate + # This method checks whether the `code_object` is a WrappedModule, + # if it is, then it returns the first candidate (monkeypatch) with + # accessible source (or docs). If `code_object` is not a WrappedModule (i.e a + # method or a command) then the `code_object` itself is just + # returned. + # + # @return [Pry::WrappedModule, Pry::Method, Pry::Command] + def code_object_with_accessible_source(code_object) + if code_object.is_a?(WrappedModule) + candidate = code_object.candidates.find(&:source) + if candidate + return candidate + else + raise CommandError, no_definition_message if !valid_superclass?(code_object) + + @used_super = true + code_object_with_accessible_source(code_object.super) + end else - raise CommandError, no_definition_message if !valid_superclass?(code_object) - - @used_super = true - code_object_with_accessible_source(code_object.super) + code_object end - else - code_object end - end - def valid_superclass?(code_object) - code_object.super && code_object.super.wrapped != Object - end + def valid_superclass?(code_object) + code_object.super && code_object.super.wrapped != Object + end - def content_and_header_for_code_object(code_object) - header(code_object) << content_for(code_object) - end + def content_and_header_for_code_object(code_object) + header(code_object) << content_for(code_object) + end - def content_and_headers_for_all_module_candidates(mod) - result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n" - mod.number_of_candidates.times do |v| - candidate = mod.candidate(v) - begin - result << "\nCandidate #{v + 1}/#{mod.number_of_candidates}: #{candidate.source_file}:#{candidate.source_line}\n" - content = content_for(candidate) - - result << "Number of lines: #{content.lines.count}\n\n" << content - rescue Pry::RescuableException - result << "\nNo content found.\n" - next + def content_and_headers_for_all_module_candidates(mod) + result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n" + mod.number_of_candidates.times do |v| + candidate = mod.candidate(v) + begin + result << "\nCandidate #{v + 1}/#{mod.number_of_candidates}: #{candidate.source_file}:#{candidate.source_line}\n" + content = content_for(candidate) + + result << "Number of lines: #{content.lines.count}\n\n" << content + rescue Pry::RescuableException + result << "\nNo content found.\n" + next + end end + result end - result - end - def no_definition_message - "Couldn't locate a definition for #{obj_name}" - end + def no_definition_message + "Couldn't locate a definition for #{obj_name}" + end - # Generate a header (meta-data information) for all the code - # object types: methods, modules, commands, procs... - def header(code_object) - file_name, line_num = file_and_line_for(code_object) - content = content_for(code_object) + # Generate a header (meta-data information) for all the code + # object types: methods, modules, commands, procs... + def header(code_object) + file_name, line_num = file_and_line_for(code_object) + content = content_for(code_object) - h = "\n#{bold('From:')} #{file_name}" - h << code_object_header(code_object, line_num) - h << "\n#{bold('Number of lines:')} " << "#{content.lines.count}\n\n" - h << bold('** Warning:') << " Cannot find code for #{@original_code_object.nonblank_name}. Showing superclass #{code_object.nonblank_name} instead. **\n\n" if @used_super + h = "\n#{bold('From:')} #{file_name}" + h << code_object_header(code_object, line_num) + h << "\n#{bold('Number of lines:')} " << "#{content.lines.count}\n\n" + h << bold('** Warning:') << " Cannot find code for #{@original_code_object.nonblank_name}. Showing superclass #{code_object.nonblank_name} instead. **\n\n" if @used_super - if content.lines.none? - h << bold('** Warning:') << " Cannot find code for '#{code_object.name}' (source_location is nil)" - end + if content.lines.none? + h << bold('** Warning:') << " Cannot find code for '#{code_object.name}' (source_location is nil)" + end - h - end + h + end - def code_object_header(code_object, line_num) - if code_object.real_method_object? - method_header(code_object, line_num) + def code_object_header(code_object, line_num) + if code_object.real_method_object? + method_header(code_object, line_num) - # It sucks we have to test for both Pry::WrappedModule and WrappedModule::Candidate, - # probably indicates a deep refactor needs to happen in those classes. - elsif code_object.is_a?(Pry::WrappedModule) || code_object.is_a?(Pry::WrappedModule::Candidate) - module_header(code_object, line_num) - else - "" + # It sucks we have to test for both Pry::WrappedModule and WrappedModule::Candidate, + # probably indicates a deep refactor needs to happen in those classes. + elsif code_object.is_a?(Pry::WrappedModule) || code_object.is_a?(Pry::WrappedModule::Candidate) + module_header(code_object, line_num) + else + "" + end end - end - def method_header(code_object, line_num) - h = "" - h << (code_object.c_method? ? "(C Method):" : ":#{line_num}:") - h << method_sections(code_object)[:owner] - h << method_sections(code_object)[:visibility] - h << method_sections(code_object)[:signature] - h - end + def method_header(code_object, line_num) + h = "" + h << (code_object.c_method? ? "(C Method):" : ":#{line_num}:") + h << method_sections(code_object)[:owner] + h << method_sections(code_object)[:visibility] + h << method_sections(code_object)[:signature] + h + end - def module_header(code_object, line_num) - h = "" - h << ":#{line_num}\n" - h << bold(code_object.module? ? "Module" : "Class") - h << " #{bold('name:')} #{code_object.nonblank_name}" + def module_header(code_object, line_num) + h = "" + h << ":#{line_num}\n" + h << bold(code_object.module? ? "Module" : "Class") + h << " #{bold('name:')} #{code_object.nonblank_name}" - if code_object.number_of_candidates > 1 - h << (bold("\nNumber of monkeypatches: ") << code_object.number_of_candidates.to_s) - h << ". Use the `-a` option to display all available monkeypatches" + if code_object.number_of_candidates > 1 + h << (bold("\nNumber of monkeypatches: ") << code_object.number_of_candidates.to_s) + h << ". Use the `-a` option to display all available monkeypatches" + end + h end - h - end - def method_sections(code_object) - { - owner: "\n#{bold("Owner:")} #{code_object.owner || "N/A"}\n", - visibility: "#{bold("Visibility:")} #{code_object.visibility}", - signature: "\n#{bold("Signature:")} #{code_object.signature}" - }.merge(header_options) { |_key, old, new| (new && old).to_s } - end + def method_sections(code_object) + { + owner: "\n#{bold("Owner:")} #{code_object.owner || "N/A"}\n", + visibility: "#{bold("Visibility:")} #{code_object.visibility}", + signature: "\n#{bold("Signature:")} #{code_object.signature}" + }.merge(header_options) { |_key, old, new| (new && old).to_s } + end - def header_options - { - owner: true, - visibility: true, - signature: nil - } - end + def header_options + { + owner: true, + visibility: true, + signature: nil + } + end - def show_all_modules?(code_object) - code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) - end + def show_all_modules?(code_object) + code_object.is_a?(Pry::WrappedModule) && opts.present?(:all) + end - def obj_name - @obj_name ||= args.empty? ? nil : args.join(' ') - end + def obj_name + @obj_name ||= args.empty? ? nil : args.join(' ') + end - def use_line_numbers? - opts.present?(:b) || opts.present?(:l) - end + def use_line_numbers? + opts.present?(:b) || opts.present?(:l) + end - def start_line_for(code_object) - if opts.present?(:'base-one') - 1 - else - code_object.source_line || 1 + def start_line_for(code_object) + if opts.present?(:'base-one') + 1 + else + code_object.source_line || 1 + end end - end - # takes into account possible yard docs, and returns yard_file / yard_line - # Also adjusts for start line of comments (using start_line_for), which it has to infer - # by subtracting number of lines of comment from start line of code_object - def file_and_line_for(code_object) - if code_object.module_with_yard_docs? - [code_object.yard_file, code_object.yard_line] - else - [code_object.source_file, start_line_for(code_object)] + # takes into account possible yard docs, and returns yard_file / yard_line + # Also adjusts for start line of comments (using start_line_for), which it has to infer + # by subtracting number of lines of comment from start line of code_object + def file_and_line_for(code_object) + if code_object.module_with_yard_docs? + [code_object.yard_file, code_object.yard_line] + else + [code_object.source_file, start_line_for(code_object)] + end end - end - def complete(input) - if input =~ /([^ ]*)#([a-z0-9_]*)\z/ - prefix = Regexp.last_match(1) - search = Regexp.last_match(2) - methods = begin - Pry::Method.all_from_class(binding.eval(prefix)) - rescue RescuableException - return super - end - methods.map do |method| - [prefix, method.name].join('#') if method.name.start_with?(search) - end.compact - else - super + def complete(input) + if input =~ /([^ ]*)#([a-z0-9_]*)\z/ + prefix = Regexp.last_match(1) + search = Regexp.last_match(2) + methods = begin + Pry::Method.all_from_class(binding.eval(prefix)) + rescue RescuableException + return super + end + methods.map do |method| + [prefix, method.name].join('#') if method.name.start_with?(search) + end.compact + else + super + end end end end diff --git a/lib/pry/commands/show_input.rb b/lib/pry/commands/show_input.rb index 8b727877..0b4f41e2 100644 --- a/lib/pry/commands/show_input.rb +++ b/lib/pry/commands/show_input.rb @@ -1,17 +1,19 @@ class Pry - class Command::ShowInput < Pry::ClassCommand - match 'show-input' - group 'Editing' - description 'Show the contents of the input buffer for the current multi-line expression.' + class Command + class ShowInput < Pry::ClassCommand + match 'show-input' + group 'Editing' + description 'Show the contents of the input buffer for the current multi-line expression.' - banner <<-'BANNER' - Show the contents of the input buffer for the current multi-line expression. - BANNER + banner <<-'BANNER' + Show the contents of the input buffer for the current multi-line expression. + BANNER - def process - output.puts Code.new(eval_string).with_line_numbers + def process + output.puts Code.new(eval_string).with_line_numbers + end end - end - Pry::Commands.add_command(Pry::Command::ShowInput) + Pry::Commands.add_command(Pry::Command::ShowInput) + end end diff --git a/lib/pry/commands/show_source.rb b/lib/pry/commands/show_source.rb index ade62fe3..6317535a 100644 --- a/lib/pry/commands/show_source.rb +++ b/lib/pry/commands/show_source.rb @@ -1,114 +1,116 @@ require 'pry/commands/show_info' class Pry - class Command::ShowSource < Command::ShowInfo - include Pry::Helpers::DocumentationHelpers - - match 'show-source' - group 'Introspection' - description 'Show the source for a method or class.' - - banner <<-'BANNER' - Usage: show-source [OPTIONS] [METH|CLASS] - Aliases: $, show-method - - Show the source for a method or class. Tries instance methods first and then - methods by default. - - show-source hi_method - show-source hi_method - show-source Pry#rep # source for Pry#rep method - show-source Pry # for Pry class - show-source Pry -a # for all Pry class definitions (all monkey patches) - show-source Pry.foo -e # for class of the return value of expression `Pry.foo` - show-source Pry --super # for superclass of Pry (Object class) - show-source Pry -d # include documentation - - https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method - BANNER - - def options(opt) - opt.on :e, :eval, "evaluate the command's argument as a ruby expression and show the class its return value" - opt.on :d, :doc, 'include documentation in the output' - super(opt) - end + class Command + class ShowSource < Command::ShowInfo + include Pry::Helpers::DocumentationHelpers + + match 'show-source' + group 'Introspection' + description 'Show the source for a method or class.' + + banner <<-'BANNER' + Usage: show-source [OPTIONS] [METH|CLASS] + Aliases: $, show-method + + Show the source for a method or class. Tries instance methods first and then + methods by default. + + show-source hi_method + show-source hi_method + show-source Pry#rep # source for Pry#rep method + show-source Pry # for Pry class + show-source Pry -a # for all Pry class definitions (all monkey patches) + show-source Pry.foo -e # for class of the return value of expression `Pry.foo` + show-source Pry --super # for superclass of Pry (Object class) + show-source Pry -d # include documentation + + https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method + BANNER + + def options(opt) + opt.on :e, :eval, "evaluate the command's argument as a ruby expression and show the class its return value" + opt.on :d, :doc, 'include documentation in the output' + super(opt) + end + + def process + if opts.present?(:e) + obj = target.eval(args.first) + self.args = Array.new(1) { Module === obj ? obj.name : obj.class.name } + end - def process - if opts.present?(:e) - obj = target.eval(args.first) - self.args = Array.new(1) { Module === obj ? obj.name : obj.class.name } + super end - super - end + # The source for code_object prepared for display. + def content_for(code_object) + content = '' + if opts.present?(:d) + code = Code.new( + render_doc_markup_for(code_object), start_line_for(code_object), :text + ) + content += code.with_line_numbers(use_line_numbers?).to_s + content += "\n" + end - # The source for code_object prepared for display. - def content_for(code_object) - content = '' - if opts.present?(:d) code = Code.new( - render_doc_markup_for(code_object), start_line_for(code_object), :text + code_object.source || [], start_line_for(code_object) ) - content += code.with_line_numbers(use_line_numbers?).to_s - content += "\n" + content += code.with_line_numbers(use_line_numbers?).highlighted + content end - code = Code.new( - code_object.source || [], start_line_for(code_object) - ) - content += code.with_line_numbers(use_line_numbers?).highlighted - content - end + # process the markup (if necessary) and apply colors + def render_doc_markup_for(code_object) + docs = docs_for(code_object) - # process the markup (if necessary) and apply colors - def render_doc_markup_for(code_object) - docs = docs_for(code_object) + if code_object.command? + # command '--help' shouldn't use markup highlighting + docs + else + if docs.empty? + raise CommandError, "No docs found for: #{obj_name ? obj_name : 'current context'}" + end - if code_object.command? - # command '--help' shouldn't use markup highlighting - docs - else - if docs.empty? - raise CommandError, "No docs found for: #{obj_name ? obj_name : 'current context'}" + process_comment_markup(docs) end - - process_comment_markup(docs) end - end - # Return docs for the code_object, adjusting for whether the code_object - # has yard docs available, in which case it returns those. - # (note we only have to check yard docs for modules since they can - # have multiple docs, but methods can only be doc'd once so we - # dont need to check them) - def docs_for(code_object) - if code_object.module_with_yard_docs? - # yard docs - code_object.yard_doc - else - # normal docs (i.e comments above method/module/command) - code_object.doc + # Return docs for the code_object, adjusting for whether the code_object + # has yard docs available, in which case it returns those. + # (note we only have to check yard docs for modules since they can + # have multiple docs, but methods can only be doc'd once so we + # dont need to check them) + def docs_for(code_object) + if code_object.module_with_yard_docs? + # yard docs + code_object.yard_doc + else + # normal docs (i.e comments above method/module/command) + code_object.doc + end end - end - # Which sections to include in the 'header', can toggle: :owner, - # :signature and visibility. - def header_options - super.merge signature: true - end + # Which sections to include in the 'header', can toggle: :owner, + # :signature and visibility. + def header_options + super.merge signature: true + end - # figure out start line of docs by back-calculating based on - # number of lines in the comment and the start line of the code_object - # @return [Fixnum] start line of docs - def start_line_for(code_object) - return 1 if code_object.command? || opts.present?(:'base-one') - return 1 unless code_object.source_line + # figure out start line of docs by back-calculating based on + # number of lines in the comment and the start line of the code_object + # @return [Fixnum] start line of docs + def start_line_for(code_object) + return 1 if code_object.command? || opts.present?(:'base-one') + return 1 unless code_object.source_line - code_object.source_line - code_object.doc.lines.count + code_object.source_line - code_object.doc.lines.count + end end - end - Pry::Commands.add_command(Pry::Command::ShowSource) - Pry::Commands.alias_command 'show-method', 'show-source' - Pry::Commands.alias_command '$', 'show-source' + Pry::Commands.add_command(Pry::Command::ShowSource) + Pry::Commands.alias_command 'show-method', 'show-source' + Pry::Commands.alias_command '$', 'show-source' + end end diff --git a/lib/pry/commands/stat.rb b/lib/pry/commands/stat.rb index b53c0505..14c7e974 100644 --- a/lib/pry/commands/stat.rb +++ b/lib/pry/commands/stat.rb @@ -1,40 +1,42 @@ class Pry - class Command::Stat < Pry::ClassCommand - match 'stat' - group 'Introspection' - description 'View method information and set _file_ and _dir_ locals.' - command_options shellwords: false + class Command + class Stat < Pry::ClassCommand + match 'stat' + group 'Introspection' + description 'View method information and set _file_ and _dir_ locals.' + command_options shellwords: false - banner <<-'BANNER' - Usage: stat [OPTIONS] [METH] + banner <<-'BANNER' + Usage: stat [OPTIONS] [METH] - Show method information for method METH and set _file_ and _dir_ locals. + Show method information for method METH and set _file_ and _dir_ locals. - stat hello_method - BANNER + stat hello_method + BANNER - def options(opt) - method_options(opt) - end + def options(opt) + method_options(opt) + end - def process - meth = method_object - aliases = meth.aliases + def process + meth = method_object + aliases = meth.aliases - output.puts unindent <<-EOS - Method Information: - -- - Name: #{meth.name} - Alias#{"es" if aliases.length > 1}: #{aliases.any? ? aliases.join(", ") : "None."} - Owner: #{meth.owner ? meth.owner : "Unknown"} - Visibility: #{meth.visibility} - Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"} - Arity: #{meth.arity} - Method Signature: #{meth.signature} - Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."} - EOS + output.puts unindent <<-EOS + Method Information: + -- + Name: #{meth.name} + Alias#{"es" if aliases.length > 1}: #{aliases.any? ? aliases.join(", ") : "None."} + Owner: #{meth.owner ? meth.owner : "Unknown"} + Visibility: #{meth.visibility} + Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"} + Arity: #{meth.arity} + Method Signature: #{meth.signature} + Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."} + EOS + end end - end - Pry::Commands.add_command(Pry::Command::Stat) + Pry::Commands.add_command(Pry::Command::Stat) + end end diff --git a/lib/pry/commands/switch_to.rb b/lib/pry/commands/switch_to.rb index ad3e71c1..f1243224 100644 --- a/lib/pry/commands/switch_to.rb +++ b/lib/pry/commands/switch_to.rb @@ -1,23 +1,25 @@ class Pry - class Command::SwitchTo < Pry::ClassCommand - match 'switch-to' - group 'Navigating Pry' - description 'Start a new subsession on a binding in the current stack.' + class Command + class SwitchTo < Pry::ClassCommand + match 'switch-to' + group 'Navigating Pry' + description 'Start a new subsession on a binding in the current stack.' - banner <<-'BANNER' - Start a new subsession on a binding in the current stack (numbered by nesting). - BANNER + banner <<-'BANNER' + Start a new subsession on a binding in the current stack (numbered by nesting). + BANNER - def process(selection) - selection = selection.to_i + def process(selection) + selection = selection.to_i - if selection < 0 || selection > _pry_.binding_stack.size - 1 - raise CommandError, "Invalid binding index #{selection} - use `nesting` command to view valid indices." - else - Pry.start(_pry_.binding_stack[selection]) + if selection < 0 || selection > _pry_.binding_stack.size - 1 + raise CommandError, "Invalid binding index #{selection} - use `nesting` command to view valid indices." + else + Pry.start(_pry_.binding_stack[selection]) + end end end - end - Pry::Commands.add_command(Pry::Command::SwitchTo) + Pry::Commands.add_command(Pry::Command::SwitchTo) + end end diff --git a/lib/pry/commands/toggle_color.rb b/lib/pry/commands/toggle_color.rb index 602c293d..53c2e0b2 100644 --- a/lib/pry/commands/toggle_color.rb +++ b/lib/pry/commands/toggle_color.rb @@ -1,24 +1,26 @@ class Pry - class Command::ToggleColor < Pry::ClassCommand - match 'toggle-color' - group 'Misc' - description 'Toggle syntax highlighting.' + class Command + class ToggleColor < Pry::ClassCommand + match 'toggle-color' + group 'Misc' + description 'Toggle syntax highlighting.' - banner <<-'BANNER' - Usage: toggle-color + banner <<-'BANNER' + Usage: toggle-color - Toggle syntax highlighting. - BANNER + Toggle syntax highlighting. + BANNER - def process - _pry_.color = color_toggle - output.puts "Syntax highlighting #{_pry_.color ? "on" : "off"}" - end + def process + _pry_.color = color_toggle + output.puts "Syntax highlighting #{_pry_.color ? "on" : "off"}" + end - def color_toggle - !_pry_.color - end + def color_toggle + !_pry_.color + end - Pry::Commands.add_command(self) + Pry::Commands.add_command(self) + end end end diff --git a/lib/pry/commands/watch_expression.rb b/lib/pry/commands/watch_expression.rb index 7d01e578..c0067850 100644 --- a/lib/pry/commands/watch_expression.rb +++ b/lib/pry/commands/watch_expression.rb @@ -1,107 +1,109 @@ class Pry - class Command::WatchExpression < Pry::ClassCommand - require 'pry/commands/watch_expression/expression.rb' + class Command + class WatchExpression < Pry::ClassCommand + require 'pry/commands/watch_expression/expression.rb' - match 'watch' - group 'Context' - description 'Watch the value of an expression and print a notification whenever it changes.' - command_options use_prefix: false + match 'watch' + group 'Context' + description 'Watch the value of an expression and print a notification whenever it changes.' + command_options use_prefix: false - banner <<-'BANNER' - Usage: watch [EXPRESSION] - watch - watch --delete [INDEX] + banner <<-'BANNER' + Usage: watch [EXPRESSION] + watch + watch --delete [INDEX] - watch [EXPRESSION] adds an expression to the list of those being watched. - It will be re-evaluated every time you hit enter in pry. If its value has - changed, the new value will be printed to the console. + watch [EXPRESSION] adds an expression to the list of those being watched. + It will be re-evaluated every time you hit enter in pry. If its value has + changed, the new value will be printed to the console. - This is useful if you are step-through debugging and want to see how - something changes over time. It's also useful if you're trying to write - a method inside pry and want to check that it gives the right answers - every time you redefine it. + This is useful if you are step-through debugging and want to see how + something changes over time. It's also useful if you're trying to write + a method inside pry and want to check that it gives the right answers + every time you redefine it. - watch on its own displays all the currently watched expressions and their - values, and watch --delete [INDEX] allows you to delete expressions from - the list being watched. - BANNER + watch on its own displays all the currently watched expressions and their + values, and watch --delete [INDEX] allows you to delete expressions from + the list being watched. + 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 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 - case - when opts.present?(:delete) - delete opts[:delete] - when opts.present?(:list) || args.empty? - list - else - add_hook - add_expression(args) + def process + case + when opts.present?(:delete) + delete opts[:delete] + when opts.present?(:list) || args.empty? + list + else + add_hook + add_expression(args) + end end - end - private + private - def expressions - _pry_.config.watch_expressions ||= [] - end + def expressions + _pry_.config.watch_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 + 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 - end - def list - if expressions.empty? - output.puts "No watched expressions" - else - _pry_.pager.open do |pager| - pager.puts "Listing all watched expressions:" - pager.puts "" - expressions.each_with_index do |expr, index| - pager.print with_line_numbers(expr.to_s, index + 1) + def list + if expressions.empty? + output.puts "No watched expressions" + else + _pry_.pager.open do |pager| + pager.puts "Listing all watched expressions:" + pager.puts "" + expressions.each_with_index do |expr, index| + pager.print with_line_numbers(expr.to_s, index + 1) + end + pager.puts "" end - pager.puts "" end end - end - def eval_and_print_changed(output) - expressions.each do |expr| - expr.eval! - if expr.changed? - output.puts "#{blue "watch"}: #{expr}" + def eval_and_print_changed(output) + expressions.each do |expr| + expr.eval! + if expr.changed? + output.puts "#{blue "watch"}: #{expr}" + end end end - end - # TODO: fix arguments. - # https://github.com/pry/pry/commit/b031df2f2f5850ee6e9018f33d35f3485a9b0423 - def add_expression(_arguments) - expressions << Expression.new(_pry_, target, arg_string) - output.puts "Watching #{Code.new(arg_string).highlighted}" - end + # TODO: fix arguments. + # https://github.com/pry/pry/commit/b031df2f2f5850ee6e9018f33d35f3485a9b0423 + def add_expression(_arguments) + expressions << Expression.new(_pry_, target, arg_string) + output.puts "Watching #{Code.new(arg_string).highlighted}" + end - def add_hook - hook = [:after_eval, :watch_expression] - unless _pry_.hooks.hook_exists?(*hook) - _pry_.hooks.add_hook(*hook) do |_, _pry_| - eval_and_print_changed _pry_.output + def add_hook + hook = [:after_eval, :watch_expression] + unless _pry_.hooks.hook_exists?(*hook) + _pry_.hooks.add_hook(*hook) do |_, _pry_| + eval_and_print_changed _pry_.output + end end end end - end - Pry::Commands.add_command(Pry::Command::WatchExpression) + Pry::Commands.add_command(Pry::Command::WatchExpression) + end end diff --git a/lib/pry/commands/watch_expression/expression.rb b/lib/pry/commands/watch_expression/expression.rb index 758e3c0d..f69bf10b 100644 --- a/lib/pry/commands/watch_expression/expression.rb +++ b/lib/pry/commands/watch_expression/expression.rb @@ -1,37 +1,39 @@ class Pry - class Command::WatchExpression - class Expression - attr_reader :target, :source, :value, :previous_value, :_pry_ + class Command + class WatchExpression + class Expression + attr_reader :target, :source, :value, :previous_value, :_pry_ - def initialize(_pry_, target, source) - @_pry_ = _pry_ - @target = target - @source = Code.new(source).strip - end + def initialize(_pry_, target, source) + @_pry_ = _pry_ + @target = target + @source = Code.new(source).strip + end - def eval! - @previous_value = value - @value = Pry::ColorPrinter.pp(target_eval(target, source), "") - end + def eval! + @previous_value = value + @value = Pry::ColorPrinter.pp(target_eval(target, source), "") + end - def to_s - "#{Code.new(source).highlighted.strip} => #{value}" - end + def to_s + "#{Code.new(source).highlighted.strip} => #{value}" + end - # Has the value of the expression changed? - # - # We use the pretty-printed string represenation to detect differences - # as this avoids problems with dup (causes too many differences) and == (causes too few) - def changed? - (value != previous_value) - end + # Has the value of the expression changed? + # + # We use the pretty-printed string represenation to detect differences + # as this avoids problems with dup (causes too many differences) and == (causes too few) + def changed? + (value != previous_value) + end - private + private - def target_eval(target, source) - target.eval(source) - rescue => e - e + def target_eval(target, source) + target.eval(source) + rescue => e + e + end end end end diff --git a/lib/pry/commands/whereami.rb b/lib/pry/commands/whereami.rb index 67764712..78ca3bbc 100644 --- a/lib/pry/commands/whereami.rb +++ b/lib/pry/commands/whereami.rb @@ -1,204 +1,206 @@ class Pry - class Command::Whereami < Pry::ClassCommand - def initialize(*) - super + class Command + class Whereami < Pry::ClassCommand + def initialize(*) + super - @method_code = nil - end + @method_code = nil + end - class << self - attr_accessor :method_size_cutoff - end + class << self + attr_accessor :method_size_cutoff + end - @method_size_cutoff = 30 + @method_size_cutoff = 30 - match 'whereami' - description 'Show code surrounding the current context.' - group 'Context' + match 'whereami' + description 'Show code surrounding the current context.' + group 'Context' - banner <<-'BANNER' - Usage: whereami [-qn] [LINES] + banner <<-'BANNER' + Usage: whereami [-qn] [LINES] - Describe the current location. If you use `binding.pry` inside a method then - whereami will print out the source for that method. + Describe the current location. If you use `binding.pry` inside a method then + whereami will print out the source for that method. - If a number is passed, then LINES lines before and after the current line will be - shown instead of the method itself. + If a number is passed, then LINES lines before and after the current line will be + shown instead of the method itself. - The `-q` flag can be used to suppress error messages in the case that there's - no code to show. This is used by pry in the default before_session hook to show - you when you arrive at a `binding.pry`. + The `-q` flag can be used to suppress error messages in the case that there's + no code to show. This is used by pry in the default before_session hook to show + you when you arrive at a `binding.pry`. - The `-n` flag can be used to hide line numbers so that code can be copy/pasted - effectively. + The `-n` flag can be used to hide line numbers so that code can be copy/pasted + effectively. - When pry was started on an Object and there is no associated method, whereami - will instead output a brief description of the current object. - BANNER + When pry was started on an Object and there is no associated method, whereami + will instead output a brief description of the current object. + BANNER - def setup - if target.respond_to?(:source_location) - file, @line = target.source_location - @file = expand_path(file) - else - @file = expand_path(target.eval('__FILE__')) - @line = target.eval('__LINE__') + def setup + if target.respond_to?(:source_location) + file, @line = target.source_location + @file = expand_path(file) + else + @file = expand_path(target.eval('__FILE__')) + @line = target.eval('__LINE__') + end + @method = Pry::Method.from_binding(target) end - @method = Pry::Method.from_binding(target) - end - def options(opt) - opt.on :q, :quiet, "Don't display anything in case of an error" - opt.on :n, :"no-line-numbers", "Do not display line numbers" - opt.on :m, :"method", "Show the complete source for the current method." - opt.on :c, :"class", "Show the complete source for the current class or module." - opt.on :f, :"file", "Show the complete source for the current file." - end - - def code - @code ||= if opts.present?(:m) - method_code || raise(CommandError, "Cannot find method code.") - elsif opts.present?(:c) - class_code || raise(CommandError, "Cannot find class code.") - elsif opts.present?(:f) - Pry::Code.from_file(@file) - elsif args.any? - code_window - else - default_code - end - end - - def code? - !!code - rescue MethodSource::SourceNotFoundError - false - end - - def bad_option_combination? - [opts.present?(:m), opts.present?(:f), - opts.present?(:c), args.any?].count(true) > 1 - end + def options(opt) + opt.on :q, :quiet, "Don't display anything in case of an error" + opt.on :n, :"no-line-numbers", "Do not display line numbers" + opt.on :m, :"method", "Show the complete source for the current method." + opt.on :c, :"class", "Show the complete source for the current class or module." + opt.on :f, :"file", "Show the complete source for the current file." + end - def location - "#{@file}:#{@line} #{@method && @method.name_with_owner}" - end + def code + @code ||= if opts.present?(:m) + method_code || raise(CommandError, "Cannot find method code.") + elsif opts.present?(:c) + class_code || raise(CommandError, "Cannot find class code.") + elsif opts.present?(:f) + Pry::Code.from_file(@file) + elsif args.any? + code_window + else + default_code + end + end - def process - if bad_option_combination? - raise CommandError, "Only one of -m, -c, -f, and LINES may be specified." + def code? + !!code + rescue MethodSource::SourceNotFoundError + false end - if nothing_to_do? - return - elsif internal_binding?(target) - handle_internal_binding - return + def bad_option_combination? + [opts.present?(:m), opts.present?(:f), + opts.present?(:c), args.any?].count(true) > 1 end - set_file_and_dir_locals(@file) + def location + "#{@file}:#{@line} #{@method && @method.name_with_owner}" + end - pretty_code = code.with_line_numbers(use_line_numbers?) - .with_marker(marker) - .highlighted - _pry_.pager.page( - "\n#{bold('From:')} #{location}:\n\n" << pretty_code << "\n" - ) - end + def process + if bad_option_combination? + raise CommandError, "Only one of -m, -c, -f, and LINES may be specified." + end - private + if nothing_to_do? + return + elsif internal_binding?(target) + handle_internal_binding + return + end - def nothing_to_do? - opts.quiet? && (internal_binding?(target) || !code?) - end + set_file_and_dir_locals(@file) - def use_line_numbers? - !opts.present?(:n) - end + pretty_code = code.with_line_numbers(use_line_numbers?) + .with_marker(marker) + .highlighted + _pry_.pager.page( + "\n#{bold('From:')} #{location}:\n\n" << pretty_code << "\n" + ) + end - def marker - !opts.present?(:n) && @line - end + private - def top_level? - target_self == Pry.main - end + def nothing_to_do? + opts.quiet? && (internal_binding?(target) || !code?) + end - def handle_internal_binding - if top_level? - output.puts "At the top level." - else - output.puts "Inside #{Pry.view_clip(target_self)}." + def use_line_numbers? + !opts.present?(:n) end - end - def small_method? - @method.source_range.count < self.class.method_size_cutoff - end + def marker + !opts.present?(:n) && @line + end - def default_code - if method_code && small_method? - method_code - else - code_window + def top_level? + target_self == Pry.main end - end - def code_window - Pry::Code.from_file(@file).around(@line, window_size) - 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 method_code - return @method_code if @method_code + def small_method? + @method.source_range.count < self.class.method_size_cutoff + end - if valid_method? - @method_code = Pry::Code.from_method(@method) + def default_code + if method_code && small_method? + method_code + else + code_window + end end - end - # This either returns the `target_self` - # or it returns the class of `target_self` if `target_self` is not a class. - # @return [Pry::WrappedModule] - def target_class - return Pry::WrappedModule(target_self) if target_self.is_a?(Module) + def code_window + Pry::Code.from_file(@file).around(@line, window_size) + end - Pry::WrappedModule(target_self.class) - end + def method_code + return @method_code if @method_code - def class_code - @class_code ||= - begin - mod = @method ? Pry::WrappedModule(@method.owner) : target_class - idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file } - idx && Pry::Code.from_module(mod, idx) + if valid_method? + @method_code = Pry::Code.from_method(@method) end - end + end - def valid_method? - @method && @method.source? && expand_path(@method.source_file) == @file && - @method.source_range.include?(@line) - end + # This either returns the `target_self` + # or it returns the class of `target_self` if `target_self` is not a class. + # @return [Pry::WrappedModule] + def target_class + return Pry::WrappedModule(target_self) if target_self.is_a?(Module) - def expand_path(f) - return if !f + Pry::WrappedModule(target_self.class) + end - if Pry.eval_path == f - f - else - File.expand_path(f) + def class_code + @class_code ||= + begin + mod = @method ? Pry::WrappedModule(@method.owner) : target_class + idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file } + idx && Pry::Code.from_module(mod, idx) + end end - end - def window_size - if args.empty? - _pry_.config.default_window_size - else - args.first.to_i + def valid_method? + @method && @method.source? && expand_path(@method.source_file) == @file && + @method.source_range.include?(@line) + end + + def expand_path(f) + return if !f + + if Pry.eval_path == f + f + else + File.expand_path(f) + end + end + + def window_size + if args.empty? + _pry_.config.default_window_size + else + args.first.to_i + end end end - end - Pry::Commands.add_command(Pry::Command::Whereami) - Pry::Commands.alias_command '@', 'whereami' - Pry::Commands.alias_command(/whereami[!?]+/, 'whereami') + Pry::Commands.add_command(Pry::Command::Whereami) + Pry::Commands.alias_command '@', 'whereami' + Pry::Commands.alias_command(/whereami[!?]+/, 'whereami') + end end diff --git a/lib/pry/commands/wtf.rb b/lib/pry/commands/wtf.rb index ae8d3fee..e3e9f6d8 100644 --- a/lib/pry/commands/wtf.rb +++ b/lib/pry/commands/wtf.rb @@ -1,70 +1,72 @@ class Pry - class Command::Wtf < Pry::ClassCommand - match(/wtf([?!]*)/) - group 'Context' - description 'Show the backtrace of the most recent exception.' - options listing: 'wtf?' + class Command + class Wtf < Pry::ClassCommand + match(/wtf([?!]*)/) + group 'Context' + description 'Show the backtrace of the most recent exception.' + options listing: 'wtf?' - banner <<-'BANNER' - Usage: wtf[?|!] + banner <<-'BANNER' + Usage: wtf[?|!] - Show's a few lines of the backtrace of the most recent exception (also available - as `_ex_.backtrace`). If you want to see more lines, add more question marks or - exclamation marks. + Show's a few lines of the backtrace of the most recent exception (also available + as `_ex_.backtrace`). If you want to see more lines, add more question marks or + exclamation marks. - wtf? - wtf?!???!?!? + wtf? + wtf?!???!?!? - # To see the entire backtrace, pass the `-v` or `--verbose` flag. - wtf -v - BANNER + # To see the entire backtrace, pass the `-v` or `--verbose` flag. + wtf -v + BANNER - def options(opt) - opt.on :v, :verbose, "Show the full backtrace" - end + def options(opt) + opt.on :v, :verbose, "Show the full backtrace" + end - def process - raise Pry::CommandError, "No most-recent exception" unless exception + def process + raise Pry::CommandError, "No most-recent exception" unless exception - output.puts "#{bold('Exception:')} #{exception.class}: #{exception}\n--" - if opts.verbose? - output.puts with_line_numbers(backtrace) - else - output.puts with_line_numbers(backtrace.first(size_of_backtrace)) - end + output.puts "#{bold('Exception:')} #{exception.class}: #{exception}\n--" + if opts.verbose? + output.puts with_line_numbers(backtrace) + else + output.puts with_line_numbers(backtrace.first(size_of_backtrace)) + end - if exception.respond_to? :cause - cause = exception.cause - while cause - output.puts "#{bold('Caused by:')} #{cause.class}: #{cause}\n--" - if opts.verbose? - output.puts with_line_numbers(cause.backtrace) - else - output.puts with_line_numbers(cause.backtrace.first(size_of_backtrace)) + if exception.respond_to? :cause + cause = exception.cause + while cause + output.puts "#{bold('Caused by:')} #{cause.class}: #{cause}\n--" + if opts.verbose? + output.puts with_line_numbers(cause.backtrace) + else + output.puts with_line_numbers(cause.backtrace.first(size_of_backtrace)) + end + cause = cause.cause end - cause = cause.cause end end - end - private + private - def exception - _pry_.last_exception - end + def exception + _pry_.last_exception + end - def with_line_numbers(bt) - Pry::Code.new(bt, 0, :text).with_line_numbers.to_s - end + def with_line_numbers(bt) + Pry::Code.new(bt, 0, :text).with_line_numbers.to_s + end - def backtrace - exception.backtrace - end + def backtrace + exception.backtrace + end - def size_of_backtrace - [captures[0].size, 0.5].max * 10 + def size_of_backtrace + [captures[0].size, 0.5].max * 10 + end end - end - Pry::Commands.add_command(Pry::Command::Wtf) + Pry::Commands.add_command(Pry::Command::Wtf) + end end diff --git a/lib/pry/exceptions.rb b/lib/pry/exceptions.rb index 2c9ba06a..5f469128 100644 --- a/lib/pry/exceptions.rb +++ b/lib/pry/exceptions.rb @@ -23,7 +23,7 @@ class Pry end # Catches SecurityErrors if $SAFE is set - module Pry::TooSafeException + module TooSafeException def self.===(exception) $SAFE > 0 && SecurityError === exception end diff --git a/lib/pry/helpers/base_helpers.rb b/lib/pry/helpers/base_helpers.rb index e2af9204..1072cf46 100644 --- a/lib/pry/helpers/base_helpers.rb +++ b/lib/pry/helpers/base_helpers.rb @@ -1,72 +1,74 @@ -module Pry::Helpers; end +class Pry + module Helpers + module BaseHelpers + extend self -module Pry::Helpers::BaseHelpers - extend self + def silence_warnings + old_verbose = $VERBOSE + $VERBOSE = nil + begin + yield + ensure + $VERBOSE = old_verbose + end + end - def silence_warnings - old_verbose = $VERBOSE - $VERBOSE = nil - begin - yield - ensure - $VERBOSE = old_verbose - end - end + # Acts like send but ignores any methods defined below Object or Class in the + # inheritance hierarchy. + # This is required to introspect methods on objects like Net::HTTP::Get that + # have overridden the `method` method. + def safe_send(obj, method, *args, &block) + (Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block) + end + public :safe_send - # Acts like send but ignores any methods defined below Object or Class in the - # inheritance hierarchy. - # This is required to introspect methods on objects like Net::HTTP::Get that - # have overridden the `method` method. - def safe_send(obj, method, *args, &block) - (Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block) - end - public :safe_send + def find_command(name, set = Pry::Commands) + command_match = set.find do |_, command| + (listing = command.options[:listing]) == name && listing != nil + end + command_match.last if command_match + end - def find_command(name, set = Pry::Commands) - command_match = set.find do |_, command| - (listing = command.options[:listing]) == name && listing != nil - end - command_match.last if command_match - end + def not_a_real_file?(file) + file =~ /^(\(.*\))$|^<.*>$/ || file =~ /__unknown__/ || file == "" || file == "-e" + end - def not_a_real_file?(file) - file =~ /^(\(.*\))$|^<.*>$/ || file =~ /__unknown__/ || file == "" || file == "-e" - end + def command_dependencies_met?(options) + return true if !options[:requires_gem] - def command_dependencies_met?(options) - return true if !options[:requires_gem] + Array(options[:requires_gem]).all? do |g| + Pry::Rubygem.installed?(g) + end + end - Array(options[:requires_gem]).all? do |g| - Pry::Rubygem.installed?(g) - end - end - - def use_ansi_codes? - Pry::Helpers::Platform.windows_ansi? || ENV['TERM'] && ENV['TERM'] != "dumb" - end + def use_ansi_codes? + Pry::Helpers::Platform.windows_ansi? || ENV['TERM'] && ENV['TERM'] != "dumb" + end - def colorize_code(code) - CodeRay.scan(code, :ruby).term - end + def colorize_code(code) + CodeRay.scan(code, :ruby).term + end - def highlight(string, regexp, highlight_color = :bright_yellow) - string.gsub(regexp) { |match| "<#{highlight_color}>#{match}</#{highlight_color}>" } - end + def highlight(string, regexp, highlight_color = :bright_yellow) + string.gsub(regexp) { |match| "<#{highlight_color}>#{match}</#{highlight_color}>" } + end - # formatting - def heading(text) - text = "#{text}\n--" - "\e[1m#{text}\e[0m" - end + # formatting + def heading(text) + text = "#{text}\n--" + "\e[1m#{text}\e[0m" + end - # Send the given text through the best available pager (if Pry.config.pager is - # enabled). Infers where to send the output if used as a mixin. - # DEPRECATED. - def stagger_output(text, _out = nil) - if defined?(_pry_) && _pry_ - _pry_.pager.page text - else - Pry.new.pager.page text + # Send the given text through the best available pager (if Pry.config.pager is + # enabled). Infers where to send the output if used as a mixin. + # DEPRECATED. + def stagger_output(text, _out = nil) + if defined?(_pry_) && _pry_ + _pry_.pager.page text + else + Pry.new.pager.page text + end + end end end end diff --git a/lib/pry/input_completer.rb b/lib/pry/input_completer.rb index 28bc2765..7c0df7b5 100644 --- a/lib/pry/input_completer.rb +++ b/lib/pry/input_completer.rb @@ -1,266 +1,268 @@ # taken from irb # Implements tab completion for Readline in Pry -class Pry::InputCompleter - NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/ - ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/ - SYMBOL_REGEXP = /^(:[^:.]*)$/ - SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/ - REGEX_REGEXP = /^(\/[^\/]*\/)\.([^.]*)$/ - PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/ - TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/ - CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/ - CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/ - HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/ - GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/ - VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/ +class Pry + class InputCompleter + NUMERIC_REGEXP = /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/ + ARRAY_REGEXP = /^([^\]]*\])\.([^.]*)$/ + SYMBOL_REGEXP = /^(:[^:.]*)$/ + SYMBOL_METHOD_CALL_REGEXP = /^(:[^:.]+)\.([^.]*)$/ + REGEX_REGEXP = /^(\/[^\/]*\/)\.([^.]*)$/ + PROC_OR_HASH_REGEXP = /^([^\}]*\})\.([^.]*)$/ + TOPLEVEL_LOOKUP_REGEXP = /^::([A-Z][^:\.\(]*)$/ + CONSTANT_REGEXP = /^([A-Z][A-Za-z0-9]*)$/ + CONSTANT_OR_METHOD_REGEXP = /^([A-Z].*)::([^:.]*)$/ + HEX_REGEXP = /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/ + GLOBALVARIABLE_REGEXP = /^(\$[^.]*)$/ + VARIABLE_REGEXP = /^([^."].*)\.([^.]*)$/ - ReservedWords = %w[ - BEGIN END - alias and - begin break - case class - def defined do - else elsif end ensure - false for - if in - module - next nil not - or - redo rescue retry return - self super - then true - undef unless until - when while - yield - ].freeze + ReservedWords = %w[ + BEGIN END + alias and + begin break + case class + def defined do + else elsif end ensure + false for + if in + module + next nil not + or + redo rescue retry return + self super + then true + undef unless until + when while + yield + ].freeze - Operators = %w[ - % & * ** + - / - < << <= <=> == === =~ > >= >> - [] []= ^ ! != !~ - ].freeze + Operators = %w[ + % & * ** + - / + < << <= <=> == === =~ > >= >> + [] []= ^ ! != !~ + ].freeze - WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{(" + WORD_ESCAPE_STR = " \t\n\"\\'`><=;|&{(" - def initialize(input, pry = nil) - @pry = pry - @input = input - @input.basic_word_break_characters = WORD_ESCAPE_STR if @input.respond_to?(:basic_word_break_characters=) - @input.completion_append_character = nil if @input.respond_to?(:completion_append_character=) - end + def initialize(input, pry = nil) + @pry = pry + @input = input + @input.basic_word_break_characters = WORD_ESCAPE_STR if @input.respond_to?(:basic_word_break_characters=) + @input.completion_append_character = nil if @input.respond_to?(:completion_append_character=) + end - # Return a new completion proc for use by Readline. - # rubocop:disable Metrics/AbcSize - def call(str, options = {}) - custom_completions = options[:custom_completions] || [] - # if there are multiple contexts e.g. cd 1/2/3 - # get new target for 1/2 and find candidates for 3 - path, input = build_path(str) + # Return a new completion proc for use by Readline. + # rubocop:disable Metrics/AbcSize + def call(str, options = {}) + custom_completions = options[:custom_completions] || [] + # if there are multiple contexts e.g. cd 1/2/3 + # get new target for 1/2 and find candidates for 3 + path, input = build_path(str) - if path.call.empty? - target = options[:target] - else - # Assume the user is tab-completing the 'cd' command - begin - target = Pry::ObjectPath.new(path.call, @pry.binding_stack).resolve.last - # but if that doesn't work, assume they're doing division with no spaces - rescue Pry::CommandError + if path.call.empty? target = options[:target] - end - end - - begin - bind = target - # Complete stdlib symbols - case input - when REGEX_REGEXP # Regexp - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) - candidates = Regexp.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when ARRAY_REGEXP # Array - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) - candidates = Array.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when PROC_OR_HASH_REGEXP # Proc or Hash - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) - candidates = Proc.instance_methods.collect(&:to_s) - candidates |= Hash.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when SYMBOL_REGEXP # Symbol - if Symbol.respond_to?(:all_symbols) - sym = Regexp.quote(Regexp.last_match(1)) - candidates = Symbol.all_symbols.collect { |s| ":" << s.id2name } - candidates.grep(/^#{sym}/) - else - [] - end - when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods - receiver = Regexp.last_match(1) - candidates = Object.constants.collect(&:to_s) - candidates.grep(/^#{receiver}/).collect { |e| "::" << e } - when CONSTANT_REGEXP # Constant - message = Regexp.last_match(1) - begin - context = target.eval("self") - context = context.class unless context.respond_to? :constants - candidates = context.constants.collect(&:to_s) - rescue - candidates = [] - end - candidates = candidates.grep(/^#{message}/).collect(&path) - when CONSTANT_OR_METHOD_REGEXP # Constant or class methods - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) - begin - candidates = eval("#{receiver}.constants.collect(&:to_s)", bind) - candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind) - rescue Pry::RescuableException - candidates = [] - end - candidates.grep(/^#{message}/).collect { |e| receiver + "::" + e } - when SYMBOL_METHOD_CALL_REGEXP # method call on a Symbol - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) - candidates = Symbol.instance_methods.collect(&:to_s) - select_message(path, receiver, message, candidates) - when NUMERIC_REGEXP - # Numeric - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(5)) - begin - candidates = eval(receiver, bind).methods.collect(&:to_s) - rescue Pry::RescuableException - candidates = [] - end - select_message(path, receiver, message, candidates) - when HEX_REGEXP - # Numeric(0xFFFF) - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) + else + # Assume the user is tab-completing the 'cd' command begin - candidates = eval(receiver, bind).methods.collect(&:to_s) - rescue Pry::RescuableException - candidates = [] + target = Pry::ObjectPath.new(path.call, @pry.binding_stack).resolve.last + # but if that doesn't work, assume they're doing division with no spaces + rescue Pry::CommandError + target = options[:target] end - select_message(path, receiver, message, candidates) - when GLOBALVARIABLE_REGEXP # global - regmessage = Regexp.new(Regexp.quote($1)) - candidates = global_variables.collect(&:to_s).grep(regmessage) - when VARIABLE_REGEXP # variable - receiver = Regexp.last_match(1) - message = Regexp.quote(Regexp.last_match(2)) - - gv = eval("global_variables", bind).collect(&:to_s) - lv = eval("local_variables", bind).collect(&:to_s) - cv = eval("self.class.constants", bind).collect(&:to_s) + end - if (gv | lv | cv).include?(receiver) || /^[A-Z]/ =~ receiver && /\./ !~ receiver - # foo.func and foo is local var. OR - # Foo::Bar.func + begin + bind = target + # Complete stdlib symbols + case input + when REGEX_REGEXP # Regexp + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + candidates = Regexp.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when ARRAY_REGEXP # Array + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + candidates = Array.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when PROC_OR_HASH_REGEXP # Proc or Hash + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + candidates = Proc.instance_methods.collect(&:to_s) + candidates |= Hash.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when SYMBOL_REGEXP # Symbol + if Symbol.respond_to?(:all_symbols) + sym = Regexp.quote(Regexp.last_match(1)) + candidates = Symbol.all_symbols.collect { |s| ":" << s.id2name } + candidates.grep(/^#{sym}/) + else + [] + end + when TOPLEVEL_LOOKUP_REGEXP # Absolute Constant or class methods + receiver = Regexp.last_match(1) + candidates = Object.constants.collect(&:to_s) + candidates.grep(/^#{receiver}/).collect { |e| "::" << e } + when CONSTANT_REGEXP # Constant + message = Regexp.last_match(1) begin - candidates = eval("#{receiver}.methods", bind).collect(&:to_s) + context = target.eval("self") + context = context.class unless context.respond_to? :constants + candidates = context.constants.collect(&:to_s) + rescue + candidates = [] + end + candidates = candidates.grep(/^#{message}/).collect(&path) + when CONSTANT_OR_METHOD_REGEXP # Constant or class methods + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + begin + candidates = eval("#{receiver}.constants.collect(&:to_s)", bind) + candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind) rescue Pry::RescuableException candidates = [] end - else - # func1.func2 - require 'set' - candidates = Set.new - to_ignore = ignored_modules - ObjectSpace.each_object(Module) do |m| - next if (to_ignore.include?(m) rescue true) + candidates.grep(/^#{message}/).collect { |e| receiver + "::" + e } + when SYMBOL_METHOD_CALL_REGEXP # method call on a Symbol + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + candidates = Symbol.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) + when NUMERIC_REGEXP + # Numeric + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(5)) + begin + candidates = eval(receiver, bind).methods.collect(&:to_s) + rescue Pry::RescuableException + candidates = [] + end + select_message(path, receiver, message, candidates) + when HEX_REGEXP + # Numeric(0xFFFF) + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + begin + candidates = eval(receiver, bind).methods.collect(&:to_s) + rescue Pry::RescuableException + candidates = [] + end + select_message(path, receiver, message, candidates) + when GLOBALVARIABLE_REGEXP # global + regmessage = Regexp.new(Regexp.quote($1)) + candidates = global_variables.collect(&:to_s).grep(regmessage) + when VARIABLE_REGEXP # variable + receiver = Regexp.last_match(1) + message = Regexp.quote(Regexp.last_match(2)) + + gv = eval("global_variables", bind).collect(&:to_s) + lv = eval("local_variables", bind).collect(&:to_s) + cv = eval("self.class.constants", bind).collect(&:to_s) - # jruby doesn't always provide #instance_methods() on each - # object. - if m.respond_to?(:instance_methods) - candidates.merge m.instance_methods(false).collect(&:to_s) + if (gv | lv | cv).include?(receiver) || /^[A-Z]/ =~ receiver && /\./ !~ receiver + # foo.func and foo is local var. OR + # Foo::Bar.func + begin + candidates = eval("#{receiver}.methods", bind).collect(&:to_s) + rescue Pry::RescuableException + candidates = [] + end + else + # func1.func2 + require 'set' + candidates = Set.new + to_ignore = ignored_modules + ObjectSpace.each_object(Module) do |m| + next if (to_ignore.include?(m) rescue true) + + # jruby doesn't always provide #instance_methods() on each + # object. + if m.respond_to?(:instance_methods) + candidates.merge m.instance_methods(false).collect(&:to_s) + end end end - end - select_message(path, receiver, message, candidates.sort) - when /^\.([^.]*)$/ - # Unknown(maybe String) - receiver = "" - message = Regexp.quote(Regexp.last_match(1)) - candidates = String.instance_methods(true).collect(&:to_s) - select_message(path, receiver, message, candidates) - else - candidates = eval( - "methods | private_methods | local_variables | " \ - "self.class.constants | instance_variables", - bind - ).collect(&:to_s) + select_message(path, receiver, message, candidates.sort) + when /^\.([^.]*)$/ + # Unknown(maybe String) + receiver = "" + message = Regexp.quote(Regexp.last_match(1)) + candidates = String.instance_methods(true).collect(&:to_s) + select_message(path, receiver, message, candidates) + else + candidates = eval( + "methods | private_methods | local_variables | " \ + "self.class.constants | instance_variables", + bind + ).collect(&:to_s) - if eval("respond_to?(:class_variables)", bind) - candidates += eval("class_variables", bind).collect(&:to_s) + if eval("respond_to?(:class_variables)", bind) + candidates += eval("class_variables", bind).collect(&:to_s) + end + candidates = (candidates | ReservedWords | custom_completions).grep(/^#{Regexp.quote(input)}/) + candidates.collect(&path) end - candidates = (candidates | ReservedWords | custom_completions).grep(/^#{Regexp.quote(input)}/) - candidates.collect(&path) + rescue Pry::RescuableException + [] end - rescue Pry::RescuableException - [] end - end - # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/AbcSize - def select_message(path, receiver, message, candidates) - candidates.grep(/^#{message}/).collect do |e| - case e - when /^[a-zA-Z_]/ - path.call(receiver + "." << e) - when /^[0-9]/ - when *Operators - # receiver + " " << e - end - end.compact - end + def select_message(path, receiver, message, candidates) + candidates.grep(/^#{message}/).collect do |e| + case e + when /^[a-zA-Z_]/ + path.call(receiver + "." << e) + when /^[0-9]/ + when *Operators + # receiver + " " << e + end + end.compact + end - # build_path seperates the input into two parts: path and input. - # input is the partial string that should be completed - # path is a proc that takes an input and builds a full path. - def build_path(input) - # check to see if the input is a regex - return proc { |i| i.to_s }, input if input[/\/\./] + # build_path seperates the input into two parts: path and input. + # input is the partial string that should be completed + # path is a proc that takes an input and builds a full path. + def build_path(input) + # check to see if the input is a regex + return proc { |i| i.to_s }, input if input[/\/\./] - trailing_slash = input.end_with?('/') - contexts = input.chomp('/').split(/\//) - input = contexts[-1] - path = proc do |i| - p = contexts[0..-2].push(i).join('/') - p += '/' if trailing_slash && !i.nil? - p + trailing_slash = input.end_with?('/') + contexts = input.chomp('/').split(/\//) + input = contexts[-1] + path = proc do |i| + p = contexts[0..-2].push(i).join('/') + p += '/' if trailing_slash && !i.nil? + p + end + return path, input end - return path, input - end - def ignored_modules - # We could cache the result, but IRB is not loaded by default. - # And this is very fast anyway. - # By using this approach, we avoid Module#name calls, which are - # relatively slow when there are a lot of anonymous modules defined. - s = Set.new + def ignored_modules + # We could cache the result, but IRB is not loaded by default. + # And this is very fast anyway. + # By using this approach, we avoid Module#name calls, which are + # relatively slow when there are a lot of anonymous modules defined. + s = Set.new - scanner = lambda do |m| - next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses. + scanner = lambda do |m| + next if s.include?(m) # IRB::ExtendCommandBundle::EXCB recurses. - s << m - m.constants(false).each do |c| - value = m.const_get(c) - scanner.call(value) if value.is_a?(Module) + s << m + m.constants(false).each do |c| + value = m.const_get(c) + scanner.call(value) if value.is_a?(Module) + end end - end - # FIXME: Add Pry here as well? - [:IRB, :SLex, :RubyLex, :RubyToken].each do |module_name| - next unless Object.const_defined?(module_name) + # FIXME: Add Pry here as well? + [:IRB, :SLex, :RubyLex, :RubyToken].each do |module_name| + next unless Object.const_defined?(module_name) - scanner.call(Object.const_get(module_name)) - end + scanner.call(Object.const_get(module_name)) + end - s.delete(IRB::Context) if defined?(IRB::Context) - s + s.delete(IRB::Context) if defined?(IRB::Context) + s + end end end diff --git a/lib/pry/inspector.rb b/lib/pry/inspector.rb index c56b02fe..c62f7840 100644 --- a/lib/pry/inspector.rb +++ b/lib/pry/inspector.rb @@ -1,27 +1,29 @@ -class Pry::Inspector - MAP = { - "default" => { - value: Pry::DEFAULT_PRINT, - description: <<-DESCRIPTION.each_line.map(&:lstrip!) - The default Pry inspector. It has paging and color support, and uses - pretty_inspect when printing an object. - DESCRIPTION - }, +class Pry + class Inspector + MAP = { + "default" => { + value: Pry::DEFAULT_PRINT, + description: <<-DESCRIPTION.each_line.map(&:lstrip!) + The default Pry inspector. It has paging and color support, and uses + pretty_inspect when printing an object. + DESCRIPTION + }, - "simple" => { - value: Pry::SIMPLE_PRINT, - description: <<-DESCRIPTION.each_line.map(&:lstrip) - A simple inspector that uses #puts and #inspect when printing an - object. It has no pager, color, or pretty_inspect support. - DESCRIPTION - }, + "simple" => { + value: Pry::SIMPLE_PRINT, + description: <<-DESCRIPTION.each_line.map(&:lstrip) + A simple inspector that uses #puts and #inspect when printing an + object. It has no pager, color, or pretty_inspect support. + DESCRIPTION + }, - "clipped" => { - value: Pry::CLIPPED_PRINT, - description: <<-DESCRIPTION.each_line.map(&:lstrip) - The clipped inspector has the same features as the 'simple' inspector - but prints large objects as a smaller string. - DESCRIPTION + "clipped" => { + value: Pry::CLIPPED_PRINT, + description: <<-DESCRIPTION.each_line.map(&:lstrip) + The clipped inspector has the same features as the 'simple' inspector + but prints large objects as a smaller string. + DESCRIPTION + } } - } + end end diff --git a/lib/pry/last_exception.rb b/lib/pry/last_exception.rb index 75b57913..52818997 100644 --- a/lib/pry/last_exception.rb +++ b/lib/pry/last_exception.rb @@ -6,56 +6,58 @@ # the original exception object is not modified and method calls are forwarded # to the wrapped exception object. # -class Pry::LastException < BasicObject - attr_accessor :bt_index - - def initialize(e) - @e = e - @bt_index = 0 - @file, @line = bt_source_location_for(0) - end +class Pry + class LastException < BasicObject + attr_accessor :bt_index + + def initialize(e) + @e = e + @bt_index = 0 + @file, @line = bt_source_location_for(0) + end - def method_missing(name, *args, &block) - if @e.respond_to?(name) - @e.public_send(name, *args, &block) - else - super + def method_missing(name, *args, &block) + if @e.respond_to?(name) + @e.public_send(name, *args, &block) + else + super + end end - end - def respond_to_missing?(name, include_all = false) - @e.respond_to?(name, include_all) - end + def respond_to_missing?(name, include_all = false) + @e.respond_to?(name, include_all) + end - # - # @return [String] - # returns the path to a file for the current backtrace. see {#bt_index}. - # - def file - @file - end + # + # @return [String] + # returns the path to a file for the current backtrace. see {#bt_index}. + # + def file + @file + end - # - # @return [Fixnum] - # returns the line for the current backtrace. see {#bt_index}. - # - def line - @line - end + # + # @return [Fixnum] + # returns the line for the current backtrace. see {#bt_index}. + # + def line + @line + end - # @return [Exception] - # returns the wrapped exception - # - def wrapped_exception - @e - end + # @return [Exception] + # returns the wrapped exception + # + def wrapped_exception + @e + end - def bt_source_location_for(index) - backtrace[index] =~ /(.*):(\d+)/ - [$1, $2.to_i] - end + def bt_source_location_for(index) + backtrace[index] =~ /(.*):(\d+)/ + [$1, $2.to_i] + end - def inc_bt_index - @bt_index = (@bt_index + 1) % backtrace.size + def inc_bt_index + @bt_index = (@bt_index + 1) % backtrace.size + end end end diff --git a/lib/pry/output.rb b/lib/pry/output.rb index b48a054a..db9af96e 100644 --- a/lib/pry/output.rb +++ b/lib/pry/output.rb @@ -1,50 +1,52 @@ -class Pry::Output - attr_reader :_pry_ +class Pry + class Output + attr_reader :_pry_ - def initialize(_pry_) - @_pry_ = _pry_ - @boxed_io = _pry_.config.output - end + def initialize(_pry_) + @_pry_ = _pry_ + @boxed_io = _pry_.config.output + end - def puts(*objs) - return print "\n" if objs.empty? + def puts(*objs) + return print "\n" if objs.empty? - objs.each do |obj| - if (ary = Array.try_convert(obj)) - puts(*ary) - else - print "#{obj.to_s.chomp}\n" + objs.each do |obj| + if (ary = Array.try_convert(obj)) + puts(*ary) + else + print "#{obj.to_s.chomp}\n" + end end + nil end - nil - end - def print(*objs) - objs.each do |obj| - @boxed_io.print decolorize_maybe(obj.to_s) + def print(*objs) + objs.each do |obj| + @boxed_io.print decolorize_maybe(obj.to_s) + end + nil end - nil - end - alias << print - alias write print + alias << print + alias write print - def tty? - @boxed_io.respond_to?(:tty?) && @boxed_io.tty? - end + def tty? + @boxed_io.respond_to?(:tty?) && @boxed_io.tty? + end - def method_missing(name, *args, &block) - @boxed_io.__send__(name, *args, &block) - end + def method_missing(name, *args, &block) + @boxed_io.__send__(name, *args, &block) + end - def respond_to_missing?(m, include_all = false) - @boxed_io.respond_to?(m, include_all) - end + def respond_to_missing?(m, include_all = false) + @boxed_io.respond_to?(m, include_all) + end - def decolorize_maybe(str) - if _pry_.config.color - str - else - Pry::Helpers::Text.strip_color str + def decolorize_maybe(str) + if _pry_.config.color + str + else + Pry::Helpers::Text.strip_color str + end end end end diff --git a/lib/pry/slop.rb b/lib/pry/slop.rb index fc437824..00661654 100644 --- a/lib/pry/slop.rb +++ b/lib/pry/slop.rb @@ -1,662 +1,664 @@ -class Pry::Slop - require_relative 'slop/option' - require_relative 'slop/commands' - include Enumerable - VERSION = '3.4.0' - - # The main Error class, all Exception classes inherit from this class. - class Error < StandardError; end - - # Raised when an option argument is expected but none are given. - class MissingArgumentError < Error; end - - # Raised when an option is expected/required but not present. - class MissingOptionError < Error; end - - # Raised when an argument does not match its intended match constraint. - class InvalidArgumentError < Error; end - - # Raised when an invalid option is found and the strict flag is enabled. - class InvalidOptionError < Error; end - - # Raised when an invalid command is found and the strict flag is enabled. - class InvalidCommandError < Error; end - - # Returns a default Hash of configuration options this Slop instance uses. - DEFAULT_OPTIONS = { - strict: false, - help: false, - banner: nil, - ignore_case: false, - autocreate: false, - arguments: false, - optional_arguments: false, - multiple_switches: true, - longest_flag: 0 - } - - class << self - # items - The Array of items to extract options from (default: ARGV). - # config - The Hash of configuration options to send to Slop.new(). - # block - An optional block used to add options. - # - # Examples: +class Pry + class Slop + require_relative 'slop/option' + require_relative 'slop/commands' + include Enumerable + VERSION = '3.4.0' + + # The main Error class, all Exception classes inherit from this class. + class Error < StandardError; end + + # Raised when an option argument is expected but none are given. + class MissingArgumentError < Error; end + + # Raised when an option is expected/required but not present. + class MissingOptionError < Error; end + + # Raised when an argument does not match its intended match constraint. + class InvalidArgumentError < Error; end + + # Raised when an invalid option is found and the strict flag is enabled. + class InvalidOptionError < Error; end + + # Raised when an invalid command is found and the strict flag is enabled. + class InvalidCommandError < Error; end + + # Returns a default Hash of configuration options this Slop instance uses. + DEFAULT_OPTIONS = { + strict: false, + help: false, + banner: nil, + ignore_case: false, + autocreate: false, + arguments: false, + optional_arguments: false, + multiple_switches: true, + longest_flag: 0 + } + + class << self + # items - The Array of items to extract options from (default: ARGV). + # config - The Hash of configuration options to send to Slop.new(). + # block - An optional block used to add options. + # + # Examples: + # + # Slop.parse(ARGV, :help => true) do + # on '-n', '--name', 'Your username', :argument => true + # end + # + # Returns a new instance of Slop. + def parse(items = ARGV, config = {}, &block) + parse! items.dup, config, &block + end + + # items - The Array of items to extract options from (default: ARGV). + # config - The Hash of configuration options to send to Slop.new(). + # block - An optional block used to add options. + # + # Returns a new instance of Slop. + def parse!(items = ARGV, config = {}, &block) + config, items = items, ARGV if items.is_a?(Hash) && config.empty? + slop = Pry::Slop.new config, &block + slop.parse! items + slop + end + + # Build a Slop object from a option specification. + # + # This allows you to design your options via a simple String rather + # than programatically. Do note though that with this method, you're + # unable to pass any advanced options to the on() method when creating + # options. + # + # string - The optspec String + # config - A Hash of configuration options to pass to Slop.new + # + # Examples: + # + # opts = Slop.optspec(<<-SPEC) + # ruby foo.rb [options] + # --- + # n,name= Your name + # a,age= Your age + # A,auth Sign in with auth + # p,passcode= Your secret pass code + # SPEC + # + # opts.fetch_option(:name).description #=> "Your name" + # + # Returns a new instance of Slop. + def optspec(string, config = {}) + config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/] + lines = optspec.split("\n").reject(&:empty?) + opts = Slop.new(config) + + lines.each do |line| + opt, description = line.split(' ', 2) + short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') } + opt = opts.on(short, long, description) + + if long && long.end_with?('=') + long.sub!(/\=$/, '') + opt.config[:argument] = true + end + end + + opts + end + end + + # The Hash of configuration options for this Slop instance. + attr_reader :config + + # The Array of Slop::Option objects tied to this Slop instance. + attr_reader :options + + # Create a new instance of Slop and optionally build options via a block. # - # Slop.parse(ARGV, :help => true) do - # on '-n', '--name', 'Your username', :argument => true - # end + # config - A Hash of configuration options. + # block - An optional block used to specify options. + def initialize(config = {}, &block) + @config = DEFAULT_OPTIONS.merge(config) + @options = [] + @commands = {} + @trash = [] + @triggered_options = [] + @unknown_options = [] + @callbacks = {} + @separators = {} + @runner = nil + + if block_given? + block.arity == 1 ? yield(self) : instance_eval(&block) + end + + if config[:help] + on('-h', '--help', 'Display this help message.', tail: true) do + $stderr.puts help + end + end + end + + # Is strict mode enabled? # - # Returns a new instance of Slop. - def parse(items = ARGV, config = {}, &block) - parse! items.dup, config, &block + # Returns true if strict mode is enabled, false otherwise. + def strict? + config[:strict] end - # items - The Array of items to extract options from (default: ARGV). - # config - The Hash of configuration options to send to Slop.new(). - # block - An optional block used to add options. + # Set the banner. # - # Returns a new instance of Slop. - def parse!(items = ARGV, config = {}, &block) - config, items = items, ARGV if items.is_a?(Hash) && config.empty? - slop = Pry::Slop.new config, &block - slop.parse! items - slop + # banner - The String to set the banner. + def banner=(banner) + config[:banner] = banner end - # Build a Slop object from a option specification. + # Get or set the banner. # - # This allows you to design your options via a simple String rather - # than programatically. Do note though that with this method, you're - # unable to pass any advanced options to the on() method when creating - # options. + # banner - The String to set the banner. + # + # Returns the banner String. + def banner(banner = nil) + config[:banner] = banner if banner + config[:banner] + end + + # Set the description (used for commands). # - # string - The optspec String - # config - A Hash of configuration options to pass to Slop.new + # desc - The String to set the description. + def description=(desc) + config[:description] = desc + end + + # Get or set the description (used for commands). # - # Examples: + # desc - The String to set the description. # - # opts = Slop.optspec(<<-SPEC) - # ruby foo.rb [options] - # --- - # n,name= Your name - # a,age= Your age - # A,auth Sign in with auth - # p,passcode= Your secret pass code - # SPEC - # - # opts.fetch_option(:name).description #=> "Your name" - # - # Returns a new instance of Slop. - def optspec(string, config = {}) - config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/] - lines = optspec.split("\n").reject(&:empty?) - opts = Slop.new(config) - - lines.each do |line| - opt, description = line.split(' ', 2) - short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') } - opt = opts.on(short, long, description) - - if long && long.end_with?('=') - long.sub!(/\=$/, '') - opt.config[:argument] = true - end - end + # Returns the description String. + def description(desc = nil) + config[:description] = desc if desc + config[:description] + end - opts + # Add a new command. + # + # command - The Symbol or String used to identify this command. + # options - A Hash of configuration options (see Slop::new) + # + # Returns a new instance of Slop mapped to this command. + def command(command, options = {}, &block) + @commands[command.to_s] = Pry::Slop.new(options, &block) end - end - # The Hash of configuration options for this Slop instance. - attr_reader :config - - # The Array of Slop::Option objects tied to this Slop instance. - attr_reader :options - - # Create a new instance of Slop and optionally build options via a block. - # - # config - A Hash of configuration options. - # block - An optional block used to specify options. - def initialize(config = {}, &block) - @config = DEFAULT_OPTIONS.merge(config) - @options = [] - @commands = {} - @trash = [] - @triggered_options = [] - @unknown_options = [] - @callbacks = {} - @separators = {} - @runner = nil - - if block_given? - block.arity == 1 ? yield(self) : instance_eval(&block) - end - - if config[:help] - on('-h', '--help', 'Display this help message.', tail: true) do - $stderr.puts help - end + # Parse a list of items, executing and gathering options along the way. + # + # items - The Array of items to extract options from (default: ARGV). + # block - An optional block which when used will yield non options. + # + # Returns an Array of original items. + def parse(items = ARGV, &block) + parse! items.dup, &block + items end - end - # Is strict mode enabled? - # - # Returns true if strict mode is enabled, false otherwise. - def strict? - config[:strict] - end + # Parse a list of items, executing and gathering options along the way. + # unlike parse() this method will remove any options and option arguments + # from the original Array. + # + # items - The Array of items to extract options from (default: ARGV). + # block - An optional block which when used will yield non options. + # + # Returns an Array of original items with options removed. + def parse!(items = ARGV, &block) + if items.empty? && @callbacks[:empty] + @callbacks[:empty].each { |cb| cb.call(self) } + return items + end - # Set the banner. - # - # banner - The String to set the banner. - def banner=(banner) - config[:banner] = banner - end + if (cmd = @commands[items[0]]) + return cmd.parse! items[1..-1] + end - # Get or set the banner. - # - # banner - The String to set the banner. - # - # Returns the banner String. - def banner(banner = nil) - config[:banner] = banner if banner - config[:banner] - end + items.each_with_index do |item, index| + @trash << index && break if item == '--' + autocreate(items, index) if config[:autocreate] + process_item(items, index, &block) unless @trash.include?(index) + end + items.reject!.with_index { |_item, index| @trash.include?(index) } - # Set the description (used for commands). - # - # desc - The String to set the description. - def description=(desc) - config[:description] = desc - end + missing_options = options.select { |opt| opt.required? && opt.count < 1 } + if missing_options.any? + raise MissingOptionError, + "Missing required option(s): #{missing_options.map(&:key).join(', ')}" + end - # Get or set the description (used for commands). - # - # desc - The String to set the description. - # - # Returns the description String. - def description(desc = nil) - config[:description] = desc if desc - config[:description] - end + if @unknown_options.any? + raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}" + end - # Add a new command. - # - # command - The Symbol or String used to identify this command. - # options - A Hash of configuration options (see Slop::new) - # - # Returns a new instance of Slop mapped to this command. - def command(command, options = {}, &block) - @commands[command.to_s] = Pry::Slop.new(options, &block) - end + if @triggered_options.empty? && @callbacks[:no_options] + @callbacks[:no_options].each { |cb| cb.call(self) } + end - # Parse a list of items, executing and gathering options along the way. - # - # items - The Array of items to extract options from (default: ARGV). - # block - An optional block which when used will yield non options. - # - # Returns an Array of original items. - def parse(items = ARGV, &block) - parse! items.dup, &block - items - end + @runner.call(self, items) if @runner.respond_to?(:call) - # Parse a list of items, executing and gathering options along the way. - # unlike parse() this method will remove any options and option arguments - # from the original Array. - # - # items - The Array of items to extract options from (default: ARGV). - # block - An optional block which when used will yield non options. - # - # Returns an Array of original items with options removed. - def parse!(items = ARGV, &block) - if items.empty? && @callbacks[:empty] - @callbacks[:empty].each { |cb| cb.call(self) } - return items + items end - if (cmd = @commands[items[0]]) - return cmd.parse! items[1..-1] + # Add an Option. + # + # objects - An Array with an optional Hash as the last element. + # + # Examples: + # + # on '-u', '--username=', 'Your username' + # on :v, :verbose, 'Enable verbose mode' + # + # Returns the created instance of Slop::Option. + def on(*objects, &block) + option = build_option(objects, &block) + options << option + option end + alias option on + alias opt on - items.each_with_index do |item, index| - @trash << index && break if item == '--' - autocreate(items, index) if config[:autocreate] - process_item(items, index, &block) unless @trash.include?(index) + # Fetch an options argument value. + # + # key - The Symbol or String option short or long flag. + # + # Returns the Object value for this option, or nil. + def [](key) + option = fetch_option(key) + option.value if option end - items.reject!.with_index { |_item, index| @trash.include?(index) } + alias get [] - missing_options = options.select { |opt| opt.required? && opt.count < 1 } - if missing_options.any? - raise MissingOptionError, - "Missing required option(s): #{missing_options.map(&:key).join(', ')}" + # Returns a new Hash with option flags as keys and option values as values. + # + # include_commands - If true, merge options from all sub-commands. + def to_hash(include_commands = false) + hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }] + if include_commands + @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) } + end + hash end + alias to_h to_hash - if @unknown_options.any? - raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}" + # Enumerable interface. Yields each Slop::Option. + def each(&block) + options.each(&block) end - if @triggered_options.empty? && @callbacks[:no_options] - @callbacks[:no_options].each { |cb| cb.call(self) } + # Specify code to be executed when these options are parsed. + # + # callable - An object responding to a call method. + # + # yields - The instance of Slop parsing these options + # An Array of unparsed arguments + # + # Example: + # + # Slop.parse do + # on :v, :verbose + # + # run do |opts, args| + # puts "Arguments: #{args.inspect}" if opts.verbose? + # end + # end + def run(callable = nil, &block) + @runner = callable || block + unless @runner.respond_to?(:call) + raise ArgumentError, "You must specify a callable object or a block to #run" + end end - @runner.call(self, items) if @runner.respond_to?(:call) + # Check for an options presence. + # + # Examples: + # + # opts.parse %w( --foo ) + # opts.present?(:foo) #=> true + # opts.present?(:bar) #=> false + # + # Returns true if all of the keys are present in the parsed arguments. + def present?(*keys) + keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 } + end - items - end + # Override this method so we can check if an option? method exists. + # + # Returns true if this option key exists in our list of options. + def respond_to_missing?(method_name, include_all = false) + options.any? { |o| o.key == method_name.to_s.chop } || super + end - # Add an Option. - # - # objects - An Array with an optional Hash as the last element. - # - # Examples: - # - # on '-u', '--username=', 'Your username' - # on :v, :verbose, 'Enable verbose mode' - # - # Returns the created instance of Slop::Option. - def on(*objects, &block) - option = build_option(objects, &block) - options << option - option - end - alias option on - alias opt on - - # Fetch an options argument value. - # - # key - The Symbol or String option short or long flag. - # - # Returns the Object value for this option, or nil. - def [](key) - option = fetch_option(key) - option.value if option - end - alias get [] + # Fetch a list of options which were missing from the parsed list. + # + # Examples: + # + # opts = Slop.new do + # on :n, :name= + # on :p, :password= + # end + # + # opts.parse %w[ --name Lee ] + # opts.missing #=> ['password'] + # + # Returns an Array of Strings representing missing options. + def missing + (options - @triggered_options).map(&:key) + end - # Returns a new Hash with option flags as keys and option values as values. - # - # include_commands - If true, merge options from all sub-commands. - def to_hash(include_commands = false) - hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }] - if include_commands - @commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) } + # Fetch a Slop::Option object. + # + # key - The Symbol or String option key. + # + # Examples: + # + # opts.on(:foo, 'Something fooey', :argument => :optional) + # opt = opts.fetch_option(:foo) + # opt.class #=> Slop::Option + # opt.accepts_optional_argument? #=> true + # + # Returns an Option or nil if none were found. + def fetch_option(key) + options.find { |option| [option.long, option.short].include?(clean(key)) } end - hash - end - alias to_h to_hash - # Enumerable interface. Yields each Slop::Option. - def each(&block) - options.each(&block) - end + # Fetch a Slop object associated with this command. + # + # command - The String or Symbol name of the command. + # + # Examples: + # + # opts.command :foo do + # on :v, :verbose, 'Enable verbose mode' + # end + # + # # ruby run.rb foo -v + # opts.fetch_command(:foo).verbose? #=> true + def fetch_command(command) + @commands[command.to_s] + end - # Specify code to be executed when these options are parsed. - # - # callable - An object responding to a call method. - # - # yields - The instance of Slop parsing these options - # An Array of unparsed arguments - # - # Example: - # - # Slop.parse do - # on :v, :verbose - # - # run do |opts, args| - # puts "Arguments: #{args.inspect}" if opts.verbose? - # end - # end - def run(callable = nil, &block) - @runner = callable || block - unless @runner.respond_to?(:call) - raise ArgumentError, "You must specify a callable object or a block to #run" + # Add a callback. + # + # label - The Symbol identifier to attach this callback. + # + # Returns nothing. + def add_callback(label, &block) + (@callbacks[label] ||= []) << block end - end - # Check for an options presence. - # - # Examples: - # - # opts.parse %w( --foo ) - # opts.present?(:foo) #=> true - # opts.present?(:bar) #=> false - # - # Returns true if all of the keys are present in the parsed arguments. - def present?(*keys) - keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 } - end + # Add string separators between options. + # + # text - The String text to print. + def separator(text) + if @separators[options.size] + @separators[options.size] << "\n#{text}" + else + @separators[options.size] = text + end + end - # Override this method so we can check if an option? method exists. - # - # Returns true if this option key exists in our list of options. - def respond_to_missing?(method_name, include_all = false) - options.any? { |o| o.key == method_name.to_s.chop } || super - end + # Print a handy Slop help string. + # + # Returns the banner followed by available option help strings. + def to_s + heads = options.reject(&:tail?) + tails = (options - heads) + opts = (heads + tails).select(&:help).map(&:to_s) + optstr = opts.each_with_index.map do |o, i| + (str = @separators[i + 1]) ? [o, str].join("\n") : o + end.join("\n") + + if @commands.any? + optstr << "\n" if !optstr.empty? + optstr << "\nAvailable commands:\n\n" + optstr << commands_to_help + optstr << "\n\nSee `<command> --help` for more information on a specific command." + end - # Fetch a list of options which were missing from the parsed list. - # - # Examples: - # - # opts = Slop.new do - # on :n, :name= - # on :p, :password= - # end - # - # opts.parse %w[ --name Lee ] - # opts.missing #=> ['password'] - # - # Returns an Array of Strings representing missing options. - def missing - (options - @triggered_options).map(&:key) - end + banner = config[:banner] + banner = "Usage: #{File.basename($0, '.*')}#{' [command]' if @commands.any?} [options]" if banner.nil? + if banner + "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}" + else + optstr + end + end + alias help to_s - # Fetch a Slop::Option object. - # - # key - The Symbol or String option key. - # - # Examples: - # - # opts.on(:foo, 'Something fooey', :argument => :optional) - # opt = opts.fetch_option(:foo) - # opt.class #=> Slop::Option - # opt.accepts_optional_argument? #=> true - # - # Returns an Option or nil if none were found. - def fetch_option(key) - options.find { |option| [option.long, option.short].include?(clean(key)) } - end + private - # Fetch a Slop object associated with this command. - # - # command - The String or Symbol name of the command. - # - # Examples: - # - # opts.command :foo do - # on :v, :verbose, 'Enable verbose mode' - # end - # - # # ruby run.rb foo -v - # opts.fetch_command(:foo).verbose? #=> true - def fetch_command(command) - @commands[command.to_s] - end + # Convenience method for present?(:option). + # + # Examples: + # + # opts.parse %( --verbose ) + # opts.verbose? #=> true + # opts.other? #=> false + # + # Returns true if this option is present. If this method does not end + # with a ? character it will instead call super(). + def method_missing(method, *args, &block) + meth = method.to_s + if meth.end_with?('?') + meth.chop! + present?(meth) || present?(meth.tr('_', '-')) + else + super + end + end - # Add a callback. - # - # label - The Symbol identifier to attach this callback. - # - # Returns nothing. - def add_callback(label, &block) - (@callbacks[label] ||= []) << block - end + # Process a list item, figure out if it's an option, execute any + # callbacks, assign any option arguments, and do some sanity checks. + # + # items - The Array of items to process. + # index - The current Integer index of the item we want to process. + # block - An optional block which when passed will yield non options. + # + # Returns nothing. + def process_item(items, index, &block) + return unless (item = items[index]) - # Add string separators between options. - # - # text - The String text to print. - def separator(text) - if @separators[options.size] - @separators[options.size] << "\n#{text}" - else - @separators[options.size] = text - end - end + option, argument = extract_option(item) if item.start_with?('-') - # Print a handy Slop help string. - # - # Returns the banner followed by available option help strings. - def to_s - heads = options.reject(&:tail?) - tails = (options - heads) - opts = (heads + tails).select(&:help).map(&:to_s) - optstr = opts.each_with_index.map do |o, i| - (str = @separators[i + 1]) ? [o, str].join("\n") : o - end.join("\n") - - if @commands.any? - optstr << "\n" if !optstr.empty? - optstr << "\nAvailable commands:\n\n" - optstr << commands_to_help - optstr << "\n\nSee `<command> --help` for more information on a specific command." - end - - banner = config[:banner] - banner = "Usage: #{File.basename($0, '.*')}#{' [command]' if @commands.any?} [options]" if banner.nil? - if banner - "#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}" - else - optstr - end - end - alias help to_s - - private - - # Convenience method for present?(:option). - # - # Examples: - # - # opts.parse %( --verbose ) - # opts.verbose? #=> true - # opts.other? #=> false - # - # Returns true if this option is present. If this method does not end - # with a ? character it will instead call super(). - def method_missing(method, *args, &block) - meth = method.to_s - if meth.end_with?('?') - meth.chop! - present?(meth) || present?(meth.tr('_', '-')) - else - super - end - end + if option + option.count += 1 unless item.start_with?('--no-') + option.count += 1 if option.key[0, 3] == "no-" + @trash << index + @triggered_options << option - # Process a list item, figure out if it's an option, execute any - # callbacks, assign any option arguments, and do some sanity checks. - # - # items - The Array of items to process. - # index - The current Integer index of the item we want to process. - # block - An optional block which when passed will yield non options. - # - # Returns nothing. - def process_item(items, index, &block) - return unless (item = items[index]) - - option, argument = extract_option(item) if item.start_with?('-') - - if option - option.count += 1 unless item.start_with?('--no-') - option.count += 1 if option.key[0, 3] == "no-" - @trash << index - @triggered_options << option - - if option.expects_argument? - argument ||= items.at(index + 1) - - if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/ - raise MissingArgumentError, "#{option.key} expects an argument" - end + if option.expects_argument? + argument ||= items.at(index + 1) - execute_option(option, argument, index, item) - elsif option.accepts_optional_argument? - argument ||= items.at(index + 1) + if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/ + raise MissingArgumentError, "#{option.key} expects an argument" + end - if argument && argument =~ /\A([^\-?]|-\d)+/ execute_option(option, argument, index, item) + elsif option.accepts_optional_argument? + argument ||= items.at(index + 1) + + if argument && argument =~ /\A([^\-?]|-\d)+/ + execute_option(option, argument, index, item) + else + option.call(nil) + end + elsif config[:multiple_switches] && argument + execute_multiple_switches(option, argument, index) else + option.value = option.count > 0 option.call(nil) end - elsif config[:multiple_switches] && argument - execute_multiple_switches(option, argument, index) else - option.value = option.count > 0 - option.call(nil) + @unknown_options << item if strict? && item =~ /\A--?/ + yield(item) if block && !@trash.include?(index) end - else - @unknown_options << item if strict? && item =~ /\A--?/ - yield(item) if block && !@trash.include?(index) end - end - # Execute an option, firing off callbacks and assigning arguments. - # - # option - The Slop::Option object found by #process_item. - # argument - The argument Object to assign to this option. - # index - The current Integer index of the object we're processing. - # item - The optional String item we're processing. - # - # Returns nothing. - def execute_option(option, argument, index, item = nil) - if !option - if config[:multiple_switches] && strict? - raise InvalidOptionError, "Unknown option -#{item}" + # Execute an option, firing off callbacks and assigning arguments. + # + # option - The Slop::Option object found by #process_item. + # argument - The argument Object to assign to this option. + # index - The current Integer index of the object we're processing. + # item - The optional String item we're processing. + # + # Returns nothing. + def execute_option(option, argument, index, item = nil) + if !option + if config[:multiple_switches] && strict? + raise InvalidOptionError, "Unknown option -#{item}" + end + + return end - return - end + if argument + unless item && item.end_with?("=#{argument}") + @trash << index + 1 unless option.argument_in_value + end + option.value = argument + else + option.value = option.count > 0 + end - if argument - unless item && item.end_with?("=#{argument}") - @trash << index + 1 unless option.argument_in_value + if option.match? && !argument.match(option.config[:match]) + raise InvalidArgumentError, "#{argument} is an invalid argument" end - option.value = argument - else - option.value = option.count > 0 + + option.call(option.value) end - if option.match? && !argument.match(option.config[:match]) - raise InvalidArgumentError, "#{argument} is an invalid argument" + # Execute a `-abc` type option where a, b and c are all options. This + # method is only executed if the multiple_switches argument is true. + # + # option - The first Option object. + # argument - The argument to this option. (Split into multiple Options). + # index - The index of the current item being processed. + # + # Returns nothing. + def execute_multiple_switches(option, argument, index) + execute_option(option, nil, index) + argument.split('').each do |key| + next unless (opt = fetch_option(key)) + + opt.count += 1 + execute_option(opt, nil, index, key) + end end - option.call(option.value) - end + # Extract an option from a flag. + # + # flag - The flag key used to extract an option. + # + # Returns an Array of [option, argument]. + def extract_option(flag) + option = fetch_option(flag) + option ||= fetch_option(flag.downcase) if config[:ignore_case] + option ||= fetch_option(flag.gsub(/([^-])-/, '\1_')) + + unless option + case flag + when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/ + option = fetch_option(Regexp.last_match(1)) + argument = Regexp.last_match(2) || false + option.argument_in_value = true if option + end + end - # Execute a `-abc` type option where a, b and c are all options. This - # method is only executed if the multiple_switches argument is true. - # - # option - The first Option object. - # argument - The argument to this option. (Split into multiple Options). - # index - The index of the current item being processed. - # - # Returns nothing. - def execute_multiple_switches(option, argument, index) - execute_option(option, nil, index) - argument.split('').each do |key| - next unless (opt = fetch_option(key)) - - opt.count += 1 - execute_option(opt, nil, index, key) + [option, argument] end - end - # Extract an option from a flag. - # - # flag - The flag key used to extract an option. - # - # Returns an Array of [option, argument]. - def extract_option(flag) - option = fetch_option(flag) - option ||= fetch_option(flag.downcase) if config[:ignore_case] - option ||= fetch_option(flag.gsub(/([^-])-/, '\1_')) - - unless option - case flag - when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/ - option = fetch_option(Regexp.last_match(1)) - argument = Regexp.last_match(2) || false - option.argument_in_value = true if option + # Autocreate an option on the fly. See the :autocreate Slop config option. + # + # items - The Array of items we're parsing. + # index - The current Integer index for the item we're processing. + # + # Returns nothing. + def autocreate(items, index) + flag = items[index] + if !fetch_option(flag) && !@trash.include?(index) + option = build_option(Array(flag)) + argument = items[index + 1] + option.config[:argument] = (argument && argument !~ /\A--?/) + option.config[:autocreated] = true + options << option end end - [option, argument] - end + # Build an option from a list of objects. + # + # objects - An Array of objects used to build this option. + # + # Returns a new instance of Slop::Option. + def build_option(objects, &block) + config = {} + config[:argument] = true if @config[:arguments] + config[:optional_argument] = true if @config[:optional_arguments] + + if objects.last.is_a?(Hash) + config.merge!(objects.last) + objects.pop + end + short = extract_short_flag(objects, config) + long = extract_long_flag(objects, config) + desc = objects[0].respond_to?(:to_str) ? objects.shift : nil - # Autocreate an option on the fly. See the :autocreate Slop config option. - # - # items - The Array of items we're parsing. - # index - The current Integer index for the item we're processing. - # - # Returns nothing. - def autocreate(items, index) - flag = items[index] - if !fetch_option(flag) && !@trash.include?(index) - option = build_option(Array(flag)) - argument = items[index + 1] - option.config[:argument] = (argument && argument !~ /\A--?/) - option.config[:autocreated] = true - options << option + Option.new(self, short, long, desc, config, &block) end - end - # Build an option from a list of objects. - # - # objects - An Array of objects used to build this option. - # - # Returns a new instance of Slop::Option. - def build_option(objects, &block) - config = {} - config[:argument] = true if @config[:arguments] - config[:optional_argument] = true if @config[:optional_arguments] - - if objects.last.is_a?(Hash) - config.merge!(objects.last) - objects.pop - end - short = extract_short_flag(objects, config) - long = extract_long_flag(objects, config) - desc = objects[0].respond_to?(:to_str) ? objects.shift : nil - - Option.new(self, short, long, desc, config, &block) - end - - # Extract the short flag from an item. - # - # objects - The Array of objects passed from #build_option. - # config - The Hash of configuration options built in #build_option. - def extract_short_flag(objects, config) - flag = clean(objects.first) + # Extract the short flag from an item. + # + # objects - The Array of objects passed from #build_option. + # config - The Hash of configuration options built in #build_option. + def extract_short_flag(objects, config) + flag = clean(objects.first) + + if flag.size == 2 && flag.end_with?('=') + config[:argument] ||= true + flag.chop! + end - if flag.size == 2 && flag.end_with?('=') - config[:argument] ||= true - flag.chop! + if flag.size == 1 + objects.shift + flag + end end - if flag.size == 1 - objects.shift - flag + # Extract the long flag from an item. + # + # objects - The Array of objects passed from #build_option. + # config - The Hash of configuration options built in #build_option. + def extract_long_flag(objects, config) + flag = objects.first.to_s + if flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/ + config[:argument] ||= true if flag.end_with?('=') + config[:optional_argument] = true if flag.end_with?('=?') + objects.shift + clean(flag).sub(/\=\??\z/, '') + end end - end - # Extract the long flag from an item. - # - # objects - The Array of objects passed from #build_option. - # config - The Hash of configuration options built in #build_option. - def extract_long_flag(objects, config) - flag = objects.first.to_s - if flag =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\??\z/ - config[:argument] ||= true if flag.end_with?('=') - config[:optional_argument] = true if flag.end_with?('=?') - objects.shift - clean(flag).sub(/\=\??\z/, '') + # Remove any leading -- characters from a string. + # + # object - The Object we want to cast to a String and clean. + # + # Returns the newly cleaned String with leading -- characters removed. + def clean(object) + object.to_s.sub(/\A--?/, '') end - end - # Remove any leading -- characters from a string. - # - # object - The Object we want to cast to a String and clean. - # - # Returns the newly cleaned String with leading -- characters removed. - def clean(object) - object.to_s.sub(/\A--?/, '') - end - - def commands_to_help - padding = 0 - @commands.each { |c, _| padding = c.size if c.size > padding } - @commands.map do |cmd, opts| - " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}" - end.join("\n") + def commands_to_help + padding = 0 + @commands.each { |c, _| padding = c.size if c.size > padding } + @commands.map do |cmd, opts| + " #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}" + end.join("\n") + end end end diff --git a/lib/pry/slop/commands.rb b/lib/pry/slop/commands.rb index 9d3159b2..9e93941d 100644 --- a/lib/pry/slop/commands.rb +++ b/lib/pry/slop/commands.rb @@ -1,194 +1,196 @@ -class Pry::Slop - class Commands - include Enumerable - - attr_reader :config, :commands, :arguments - attr_writer :banner - - # Create a new instance of Slop::Commands and optionally build - # Slop instances via a block. Any configuration options used in - # this method will be the default configuration options sent to - # each Slop object created. - # - # config - An optional configuration Hash. - # block - Optional block used to define commands. - # - # Examples: - # - # commands = Slop::Commands.new do - # on :new do - # on '-o', '--outdir=', 'The output directory' - # on '-v', '--verbose', 'Enable verbose mode' - # end - # - # on :generate do - # on '--assets', 'Generate assets', :default => true - # end - # - # global do - # on '-D', '--debug', 'Enable debug mode', :default => false - # end - # end - # - # commands[:new].class #=> Slop - # commands.parse - # - def initialize(config = {}, &block) - @config = config - @commands = {} - @banner = nil - @triggered_command = nil - - warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\ - "Slop version 4. Check out http://injekt.github.com/slop/#commands for "\ - "a new implementation of commands." - - if block_given? - block.arity == 1 ? yield(self) : instance_eval(&block) +class Pry + class Slop + class Commands + include Enumerable + + attr_reader :config, :commands, :arguments + attr_writer :banner + + # Create a new instance of Slop::Commands and optionally build + # Slop instances via a block. Any configuration options used in + # this method will be the default configuration options sent to + # each Slop object created. + # + # config - An optional configuration Hash. + # block - Optional block used to define commands. + # + # Examples: + # + # commands = Slop::Commands.new do + # on :new do + # on '-o', '--outdir=', 'The output directory' + # on '-v', '--verbose', 'Enable verbose mode' + # end + # + # on :generate do + # on '--assets', 'Generate assets', :default => true + # end + # + # global do + # on '-D', '--debug', 'Enable debug mode', :default => false + # end + # end + # + # commands[:new].class #=> Slop + # commands.parse + # + def initialize(config = {}, &block) + @config = config + @commands = {} + @banner = nil + @triggered_command = nil + + warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\ + "Slop version 4. Check out http://injekt.github.com/slop/#commands for "\ + "a new implementation of commands." + + if block_given? + block.arity == 1 ? yield(self) : instance_eval(&block) + end end - end - # Optionally set the banner for this command help output. - # - # banner - The String text to set the banner. - # - # Returns the String banner if one is set. - def banner(banner = nil) - @banner = banner if banner - @banner - end + # Optionally set the banner for this command help output. + # + # banner - The String text to set the banner. + # + # Returns the String banner if one is set. + def banner(banner = nil) + @banner = banner if banner + @banner + end - # Add a Slop instance for a specific command. - # - # command - A String or Symbol key used to identify this command. - # config - A Hash of configuration options to pass to Slop. - # block - An optional block used to pass options to Slop. - # - # Returns the newly created Slop instance mapped to command. - def on(command, config = {}, &block) - commands[command.to_s] = Slop.new(@config.merge(config), &block) - end + # Add a Slop instance for a specific command. + # + # command - A String or Symbol key used to identify this command. + # config - A Hash of configuration options to pass to Slop. + # block - An optional block used to pass options to Slop. + # + # Returns the newly created Slop instance mapped to command. + def on(command, config = {}, &block) + commands[command.to_s] = Slop.new(@config.merge(config), &block) + end - # Add a Slop instance used when no other commands exist. - # - # config - A Hash of configuration options to pass to Slop. - # block - An optional block used to pass options to Slop. - # - # Returns the newly created Slop instance mapped to default. - def default(config = {}, &block) - on('default', config, &block) - end + # Add a Slop instance used when no other commands exist. + # + # config - A Hash of configuration options to pass to Slop. + # block - An optional block used to pass options to Slop. + # + # Returns the newly created Slop instance mapped to default. + def default(config = {}, &block) + on('default', config, &block) + end - # Add a global Slop instance. - # - # config - A Hash of configuration options to pass to Slop. - # block - An optional block used to pass options to Slop. - # - # Returns the newly created Slop instance mapped to global. - def global(config = {}, &block) - on('global', config, &block) - end + # Add a global Slop instance. + # + # config - A Hash of configuration options to pass to Slop. + # block - An optional block used to pass options to Slop. + # + # Returns the newly created Slop instance mapped to global. + def global(config = {}, &block) + on('global', config, &block) + end - # Fetch the instance of Slop tied to a command. - # - # key - The String or Symbol key used to locate this command. - # - # Returns the Slop instance if this key is found, nil otherwise. - def [](key) - commands[key.to_s] - end - alias get [] - - # Check for a command presence. - # - # Examples: - # - # cmds.parse %w( foo ) - # cmds.present?(:foo) #=> true - # cmds.present?(:bar) #=> false - # - # Returns true if the given key is present in the parsed arguments. - def present?(key) - key.to_s == @triggered_command - end + # Fetch the instance of Slop tied to a command. + # + # key - The String or Symbol key used to locate this command. + # + # Returns the Slop instance if this key is found, nil otherwise. + def [](key) + commands[key.to_s] + end + alias get [] + + # Check for a command presence. + # + # Examples: + # + # cmds.parse %w( foo ) + # cmds.present?(:foo) #=> true + # cmds.present?(:bar) #=> false + # + # Returns true if the given key is present in the parsed arguments. + def present?(key) + key.to_s == @triggered_command + end - # Enumerable interface. - def each(&block) - @commands.each(&block) - end + # Enumerable interface. + def each(&block) + @commands.each(&block) + end - # Parse a list of items. - # - # items - The Array of items to parse. - # - # Returns the original Array of items. - def parse(items = ARGV) - parse! items.dup - items - end + # Parse a list of items. + # + # items - The Array of items to parse. + # + # Returns the original Array of items. + def parse(items = ARGV) + parse! items.dup + items + end - # Parse a list of items, removing any options or option arguments found. - # - # items - The Array of items to parse. - # - # Returns the original Array of items with options removed. - def parse!(items = ARGV) - if (opts = commands[items[0].to_s]) - @triggered_command = items.shift - execute_arguments! items - opts.parse! items - execute_global_opts! items - else - if (opts = commands['default']) + # Parse a list of items, removing any options or option arguments found. + # + # items - The Array of items to parse. + # + # Returns the original Array of items with options removed. + def parse!(items = ARGV) + if (opts = commands[items[0].to_s]) + @triggered_command = items.shift + execute_arguments! items opts.parse! items + execute_global_opts! items else - if config[:strict] && items[0] - raise InvalidCommandError, "Unknown command `#{items[0]}`" + if (opts = commands['default']) + opts.parse! items + else + if config[:strict] && items[0] + raise InvalidCommandError, "Unknown command `#{items[0]}`" + end end + execute_global_opts! items end - execute_global_opts! items + items end - items - end - # Returns a nested Hash with Slop options and values. See Slop#to_hash. - def to_hash - Hash[commands.map { |k, v| [k.to_sym, v.to_hash] }] - end - - # Returns the help String. - def to_s - defaults = commands.delete('default') - globals = commands.delete('global') - helps = commands.reject { |_, v| v.options.none? } - if globals && globals.options.any? - helps['Global options'] = globals.to_s + # Returns a nested Hash with Slop options and values. See Slop#to_hash. + def to_hash + Hash[commands.map { |k, v| [k.to_sym, v.to_hash] }] end - if defaults && defaults.options.any? - helps['Other options'] = defaults.to_s + + # Returns the help String. + def to_s + defaults = commands.delete('default') + globals = commands.delete('global') + helps = commands.reject { |_, v| v.options.none? } + if globals && globals.options.any? + helps['Global options'] = globals.to_s + end + if defaults && defaults.options.any? + helps['Other options'] = defaults.to_s + end + banner = @banner ? "#{@banner}\n" : "" + banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n") end - banner = @banner ? "#{@banner}\n" : "" - banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n") - end - alias help to_s + alias help to_s - # Returns the inspection String. - def inspect - "#<Slop::Commands #{config.inspect} #{commands.values.map(&:inspect)}>" - end + # Returns the inspection String. + def inspect + "#<Slop::Commands #{config.inspect} #{commands.values.map(&:inspect)}>" + end - private + private - # Returns nothing. - def execute_arguments!(items) - @arguments = items.take_while { |arg| !arg.start_with?('-') } - items.shift @arguments.size - end + # Returns nothing. + def execute_arguments!(items) + @arguments = items.take_while { |arg| !arg.start_with?('-') } + items.shift @arguments.size + end - # Returns nothing. - def execute_global_opts!(items) - if (global_opts = commands['global']) - global_opts.parse! items + # Returns nothing. + def execute_global_opts!(items) + if (global_opts = commands['global']) + global_opts.parse! items + end end end end diff --git a/lib/pry/slop/option.rb b/lib/pry/slop/option.rb index 54a08196..a94847dc 100644 --- a/lib/pry/slop/option.rb +++ b/lib/pry/slop/option.rb @@ -1,208 +1,210 @@ -class Pry::Slop - class Option - # The default Hash of configuration options this class uses. - DEFAULT_OPTIONS = { - argument: false, - optional_argument: false, - tail: false, - default: nil, - callback: nil, - delimiter: ',', - limit: 0, - match: nil, - optional: true, - required: false, - as: String, - autocreated: false - } - - attr_reader :short, :long, :description, :config, :types - attr_accessor :count, :argument_in_value - - # Incapsulate internal option information, mainly used to store - # option specific configuration data, most of the meat of this - # class is found in the #value method. - # - # slop - The instance of Slop tied to this Option. - # short - The String or Symbol short flag. - # long - The String or Symbol long flag. - # description - The String description text. - # config - A Hash of configuration options. - # block - An optional block used as a callback. - def initialize(slop, short, long, description, config = {}, &block) - @slop = slop - @short = short - @long = long - @description = description - @config = DEFAULT_OPTIONS.merge(config) - @count = 0 - @callback = block_given? ? block : config[:callback] - @value = nil - - @types = { - string: proc { |v| v.to_s }, - symbol: proc { |v| v.to_sym }, - integer: proc { |v| value_to_integer(v) }, - float: proc { |v| value_to_float(v) }, - range: proc { |v| value_to_range(v) }, - count: proc { @count } +class Pry + class Slop + class Option + # The default Hash of configuration options this class uses. + DEFAULT_OPTIONS = { + argument: false, + optional_argument: false, + tail: false, + default: nil, + callback: nil, + delimiter: ',', + limit: 0, + match: nil, + optional: true, + required: false, + as: String, + autocreated: false } - if long && long.size > @slop.config[:longest_flag] - @slop.config[:longest_flag] = long.size - end + attr_reader :short, :long, :description, :config, :types + attr_accessor :count, :argument_in_value + + # Incapsulate internal option information, mainly used to store + # option specific configuration data, most of the meat of this + # class is found in the #value method. + # + # slop - The instance of Slop tied to this Option. + # short - The String or Symbol short flag. + # long - The String or Symbol long flag. + # description - The String description text. + # config - A Hash of configuration options. + # block - An optional block used as a callback. + def initialize(slop, short, long, description, config = {}, &block) + @slop = slop + @short = short + @long = long + @description = description + @config = DEFAULT_OPTIONS.merge(config) + @count = 0 + @callback = block_given? ? block : config[:callback] + @value = nil + + @types = { + string: proc { |v| v.to_s }, + symbol: proc { |v| v.to_sym }, + integer: proc { |v| value_to_integer(v) }, + float: proc { |v| value_to_float(v) }, + range: proc { |v| value_to_range(v) }, + count: proc { @count } + } + + if long && long.size > @slop.config[:longest_flag] + @slop.config[:longest_flag] = long.size + end - @config.each_key do |key| - predicate = :"#{key}?" - unless self.class.method_defined? predicate - self.class.__send__(:define_method, predicate) { !!@config[key] } + @config.each_key do |key| + predicate = :"#{key}?" + unless self.class.method_defined? predicate + self.class.__send__(:define_method, predicate) { !!@config[key] } + end end end - end - # Returns true if this option expects an argument. - def expects_argument? - config[:argument] && config[:argument] != :optional - end + # Returns true if this option expects an argument. + def expects_argument? + config[:argument] && config[:argument] != :optional + end - # Returns true if this option accepts an optional argument. - def accepts_optional_argument? - config[:optional_argument] || config[:argument] == :optional - end + # Returns true if this option accepts an optional argument. + def accepts_optional_argument? + config[:optional_argument] || config[:argument] == :optional + end - # Returns the String flag of this option. Preferring the long flag. - def key - long || short - end + # Returns the String flag of this option. Preferring the long flag. + def key + long || short + end - # Call this options callback if one exists, and it responds to call(). - # - # Returns nothing. - def call(*objects) - @callback.call(*objects) if @callback.respond_to?(:call) - end + # Call this options callback if one exists, and it responds to call(). + # + # Returns nothing. + def call(*objects) + @callback.call(*objects) if @callback.respond_to?(:call) + end - # Set the new argument value for this option. - # - # We use this setter method to handle concatenating lists. That is, - # when an array type is specified and used more than once, values from - # both options will be grouped together and flattened into a single array. - def value=(new_value) - if config[:as].to_s.casecmp('array') == 0 - @value ||= [] - - if new_value.respond_to?(:split) - @value.concat new_value.split(config[:delimiter], config[:limit]) + # Set the new argument value for this option. + # + # We use this setter method to handle concatenating lists. That is, + # when an array type is specified and used more than once, values from + # both options will be grouped together and flattened into a single array. + def value=(new_value) + if config[:as].to_s.casecmp('array') == 0 + @value ||= [] + + if new_value.respond_to?(:split) + @value.concat new_value.split(config[:delimiter], config[:limit]) + end + else + @value = new_value end - else - @value = new_value end - end - # Fetch the argument value for this option. - # - # Returns the Object once any type conversions have taken place. - def value - value = @value.nil? ? config[:default] : @value + # Fetch the argument value for this option. + # + # Returns the Object once any type conversions have taken place. + def value + value = @value.nil? ? config[:default] : @value - if [true, false, nil].include?(value) && config[:as].to_s != 'count' - return value - end + if [true, false, nil].include?(value) && config[:as].to_s != 'count' + return value + end - type = config[:as] - if type.respond_to?(:call) - type.call(value) - else - if (callable = types[type.to_s.downcase.to_sym]) - callable.call(value) + type = config[:as] + if type.respond_to?(:call) + type.call(value) else - value + if (callable = types[type.to_s.downcase.to_sym]) + callable.call(value) + else + value + end end end - end - # Returns the help String for this option. - def to_s - return config[:help] if config[:help].respond_to?(:to_str) + # Returns the help String for this option. + def to_s + return config[:help] if config[:help].respond_to?(:to_str) + + out = " #{short ? "-#{short}, " : ' ' * 4}" - out = " #{short ? "-#{short}, " : ' ' * 4}" + if long + out << "--#{long}" + size = long.size + diff = @slop.config[:longest_flag] - size + out << (' ' * (diff + 6)) + else + out << (' ' * (@slop.config[:longest_flag] + 8)) + end - if long - out << "--#{long}" - size = long.size - diff = @slop.config[:longest_flag] - size - out << (' ' * (diff + 6)) - else - out << (' ' * (@slop.config[:longest_flag] + 8)) + "#{out}#{description}" end + alias help to_s - "#{out}#{description}" - end - alias help to_s + # Returns the String inspection text. + def inspect + "#<Slop::Option [-#{short} | --#{long}" \ + "#{'=' if expects_argument?}#{'=?' if accepts_optional_argument?}]" \ + " (#{description}) #{config.inspect}" + end - # Returns the String inspection text. - def inspect - "#<Slop::Option [-#{short} | --#{long}" \ - "#{'=' if expects_argument?}#{'=?' if accepts_optional_argument?}]" \ - " (#{description}) #{config.inspect}" - end + private - private - - # Convert an object to an Integer if possible. - # - # value - The Object we want to convert to an integer. - # - # Returns the Integer value if possible to convert, else a zero. - def value_to_integer(value) - if @slop.strict? - begin - Integer(value.to_s, 10) - rescue ArgumentError - raise InvalidArgumentError, "#{value} could not be coerced into Integer" + # Convert an object to an Integer if possible. + # + # value - The Object we want to convert to an integer. + # + # Returns the Integer value if possible to convert, else a zero. + def value_to_integer(value) + if @slop.strict? + begin + Integer(value.to_s, 10) + rescue ArgumentError + raise InvalidArgumentError, "#{value} could not be coerced into Integer" + end + else + value.to_s.to_i end - else - value.to_s.to_i end - end - # Convert an object to a Float if possible. - # - # value - The Object we want to convert to a float. - # - # Returns the Float value if possible to convert, else a zero. - def value_to_float(value) - if @slop.strict? - begin - Float(value.to_s) - rescue ArgumentError - raise InvalidArgumentError, "#{value} could not be coerced into Float" + # Convert an object to a Float if possible. + # + # value - The Object we want to convert to a float. + # + # Returns the Float value if possible to convert, else a zero. + def value_to_float(value) + if @slop.strict? + begin + Float(value.to_s) + rescue ArgumentError + raise InvalidArgumentError, "#{value} could not be coerced into Float" + end + else + value.to_s.to_f end - else - value.to_s.to_f end - end - # Convert an object to a Range if possible. - # - # value - The Object we want to convert to a range. - # - # Returns the Range value if one could be found, else the original object. - def value_to_range(value) - case value.to_s - when /\A(\-?\d+)\z/ - Range.new(Regexp.last_match(1).to_i, Regexp.last_match(1).to_i) - when /\A(-?\d+?)(\.\.\.?|-|,)(-?\d+)\z/ - Range.new( - Regexp.last_match(1).to_i, - Regexp.last_match(3).to_i, - Regexp.last_match(2) == '...' - ) - else - if @slop.strict? - raise InvalidArgumentError, "#{value} could not be coerced into Range" + # Convert an object to a Range if possible. + # + # value - The Object we want to convert to a range. + # + # Returns the Range value if one could be found, else the original object. + def value_to_range(value) + case value.to_s + when /\A(\-?\d+)\z/ + Range.new(Regexp.last_match(1).to_i, Regexp.last_match(1).to_i) + when /\A(-?\d+?)(\.\.\.?|-|,)(-?\d+)\z/ + Range.new( + Regexp.last_match(1).to_i, + Regexp.last_match(3).to_i, + Regexp.last_match(2) == '...' + ) else - value + if @slop.strict? + raise InvalidArgumentError, "#{value} could not be coerced into Range" + else + value + end end end end diff --git a/lib/pry/terminal.rb b/lib/pry/terminal.rb index f57268ff..35e3d257 100644 --- a/lib/pry/terminal.rb +++ b/lib/pry/terminal.rb @@ -1,92 +1,94 @@ # coding: utf-8 -class Pry::Terminal - class << self - # Return a pair of [rows, columns] which gives the size of the window. - # - # If the window size cannot be determined, return nil. - def screen_size - rows, cols = actual_screen_size - if rows.to_i != 0 && cols.to_i != 0 - [rows.to_i, cols.to_i] - else - nil +class Pry + class Terminal + class << self + # Return a pair of [rows, columns] which gives the size of the window. + # + # If the window size cannot be determined, return nil. + def screen_size + rows, cols = actual_screen_size + if rows.to_i != 0 && cols.to_i != 0 + [rows.to_i, cols.to_i] + else + nil + end end - end - # Return a screen size or a default if that fails. - def size! default = [27, 80] - screen_size || default - end - - # Return a screen width or the default if that fails. - def width! - size![1] - end + # Return a screen size or a default if that fails. + def size! default = [27, 80] + screen_size || default + end - # Return a screen height or the default if that fails. - def height! - size![0] - end + # Return a screen width or the default if that fails. + def width! + size![1] + end - def actual_screen_size - # The best way, if possible (requires non-jruby ≥1.9 or io-console gem) - screen_size_according_to_io_console || - # Fall back to the old standby, though it might be stale: - screen_size_according_to_env || - # Fall further back, though this one is also out of date without something - # calling Readline.set_screen_size - screen_size_according_to_readline || - # Windows users can otherwise run ansicon and get a decent answer: - screen_size_according_to_ansicon_env - end + # Return a screen height or the default if that fails. + def height! + size![0] + end - def screen_size_according_to_io_console - return if Pry::Helpers::Platform.jruby? + def actual_screen_size + # The best way, if possible (requires non-jruby ≥1.9 or io-console gem) + screen_size_according_to_io_console || + # Fall back to the old standby, though it might be stale: + screen_size_according_to_env || + # Fall further back, though this one is also out of date without something + # calling Readline.set_screen_size + screen_size_according_to_readline || + # Windows users can otherwise run ansicon and get a decent answer: + screen_size_according_to_ansicon_env + end - begin - require 'io/console' + def screen_size_according_to_io_console + return if Pry::Helpers::Platform.jruby? begin - if $stdout.respond_to?(:tty?) && $stdout.tty? && $stdout.respond_to?(:winsize) - $stdout.winsize + require 'io/console' + + begin + if $stdout.respond_to?(:tty?) && $stdout.tty? && $stdout.respond_to?(:winsize) + $stdout.winsize + end + rescue Errno::EOPNOTSUPP + # $stdout is probably a socket, which doesn't support #winsize. end - rescue Errno::EOPNOTSUPP - # $stdout is probably a socket, which doesn't support #winsize. + rescue LoadError + # They probably don't have the io/console stdlib or the io-console gem. + # We'll keep trying. end - rescue LoadError - # They probably don't have the io/console stdlib or the io-console gem. - # We'll keep trying. end - end - - def screen_size_according_to_env - size = [ENV['LINES'] || ENV['ROWS'], ENV['COLUMNS']] - size if nonzero_column?(size) - end - def screen_size_according_to_readline - if defined?(Readline) && Readline.respond_to?(:get_screen_size) - size = Readline.get_screen_size + def screen_size_according_to_env + size = [ENV['LINES'] || ENV['ROWS'], ENV['COLUMNS']] size if nonzero_column?(size) end - rescue Java::JavaLang::NullPointerException - # This rescue won't happen on jrubies later than: - # https://github.com/jruby/jruby/pull/436 - nil - end - def screen_size_according_to_ansicon_env - return unless ENV['ANSICON'] =~ /\((.*)x(.*)\)/ + def screen_size_according_to_readline + if defined?(Readline) && Readline.respond_to?(:get_screen_size) + size = Readline.get_screen_size + size if nonzero_column?(size) + end + rescue Java::JavaLang::NullPointerException + # This rescue won't happen on jrubies later than: + # https://github.com/jruby/jruby/pull/436 + nil + end + + def screen_size_according_to_ansicon_env + return unless ENV['ANSICON'] =~ /\((.*)x(.*)\)/ - size = [Regexp.last_match(2), Regexp.last_match(1)] - size if nonzero_column?(size) - end + size = [Regexp.last_match(2), Regexp.last_match(1)] + size if nonzero_column?(size) + end - private + private - def nonzero_column?(size) - size[1].to_i > 0 + def nonzero_column?(size) + size[1].to_i > 0 + end end end end diff --git a/lib/pry/testable.rb b/lib/pry/testable.rb index 695eded3..f13826c1 100644 --- a/lib/pry/testable.rb +++ b/lib/pry/testable.rb @@ -2,69 +2,71 @@ # if you're testing pry plugin you should require pry by yourself, no? require 'pry' if not defined?(Pry) -module Pry::Testable - extend self - require_relative "testable/pry_tester" - require_relative "testable/evalable" - require_relative "testable/mockable" - require_relative "testable/variables" - require_relative "testable/utility" +class Pry + module Testable + extend self + require_relative "testable/pry_tester" + require_relative "testable/evalable" + require_relative "testable/mockable" + require_relative "testable/variables" + require_relative "testable/utility" - # - # When {Pry::Testable} is included into another module or class, - # the following modules are also included: {Pry::Testable::Mockable}, - # {Pry::Testable::Evalable}, {Pry::Testable::Variables}, and - # {Pry::Testable::Utility}. - # - # @note - # Each of the included modules mentioned above may also be used - # standalone or in a pick-and-mix fashion. - # - # @param [Module] mod - # A class or module. - # - # @return [void] - # - def self.included(mod) - mod.module_eval do - include Pry::Testable::Mockable - include Pry::Testable::Evalable - include Pry::Testable::Variables - include Pry::Testable::Utility + # + # When {Pry::Testable} is included into another module or class, + # the following modules are also included: {Pry::Testable::Mockable}, + # {Pry::Testable::Evalable}, {Pry::Testable::Variables}, and + # {Pry::Testable::Utility}. + # + # @note + # Each of the included modules mentioned above may also be used + # standalone or in a pick-and-mix fashion. + # + # @param [Module] mod + # A class or module. + # + # @return [void] + # + def self.included(mod) + mod.module_eval do + include Pry::Testable::Mockable + include Pry::Testable::Evalable + include Pry::Testable::Variables + include Pry::Testable::Utility + end end - end - TEST_DEFAULTS = { - color: false, - pager: false, - should_load_rc: false, - should_load_local_rc: false, - correct_indent: false, - collison_warning: false, - history: { - should_load: false, - should_save: false + TEST_DEFAULTS = { + color: false, + pager: false, + should_load_rc: false, + should_load_local_rc: false, + correct_indent: false, + collison_warning: false, + history: { + should_load: false, + should_save: false + } } - } - private_constant :TEST_DEFAULTS + private_constant :TEST_DEFAULTS - # - # Sets various configuration options that make Pry optimal for a test - # environment, see source code for complete details. - # - # @return [void] - # - def self.set_testenv_variables - Pry.config = Pry::Config.from_hash TEST_DEFAULTS, Pry::Config.defaults - Pry.config.hooks = Pry::Hooks.new - end + # + # Sets various configuration options that make Pry optimal for a test + # environment, see source code for complete details. + # + # @return [void] + # + def self.set_testenv_variables + Pry.config = Pry::Config.from_hash TEST_DEFAULTS, Pry::Config.defaults + Pry.config.hooks = Pry::Hooks.new + end - # - # Reset the Pry configuration to their default values. - # - # @return [void] - # - def self.unset_testenv_variables - Pry.config = Pry::Config.from_hash({}, Pry::Config.defaults) + # + # Reset the Pry configuration to their default values. + # + # @return [void] + # + def self.unset_testenv_variables + Pry.config = Pry::Config.from_hash({}, Pry::Config.defaults) + end end end diff --git a/lib/pry/testable/evalable.rb b/lib/pry/testable/evalable.rb index a1cb84a3..78f43824 100644 --- a/lib/pry/testable/evalable.rb +++ b/lib/pry/testable/evalable.rb @@ -1,15 +1,19 @@ -module Pry::Testable::Evalable - def pry_tester(*args, &block) - if args.length == 0 || args[0].is_a?(Hash) - args.unshift(Pry.toplevel_binding) - end - Pry::Testable::PryTester.new(*args).tap do |t| - t.singleton_class.class_eval(&block) if block - end - end +class Pry + module Testable + module Evalable + def pry_tester(*args, &block) + if args.length == 0 || args[0].is_a?(Hash) + args.unshift(Pry.toplevel_binding) + end + Pry::Testable::PryTester.new(*args).tap do |t| + t.singleton_class.class_eval(&block) if block + end + end - def pry_eval(*eval_strs) - b = String === eval_strs.first ? Pry.toplevel_binding : Pry.binding_for(eval_strs.shift) - pry_tester(b).eval(*eval_strs) + def pry_eval(*eval_strs) + b = String === eval_strs.first ? Pry.toplevel_binding : Pry.binding_for(eval_strs.shift) + pry_tester(b).eval(*eval_strs) + end + end end end diff --git a/lib/pry/testable/mockable.rb b/lib/pry/testable/mockable.rb index 5e8880ec..ebcf8673 100644 --- a/lib/pry/testable/mockable.rb +++ b/lib/pry/testable/mockable.rb @@ -1,14 +1,18 @@ -module Pry::Testable::Mockable - def mock_command(cmd, args = [], opts = {}) - output = StringIO.new - pry = Pry.new(output: output) - ret = cmd.new(opts.merge(pry_instance: pry, output: output)).call_safely(*args) - Struct.new(:output, :return).new(output.string, ret) - end +class Pry + module Testable + module Mockable + def mock_command(cmd, args = [], opts = {}) + output = StringIO.new + pry = Pry.new(output: output) + ret = cmd.new(opts.merge(pry_instance: pry, output: output)).call_safely(*args) + Struct.new(:output, :return).new(output.string, ret) + end - def mock_exception(*mock_backtrace) - StandardError.new.tap do |e| - e.define_singleton_method(:backtrace) { mock_backtrace } + def mock_exception(*mock_backtrace) + StandardError.new.tap do |e| + e.define_singleton_method(:backtrace) { mock_backtrace } + end + end end end end diff --git a/lib/pry/testable/pry_tester.rb b/lib/pry/testable/pry_tester.rb index f82aaf37..e6c3a61e 100644 --- a/lib/pry/testable/pry_tester.rb +++ b/lib/pry/testable/pry_tester.rb @@ -1,73 +1,77 @@ -class Pry::Testable::PryTester - extend Pry::Forwardable - attr_reader :pry, :out - def_delegators :@pry, :eval_string, :eval_string= +class Pry + module Testable + class PryTester + extend Pry::Forwardable + attr_reader :pry, :out + def_delegators :@pry, :eval_string, :eval_string= - def initialize(target = TOPLEVEL_BINDING, options = {}) - @pry = Pry.new(options.merge(target: target)) - @history = options[:history] - @pry.inject_sticky_locals! - reset_output - end + def initialize(target = TOPLEVEL_BINDING, options = {}) + @pry = Pry.new(options.merge(target: target)) + @history = options[:history] + @pry.inject_sticky_locals! + reset_output + end - def eval(*strs) - reset_output - result = nil + def eval(*strs) + reset_output + result = nil - strs.flatten.each do |str| - # Check for space prefix. See #1369. - if str !~ /^\s\S/ - str = "#{str.strip}\n" - end - @history.push str if @history + strs.flatten.each do |str| + # Check for space prefix. See #1369. + if str !~ /^\s\S/ + str = "#{str.strip}\n" + end + @history.push str if @history - if @pry.process_command(str) - result = last_command_result_or_output - else - result = @pry.evaluate_ruby(str) + if @pry.process_command(str) + result = last_command_result_or_output + else + result = @pry.evaluate_ruby(str) + end + end + + result end - end - result - end + def push(*lines) + Array(lines).flatten.each do |line| + @pry.eval(line) + end + end - def push(*lines) - Array(lines).flatten.each do |line| - @pry.eval(line) - end - end + def push_binding(context) + @pry.push_binding context + end - def push_binding(context) - @pry.push_binding context - end + def last_output + @out.string if @out + end - def last_output - @out.string if @out - end + def process_command(command_str) + @pry.process_command(command_str) || raise("Not a valid command") + last_command_result_or_output + end - def process_command(command_str) - @pry.process_command(command_str) || raise("Not a valid command") - last_command_result_or_output - end + def last_command_result + result = Pry.current[:pry_cmd_result] + result.retval if result + end - def last_command_result - result = Pry.current[:pry_cmd_result] - result.retval if result - end + protected - protected + def last_command_result_or_output + result = last_command_result + if result != Pry::Command::VOID_VALUE + result + else + last_output + end + end - def last_command_result_or_output - result = last_command_result - if result != Pry::Command::VOID_VALUE - result - else - last_output + def reset_output + @out = StringIO.new + @pry.output = @out + end end end - - def reset_output - @out = StringIO.new - @pry.output = @out - end end diff --git a/lib/pry/testable/utility.rb b/lib/pry/testable/utility.rb index d78730c0..bf038831 100644 --- a/lib/pry/testable/utility.rb +++ b/lib/pry/testable/utility.rb @@ -1,26 +1,30 @@ -module Pry::Testable::Utility - # - # Creates a Tempfile then unlinks it after the block has yielded. - # - # @yieldparam [String] file - # The path of the temp file - # - # @return [void] - # - def temp_file(ext = '.rb') - file = Tempfile.open(['pry', ext]) - yield file - ensure - file.close(true) if file - end +class Pry + module Testable + module Utility + # + # Creates a Tempfile then unlinks it after the block has yielded. + # + # @yieldparam [String] file + # The path of the temp file + # + # @return [void] + # + def temp_file(ext = '.rb') + file = Tempfile.open(['pry', ext]) + yield file + ensure + file.close(true) if file + end - def unindent(*args) - Pry::Helpers::CommandHelpers.unindent(*args) - end + def unindent(*args) + Pry::Helpers::CommandHelpers.unindent(*args) + end - def inner_scope - catch(:inner_scope) do - yield -> { throw(:inner_scope, self) } + def inner_scope + catch(:inner_scope) do + yield -> { throw(:inner_scope, self) } + end + end end end end diff --git a/lib/pry/testable/variables.rb b/lib/pry/testable/variables.rb index 4131d359..89405c60 100644 --- a/lib/pry/testable/variables.rb +++ b/lib/pry/testable/variables.rb @@ -1,46 +1,50 @@ -module Pry::Testable::Variables - # - # @example - # temporary_constants(:Foo, :Bar) do - # Foo = Class.new(RuntimeError) - # Bar = Class.new(RuntimeError) - # end - # Foo # => NameError - # Bar # => NameError - # - # @param [Array<Symbol>] names - # An array of constant names that be defined by a block, - # and removed by this method afterwards. - # - # @return [void] - # - def temporary_constants(*names) - names.each do |name| - Object.remove_const name if Object.const_defined?(name) - end - yield - ensure - names.each do |name| - Object.remove_const name if Object.const_defined?(name) - end - end +class Pry + module Testable + module Variables + # + # @example + # temporary_constants(:Foo, :Bar) do + # Foo = Class.new(RuntimeError) + # Bar = Class.new(RuntimeError) + # end + # Foo # => NameError + # Bar # => NameError + # + # @param [Array<Symbol>] names + # An array of constant names that be defined by a block, + # and removed by this method afterwards. + # + # @return [void] + # + def temporary_constants(*names) + names.each do |name| + Object.remove_const name if Object.const_defined?(name) + end + yield + ensure + names.each do |name| + Object.remove_const name if Object.const_defined?(name) + end + end - # - # @param [String] name - # The name of a variable. - # - # @param [String] value - # Its value. - # - # @param [Binding] b - # The binding object to insert a variable into. - # - # @return [void] - # - def insert_variable(name, value, b) - Pry.current[:pry_local] = value - b.eval("#{name} = ::Pry.current[:pry_local]") - ensure - Pry.current[:pry_local] = nil + # + # @param [String] name + # The name of a variable. + # + # @param [String] value + # Its value. + # + # @param [Binding] b + # The binding object to insert a variable into. + # + # @return [void] + # + def insert_variable(name, value, b) + Pry.current[:pry_local] = value + b.eval("#{name} = ::Pry.current[:pry_local]") + ensure + Pry.current[:pry_local] = nil + end + end end end diff --git a/spec/commands/gist_spec.rb b/spec/commands/gist_spec.rb index 6f98d176..5f6a0015 100644 --- a/spec/commands/gist_spec.rb +++ b/spec/commands/gist_spec.rb @@ -11,7 +11,7 @@ describe 'gist' do end # In absence of normal mocking, just monkeysmash these with no undoing after. - module ::Gist + module ::Gist # rubocop:disable Style/ClassAndModuleChildren class << self def login!; Pad.gist_calls[:login!] = true end diff --git a/spec/commands/show_source_spec.rb b/spec/commands/show_source_spec.rb index 71a26c4f..112c8629 100644 --- a/spec/commands/show_source_spec.rb +++ b/spec/commands/show_source_spec.rb @@ -730,10 +730,12 @@ describe "show-source" do describe "real class-based commands" do before do + # rubocop:disable Style/ClassAndModuleChildren class ::TemporaryCommand < Pry::ClassCommand match 'temp-command' def process() :body_of_temp end end + # rubocop:enable Style/ClassAndModuleChildren Pry.config.commands.add_command(::TemporaryCommand) end diff --git a/spec/editor_spec.rb b/spec/editor_spec.rb index 007a740c..ebad40fd 100644 --- a/spec/editor_spec.rb +++ b/spec/editor_spec.rb @@ -1,8 +1,10 @@ require 'pathname' describe Pry::Editor do - class Pry::Editor - public :build_editor_invocation_string + class Pry + class Editor + public :build_editor_invocation_string + end end before do |