34 Commits

Author SHA1 Message Date
Marc Peabody
9b97fea75a experimenting with more java interop 2010-09-28 09:38:41 -04:00
Marc Peabody + Jim Weirich
45523c03c0 More class conflict examples. 2010-09-27 16:54:10 -04:00
Jim Weirich
8436b46c1a Added Ruby version information to the output. 2010-09-27 14:36:38 -04:00
Jim Weirich
322ca38767 Minitest lines in backtrace are no longer interesting. 2010-09-27 14:15:59 -04:00
Jim Weirich
91f15dc690 Simplified output for running against multiple rubies. 2010-09-27 14:09:38 -04:00
Jim Weirich
beb7fe591e Added rake task for running koans against several rubies. 2010-09-27 13:46:21 -04:00
Jim Weirich
17fb071814 Evaluated each assert line without a replacement test specified. 2010-09-27 13:38:50 -04:00
Jim Weirich
012cb20bb3 Moved checks into a separate rake file. 2010-09-27 13:38:22 -04:00
Jim Weirich
ece35b2539 Added symbol & string identity tests. 2010-09-27 11:36:38 -04:00
Jim Weirich
fe2ee86172 Added Koan on basic objects 2010-09-27 11:36:00 -04:00
Jim Weirich
9caf7a950a Updated koans from source directory. 2010-09-27 10:50:29 -04:00
Jim Weirich
a13f184ef1 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
2010-09-27 10:49:17 -04:00
Jim Weirich
584b26e532 Updated for JRuby 2010-09-27 10:44:58 -04:00
Jim Weirich
b2c47e0c0f Switch blue color to cyan for better contrast on dark terminals. 2010-09-27 10:37:35 -04:00
Marc Peabody
f56117c0ca merge and 80 char limit on src 2010-09-22 15:14:11 -04:00
Marc Peabody
15551eaf53 80 char limit to end screen 2010-09-22 15:09:08 -04:00
Jim Weirich
b4e907e30e Updated Koans directory 2010-09-22 14:54:42 -04:00
Jim Weirich
754a7694ad Added else clause if progress file is not there. 2010-09-22 14:50:25 -04:00
Marc Peabody
1492d7003a regen for updated koans/edgecase.rb 2010-09-22 14:10:34 -04:00
Marc Peabody
493300b24d end screen with koans logo ascii art 2010-09-22 14:08:38 -04:00
Matt Yoho
1bc1b8e50c Add timestamps to package output 2010-09-21 17:48:30 -04:00
Matt Yoho
9b5dfb4e42 Add koan on asserting exceptions 2010-09-21 17:12:50 -04:00
Matt Yoho
3ed32b4534 Use correct method for exception fill-in blank 2010-09-21 16:05:02 -04:00
Matt Yoho
aa09d17630 Clean up about_koans and add another example 2010-09-21 16:04:58 -04:00
Daniel Parker
a36c3b7a4d Fixed typo 2010-09-21 17:41:09 +01:00
Matt Yoho
18cccf33fb Add about_symbols koan 2010-09-21 12:03:31 -04:00
Erik Ogan
1597b2f912 minor grammar nit 2010-09-19 16:53:17 -07:00
Jim Weirich
57f0f4f178 Updated koans from source. 2010-09-12 21:33:11 -04:00
Jim Weirich
0794235441 Removed utf-8 character in comments. 2010-09-12 21:32:55 -04:00
Jim Weirich
ad99c372c3 Normalized file name and koan category name. 2010-09-12 21:27:22 -04:00
Jim Weirich
8d8287365b Added bonus question about the file read. 2010-09-12 21:27:01 -04:00
Jim Weirich
0c18e9742f Simplified file read example. 2010-09-12 21:26:49 -04:00
Jim Weirich
28eec8cc41 Ignoring project env rc file. 2010-09-12 21:25:42 -04:00
Greg Mefford
b0e34a90e0 Cleaned up some messy File housekeeping. 2010-09-12 17:07:22 -04:00
48 changed files with 1098 additions and 217 deletions

2
.gitignore vendored
View File

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

View File

