Module: PryStack

Defined in:
lib/pry-stack.rb,
lib/pry-stack/commands.rb,
lib/pry-stack/frame_manager.rb,
lib/pry-stack/when_started_hook.rb

Defined Under Namespace

Modules: FrameHelpers Classes: FrameManager, WhenStartedHook

Constant Summary collapse

Commands =
Pry::CommandSet.new do
  create_command "up", "Go up to the caller's context." do
    include FrameHelpers

    banner "      Usage: up [OPTIONS]\n        Go up to the caller's context. Accepts optional numeric parameter for how many frames to move up.\n        Also accepts a string (regex) instead of numeric; for jumping to nearest parent method frame which matches the regex.\n        e.g: up      #=> Move up 1 stack frame.\n        e.g: up 3    #=> Move up 2 stack frames.\n        e.g: up meth #=> Jump to nearest parent stack frame whose method matches /meth/ regex, i.e `my_method`.\n    BANNER\n\n    def process\n      inc = args.first.nil? ? \"1\" : args.first\n\n      if !frame_manager\n        raise Pry::CommandError, \"Nowhere to go!\"\n      else\n        if inc =~ /\\d+/\n          frame_manager.change_frame_to frame_manager.binding_index + inc.to_i\n        elsif match = /^([A-Z]+[^#.]*)(#|\\.)(.+)$/.match(inc)\n          new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :up)\n          frame_manager.change_frame_to new_frame_index\n        elsif inc =~ /^[^-].*$/\n          new_frame_index = find_frame_by_regex(Regexp.new(inc), :up)\n          frame_manager.change_frame_to new_frame_index\n        end\n      end\n    end\n  end\n\n  create_command \"down\", \"Go down to the callee's context.\" do\n    include FrameHelpers\n\n    banner <<-BANNER\n      Usage: down [OPTIONS]\n        Go down to the callee's context. Accepts optional numeric parameter for how many frames to move down.\n        Also accepts a string (regex) instead of numeric; for jumping to nearest child method frame which matches the regex.\n        e.g: down      #=> Move down 1 stack frame.\n        e.g: down 3    #=> Move down 2 stack frames.\n        e.g: down meth #=> Jump to nearest child stack frame whose method matches /meth/ regex, i.e `my_method`.\n    BANNER\n\n    def process\n      inc = args.first.nil? ? \"1\" : args.first\n\n      if !frame_manager\n        raise Pry::CommandError, \"Nowhere to go!\"\n      else\n        if inc =~ /\\d+/\n          if frame_manager.binding_index - inc.to_i < 0\n            raise Pry::CommandError, \"At bottom of stack, cannot go further!\"\n          else\n            frame_manager.change_frame_to frame_manager.binding_index - inc.to_i\n          end\n        elsif match = /^([A-Z]+[^#.]*)(#|\\.)(.+)$/.match(inc)\n          new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :down)\n          frame_manager.change_frame_to new_frame_index\n        elsif inc =~ /^[^-].*$/\n          new_frame_index = find_frame_by_regex(Regexp.new(inc), :down)\n          frame_manager.change_frame_to new_frame_index\n        end\n      end\n    end\n  end\n\n  create_command \"frame\", \"Switch to a particular frame.\" do\n    include FrameHelpers\n\n    banner <<-BANNER\n      Usage: frame [OPTIONS]\n        Switch to a particular frame. Accepts numeric parameter (or regex for method name) for the target frame to switch to (use with show-stack).\n        Negative frame numbers allowed. When given no parameter show information about the current frame.\n\n        e.g: frame 4         #=> jump to the 4th frame\n        e.g: frame meth      #=> jump to nearest parent stack frame whose method matches /meth/ regex, i.e `my_method`\n        e.g: frame -2        #=> jump to the second-to-last frame\n        e.g: frame           #=> show information info about current frame\n    BANNER\n\n    def process\n      if !frame_manager\n        raise Pry::CommandError, \"nowhere to go!\"\n      else\n\n        if args[0] =~ /\\d+/\n          frame_manager.change_frame_to args[0].to_i\n        elsif match = /^([A-Z]+[^#.]*)(#|\\.)(.+)$/.match(args[0])\n          new_frame_index = find_frame_by_object_regex(Regexp.new(match[1]), Regexp.new(match[3]), :up)\n          frame_manager.change_frame_to new_frame_index\n        elsif args[0] =~ /^[^-].*$/\n          new_frame_index = find_frame_by_regex(Regexp.new(args[0]), :up)\n          frame_manager.change_frame_to new_frame_index\n        else\n          output.puts \"#\#{frame_manager.binding_index} \#{frame_info(target, true)}\"\n        end\n      end\n    end\n  end\n\n  create_command \"stack\", \"Show all frames\" do\n    include FrameHelpers\n\n    banner <<-BANNER\n      Usage: stack [OPTIONS]\n        Show all accessible stack frames.\n        e.g: show-stack -v\n    BANNER\n\n    def options(opt)\n      opt.on :v, :verbose, \"Include extra information.\"\n      opt.on :H, :head, \"Display the first N stack frames (defaults to 10).\", :optional_argument => true, :as => Integer, :default => 10\n      opt.on :T, :tail, \"Display the last N stack frames (defaults to 10).\", :optional_argument => true, :as => Integer, :default => 10\n      opt.on :c, :current, \"Display N frames either side of current frame (default to 5).\", :optional_argument => true, :as => Integer, :default => 5\n    end\n\n    def memoized_info(index, b, verbose)\n      frame_manager.user[:frame_info] ||= Hash.new { |h, k| h[k] = [] }\n\n      if verbose\n        frame_manager.user[:frame_info][:v][index]      ||= frame_info(b, verbose)\n      else\n        frame_manager.user[:frame_info][:normal][index] ||= frame_info(b, verbose)\n      end\n    end\n\n    private :memoized_info\n\n    # @return [Array<Fixnum, Array<Binding>>] Return tuple of\n    #   base_frame_index and the array of frames.\n    def selected_stack_frames\n      if opts.present?(:head)\n        [0, frame_manager.bindings[0..(opts[:head] - 1)]]\n\n      elsif opts.present?(:tail)\n        tail = opts[:tail]\n        if tail > frame_manager.bindings.size\n          tail = frame_manager.bindings.size\n        end\n\n        base_frame_index = frame_manager.bindings.size - tail\n        [base_frame_index, frame_manager.bindings[base_frame_index..-1]]\n\n      elsif opts.present?(:current)\n        first_frame_index = frame_manager.binding_index - (opts[:current])\n        first_frame_index = 0 if first_frame_index < 0\n        last_frame_index = frame_manager.binding_index + (opts[:current])\n        [first_frame_index, frame_manager.bindings[first_frame_index..last_frame_index]]\n\n      else\n        [0, frame_manager.bindings]\n      end\n    end\n\n    private :selected_stack_frames\n\n    def process\n      if !frame_manager\n        output.puts \"No caller stack available!\"\n      else\n        content = \"\"\n        content << \"\\n\#{text.bold(\"Showing all accessible frames in stack (\#{frame_manager.bindings.size} in total):\")}\\n--\\n\"\n\n        base_frame_index, frames = selected_stack_frames\n        frames.each_with_index.to_a.reverse.each do |b, index|\n          i = index + base_frame_index\n          if i == frame_manager.binding_index\n            content << \"=> #\#{i} \#{memoized_info(i, b, opts[:v])}\\n\"\n          else\n            content << \"   #\#{i} \#{memoized_info(i, b, opts[:v])}\\n\"\n          end\n        end\n\n        stagger_output content\n      end\n    end\n\n  end\n\n  alias_command \"show-stack\", \"stack\"\n\nend\n"

