moving to a new repo

This commit is contained in:
Joe O'Brien
2009-01-26 11:56:38 -05:00
parent 55fd4afa0f
commit 4e78262965
37 changed files with 2693 additions and 0 deletions

66
GREED_RULES.txt Normal file
View File

@@ -0,0 +1,66 @@
= Playing Greed
Greed is a dice game played amoung 2 or more players, using 5
six-sided dice.
== Playing Greed
Each player takes a turn consisting of one or more rolls of the dice.
On the first roll of the game, a player rolls all six dice which are
scored according to the following:
Three 1's => 1000 points
Three 6's => 600 points
Three 5's => 500 points
Three 4's => 400 points
Three 3's => 300 points
Three 2's => 200 points
One 1 => 100 points
One 5 => 50 points
A single die can only be counted once in each roll. For example,
a "5" can only count as part of a triplet (contributing to the 500
points) or as a single 50 points, but not both in the same roll.
Example Scoring
Throw Score
--------- ------------------
5 1 3 4 1 50 + 2 * 100 = 250
1 1 1 3 1 1000 + 100 = 1100
2 4 4 5 4 400 + 50 = 450
The dice not contributing to the score are called the non-scoring
dice. "3" and "4" are non-scoring dice in the first example. "3" is
a non-scoring die in the second, and "2" is a non-score die in the
final example.
After a player rolls and the score is calculated, the scoring dice are
removed and the player has the option of rolling again using only the
non-scoring dice. If there all no non-scoring dice), then the player
may roll all 5 dice in the next roll.
The player may continue to roll as long as each roll scores points. If
a roll has zero points, then the player loses not only their turn, but
also accumulated score for that turn. If a player decides to stop
rolling before rolling a zero-point roll, then the accumulated points
for the turn is added to his total score.
== Getting "In The Game"
Before a player is allowed to accumulate points, they must get at
least 300 points in a single turn. Once they have achieved 300 points
in a single turn, the points earned in that turn and each following
turn will be counted toward their total score.
== End Game
Once a player reaches 3000 (or more) points, the game enters the final
round where each of the other players gets one more turn. The winner
is the player with the highest score after the final round.
== References
Greed is described on Wikipedia at
http://en.wikipedia.org/wiki/Greed_(dice_game), however the rules are
a bit different from the rules given here.

38
README
View File

@@ -0,0 +1,38 @@
= EdgeCase Ruby Koans
The Ruby Koans walk you along the path to enlightenment in order to learn Ruby.
The goal is to learn the Ruby language, syntax, structure, and some common
functions and libraries. We also teach you culture. Testing is not just something we
pay lip service to, but something we live. It is essential in your quest to learn
and do great things in the language.
== The Structure
The koans are broken out into areas by file, hashes are covered in about_hashes.rb,
modules are introduced in about_modules.rb, etc. They are presented in order in the
path_to_enlightenment.rb file.
Each koan builds up your knowledge of Ruby and builds upon itself. It will stop at
the first place you need to correct.
Some koans simply need to have the correct answer substituted for an incorrect one.
Some, however, require you to supply your own answer. If you see the method __ (a
double underscore) listed, it is a hint to you to supply your own code in order to
make it work correctly.
== The Path To Enlightenment
In order to achieve enlightenment you need to follow the path_to_enlightenment. This
can be done in two ways
*nix platforms, from the koans directory
[koans] $ rake # runs the default target :walk_the_path
[koans] $ ruby path_to_enlightenment.rb # simply call the file directly
Windows is the same thing
c:\dev\koans\rake # runs the default target :walk_the_path
c:\dev\koans\ruby path_to_enlightenment.rb # simply call the file directly

6
Rakefile.rb Normal file
View File

@@ -0,0 +1,6 @@
task :default => :walk_the_path
task :walk_the_path do
ruby 'path_to_enlightenment.rb'
end

38
about_array_assignment.rb Normal file
View File

@@ -0,0 +1,38 @@
require 'edgecase'
class AboutArrayAssignment < EdgeCase::Koan
def test_non_parallel_assignment
names = ["John", "Smith"]
assert_equal __, names
end
def test_parallel_assignments
first_name, last_name = ["John", "Smith"]
assert_equal __, first_name
assert_equal __, last_name
end
def test_parallel_assignments_with_extra_values
first_name, last_name = ["John", "Smith", "III"]
assert_equal __, first_name
assert_equal __, last_name
end
def test_parallel_assignments_with_extra_variables
first_name, last_name = ["Cher"]
assert_equal __, first_name
assert_equal __, last_name
end
def test_parallel_assignements_with_subarrays
first_name, last_name = [["Willie", "Rae"], "Johnson"]
assert_equal __, first_name
assert_equal __, last_name
end
def test_parallel_assignment_with_one_variable
first_name, = ["John", "Smith"]
assert_equal __, first_name
end
end

101
about_arrays.rb Normal file
View File

@@ -0,0 +1,101 @@
require 'edgecase'
class AboutArrays < EdgeCase::Koan
def test_creating_arrays
empty_array = Array.new
assert_equal Array, empty_array.class
assert_equal __, empty_array.size
end
def test_array_literals
array = Array.new
assert_equal [], array
array[0] = 1
assert_equal [1], array
array[1] = 2
assert_equal [1, __], array
array << 333
assert_equal __, array
end
def test_accessing_array_elements
array = [:peanut, :butter, :and, :jelly]
assert_equal __, array[0]
assert_equal __, array.first
assert_equal __, array[3]
assert_equal __, array.last
assert_equal __, array[-1]
assert_equal __, array[-3]
end
def test_slicing_arrays
array = [:peanut, :butter, :and, :jelly]
assert_equal __, array[0,1]
assert_equal __, array[0,2]
assert_equal __, array[2,2]
assert_equal __, array[2,20]
assert_equal __, array[4,0]
assert_equal __, array[5,0]
end
def test_arrays_and_ranges
assert_equal Range, (1..5).class
assert_not_equal [1,2,3,4,5], (1..5)
assert_equal [1,2,3,4,5], (1..5).to_a
assert_equal __, (1...5).to_a
end
def test_slicing_with_ranges
array = [:peanut, :butter, :and, :jelly]
assert_equal __, array[0..2]
assert_equal __, array[0...2]
assert_equal __, array[2..-1]
end
def test_pushing_and_popping_arrays
array = [1,2]
array.push(:last)
assert_equal __, array
popped_value = array.pop
assert_equal __, popped_value
assert_equal __, array
end
def test_shifting_arrays
array = [1,2]
array.unshift(:first)
assert_equal __, array
shifted_value = array.shift
assert_equal __, shifted_value
assert_equal __, array
end
def test_parallel_assignments
first_name, last_name = ["John", "Smith"]
assert_equal __, first_name
assert_equal __, last_name
end
def test_parallel_assignments_with_extra_values
first_name, last_name = ["John", "Smith", "III"]
assert_equal __, first_name
assert_equal __, last_name
end
def test_parallel_assignments_with_extra_variables
first_name, last_name = ["Cher"]
assert_equal __, first_name
assert_equal __, last_name
end
end