@@ -11,18 +11,24 @@ DIST_DIR = 'dist'
SRC_FILES = FileList["#{SRC_DIR}/*"] SRC_FILES = FileList["#{SRC_DIR}/*"]
KOAN_FILES = SRC_FILES.pathmap("#{PROB_DIR}/%f") KOAN_FILES = SRC_FILES.pathmap("#{PROB_DIR}/%f")
TAR_FILE = "#{DIST_DIR}/rubykoans.tgz" today = Time.now.strftime("%Y-%m-%d")
ZIP_FILE = "#{DIST_DIR}/rubykoans.zip" TAR_FILE = "#{DIST_DIR}/rubykoans-#{today}.tgz"
ZIP_FILE = "#{DIST_DIR}/rubykoans-#{today}.zip"
CLOBBER.include(DIST_DIR) CLOBBER.include(DIST_DIR)
module Koans module Koans
# Remove solution info from source
# __(a,b) => __
# _n_(number) => __
# # __ =>
def Koans.remove_solution(line) def Koans.remove_solution(line)
line = line.gsub(/\b____\([^\)]+\)/, "____") line = line.gsub(/\b____\([^\)]+\)/, "____")
line = line.gsub(/\b___\([^\)]+\)/, "___") line = line.gsub(/\b___\([^\)]+\)/, "___")
line = line.gsub(/\b__\([^\)]+\)/, "__") line = line.gsub(/\b__\([^\)]+\)/, "__")
line = line.gsub(/\b_n_\([^\)]+\)/, "_n_") line = line.gsub(/\b_n_\([^\)]+\)/, "_n_")
line = line.gsub(%r(/\#\{__\}/), "/__/") line = line.gsub(%r(/\#\{__\}/), "/__/")
line = line.gsub(/\s*#\s*__\s*$/, '')
line line
end end
@@ -83,14 +89,6 @@ task :upload => [TAR_FILE, ZIP_FILE] do
sh "scp #{ZIP_FILE} linode:sites/onestepback.org/download" sh "scp #{ZIP_FILE} linode:sites/onestepback.org/download"
end end
desc "Check that the require files match the about_* files"
task :check do
about_files = Dir['src/about_*.rb'].size
about_requires = `grep require src/path_to_enlightenment.rb | wc -l`.to_i
puts "# of about files: #{about_files}"
puts "# of about requires: #{about_requires}"
end
desc "Generate the Koans from the source files from scratch." desc "Generate the Koans from the source files from scratch."
task :regen => [:clobber_koans, :gen] task :regen => [:clobber_koans, :gen]

View File

@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutArrays < EdgeCase::Koan class AboutArrays < EdgeCase::Koan
def test_creating_arrays def test_creating_arrays
empty_array = Array.new empty_array = Array.new
assert_equal Array, empty_array.class assert_equal __, empty_array.class
assert_equal __, empty_array.size assert_equal __, empty_array.size
end end

View File

@@ -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

View File

@@ -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

View File

@@ -57,4 +57,12 @@ class AboutExceptions < EdgeCase::Koan
assert_equal __, result assert_equal __, result
end end
# Sometimes, we must know about the unknown
def test_asserting_an_error_is_raised
# A do-end is a block, a topic to explore more later
assert_raise(___) do
raise MySpecialError.new("New instances can be raised directly.")
end
end
end end

View File

@@ -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

View File

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

56
koans/about_objects.rb Normal file
View File

@@ -0,0 +1,56 @@
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutObjects < EdgeCase::Koan
def test_everything_is_an_object
assert_equal __, 1.is_a?(Object)
assert_equal __, 1.5.is_a?(Object)
assert_equal __, "string".is_a?(Object)
assert_equal __, nil.is_a?(Object)
assert_equal __, Object.is_a?(Object)
end
def test_objects_can_be_converted_to_strings
assert_equal __, 123.to_s
assert_equal __, nil.to_s
end
def test_objects_can_be_inspected
assert_equal __, 123.inspect
assert_equal __, nil.inspect
end
def test_every_object_has_an_id
obj = Object.new
assert_equal __, obj.object_id.class
end
def test_every_object_has_different_id
obj = Object.new
another_obj = Object.new
assert_equal __, obj.object_id != another_obj.object_id
end
def test_some_system_objects_always_have_the_same_id
assert_equal __, false.object_id
assert_equal __, true.object_id
assert_equal __, nil.object_id
end
def test_small_integers_have_fixed_ids
assert_equal __, 0.object_id
assert_equal __, 1.object_id
assert_equal __, 2.object_id
assert_equal __, 100.object_id
# THINK ABOUT IT:
# What pattern do the object IDs for small integers follow?
end
def test_clone_creates_a_different_object
obj = Object.new
copy = obj.clone
assert_equal __, obj != copy
assert_equal __, obj.object_id != copy.object_id
end
end

View File

@@ -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 (|).
# ------------------------------------------------------------------ # ------------------------------------------------------------------

View File

@@ -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)

View File

@@ -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

View File

@@ -182,4 +182,12 @@ EOS
words = ["Now", "is", "the", "time"] words = ["Now", "is", "the", "time"]
assert_equal __, words.join(" ") assert_equal __, words.join(" ")
end end
def test_strings_are_not_unique_objects
a = "a string"
b = "a string"
assert_equal __, a == b
assert_equal __, a.object_id == b.object_id
end
end end

100
koans/about_symbols.rb Normal file
View 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 __, 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_equal __, symbol1 == symbol2
assert_equal __, symbol1.object_id == symbol2.object_id
end
def test_method_names_become_symbols
all_symbols = Symbol.all_symbols
assert_equal __, 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 __, all_symbols.include?(__)
end
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_symbols_with_spaces_can_be_built
value = "and"
symbol = :"cats #{value} 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
def test_symbols_can_be_dynamically_created
assert_equal __, ("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

@@ -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)

View File

@@ -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 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

View File

@@ -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?
@pass_count += 1 def add_progress(prog)
puts Color.green(" #{test.name} has expanded your awareness.") @_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 else
puts Color.red(" #{test.name} has damaged your karma.") @_contents = []
@failed_test = test end
@failure = test.failure 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 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?
#puts find_interesting_lines(failure.backtrace)
puts find_interesting_lines(failure.backtrace).collect {|l| Color.red(l) }
else 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 end
puts puts
end 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 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
} }

