moving to a new repo
authorJoe O'Brien <joe@theedgecase.com>
Mon, 26 Jan 2009 16:56:38 +0000 (11:56 -0500)
committerJoe O'Brien <joe@theedgecase.com>
Mon, 26 Jan 2009 16:56:38 +0000 (11:56 -0500)
37 files changed:
GREED_RULES.txt [new file with mode: 0644]
README
Rakefile.rb [new file with mode: 0644]
about_array_assignment.rb [new file with mode: 0644]
about_arrays.rb [new file with mode: 0644]
about_basics.rb [new file with mode: 0644]
about_blocks.rb [new file with mode: 0644]
about_class_methods.rb [new file with mode: 0644]
about_classes.rb [new file with mode: 0644]
about_control_statements.rb [new file with mode: 0644]
about_dice_project.rb [new file with mode: 0644]
about_exceptions.rb [new file with mode: 0644]
about_extra_credit.rb [new file with mode: 0644]
about_hashes.rb [new file with mode: 0644]
about_inheritance.rb [new file with mode: 0644]
about_iteration.rb [new file with mode: 0644]
about_message_passing.rb [new file with mode: 0644]
about_methods.rb [new file with mode: 0644]
about_modules.rb [new file with mode: 0644]
about_nil.rb [new file with mode: 0644]
about_open_classes.rb [new file with mode: 0644]
about_proxy_object_project.rb [new file with mode: 0644]
about_sandwich_code.rb [new file with mode: 0644]
about_scope.rb [new file with mode: 0644]
about_scoring_project.rb [new file with mode: 0644]
about_strings.rb [new file with mode: 0644]
about_triangle_project.rb [new file with mode: 0644]
about_triangle_project_2.rb [new file with mode: 0644]
about_true_and_false.rb [new file with mode: 0644]
array_test.rb [new file with mode: 0644]
code_mash.rb [new file with mode: 0644]
edgecase.rb [new file with mode: 0644]
example_file.txt [new file with mode: 0644]
first_test.rb [new file with mode: 0644]
path_to_enlightenment.rb [new file with mode: 0644]
test_helper.rb [new file with mode: 0644]
triangle.rb [new file with mode: 0644]