40
about_basics.rb Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env ruby
# -*- ruby -*-
require 'edgecase'
class AboutAsserts < EdgeCase::Koan
# We shall contemplate truth by testing reality, via asserts.
def test_assert_truth
assert false # This should be true
end
# Enlightenment may be more easily achieved with appropriate
# messages.
def test_assert_with_message
assert false, "This should be true -- Please fix this"
end
# To understand reality, we must compare our expectations against
# reality.
def test_assert_equality
expected_value = 3
actual_value = 1 + 1
assert expected_value == actual_value
end
# Some ways of asserting equality are better than others.
def test_a_better_way_of_asserting_equality
expected_value = 3
actual_value = 1 + 1
assert_equal expected_value, actual_value
end
# Sometimes we will ask you to fill in the values
def test_fill_in_values
assert_equal __, 1 + 1
end
end

96
about_blocks.rb Normal file
View File

@@ -0,0 +1,96 @@
require 'edgecase'
class AboutBlocks < EdgeCase::Koan
def method_with_block
result = yield
result
end
def test_methods_can_take_blocks
yielded_result = method_with_block { 1 + 2 }
assert_equal __, yielded_result
end
def test_blocks_can_be_defined_with_do_end_too
yielded_result = method_with_block do 1 + 2 end
assert_equal __, yielded_result
end
# ------------------------------------------------------------------
def method_with_block_arguments
yield("Jim")
end
def test_blocks_can_take_arguments
result = method_with_block_arguments do |argument|
assert_equal __, argument
end
end
# ------------------------------------------------------------------
def many_yields
yield(:peanut)
yield(:butter)
yield(:and)
yield(:jelly)
end
def test_methods_can_call_yield_many_times
result = []
many_yields { |item| result << item }
assert_equal __, result
end
# ------------------------------------------------------------------
def yield_tester
if block_given?
yield
else
:no_block
end
end
def test_methods_can_see_if_they_have_been_called_with_a_block
assert_equal __, yield_tester { :with_block }
assert_equal __, yield_tester
end
# ------------------------------------------------------------------
def test_block_can_effect_variables_in_the_code_where_they_are_created
value = :initial_value
method_with_block { value = :modified_in_a_block }
assert_equal __, value
end
def test_blocks_can_be_assigned_to_variables_and_called_explicitly
add_one = lambda { |n| n + 1 }
assert_equal __, add_one.call(10)
# Alternative calling sequence
assert_equal __, add_one[10]
end
def test_stand_alone_blocks_can_be_passed_to_methods_expecting_blocks
make_upper = lambda { |n| n.upcase }
result = method_with_block_arguments(&make_upper)
assert_equal __, result
end
# ------------------------------------------------------------------
def method_with_explict_block(&block)
block.call(10)
end
def test_methods_can_take_an_explicit_block_argument
assert_equal __, method_with_explict_block { |n| n * 2 }
add_one = lambda { |n| n + 1 }
assert_equal __, method_with_explict_block(&add_one)
end
end

170
about_class_methods.rb Normal file
View File

@@ -0,0 +1,170 @@
require 'edgecase'
class AboutClassMethods < EdgeCase::Koan
class Dog
end
def test_objects_are_objects
fido = Dog.new
assert_equal __, fido.is_a?(Object)
end
def test_classes_are_objects_too
assert_equal __, Dog.is_a?(Class)
end
def test_classes_are_objects_too
assert_equal __, Dog.is_a?(Object)
end
def test_objects_have_methods
fido = Dog.new
assert_equal __, fido.methods.size
end
def test_classes_have_methods
assert_equal __, Dog.methods.size
end
def test_you_can_define_methods_on_individual_objects
fido = Dog.new
def fido.wag
:fidos_wag
end
assert_equal __, fido.wag
end
def test_other_objects_are_affected_by_these_singleton_methods
fido = Dog.new
rover = Dog.new
def fido.wag
:fidos_wag
end
assert_raise(___) do
rover.wag
end
end
# ------------------------------------------------------------------
def Dog.wag
:class_level_wag
end
class Dog
def wag
:instance_level_wag
end
end
def test_since_classes_are_objects_you_can_define_singleton_methods_on_them_too
assert_equal __, Dog.a_class_method
end
def test_class_methods_are_independent_of_instance_methods
fido = Dog.new
assert_equal __, fido.wag
assert_equal __, Dog.wag
end
# ------------------------------------------------------------------
class Dog
attr_accessor :name
end
def Dog.name
@name
end
def test_classes_and_instances_do_not_share_instance_variables
fido = Dog.new
fido.name = "Fido"
assert_equal __, fido.name
assert_equal __, Dog.name
end
# ------------------------------------------------------------------
class Dog
def Dog.a_class_method
:dogs_class_method
end
end
def test_you_can_define_class_methods_inside_the_class
assert_equal __, Dog.a_class_method
end
# ------------------------------------------------------------------
LastExpressionInClassStatement = class Dog
21
end
def test_class_statements_return_the_value_of_their_last_expression
assert_equal __, LastExpressionInClassStatement
end
# ------------------------------------------------------------------
SelfInsideOfClassStatement = class Dog
self
end
def test_self_while_inside_class_is_class_object_not_instance
assert_equal __, Dog == SelfInsideOfClassStatement
end
# ------------------------------------------------------------------
class Dog
def self.class_method2
:another_way_to_write_class_methods
end
end
def test_you_can_use_self_instead_of_an_explicit_reference_to_dog
assert_equal __, Dog.class_method2
end
# ------------------------------------------------------------------
class Dog
class << self
def another_class_method
:still_another_way
end
end
end
def test_heres_still_another_way_to_write_class_methods
assert_equal __, Dog.another_class_method
end
# THINK ABOUT IT:
#
# The two major ways to write class methods are:
# class Demo
# def self.method
# end
#
# class << self
# def class_methods
# end
# end
# end
#
# Which do you prefer and why?
# Are there times you might prefer one over the other?
# ------------------------------------------------------------------
def test_heres_an_easy_way_to_call_class_methods_from_instance_methods
fido = Dog.new
assert_equal __, fido.class.another_class_method
end
end

190
about_classes.rb Normal file
View File