View File

@@ -4,10 +4,12 @@ $LOAD_PATH << File.dirname(__FILE__)
require 'about_asserts' require 'about_asserts'
require 'about_nil' require 'about_nil'
require 'about_objects'
require 'about_arrays' 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'

31
rakelib/checks.rake Normal file
View File

@@ -0,0 +1,31 @@
namespace "check" do
desc "Check that the require files match the about_* files"
task :abouts do
about_files = Dir['src/about_*.rb'].size
about_requires = `grep require src/path_to_enlightenment.rb | wc -l`.to_i
puts "Checking path_to_enlightenment completeness"
puts "# of about files: #{about_files}"
puts "# of about requires: #{about_requires}"
if about_files > about_requires
puts "*** There seems to be requires missing in the path to enlightenment"
else
puts "OK"
end
puts
end
task :asserts do
puts "Checking for asserts missing the replacement text:"
begin
sh "egrep -n 'assert( |_)' src/about_* | egrep -v '__|_n_|project|about_assert' | egrep -v ' *#'"
puts
puts "Examine the above lines for missing __ replacements"
rescue RuntimeError => ex
puts "OK"
end
puts
end
end
task :check => ["check:abouts", "check:asserts"]

8
rakelib/run.rake Normal file
View File

@@ -0,0 +1,8 @@
RUBIES = ENV['KOAN_RUBIES'] || %w(ruby-1.8.7-p299,ruby-1.9.2-p0,jruby-1.5.2,jruby-head)
task :runall do
chdir('src') do
ENV['SIMPLE_KOAN_OUTPUT'] = 'true'
sh "rvm #{RUBIES} path_to_enlightenment.rb"
end
end

View File

@@ -10,3 +10,7 @@ task :test do
ruby 'path_to_enlightenment.rb' ruby 'path_to_enlightenment.rb'
end end
task :java do
`javac **/*.java`
end

View File

@@ -3,16 +3,16 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutArrays < EdgeCase::Koan class AboutArrays < EdgeCase::Koan
def test_creating_arrays def test_creating_arrays
empty_array = Array.new empty_array = Array.new
assert_equal Array, empty_array.class assert_equal __(Array), empty_array.class
assert_equal __(0), empty_array.size assert_equal __(0), empty_array.size
end end
def test_array_literals def test_array_literals
array = Array.new array = Array.new
assert_equal [], array assert_equal [], array # __
array[0] = 1 array[0] = 1
assert_equal [1], array assert_equal [1], array # __
array[1] = 2 array[1] = 2
assert_equal [1, __(2)], array assert_equal [1, __(2)], array
@@ -45,9 +45,9 @@ class AboutArrays < EdgeCase::Koan
end end
def test_arrays_and_ranges def test_arrays_and_ranges
assert_equal Range, (1..5).class assert_equal __(Range), (1..5).class
assert_not_equal [1,2,3,4,5], (1..5) assert_not_equal [1,2,3,4,5], (1..5) # __
assert_equal [1,2,3,4,5], (1..5).to_a assert_equal __([1,2,3,4,5]), (1..5).to_a
assert_equal __([1,2,3,4]), (1...5).to_a assert_equal __([1,2,3,4]), (1...5).to_a
end end

