Added a numeric fill in method (_n_)
[ruby_koans.git] / src / edgecase.rb
1 #!/usr/bin/env ruby
2 # -*- ruby -*-
3
4 require 'test/unit/assertions'
5
6 class FillMeInError < StandardError
7 end
8
9 def __(value="FILL ME IN")
10   value
11 end
12
13 def _n_(value=999999)
14   value
15 end
16
17 def ___(value=FillMeInError)
18   value
19 end
20
21 class Object
22   def ____(method=nil)
23     if method
24       self.send(method)
25     end
26   end
27 end
28
29 module EdgeCase
30   class Sensei
31     attr_reader :failure, :failed_test
32
33     AssertionError = Test::Unit::AssertionFailedError
34
35     def initialize
36       @pass_count = 0
37       @failure = nil
38       @failed_test = nil
39     end
40
41     def accumulate(test)
42       if test.passed?
43         @pass_count += 1
44         puts "  #{test.name} has expanded your awareness."
45       else
46         puts "  #{test.name} has damaged your karma."
47         @failed_test = test
48         @failure = test.failure
49         throw :edgecase_exit
50       end
51     end
52
53     def failed?
54       ! @failure.nil?
55     end
56
57     def assert_failed?
58       failure.is_a?(AssertionError)
59     end
60
61     def report
62       if failed?
63         puts
64         puts "You have not yet reached enlightenment ..."
65         puts failure.message
66         puts
67         puts "Please meditate on the following code:"
68         if assert_failed?
69           puts find_interesting_lines(failure.backtrace)
70         else
71           puts failure.backtrace
72         end
73         puts
74       end
75       say_something_zenlike
76     end
77
78     def find_interesting_lines(backtrace)
79       backtrace.reject { |line|
80         line =~ /test\/unit\/|edgecase\.rb/
81       }
82     end
83
84     # Hat's tip to Ara T. Howard for the zen statements from his
85     # metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
86     def say_something_zenlike
87       puts
88       if !failed?
89         puts "Mountains are again merely mountains"
90       else
91         case (@pass_count % 10)
92         when 0
93           puts "mountains are merely mountains"
94         when 1, 2
95           puts "learn the rules so you know how to break them properly"
96         when 3, 4
97           puts "remember that silence is sometimes the best answer"
98         when 5, 6
99           puts "sleep is the best meditation"
100         when 7, 8
101           puts "when you lose, don't lose the lesson"
102         else
103           puts "things are not what they appear to be: nor are they otherwise"
104         end
105       end
106     end
107   end      
108
109   class Koan
110     include Test::Unit::Assertions
111
112     attr_reader :name, :failure
113
114     def initialize(name)
115       @name = name
116       @failure = nil
117     end
118
119     def passed?
120       @failure.nil?
121     end
122
123     def failed(failure)
124       @failure = failure
125     end
126
127     def setup
128     end
129
130     def teardown
131     end
132
133     # Class methods for the EdgeCase test suite.
134     class << self
135       def inherited(subclass)
136         subclasses << subclass
137       end
138
139       def method_added(name)
140         testmethods << name unless tests_disabled?
141       end
142
143       def run_tests(accumulator)
144         puts
145         puts "Thinking #{self}"
146         testmethods.each do |m|
147           self.run_test(m, accumulator) if Koan.test_pattern =~ m.to_s
148         end
149       end
150
151       def run_test(method, accumulator)
152         test = self.new(method)
153         test.setup
154         begin
155           test.send(method)
156         rescue StandardError => ex
157           test.failed(ex)
158         ensure
159           begin
160             test.teardown
161           rescue StandardError => ex
162             test.failed(ex) if test.passed?
163           end
164         end
165         accumulator.accumulate(test)
166       end
167
168       def end_of_enlightenment
169         @tests_disabled = true
170       end
171
172       def command_line(args)
173         args.each do |arg|
174           case arg
175           when /^-n\/(.*)\/$/
176             @test_pattern = Regexp.new($1)
177           when /^-n(.*)$/
178             @test_pattern = Regexp.new(Regexp.quote($1))
179           else
180             if File.exist?(arg)
181               load(arg)
182             else
183               fail "Unknown command line argument '#{arg}'"
184             end              
185           end
186         end
187       end
188
189       # Lazy initialize list of subclasses
190       def subclasses
191         @subclasses ||= []
192       end
193
194        # Lazy initialize list of test methods.
195       def testmethods
196         @test_methods ||= []
197       end
198
199       def tests_disabled?
200         @tests_disabled ||= false
201       end
202
203       def test_pattern
204         @test_pattern ||= /^test_/
205       end
206
207     end
208   end
209 end
210
211 END {
212   EdgeCase::Koan.command_line(ARGV)
213   zen_master = EdgeCase::Sensei.new
214   catch(:edgecase_exit) {
215     EdgeCase::Koan.subclasses.each do |sc|
216       sc.run_tests(zen_master)
217     end
218   }
219   zen_master.report
220 }