mirror of
https://github.com/edgecase/ruby_koans.git
synced 2026-04-15 07:23:19 -04:00
Compare commits
20 Commits
cinci-day-
...
output_ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
584b26e532 | ||
|
|
b2c47e0c0f | ||
|
|
f56117c0ca | ||
|
|
15551eaf53 | ||
|
|
b4e907e30e | ||
|
|
754a7694ad | ||
|
|
1492d7003a | ||
|
|
493300b24d | ||
|
|
3ed32b4534 | ||
|
|
aa09d17630 | ||
|
|
a36c3b7a4d | ||
|
|
18cccf33fb | ||
|
|
1597b2f912 | ||
|
|
57f0f4f178 | ||
|
|
0794235441 | ||
|
|
ad99c372c3 | ||
|
|
8d8287365b | ||
|
|
0c18e9742f | ||
|
|
28eec8cc41 | ||
|
|
b0e34a90e0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
dist
|
dist
|
||||||
|
.project_env.rc
|
||||||
|
.path_progress
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class AboutBlocks < EdgeCase::Koan
|
|||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def test_block_can_effect_variables_in_the_code_where_they_are_created
|
def test_block_can_affect_variables_in_the_code_where_they_are_created
|
||||||
value = :initial_value
|
value = :initial_value
|
||||||
method_with_block { value = :modified_in_a_block }
|
method_with_block { value = :modified_in_a_block }
|
||||||
assert_equal __, value
|
assert_equal __, value
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class DiceSet
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class AboutDiceSet < EdgeCase::Koan
|
class AboutDiceProject < EdgeCase::Koan
|
||||||
def test_can_create_a_dice_set
|
def test_can_create_a_dice_set
|
||||||
dice = DiceSet.new
|
dice = DiceSet.new
|
||||||
assert_not_nil dice
|
assert_not_nil dice
|
||||||
|
|||||||
@@ -78,16 +78,26 @@ class AboutIteration < EdgeCase::Koan
|
|||||||
assert_equal __, result
|
assert_equal __, result
|
||||||
|
|
||||||
# Files act like a collection of lines
|
# Files act like a collection of lines
|
||||||
file = File.open("example_file.txt")
|
File.open("example_file.txt") do |file|
|
||||||
upcase_lines = file.map { |line| line.strip.upcase }
|
upcase_lines = file.map { |line| line.strip.upcase }
|
||||||
assert_equal __, upcase_lines
|
assert_equal __, upcase_lines
|
||||||
|
end
|
||||||
|
|
||||||
# NOTE: You can create your own collections that work with each,
|
# NOTE: You can create your own collections that work with each,
|
||||||
# map, select, etc.
|
# map, select, etc.
|
||||||
ensure
|
|
||||||
# Arg, this is ugly.
|
|
||||||
# We will figure out how to fix this later.
|
|
||||||
file.close if file
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Bonus Question: In the previous koan, we saw the construct:
|
||||||
|
#
|
||||||
|
# File.open(filename) do |file|
|
||||||
|
# # code to read 'file'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Why did we do it that way instead of the following?
|
||||||
|
#
|
||||||
|
# file = File.open(filename)
|
||||||
|
# # code to read 'file'
|
||||||
|
#
|
||||||
|
# When you get to the "AboutSandwichCode" koan, recheck your answer.
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class AboutRegularExpressions < EdgeCase::Koan
|
|||||||
|
|
||||||
# THINK ABOUT IT:
|
# THINK ABOUT IT:
|
||||||
#
|
#
|
||||||
# Explain the difference between a character class ([…]) and alternation (|).
|
# Explain the difference between a character class ([...]) and alternation (|).
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||||
|
|
||||||
class AboutUsingBlocks < EdgeCase::Koan
|
class AboutSandwichCode < EdgeCase::Koan
|
||||||
|
|
||||||
def count_lines(file_name)
|
def count_lines(file_name)
|
||||||
file = open(file_name)
|
file = open(file_name)
|
||||||
@@ -86,7 +86,7 @@ class AboutUsingBlocks < EdgeCase::Koan
|
|||||||
def test_finding_lines2
|
def test_finding_lines2
|
||||||
assert_equal __, find_line2("example_file.txt")
|
assert_equal __, find_line2("example_file.txt")
|
||||||
end
|
end
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def count_lines3(file_name)
|
def count_lines3(file_name)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
# A greed roll is scored as follows:
|
# A greed roll is scored as follows:
|
||||||
#
|
#
|
||||||
# * A set of three ones is 1000 points
|
# * A set of three ones is 1000 points
|
||||||
#
|
#
|
||||||
# * A set of three numbers (other than ones) is worth 100 times the
|
# * A set of three numbers (other than ones) is worth 100 times the
|
||||||
# number. (e.g. three fives is 500 points).
|
# number. (e.g. three fives is 500 points).
|
||||||
#
|
#
|
||||||
@@ -33,7 +33,7 @@ def score(dice)
|
|||||||
# You need to write this method
|
# You need to write this method
|
||||||
end
|
end
|
||||||
|
|
||||||
class AboutScoringAssignment < EdgeCase::Koan
|
class AboutScoringProject < EdgeCase::Koan
|
||||||
def test_score_of_an_empty_list_is_zero
|
def test_score_of_an_empty_list_is_zero
|
||||||
assert_equal 0, score([])
|
assert_equal 0, score([])
|
||||||
end
|
end
|
||||||
|
|||||||
77
koans/about_symbols.rb
Normal file
77
koans/about_symbols.rb
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||||
|
|
||||||
|
class AboutSymbols < EdgeCase::Koan
|
||||||
|
def test_symbols_are_symbols
|
||||||
|
symbol = :ruby
|
||||||
|
assert_equal __, symbol.is_a?(Symbol)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_can_be_compared
|
||||||
|
symbol1 = :a_symbol
|
||||||
|
symbol2 = :a_symbol
|
||||||
|
symbol3 = :something_else
|
||||||
|
|
||||||
|
assert symbol1 == __
|
||||||
|
assert symbol1 != __
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_identical_symbols_are_a_single_internal_object
|
||||||
|
symbol1 = :a_symbol
|
||||||
|
symbol2 = :a_symbol
|
||||||
|
|
||||||
|
assert symbol1.equal?(__)
|
||||||
|
assert_equal __, symbol2.object_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_method_names_become_symbols
|
||||||
|
all_symbols = Symbol.all_symbols
|
||||||
|
|
||||||
|
assert_equal __, all_symbols.include?(:test_method_names_are_symbols)
|
||||||
|
end
|
||||||
|
|
||||||
|
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?(__)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_can_be_made_from_strings
|
||||||
|
string = "catsAndDogs"
|
||||||
|
assert_equal __, string.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_with_spaces_can_be_built
|
||||||
|
symbol = :"cats and dogs"
|
||||||
|
|
||||||
|
assert_equal symbol, __.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_to_s_is_called_on_interpolated_symbols
|
||||||
|
symbol = :cats
|
||||||
|
string = "It is raining #{symbol} and dogs."
|
||||||
|
|
||||||
|
assert_equal __, string
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_are_not_strings
|
||||||
|
symbol = :ruby
|
||||||
|
assert_equal __, symbol.is_a?(String)
|
||||||
|
assert_equal __, symbol.eql?("ruby")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_do_not_have_string_methods
|
||||||
|
symbol = :not_a_string
|
||||||
|
assert_equal __, symbol.respond_to?(:each_char)
|
||||||
|
assert_equal __, 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(___) do
|
||||||
|
:cats + :dogs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
# You need to write the triangle method in the file 'triangle.rb'
|
# You need to write the triangle method in the file 'triangle.rb'
|
||||||
require 'triangle.rb'
|
require 'triangle.rb'
|
||||||
|
|
||||||
class AboutTriangleAssignment < EdgeCase::Koan
|
class AboutTriangleProject < EdgeCase::Koan
|
||||||
def test_equilateral_triangles_have_equal_sides
|
def test_equilateral_triangles_have_equal_sides
|
||||||
assert_equal :equilateral, triangle(2, 2, 2)
|
assert_equal :equilateral, triangle(2, 2, 2)
|
||||||
assert_equal :equilateral, triangle(10, 10, 10)
|
assert_equal :equilateral, triangle(10, 10, 10)
|
||||||
@@ -22,4 +22,4 @@ class AboutTriangleAssignment < EdgeCase::Koan
|
|||||||
assert_equal :scalene, triangle(5, 4, 2)
|
assert_equal :scalene, triangle(5, 4, 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
# You need to write the triangle method in the file 'triangle.rb'
|
# You need to write the triangle method in the file 'triangle.rb'
|
||||||
require 'triangle.rb'
|
require 'triangle.rb'
|
||||||
|
|
||||||
class AboutTriangleAssignment2 < EdgeCase::Koan
|
class AboutTriangleProject2 < EdgeCase::Koan
|
||||||
# The first assignment did not talk about how to handle errors.
|
# The first assignment did not talk about how to handle errors.
|
||||||
# Let's handle that part now.
|
# Let's handle that part now.
|
||||||
def test_illegal_triangles_throw_exceptions
|
def test_illegal_triangles_throw_exceptions
|
||||||
assert_raise(TriangleError) do triangle(0, 0, 0) end
|
assert_raise(TriangleError) do triangle(0, 0, 0) end
|
||||||
assert_raise(TriangleError) do triangle(3, 4, -5) end
|
assert_raise(TriangleError) do triangle(3, 4, -5) end
|
||||||
assert_raise(TriangleError) do triangle(1, 1, 3) end
|
assert_raise(TriangleError) do triangle(1, 1, 3) end
|
||||||
assert_raise(TriangleError) do triangle(2, 4, 2) end
|
assert_raise(TriangleError) do triangle(2, 4, 2) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ module EdgeCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Sensei
|
class Sensei
|
||||||
attr_reader :failure, :failed_test
|
attr_reader :failure, :failed_test, :pass_count
|
||||||
|
|
||||||
in_ruby_version("1.8") do
|
in_ruby_version("1.8") do
|
||||||
AssertionError = Test::Unit::AssertionFailedError
|
AssertionError = Test::Unit::AssertionFailedError
|
||||||
@@ -91,16 +91,43 @@ module EdgeCase
|
|||||||
@pass_count = 0
|
@pass_count = 0
|
||||||
@failure = nil
|
@failure = nil
|
||||||
@failed_test = nil
|
@failed_test = nil
|
||||||
|
@observations = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def accumulate(test)
|
PROGRESS_FILE_NAME = '.path_progress'
|
||||||
if test.passed?
|
|
||||||
|
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
|
||||||
|
@_contents = []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@_contents
|
||||||
|
end
|
||||||
|
|
||||||
|
def observe(step)
|
||||||
|
if step.passed?
|
||||||
@pass_count += 1
|
@pass_count += 1
|
||||||
puts Color.green(" #{test.name} has expanded your awareness.")
|
if @pass_count > progress.last.to_i
|
||||||
|
@observations << Color.green("#{step.koan_file}##{step.name} has expanded your awareness.")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
puts Color.red(" #{test.name} has damaged your karma.")
|
@failed_test = step
|
||||||
@failed_test = test
|
@failure = step.failure
|
||||||
@failure = test.failure
|
add_progress(@pass_count)
|
||||||
|
@observations << Color.red("#{step.koan_file}##{step.name} has damaged your karma.")
|
||||||
throw :edgecase_exit
|
throw :edgecase_exit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -113,22 +140,115 @@ module EdgeCase
|
|||||||
failure.is_a?(AssertionError)
|
failure.is_a?(AssertionError)
|
||||||
end
|
end
|
||||||
|
|
||||||
def report
|
def instruct
|
||||||
if failed?
|
if failed?
|
||||||
puts
|
@observations.each{|c| puts c }
|
||||||
puts Color.green("You have not yet reached enlightenment ...")
|
encourage
|
||||||
puts Color.red(failure.message)
|
guide_through_error
|
||||||
puts
|
a_zenlike_statement
|
||||||
puts Color.green("Please meditate on the following code:")
|
show_progress
|
||||||
if assert_failed?
|
else
|
||||||
#puts find_interesting_lines(failure.backtrace)
|
end_screen
|
||||||
puts find_interesting_lines(failure.backtrace).collect {|l| Color.red(l) }
|
|
||||||
else
|
|
||||||
puts Color.red(failure.backtrace)
|
|
||||||
end
|
|
||||||
puts
|
|
||||||
end
|
end
|
||||||
puts Color.green(a_zenlike_statement)
|
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
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def find_interesting_lines(backtrace)
|
def find_interesting_lines(backtrace)
|
||||||
@@ -140,7 +260,6 @@ module EdgeCase
|
|||||||
# Hat's tip to Ara T. Howard for the zen statements from his
|
# Hat's tip to Ara T. Howard for the zen statements from his
|
||||||
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
|
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
|
||||||
def a_zenlike_statement
|
def a_zenlike_statement
|
||||||
puts
|
|
||||||
if !failed?
|
if !failed?
|
||||||
zen_statement = "Mountains are again merely mountains"
|
zen_statement = "Mountains are again merely mountains"
|
||||||
else
|
else
|
||||||
@@ -159,18 +278,21 @@ module EdgeCase
|
|||||||
"things are not what they appear to be: nor are they otherwise"
|
"things are not what they appear to be: nor are they otherwise"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
zen_statement
|
puts Color.green(zen_statement)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Koan
|
class Koan
|
||||||
include Test::Unit::Assertions
|
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
|
@name = name
|
||||||
@failure = nil
|
@failure = nil
|
||||||
|
@koan_count = koan_count
|
||||||
|
@step_count = step_count
|
||||||
|
@koan_file = koan_file
|
||||||
end
|
end
|
||||||
|
|
||||||
def passed?
|
def passed?
|
||||||
@@ -187,6 +309,22 @@ module EdgeCase
|
|||||||
def teardown
|
def teardown
|
||||||
end
|
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 methods for the EdgeCase test suite.
|
||||||
class << self
|
class << self
|
||||||
def inherited(subclass)
|
def inherited(subclass)
|
||||||
@@ -194,32 +332,7 @@ module EdgeCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def method_added(name)
|
def method_added(name)
|
||||||
testmethods << name unless tests_disabled?
|
testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_of_enlightenment
|
def end_of_enlightenment
|
||||||
@@ -261,17 +374,36 @@ module EdgeCase
|
|||||||
@test_pattern ||= /^test_/
|
@test_pattern ||= /^test_/
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
END {
|
END {
|
||||||
EdgeCase::Koan.command_line(ARGV)
|
EdgeCase::Koan.command_line(ARGV)
|
||||||
zen_master = EdgeCase::Sensei.new
|
EdgeCase::ThePath.new.walk
|
||||||
catch(:edgecase_exit) {
|
|
||||||
EdgeCase::Koan.subclasses.each do |sc|
|
|
||||||
sc.run_tests(zen_master)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
zen_master.report
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ require 'about_arrays'
|
|||||||
require 'about_array_assignment'
|
require 'about_array_assignment'
|
||||||
require 'about_hashes'
|
require 'about_hashes'
|
||||||
require 'about_strings'
|
require 'about_strings'
|
||||||
|
require 'about_symbols'
|
||||||
require 'about_regular_expressions'
|
require 'about_regular_expressions'
|
||||||
require 'about_methods'
|
require 'about_methods'
|
||||||
require 'about_constants'
|
require 'about_constants'
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ class AboutBlocks < EdgeCase::Koan
|
|||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def test_block_can_effect_variables_in_the_code_where_they_are_created
|
def test_block_can_affect_variables_in_the_code_where_they_are_created
|
||||||
value = :initial_value
|
value = :initial_value
|
||||||
method_with_block { value = :modified_in_a_block }
|
method_with_block { value = :modified_in_a_block }
|
||||||
assert_equal __(:modified_in_a_block), value
|
assert_equal __(:modified_in_a_block), value
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ class DiceSet
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class AboutDiceSet < EdgeCase::Koan
|
class AboutDiceProject < EdgeCase::Koan
|
||||||
def test_can_create_a_dice_set
|
def test_can_create_a_dice_set
|
||||||
dice = DiceSet.new
|
dice = DiceSet.new
|
||||||
assert_not_nil dice
|
assert_not_nil dice
|
||||||
|
|||||||
@@ -78,16 +78,26 @@ class AboutIteration < EdgeCase::Koan
|
|||||||
assert_equal __([11, 12, 13]), result
|
assert_equal __([11, 12, 13]), result
|
||||||
|
|
||||||
# Files act like a collection of lines
|
# Files act like a collection of lines
|
||||||
file = File.open("example_file.txt")
|
File.open("example_file.txt") do |file|
|
||||||
upcase_lines = file.map { |line| line.strip.upcase }
|
upcase_lines = file.map { |line| line.strip.upcase }
|
||||||
assert_equal __(["THIS", "IS", "A", "TEST"]), upcase_lines
|
assert_equal __(["THIS", "IS", "A", "TEST"]), upcase_lines
|
||||||
|
end
|
||||||
|
|
||||||
# NOTE: You can create your own collections that work with each,
|
# NOTE: You can create your own collections that work with each,
|
||||||
# map, select, etc.
|
# map, select, etc.
|
||||||
ensure
|
|
||||||
# Arg, this is ugly.
|
|
||||||
# We will figure out how to fix this later.
|
|
||||||
file.close if file
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Bonus Question: In the previous koan, we saw the construct:
|
||||||
|
#
|
||||||
|
# File.open(filename) do |file|
|
||||||
|
# # code to read 'file'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Why did we do it that way instead of the following?
|
||||||
|
#
|
||||||
|
# file = File.open(filename)
|
||||||
|
# # code to read 'file'
|
||||||
|
#
|
||||||
|
# When you get to the "AboutSandwichCode" koan, recheck your answer.
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
def my_global_method(a,b)
|
def my_global_method(a,b)
|
||||||
a + b
|
a + b
|
||||||
end
|
end
|
||||||
|
|
||||||
class AboutMethods < EdgeCase::Koan
|
class AboutMethods < EdgeCase::Koan
|
||||||
|
|
||||||
def test_calling_global_methods
|
def test_calling_global_methods
|
||||||
@@ -36,19 +36,19 @@ class AboutMethods < EdgeCase::Koan
|
|||||||
# Rewrite the eval string to continue.
|
# Rewrite the eval string to continue.
|
||||||
#
|
#
|
||||||
end
|
end
|
||||||
|
|
||||||
# NOTE: wrong number of argument is not a SYNTAX error, but a
|
# NOTE: wrong number of argument is not a SYNTAX error, but a
|
||||||
# runtime error.
|
# runtime error.
|
||||||
def test_calling_global_methods_with_wrong_number_of_arguments
|
def test_calling_global_methods_with_wrong_number_of_arguments
|
||||||
exception = assert_raise(___(ArgumentError)) do
|
exception = assert_raise(___(ArgumentError)) do
|
||||||
my_global_method
|
my_global_method
|
||||||
end
|
end
|
||||||
assert_match(/#{__("wrong number of arguments")}/, exception.message)
|
assert_match(/#{__("wrong (number|#) of arguments")}/, exception.message)
|
||||||
|
|
||||||
exception = assert_raise(___(ArgumentError)) do
|
exception = assert_raise(___(ArgumentError)) do
|
||||||
my_global_method(1,2,3)
|
my_global_method(1,2,3)
|
||||||
end
|
end
|
||||||
assert_match(/#{__("wrong number of arguments")}/, exception.message)
|
assert_match(/#{__("wrong (number|#) of arguments")}/, exception.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@@ -142,7 +142,7 @@ class AboutMethods < EdgeCase::Koan
|
|||||||
"tail"
|
"tail"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_calling_methods_in_other_objects_require_explicit_receiver
|
def test_calling_methods_in_other_objects_require_explicit_receiver
|
||||||
rover = Dog.new
|
rover = Dog.new
|
||||||
assert_equal __("Fido"), rover.name
|
assert_equal __("Fido"), rover.name
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class AboutRegularExpressions < EdgeCase::Koan
|
|||||||
|
|
||||||
# THINK ABOUT IT:
|
# THINK ABOUT IT:
|
||||||
#
|
#
|
||||||
# Explain the difference between a character class ([…]) and alternation (|).
|
# Explain the difference between a character class ([...]) and alternation (|).
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||||
|
|
||||||
class AboutUsingBlocks < EdgeCase::Koan
|
class AboutSandwichCode < EdgeCase::Koan
|
||||||
|
|
||||||
def count_lines(file_name)
|
def count_lines(file_name)
|
||||||
file = open(file_name)
|
file = open(file_name)
|
||||||
@@ -93,7 +93,7 @@ class AboutUsingBlocks < EdgeCase::Koan
|
|||||||
def test_finding_lines2
|
def test_finding_lines2
|
||||||
assert_equal __("test\n"), find_line2("example_file.txt")
|
assert_equal __("test\n"), find_line2("example_file.txt")
|
||||||
end
|
end
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def count_lines3(file_name)
|
def count_lines3(file_name)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
# A greed roll is scored as follows:
|
# A greed roll is scored as follows:
|
||||||
#
|
#
|
||||||
# * A set of three ones is 1000 points
|
# * A set of three ones is 1000 points
|
||||||
#
|
#
|
||||||
# * A set of three numbers (other than ones) is worth 100 times the
|
# * A set of three numbers (other than ones) is worth 100 times the
|
||||||
# number. (e.g. three fives is 500 points).
|
# number. (e.g. three fives is 500 points).
|
||||||
#
|
#
|
||||||
@@ -54,7 +54,7 @@ def score(dice)
|
|||||||
#++
|
#++
|
||||||
end
|
end
|
||||||
|
|
||||||
class AboutScoringAssignment < EdgeCase::Koan
|
class AboutScoringProject < EdgeCase::Koan
|
||||||
def test_score_of_an_empty_list_is_zero
|
def test_score_of_an_empty_list_is_zero
|
||||||
assert_equal 0, score([])
|
assert_equal 0, score([])
|
||||||
end
|
end
|
||||||
|
|||||||
100
src/about_symbols.rb
Normal file
100
src/about_symbols.rb
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||||
|
|
||||||
|
class AboutSymbols < EdgeCase::Koan
|
||||||
|
def test_symbols_are_symbols
|
||||||
|
symbol = :ruby
|
||||||
|
assert_equal __(true), symbol.is_a?(Symbol)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_can_be_compared
|
||||||
|
symbol1 = :a_symbol
|
||||||
|
symbol2 = :a_symbol
|
||||||
|
symbol3 = :something_else
|
||||||
|
|
||||||
|
assert symbol1 == __(symbol2)
|
||||||
|
assert symbol1 != __(symbol3)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_identical_symbols_are_a_single_internal_object
|
||||||
|
symbol1 = :a_symbol
|
||||||
|
symbol2 = :a_symbol
|
||||||
|
|
||||||
|
assert symbol1.equal?(__(symbol2))
|
||||||
|
assert_equal __(symbol1.object_id), symbol2.object_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_method_names_become_symbols
|
||||||
|
all_symbols = Symbol.all_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))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_can_be_made_from_strings
|
||||||
|
string = "catsAndDogs"
|
||||||
|
assert_equal __(:catsAndDogs), string.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_with_spaces_can_be_built
|
||||||
|
symbol = :"cats and dogs"
|
||||||
|
|
||||||
|
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."
|
||||||
|
|
||||||
|
assert_equal __('It is raining cats and dogs.'), string
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_are_not_strings
|
||||||
|
symbol = :ruby
|
||||||
|
assert_equal __(false), symbol.is_a?(String)
|
||||||
|
assert_equal __(false), symbol.eql?("ruby")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_symbols_do_not_have_string_methods
|
||||||
|
symbol = :not_a_string
|
||||||
|
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
|
||||||
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
# You need to write the triangle method in the file 'triangle.rb'
|
# You need to write the triangle method in the file 'triangle.rb'
|
||||||
require 'triangle.rb'
|
require 'triangle.rb'
|
||||||
|
|
||||||
class AboutTriangleAssignment < EdgeCase::Koan
|
class AboutTriangleProject < EdgeCase::Koan
|
||||||
def test_equilateral_triangles_have_equal_sides
|
def test_equilateral_triangles_have_equal_sides
|
||||||
assert_equal :equilateral, triangle(2, 2, 2)
|
assert_equal :equilateral, triangle(2, 2, 2)
|
||||||
assert_equal :equilateral, triangle(10, 10, 10)
|
assert_equal :equilateral, triangle(10, 10, 10)
|
||||||
@@ -22,4 +22,4 @@ class AboutTriangleAssignment < EdgeCase::Koan
|
|||||||
assert_equal :scalene, triangle(5, 4, 2)
|
assert_equal :scalene, triangle(5, 4, 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
|||||||
# You need to write the triangle method in the file 'triangle.rb'
|
# You need to write the triangle method in the file 'triangle.rb'
|
||||||
require 'triangle.rb'
|
require 'triangle.rb'
|
||||||
|
|
||||||
class AboutTriangleAssignment2 < EdgeCase::Koan
|
class AboutTriangleProject2 < EdgeCase::Koan
|
||||||
# The first assignment did not talk about how to handle errors.
|
# The first assignment did not talk about how to handle errors.
|
||||||
# Let's handle that part now.
|
# Let's handle that part now.
|
||||||
def test_illegal_triangles_throw_exceptions
|
def test_illegal_triangles_throw_exceptions
|
||||||
assert_raise(TriangleError) do triangle(0, 0, 0) end
|
assert_raise(TriangleError) do triangle(0, 0, 0) end
|
||||||
assert_raise(TriangleError) do triangle(3, 4, -5) end
|
assert_raise(TriangleError) do triangle(3, 4, -5) end
|
||||||
assert_raise(TriangleError) do triangle(1, 1, 3) end
|
assert_raise(TriangleError) do triangle(1, 1, 3) end
|
||||||
assert_raise(TriangleError) do triangle(2, 4, 2) end
|
assert_raise(TriangleError) do triangle(2, 4, 2) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
259
src/edgecase.rb
259
src/edgecase.rb
@@ -6,8 +6,14 @@ require 'test/unit/assertions'
|
|||||||
class FillMeInError < StandardError
|
class FillMeInError < StandardError
|
||||||
end
|
end
|
||||||
|
|
||||||
def in_ruby_version(version)
|
def ruby_version?(version)
|
||||||
yield if 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
|
end
|
||||||
|
|
||||||
def __(value="FILL ME IN", value19=:mu)
|
def __(value="FILL ME IN", value19=:mu)
|
||||||
@@ -73,7 +79,7 @@ module EdgeCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Sensei
|
class Sensei
|
||||||
attr_reader :failure, :failed_test
|
attr_reader :failure, :failed_test, :pass_count
|
||||||
|
|
||||||
in_ruby_version("1.8") do
|
in_ruby_version("1.8") do
|
||||||
AssertionError = Test::Unit::AssertionFailedError
|
AssertionError = Test::Unit::AssertionFailedError
|
||||||
@@ -91,16 +97,43 @@ module EdgeCase
|
|||||||
@pass_count = 0
|
@pass_count = 0
|
||||||
@failure = nil
|
@failure = nil
|
||||||
@failed_test = nil
|
@failed_test = nil
|
||||||
|
@observations = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def accumulate(test)
|
PROGRESS_FILE_NAME = '.path_progress'
|
||||||
if test.passed?
|
|
||||||
|
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
|
||||||
|
@_contents = []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@_contents
|
||||||
|
end
|
||||||
|
|
||||||
|
def observe(step)
|
||||||
|
if step.passed?
|
||||||
@pass_count += 1
|
@pass_count += 1
|
||||||
puts Color.green(" #{test.name} has expanded your awareness.")
|
if @pass_count > progress.last.to_i
|
||||||
|
@observations << Color.green("#{step.koan_file}##{step.name} has expanded your awareness.")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
puts Color.red(" #{test.name} has damaged your karma.")
|
@failed_test = step
|
||||||
@failed_test = test
|
@failure = step.failure
|
||||||
@failure = test.failure
|
add_progress(@pass_count)
|
||||||
|
@observations << Color.red("#{step.koan_file}##{step.name} has damaged your karma.")
|
||||||
throw :edgecase_exit
|
throw :edgecase_exit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -113,22 +146,116 @@ module EdgeCase
|
|||||||
failure.is_a?(AssertionError)
|
failure.is_a?(AssertionError)
|
||||||
end
|
end
|
||||||
|
|
||||||
def report
|
def instruct
|
||||||
if failed?
|
if failed?
|
||||||
puts
|
@observations.each{|c| puts c }
|
||||||
puts Color.green("You have not yet reached enlightenment ...")
|
encourage
|
||||||
puts Color.red(failure.message)
|
guide_through_error
|
||||||
puts
|
a_zenlike_statement
|
||||||
puts Color.green("Please meditate on the following code:")
|
show_progress
|
||||||
if assert_failed?
|
else
|
||||||
#puts find_interesting_lines(failure.backtrace)
|
end_screen
|
||||||
puts find_interesting_lines(failure.backtrace).collect {|l| Color.red(l) }
|
|
||||||
else
|
|
||||||
puts Color.red(failure.backtrace)
|
|
||||||
end
|
|
||||||
puts
|
|
||||||
end
|
end
|
||||||
puts Color.green(a_zenlike_statement)
|
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
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def find_interesting_lines(backtrace)
|
def find_interesting_lines(backtrace)
|
||||||
@@ -140,7 +267,6 @@ module EdgeCase
|
|||||||
# Hat's tip to Ara T. Howard for the zen statements from his
|
# Hat's tip to Ara T. Howard for the zen statements from his
|
||||||
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
|
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
|
||||||
def a_zenlike_statement
|
def a_zenlike_statement
|
||||||
puts
|
|
||||||
if !failed?
|
if !failed?
|
||||||
zen_statement = "Mountains are again merely mountains"
|
zen_statement = "Mountains are again merely mountains"
|
||||||
else
|
else
|
||||||
@@ -159,18 +285,21 @@ module EdgeCase
|
|||||||
"things are not what they appear to be: nor are they otherwise"
|
"things are not what they appear to be: nor are they otherwise"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
zen_statement
|
puts Color.green(zen_statement)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Koan
|
class Koan
|
||||||
include Test::Unit::Assertions
|
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
|
@name = name
|
||||||
@failure = nil
|
@failure = nil
|
||||||
|
@koan_count = koan_count
|
||||||
|
@step_count = step_count
|
||||||
|
@koan_file = koan_file
|
||||||
end
|
end
|
||||||
|
|
||||||
def passed?
|
def passed?
|
||||||
@@ -187,6 +316,22 @@ module EdgeCase
|
|||||||
def teardown
|
def teardown
|
||||||
end
|
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 methods for the EdgeCase test suite.
|
||||||
class << self
|
class << self
|
||||||
def inherited(subclass)
|
def inherited(subclass)
|
||||||
@@ -194,32 +339,7 @@ module EdgeCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def method_added(name)
|
def method_added(name)
|
||||||
testmethods << name unless tests_disabled?
|
testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_of_enlightenment
|
def end_of_enlightenment
|
||||||
@@ -261,17 +381,36 @@ module EdgeCase
|
|||||||
@test_pattern ||= /^test_/
|
@test_pattern ||= /^test_/
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
END {
|
END {
|
||||||
EdgeCase::Koan.command_line(ARGV)
|
EdgeCase::Koan.command_line(ARGV)
|
||||||
zen_master = EdgeCase::Sensei.new
|
EdgeCase::ThePath.new.walk
|
||||||
catch(:edgecase_exit) {
|
|
||||||
EdgeCase::Koan.subclasses.each do |sc|
|
|
||||||
sc.run_tests(zen_master)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
zen_master.report
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ require 'about_arrays'
|
|||||||
require 'about_array_assignment'
|
require 'about_array_assignment'
|
||||||
require 'about_hashes'
|
require 'about_hashes'
|
||||||
require 'about_strings'
|
require 'about_strings'
|
||||||
|
require 'about_symbols'
|
||||||
require 'about_regular_expressions'
|
require 'about_regular_expressions'
|
||||||
require 'about_methods'
|
require 'about_methods'
|
||||||
require 'about_constants'
|
require 'about_constants'
|
||||||
|
|||||||
Reference in New Issue
Block a user