Merge branch 'output_refactoring'

* output_refactoring:
  Updated for JRuby
  Switch blue color to cyan for better contrast on dark terminals.
  80 char limit to end screen
  Updated Koans directory
  Added else clause if progress file is not there.
  regen for updated koans/edgecase.rb
  end screen with koans logo ascii art
This commit is contained in:
Jim Weirich
2010-09-27 10:49:17 -04:00
5 changed files with 424 additions and 129 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
dist
.project_env.rc
.path_progress

View File

@@ -73,7 +73,7 @@ module EdgeCase
end
class Sensei
attr_reader :failure, :failed_test
attr_reader :failure, :failed_test, :pass_count
in_ruby_version("1.8") do
AssertionError = Test::Unit::AssertionFailedError
@@ -91,16 +91,43 @@ module EdgeCase
@pass_count = 0
@failure = nil
@failed_test = nil
@observations = []
end
def accumulate(test)
if test.passed?
@pass_count += 1
puts Color.green(" #{test.name} has expanded your awareness.")
PROGRESS_FILE_NAME = '.path_progress'
def add_progress(prog)
@_contents = nil
exists = File.exists?(PROGRESS_FILE_NAME)
File.open(PROGRESS_FILE_NAME,'a+') do |f|
f.print "#{',' if exists}#{prog}"
end
end
def progress
if @_contents.nil?
if File.exists?(PROGRESS_FILE_NAME)
File.open(PROGRESS_FILE_NAME,'r') do |f|
@_contents = f.read.to_s.gsub(/\s/,'').split(',')
end
else
puts Color.red(" #{test.name} has damaged your karma.")
@failed_test = test
@failure = test.failure
@_contents = []
end
end
@_contents
end
def observe(step)
if step.passed?
@pass_count += 1
if @pass_count > progress.last.to_i
@observations << Color.green("#{step.koan_file}##{step.name} has expanded your awareness.")
end
else
@failed_test = step
@failure = step.failure
add_progress(@pass_count)
@observations << Color.red("#{step.koan_file}##{step.name} has damaged your karma.")
throw :edgecase_exit
end
end
@@ -113,22 +140,115 @@ module EdgeCase
failure.is_a?(AssertionError)
end
def report
def instruct
if failed?
puts
puts Color.green("You have not yet reached enlightenment ...")
puts Color.red(failure.message)
puts
puts Color.green("Please meditate on the following code:")
if assert_failed?
#puts find_interesting_lines(failure.backtrace)
puts find_interesting_lines(failure.backtrace).collect {|l| Color.red(l) }
@observations.each{|c| puts c }
encourage
guide_through_error
a_zenlike_statement
show_progress
else
puts Color.red(failure.backtrace)
end_screen
end
end
def show_progress
bar_width = 50
total_tests = EdgeCase::Koan.total_tests
scale = bar_width.to_f/total_tests
print Color.green("your path thus far [")
happy_steps = (pass_count*scale).to_i
happy_steps = 1 if happy_steps == 0 && pass_count > 0
print Color.green('.'*happy_steps)
if failed?
print Color.red('X')
print Color.cyan('_'*(bar_width-1-happy_steps))
end
print Color.green(']')
print " #{pass_count}/#{total_tests}"
puts
end
def end_screen
completed = <<-ENDTEXT
,, , ,,
: ::::, :::,
, ,,: :::::::::::::,, :::: : ,
, ,,, ,:::::::::::::::::::, ,: ,: ,,
:, ::, , , :, ,::::::::::::::::::, ::: ,::::
: : ::, ,:::::::: ::, ,::::
, ,::::: :,:::::::,::::,
,: , ,:,,: :::::::::::::
::,: ,,:::, ,::::::::::::,
,:::, :,,::: ::::::::::::,
,::: :::::::, Mountains are again merely mountains ,::::::::::::
:::,,,:::::: ::::::::::::
,:::::::::::, ::::::::::::,
:::::::::::, ,::::::::::::
::::::::::::: ,::::::::::::
:::::::::::: Ruby Koans ::::::::::::,
:::::::::::: ,::::::::::::,
:::::::::::, , ::::::::::::
,:::::::::::::, brought to you by ,,::::::::::::,
:::::::::::::: ,::::::::::::
::::::::::::::, ,:::::::::::::
::::::::::::, EdgeCase Software Artisans , ::::::::::::
:,::::::::: :::: :::::::::::::
,::::::::::: ,: ,,:::::::::::::,
:::::::::::: ,::::::::::::::,
:::::::::::::::::, ::::::::::::::::
:::::::::::::::::::, ::::::::::::::::
::::::::::::::::::::::, ,::::,:, , ::::,:::
:::::::::::::::::::::::, ::,: ::,::, ,,: ::::
,:::::::::::::::::::: ::,, , ,, ,::::
,:::::::::::::::: ::,, , ,:::,
,:::: , ,,
,,,
ENDTEXT
puts completed
end
def encourage
puts
puts "The Master says:"
puts Color.cyan(" You have not yet reached enlightenment.")
if ((recents = progress.last(5)) && recents.size == 5 && recents.uniq.size == 1)
puts Color.cyan(" I sense frustration. Do not be afraid to ask for help.")
elsif progress.last(2).size == 2 && progress.last(2).uniq.size == 1
puts Color.cyan(" Do not lose hope.")
elsif progress.last.to_i > 0
puts Color.cyan(" You are progressing. Excellent. #{progress.last} completed.")
end
end
def guide_through_error
puts
puts "The answers you seek..."
puts Color.red(indent(failure.message).join)
puts
puts "Please meditate on the following code:"
if assert_failed?
puts embolden_first_line_only(indent(find_interesting_lines(failure.backtrace)))
else
puts embolden_first_line_only(indent(failure.backtrace))
end
puts
end
puts Color.green(a_zenlike_statement)
def embolden_first_line_only(text)
first_line = true
text.collect { |t|
if first_line
first_line = false
Color.red(t)
else
Color.cyan(t)
end
}
end
def indent(text)
text.collect{|t| " #{t}"}
end
def find_interesting_lines(backtrace)
@@ -140,7 +260,6 @@ module EdgeCase
# Hat's tip to Ara T. Howard for the zen statements from his
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
def a_zenlike_statement
puts
if !failed?
zen_statement = "Mountains are again merely mountains"
else
@@ -159,18 +278,21 @@ module EdgeCase
"things are not what they appear to be: nor are they otherwise"
end
end
zen_statement
puts Color.green(zen_statement)
end
end
class Koan
include Test::Unit::Assertions
attr_reader :name, :failure
attr_reader :name, :failure, :koan_count, :step_count, :koan_file
def initialize(name)
def initialize(name, koan_file=nil, koan_count=0, step_count=0)
@name = name
@failure = nil
@koan_count = koan_count
@step_count = step_count
@koan_file = koan_file
end
def passed?
@@ -187,6 +309,22 @@ module EdgeCase
def teardown
end
def meditate
setup
begin
send(name)
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
failed(ex)
ensure
begin
teardown
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
failed(ex) if passed?
end
end
self
end
# Class methods for the EdgeCase test suite.
class << self
def inherited(subclass)
@@ -194,32 +332,7 @@ module EdgeCase
end
def method_added(name)
testmethods << name unless tests_disabled?
end
def run_tests(accumulator)
puts
puts Color.green("Thinking #{self}")
testmethods.each do |m|
self.run_test(m, accumulator) if Koan.test_pattern =~ m.to_s
end
end
def run_test(method, accumulator)
test = self.new(method)
test.setup
begin
test.send(method)
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
test.failed(ex)
ensure
begin
test.teardown
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
test.failed(ex) if test.passed?
end
end
accumulator.accumulate(test)
testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
end
def end_of_enlightenment
@@ -261,17 +374,36 @@ module EdgeCase
@test_pattern ||= /^test_/
end
def total_tests
self.subclasses.inject(0){|total, k| total + k.testmethods.size }
end
end
end
class ThePath
def walk
sensei = EdgeCase::Sensei.new
each_step do |step|
sensei.observe(step.meditate)
end
sensei.instruct
end
def each_step
catch(:edgecase_exit) {
step_count = 0
EdgeCase::Koan.subclasses.each_with_index do |koan,koan_index|
koan.testmethods.each do |method_name|
step = koan.new(method_name, koan.to_s, koan_index+1, step_count+=1)
yield step
end
end
}
end
end
end
END {
EdgeCase::Koan.command_line(ARGV)
zen_master = EdgeCase::Sensei.new
catch(:edgecase_exit) {
EdgeCase::Koan.subclasses.each do |sc|
sc.run_tests(zen_master)
end
}
zen_master.report
EdgeCase::ThePath.new.walk
}