Class Method Summary collapse

Class Method Details

.bindings_equal?(b1, b2) ⇒ Boolean

Simple test to check whether two Binding objects are equal.

Parameters:

  • b1 (Binding)

    First binding.

  • b2 (Binding)

    Second binding.

Returns:

  • (Boolean)

    Whether the Bindings are equal.



104
105
106
107
108
109
# File 'lib/pry-stack.rb', line 104

def bindings_equal?(b1, b2)
  (b1.eval('self').equal?(b2.eval('self'))) &&
    (b1.eval('__method__') == b2.eval('__method__')) &&
    (b1.eval('local_variables').map { |v| b1.eval("#{v}") }.equal?(
     b2.eval('local_variables').map { |v| b2.eval("#{v}") }))
end

.clear_frame_managers(_pry_) ⇒ Object Also known as: delete_frame_managers

Clear the stack of frame managers for the Pry instance

Parameters:

  • _pry_ (Pry)

    The Pry instance associated with the frame managers



88
89
90
91
# File 'lib/pry-stack.rb', line 88

def clear_frame_managers(_pry_)
  pop_frame_manager(_pry_) until frame_managers(_pry_).empty?
  frame_hash.delete(_pry_) # this line should be unnecessary!
end

.create_and_push_frame_manager(bindings, _pry_, options = {}) ⇒ Object

