Skip to content

Commit

Permalink
Initial implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Avdi Grimm committed Jan 17, 2010
1 parent 7a2a8ee commit 14673b5
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 11 deletions.
85 changes: 74 additions & 11 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,16 +1,79 @@
= hammertime

Description goes here.

== Note on Patches/Pull Requests

* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
future version unintentionally.
* Commit, do not mess with rakefile, version, or history.
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.
An interactive error console similar to those found in Lisp and Smalltalk environments.

== Synopsis

Simply require the Hammertime library:

require 'hammertime'

$broken = true

def faulty_method
raise "Oh no!" if $broken
end

3.times do |n|
puts "Attempt (#{n+1}/3)"
begin
faulty_method
puts "No error raised"
rescue => error
puts "Error raised: #{error.inspect}"
end
end

When an error is raised, a menu of possible actions will be presented at the console:

=== Stop! Hammertime. ===
An error has occurred at example.rb:4:in `raise_runtime_error'
The error is: #<RuntimeError: Oh no!>
1. Continue (process the exception normally)
2. Ignore (proceed without raising an exception)
3. Permit by type (don't ask about future errors of this type)
4. Permit by line (don't ask about future errors raised from this point)
5. Backtrace (show the call stack leading up to the error)
6. Debug (start a debugger)
7. Console (start an IRB session)
What now?

This enables a fix-and-continue style of development:

$ ruby example.rb
Attempt (1/3)

=== Stop! Hammertime. ===
An error has occurred at example.rb:6:in `faulty_method'
The error is: #<RuntimeError: Oh no!>
1. Continue (process the exception normally)
2. Ignore (proceed without raising an exception)
3. Permit by type (don't ask about future errors of this type)
4. Permit by line (don't ask about future errors raised from this point)
5. Backtrace (show the call stack leading up to the error)
6. Debug (start a debugger)
7. Console (start an IRB session)
What now?
7

>> $broken = false
=> false
>> exit

1. Continue (process the exception normally)
2. Ignore (proceed without raising an exception)
3. Permit by type (don't ask about future errors of this type)
4. Permit by line (don't ask about future errors raised from this point)
5. Backtrace (show the call stack leading up to the error)
6. Debug (start a debugger)
7. Console (start an IRB session)
What now?
2
No error raised
Attempt (2/3)
No error raised
Attempt (3/3)
No error raised

== Copyright

Expand Down
17 changes: 17 additions & 0 deletions example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'lib/hammertime'

$broken = true

def faulty_method
raise "Oh no!" if $broken
end

3.times do |n|
puts "Attempt (#{n+1}/3)"
begin
faulty_method
puts "No error raised"
rescue => error
puts "Error raised: #{error.inspect}"
end
end
128 changes: 128 additions & 0 deletions lib/hammertime.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require 'highline'
require 'ruby-debug'

module Hammertime
def self.ignored_errors
@ignored_errors ||= [LoadError]
end

def self.ignored_lines
@ignored_lines ||= []
end

def self.stopped
@stopped ||= false
end

def self.stopped=(value)
@stopped = value
end

def hammertime_raise(*args)
backtrace = caller(2)
Thread.exclusive do
error, backtrace =
case args.size
when 0 then [($!.nil? ? RuntimeError.new : $!), backtrace]
when 1 then
if args[0].is_a?(String)
[RuntimeError.exception(args[0]), backtrace]
else
[args[0].exception, backtrace]
end
when 2 then
[args[0].exception(args[1]), backtrace]
when 3 then
[args[0].exception(args[1]), args[2]]
else
super(ArgumentError, "wrong number of arguments", backtrace)
end
error.set_backtrace(backtrace)

if hammertime_ignore_error?(error, backtrace)
hammertime_original_raise(error)
else
::Hammertime.stopped = true
end

c = ::Hammertime.hammertime_console
c.say "\n"
c.say "=== Stop! Hammertime. ==="
c.say "An error has occurred at #{backtrace.first}"
c.say "The error is: #{error.inspect}"
menu_config = lambda do |menu|
menu.prompt = "What now?"
menu.default = "Continue"
menu.select_by = :index_or_name

menu.choice "Continue (process the exception normally)" do
hammertime_original_raise(error)
true
end
menu.choice "Ignore (proceed without raising an exception)" do
true
end
menu.choice "Permit by type (don't ask about future errors of this type)" do
::Hammertime.ignored_errors << error.class
c.say "Added #{error.class} to permitted error types"
hammertime_original_raise(error)
true
end
menu.choice "Permit by line (don't ask about future errors raised from this point)" do
::Hammertime.ignored_lines << backtrace.first
c.say "Added #{backtrace.first} to permitted error lines"
hammertime_original_raise(error)
true
end
menu.choice "Backtrace (show the call stack leading up to the error)" do
backtrace.each do |frame| c.say frame end
false
end
menu.choice "Debug (start a debugger)" do
debugger
false
end
menu.choice "Console (start an IRB session)" do
require 'irb'
IRB.start
false
end
end
continue = c.choose(&menu_config) until continue
end
ensure
::Hammertime.stopped = false
end

def hammertime_original_raise(*args)
Kernel.instance_method(:raise).bind(self).call(*args)
end

def fail(*args)
hammertime_raise(*args)
end

def raise(*args)
hammertime_raise(*args)
end

private

def self.hammertime_console
@console ||= HighLine.new($stdin, $stderr)
end

def hammertime_ignore_error?(error, backtrace)
return true if ::Hammertime.stopped
return true if ::Hammertime.ignored_errors.any?{|e| error.is_a?(e)}
return true if ::Hammertime.ignored_lines.include?(backtrace.first)
return false
end
end

unless ::Object < Hammertime
class ::Object
include ::Hammertime
end
Debugger.start
end

0 comments on commit 14673b5

Please sign in to comment.