@@ -0,0 +1,190 @@
require 'edgecase'
class AboutClasses < EdgeCase::Koan
class Dog
end
def test_instances_of_classes_can_be_created_with_new
fido = Dog.new
assert_equal __, fido.class
end
# ------------------------------------------------------------------
class Dog2
def set_name(a_name)
@name = a_name
end
end
def test_instance_variables_can_be_set_by_assigning_to_them
fido = Dog2.new
assert_equal __, fido.instance_variables
fido.set_name("Fido")
assert_equal __, fido.instance_variables
end
def test_instance_variables_cannot_be_accessed_outside_the_class
fido = Dog2.new
fido.set_name("Fido")
assert_raise(___) do
fido.name
end
assert_raise(___) do
eval "fido.@name"
# NOTE: Using eval because the above line is a syntax error.
end
end
def test_you_can_politely_ask_for_instance_variable_values
fido = Dog2.new
fido.set_name("Fido")
assert_equal __, fido.instance_variable_get("@name")
end
def test_you_can_rip_the_value_out_using_instance_eval
fido = Dog2.new
fido.set_name("Fido")
assert_equal __, fido.instance_eval("@name") # string version
assert_equal __, fido.instance_eval { @name } # block version
end
# ------------------------------------------------------------------
class Dog3
def set_name(a_name)
@name = a_name
end
def name
@name
end
end
def test_you_can_create_accessor_methods_to_return_instance_variables
fido = Dog3.new
fido.set_name("Fido")
assert_equal __, fido.name
end
# ------------------------------------------------------------------
class Dog4
attr_reader :name
def set_name(a_name)
@name = a_name
end
end
def test_attr_reader_will_automatically_define_an_accessor
fido = Dog4.new
fido.set_name("Fido")
assert_equal __, fido.name
end
# ------------------------------------------------------------------
class Dog5
attr_accessor :name
end
def test_attr_accessor_will_automatically_define_both_read_and_write_accessors
fido = Dog5.new
fido.name = "Fido"
assert_equal __, fido.name
end
# ------------------------------------------------------------------
class Dog6
attr_reader :name
def initialize(initial_name)
@name = initial_name
end
end
def test_initialize_provides_initial_values_for_instance_variables
fido = Dog6.new("Fido")
assert_equal __, fido.name
end
def test_args_to_new_must_match_initialize
assert_raise(___) do
Dog6.new
end
# THINK ABOUT IT:
# Why is this so?
end
def test_different_objects_have_difference_instance_variables
fido = Dog6.new("Fido")
rover = Dog6.new("Rover")
assert_not_equal rover.name, fido.name
end
# ------------------------------------------------------------------
class Dog7
attr_reader :name
def initialize(initial_name)
@name = initial_name
end
def get_self
self
end
def to_s
__
end
def inspect
"<Dog named '#{name}'>"
end
end
def test_inside_a_method_self_refers_to_the_containing_object
fido = Dog7.new("Fido")
fidos_self = fido.get_self
assert_equal __, fidos_self
end
def test_to_s_provides_a_string_version_of_the_object
fido = Dog7.new("Fido")
assert_equal "Fido", fido.to_s
end
def test_to_s_is_used_in_string_interpolation
fido = Dog7.new("Fido")
assert_equal "My dog is Fido", "My dog is #{fido}"
end
def test_inspect_provides_a_more_complete_string_version
fido = Dog7.new("Fido")
assert_equal __, fido.inspect
end
def test_all_objects_support_to_s_and_inspect
array = [1,2,3]
assert_equal __, array.to_s
assert_equal __, array.inspect
assert_equal __, "STRING".to_s
assert_equal __, "STRING".inspect
end
end

116
about_control_statements.rb Normal file
View File

@@ -0,0 +1,116 @@
require 'edgecase'
class AboutControlStatements < EdgeCase::Koan
def test_if_then_else_statements
if true
result = :true_value
else
result = :false_value
end
assert_equal __, result
end
def test_if_then_else_statements
result = :default_value
if true
result = :true_value
end
assert_equal __, result
end
def test_if_statements_return_values
value = if true
:true_value
else
:false_value
end
assert_equal __, value
value = if false
:true_value
else
:false_value
end
assert_equal __, value
# NOTE: Actually, EVERY statement in Ruby will return a value, not
# just if statements.
end
def test_if_statements_with_no_else_with_false_condition_return_value
value = if false
:true_value
end
assert_equal __, value
end
def test_condition_operators
assert_equal __, (true ? :true_value : :false_value)
assert_equal __, (false ? :true_value : :false_value)
end
def test_if_statement_modifiers
result = :default_value
result = :true_value if true
assert_equal __, result
end
def test_unless_statement
result = :default_value
unless false
result = :false_value
end
assert_equal __, result
end
def test_unless_statement_modifier
result = :default_value
result = :false_value unless false
assert_equal __, result
end
def test_while_statement
i = 1
result = 1
while i <= 10
result = result * i
i += 1
end
assert_equal __, result
end
def test_break_statement
i = 1
result = 1
while true
break unless i <= 10
result = result * i
i += 1
end
assert_equal __, result
end
def test_next_statement
i = 0
result = []
while i < 10
i += 1
next if (i % 2) == 0
result << i
end
assert_equal __, result
end
def test_for_statement
array = ["fish", "and", "chips"]
result = []
for item in array
result << item.upcase
end
assert_equal [__, __, __], result
end
end

64
about_dice_project.rb Normal file
View File

@@ -0,0 +1,64 @@
require 'edgecase'
class DiceSet
attr_reader :values
def roll(n)
@values = (1..n).map { rand(6) + 1 }
end
end
class AboutDiceSet < EdgeCase::Koan
def test_can_create_a_dice_set
dice = DiceSet.new
assert_not_nil dice
end
def test_rolling_the_dice_returns_a_set_of_integers_between_1_and_6
dice = DiceSet.new
dice.roll(5)
assert dice.values.is_a?(Array), "should be an array"
assert_equal 5, dice.values.size
dice.values.each do |value|
assert value >= 1 && value <= 6, "value #{value} must be between 1 and 6"
end
end
def test_dice_values_do_not_change_unless_explicitly_rolled
dice = DiceSet.new
dice.roll(5)
first_time = dice.values
second_time = dice.values
assert_equal first_time, second_time
end
def test_dice_values_should_change_between_rolls
dice = DiceSet.new
dice.roll(5)
first_time = dice.values
dice.roll(5)
second_time = dice.values
assert_not_equal first_time, second_time,
"Two rolls should not be equal"
# THINK ABOUT IT:
#
# If the rolls are random, then it is possible (although not
# likely) that two consecutive rolls are equal. What would be a
# better way to test this.
end
def test_you_can_roll_different_numbers_of_dice
dice = DiceSet.new
dice.roll(3)
assert_equal 3, dice.values.size
dice.roll(1)
assert_equal 1, dice.values.size
end
end

60
about_exceptions.rb Normal file
View File

@@ -0,0 +1,60 @@
require 'edgecase'
class AboutExceptions < EdgeCase::Koan
class MySpecialError < RuntimeError
end
def test_exceptions_inherit_from_Exception
assert MySpecialError.ancestors.include?(RuntimeError)
assert MySpecialError.ancestors.include?(StandardError)
assert MySpecialError.ancestors.include?(Exception)
assert MySpecialError.ancestors.include?(Object)
end
def test_rescue_clause
result = nil
begin
fail "Oops"
rescue StandardError => ex
result = :exception_handled
end
assert_equal __, result
assert ex.is_a?(StandardError), "Failure message."
assert ex.is_a?(RuntimeError), "Failure message."
assert RuntimeError.ancestors.include?(StandardError),
"RuntimeError is a subclass of StandardError"
assert_equal __, ex.message
end
def test_raising_a_particular_error
result = nil
begin
# 'raise' and 'fail' are synonyms
raise MySpecialError, "My Message"
rescue MySpecialError => ex
result = :exception_handled
end
assert_equal __(:exception_handled), result
assert_equal __, ex.message
end
def test_ensure_clause
result = nil
begin
fail "Oops"
rescue StandardError => ex
# no code here
ensure
result = :always_run
end
assert_equal __, result
end
end

8
about_extra_credit.rb Normal file
View File

@@ -0,0 +1,8 @@
# EXTRA CREDIT:
#
# Create a program that will play the Greed Game.
# Rules for the game are in GREED_RULES.TXT.
#
# You already have a DiceSet class and score function you can use.
# Write a player class and a Game class to complete the project. This
# is a free form assignment, so approach it however you desire.

56
about_hashes.rb Normal file
View File