View File

@@ -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

View File

@@ -130,7 +130,7 @@ class AboutClasses < EdgeCase::Koan
fido = Dog6.new("Fido") fido = Dog6.new("Fido")
rover = Dog6.new("Rover") rover = Dog6.new("Rover")
assert_not_equal rover.name, fido.name assert_equal __(true), rover.name != fido.name
end end
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@@ -164,12 +164,12 @@ class AboutClasses < EdgeCase::Koan
def test_to_s_provides_a_string_version_of_the_object def test_to_s_provides_a_string_version_of_the_object
fido = Dog7.new("Fido") fido = Dog7.new("Fido")
assert_equal "Fido", fido.to_s assert_equal __("Fido"), fido.to_s
end end
def test_to_s_is_used_in_string_interpolation def test_to_s_is_used_in_string_interpolation
fido = Dog7.new("Fido") fido = Dog7.new("Fido")
assert_equal "My dog is Fido", "My dog is #{fido}" assert_equal __("My dog is Fido"), "My dog is #{fido}"
end end
def test_inspect_provides_a_more_complete_string_version def test_inspect_provides_a_more_complete_string_version

View File

@@ -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

View File

@@ -22,10 +22,10 @@ class AboutExceptions < EdgeCase::Koan
assert_equal __(:exception_handled), result assert_equal __(:exception_handled), result
assert ex.is_a?(StandardError), "Failure message." assert ex.is_a?(___(StandardError)), "Failure message."
assert ex.is_a?(RuntimeError), "Failure message." assert ex.is_a?(___(RuntimeError)), "Failure message."
assert RuntimeError.ancestors.include?(StandardError), assert RuntimeError.ancestors.include?(StandardError), # __
"RuntimeError is a subclass of StandardError" "RuntimeError is a subclass of StandardError"
assert_equal __("Oops"), ex.message assert_equal __("Oops"), ex.message
@@ -57,4 +57,12 @@ class AboutExceptions < EdgeCase::Koan
assert_equal __(:always_run), result assert_equal __(:always_run), result
end end
# Sometimes, we must know about the unknown
def test_asserting_an_error_is_raised # __
# A do-end is a block, a topic to explore more later
assert_raise(___(MySpecialError)) do
raise MySpecialError.new("New instances can be raised directly.")
end
end
end end

View File

@@ -3,8 +3,8 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutHashes < EdgeCase::Koan class AboutHashes < EdgeCase::Koan
def test_creating_hashes def test_creating_hashes
empty_hash = Hash.new empty_hash = Hash.new
assert_equal Hash, empty_hash.class assert_equal __(Hash), empty_hash.class
assert_equal({}, empty_hash) assert_equal({}, empty_hash) # __
assert_equal __(0), empty_hash.size assert_equal __(0), empty_hash.size
end end
@@ -25,7 +25,7 @@ class AboutHashes < EdgeCase::Koan
hash[:one] = "eins" hash[:one] = "eins"
expected = { :one => __("eins"), :two => "dos" } expected = { :one => __("eins"), :two => "dos" }
assert_equal expected, hash assert_equal __(true), expected == hash
# Bonus Question: Why was "expected" broken out into a variable # Bonus Question: Why was "expected" broken out into a variable
# rather than used as a literal? # rather than used as a literal?
@@ -35,7 +35,7 @@ class AboutHashes < EdgeCase::Koan
hash1 = { :one => "uno", :two => "dos" } hash1 = { :one => "uno", :two => "dos" }
hash2 = { :two => "dos", :one => "uno" } hash2 = { :two => "dos", :one => "uno" }
assert_equal hash1, hash2 assert_equal __(true), hash1 == hash2
end end
def test_hash_keys def test_hash_keys
@@ -43,7 +43,7 @@ class AboutHashes < EdgeCase::Koan
assert_equal __(2), hash.keys.size assert_equal __(2), hash.keys.size
assert_equal __(true), hash.keys.include?(:one) assert_equal __(true), hash.keys.include?(:one)
assert_equal __(true), hash.keys.include?(:two) assert_equal __(true), hash.keys.include?(:two)
assert_equal Array, hash.keys.class assert_equal __(Array), hash.keys.class
end end
def test_hash_values def test_hash_values
@@ -51,16 +51,16 @@ class AboutHashes < EdgeCase::Koan
assert_equal __(2), hash.values.size assert_equal __(2), hash.values.size
assert_equal __(true), hash.values.include?("uno") assert_equal __(true), hash.values.include?("uno")
assert_equal __(true), hash.values.include?("dos") assert_equal __(true), hash.values.include?("dos")
assert_equal Array, hash.values.class assert_equal __(Array), hash.values.class
end end
def test_combining_hashes def test_combining_hashes
hash = { "jim" => 53, "amy" => 20, "dan" => 23 } hash = { "jim" => 53, "amy" => 20, "dan" => 23 }
new_hash = hash.merge({ "jim" => 54, "jenny" => 26 }) new_hash = hash.merge({ "jim" => 54, "jenny" => 26 })
assert_not_equal hash, new_hash assert_equal __(true), hash != new_hash
expected = { "jim" => __(54), "amy" => 20, "dan" => 23, "jenny" => __(26) } expected = { "jim" => __(54), "amy" => 20, "dan" => 23, "jenny" => __(26) }
assert_equal expected, new_hash assert_equal __(true), expected == new_hash
end end
end end