diff --git a/GREED_RULES.txt b/GREED_RULES.txt
new file mode 100644 (file)
index 0000000..f120604
--- /dev/null
@@ -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.
diff --git a/README b/README
index e69de29..3dbdc71 100644 (file)
--- a/README
+++ b/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
+    
diff --git a/Rakefile.rb b/Rakefile.rb
new file mode 100644 (file)
index 0000000..146b876
--- /dev/null
@@ -0,0 +1,6 @@
+
+task :default => :walk_the_path
+
+task :walk_the_path do
+  ruby 'path_to_enlightenment.rb'
+end
\ No newline at end of file
diff --git a/about_array_assignment.rb b/about_array_assignment.rb
new file mode 100644 (file)
index 0000000..37b1068
--- /dev/null
@@ -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
diff --git a/about_arrays.rb b/about_arrays.rb
new file mode 100644 (file)
index 0000000..5fd016a
--- /dev/null
@@ -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
diff --git a/about_basics.rb b/about_basics.rb
new file mode 100644 (file)
index 0000000..db0bfe3
--- /dev/null
@@ -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
diff --git a/about_blocks.rb b/about_blocks.rb
new file mode 100644 (file)
index 0000000..df3a745
--- /dev/null
@@ -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
diff --git a/about_class_methods.rb b/about_class_methods.rb
new file mode 100644 (file)
index 0000000..c318403
--- /dev/null
@@ -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
diff --git a/about_classes.rb b/about_classes.rb
new file mode 100644 (file)
index 0000000..d576e4b
--- /dev/null
@@ -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
diff --git a/about_control_statements.rb b/about_control_statements.rb
new file mode 100644 (file)
index 0000000..d2b75ad
--- /dev/null
@@ -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
diff --git a/about_dice_project.rb b/about_dice_project.rb
new file mode 100644 (file)
index 0000000..c1fccb1
--- /dev/null
@@ -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
diff --git a/about_exceptions.rb b/about_exceptions.rb
new file mode 100644 (file)
index 0000000..b2843b1
--- /dev/null
@@ -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
diff --git a/about_extra_credit.rb b/about_extra_credit.rb
new file mode 100644 (file)
index 0000000..5012edf
--- /dev/null
@@ -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.
diff --git a/about_hashes.rb b/about_hashes.rb
new file mode 100644 (file)
index 0000000..6208f38
--- /dev/null
@@ -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
diff --git a/about_inheritance.rb b/about_inheritance.rb
new file mode 100644 (file)
index 0000000..7bc3a1f
--- /dev/null
@@ -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
diff --git a/about_iteration.rb b/about_iteration.rb
new file mode 100644 (file)
index 0000000..01808dc
--- /dev/null
@@ -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
diff --git a/about_message_passing.rb b/about_message_passing.rb
new file mode 100644 (file)
index 0000000..d396c80
--- /dev/null
@@ -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
diff --git a/about_methods.rb b/about_methods.rb
new file mode 100644 (file)
index 0000000..187ad27
--- /dev/null
@@ -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
diff --git a/about_modules.rb b/about_modules.rb
new file mode 100644 (file)
index 0000000..c18c81e
--- /dev/null
@@ -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
diff --git a/about_nil.rb b/about_nil.rb
new file mode 100644 (file)
index 0000000..b007cc0
--- /dev/null
@@ -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
diff --git a/about_open_classes.rb b/about_open_classes.rb
new file mode 100644 (file)
index 0000000..233cef5
--- /dev/null
@@ -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
diff --git a/about_proxy_object_project.rb b/about_proxy_object_project.rb
new file mode 100644 (file)
index 0000000..dad63d7
--- /dev/null
@@ -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
diff --git a/about_sandwich_code.rb b/about_sandwich_code.rb
new file mode 100644 (file)
index 0000000..37f0c5f
--- /dev/null
@@ -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
diff --git a/about_scope.rb b/about_scope.rb
new file mode 100644 (file)
index 0000000..968c360
--- /dev/null
@@ -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
diff --git a/about_scoring_project.rb b/about_scoring_project.rb
new file mode 100644 (file)
index 0000000..69aeae0
--- /dev/null
@@ -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
diff --git a/about_strings.rb b/about_strings.rb
new file mode 100644 (file)
index 0000000..db3289a
--- /dev/null
@@ -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
diff --git a/about_triangle_project.rb b/about_triangle_project.rb
new file mode 100644 (file)
index 0000000..5c1855c
--- /dev/null
@@ -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
+  
diff --git a/about_triangle_project_2.rb b/about_triangle_project_2.rb
new file mode 100644 (file)
index 0000000..f9e4728
--- /dev/null
@@ -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
+  
diff --git a/about_true_and_false.rb b/about_true_and_false.rb
new file mode 100644 (file)
index 0000000..5192251
--- /dev/null
@@ -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
diff --git a/array_test.rb b/array_test.rb
new file mode 100644 (file)
index 0000000..83ec07b
--- /dev/null
@@ -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
+
diff --git a/code_mash.rb b/code_mash.rb
new file mode 100644 (file)
index 0000000..fe089a5
--- /dev/null
@@ -0,0 +1 @@
+require 'edgecase'
\ No newline at end of file
diff --git a/edgecase.rb b/edgecase.rb
new file mode 100644 (file)
index 0000000..7ab429e
--- /dev/null
@@ -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
+}
diff --git a/example_file.txt b/example_file.txt
new file mode 100644 (file)
index 0000000..ffe7cbd
--- /dev/null
@@ -0,0 +1,4 @@
+this
+is
+a
+test
diff --git a/first_test.rb b/first_test.rb
new file mode 100644 (file)
index 0000000..708baf1
--- /dev/null
@@ -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
+
+
diff --git a/path_to_enlightenment.rb b/path_to_enlightenment.rb
new file mode 100644 (file)
index 0000000..f8e323f
--- /dev/null
@@ -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'
diff --git a/test_helper.rb b/test_helper.rb
new file mode 100644 (file)
index 0000000..9accf96
--- /dev/null
@@ -0,0 +1,7 @@
+require 'test/unit'
+
+def __
+  "FILL ME IN"
+end
+
+EdgeCase = Test::Unit
diff --git a/triangle.rb b/triangle.rb
new file mode 100644 (file)
index 0000000..8df052a
--- /dev/null
@@ -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