@@ -0,0 +1,56 @@
require 'edgecase'
class AboutHashes < EdgeCase::Koan
def test_creating_hashes
empty_hash = Hash.new
assert_equal Hash, empty_hash.class
assert_equal({}, empty_hash)
assert_equal __, empty_hash.size
end
def test_hash_literals
hash = { :one => "uno", :two => "dos" }
assert_equal __, hash.size
end
def test_accessing_hashes
hash = { :one => "uno", :two => "dos" }
assert_equal __, hash[:one]
assert_equal __, hash[:two]
assert_equal __, hash[:doesnt_exist]
end
def test_changing_hashes
hash = { :one => "uno", :two => "dos" }
hash[:one] = "eins"
expected = { :one => __, :two => "dos" }
assert_equal expected, hash
# Bonus Question: Why was "expected" broken out into a variable
# rather than used as a literal?
end
def test_hash_is_unordered
hash1 = { :one => "uno", :two => "dos" }
hash2 = { :two => "dos", :one => "uno" }
assert_equal hash1, hash2
end
def test_hash_keys_and_values
hash = { :one => "uno", :two => "dos" }
assert_equal __, hash.keys
assert_equal __, hash.values
end
def test_combining_hashes
hash = { "jim" => 53, "amy" => 20, "dan" => 23 }
new_hash = hash.merge({ "jim" => 54, "jenny" => 26 })
assert_not_equal hash, new_hash
expected = { "jim" => __, "amy" => 20, "dan" => 23, "jenny" => __ }
assert_equal expected, new_hash
end
end

94
about_inheritance.rb Normal file
View File

@@ -0,0 +1,94 @@
require 'edgecase'
class AboutInheritance < EdgeCase::Koan
class Dog
attr_reader :name
def initialize(name)
@name = name
end
def bark
"WOOF"
end
end
class Chihuahua < Dog
def wag
:happy
end
def bark
"yip"
end
end
def test_subclasses_have_the_parent_as_an_ancestor
assert_equal __, Chihuahua.ancestors.include?(Dog)
end
def test_all_classes_ultimately_inherit_from_object
assert_equal __, Chihuahua.ancestors.include?(Object)
end
def test_subcases_inherit_behavior_from_parent_class
chico = Chihuahua.new("Chico")
assert_equal __, chico.name
end
def test_subclasses_add_new_behavior
chico = Chihuahua.new("Chico")
assert_equal __, chico.wag
assert_raise(___) do
fido = Dog.new("Fido")
fido.wag
end
end
def test_subclasses_can_modify_existing_behavior
chico = Chihuahua.new("Chico")
assert_equal __, chico.bark
fido = Dog.new("Fido")
assert_equal __, fido.bark
end
# ------------------------------------------------------------------
class BullDog < Dog
def bark
super + ", GROWL"
end
def growl
super.bark + ", GROWL"
end
end
def test_subclasses_can_invoke_parent_behavior_via_super
ralph = BullDog.new("Ralph")
assert_equal __, ralph.bark
end
def test_super_does_not_work_cross_method
ralph = BullDog.new("Ralph")
end
# ------------------------------------------------------------------
class GreatDane < Dog
def growl
super.bark + ", GROWL"
end
end
def test_super_does_not_work_cross_method
george = GreatDane.new("George")
assert_raise(___) do
george.growl
end
end
end

93
about_iteration.rb Normal file
View File

@@ -0,0 +1,93 @@
require 'edgecase'
class AboutIteration < EdgeCase::Koan
def test_each_is_a_method_on_arrays
[].methods.include?("each")
end
def test_iterating_with_each
array = [1, 2, 3]
sum = 0
array.each do |item|
sum += item
end
assert_equal 6, sum
end
def test_each_can_use_curly_brace_blocks_too
array = [1, 2, 3]
sum = 0
array.each { |item|
sum += item
}
assert_equal __, sum
end
def test_break_works_with_each_style_iterations
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sum = 0
array.each { |item|
break if item > 3
sum += item
}
assert_equal __, sum
end
def test_collect_transforms_elements_of_an_array
array = [1, 2, 3]
new_array = array.collect { |item| item + 10 }
assert_equal __, new_array
# NOTE: 'map' is another name for the 'collect' operation
another_array = array.map { |item| item + 10 }
assert_equal __, another_array
end
def test_select_selects_certain_items_from_an_array
array = [1, 2, 3, 4, 5, 6]
even_numbers = array.select { |item| (item % 2) == 0 }
assert_equal __, even_numbers
# NOTE: 'find_all' is another name for the 'select' operation
more_even_numbers = array.find_all { |item| (item % 2) == 0 }
assert_equal __, more_even_numbers
end
def test_find_locates_the_first_element_matching_a_criteria
array = ["Jim", "Bill", "Clarence", "Doug", "Eli"]
assert_equal __, array.find { |item| item.size > 4 }
end
def test_inject_will_blow_your_mind
result = [2, 3, 4].inject(0) { |sum, item| sum + item }
assert_equal __, result
result2 = [2, 3, 4].inject(1) { |sum, item| sum * item }
assert_equal __, result2
# Extra Credit:
# Describe in your own words what inject does.
end
def test_all_iteration_methods_work_on_any_collection_not_just_arrays
# Ranges act like a collection
result = (1..3).map { |item| item + 10 }
assert_equal __, result
# Files act like a collection of lines
file = File.open("example_file.txt")
upcase_lines = file.map { |line| line.strip.upcase }
assert_equal __, upcase_lines
# NOTE: You can create your own collections that work with each,
# map, select, etc.
ensure
# Arg, this is ugly.
# We will figure out how to fix this later.
file.close if file
end
end

167
about_message_passing.rb Normal file
View File

@@ -0,0 +1,167 @@
require 'edgecase'
class AboutMessagePassing < EdgeCase::Koan
class MessageCatcher
def caught?
true
end
end
def test_methods_can_be_called_directly
mc = MessageCatcher.new
assert mc.caught?
end
def test_methods_can_be_invoked_by_sending_the_message
mc = MessageCatcher.new
assert mc.send(:caught?)
end
def test_methods_can_be_invoked_more_dynamically
mc = MessageCatcher.new
assert mc.send("caught?")
assert mc.send("caught" + __ ) # What do you need to add to the first string?
assert mc.send("CAUGHT?".__ ) # What would you need to do to the string?
end
def test_send_with_underscores_will_also_send_messages
mc = MessageCatcher.new
assert_equal __, mc.__send__(:caught?)
# THINK ABOUT IT:
#
# Why does Ruby provide both send and __send__ ?
end
def test_classes_can_be_asked_if_they_know_how_to_respond
mc = MessageCatcher.new
assert_equal __, mc.respond_to?(:caught?)
assert_equal __, mc.respond_to?(:does_not_exist)
end
# ------------------------------------------------------------------
class MessageCatcher
def add_a_payload(*args)
return :empty unless args
args
end
end
def test_sending_a_message_with_arguments
mc = MessageCatcher.new
assert_equal __, mc.add_a_payload
assert_equal __, mc.send(:add_a_payload)
assert_equal __, mc.add_a_payload(3, 4, nil, 6)
assert_equal __, mc.send(:add_a_payload, 3, 4, nil, 6)
end
# ------------------------------------------------------------------
class TypicalObject
end
def test_sending_undefined_messages_to_a_typical_object_results_in_errors
typical = TypicalObject.new
assert_raise(___) do
typical.foobar
end
assert_match(/foobar/, exception.message)
end
def test_calling_method_missing_causes_the_no_method_error
typical = TypicalObject.new
exception = assert_raise(___) do
typical.method_missing(:foobar)
end
assert_match(/foobar/, exception.message)
# THINK ABOUT IT:
#
# If the method :method_missing causes the NoMethodError, then
# what would happen if we redefine method_missing?
end
# ------------------------------------------------------------------
class AllMessageCatcher
def method_missing(method_name, *args, &block)
"Someone called #{method_name} with (#{args.join(", ")})"
end
end
def test_all_messages_are_caught
catcher = AllMessageCatcher.new
assert_equal __, catcher.foobar
assert_equal __, catcher.foobaz(1)
assert_equal __, catcher.sum(1,2,3,4,5,6)
end
def test_catching_messages_makes_respond_to_lie
catcher = AllMessageCatcher.new
assert_nothing_raised(NoMethodError) do
catcher.any_method
end
assert_equal __, catcher.respond_to?(:any_method)
end
# ------------------------------------------------------------------
class WellBehavedFooCatcher
def method_missing(method_name, *args, &block)
if method_name.to_s[0,3] == "foo"
"Foo to you too"
else
super(method_name, *args, &block)
end
end
end
def test_foo_method_are_caught
catcher = WellBehavedFooCatcher.new
assert_equal __, catcher.foo_bar
assert_equal __, catcher.foo_baz
end
def test_non_foo_messages_are_treated_normally
catcher = WellBehavedFooCatcher.new
assert_raise(___) do
catcher.normal_undefined_method
end
end
# ------------------------------------------------------------------
# (note: just reopening class from above)
class WellBehavedFooCatcher
def respond_to?(method_name)
if method_name.to_s[0,3] == "foo"
true
else
super(method_name)
end
end
end
def test_explicitly_implementing_respond_to_lets_objects_tell_the_truth
catcher = WellBehavedFooCatcher.new
assert_equal __, catcher.respond_to?(:foo_bar)
assert_equal __, catcher.respond_to?(:something_else)
end
end

