Module: PryMoves

Extended by:
PryMoves, Restartable
Included in:
PryMoves
Defined in:
lib/pry-moves.rb,
lib/pry-moves/version.rb,
lib/pry-moves/commands.rb,
lib/pry-moves/add_suffix.rb,
lib/pry-moves/pry_wrapper.rb,
lib/commands/trace_command.rb

Defined Under Namespace

Modules: Painter, Recursion, Restartable, TraceHelpers, Vars Classes: AddSuffix, ArgumentCall, ArrayCall, ArrayIndex, Backtrace, BacktraceBuilder, BindingsStack, Debug, Diff, ErrorWithData, Finish, Formatter, Goto, HashKey, Iterate, Method, Next, NextBreakpoint, Profile, PryWrapper, Reload, Restart, Step, Tools, TraceCommand, TracedMethod, Watch

Constant Summary collapse

TRACE_IGNORE_FILES =
Dir[File.join(File.dirname(__FILE__), '**', '*.rb')].map { |f| File.expand_path(f) }
ROOT_DIR =
File.expand_path(".")
MAX_MESSAGE_CHARS =
520
TRIGGERS =
[:each_new_run, :restart]
VERSION =
'1.0.12'
Commands =
Pry::CommandSet.new do
  block_command 'step', 'Step execution into the next line or method.' do |param|
    breakout_navigation :step, param
  end
  alias_command 's', 'step'

  block_command 'finish', 'Finish - xule tut neponyatnogo' do |param|
    breakout_navigation :finish, param
  end
  alias_command 'f', 'finish'

  block_command 'next', 'Execute the next line stepping into blocks' do |param|
    breakout_navigation :next, param
  end
  alias_command 'n', 'next'

  block_command 'nn', 'Execute the next line skipping blocks' do |param|
    breakout_navigation :next, 'blockless'
  end

  block_command 'next_breakpoint', 'Go to next breakpoint' do |param|
    breakout_navigation :next_breakpoint, param
  end
  alias_command 'b', 'next_breakpoint'

  block_command 'add-bp', 'Add conditional breakpoint to script' do |var_name, line_number|
    PryMoves::Tools.new(_pry_).add_breakpoint var_name, line_number&.to_i, target
    run 'restart'
  end

  block_command 'iterate', 'Go to next iteration of current block' do |param|
    breakout_navigation :iterate, param
  end
  alias_command 'ir', 'iterate'

  block_command 'goto', 'goto line' do |param|
    breakout_navigation :goto, param
  end
  alias_command 'g', 'goto'

  block_command 'continue', 'Continue program execution and end the Pry session' do
    _check_file_context
    run 'exit-all'
  end
  alias_command 'c', 'continue'

  block_command 'watch', 'Display value of expression on every move' do |param|
    PryMoves::Watch.instance.process_cmd param, target
  end

  block_command 'diff', 'Display difference' do
    next if PryMoves::Vars.var_precedence :diff, target
    cmd = arg_string.gsub(/^diff/, '').strip
    PryMoves::Diff.new(_pry_, target).run_command cmd
  end

  block_command 'bt', 'Backtrace' do |param, param2|
    PryMoves::Backtrace.new(_pry_).run_command param, param2
  end

  block_command 'debug', '' do
    cmd = arg_string.gsub(/^debug/, '').strip
    breakout_navigation :debug, cmd
  end

  block_command 'profile', '' do |param|
    breakout_navigation :profile, param
  end

  block_command 'off', '' do
    PryMoves.switch if PryMoves.stop_on_breakpoints?
    run 'continue'
  end

  block_command :restart, '' do
    PryMoves.restart_requested = true
    run 'continue'
  end
  alias_command '@', 'restart'

  block_command :reload, '' do
    PryMoves.reload_requested = true
    run 'continue'
  end
  alias_command '#', 'reload'

  block_command /^\\(\w+)$/, 'Execute command explicitly' do |param|
    Pry.config.ignore_once_var_precedence = true
    run param
  end

  block_command '!', 'exit' do
    PryMoves.unlock
    Pry.config.exit_requested = true
    run '!!!'
  end

  # Hit Enter to repeat last command
  command /^$/, "repeat last command" do
    _pry_.run_command Pry.history.to_a.last
  end

  helpers do
    def breakout_navigation(action, param)
      return if PryMoves::Vars.var_precedence action, target

      _check_file_context
      _pry_.binding_stack.clear     # Clear the binding stack.
      throw :breakout_nav, {        # Break out of the REPL loop and
        action: action,          #   signal the tracer.
        param:  param,
        binding: target
      }
    end

    # Ensures that a command is executed in a local file context.
    def _check_file_context
      unless PryMoves.check_file_context(target)
        raise Pry::CommandError, 'Cannot find local context. Did you use `binding.pry`?'
      end
    end
  end