View File

@@ -43,12 +43,12 @@ class AboutMethods < EdgeCase::Koan
exception = assert_raise(___(ArgumentError)) do
my_global_method
end
assert_match(/#{__("wrong number of arguments")}/, exception.message)
assert_match(/#{__("wrong (number|#) of arguments")}/, exception.message)
exception = assert_raise(___(ArgumentError)) do
my_global_method(1,2,3)
end
assert_match(/#{__("wrong number of arguments")}/, exception.message)
assert_match(/#{__("wrong (number|#) of arguments")}/, exception.message)
end
# ------------------------------------------------------------------

View File

@@ -25,15 +25,21 @@ class AboutSymbols < EdgeCase::Koan
def test_method_names_become_symbols
all_symbols = Symbol.all_symbols
assert_equal __(true), all_symbols.include?(:test_method_names_are_symbols)
assert_equal __(true), all_symbols.include?(:test_method_names_become_symbols)
end
# THINK ABOUT IT:
#
# Why do we capture the list of symbols before we check for the
# method name?
in_ruby_version("mri") do
RubyConstant = "What is the sound of one hand clapping?"
def test_constants_become_symbols
all_symbols = Symbol.all_symbols
assert_equal true, all_symbols.include?(__(:RubyConstant))
assert_equal __(true), all_symbols.include?(__(:RubyConstant))
end
end
def test_symbols_can_be_made_from_strings
@@ -47,6 +53,13 @@ class AboutSymbols < EdgeCase::Koan
assert_equal symbol, __("cats and dogs").to_sym
end
def test_symbols_with_spaces_can_be_built
value = "and"
symbol = :"cats #{value} dogs"
assert_equal symbol, __("cats and dogs").to_sym
end
def test_to_s_is_called_on_interpolated_symbols
symbol = :cats
string = "It is raining #{symbol} and dogs."
@@ -65,13 +78,23 @@ class AboutSymbols < EdgeCase::Koan
assert_equal __(false), symbol.respond_to?(:each_char)
assert_equal __(false), symbol.respond_to?(:reverse)
end
# It's important to realize that symbols are not "immutable
# strings", though they are immutable. None of the
# interesting string operations are available on symbols.
def test_symbols_cannot_be_concatenated
# Exceptions will be pondered further father down the path
assert_raise(___(NoMethodError)) do
:cats + :dogs
end
end
def test_symbols_can_be_dynamically_created
assert_equal __(:catsdogs), ("cats" + "dogs").to_sym
end
# THINK ABOUT IT:
#
# Why is it not a good idea to dynamically create a lot of symbols?
end

View File

@@ -6,8 +6,14 @@ require 'test/unit/assertions'
class FillMeInError < StandardError
end
def in_ruby_version(version)
yield if RUBY_VERSION =~ /^#{version}/
def ruby_version?(version)
RUBY_VERSION =~ /^#{version}/ ||
(version == 'jruby' && defined?(JRUBY_VERSION)) ||
(version == 'mri' && ! defined?(JRUBY_VERSION))
end
def in_ruby_version(*versions)
yield if versions.any? { |v| ruby_version?(v) }
end
def __(value="FILL ME IN", value19=:mu)
@@ -73,7 +79,7 @@ module EdgeCase
end
class Sensei
attr_reader :failure, :failed_test
attr_reader :failure, :failed_test, :pass_count
in_ruby_version("1.8") do
AssertionError = Test::Unit::AssertionFailedError
@@ -91,16 +97,43 @@ module EdgeCase
@pass_count = 0
@failure = nil
@failed_test = nil
@observations = []
end
def accumulate(test)
if test.passed?
@pass_count += 1
puts Color.green(" #{test.name} has expanded your awareness.")
PROGRESS_FILE_NAME = '.path_progress'
def add_progress(prog)
@_contents = nil
exists = File.exists?(PROGRESS_FILE_NAME)
File.open(PROGRESS_FILE_NAME,'a+') do |f|
f.print "#{',' if exists}#{prog}"
end
end
def progress
if @_contents.nil?
if File.exists?(PROGRESS_FILE_NAME)
File.open(PROGRESS_FILE_NAME,'r') do |f|
@_contents = f.read.to_s.gsub(/\s/,'').split(',')
end
else
puts Color.red(" #{test.name} has damaged your karma.")
@failed_test = test
@failure = test.failure
@_contents = []
end
end
@_contents
end
def observe(step)
if step.passed?
@pass_count += 1
if @pass_count > progress.last.to_i
@observations << Color.green("#{step.koan_file}##{step.name} has expanded your awareness.")
end
else
@failed_test = step
@failure = step.failure
add_progress(@pass_count)
@observations << Color.red("#{step.koan_file}##{step.name} has damaged your karma.")
throw :edgecase_exit
end
end
@@ -113,22 +146,116 @@ module EdgeCase
failure.is_a?(AssertionError)
end
def report
def instruct
if failed?
puts
puts Color.green("You have not yet reached enlightenment ...")
puts Color.red(failure.message)
puts
puts Color.green("Please meditate on the following code:")
if assert_failed?
#puts find_interesting_lines(failure.backtrace)
puts find_interesting_lines(failure.backtrace).collect {|l| Color.red(l) }
@observations.each{|c| puts c }
encourage
guide_through_error
a_zenlike_statement
show_progress
else
puts Color.red(failure.backtrace)
end_screen
end
end
def show_progress
bar_width = 50
total_tests = EdgeCase::Koan.total_tests
scale = bar_width.to_f/total_tests
print Color.green("your path thus far [")
happy_steps = (pass_count*scale).to_i
happy_steps = 1 if happy_steps == 0 && pass_count > 0
print Color.green('.'*happy_steps)
if failed?
print Color.red('X')
print Color.cyan('_'*(bar_width-1-happy_steps))
end
print Color.green(']')
print " #{pass_count}/#{total_tests}"
puts
end
def end_screen
completed = <<-ENDTEXT
,, , ,,
: ::::, :::,
, ,,: :::::::::::::,, :::: : ,
, ,,, ,:::::::::::::::::::, ,: ,: ,,
:, ::, , , :, ,::::::::::::::::::, ::: ,::::
: : ::, ,:::::::: ::, ,::::
, ,::::: :,:::::::,::::,
,: , ,:,,: :::::::::::::
::,: ,,:::, ,::::::::::::,
,:::, :,,::: ::::::::::::,
,::: :::::::, Mountains are again merely mountains ,::::::::::::
:::,,,:::::: ::::::::::::
,:::::::::::, ::::::::::::,
:::::::::::, ,::::::::::::
::::::::::::: ,::::::::::::
:::::::::::: Ruby Koans ::::::::::::,
:::::::::::: ,::::::::::::,
:::::::::::, , ::::::::::::
,:::::::::::::, brought to you by ,,::::::::::::,
:::::::::::::: ,::::::::::::
::::::::::::::, ,:::::::::::::
::::::::::::, EdgeCase Software Artisans , ::::::::::::
:,::::::::: :::: :::::::::::::
,::::::::::: ,: ,,:::::::::::::,
:::::::::::: ,::::::::::::::,
:::::::::::::::::, ::::::::::::::::
:::::::::::::::::::, ::::::::::::::::
::::::::::::::::::::::, ,::::,:, , ::::,:::
:::::::::::::::::::::::, ::,: ::,::, ,,: ::::
,:::::::::::::::::::: ::,, , ,, ,::::
,:::::::::::::::: ::,, , ,:::,
,:::: , ,,
,,,
ENDTEXT
puts completed
end
def encourage
puts
puts "The Master says:"
puts Color.cyan(" You have not yet reached enlightenment.")
if ((recents = progress.last(5)) && recents.size == 5 && recents.uniq.size == 1)
puts Color.cyan(" I sense frustration. Do not be afraid to ask for help.")
elsif progress.last(2).size == 2 && progress.last(2).uniq.size == 1
puts Color.cyan(" Do not lose hope.")
elsif progress.last.to_i > 0
puts Color.cyan(" You are progressing. Excellent. #{progress.last} completed.")
end
end
def guide_through_error
puts
puts "The answers you seek..."
puts Color.red(indent(failure.message).join)
puts
puts "Please meditate on the following code:"
if assert_failed?
puts embolden_first_line_only(indent(find_interesting_lines(failure.backtrace)))
else
puts embolden_first_line_only(indent(failure.backtrace))
end
puts
end
puts Color.green(a_zenlike_statement)
def embolden_first_line_only(text)
first_line = true
text.collect { |t|
if first_line
first_line = false
Color.red(t)
else
Color.cyan(t)
end
}
end
def indent(text)
text = text.split(/\n/) if text.is_a?(String)
text.collect{|t| " #{t}"}
end
def find_interesting_lines(backtrace)
@@ -140,7 +267,6 @@ module EdgeCase
# Hat's tip to Ara T. Howard for the zen statements from his
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
def a_zenlike_statement
puts
if !failed?
zen_statement = "Mountains are again merely mountains"
else
@@ -159,18 +285,21 @@ module EdgeCase
"things are not what they appear to be: nor are they otherwise"
end
end
zen_statement
puts Color.green(zen_statement)
end
end
class Koan
include Test::Unit::Assertions
attr_reader :name, :failure
attr_reader :name, :failure, :koan_count, :step_count, :koan_file
def initialize(name)
def initialize(name, koan_file=nil, koan_count=0, step_count=0)
@name = name
@failure = nil
@koan_count = koan_count
@step_count = step_count
@koan_file = koan_file
end
def passed?
@@ -187,6 +316,22 @@ module EdgeCase
def teardown
end
def meditate
setup
begin
send(name)
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
failed(ex)
ensure
begin
teardown
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
failed(ex) if passed?
end
end
self
end
# Class methods for the EdgeCase test suite.
class << self
def inherited(subclass)
@@ -194,32 +339,7 @@ module EdgeCase
end
def method_added(name)
testmethods << name unless tests_disabled?
end
def run_tests(accumulator)
puts
puts Color.green("Thinking #{self}")
testmethods.each do |m|
self.run_test(m, accumulator) if Koan.test_pattern =~ m.to_s
end
end
def run_test(method, accumulator)
test = self.new(method)
test.setup
begin
test.send(method)
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
test.failed(ex)
ensure
begin
test.teardown
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
test.failed(ex) if test.passed?
end
end
accumulator.accumulate(test)
testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
end
def end_of_enlightenment
@@ -261,17 +381,36 @@ module EdgeCase
@test_pattern ||= /^test_/
end
def total_tests
self.subclasses.inject(0){|total, k| total + k.testmethods.size }
end
end
end
class ThePath
def walk
sensei = EdgeCase::Sensei.new
each_step do |step|
sensei.observe(step.meditate)
end
sensei.instruct
end
def each_step
catch(:edgecase_exit) {
step_count = 0
EdgeCase::Koan.subclasses.each_with_index do |koan,koan_index|
koan.testmethods.each do |method_name|
step = koan.new(method_name, koan.to_s, koan_index+1, step_count+=1)
yield step
end
end
}
end
end
end
END {
EdgeCase::Koan.command_line(ARGV)
zen_master = EdgeCase::Sensei.new
catch(:edgecase_exit) {
EdgeCase::Koan.subclasses.each do |sc|
sc.run_tests(zen_master)
end
}
zen_master.report
EdgeCase::ThePath.new.walk
}