150
about_methods.rb Normal file
View File

@@ -0,0 +1,150 @@
require 'edgecase'
def my_global_method(a,b)
a + b
end
class AboutMethods < EdgeCase::Koan
def test_calling_global_methods
assert_equal __, my_global_method(2,3)
end
def test_calling_global_methods_without_parenthesis
result = my_global_method 2, 3
assert_equal __, result
end
# (NOTE: We are Using eval below because the example code is
# considered to be syntactically invalid).
def test_sometimes_missing_parenthesis_are_ambiguous
eval "assert_equal 5, my_global_method 2, 3"
#
# Ruby doesn't know if you mean:
#
# assert_equal(5, my_global_method(2), 3)
# or
# assert_equal(5, my_global_method(2, 3))
#
# Rewrite the eval string to continue.
#
end
# NOTE: wrong number of argument is not a SYNTAX error, but a
# runtime error.
def test_calling_global_methods_with_wrong_number_of_arguments
exception = assert_raise(___) do
my_global_method
end
assert_equal __, exception.message
exception = assert_raise(___) do
my_global_method(1,2,3)
end
assert_equal __, exception.message
end
# ------------------------------------------------------------------
def method_with_defaults(a, b=:default_value)
[a, b]
end
def test_calling_with_default_values
assert_equal [1, __], method_with_defaults(1)
assert_equal [1, __], method_with_defaults(1, 2)
end
# ------------------------------------------------------------------
def method_with_var_args(*args)
args
end
def test_calling_with_variable_arguments
assert_equal __, method_with_var_args
assert_equal __, method_with_var_args(:one)
assert_equal __, method_with_var_args(:one, :two)
end
# ------------------------------------------------------------------
def method_with_explicit_return
:a_non_return_value
return :return_value
:anoher_non_return_value
end
def test_method_with_explicit_return
assert_equal __, method_with_explicit_return
end
# ------------------------------------------------------------------
def method_without_explicit_return
:a_non_return_value
:return_value
end
def test_method_without_explicit_return
assert_equal __, method_without_explicit_return
end
# ------------------------------------------------------------------
def my_same_class_method(a, b)
a * b
end
def test_calling_methods_in_same_class
assert_equal __, my_same_class_method(3,4)
end
def test_calling_methods_in_same_class_with_explicit_receiver
assert_equal __, self.my_same_class_method(3,4)
end
# ------------------------------------------------------------------
def my_private_method
"a secret"
end
private :my_private_method
def test_calling_private_methods_without_receiver
assert_equal __, my_private_method
end
def test_calling_private_methods_with_an_explicit_receiver
exception = assert_raise(___) do
self.my_private_method
end
assert_match /__/, exception.message
end
# ------------------------------------------------------------------
class Dog
def name
"Fido"
end
private
def tail
"tail"
end
end
def test_calling_methods_in_other_objects_require_explicit_receiver
rover = Dog.new
assert_equal __, rover.name
end
def test_calling_private_methods_in_other_objects
rover = Dog.new
assert_raise(___) do
rover.tail
end
end
end

63
about_modules.rb Normal file
View File

@@ -0,0 +1,63 @@
require 'edgecase'
class AboutModules < EdgeCase::Koan
module Nameable
def set_name(new_name)
@name = new_name
end
def here
:in_module
end
end
def test_cant_instantiate_modules
assert_raise(___) do
Nameable.new
end
end
# ------------------------------------------------------------------
class Dog
include Nameable
attr_reader :name
def initialize
@name = "Fido"
end
def bark
"WOOF"
end
def here
:in_object
end
end
def test_normal_methods_are_available_in_the_object
fido = Dog.new
assert_equal __, fido.bark
end
def test_module_methods_are_also_availble_in_the_object
fido = Dog.new
assert_nothing_raised(Exception) do
fido.set_name("Rover")
end
end
def test_module_methods_can_affect_instance_variables_in_the_object
fido = Dog.new
assert_equal __, fido.name
fido.set_name("Rover")
assert_equal __, fido.name
end
def test_classes_can_override_module_methods
fido = Dog.new
assert_equal __, fido.here
end
end

46
about_nil.rb Normal file
View File

@@ -0,0 +1,46 @@
require 'edgecase'
class AboutNil < EdgeCase::Koan
def test_nil_is_an_object
assert nil.is_a?(Object), "Unlike NULL in other languages"
end
def test_you_dont_get_null_pointer_errors_when_calling_methods_on_nil
#
# What is the Exception that is thrown when you call a method that
# does not exist?
#
# Hint: launch irb and try the code in the block below.
#
# Don't be confused by the code below yet. It's using blocks
# which are explained later on in about_blocks.rb. For now,
# think about it like running nil.some_method_nil_doesnt_know_about
# in a sandbox and catching the error class into the exception
# variable.
#
exception = assert_raise(___) do
nil.some_method_nil_doesnt_know_about
end
#
# What is the error message itself? What substring or pattern could
# you test against in order to have a good idea what the string is?
#
assert_match /__/, exception.message
end
def test_nil_has_a_few_methods_defined_on_it
assert_equal __, nil.nil?
assert_equal __, nil.to_s
assert_equal __, nil.inspect
# THINK ABOUT IT:
#
# Is it better to use
# obj.nil?
# or
# obj == nil
# Why?
end
end

45
about_open_classes.rb Normal file
View File

@@ -0,0 +1,45 @@
require 'edgecase'
class AboutOpenClasses < EdgeCase::Koan
class Dog
def bark
"WOOF"
end
end
def test_as_defined_dogs_do_bark
fido = Dog.new
assert_equal __, fido.bark
end
# ------------------------------------------------------------------
# Open the existing Dog class and add a new method.
class Dog
def wag
"HAPPY"
end
end
def test_after_reopening_dogs_can_both_wag_and_bark
fido = Dog.new
assert_equal __, fido.wag
assert_equal __, fido.bark
end
# ------------------------------------------------------------------
class ::Integer
def even?
(self % 2) == 0
end
end
def test_even_existing_built_in_classes_can_be_reopened
assert_equal __, 1.even?
assert_equal __, 2.even?
end
# NOTE: To understand why we need the :: before Integer, you need to
# become enlightened about scope.
end

