diff --git a/cve.rb b/cve.rb
new file mode 100755
index 0000000..98aed70
--- /dev/null
+++ b/cve.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+# CVE class
+
+# using this code as example to start:
+# https://github.com/fidius/cvedb/blob/master/lib/tasks/parse_cves.rake
+
+require 'net/http'
+
+class CVE
+
+ def base_uri
+ @base_uri ||= 'https://nvd.nist.gov/feeds/'
+ end
+
+ def cve_years
+ # i know this is long, ill fix it later to generate the numbers or something
+ @cve_years ||= %w[
+ 2002 2003 2004 2005 2006 2007
+ 2008 2009 2010 2011 2012 2013
+ 2014 2015 2016 2017 2018]
+ end
+
+ def xml_feed
+ endpoint = 'cve/2.0/'
+ uri = URI(base_uri)
+ end
+end
diff --git a/koans/.path_progress b/koans/.path_progress
new file mode 100644
index 0000000..198f356
--- /dev/null
+++ b/koans/.path_progress
@@ -0,0 +1 @@
+0,0,0,5,5,5,5
\ No newline at end of file
diff --git a/koans/GREED_RULES.txt b/koans/GREED_RULES.txt
new file mode 100644
index 0000000..58b5a9c
--- /dev/null
+++ b/koans/GREED_RULES.txt
@@ -0,0 +1,66 @@
+= Playing Greed
+
+Greed is a dice game played among 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 five 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 all of the thrown dice are scoring, 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/koans/README.rdoc b/koans/README.rdoc
new file mode 100644
index 0000000..5908547
--- /dev/null
+++ b/koans/README.rdoc
@@ -0,0 +1,191 @@
+= Neo 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 by basing the koans on tests.
+Testing is not just something we pay lip service to, but something we
+live. Testing is essential in your quest to learn and do great things in Ruby.
+
+== 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.
+
+== Installing Ruby
+
+If you do not have Ruby setup, please visit http://ruby-lang.org/en/downloads/ for
+operating specific instructions. In order to run the koans you need +ruby+ and
++rake+ installed. To check your installations simply type:
+
+*nix platforms from any terminal window:
+
+ [~] $ ruby --version
+ [~] $ rake --version
+
+Windows from the command prompt (+cmd.exe+)
+
+ c:\ruby --version
+ c:\rake --version
+
+If you don't have +rake+ installed, just run gem install rake
+
+Any response for Ruby with a version number greater than 1.8 is fine (should be
+around 1.8.6 or more). Any version of +rake+ will do.
+
+== Generating the Koans
+
+A fresh checkout will not include the koans, you will need to generate
+them.
+
+ [ruby_koans] $ rake gen # generates the koans directory
+
+If you need to regenerate the koans, thus wiping your current `koans`,
+
+ [ruby_koans] $ rake regen # regenerates the koans directory, wiping the original
+
+== The Path To Enlightenment
+
+You can run the tests through +rake+ or by calling the file itself (+rake+ is the
+recommended way to run them as we might build more functionality into this task).
+
+*nix platforms, from the +ruby_koans+ directory
+
+ [ruby_koans] $ rake # runs the default target :walk_the_path
+ [ruby_koans] $ ruby path_to_enlightenment.rb # simply call the file directly
+
+Windows is the same thing
+
+ c:\ruby_koans\rake # runs the default target :walk_the_path
+ c:\ruby_koans\ruby path_to_enlightenment.rb # simply call the file directly
+
+=== Red, Green, Refactor
+
+In test-driven development the mantra has always been red, green, refactor.
+Write a failing test and run it (red), make the test pass (green),
+then look at the code and consider if you can make it any better (refactor).
+
+While walking the path to Ruby enlightenment you will need to run the koan and
+see it fail (red), make the test pass (green), then take a moment
+and reflect upon the test to see what it is teaching you and improve the code to
+better communicate its intent (refactor).
+
+The very first time you run the koans you will see the following output:
+
+ [ ruby_koans ] $ rake
+ (in /Users/person/dev/ruby_koans)
+ /usr/bin/ruby1.8 path_to_enlightenment.rb
+
+ AboutAsserts#test_assert_truth has damaged your karma.
+
+ The Master says:
+ You have not yet reached enlightenment.
+
+ The answers you seek...
+ is not true.
+
+ Please meditate on the following code:
+ ./about_asserts.rb:10:in `test_assert_truth'
+ path_to_enlightenment.rb:38:in `each_with_index'
+ path_to_enlightenment.rb:38
+
+ mountains are merely mountains
+ your path thus far [X_________________________________________________] 0/280
+
+You have come to your first stage. Notice it is telling you where to look for
+the first solution:
+
+ Please meditate on the following code:
+ ./about_asserts.rb:10:in `test_assert_truth'
+ path_to_enlightenment.rb:38:in `each_with_index'
+ path_to_enlightenment.rb:38
+
+Open the +about_asserts.rb+ file and look at the first test:
+
+ # We shall contemplate truth by testing reality, via asserts.
+ def test_assert_truth
+ assert false # This should be true
+ end
+
+Change the +false+ to +true+ and re-run the test. After you are
+done, think about what you are learning. In this case, ignore everything except
+the method name (+test_assert_truth+) and the parts inside the method (everything
+before the +end+).
+
+In this case the goal is for you to see that if you pass a value to the +assert+
+method, it will either ensure it is +true+ and continue on, or fail if
+the statement is +false+.
+
+=== Running the Koans automatically
+
+This section is optional.
+
+Normally the path to enlightenment looks like this:
+
+ cd ruby_koans
+ rake
+ # edit
+ rake
+ # edit
+ rake
+ # etc
+
+If you prefer, you can keep the koans running in the background so that after you
+make a change in your editor, the koans will immediately run again. This will
+hopefully keep your focus on learning Ruby instead of on the command line.
+
+Install the Ruby gem (library) called +watchr+ and then ask it to
+"watch" the koans for changes:
+
+ cd ruby_koans
+ rake
+ # decide to run rake automatically from now on as you edit
+ gem install watchr
+ watchr ./koans/koans.watchr
+
+== Inspiration
+
+A special thanks to Mike Clark and Ara Howard for inspiring this
+project. Mike Clark wrote an excellent blog post about learning Ruby
+through unit testing. This sparked an idea that has taken a bit to
+solidify, that of bringing new rubyists into the community through
+testing. Ara Howard then gave us the idea for the Koans in his ruby
+quiz entry on Meta Koans (a must for any rubyist wanting to improve
+their skills). Also, "The Little Lisper" taught us all the value of
+the short questions/simple answers style of learning.
+
+Mike Clark's post :: http://www.clarkware.com/cgi/blosxom/2005/03/18
+Meta Koans :: http://rubyquiz.com/quiz67.html
+The Little Lisper :: http://www.amazon.com/Little-LISPer-Third-Daniel-Friedman/dp/0023397632
+
+== Other Resources
+
+The Ruby Language :: http://ruby-lang.org
+Try Ruby in your browser :: http://tryruby.org
+
+Dave Thomas' introduction to Ruby Programming Ruby (the Pick Axe) :: http://pragprog.com/titles/ruby/programming-ruby
+
+Brian Marick's fantastic guide for beginners Everyday Scripting with Ruby :: http://pragprog.com/titles/bmsft/everyday-scripting-with-ruby
+
+= Other stuff
+
+Author :: Jim Weirich
+Author :: Joe O'Brien
+Issue Tracker :: http://www.pivotaltracker.com/projects/48111
+Requires :: Ruby 1.8.x or later and Rake (any recent version)
+
+= License
+
+http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png
+
+RubyKoans is released under a Creative Commons,
+Attribution-NonCommercial-ShareAlike, Version 3.0
+(http://creativecommons.org/licenses/by-nc-sa/3.0/) License.
diff --git a/koans/Rakefile b/koans/Rakefile
new file mode 100644
index 0000000..1a2c7f2
--- /dev/null
+++ b/koans/Rakefile
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+# -*- ruby -*-
+
+require 'rake/clean'
+require 'rake/testtask'
+
+task :default => :test
+
+task :test do
+ ruby 'path_to_enlightenment.rb'
+end
+
diff --git a/koans/about_array_assignment.rb b/koans/about_array_assignment.rb
new file mode 100644
index 0000000..87186ff
--- /dev/null
+++ b/koans/about_array_assignment.rb
@@ -0,0 +1,51 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutArrayAssignment < Neo::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_splat_operator
+ first_name, *last_name = ["John", "Smith", "III"]
+ assert_equal __, first_name
+ assert_equal __, last_name
+ end
+
+ def test_parallel_assignments_with_too_few_variables
+ first_name, last_name = ["Cher"]
+ assert_equal __, first_name
+ assert_equal __, last_name
+ end
+
+ def test_parallel_assignments_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
+
+ def test_swapping_with_parallel_assignment
+ first_name = "Roy"
+ last_name = "Rob"
+ first_name, last_name = last_name, first_name
+ assert_equal __, first_name
+ assert_equal __, last_name
+ end
+end
diff --git a/koans/about_arrays.rb b/koans/about_arrays.rb
new file mode 100644
index 0000000..ce9b46e
--- /dev/null
+++ b/koans/about_arrays.rb
@@ -0,0 +1,84 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutArrays < Neo::Koan
+ def test_creating_arrays
+ empty_array = Array.new
+ assert_equal __, 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[4,100]
+ assert_equal __, array[5,0]
+ end
+
+ def test_arrays_and_ranges
+ assert_equal __, (1..5).class
+ assert_not_equal [1,2,3,4,5], (1..5)
+ assert_equal __, (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
+
+end
diff --git a/koans/about_asserts.rb b/koans/about_asserts.rb
new file mode 100644
index 0000000..41ef1ab
--- /dev/null
+++ b/koans/about_asserts.rb
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+# -*- ruby -*-
+
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutAsserts < Neo::Koan
+
+ # We shall contemplate truth by testing reality, via asserts.
+ def test_assert_truth
+ assert true # This should be true
+ end
+
+ # Enlightenment may be more easily achieved with appropriate
+ # messages.
+ def test_assert_with_message
+ assert true, "This should be true -- Please fix this"
+ end
+
+ # To understand reality, we must compare our expectations against
+ # reality.
+ def test_assert_equality
+ expected_value = 2
+ 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 = 2
+ 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 2, 1 + 1
+ end
+end
diff --git a/koans/about_blocks.rb b/koans/about_blocks.rb
new file mode 100644
index 0000000..c02dab9
--- /dev/null
+++ b/koans/about_blocks.rb
@@ -0,0 +1,96 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutBlocks < Neo::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
+ 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_affect_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 syntax
+ 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_explicit_block(&block)
+ block.call(10)
+ end
+
+ def test_methods_can_take_an_explicit_block_argument
+ assert_equal __, method_with_explicit_block { |n| n * 2 }
+
+ add_one = lambda { |n| n + 1 }
+ assert_equal __, method_with_explicit_block(&add_one)
+ end
+
+end
diff --git a/koans/about_class_methods.rb b/koans/about_class_methods.rb
new file mode 100644
index 0000000..46aab74
--- /dev/null
+++ b/koans/about_class_methods.rb
@@ -0,0 +1,169 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutClassMethods < Neo::Koan
+ class Dog
+ end
+
+ def test_objects_are_objects
+ fido = Dog.new
+ assert_equal __, fido.is_a?(Object)
+ end
+
+ def test_classes_are_classes
+ 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 fido.methods.size > _n_
+ end
+
+ def test_classes_have_methods
+ assert Dog.methods.size > _n_
+ 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_not_affected_by_these_singleton_methods
+ fido = Dog.new
+ rover = Dog.new
+ def fido.wag
+ :fidos_wag
+ end
+
+ assert_raise(___) do
+ rover.wag
+ end
+ end
+
+ # ------------------------------------------------------------------
+
+ class Dog2
+ def wag
+ :instance_level_wag
+ end
+ end
+
+ def Dog2.wag
+ :class_level_wag
+ end
+
+ def test_since_classes_are_objects_you_can_define_singleton_methods_on_them_too
+ assert_equal __, Dog2.wag
+ end
+
+ def test_class_methods_are_independent_of_instance_methods
+ fido = Dog2.new
+ assert_equal __, fido.wag
+ assert_equal __, Dog2.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/koans/about_classes.rb b/koans/about_classes.rb
new file mode 100644
index 0000000..1d146be
--- /dev/null
+++ b/koans/about_classes.rb
@@ -0,0 +1,190 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutClasses < Neo::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_different_instance_variables
+ fido = Dog6.new("Fido")
+ rover = Dog6.new("Rover")
+
+ assert_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
+ @name
+ 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.to_s
+ end
+
+ def test_to_s_is_used_in_string_interpolation
+ fido = Dog7.new("Fido")
+ assert_equal __, "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/koans/about_constants.rb b/koans/about_constants.rb
new file mode 100644
index 0000000..68ae078
--- /dev/null
+++ b/koans/about_constants.rb
@@ -0,0 +1,87 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+C = "top level"
+
+class AboutConstants < Neo::Koan
+
+ C = "nested"
+
+ def test_nested_constants_may_also_be_referenced_with_relative_paths
+ assert_equal __, C
+ end
+
+ def test_top_level_constants_are_referenced_by_double_colons
+ assert_equal __, ::C
+ end
+
+ def test_nested_constants_are_referenced_by_their_complete_path
+ assert_equal __, AboutConstants::C
+ assert_equal __, ::AboutConstants::C
+ end
+
+ # ------------------------------------------------------------------
+
+ class Animal
+ LEGS = 4
+ def legs_in_animal
+ LEGS
+ end
+
+ class NestedAnimal
+ def legs_in_nested_animal
+ LEGS
+ end
+ end
+ end
+
+ def test_nested_classes_inherit_constants_from_enclosing_classes
+ assert_equal __, Animal::NestedAnimal.new.legs_in_nested_animal
+ end
+
+ # ------------------------------------------------------------------
+
+ class Reptile < Animal
+ def legs_in_reptile
+ LEGS
+ end
+ end
+
+ def test_subclasses_inherit_constants_from_parent_classes
+ assert_equal __, Reptile.new.legs_in_reptile
+ end
+
+ # ------------------------------------------------------------------
+
+ class MyAnimals
+ LEGS = 2
+
+ class Bird < Animal
+ def legs_in_bird
+ LEGS
+ end
+ end
+ end
+
+ def test_who_wins_with_both_nested_and_inherited_constants
+ assert_equal __, MyAnimals::Bird.new.legs_in_bird
+ end
+
+ # QUESTION: Which has precedence: The constant in the lexical scope,
+ # or the constant from the inheritance hierarchy?
+
+ # ------------------------------------------------------------------
+
+ class MyAnimals::Oyster < Animal
+ def legs_in_oyster
+ LEGS
+ end
+ end
+
+ def test_who_wins_with_explicit_scoping_on_class_definition
+ assert_equal __, MyAnimals::Oyster.new.legs_in_oyster
+ end
+
+ # QUESTION: Now which has precedence: The constant in the lexical
+ # scope, or the constant from the inheritance hierarchy? Why is it
+ # different than the previous answer?
+end
diff --git a/koans/about_control_statements.rb b/koans/about_control_statements.rb
new file mode 100644
index 0000000..f323757
--- /dev/null
+++ b/koans/about_control_statements.rb
@@ -0,0 +1,142 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutControlStatements < Neo::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_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 # same as saying 'if !false', which evaluates as 'if true'
+ result = :false_value
+ end
+ assert_equal __, result
+ end
+
+ def test_unless_statement_evaluate_true
+ result = :default_value
+ unless true # same as saying 'if !true', which evaluates as 'if false'
+ result = :true_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_break_statement_returns_values
+ i = 1
+ result = while i <= 10
+ break i if i % 2 == 0
+ 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
+
+ def test_times_statement
+ sum = 0
+ 10.times do
+ sum += 1
+ end
+ assert_equal __, sum
+ end
+
+end
diff --git a/koans/about_dice_project.rb b/koans/about_dice_project.rb
new file mode 100644
index 0000000..5a1848f
--- /dev/null
+++ b/koans/about_dice_project.rb
@@ -0,0 +1,63 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+# Implement a DiceSet Class here:
+#
+# class DiceSet
+# code ...
+# end
+
+class AboutDiceProject < Neo::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/koans/about_exceptions.rb b/koans/about_exceptions.rb
new file mode 100644
index 0000000..464f57e
--- /dev/null
+++ b/koans/about_exceptions.rb
@@ -0,0 +1,68 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutExceptions < Neo::Koan
+
+ class MySpecialError < RuntimeError
+ end
+
+ def test_exceptions_inherit_from_Exception
+ assert_equal __, MySpecialError.ancestors[1]
+ assert_equal __, MySpecialError.ancestors[2]
+ assert_equal __, MySpecialError.ancestors[3]
+ assert_equal __, MySpecialError.ancestors[4]
+ end
+
+ def test_rescue_clause
+ result = nil
+ begin
+ fail "Oops"
+ rescue StandardError => ex
+ result = :exception_handled
+ end
+
+ assert_equal __, result
+
+ assert_equal __, ex.is_a?(StandardError), "Should be a Standard Error"
+ assert_equal __, ex.is_a?(RuntimeError), "Should be a Runtime Error"
+
+ 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 __, result
+ assert_equal __, ex.message
+ end
+
+ def test_ensure_clause
+ result = nil
+ begin
+ fail "Oops"
+ rescue StandardError
+ # no code here
+ ensure
+ result = :always_run
+ end
+
+ assert_equal __, result
+ end
+
+ # Sometimes, we must know about the unknown
+ def test_asserting_an_error_is_raised
+ # A do-end is a block, a topic to explore more later
+ assert_raise(___) do
+ raise MySpecialError.new("New instances can be raised directly.")
+ end
+ end
+
+end
diff --git a/koans/about_extra_credit.rb b/koans/about_extra_credit.rb
new file mode 100644
index 0000000..5012edf
--- /dev/null
+++ b/koans/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/koans/about_hashes.rb b/koans/about_hashes.rb
new file mode 100644
index 0000000..7287ba0
--- /dev/null
+++ b/koans/about_hashes.rb
@@ -0,0 +1,116 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutHashes < Neo::Koan
+ def test_creating_hashes
+ empty_hash = Hash.new
+ assert_equal __, 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_accessing_hashes_with_fetch
+ hash = { :one => "uno" }
+ assert_equal __, hash.fetch(:one)
+ assert_raise(___) do
+ hash.fetch(:doesnt_exist)
+ end
+
+ # THINK ABOUT IT:
+ #
+ # Why might you want to use #fetch instead of #[] when accessing hash keys?
+ end
+
+ def test_changing_hashes
+ hash = { :one => "uno", :two => "dos" }
+ hash[:one] = "eins"
+
+ expected = { :one => __, :two => "dos" }
+ assert_equal __, 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
+ hash = { :one => "uno", :two => "dos" }
+ assert_equal __, hash.keys.size
+ assert_equal __, hash.keys.include?(:one)
+ assert_equal __, hash.keys.include?(:two)
+ assert_equal __, hash.keys.class
+ end
+
+ def test_hash_values
+ hash = { :one => "uno", :two => "dos" }
+ assert_equal __, hash.values.size
+ assert_equal __, hash.values.include?("uno")
+ assert_equal __, hash.values.include?("dos")
+ assert_equal __, hash.values.class
+ end
+
+ def test_combining_hashes
+ hash = { "jim" => 53, "amy" => 20, "dan" => 23 }
+ new_hash = hash.merge({ "jim" => 54, "jenny" => 26 })
+
+ assert_equal __, hash != new_hash
+
+ expected = { "jim" => __, "amy" => 20, "dan" => 23, "jenny" => __ }
+ assert_equal __, expected == new_hash
+ end
+
+ def test_default_value
+ hash1 = Hash.new
+ hash1[:one] = 1
+
+ assert_equal __, hash1[:one]
+ assert_equal __, hash1[:two]
+
+ hash2 = Hash.new("dos")
+ hash2[:one] = 1
+
+ assert_equal __, hash2[:one]
+ assert_equal __, hash2[:two]
+ end
+
+ def test_default_value_is_the_same_object
+ hash = Hash.new([])
+
+ hash[:one] << "uno"
+ hash[:two] << "dos"
+
+ assert_equal __, hash[:one]
+ assert_equal __, hash[:two]
+ assert_equal __, hash[:three]
+
+ assert_equal __, hash[:one].object_id == hash[:two].object_id
+ end
+
+ def test_default_value_with_block
+ hash = Hash.new {|hash, key| hash[key] = [] }
+
+ hash[:one] << "uno"
+ hash[:two] << "dos"
+
+ assert_equal __, hash[:one]
+ assert_equal __, hash[:two]
+ assert_equal __, hash[:three]
+ end
+end
diff --git a/koans/about_inheritance.rb b/koans/about_inheritance.rb
new file mode 100644
index 0000000..61bc890
--- /dev/null
+++ b/koans/about_inheritance.rb
@@ -0,0 +1,85 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutInheritance < Neo::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_subclasses_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
+ end
+
+ def test_subclasses_can_invoke_parent_behavior_via_super
+ ralph = BullDog.new("Ralph")
+ assert_equal __, ralph.bark
+ 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/koans/about_iteration.rb b/koans/about_iteration.rb
new file mode 100644
index 0000000..6179c2c
--- /dev/null
+++ b/koans/about_iteration.rb
@@ -0,0 +1,122 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutIteration < Neo::Koan
+
+ # -- An Aside ------------------------------------------------------
+ # Ruby 1.8 stores names as strings. Ruby 1.9 and later stores names
+ # as symbols. So we use a version dependent method "as_name" to
+ # convert to the right format in the koans. We will use "as_name"
+ # whenever comparing to lists of methods.
+
+ in_ruby_version("1.8") do
+ def as_name(name)
+ name.to_s
+ end
+ end
+
+ in_ruby_version("1.9", "2") do
+ def as_name(name)
+ name.to_sym
+ end
+ end
+
+ # Ok, now back to the Koans.
+ # -------------------------------------------------------------------
+
+ def test_each_is_a_method_on_arrays
+ assert_equal __, [].methods.include?(as_name(:each))
+ end
+
+ def test_iterating_with_each
+ array = [1, 2, 3]
+ sum = 0
+ array.each do |item|
+ sum += item
+ end
+ assert_equal __, 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 do |item|
+ break if item > 3
+ sum += item
+ end
+ 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) { |product, item| product * 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.open("example_file.txt") do |file|
+ upcase_lines = file.map { |line| line.strip.upcase }
+ assert_equal __, upcase_lines
+ end
+
+ # NOTE: You can create your own collections that work with each,
+ # map, select, etc.
+ end
+
+ # Bonus Question: In the previous koan, we saw the construct:
+ #
+ # File.open(filename) do |file|
+ # # code to read 'file'
+ # end
+ #
+ # Why did we do it that way instead of the following?
+ #
+ # file = File.open(filename)
+ # # code to read 'file'
+ #
+ # When you get to the "AboutSandwichCode" koan, recheck your answer.
+
+end
diff --git a/koans/about_java_interop.rb b/koans/about_java_interop.rb
new file mode 100644
index 0000000..47fef28
--- /dev/null
+++ b/koans/about_java_interop.rb
@@ -0,0 +1,137 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+include Java
+
+# Concepts
+# * Pull in a java class
+# * calling a method, Camel vs snake
+# * Resolving module/class name conflicts
+# * Showing what gets returned
+# * Ruby Strings VS Java Strings
+# * Calling custom java class
+# * Calling Ruby from java???
+
+class AboutJavaInterop < Neo::Koan
+ def test_using_a_java_library_class
+ java_array = java.util.ArrayList.new
+ assert_equal __, java_array.class
+ end
+
+ def test_java_class_can_be_referenced_using_both_ruby_and_java_like_syntax
+ assert_equal __, Java::JavaUtil::ArrayList == java.util.ArrayList
+ end
+
+ def test_include_class_includes_class_in_module_scope
+ assert_nil defined?(TreeSet)
+ include_class "java.util.TreeSet"
+ assert_equal __, defined?(TreeSet)
+ end
+
+ # THINK ABOUT IT:
+ #
+ # What if we use:
+ #
+ # include_class "java.lang.String"
+ #
+ # What would be the value of the String constant after this
+ # include_class is run? Would it be useful to provide a way of
+ # aliasing java classes to different names?
+
+ JString = java.lang.String
+ def test_also_java_class_can_be_given_ruby_aliases
+ java_string = JString.new("A Java String")
+ assert_equal __, java_string.class
+ assert_equal __, JString
+ end
+
+ def test_can_directly_call_java_methods_on_java_objects
+ java_string = JString.new("A Java String")
+ assert_equal __, java_string.toLowerCase
+ end
+
+ def test_jruby_provides_snake_case_versions_of_java_methods
+ java_string = JString.new("A Java String")
+ assert_equal __, java_string.to_lower_case
+ end
+
+ def test_jruby_provides_question_mark_versions_of_boolean_methods
+ java_string = JString.new("A Java String")
+ assert_equal __, java_string.endsWith("String")
+ assert_equal __, java_string.ends_with("String")
+ assert_equal __, java_string.ends_with?("String")
+ end
+
+ def test_java_string_are_not_ruby_strings
+ ruby_string = "A Java String"
+ java_string = java.lang.String.new(ruby_string)
+ assert_equal __, java_string.is_a?(java.lang.String)
+ assert_equal __, java_string.is_a?(String)
+ end
+
+ def test_java_strings_can_be_compared_to_ruby_strings_maybe
+ ruby_string = "A Java String"
+ java_string = java.lang.String.new(ruby_string)
+ assert_equal __, ruby_string == java_string
+ assert_equal __, java_string == ruby_string
+
+ # THINK ABOUT IT:
+ #
+ # Is there any possible way for this to be more wrong?
+ #
+ # SERIOUSLY, THINK ABOUT IT:
+ #
+ # Why do you suppose that Ruby and Java strings compare like that?
+ #
+ # ADVANCED THINK ABOUT IT:
+ #
+ # Is there a way to make Ruby/Java string comparisons commutative?
+ # How would you do it?
+ end
+
+ def test_however_most_methods_returning_strings_return_ruby_strings
+ java_array = java.util.ArrayList.new
+ assert_equal __, java_array.toString
+ assert_equal __, java_array.toString.is_a?(String)
+ assert_equal __, java_array.toString.is_a?(java.lang.String)
+ end
+
+ def test_some_ruby_objects_can_be_coerced_to_java
+ assert_equal __, "ruby string".to_java.class
+ assert_equal __, 1.to_java.class
+ assert_equal __, 9.32.to_java.class
+ assert_equal __, false.to_java.class
+ end
+
+ def test_some_ruby_objects_are_not_coerced_to_what_you_might_expect
+ assert_equal __, [].to_java.class == Java::JavaUtil::ArrayList
+ assert_equal __, {}.to_java.class == Java::JavaUtil::HashMap
+ assert_equal __, Object.new.to_java.class == Java::JavaLang::Object
+ end
+
+ def test_java_collections_are_enumerable
+ java_array = java.util.ArrayList.new
+ java_array << "one" << "two" << "three"
+ assert_equal __, java_array.map { |item| item.upcase }
+ end
+
+ # ------------------------------------------------------------------
+
+ # Open the Java ArrayList class and add a new method.
+ class Java::JavaUtil::ArrayList
+ def multiply_all
+ result = 1
+ each do |item|
+ result *= item
+ end
+ result
+ end
+ end
+
+ def test_java_class_are_open_from_ruby
+ java_array = java.util.ArrayList.new
+ java_array.add_all([1,2,3,4,5])
+
+ assert_equal __, java_array.multiply_all
+ end
+
+end
diff --git a/koans/about_keyword_arguments.rb b/koans/about_keyword_arguments.rb
new file mode 100644
index 0000000..740d0c0
--- /dev/null
+++ b/koans/about_keyword_arguments.rb
@@ -0,0 +1,31 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutKeywordArguments < Neo::Koan
+
+ def method_with_keyword_arguments(one: 1, two: 'two')
+ [one, two]
+ end
+
+ def test_keyword_arguments
+ assert_equal __, method_with_keyword_arguments.class
+ assert_equal __, method_with_keyword_arguments
+ assert_equal __, method_with_keyword_arguments(one: 'one')
+ assert_equal __, method_with_keyword_arguments(two: 2)
+ end
+
+ def method_with_keyword_arguments_with_mandatory_argument(one, two: 2, three: 3)
+ [one, two, three]
+ end
+
+ def test_keyword_arguments_with_wrong_number_of_arguments
+ exception = assert_raise (___) do
+ method_with_keyword_arguments_with_mandatory_argument
+ end
+ assert_match(/__/, exception.message)
+ end
+
+ # THINK ABOUT IT:
+ #
+ # Keyword arguments always have a default value, making them optional to the caller
+
+end
diff --git a/koans/about_message_passing.rb b/koans/about_message_passing.rb
new file mode 100644
index 0000000..c6d80a8
--- /dev/null
+++ b/koans/about_message_passing.rb
@@ -0,0 +1,185 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutMessagePassing < Neo::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)
+ 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
+
+ # NOTE:
+ #
+ # Both obj.msg and obj.send(:msg) sends the message named :msg to
+ # the object. We use "send" when the name of the message can vary
+ # dynamically (e.g. calculated at run time), but by far the most
+ # common way of sending a message is just to say: obj.msg.
+
+ # ------------------------------------------------------------------
+
+ class TypicalObject
+ end
+
+ def test_sending_undefined_messages_to_a_typical_object_results_in_errors
+ typical = TypicalObject.new
+
+ exception = 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?
+ #
+ # NOTE:
+ #
+ # In Ruby 1.8 the method_missing method is public and can be
+ # called as shown above. However, in Ruby 1.9 (and later versions)
+ # the method_missing method is private. We explicitly made it
+ # public in the testing framework so this example works in both
+ # versions of Ruby. Just keep in mind you can't call
+ # method_missing like that after Ruby 1.9 normally.
+ #
+ # Thanks. We now return you to your regularly scheduled Ruby
+ # Koans.
+ 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 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/koans/about_methods.rb b/koans/about_methods.rb
new file mode 100644
index 0000000..5678f22
--- /dev/null
+++ b/koans/about_methods.rb
@@ -0,0 +1,151 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+def my_global_method(a,b)
+ a + b
+end
+
+class AboutMethods < Neo::Koan
+
+ def test_calling_global_methods
+ assert_equal __, my_global_method(2,3)
+ end
+
+ def test_calling_global_methods_without_parentheses
+ 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_parentheses_are_ambiguous
+ eval "assert_equal 5, my_global_method 2, 3" # ENABLE CHECK
+ #
+ # 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 arguments 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_match(/__/, exception.message)
+
+ exception = assert_raise(___) do
+ my_global_method(1,2,3)
+ end
+ assert_match(/__/, 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.class
+ 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
+ :another_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_method_in_the_same_class(a, b)
+ a * b
+ end
+
+ def test_calling_methods_in_same_class
+ assert_equal __, my_method_in_the_same_class(3,4)
+ end
+
+ def test_calling_methods_in_same_class_with_explicit_receiver
+ assert_equal __, self.my_method_in_the_same_class(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/koans/about_modules.rb b/koans/about_modules.rb
new file mode 100644
index 0000000..38652df
--- /dev/null
+++ b/koans/about_modules.rb
@@ -0,0 +1,63 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutModules < Neo::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_available_in_the_object
+ fido = Dog.new
+ assert_nothing_raised 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/koans/about_nil.rb b/koans/about_nil.rb
new file mode 100644
index 0000000..be1d5db
--- /dev/null
+++ b/koans/about_nil.rb
@@ -0,0 +1,38 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutNil < Neo::Koan
+ def test_nil_is_an_object
+ assert_equal __, nil.is_a?(Object), "Unlike NULL in other languages"
+ end
+
+ def test_you_dont_get_null_pointer_errors_when_calling_methods_on_nil
+ # What happens when you call a method that doesn't exist. The
+ # following begin/rescue/end code block captures the exception and
+ # makes some assertions about it.
+ begin
+ nil.some_method_nil_doesnt_know_about
+ rescue Exception => ex
+ # What exception has been caught?
+ assert_equal __, ex.class
+
+ # What message was attached to the exception?
+ # (HINT: replace __ with part of the error message.)
+ assert_match(/__/, ex.message)
+ end
+ 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/koans/about_objects.rb b/koans/about_objects.rb
new file mode 100644
index 0000000..514fcdc
--- /dev/null
+++ b/koans/about_objects.rb
@@ -0,0 +1,50 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutObjects < Neo::Koan
+ def test_everything_is_an_object
+ assert_equal __, 1.is_a?(Object)
+ assert_equal __, 1.5.is_a?(Object)
+ assert_equal __, "string".is_a?(Object)
+ assert_equal __, nil.is_a?(Object)
+ assert_equal __, Object.is_a?(Object)
+ end
+
+ def test_objects_can_be_converted_to_strings
+ assert_equal __, 123.to_s
+ assert_equal __, nil.to_s
+ end
+
+ def test_objects_can_be_inspected
+ assert_equal __, 123.inspect
+ assert_equal __, nil.inspect
+ end
+
+ def test_every_object_has_an_id
+ obj = Object.new
+ assert_equal __, obj.object_id.class
+ end
+
+ def test_every_object_has_different_id
+ obj = Object.new
+ another_obj = Object.new
+ assert_equal __, obj.object_id != another_obj.object_id
+ end
+
+ def test_small_integers_have_fixed_ids
+ assert_equal __, 0.object_id
+ assert_equal __, 1.object_id
+ assert_equal __, 2.object_id
+ assert_equal __, 100.object_id
+
+ # THINK ABOUT IT:
+ # What pattern do the object IDs for small integers follow?
+ end
+
+ def test_clone_creates_a_different_object
+ obj = Object.new
+ copy = obj.clone
+
+ assert_equal __, obj != copy
+ assert_equal __, obj.object_id != copy.object_id
+ end
+end
diff --git a/koans/about_open_classes.rb b/koans/about_open_classes.rb
new file mode 100644
index 0000000..56d1817
--- /dev/null
+++ b/koans/about_open_classes.rb
@@ -0,0 +1,45 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutOpenClasses < Neo::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/koans/about_proxy_object_project.rb b/koans/about_proxy_object_project.rb
new file mode 100644
index 0000000..36d1cf0
--- /dev/null
+++ b/koans/about_proxy_object_project.rb
@@ -0,0 +1,156 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+# 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 sent.
+#
+# 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
+ # ADD MORE CODE HERE
+ end
+
+ # WRITE CODE HERE
+end
+
+# The proxy object should pass the following Koan:
+#
+class AboutProxyObjectProject < Neo::Koan
+ def test_proxy_method_returns_wrapped_object
+ # NOTE: The Television class is defined below
+ tv = Proxy.new(Television.new)
+
+ # HINT: Proxy class is defined above, may need tweaking...
+
+ 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 < Neo::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/koans/about_regular_expressions.rb b/koans/about_regular_expressions.rb
new file mode 100644
index 0000000..b1b47a2
--- /dev/null
+++ b/koans/about_regular_expressions.rb
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutRegularExpressions < Neo::Koan
+ def test_a_pattern_is_a_regular_expression
+ assert_equal __, /pattern/.class
+ end
+
+ def test_a_regexp_can_search_a_string_for_matching_content
+ assert_equal __, "some matching content"[/match/]
+ end
+
+ def test_a_failed_match_returns_nil
+ assert_equal __, "some matching content"[/missing/]
+ end
+
+ # ------------------------------------------------------------------
+
+ def test_question_mark_means_optional
+ assert_equal __, "abbcccddddeeeee"[/ab?/]
+ assert_equal __, "abbcccddddeeeee"[/az?/]
+ end
+
+ def test_plus_means_one_or_more
+ assert_equal __, "abbcccddddeeeee"[/bc+/]
+ end
+
+ def test_asterisk_means_zero_or_more
+ assert_equal __, "abbcccddddeeeee"[/ab*/]
+ assert_equal __, "abbcccddddeeeee"[/az*/]
+ assert_equal __, "abbcccddddeeeee"[/z*/]
+
+ # THINK ABOUT IT:
+ #
+ # When would * fail to match?
+ end
+
+ # THINK ABOUT IT:
+ #
+ # We say that the repetition operators above are "greedy."
+ #
+ # Why?
+
+ # ------------------------------------------------------------------
+
+ def test_the_left_most_match_wins
+ assert_equal __, "abbccc az"[/az*/]
+ end
+
+ # ------------------------------------------------------------------
+
+ def test_character_classes_give_options_for_a_character
+ animals = ["cat", "bat", "rat", "zat"]
+ assert_equal __, animals.select { |a| a[/[cbr]at/] }
+ end
+
+ def test_slash_d_is_a_shortcut_for_a_digit_character_class
+ assert_equal __, "the number is 42"[/[0123456789]+/]
+ assert_equal __, "the number is 42"[/\d+/]
+ end
+
+ def test_character_classes_can_include_ranges
+ assert_equal __, "the number is 42"[/[0-9]+/]
+ end
+
+ def test_slash_s_is_a_shortcut_for_a_whitespace_character_class
+ assert_equal __, "space: \t\n"[/\s+/]
+ end
+
+ def test_slash_w_is_a_shortcut_for_a_word_character_class
+ # NOTE: This is more like how a programmer might define a word.
+ assert_equal __, "variable_1 = 42"[/[a-zA-Z0-9_]+/]
+ assert_equal __, "variable_1 = 42"[/\w+/]
+ end
+
+ def test_period_is_a_shortcut_for_any_non_newline_character
+ assert_equal __, "abc\n123"[/a.+/]
+ end
+
+ def test_a_character_class_can_be_negated
+ assert_equal __, "the number is 42"[/[^0-9]+/]
+ end
+
+ def test_shortcut_character_classes_are_negated_with_capitals
+ assert_equal __, "the number is 42"[/\D+/]
+ assert_equal __, "space: \t\n"[/\S+/]
+ # ... a programmer would most likely do
+ assert_equal __, "variable_1 = 42"[/[^a-zA-Z0-9_]+/]
+ assert_equal __, "variable_1 = 42"[/\W+/]
+ end
+
+ # ------------------------------------------------------------------
+
+ def test_slash_a_anchors_to_the_start_of_the_string
+ assert_equal __, "start end"[/\Astart/]
+ assert_equal __, "start end"[/\Aend/]
+ end
+
+ def test_slash_z_anchors_to_the_end_of_the_string
+ assert_equal __, "start end"[/end\z/]
+ assert_equal __, "start end"[/start\z/]
+ end
+
+ def test_caret_anchors_to_the_start_of_lines
+ assert_equal __, "num 42\n2 lines"[/^\d+/]
+ end
+
+ def test_dollar_sign_anchors_to_the_end_of_lines
+ assert_equal __, "2 lines\nnum 42"[/\d+$/]
+ end
+
+ def test_slash_b_anchors_to_a_word_boundary
+ assert_equal __, "bovine vines"[/\bvine./]
+ end
+
+ # ------------------------------------------------------------------
+
+ def test_parentheses_group_contents
+ assert_equal __, "ahahaha"[/(ha)+/]
+ end
+
+ # ------------------------------------------------------------------
+
+ def test_parentheses_also_capture_matched_content_by_number
+ assert_equal __, "Gray, James"[/(\w+), (\w+)/, 1]
+ assert_equal __, "Gray, James"[/(\w+), (\w+)/, 2]
+ end
+
+ def test_variables_can_also_be_used_to_access_captures
+ assert_equal __, "Name: Gray, James"[/(\w+), (\w+)/]
+ assert_equal __, $1
+ assert_equal __, $2
+ end
+
+ # ------------------------------------------------------------------
+
+ def test_a_vertical_pipe_means_or
+ grays = /(James|Dana|Summer) Gray/
+ assert_equal __, "James Gray"[grays]
+ assert_equal __, "Summer Gray"[grays, 1]
+ assert_equal __, "Jim Gray"[grays, 1]
+ end
+
+ # THINK ABOUT IT:
+ #
+ # Explain the difference between a character class ([...]) and alternation (|).
+
+ # ------------------------------------------------------------------
+
+ def test_scan_is_like_find_all
+ assert_equal __, "one two-three".scan(/\w+/)
+ end
+
+ def test_sub_is_like_find_and_replace
+ assert_equal __, "one two-three".sub(/(t\w*)/) { $1[0, 1] }
+ end
+
+ def test_gsub_is_like_find_and_replace_all
+ assert_equal __, "one two-three".gsub(/(t\w*)/) { $1[0, 1] }
+ end
+end
diff --git a/koans/about_sandwich_code.rb b/koans/about_sandwich_code.rb
new file mode 100644
index 0000000..ca9c554
--- /dev/null
+++ b/koans/about_sandwich_code.rb
@@ -0,0 +1,106 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutSandwichCode < Neo::Koan
+
+ def count_lines(file_name)
+ file = open(file_name)
+ count = 0
+ while 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
+ # 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 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 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/koans/about_scope.rb b/koans/about_scope.rb
new file mode 100644
index 0000000..451e98b
--- /dev/null
+++ b/koans/about_scope.rb
@@ -0,0 +1,79 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutScope < Neo::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
+ 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_equal __, fido.class != rover.class
+ assert_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 Object.constants.size > _n_
+ end
+end
diff --git a/koans/about_scoring_project.rb b/koans/about_scoring_project.rb
new file mode 100644
index 0000000..bdc5dd1
--- /dev/null
+++ b/koans/about_scoring_project.rb
@@ -0,0 +1,77 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+# Greed is a dice game where you roll up to five dice to accumulate
+# points. The following "score" function will be used to 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 scoring 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 AboutScoringProject < Neo::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_multiple_1s_and_5s_is_the_sum_of_individual_scores
+ assert_equal 300, 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])
+ assert_equal 1100, score([1,1,1,1])
+ assert_equal 1200, score([1,1,1,1,1])
+ assert_equal 1150, score([1,1,1,5,1])
+ end
+
+end
diff --git a/koans/about_strings.rb b/koans/about_strings.rb
new file mode 100644
index 0000000..1e0ea49
--- /dev/null
+++ b/koans/about_strings.rb
@@ -0,0 +1,197 @@
+require File.expand_path(File.dirname(__FILE__) + '/neo')
+
+class AboutStrings < Neo::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.length
+ assert_equal __, long_string.lines.count
+ assert_equal __, long_string[0,1]
+ end
+
+ def test_here_documents_can_also_handle_multiple_lines
+ long_string = < 0, :black => 30, :red => 31,
+ :green => 32, :yellow => 33, :blue => 34,
+ :magenta => 35, :cyan => 36,
+ }
+
+ module_function
+
+ COLORS.each do |color, value|
+ module_eval "def #{color}(string); colorize(string, #{value}); end"
+ module_function color
+ end
+
+ def colorize(string, color_value)
+ if use_colors?
+ color(color_value) + string + color(COLORS[:clear])
+ else
+ string
+ end
+ end
+
+ def color(color_value)
+ "\e[#{color_value}m"
+ end
+
+ def use_colors?
+ return false if ENV['NO_COLOR']
+ if ENV['ANSI_COLOR'].nil?
+ if using_windows?
+ using_win32console
+ else
+ return true
+ end
+ else
+ ENV['ANSI_COLOR'] =~ /^(t|y)/i
+ end
+ end
+
+ def using_windows?
+ File::ALT_SEPARATOR
+ end
+
+ def using_win32console
+ defined? Win32::Console
+ end
+ end
+
+ module Assertions
+ FailedAssertionError = Class.new(StandardError)
+
+ def flunk(msg)
+ raise FailedAssertionError, msg
+ end
+
+ def assert(condition, msg=nil)
+ msg ||= "Failed assertion."
+ flunk(msg) unless condition
+ true
+ end
+
+ def assert_equal(expected, actual, msg=nil)
+ msg ||= "Expected #{expected.inspect} to equal #{actual.inspect}"
+ assert(expected == actual, msg)
+ end
+
+ def assert_not_equal(expected, actual, msg=nil)
+ msg ||= "Expected #{expected.inspect} to not equal #{actual.inspect}"
+ assert(expected != actual, msg)
+ end
+
+ def assert_nil(actual, msg=nil)
+ msg ||= "Expected #{actual.inspect} to be nil"
+ assert(nil == actual, msg)
+ end
+
+ def assert_not_nil(actual, msg=nil)
+ msg ||= "Expected #{actual.inspect} to not be nil"
+ assert(nil != actual, msg)
+ end
+
+ def assert_match(pattern, actual, msg=nil)
+ msg ||= "Expected #{actual.inspect} to match #{pattern.inspect}"
+ assert pattern =~ actual, msg
+ end
+
+ def assert_raise(exception)
+ begin
+ yield
+ rescue Exception => ex
+ expected = ex.is_a?(exception)
+ assert(expected, "Exception #{exception.inspect} expected, but #{ex.inspect} was raised")
+ return ex
+ end
+ flunk "Exception #{exception.inspect} expected, but nothing raised"
+ end
+
+ def assert_nothing_raised
+ begin
+ yield
+ rescue Exception => ex
+ flunk "Expected nothing to be raised, but exception #{exception.inspect} was raised"
+ end
+ end
+ end
+
+ class Sensei
+ attr_reader :failure, :failed_test, :pass_count
+
+ FailedAssertionError = Assertions::FailedAssertionError
+
+ def initialize
+ @pass_count = 0
+ @failure = nil
+ @failed_test = nil
+ @observations = []
+ end
+
+ PROGRESS_FILE_NAME = '.path_progress'
+
+ def add_progress(prog)
+ @_contents = nil
+ exists = File.exists?(PROGRESS_FILE_NAME)
+ File.open(PROGRESS_FILE_NAME,'a+') do |f|
+ f.print "#{',' if exists}#{prog}"
+ end
+ end
+
+ def progress
+ if @_contents.nil?
+ if File.exists?(PROGRESS_FILE_NAME)
+ File.open(PROGRESS_FILE_NAME,'r') do |f|
+ @_contents = f.read.to_s.gsub(/\s/,'').split(',')
+ end
+ else
+ @_contents = []
+ end
+ end
+ @_contents
+ end
+
+ def observe(step)
+ if step.passed?
+ @pass_count += 1
+ if @pass_count > progress.last.to_i
+ @observations << Color.green("#{step.koan_file}##{step.name} has expanded your awareness.")
+ end
+ else
+ @failed_test = step
+ @failure = step.failure
+ add_progress(@pass_count)
+ @observations << Color.red("#{step.koan_file}##{step.name} has damaged your karma.")
+ throw :neo_exit
+ end
+ end
+
+ def failed?
+ ! @failure.nil?
+ end
+
+ def assert_failed?
+ failure.is_a?(FailedAssertionError)
+ end
+
+ def instruct
+ if failed?
+ @observations.each{|c| puts c }
+ encourage
+ guide_through_error
+ a_zenlike_statement
+ show_progress
+ else
+ end_screen
+ end
+ end
+
+ def show_progress
+ bar_width = 50
+ total_tests = Neo::Koan.total_tests
+ scale = bar_width.to_f/total_tests
+ print Color.green("your path thus far [")
+ happy_steps = (pass_count*scale).to_i
+ happy_steps = 1 if happy_steps == 0 && pass_count > 0
+ print Color.green('.'*happy_steps)
+ if failed?
+ print Color.red('X')
+ print Color.cyan('_'*(bar_width-1-happy_steps))
+ end
+ print Color.green(']')
+ print " #{pass_count}/#{total_tests}"
+ puts
+ end
+
+ def end_screen
+ if Neo.simple_output
+ boring_end_screen
+ else
+ artistic_end_screen
+ end
+ end
+
+ def boring_end_screen
+ puts "Mountains are again merely mountains"
+ end
+
+ def artistic_end_screen
+ "JRuby 1.9.x Koans"
+ ruby_version = "(in #{'J' if defined?(JRUBY_VERSION)}Ruby #{defined?(JRUBY_VERSION) ? JRUBY_VERSION : RUBY_VERSION})"
+ ruby_version = ruby_version.side_padding(54)
+ completed = <<-ENDTEXT
+ ,, , ,,
+ : ::::, :::,
+ , ,,: :::::::::::::,, :::: : ,
+ , ,,, ,:::::::::::::::::::, ,: ,: ,,
+ :, ::, , , :, ,::::::::::::::::::, ::: ,::::
+ : : ::, ,:::::::: ::, ,::::
+ , ,::::: :,:::::::,::::,
+ ,: , ,:,,: :::::::::::::
+ ::,: ,,:::, ,::::::::::::,
+ ,:::, :,,::: ::::::::::::,
+ ,::: :::::::, Mountains are again merely mountains ,::::::::::::
+ :::,,,:::::: ::::::::::::
+ ,:::::::::::, ::::::::::::,
+ :::::::::::, ,::::::::::::
+::::::::::::: ,::::::::::::
+:::::::::::: Ruby Koans ::::::::::::
+::::::::::::#{ ruby_version },::::::::::::
+:::::::::::, , :::::::::::
+,:::::::::::::, brought to you by ,,::::::::::::
+:::::::::::::: ,::::::::::::
+ ::::::::::::::, ,:::::::::::::
+ ::::::::::::, Neo Software Artisans , ::::::::::::
+ :,::::::::: :::: :::::::::::::
+ ,::::::::::: ,: ,,:::::::::::::,
+ :::::::::::: ,::::::::::::::,
+ :::::::::::::::::, ::::::::::::::::
+ :::::::::::::::::::, ::::::::::::::::
+ ::::::::::::::::::::::, ,::::,:, , ::::,:::
+ :::::::::::::::::::::::, ::,: ::,::, ,,: ::::
+ ,:::::::::::::::::::: ::,, , ,, ,::::
+ ,:::::::::::::::: ::,, , ,:::,
+ ,:::: , ,,
+ ,,,
+ENDTEXT
+ puts completed
+ end
+
+ def encourage
+ puts
+ puts "The Master says:"
+ puts Color.cyan(" You have not yet reached enlightenment.")
+ if ((recents = progress.last(5)) && recents.size == 5 && recents.uniq.size == 1)
+ puts Color.cyan(" I sense frustration. Do not be afraid to ask for help.")
+ elsif progress.last(2).size == 2 && progress.last(2).uniq.size == 1
+ puts Color.cyan(" Do not lose hope.")
+ elsif progress.last.to_i > 0
+ puts Color.cyan(" You are progressing. Excellent. #{progress.last} completed.")
+ end
+ end
+
+ def guide_through_error
+ puts
+ puts "The answers you seek..."
+ puts Color.red(indent(failure.message).join)
+ puts
+ puts "Please meditate on the following code:"
+ puts embolden_first_line_only(indent(find_interesting_lines(failure.backtrace)))
+ puts
+ end
+
+ def embolden_first_line_only(text)
+ first_line = true
+ text.collect { |t|
+ if first_line
+ first_line = false
+ Color.red(t)
+ else
+ Color.cyan(t)
+ end
+ }
+ end
+
+ def indent(text)
+ text = text.split(/\n/) if text.is_a?(String)
+ text.collect{|t| " #{t}"}
+ end
+
+ def find_interesting_lines(backtrace)
+ backtrace.reject { |line|
+ line =~ /neo\.rb/
+ }
+ end
+
+ # Hat's tip to Ara T. Howard for the zen statements from his
+ # metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
+ def a_zenlike_statement
+ if !failed?
+ zen_statement = "Mountains are again merely mountains"
+ else
+ zen_statement = case (@pass_count % 10)
+ when 0
+ "mountains are merely mountains"
+ when 1, 2
+ "learn the rules so you know how to break them properly"
+ when 3, 4
+ "remember that silence is sometimes the best answer"
+ when 5, 6
+ "sleep is the best meditation"
+ when 7, 8
+ "when you lose, don't lose the lesson"
+ else
+ "things are not what they appear to be: nor are they otherwise"
+ end
+ end
+ puts Color.green(zen_statement)
+ end
+ end
+
+ class Koan
+ include Assertions
+
+ attr_reader :name, :failure, :koan_count, :step_count, :koan_file
+
+ def initialize(name, koan_file=nil, koan_count=0, step_count=0)
+ @name = name
+ @failure = nil
+ @koan_count = koan_count
+ @step_count = step_count
+ @koan_file = koan_file
+ end
+
+ def passed?
+ @failure.nil?
+ end
+
+ def failed(failure)
+ @failure = failure
+ end
+
+ def setup
+ end
+
+ def teardown
+ end
+
+ def meditate
+ setup
+ begin
+ send(name)
+ rescue StandardError, Neo::Sensei::FailedAssertionError => ex
+ failed(ex)
+ ensure
+ begin
+ teardown
+ rescue StandardError, Neo::Sensei::FailedAssertionError => ex
+ failed(ex) if passed?
+ end
+ end
+ self
+ end
+
+ # Class methods for the Neo test suite.
+ class << self
+ def inherited(subclass)
+ subclasses << subclass
+ end
+
+ def method_added(name)
+ testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
+ 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
+
+ def total_tests
+ self.subclasses.inject(0){|total, k| total + k.testmethods.size }
+ end
+ end
+ end
+
+ class ThePath
+ def walk
+ sensei = Neo::Sensei.new
+ each_step do |step|
+ sensei.observe(step.meditate)
+ end
+ sensei.instruct
+ end
+
+ def each_step
+ catch(:neo_exit) {
+ step_count = 0
+ Neo::Koan.subclasses.each_with_index do |koan,koan_index|
+ koan.testmethods.each do |method_name|
+ step = koan.new(method_name, koan.to_s, koan_index+1, step_count+=1)
+ yield step
+ end
+ end
+ }
+ end
+ end
+end
+
+END {
+ Neo::Koan.command_line(ARGV)
+ Neo::ThePath.new.walk
+}
diff --git a/koans/path_to_enlightenment.rb b/koans/path_to_enlightenment.rb
new file mode 100644
index 0000000..9e8ccbe
--- /dev/null
+++ b/koans/path_to_enlightenment.rb
@@ -0,0 +1,41 @@
+# The path to Ruby Enlightenment starts with the following:
+
+$LOAD_PATH << File.dirname(__FILE__)
+
+require 'about_asserts'
+require 'about_nil'
+require 'about_objects'
+require 'about_arrays'
+require 'about_array_assignment'
+require 'about_hashes'
+require 'about_strings'
+require 'about_symbols'
+require 'about_regular_expressions'
+require 'about_methods'
+in_ruby_version("2") do
+ require 'about_keyword_arguments'
+end
+require 'about_constants'
+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_open_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_to_str'
+in_ruby_version("jruby") do
+ require 'about_java_interop'
+end
+require 'about_extra_credit'
diff --git a/koans/triangle.rb b/koans/triangle.rb
new file mode 100644
index 0000000..8df052a
--- /dev/null
+++ b/koans/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
diff --git a/koans/versioning_spec.rb b/koans/versioning_spec.rb
new file mode 100644
index 0000000..44cc763
--- /dev/null
+++ b/koans/versioning_spec.rb
@@ -0,0 +1,29 @@
+
+def version_ints(version)
+ version.split(".").map { |v| v.to_i }
+end
+
+def at_least_ruby_version(version)
+ vints = version_ints(version)
+ ruby_vints = version_ints(RUBY_VERSION)
+ vints.zip(ruby_vints).all? { |v, rv| v.nil? || rv.nil? || v >= rv }
+end
+
+
+require 'rspec/given'
+
+describe "#version_ints" do
+ Then { version_ints("1.2") == [1, 2] }
+ Then { version_ints("2.1.20") == [2, 1, 20] }
+end
+
+describe "at_least_ruby_version" do
+ Then { at_least_ruby_version("2") }
+ Then { at_least_ruby_version("2.0") }
+ Then { at_least_ruby_version("2.0.1") }
+ Then { at_least_ruby_version("2.1") }
+ Then { at_least_ruby_version("2.1.3.4.1") }
+
+ Then { ! at_least_ruby_version("1.9") }
+ Then { ! at_least_ruby_version("1.9.9.9.9") }
+end