From: Joe O'Brien Date: Mon, 26 Jan 2009 16:56:38 +0000 (-0500) Subject: moving to a new repo X-Git-Tag: codemash-2010~49 X-Git-Url: https://git.eng.unimelb.edu.au/public?a=commitdiff_plain;h=4e78262965cd05f917464b36ad7bcecc8836cfb4;p=ruby_koans.git moving to a new repo --- diff --git a/GREED_RULES.txt b/GREED_RULES.txt new file mode 100644 index 0000000..f120604 --- /dev/null +++ b/GREED_RULES.txt @@ -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 --- 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 index 0000000..146b876 --- /dev/null +++ b/Rakefile.rb @@ -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 index 0000000..37b1068 --- /dev/null +++ b/about_array_assignment.rb @@ -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 index 0000000..5fd016a --- /dev/null +++ b/about_arrays.rb @@ -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 index 0000000..db0bfe3 --- /dev/null +++ b/about_basics.rb @@ -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 index 0000000..df3a745 --- /dev/null +++ b/about_blocks.rb @@ -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 index 0000000..c318403 --- /dev/null +++ b/about_class_methods.rb @@ -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 index 0000000..d576e4b --- /dev/null +++ b/about_classes.rb @@ -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 + "" + 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 index 0000000..d2b75ad --- /dev/null +++ b/about_control_statements.rb @@ -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 index 0000000..c1fccb1 --- /dev/null +++ b/about_dice_project.rb @@ -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 index 0000000..b2843b1 --- /dev/null +++ b/about_exceptions.rb @@ -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 index 0000000..5012edf --- /dev/null +++ b/about_extra_credit.rb @@ -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 index 0000000..6208f38 --- /dev/null +++ b/about_hashes.rb @@ -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 index 0000000..7bc3a1f --- /dev/null +++ b/about_inheritance.rb @@ -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 index 0000000..01808dc --- /dev/null +++ b/about_iteration.rb @@ -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 index 0000000..d396c80 --- /dev/null +++ b/about_message_passing.rb @@ -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 index 0000000..187ad27 --- /dev/null +++ b/about_methods.rb @@ -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 index 0000000..c18c81e --- /dev/null +++ b/about_modules.rb @@ -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 index 0000000..b007cc0 --- /dev/null +++ b/about_nil.rb @@ -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 index 0000000..233cef5 --- /dev/null +++ b/about_open_classes.rb @@ -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 index 0000000..dad63d7 --- /dev/null +++ b/about_proxy_object_project.rb @@ -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 index 0000000..37f0c5f --- /dev/null +++ b/about_sandwich_code.rb @@ -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 index 0000000..968c360 --- /dev/null +++ b/about_scope.rb @@ -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 index 0000000..69aeae0 --- /dev/null +++ b/about_scoring_project.rb @@ -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 index 0000000..db3289a --- /dev/null +++ b/about_strings.rb @@ -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 = < 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 index 0000000..ffe7cbd --- /dev/null +++ b/example_file.txt @@ -0,0 +1,4 @@ +this +is +a +test diff --git a/first_test.rb b/first_test.rb new file mode 100644 index 0000000..708baf1 --- /dev/null +++ b/first_test.rb @@ -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 index 0000000..f8e323f --- /dev/null +++ b/path_to_enlightenment.rb @@ -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 index 0000000..9accf96 --- /dev/null +++ b/test_helper.rb @@ -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 index 0000000..8df052a --- /dev/null +++ b/triangle.rb @@ -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