end

Instance Attribute Summary collapse

Attributes included from Restartable

#reload_rake_tasks, #reload_requested, #reload_ruby_scripts, #reloader, #restart_requested

Instance Method Summary collapse

Methods included from Restartable

re_execution, reload_sources, restartable

Instance Attribute Details

#current_remote_serverObject

Reference to currently running pry-remote server. Used by the tracer.



207
208
209
# File 'lib/pry-moves.rb', line 207

def current_remote_server
  @current_remote_server
end

#debug_called_timesObject

Returns the value of attribute debug_called_times.



9
10
11
# File 'lib/pry-moves.rb', line 9

def debug_called_times
  @debug_called_times
end

#dont_print_errorsObject

Returns the value of attribute dont_print_errors.



9
10
11
# File 'lib/pry-moves.rb', line 9

def dont_print_errors
  @dont_print_errors
end

#is_openObject

Returns the value of attribute is_open.



9
10
11
# File 'lib/pry-moves.rb', line 9

def is_open
  @is_open
end

#launched_specs_examplesObject

Returns the value of attribute launched_specs_examples.



9
10
11
# File 'lib/pry-moves.rb', line 9

def launched_specs_examples
  @launched_specs_examples
end

#stack_tipsObject

Returns the value of attribute stack_tips.



9
10
11
# File 'lib/pry-moves.rb', line 9

def stack_tips
  @stack_tips
end

#step_in_everywhereObject

Returns the value of attribute step_in_everywhere.



9
10
11
# File 'lib/pry-moves.rb', line 9

def step_in_everywhere
  @step_in_everywhere
end

#stop_on_breakpointsObject

Returns the value of attribute stop_on_breakpoints.



9
10
11
# File 'lib/pry-moves.rb', line 9

def stop_on_breakpoints
  @stop_on_breakpoints
end

#test_exampleObject

Returns the value of attribute test_example.



9
10
11
# File 'lib/pry-moves.rb', line 9

def test_example
  @test_example
end

#traceObject

Returns the value of attribute trace.



9
10
11
# File 'lib/pry-moves.rb', line 9

def trace
  @trace
end

Instance Method Details

#add_command(command, &block) ⇒ Object



158
159
160
# File 'lib/pry-moves.rb', line 158

def add_command(command, &block)
  Pry.commands.block_command command, "", &block
end

#check_file_context(target) ⇒ Object

Checks that a binding is in a local file context. Extracted from github.com/pry/pry/blob/master/lib/pry/default_commands/context.rb



145
146
147
148
# File 'lib/pry-moves.rb', line 145

def check_file_context(target)
  file = target.eval('__FILE__')
  file == Pry.eval_path || (file !~ /(\(.*\))|<.*>/ && file != '' && file != '-e')
end

#debug(message = nil, data: nil, at: nil, from: nil, options: nil) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/pry-moves.rb', line 87

def debug(message = nil, data: nil, at: nil, from: nil, options: nil)
  pry_moves_stack_end = true
  message ||= data
  PryMoves.re_execution
  if PryMoves.stop_on_breakpoints?
    self.debug_called_times += 1
    return if at and self.debug_called_times != at
    return if from and self.debug_called_times < from
    if message
      PryMoves.messages << (message.is_a?(String) ? message : message.ai)
    end
    binding.pry options
    PryMoves.re_execution
  end
end

#debug_error(message, debug_object = nil) ⇒ Object



135
136
137
138
139
140
141
# File 'lib/pry-moves.rb', line 135

def debug_error(message, debug_object=nil)
  pry_moves_stack_end = true
  if debug_object
    message = [format_debug_object(debug_object), message].join "\n"
  end
  debug message, options: {is_error: true}
end

#debug_window_attached?Boolean

Returns:

  • (Boolean)


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/pry-moves.rb', line 49

def debug_window_attached?
  if !@last_attachment_check or Time.now - @last_attachment_check > 2
    @is_debug_window_attached = begin
      @last_attachment_check = Time.now
      if `echo $STY` # is in screen
        my_window = `echo $WINDOW`.strip
        windows_list = `screen -Q windows`
        windows_list.include? " #{my_window}*" # process is in currently active window
      else
        true
      end
    end
  else
    @is_debug_window_attached
  end
end

#format_debug_object(obj) ⇒ Object



129
130
131
132
133
# File 'lib/pry-moves.rb', line 129

def format_debug_object obj
  output = obj.ai rescue "#{obj.class} #{obj}"
  output.length > MAX_MESSAGE_CHARS ?
    output[0 .. MAX_MESSAGE_CHARS] + "... (cut)" : output
end

#initObject



66
67
68
69
70
71
72
73
74
75
# File 'lib/pry-moves.rb', line 66

