Skip to content

Instantly share code, notes, and snippets.

@brainopia
Created September 6, 2011 23:47
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save brainopia/1199316 to your computer and use it in GitHub Desktop.
Save brainopia/1199316 to your computer and use it in GitHub Desktop.
Non blocking delayed debugger for production
module Developer
extend self
delegate :establish_connection, :clear_all_connections!, :to => ActiveRecord::Base
def delayed_debug(scope)
detach_process do
close_io_objects
establish_connection
setup_process_name
notify_developers
place_debugger_in scope
end
end
private
def setup_process_name
$0 = 'delayed_debug'
end
def detach_process
kill_running_debugger
clear_all_connections!
Process.detach fork { yield }
establish_connection
cleanup_on_exit
end
def place_debugger_in(scope)
code = debugger_code + app_code_from(scope)
scope.eval code, *file_and_line_after_debugger(scope)
end
def debugger_code
<<-RUBY
require 'ruby-debug'
Debugger.wait_connection = true
Debugger.start_remote
debugger
RUBY
end
def notify_developers
HoptoadNotifier.notify \
:error_class => 'debugger',
:error_message => 'Delayed Debugger was triggerred'
end
def cleanup_on_exit
@at_exit_hook ||= at_exit { kill_running_debugger }
end
def app_code_from(scope)
current_method(scope).source.gsub(/(\A.*debug[^\n\;]*)|(end\Z)/m, '')
end
def current_method(scope)
scope.eval %q{ singleton_class.instance_method __method__ }
end
def file_and_line_after_debugger(scope)
file, line = scope.eval %q{ caller[0].split(':') }
[file, line.to_i-4]
end
def kill_running_debugger
`pkill -f delayed_debug`
end
def close_io_objects
ObjectSpace.each_object(IO) do |io|
next if [STDIN, STDOUT, STDERR].include? io
io.close unless io.closed? rescue nil
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment