aboutsummaryrefslogblamecommitdiffstats
path: root/railties/lib/breakpoint_client.rb
blob: fa93c11f3ebcd3aad4713be0f17bbfea63505acf (plain) (tree)
1
2
3
4
5
6
7
8
9

                    
                 



                                           

                      





























                                                          




                                                



















                                               



                             
                                        
 

                                        

       














                                                                                 

       


                                             





                               































                                                           

                          
         
 




                                                                        
 
                                                                                       
 
                    





                                                                          
 



                                
       
                           
                         




                                                                                                                        
 




                                
     
  
require 'breakpoint'
require 'optparse'
require 'timeout'

options = {
  :ClientURI  => nil,
  :ServerURI  => "druby://localhost:42531",
  :RetryDelay => 1,
  :Verbose    => false
}

ARGV.options do |opts|
  script_name = File.basename($0)
  opts.banner = [
    "Usage: ruby #{script_name} [options] [server uri]",
    "",
    "This tool lets you connect to a breakpoint service ",
    "which was started via Breakpoint.activate_drb.",
    "",
    "The server uri defaults to druby://localhost:42531"
  ].join("\n")

  opts.separator ""

  opts.on("-c", "--client-uri=uri",
    "Run the client on the specified uri.",
    "This can be used to specify the port",
    "that the client uses to allow for back",
    "connections from the server.",
    "Default: Find a good URI automatically.",
    "Example: -c druby://localhost:12345"
  ) { |options[:ClientURI]| }

  opts.on("-s", "--server-uri=uri",
    "Connect to the server specified at the",
    "specified uri.",
    "Default: druby://localhost:42531"
  ) { |options[:ServerURI]| }

  opts.on("-v", "--verbose",
    "Report all connections and disconnections",
    "Default: false"
  ) { |options[:Verbose]| }

  opts.on("-R", "--retry-delay=delay", Integer,
    "Automatically try to reconnect to the",
    "server after delay seconds when the",
    "connection failed or timed out.",
    "A value of 0 disables automatical",
    "reconnecting completely.",
    "Default: 10"
  ) { |options[:RetryDelay]| }

  opts.separator ""

  opts.on("-h", "--help",
    "Show this help message."
  ) { puts opts; exit }

  opts.parse!
end

options[:ServerURI] = ARGV[0] if ARGV[0]

$running = true

trap("INT"){$running = false}

puts "Waiting for initial breakpoint..."

loop do
  DRb.start_service(options[:ClientURI])

  begin
    service = DRbObject.new(nil, options[:ServerURI])

    begin
      timeout(10) { service.ping }
    rescue Timeout::Error, DRb::DRbConnError
      if options[:Verbose]
        puts "",
          "  *** Breakpoint service didn't respond to ping request ***",
          "  This likely happened because of a misconfigured ACL (see the",
          "  documentation of Breakpoint.activate_drb, note that by default",
          "  you can only connect to a remote Breakpoint service via a SSH",
          "  tunnel), but might also be caused by an extremely slow connection.",
          ""
        end
      raise
    end

    begin
      service.register_eval_handler do |code|
        result = eval(code, TOPLEVEL_BINDING)
        if result
          DRbObject.new(result)
        else
          result
        end
      end 

      service.register_collision_handler do
        msg = [
          "  *** Breakpoint service collision ***",
          "  Another Breakpoint service tried to use the",
          "  port already occupied by this one. It will",
          "  keep waiting until this Breakpoint service",
          "  is shut down.",
          "  ",
          "  If you are using the Breakpoint library for",
          "  debugging a Rails or other CGI application",
          "  this likely means that this Breakpoint",
          "  session belongs to an earlier, outdated",
          "  request and should be shut down via 'exit'."
        ].join("\n")

        if RUBY_PLATFORM["win"] then
          # This sucks. Sorry, I'm not doing this because
          # I like funky message boxes -- I need to do this
          # because on Windows I have no way of displaying
          # my notification via puts() when gets() is still
          # being performed on STDIN. I have not found a
          # better solution.
          begin
            require 'tk'
            root = TkRoot.new { withdraw }
            Tk.messageBox('message' => msg, 'type' => 'ok')
            root.destroy
          rescue Exception
            puts "", msg, ""
          end
        else
          puts "", msg, ""
        end
      end

      service.register_handler do |workspace, message|
        puts message
        IRB.start(nil, nil, workspace)
        puts "", "Resumed execution. Waiting for next breakpoint...", ""
      end

      puts "Connection established. Waiting for breakpoint...", "" if options[:Verbose]

      while $running
        begin
          service.ping
        rescue DRb::DRbConnError => error
          puts "Server exited. Closing connection..." if options[:Verbose]
          break
        end

        sleep(0.5)
      end
    ensure
      service.unregister_handler
    end
  rescue Exception => error
    break unless $running
    if options[:RetryDelay] > 0 then
      puts "No connection to breakpoint service at #{options[:ServerURI]}:", "  (#{error.inspect})" if options[:Verbose]
      error.backtrace if $DEBUG

      puts "  Reconnecting in #{options[:RetryDelay]} seconds..." if options[:Verbose]
 
      sleep options[:RetryDelay]
      retry
    else
      raise
    end
  end
end