View File

@@ -0,0 +1,151 @@
require 'edgecase'
# Project: Create a Proxy Class
#
# In this assignment, create a proxy class (one is started for you
# below). You should be able to initialize the proxy object with any
# object. Any messages sent to the proxy object should be forwarded
# to the target object. As each message is sent, the proxy should
# record the name of the method send.
#
# The proxy class is started for you. You will need to add a method
# missing handler and any other supporting methods. The specification
# of the Proxy class is given in the AboutProxyObjectProject koan.
class Proxy
def initialize(target_object)
@object = target_object
end
end
# The proxy object should pass the following Koan:
#
class AboutProxyObjectProject < EdgeCase::Koan
def test_proxy_method_returns_wrapped_object
# NOTE: The Television class is defined below
tv = Proxy.new(Television.new)
assert tv.instance_of?(Proxy)
end
def test_tv_methods_still_perform_their_function
tv = Proxy.new(Television.new)
tv.channel = 10
tv.power
assert_equal 10, tv.channel
assert tv.on?
end
def test_proxy_records_messages_sent_to_tv
tv = Proxy.new(Television.new)
tv.power
tv.channel = 10
assert_equal [:power, :channel=], tv.messages
end
def test_proxy_handles_invalid_messages
tv = Proxy.new(Television.new)
assert_raise(NoMethodError) do
tv.no_such_method
end
end
def test_proxy_reports_methods_have_been_called
tv = Proxy.new(Television.new)
tv.power
tv.power
assert tv.called?(:power)
assert ! tv.called?(:channel)
end
def test_proxy_counts_method_calls
tv = Proxy.new(Television.new)
tv.power
tv.channel = 48
tv.power
assert_equal 2, tv.number_of_times_called(:power)
assert_equal 1, tv.number_of_times_called(:channel=)
assert_equal 0, tv.number_of_times_called(:on?)
end
def test_proxy_can_record_more_than_just_tv_objects
proxy = Proxy.new("Code Mash 2009")
proxy.upcase!
result = proxy.split
assert_equal ["CODE", "MASH", "2009"], result
assert_equal [:upcase!, :split], proxy.messages
end
end
# ====================================================================
# The following code is to support the testing of the Proxy class. No
# changes should be necessary to anything below this comment.
# Example class using in the proxy testing above.
class Television
attr_accessor :channel
def power
if @power == :on
@power = :off
else
@power = :on
end
end
def on?
@power == :on
end
end
# Tests for the Television class. All of theses tests should pass.
class TelevisionTest < EdgeCase::Koan
def test_it_turns_on
tv = Television.new
tv.power
assert tv.on?
end
def test_it_also_turns_off
tv = Television.new
tv.power
tv.power
assert ! tv.on?
end
def test_edge_case_on_off
tv = Television.new
tv.power
tv.power
tv.power
assert tv.on?
tv.power
assert ! tv.on?
end
def test_can_set_the_channel
tv = Television.new
tv.channel = 11
assert_equal 11, tv.channel
end
end

106
about_sandwich_code.rb Normal file
View File

@@ -0,0 +1,106 @@
require 'edgecase'
class AboutUsingBlocks < EdgeCase::Koan
def count_lines(file_name)
file = open(file_name)
count = 0
while line = file.gets
count += 1
end
count
ensure
file.close if file
end
def test_counting_lines
assert_equal __, count_lines("example_file.txt")
end
# ------------------------------------------------------------------
def find_line(file_name)
file = open(file_name)
while line = file.gets
return line if line.match(/e/)
end
ensure
file.close if file
end
def test_finding_lines
assert_equal __, find_line("example_file.txt")
end
# ------------------------------------------------------------------
# THINK ABOUT IT:
#
# The count_lines and find_line are similar, and yet different.
# They both follow the pattern of "sandwich code".
#
# Sandwich code is code that comes in three parts: (1) the top slice
# of bread, (2) the meat, and (3) the bottom slice of bread. The
# the bread part of the sandwich almost always goes together, but
# the meat part changes all the time.
#
# Because the changing part of the sandwich code is in the middle,
# abstracting the top and bottom bread slices to a library can be
# difficult in many languages.
#
# (Aside for C++ programmers: The idiom of capturing allocated
# pointers in a smart pointer constructor is an attempt to deal with
# the problem of sandwich code for resource allocation.)
#
# Consider the following code:
#
def file_sandwich(file_name)
file = open(file_name)
yield(file)
ensure
file.close if file
end
# Now we write:
def count_lines2(file_name)
file_sandwich(file_name) do |file|
count = 0
while line = file.gets
count += 1
end
count
end
end
def test_counting_lines2
assert_equal __, count_lines2("example_file.txt")
end
# ------------------------------------------------------------------
def find_line2(file_name)
# Rewrite find_line using the file_sandwich library function.
end
def test_finding_lines2
assert_equal __, find_line2("example_file.txt")
end
# ------------------------------------------------------------------
def count_lines3(file_name)
open(file_name) do |file|
count = 0
while line = file.gets
count += 1
end
count
end
end
def test_open_handles_the_file_sandwich_when_given_a_block
assert_equal __, count_lines3("example_file.txt")
end
end

79
about_scope.rb Normal file
View File

@@ -0,0 +1,79 @@
require 'edgecase'
class AboutScope < EdgeCase::Koan
module Jims
class Dog
def identify
:jims_dog
end
end
end
module Joes
class Dog
def identify
:joes_dog
end
end
end
def test_dog_is_not_available_in_the_current_scope
assert_raise(___) do
fido = Dog.new
end
end
def test_you_can_reference_nested_classes_using_the_scope_operator
fido = Jims::Dog.new
rover = Joes::Dog.new
assert_equal __, fido.identify
assert_equal __, rover.identify
assert_not_equal fido.class, rover.class
assert_not_equal Jims::Dog, Joes::Dog
end
# ------------------------------------------------------------------
class String
end
def test_bare_bones_class_names_assume_the_current_scope
assert_equal __, AboutScope::String == String
end
def test_nested_string_is_not_the_same_as_the_system_string
assert_equal __, String == "HI".class
end
def test_use_the_prefix_scope_operator_to_force_the_global_scope
assert_equal __, ::String == "HI".class
end
# ------------------------------------------------------------------
PI = 3.1416
def test_constants_are_defined_with_an_initial_uppercase_letter
assert_equal __, PI
end
# ------------------------------------------------------------------
MyString = ::String
def test_class_names_are_just_constants
assert_equal __, MyString == ::String
assert_equal __, MyString == "HI".class
end
def test_constants_can_be_looked_up_explicitly
assert_equal __, PI == AboutScope.const_get("PI")
assert_equal __, MyString == AboutScope.const_get("MyString")
end
def test_you_can_get_a_list_of_constants_for_any_class_or_module
assert_equal __, Jims.constants
assert_equal __, Object.constants.size
end
end

74
about_scoring_project.rb Normal file
View File