Create a Pry::FrameManager object and push it onto the frame manager stack for the relevant _pry_ instance.

Parameters:

  • bindings (Array)

    The array of bindings (frames)

  • _pry_ (Pry)

    The Pry instance associated with the frame manager



30
31
32
33
34
35
# File 'lib/pry-stack.rb', line 30

def create_and_push_frame_manager(bindings, _pry_, options={})
  fm = FrameManager.new(bindings, _pry_)
  frame_hash[_pry_].push fm
  push_helper(fm, options)
  fm
end

.frame_hashHash

Returns The hash storing all frames for all Pry instances for the current thread.

Returns:

  • (Hash)

    The hash storing all frames for all Pry instances for the current thread.



14
15
16
# File 'lib/pry-stack.rb', line 14

def frame_hash
  Thread.current[:__pry_frame_managers__] ||= Hash.new { |h, k| h[k] = [] }
end

.frame_manager(_pry_) ⇒ PryStack::FrameManager

Returns The currently active frame manager.

Returns:



96
97
98
# File 'lib/pry-stack.rb', line 96

def frame_manager(_pry_)
  frame_hash[_pry_].last
end

.frame_managers(_pry_) ⇒ Array

Return the complete frame manager stack for the Pry instance

Parameters:

  • _pry_ (Pry)

    The Pry instance associated with the frame managers

Returns:

  • (Array)

    The stack of Pry::FrameManager objections



22
23
24
# File 'lib/pry-stack.rb', line 22

def frame_managers(_pry_)
  frame_hash[_pry_]
end

.pop_frame_manager(_pry_) ⇒ Pry::FrameManager

Delete the currently active frame manager

Parameters:

  • _pry_ (Pry)

    The Pry instance associated with the frame managers.

Returns:

  • (Pry::FrameManager)

    The popped frame manager.



55
56
57
58
59
60
61
# File 'lib/pry-stack.rb', line 55

def pop_frame_manager(_pry_)
  return if frame_managers(_pry_).empty?

  popped_fm = frame_managers(_pry_).pop
  pop_helper(popped_fm, _pry_)
  popped_fm
end

.pop_helper(popped_fm, _pry_) ⇒ Object

Restore the Pry instance to operate on the previous binding. Also responsible for restoring Pry instance's backtrace.

Parameters:

  • popped_fm (Pry::FrameManager)

    The recently popped frame manager.

  • _pry_ (Pry)

    The Pry instance associated with the frame managers.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pry-stack.rb', line 67

def pop_helper(popped_fm, _pry_)
  if frame_managers(_pry_).empty?
    if _pry_.binding_stack.empty?
      _pry_.binding_stack.push popped_fm.prior_binding
    else
      _pry_.binding_stack[-1] = popped_fm.prior_binding
    end

    frame_hash.delete(_pry_)
  else
    frame_manager(_pry_).refresh_frame(false)
  end

  # restore backtrace
  _pry_.backtrace = popped_fm.prior_backtrace
end

.push_helper(fm, options = {}) ⇒ Object

Update the Pry instance to operate on the specified frame for the current frame manager.

Parameters:

  • fm (PryStack::FrameManager)

    The active frame manager.

  • options (Hash) (defaults to: {})

    The options hash.



41
42
43
44
45
46
47
# File 'lib/pry-stack.rb', line 41

def push_helper(fm, options={})
  options = {
    :initial_frame => 0
  }.merge!(options)

  fm.change_frame_to(options[:initial_frame], false)
end