This started out with a question on the RSpec group about why debugger invocations in Cucumber steps seemed to be ignored.
Now, I didn't know the answer to that, but I did have a bit of related knowledge since one of my contributions to RSpec was adding the --debugger/-u option to the spec command as an analog to the same option in the script/server command in Rails. As far as I know that option hasn't been added to Cucumber.
Which led me to refreshing myself on how Rails implements that option in order to help answer the Cucumber question. So I brought up the rails gem in textmate on the latest version of Rails, and had a minor epiphany.
Under the Hood of the script/server --debugger Option
In Rails you can put debugger calls in your code which will cause a debugger breakpoint if you are running with the --debug option, but do nothing (actually it writes an info level entry to the log ) if you aren't.
The latter is pretty easy, during initialization, ActiveSupport asks Kernel if it responds to :debugger, and if not defines the method to do the log.
The other part involves script/server running this code when it starts up if -u or --debugger was one of the command line options:
begin
require_library_or_gem 'ruby-debug'
Debugger.start
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
puts "=> Debugger enabled"
rescue Exception
puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
exit
end
And the rspec command does pretty much the same thing since David picked up my contribution a few releases ago.
Rails 2.2 vs Rails 2.3
My epiphany came when I looked at Rails 2.3.1, and didn't have as much of a deja-vu as I expected. The difference is how that code to conditionally install and start the debugger gets executed.
In Rails 2.2, script/server looks at the first argument to determine whether to run mongrel, lighty, thin, or webrick. If that's not specified it goes through some sniff tests to infer which to run.
It then requires a specific server based script to actually run the server. The mongrel and webrick scripts support the debugger option and run the load code if it is specified.
Rails 2.3.1 does things a bit differently. The script/server program parse the options, then loads the debugger with this snippet of code:
app = Rack::Builder.new {
use Rails::Rack::LogTailer unless options[:detach]
use Rails::Rack::Debugger if options[:debugger]
map map_path do
use Rails::Rack::Static
run inner_app
end
}.to_app
So the debugger is turned on by inserting a rack middleware, what does this look like?
module Rails
module Rack
class Debugger
def initialize(app)
@app = app
require_library_or_gem 'ruby-debug'
::Debugger.start
::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings)
puts "=> Debugger enabled"
rescue Exception
puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
exit
end
def call(env)
@app.call(env)
end
end
end
end
The code in the initialize method does give me deja-vu!
This is an interesting approach. It's consistent with Rails move to improved configurability using Rack. There's a slight overhead (1 method call per request) due to the passthrough, but that's insignificant in the overall scheme of things, particularly since it's only inserted when you are running with the debugger.