@@ -0,0 +1,74 @@
require 'edgecase'
# Greed is a dice game where you roll up to five dice to accumulate
# points. The following "score" function will be used calculate the
# score of a single roll of the dice.
#
# A greed roll is scored as follows:
#
# * A set of three ones is 1000 points
#
# * A set of three numbers (other than ones) is worth 100 times the
# number. (e.g. three fives is 500 points).
#
# * A one (that is not part of a set of three) is worth 100 points.
#
# * A five (that is not part of a set of three) is worth 50 points.
#
# * Everything else is worth 0 points.
#
#
# Examples:
#
# score([1,1,1,5,1]) => 1150 points
# score([2,3,4,6,2]) => 0 points
# score([3,4,5,3,3]) => 350 points
# score([1,5,1,2,4]) => 250 points
#
# More scoing examples are given in the tests below:
#
# Your goal is to write the score method.
def score(dice)
# You need to write this method
end
class AboutScoringAssignment < EdgeCase::Koan
def test_score_of_an_empty_list_is_zero
assert_equal 0, score([])
end
def test_score_of_a_single_roll_of_5_is_50
assert_equal 50, score([5])
end
def test_score_of_a_single_roll_of_1_is_100
assert_equal 100, score([1])
end
def test_score_of_mulitple_1s_and_5s_is_the_sum
assert_equal 200, score([1,5,5,1])
end
def test_score_of_single_2s_3s_4s_and_6s_are_zero
assert_equal 0, score([2,3,4,6])
end
def test_score_of_a_triple_1_is_1000
assert_equal 1000, score([1,1,1])
end
def test_score_of_other_triples_is_100x
assert_equal 200, score([2,2,2])
assert_equal 300, score([3,3,3])
assert_equal 400, score([4,4,4])
assert_equal 500, score([5,5,5])
assert_equal 600, score([6,6,6])
end
def test_score_of_mixed_is_sum
assert_equal 250, score([2,5,2,2,3])
assert_equal 550, score([5,5,5,5])
end
end

176
about_strings.rb Normal file
View File