def init
  reset
  self.trace = true if ENV['TRACE_MOVES']
  self.reload_ruby_scripts = {
    monitor: %w(app test spec),
    except: %w(app/assets app/views)
  }
  self.reloader = CodeReloader.new unless ENV['PRY_MOVES_RELOADER'] == 'off'
  self.reload_rake_tasks = true
end

#is_project_file?Boolean

Returns:

  • (Boolean)


121
122
123
124
125
126
# File 'lib/pry-moves.rb', line 121

def is_project_file?
  files = caller[2..4] # -2 steps upside: runtime_debug, debug sugar function
  files.any? do |file|
    !file.start_with?("/") || file.start_with?(ROOT_DIR)
  end
end

#listen_signal_to_switch(use_signals = %w[INFO QUIT]) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/pry-moves.rb', line 35

def listen_signal_to_switch use_signals = %w[INFO QUIT]
  # signals list in bash: stty -a
  signal = use_signals.find {Signal.list[_1]}
  unless signal
    puts "No signals supported for PryMoves switch: #{use_signals}"
    return
  end

  puts "🚥 Overriding #{signal} signal for PryMoves debug switch"
  Signal.trap signal do
    PryMoves.switch
  end
end

#lockObject



167
168
169
# File 'lib/pry-moves.rb', line 167

def lock
  semaphore.lock unless semaphore.locked?
end

#locked?Boolean Also known as: tracing?

Returns:

  • (Boolean)


162
163
164
# File 'lib/pry-moves.rb', line 162

def locked?
  semaphore.locked?
end

#loopObject



14
15
16
17
18
19
20
# File 'lib/pry-moves.rb', line 14

def loop
  Kernel.loop do
    result = yield
    debug "⏸  execution loop complete\n#{result}"
    PryMoves.reload_sources
  end
end

#messagesObject



154
155
156
# File 'lib/pry-moves.rb', line 154

def messages
  @messages ||= []
end

#on(trigger, &block) ⇒ Object



197
198
199
200
# File 'lib/pry-moves.rb', line 197

def on(trigger, &block)
  error "Invalid trigger, possible triggers: #{TRIGGERS}", trigger unless TRIGGERS.include? trigger
  triggers[trigger] << block
end

#open?Boolean

Returns:

  • (Boolean)


175
176
177
# File 'lib/pry-moves.rb', line 175

def open?
  @is_open
end

#project_rootObject



202
203
204
# File 'lib/pry-moves.rb', line 202

def project_root
  @project_root ||= defined?(Rails) ? Rails.root.to_s : Dir.pwd
end

#resetObject



77
78
79
80
81
82
83
84
85
# File 'lib/pry-moves.rb', line 77

def reset
  self.launched_specs_examples = 0
  unless ENV['PRY_MOVES'] == 'off' ||
      (defined?(Rails) and Rails.env.production?)
    self.stop_on_breakpoints = STDIN.tty? && STDOUT.tty?
  end
  self.debug_called_times = 0
  self.step_in_everywhere = false
end

#runtime_debug(instance, external: false) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/pry-moves.rb', line 105

def runtime_debug(instance, external: false)
  do_debug = (
    stop_on_breakpoints? and
      not open? and
      (external or is_project_file?) and
      not [RubyVM::InstructionSequence].include?(instance)
  )
  if do_debug
    hide_from_stack = true
    err, obj = yield
    # HINT: when pry failed to start use: caller.reverse
    PryMoves.debug_error err, obj
    true
  end
end

#semaphoreObject



150
151
152
# File 'lib/pry-moves.rb', line 150

def semaphore
  @semaphore ||= Mutex.new
end

#stop_on_breakpoints?Boolean

Returns:

  • (Boolean)


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

def stop_on_breakpoints?
  stop_on_breakpoints
end

#switchObject



26
27
28
29
30
31
32
33
# File 'lib/pry-moves.rb', line 26

def switch
  self.stop_on_breakpoints = !self.stop_on_breakpoints
  if self.stop_on_breakpoints
    puts '🪲  Debugging is turned on'
  else
    puts '🐞 Debugging is turned off'
  end
end

#synchronize_threadsObject



179
180
181
182
183
184
# File 'lib/pry-moves.rb', line 179

def synchronize_threads
  return true if Thread.current[:pry_moves_debug]

  semaphore.synchronize {} rescue return
  true
end

#trigger(event, context) ⇒ Object



186
187
188
# File 'lib/pry-moves.rb', line 186

def trigger(event, context)
  triggers[event].each {|t| t.call context}
end

#triggersObject



190
191
192
193
194
# File 'lib/pry-moves.rb', line 190

def triggers
  @triggers ||= Hash.new do |hash, key|
    hash[key] = []
  end
end

#unlockObject



171
172
173
# File 'lib/pry-moves.rb', line 171

def unlock
  semaphore.unlock unless Thread.current[:pry_moves_debug]
end