View File

@@ -12,7 +12,7 @@ class AboutIteration < EdgeCase::Koan
array.each do |item| array.each do |item|
sum += item sum += item
end end
assert_equal 6, sum assert_equal __(6), sum
end end
def test_each_can_use_curly_brace_blocks_too def test_each_can_use_curly_brace_blocks_too
@@ -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

99
src/about_java_interop.rb Normal file
View File

@@ -0,0 +1,99 @@
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
include Java
# Concepts
# * Pull in a java class
# * calling a method, Camel vs snake
# * Resovling module/class name conflicts
# * Showing what gets returned
# * Ruby Strings VS Java Strings
# * Calling custom java class
# * Calling Ruby from java???
class AboutJavaInterop < EdgeCase::Koan
def test_using_a_java_library_class
java_array = java.util.ArrayList.new
assert_equal __(Java::JavaUtil::ArrayList), java_array.class
end
def test_java_class_can_be_referenced_using_both_ruby_and_java_like_syntax
assert_equal __(true), Java::JavaUtil::ArrayList == java.util.ArrayList
end
def test_include_class_includes_class_in_module_scope
assert_nil defined?(TreeSet) # __
include_class "java.util.TreeSet"
assert_equal __("constant"), defined?(TreeSet)
end
# THINK ABOUT IT:
#
# What if we use:
#
# include_class "java.lang.String"
#
# What would be the value of the String constant after this
# include_class is run? Would it be useful to provide a way of
# aliasing java classes to different names?
JString = java.lang.String
def test_also_java_class_can_be_given_ruby_aliases
java_string = JString.new("A Java String")
assert_equal __(java.lang.String), java_string.class
assert_equal __(java.lang.String), JString
end
def test_can_directly_call_java_methods_on_java_objects
java_string = JString.new("A Java String")
assert_equal __("a java string"), java_string.toLowerCase
end
def test_jruby_provides_snake_case_versions_of_java_methods
java_string = JString.new("A Java String")
assert_equal __("a java string"), java_string.to_lower_case
end
def test_jruby_provides_question_mark_versions_of_boolean_methods
java_string = JString.new("A Java String")
assert_equal __(true), java_string.endsWith("String")
assert_equal __(true), java_string.ends_with("String")
assert_equal __(true), java_string.ends_with?("String")
end
def test_java_string_are_not_ruby_strings
ruby_string = "A Java String"
java_string = java.lang.String.new(ruby_string)
assert_equal __(true), java_string.is_a?(java.lang.String)
assert_equal __(false), java_string.is_a?(String)
end
def test_java_strings_can_be_compared_to_ruby_strings_maybe
ruby_string = "A Java String"
java_string = java.lang.String.new(ruby_string)
assert_equal __(false), ruby_string == java_string
assert_equal __(true), java_string == ruby_string
# THINK ABOUT IT:
# Is there any possible way for this to be more wrong?
end
def test_however_most_methods_returning_strings_return_ruby_strings
java_array = java.util.ArrayList.new
assert_equal __("[]"), java_array.toString
assert_equal __(true), java_array.toString.is_a?(String)
assert_equal __(false), java_array.toString.is_a?(java.lang.String)
end
def test_call_custom_class
number_generator = com.edgecase.JavaStuff.new
assert_equal __(true), number_generator.random_number > 0
end
# FREAKS OUT
# def test_call_ruby_to_java_to_ruby
# number_generator = com.edgecase.JavaToRuby.new
# puts number_generator.random_number
# puts "*"*20
# end
end