@@ -0,0 +1,176 @@
require 'edgecase'
class AboutStrings < EdgeCase::Koan
def test_double_quoted_strings_are_strings
string = "Hello, World"
assert_equal __, string.is_a?(String)
end
def test_single_quoted_strings_are_also_strings
string = 'Goodbye, World'
assert_equal __, string.is_a?(String)
end
def test_use_single_quotes_to_create_string_with_double_quotes
string = 'He said, "Go Away."'
assert_equal __, string
end
def test_use_double_quotes_to_create_strings_with_single_quotes
string = "Don't"
assert_equal __, string
end
def test_use_backslash_for_those_hard_cases
a = "He said, \"Don't\""
b = 'He said, "Don\'t"'
assert_equal __, a == b
end
def test_use_flexible_quoting_to_handle_really_hard_cases
a = %(flexible quotes can handle both ' and " characters)
b = %!flexible quotes can handle both ' and " characters!
c = %{flexible quotes can handle both ' and " characters}
assert_equal __, a == b
assert_equal __, a == c
end
def test_flexible_quotes_can_handle_multiple_lines
long_string = %{
It was the best of times,
It was the worst of times.
}
assert_equal __, long_string.size
end
def test_here_documents_can_also_handle_multiple_lines
long_string = <<EOS
It was the best of times,
It was the worst of times.
EOS
assert_equal __, long_string.size
end
def test_plus_will_concatenate_two_strings
string = "Hello, " + "World"
assert_equal __, string
end
def test_plus_concatenation_will_leave_the_original_strings_unmodified
hi = "Hello, "
there = "World"
string = hi + there
assert_equal __, hi
assert_equal __, there
end
def test_plus_equals_will_concatenate_to_the_end_of_a_string
hi = "Hello, "
there = "World"
hi += there
assert_equal __, hi
end
def test_plus_equals_also_will_leave_the_original_string_unmodified
original_string = "Hello, "
hi = original_string
there = "World"
hi += there
assert_equal __, original_string
end
def test_the_shovel_operator_will_also_append_content_to_a_string
hi = "Hello, "
there = "World"
hi << there
assert_equal __, hi
assert_equal __, there
end
def test_the_shovel_operator_modifies_the_original_string
original_string = "Hello, "
hi = original_string
there = "World"
hi << there
assert_equal __, original_string
# THINK ABOUT IT:
#
# Ruby programmers tend to favor the shovel operator (<<) over the
# plus equals operator (+=) when building up strings. Why?
end
def test_double_quoted_string_interpret_escape_characters
string = "\n"
assert_equal __, string.size
end
def test_single_quoted_string_do_not_interpret_escape_characters
string = '\n'
assert_equal __, string.size
end
def test_single_quotes_sometimes_interpret_escape_characters
string = '\\\''
assert_equal __, string.size
assert_equal __, string
end
def test_double_quoted_strings_interpolate_variables
value = 123
string = "The value is #{value}"
assert_equal __, string
end
def test_single_quoted_strings_do_not_interpolate
value = 123
string = 'The value is #{value}'
assert_equal __, string
end
def test_any_ruby_expression_my_be_interpolated
string = "The square root of 5 is #{Math.sqrt(5)}"
assert_equal __, string
end
def test_you_can_get_a_substring_from_a_string
string = "Bacon, lettuce and tomato"
assert_equal __, string[7,3]
assert_equal __, string[7..9]
end
def test_you_can_get_a_single_character_from_a_string
string = "Bacon, lettuce and tomato"
assert_equal __, string[1]
# Surprised?
end
def test_single_characters_are_represented_by_integers
assert_equal __, ?a
assert_equal __, ?a == 97
assert_equal __, ?b == (?a + 1)
end
def test_strings_can_be_split
string = "Sausage Egg Cheese"
words = string.split
assert_equal [__, __, __], words
end
def test_strings_can_be_split_with_different_patterns
string = "the:rain:in:spain"
words = string.split(/:/)
assert_equal [__, __, __, __], words
# NOTE: Patterns are formed from Regular Expressions. Ruby has a
# very powerful Regular Expression library. Unfortunately, time
# does not permit us to explore it in detail in Ruby 101.
end
def test_strings_can_be_joined
words = ["Now", "is", "the", "time"]
assert_equal __, words.join(" ")
end
end

25
about_triangle_project.rb Normal file
View File

@@ -0,0 +1,25 @@
require 'edgecase'
# You need to write the triangle method in the file 'triangle.rb'
require 'triangle.rb'
class AboutTriangleAssignment < EdgeCase::Koan
def test_equilateral_triangles_have_equal_sides
assert_equal :equilateral, triangle(2, 2, 2)
assert_equal :equilateral, triangle(10, 10, 10)
end
def test_isosceles_triangles_have_exactly_two_sides_equal
assert_equal :isosceles, triangle(3, 4, 4)
assert_equal :isosceles, triangle(4, 3, 4)
assert_equal :isosceles, triangle(4, 4, 3)
assert_equal :isosceles, triangle(10, 10, 2)
end
def test_scalene_triangles_have_no_equal_sides
assert_equal :scalene, triangle(3, 4, 5)
assert_equal :scalene, triangle(10, 11, 12)
assert_equal :scalene, triangle(5, 4, 2)
end
end

View File

@@ -0,0 +1,15 @@
require 'edgecase'
# You need to write the triangle method in the file 'triangle.rb'
require 'triangle.rb'
class AboutTriangleAssignment < EdgeCase::Koan
# The first assignment did not talk about how to handle errors.
# Let's handle that part now.
def test_illegal_triangles_throw_exceptions
assert_raise(TriangleError) do triangle(0, 0, 0) end
assert_raise(TriangleError) do triangle(3, 4, -5) end
assert_raise(TriangleError) do triangle(2, 4, 2) end
end
end

33
about_true_and_false.rb Normal file
View File

@@ -0,0 +1,33 @@
require 'edgecase'
class AboutTrueAndFalse < EdgeCase::Koan
def truth_value(condition)
if condition
:true_stuff
else
:false_stuff
end
end
def test_true_is_treated_as_true
assert_equal __, truth_value(true)
end
def test_false_is_treated_as_false
assert_equal __, truth_value(false)
end
def test_nil_is_treated_as_false_too
assert_equal __, truth_value(nil)
end
def test_everything_else_is_treated_as_true
assert_equal __, truth_value(1)
assert_equal __, truth_value(0)
assert_equal __, truth_value([])
assert_equal __, truth_value({})
assert_equal __, truth_value("Strings")
assert_equal __, truth_value("")
end
end

47
array_test.rb Normal file
View File

@@ -0,0 +1,47 @@
require 'test_helper'
class ArrayTest < EdgeCase::TestCase
def test_basic_arrays
food = [:peanut, :button, :and, :jelly]
assert_equal __, food[0]
assert_equal __, food.size
end
def test_array_access
food = [:peanut, :button, :and, :jelly]
assert_equal __, food.first
assert_equal __, food.last
assert_equal __, food[0]
assert_equal __, food[2]
assert_equal __, food[(food.size() - 1)]
end
def test_arrays_with_other_objects
food = [:peanut, :button, :and, :jelly, 1, nil]
assert_equal __, food.size
assert_equal __, food.last
assert_equal __, food[5]
end
def test_adding_to_an_array_with_shovel_shovel
food = [:peanut, :button, :and, :jelly]
food << 'sandwich'
assert_equal __, food.size
assert_equal __, food.first
end
def test_adding_to_an_array_with_push
food = [:peanut, :button, :and, :jelly]
food.push('sandwich')
assert_equal __, food.last
end
def test_adding_to_an_array_with_unshift
food = [:peanut, :button, :and, :jelly]
food.unshift('a')
assert_equal __, food.first
end
end

1
code_mash.rb Normal file
View File

@@ -0,0 +1 @@
require 'edgecase'

208
edgecase.rb Normal file
View File

@@ -0,0 +1,208 @@
#!/usr/bin/env ruby
# -*- ruby -*-
require 'test/unit/assertions'
class FillMeInError < StandardError
end
def __(value="FILL ME IN")
value
end
def ___(value=FillMeInError)
value
end
module EdgeCase
class Sensei
attr_reader :failure, :failed_test
AssertionError = Test::Unit::AssertionFailedError
def initialize
@pass_count = 0
@failure = nil
@failed_test = nil
end
def accumulate(test)
if test.passed?
@pass_count += 1
puts " #{test.name} has expanded your awareness."
else
puts " #{test.name} has damaged your karma."
@failed_test = test
@failure = test.failure
throw :edgecase_exit
end
end
def failed?
! @failure.nil?
end
def assert_failed?
failure.is_a?(AssertionError)
end
def report
if failed?
puts
puts "You have not yet reached enlightenment ..."
puts failure.message
puts
puts "Please meditate on the following code:"
if assert_failed?
puts find_interesting_lines(failure.backtrace)
else
puts failure.backtrace
end
puts
end
say_something_zenlike
end
def find_interesting_lines(backtrace)
backtrace.reject { |line|
line =~ /test\/unit\/|edgecase\.rb/
}
end
# Hat's tip to Ara T. Howard for the zen statements from his
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
def say_something_zenlike
puts
if !failed?
puts "Mountains are again merely mountains"
else
case (@pass_count % 10)
when 0
puts "mountains are merely mountains"
when 1, 2
puts "learn the rules so you know how to break them properly"
when 3, 4
puts "remember that silence is sometimes the best answer"
when 5, 6
puts "sleep is the best meditation"
when 7, 8
puts "when you lose, don't lose the lesson"
else
puts "things are not what they appear to be: nor are they otherwise"
end
end
end
end
class Koan
include Test::Unit::Assertions
attr_reader :name, :failure
def initialize(name)
@name = name
@failure = nil
end
def passed?
@failure.nil?
end
def failed(failure)
@failure = failure
end
def setup
end
def teardown
end
# Class methods for the EdgeCase test suite.
class << self
def inherited(subclass)
subclasses << subclass
end
def method_added(name)
testmethods << name unless tests_disabled?
end
def run_tests(accumulator)
puts
puts "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 => ex
test.failed(ex)
ensure
begin
test.teardown
rescue StandardError => ex
test.failed(ex) if test.passed?
end
end
accumulator.accumulate(test)
end
def end_of_enlightenment
@tests_disabled = true
end
def command_line(args)
args.each do |arg|
case arg
when /^-n\/(.*)\/$/
@test_pattern = Regexp.new($1)
when /^-n(.*)$/
@test_pattern = Regexp.new(Regexp.quote($1))
else
if File.exist?(arg)
load(arg)
else
fail "Unknown command line argument '#{arg}'"
end
end
end
end
# Lazy initialize list of subclasses
def subclasses
@subclasses ||= []
end
# Lazy initialize list of test methods.
def testmethods
@test_methods ||= []
end
def tests_disabled?
@tests_disabled ||= false
end
def test_pattern
@test_pattern ||= /^test_/
end
end
end
end
END {
EdgeCase::Koan.command_line(ARGV)
zen_master = EdgeCase::Sensei.new
catch(:edgecase_exit) {
EdgeCase::Koan.subclasses.each do |sc|
sc.run_tests(zen_master)
end
}
zen_master.report
}

4
example_file.txt Normal file
View File

@@ -0,0 +1,4 @@
this
is
a
test

11
first_test.rb Normal file
View File

@@ -0,0 +1,11 @@
require 'test/unit'
class TestSomething < Test::Unit::TestCase
def test_assert
assert true
assert_equal 1, 1
assert_equal 1, 1.0
end
end

27
path_to_enlightenment.rb Normal file
View File

@@ -0,0 +1,27 @@
# The path to Ruby Enlightenment starts with the following:
require 'about_basics'
require 'about_nil'
require 'about_arrays'
require 'about_array_assignment'
require 'about_hashes'
require 'about_strings'
require 'about_methods'
require 'about_control_statements'
require 'about_true_and_false'
require 'about_triangle_project'
require 'about_exceptions'
require 'about_triangle_project_2'
require 'about_iteration'
require 'about_blocks'
require 'about_sandwich_code'
require 'about_scoring_project'
require 'about_classes'
require 'about_dice_project'
require 'about_inheritance'
require 'about_modules'
require 'about_scope'
require 'about_class_methods'
require 'about_message_passing'
require 'about_proxy_object_project'
require 'about_extra_credit'

7
test_helper.rb Normal file
View File

@@ -0,0 +1,7 @@
require 'test/unit'
def __
"FILL ME IN"
end
EdgeCase = Test::Unit

22
triangle.rb Normal file
View File

@@ -0,0 +1,22 @@
# Triangle Project Code.
# Triangle analyzes the lengths of the sides of a triangle
# (represented by a, b and c) and returns the type of triangle.
#
# It returns:
# :equilateral if all sides are equal
# :isosceles if exactly 2 sides are equal
# :scalene if no sides are equal
#
# The tests for this method can be found in
# about_triangle_project.rb
# and
# about_triangle_project_2.rb
#
def triangle(a, b, c)
# WRITE THIS CODE
end
# Error class used in part 2. No need to change this code.
class TriangleError < StandardError
end