moving to a new repo
This commit is contained in:
66
GREED_RULES.txt
Normal file
66
GREED_RULES.txt
Normal 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
38
README
@@ -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
6
Rakefile.rb
Normal 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
38
about_array_assignment.rb
Normal 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
101
about_arrays.rb
Normal 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
40
about_basics.rb
Normal 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
96
about_blocks.rb
Normal 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
170
about_class_methods.rb
Normal 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
190
about_classes.rb
Normal 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
116
about_control_statements.rb
Normal 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
64
about_dice_project.rb
Normal 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
60
about_exceptions.rb
Normal 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
8
about_extra_credit.rb
Normal 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
56
about_hashes.rb
Normal 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
94
about_inheritance.rb
Normal 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
93
about_iteration.rb
Normal 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
167
about_message_passing.rb
Normal 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
150
about_methods.rb
Normal 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
63
about_modules.rb
Normal 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
46
about_nil.rb
Normal 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
45
about_open_classes.rb
Normal 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
|
||||
151
about_proxy_object_project.rb
Normal file
151
about_proxy_object_project.rb
Normal 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
106
about_sandwich_code.rb
Normal 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
79
about_scope.rb
Normal 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
74
about_scoring_project.rb
Normal 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
176
about_strings.rb
Normal 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
25
about_triangle_project.rb
Normal 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
|
||||
|
||||
15
about_triangle_project_2.rb
Normal file
15
about_triangle_project_2.rb
Normal 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
33
about_true_and_false.rb
Normal 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
47
array_test.rb
Normal 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
1
code_mash.rb
Normal file
@@ -0,0 +1 @@
|
||||
require 'edgecase'
|
||||
208
edgecase.rb
Normal file
208
edgecase.rb
Normal 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
4
example_file.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
this
|
||||
is
|
||||
a
|
||||
test
|
||||
11
first_test.rb
Normal file
11
first_test.rb
Normal 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
27
path_to_enlightenment.rb
Normal 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
7
test_helper.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
require 'test/unit'
|
||||
|
||||
def __
|
||||
"FILL ME IN"
|
||||
end
|
||||
|
||||
EdgeCase = Test::Unit
|
||||
22
triangle.rb
Normal file
22
triangle.rb
Normal 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
|
||||
Reference in New Issue
Block a user