View File

@@ -11,19 +11,19 @@ class AboutMessagePassing < EdgeCase::Koan
def test_methods_can_be_called_directly def test_methods_can_be_called_directly
mc = MessageCatcher.new mc = MessageCatcher.new
assert mc.caught? assert mc.caught? # __
end end
def test_methods_can_be_invoked_by_sending_the_message def test_methods_can_be_invoked_by_sending_the_message
mc = MessageCatcher.new mc = MessageCatcher.new
assert mc.send(:caught?) assert mc.send(:caught?) # __
end end
def test_methods_can_be_invoked_more_dynamically def test_methods_can_be_invoked_more_dynamically
mc = MessageCatcher.new mc = MessageCatcher.new
assert mc.send("caught?") assert mc.send("caught?") # __
assert mc.send("caught" + __("?") ) # What do you need to add to the first string? assert mc.send("caught" + __("?") ) # What do you need to add to the first string?
assert mc.send("CAUGHT?".____(:downcase) ) # What would you need to do to the string? assert mc.send("CAUGHT?".____(:downcase) ) # What would you need to do to the string?
end end
@@ -74,7 +74,7 @@ class AboutMessagePassing < EdgeCase::Koan
exception = assert_raise(___(NoMethodError)) do exception = assert_raise(___(NoMethodError)) do
typical.foobar typical.foobar
end end
assert_match(/foobar/, exception.message) assert_match(/foobar/, exception.message) # __
end end
def test_calling_method_missing_causes_the_no_method_error def test_calling_method_missing_causes_the_no_method_error
@@ -83,7 +83,7 @@ class AboutMessagePassing < EdgeCase::Koan
exception = assert_raise(___(NoMethodError)) do exception = assert_raise(___(NoMethodError)) do
typical.method_missing(:foobar) typical.method_missing(:foobar)
end end
assert_match(/foobar/, exception.message) assert_match(/foobar/, exception.message) # __
# THINK ABOUT IT: # THINK ABOUT IT:
# #
@@ -122,7 +122,7 @@ class AboutMessagePassing < EdgeCase::Koan
def test_catching_messages_makes_respond_to_lie def test_catching_messages_makes_respond_to_lie
catcher = AllMessageCatcher.new catcher = AllMessageCatcher.new
assert_nothing_raised(NoMethodError) do assert_nothing_raised(NoMethodError) do # __
catcher.any_method catcher.any_method
end end
assert_equal __(false), catcher.respond_to?(:any_method) assert_equal __(false), catcher.respond_to?(:any_method)

View File

@@ -19,10 +19,10 @@ class AboutMethods < EdgeCase::Koan
# considered to be syntactically invalid). # considered to be syntactically invalid).
def test_sometimes_missing_parentheses_are_ambiguous def test_sometimes_missing_parentheses_are_ambiguous
#-- #--
eval "assert_equal 5, my_global_method(2, 3)" # REMOVE CHECK eval "assert_equal 5, my_global_method(2, 3)" # REMOVE CHECK # __
if false if false
#++ #++
eval "assert_equal 5, my_global_method 2, 3" # ENABLE CHECK eval "assert_equal 5, my_global_method 2, 3" # ENABLE CHECK # __
#-- #--
end end
#++ #++
@@ -43,12 +43,12 @@ class AboutMethods < EdgeCase::Koan
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
# ------------------------------------------------------------------ # ------------------------------------------------------------------

View File

@@ -44,7 +44,7 @@ class AboutModules < EdgeCase::Koan
def test_module_methods_are_also_availble_in_the_object def test_module_methods_are_also_availble_in_the_object
fido = Dog.new fido = Dog.new
assert_nothing_raised(Exception) do assert_nothing_raised(Exception) do # __
fido.set_name("Rover") fido.set_name("Rover")
end end
end end

56
src/about_objects.rb Normal file
View File

@@ -0,0 +1,56 @@
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutObjects < EdgeCase::Koan
def test_everything_is_an_object
assert_equal __(true), 1.is_a?(Object)
assert_equal __(true), 1.5.is_a?(Object)
assert_equal __(true), "string".is_a?(Object)
assert_equal __(true), nil.is_a?(Object)
assert_equal __(true), Object.is_a?(Object)
end
def test_objects_can_be_converted_to_strings
assert_equal __("123"), 123.to_s
assert_equal __(""), nil.to_s
end
def test_objects_can_be_inspected
assert_equal __("123"), 123.inspect
assert_equal __("nil"), nil.inspect
end
def test_every_object_has_an_id
obj = Object.new
assert_equal __(Fixnum), obj.object_id.class
end
def test_every_object_has_different_id
obj = Object.new
another_obj = Object.new
assert_equal __(true), obj.object_id != another_obj.object_id
end
def test_some_system_objects_always_have_the_same_id
assert_equal __(0), false.object_id
assert_equal __(2), true.object_id
assert_equal __(4), nil.object_id
end
def test_small_integers_have_fixed_ids
assert_equal __(1), 0.object_id
assert_equal __(3), 1.object_id
assert_equal __(5), 2.object_id
assert_equal __(201), 100.object_id
# THINK ABOUT IT:
# What pattern do the object IDs for small integers follow?
end
def test_clone_creates_a_different_object
obj = Object.new
copy = obj.clone
assert_equal __(true), obj != copy
assert_equal __(true), obj.object_id != copy.object_id
end
end

View File

@@ -3,11 +3,11 @@ require File.expand_path(File.dirname(__FILE__) + '/edgecase')
class AboutRegularExpressions < EdgeCase::Koan class AboutRegularExpressions < EdgeCase::Koan
def test_a_pattern_is_a_regular_expression def test_a_pattern_is_a_regular_expression
assert_equal Regexp, /pattern/.class assert_equal __(Regexp), /pattern/.class
end end
def test_a_regexp_can_search_a_string_for_matching_content def test_a_regexp_can_search_a_string_for_matching_content
assert_equal "match", "some matching content"[/match/] assert_equal __("match"), "some matching content"[/match/]
end end
def test_a_failed_match_returns_nil def test_a_failed_match_returns_nil
@@ -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 (|).
# ------------------------------------------------------------------ # ------------------------------------------------------------------

View File

@@ -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)

View File

@@ -29,8 +29,8 @@ class AboutScope < EdgeCase::Koan
assert_equal __(:jims_dog), fido.identify assert_equal __(:jims_dog), fido.identify
assert_equal __(:joes_dog), rover.identify assert_equal __(:joes_dog), rover.identify
assert_not_equal fido.class, rover.class assert_equal __(true), fido.class != rover.class
assert_not_equal Jims::Dog, Joes::Dog assert_equal __(true), Jims::Dog != Joes::Dog
end end
# ------------------------------------------------------------------ # ------------------------------------------------------------------

View File

@@ -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

View File

@@ -182,4 +182,12 @@ EOS
words = ["Now", "is", "the", "time"] words = ["Now", "is", "the", "time"]
assert_equal __("Now is the time"), words.join(" ") assert_equal __("Now is the time"), words.join(" ")
end end
def test_strings_are_not_unique_objects
a = "a string"
b = "a string"
assert_equal __(true), a == b
assert_equal __(false), a.object_id == b.object_id
end
end end

100
src/about_symbols.rb Normal file
View 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_equal __(true), symbol1 == symbol2
assert_equal __(true), 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

View File

@@ -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)

View File

@@ -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 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

Binary file not shown.

View File

@@ -0,0 +1,18 @@
package com.edgecase;
import java.util.Random;
class JavaStuff{
public static final Random GENERATOR = new Random(System.currentTimeMillis());
public static final int DEFAULT_MAX = 100;
public JavaStuff(){}
public int randomNumber(){
return randomNumber(DEFAULT_MAX);
}
public int randomNumber(int max){
return GENERATOR.nextInt(max) + 1;
}
}

Binary file not shown.

View File

@@ -0,0 +1,28 @@
package com.edgecase;
import org.jruby.Ruby;
import java.lang.reflect.Method;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
class JavaToRuby{
public static final Ruby RUBY_RUNTIME = Ruby.getDefaultInstance();
public static final int DEFAULT_MAX = 100;
public JavaToRuby() {}
public Object randomNumber() throws ScriptException{
return randomNumber(DEFAULT_MAX);
}
public Object randomNumber(int max) throws ScriptException{
ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby");
/* jruby.put("message", "hello world"); */
return jruby.eval("puts 'craaaaaaaap!!!!'; rand "+max);
}
}

View File

@@ -3,13 +3,25 @@
require 'test/unit/assertions' require 'test/unit/assertions'
# --------------------------------------------------------------------
# Support code for the Ruby Koans.
# --------------------------------------------------------------------
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 end
def in_ruby_version(*versions)
yield if versions.any? { |v| ruby_version?(v) }
end
# Standard, generic replacement value.
# If value19 is given, it is used inplace of value for Ruby 1.9.
def __(value="FILL ME IN", value19=:mu) def __(value="FILL ME IN", value19=:mu)
if RUBY_VERSION < "1.9" if RUBY_VERSION < "1.9"
value value
@@ -18,6 +30,7 @@ def __(value="FILL ME IN", value19=:mu)
end end
end end
# Numeric replacement value.
def _n_(value=999999, value19=:mu) def _n_(value=999999, value19=:mu)
if RUBY_VERSION < "1.9" if RUBY_VERSION < "1.9"
value value
@@ -26,10 +39,12 @@ def _n_(value=999999, value19=:mu)
end end
end end
# Error object replacement value.
def ___(value=FillMeInError) def ___(value=FillMeInError)
value value
end end
# Method name replacement.
class Object class Object
def ____(method=nil) def ____(method=nil)
if method if method
@@ -42,7 +57,25 @@ class Object
end end
end end
class String
def side_padding(width)
extra = width - self.size
if width < 0
self
else
left_padding = extra / 2
right_padding = (extra+1)/2
(" " * left_padding) + self + (" " *right_padding)
end
end
end
module EdgeCase module EdgeCase
class << self
def simple_output
ENV['SIMPLE_KOAN_OUTPUT'] == 'true'
end
end
module Color module Color
#shamelessly stolen (and modified) from redgreen #shamelessly stolen (and modified) from redgreen
@@ -73,7 +106,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 +124,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?
@pass_count += 1 def add_progress(prog)
puts Color.green(" #{test.name} has expanded your awareness.") @_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 else
puts Color.red(" #{test.name} has damaged your karma.") @_contents = []
@failed_test = test end
@failure = test.failure 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 throw :edgecase_exit
end end
end end
@@ -113,34 +173,142 @@ 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?
#puts find_interesting_lines(failure.backtrace)
puts find_interesting_lines(failure.backtrace).collect {|l| Color.red(l) }
else 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
if EdgeCase.simple_output
boring_end_screen
else
artistic_end_screen
end
end
def boring_end_screen
puts "Mountains are again merely mountains"
end
def artistic_end_screen
"JRuby 1.9.x Koans"
ruby_version = "(in #{'J' if defined?(JRUBY_VERSION)}Ruby #{defined?(JRUBY_VERSION) ? JRUBY_VERSION : RUBY_VERSION})"
ruby_version = ruby_version.side_padding(54)
completed = <<-ENDTEXT
,, , ,,
: ::::, :::,
, ,,: :::::::::::::,, :::: : ,
, ,,, ,:::::::::::::::::::, ,: ,: ,,
:, ::, , , :, ,::::::::::::::::::, ::: ,::::
: : ::, ,:::::::: ::, ,::::
, ,::::: :,:::::::,::::,
,: , ,:,,: :::::::::::::
::,: ,,:::, ,::::::::::::,
,:::, :,,::: ::::::::::::,
,::: :::::::, Mountains are again merely mountains ,::::::::::::
:::,,,:::::: ::::::::::::
,:::::::::::, ::::::::::::,
:::::::::::, ,::::::::::::
::::::::::::: ,::::::::::::
:::::::::::: Ruby Koans ::::::::::::,
::::::::::::#{ ruby_version },::::::::::::,
:::::::::::, , ::::::::::::
,:::::::::::::, 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 end
puts puts
end 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 end
def find_interesting_lines(backtrace) def find_interesting_lines(backtrace)
backtrace.reject { |line| backtrace.reject { |line|
line =~ /test\/unit\/|edgecase\.rb/ line =~ /test\/unit\/|edgecase\.rb|minitest/
} }
end end
# 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 +327,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 +358,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 +381,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 +423,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
} }

View File

@@ -4,10 +4,12 @@ $LOAD_PATH << File.dirname(__FILE__)
require 'about_asserts' require 'about_asserts'
require 'about_nil' require 'about_nil'
require 'about_objects'
require 'about_arrays' 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'

5
src/try_java_to_ruby.rb Normal file
View File

@@ -0,0 +1,5 @@
include Java
number_generator = com.edgecase.JavaToRuby.new
puts number_generator.random_number
puts "*"*20