mirror of
https://github.com/edgecase/ruby_koans.git
synced 2026-04-15 07:23:19 -04:00
Compare commits
87 Commits
codemash-2
...
more_jruby
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e26666280c | ||
|
|
e5843f64fd | ||
|
|
fdb8774c95 | ||
|
|
5b483df29b | ||
|
|
7dee146a8c | ||
|
|
e24d94eeff | ||
|
|
4789e831df | ||
|
|
b41d6167b4 | ||
|
|
c0bbe773d9 | ||
|
|
6fb8e3c3af | ||
|
|
1dcd9babd4 | ||
|
|
45523c03c0 | ||
|
|
8436b46c1a | ||
|
|
322ca38767 | ||
|
|
91f15dc690 | ||
|
|
beb7fe591e | ||
|
|
17fb071814 | ||
|
|
012cb20bb3 | ||
|
|
ece35b2539 | ||
|
|
fe2ee86172 | ||
|
|
9caf7a950a | ||
|
|
a13f184ef1 | ||
|
|
584b26e532 | ||
|
|
b2c47e0c0f | ||
|
|
f56117c0ca | ||
|
|
15551eaf53 | ||
|
|
b4e907e30e | ||
|
|
754a7694ad | ||
|
|
1492d7003a | ||
|
|
493300b24d | ||
|
|
1bc1b8e50c | ||
|
|
9b5dfb4e42 | ||
|
|
3ed32b4534 | ||
|
|
aa09d17630 | ||
|
|
a36c3b7a4d | ||
|
|
18cccf33fb | ||
|
|
1597b2f912 | ||
|
|
57f0f4f178 | ||
|
|
0794235441 | ||
|
|
ad99c372c3 | ||
|
|
8d8287365b | ||
|
|
0c18e9742f | ||
|
|
28eec8cc41 | ||
|
|
b0e34a90e0 | ||
|
|
b0a01786f4 | ||
|
|
ed2ef0f439 | ||
|
|
1f6c0e03a5 | ||
|
|
06c47eb284 | ||
|
|
bc1a20a310 | ||
|
|
e6fc8f6434 | ||
|
|
36f93c6c63 | ||
|
|
bc55853983 | ||
|
|
d17cc7b457 | ||
|
|
f88e9f1dfc | ||
|
|
05d5ab9a5e | ||
|
|
1b31a5b4dc | ||
|
|
3aa75e86aa | ||
|
|
9c0ccd53a2 | ||
|
|
9a459a7eb3 | ||
|
|
e1ada6ca9f | ||
|
|
a5180d9ea1 | ||
|
|
7db19b5e92 | ||
|
|
aa21cae64b | ||
|
|
7c52931392 | ||
|
|
028e4a0c75 | ||
|
|
eee3429394 | ||
|
|
6365c8d61d | ||
|
|
f2ac27f52e | ||
|
|
c4549eb24b | ||
|
|
ee9a03740b | ||
|
|
8ef93f1d3a | ||
|
|
7046294343 | ||
|
|
e8456fa321 | ||
|
|
138bc7c9bc | ||
|
|
2c3b65ec79 | ||
|
|
fbd36f6b0d | ||
|
|
56206e0a41 | ||
|
|
308ad5d9c5 | ||
|
|
c40424e9a4 | ||
|
|
f0b1560f34 | ||
|
|
3b824d0e86 | ||
|
|
110ff38b3f | ||
|
|
1d1ab85777 | ||
|
|
d4c9150453 | ||
|
|
ec6458e174 | ||
|
|
6c69bcb0ef | ||
|
|
6052c776bc |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
dist
|
||||
.project_env.rc
|
||||
.path_progress
|
||||
|
||||
21
Rakefile
21
Rakefile
@@ -11,23 +11,32 @@ DIST_DIR = 'dist'
|
||||
SRC_FILES = FileList["#{SRC_DIR}/*"]
|
||||
KOAN_FILES = SRC_FILES.pathmap("#{PROB_DIR}/%f")
|
||||
|
||||
TAR_FILE = "#{DIST_DIR}/rubykoans.tgz"
|
||||
ZIP_FILE = "#{DIST_DIR}/rubykoans.zip"
|
||||
today = Time.now.strftime("%Y-%m-%d")
|
||||
TAR_FILE = "#{DIST_DIR}/rubykoans-#{today}.tgz"
|
||||
ZIP_FILE = "#{DIST_DIR}/rubykoans-#{today}.zip"
|
||||
|
||||
CLOBBER.include(DIST_DIR)
|
||||
|
||||
module Koans
|
||||
# Remove solution info from source
|
||||
# __(a,b) => __
|
||||
# _n_(number) => __
|
||||
# # __ =>
|
||||
def Koans.remove_solution(line)
|
||||
line = line.gsub(/\b____\([^\)]+\)/, "____")
|
||||
line = line.gsub(/\b___\([^\)]+\)/, "___")
|
||||
line = line.gsub(/\b__\([^\)]+\)/, "__")
|
||||
line = line.gsub(/\b_n_\([^\)]+\)/, "_n_")
|
||||
line = line.gsub(%r(/\#\{__\}/), "/__/")
|
||||
line = line.gsub(/\s*#\s*__\s*$/, '')
|
||||
line
|
||||
end
|
||||
|
||||
def Koans.make_koan_file(infile, outfile)
|
||||
if infile =~ /edgecase/
|
||||
cp infile, outfile
|
||||
elsif infile =~ /autotest/
|
||||
cp_r infile, outfile
|
||||
else
|
||||
open(infile) do |ins|
|
||||
open(outfile, "w") do |outs|
|
||||
@@ -80,14 +89,6 @@ task :upload => [TAR_FILE, ZIP_FILE] do
|
||||
sh "scp #{ZIP_FILE} linode:sites/onestepback.org/download"
|
||||
end
|
||||
|
||||
desc "Check that the require files match the about_* files"
|
||||
task :check do
|
||||
about_files = Dir['src/about_*.rb'].size
|
||||
about_requires = `grep require src/path_to_enlightenment.rb | wc -l`.to_i
|
||||
puts "# of about files: #{about_files}"
|
||||
puts "# of about requires: #{about_requires}"
|
||||
end
|
||||
|
||||
desc "Generate the Koans from the source files from scratch."
|
||||
task :regen => [:clobber_koans, :gen]
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutArrayAssignment < EdgeCase::Koan
|
||||
def test_non_parallel_assignment
|
||||
@@ -18,13 +18,19 @@ class AboutArrayAssignment < EdgeCase::Koan
|
||||
assert_equal __, last_name
|
||||
end
|
||||
|
||||
def test_parallel_assignments_with_extra_variables
|
||||
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_assignements_with_subarrays
|
||||
def test_parallel_assignments_with_subarrays
|
||||
first_name, last_name = [["Willie", "Rae"], "Johnson"]
|
||||
assert_equal __, first_name
|
||||
assert_equal __, last_name
|
||||
@@ -35,4 +41,11 @@ class AboutArrayAssignment < EdgeCase::Koan
|
||||
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
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutArrays < EdgeCase::Koan
|
||||
def test_creating_arrays
|
||||
empty_array = Array.new
|
||||
assert_equal Array, empty_array.class
|
||||
assert_equal __, empty_array.class
|
||||
assert_equal __, empty_array.size
|
||||
end
|
||||
|
||||
@@ -45,9 +45,9 @@ class AboutArrays < EdgeCase::Koan
|
||||
end
|
||||
|
||||
def test_arrays_and_ranges
|
||||
assert_equal Range, (1..5).class
|
||||
assert_equal __, (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
|
||||
assert_equal __, (1...5).to_a
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
# -*- ruby -*-
|
||||
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutAsserts < EdgeCase::Koan
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutBlocks < EdgeCase::Koan
|
||||
def method_with_block
|
||||
@@ -60,7 +60,7 @@ class AboutBlocks < EdgeCase::Koan
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_block_can_effect_variables_in_the_code_where_they_are_created
|
||||
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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutClassMethods < EdgeCase::Koan
|
||||
class Dog
|
||||
@@ -19,11 +19,11 @@ class AboutClassMethods < EdgeCase::Koan
|
||||
|
||||
def test_objects_have_methods
|
||||
fido = Dog.new
|
||||
assert_equal __, fido.methods.size
|
||||
assert fido.methods.size > _n_
|
||||
end
|
||||
|
||||
def test_classes_have_methods
|
||||
assert_equal __, Dog.methods.size
|
||||
assert Dog.methods.size > _n_
|
||||
end
|
||||
|
||||
def test_you_can_define_methods_on_individual_objects
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutClasses < EdgeCase::Koan
|
||||
class Dog
|
||||
@@ -130,7 +130,7 @@ class AboutClasses < EdgeCase::Koan
|
||||
fido = Dog6.new("Fido")
|
||||
rover = Dog6.new("Rover")
|
||||
|
||||
assert_not_equal rover.name, fido.name
|
||||
assert_equal __, rover.name != fido.name
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -164,12 +164,12 @@ class AboutClasses < EdgeCase::Koan
|
||||
|
||||
def test_to_s_provides_a_string_version_of_the_object
|
||||
fido = Dog7.new("Fido")
|
||||
assert_equal "Fido", fido.to_s
|
||||
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", "My dog is #{fido}"
|
||||
assert_equal __, "My dog is #{fido}"
|
||||
end
|
||||
|
||||
def test_inspect_provides_a_more_complete_string_version
|
||||
|
||||
87
koans/about_constants.rb
Normal file
87
koans/about_constants.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
C = "top level"
|
||||
|
||||
class AboutConstants < EdgeCase::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 heirarachy?
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
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 heirarachy? Why is it
|
||||
# different than the previous answer?
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutControlStatements < EdgeCase::Koan
|
||||
|
||||
@@ -11,7 +11,7 @@ class AboutControlStatements < EdgeCase::Koan
|
||||
assert_equal __, result
|
||||
end
|
||||
|
||||
def test_if_then_else_statements
|
||||
def test_if_then_statements
|
||||
result = :default_value
|
||||
if true
|
||||
result = :true_value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class DiceSet
|
||||
attr_reader :values
|
||||
@@ -7,7 +7,7 @@ class DiceSet
|
||||
end
|
||||
end
|
||||
|
||||
class AboutDiceSet < EdgeCase::Koan
|
||||
class AboutDiceProject < EdgeCase::Koan
|
||||
def test_can_create_a_dice_set
|
||||
dice = DiceSet.new
|
||||
assert_not_nil dice
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutExceptions < EdgeCase::Koan
|
||||
|
||||
@@ -22,8 +22,8 @@ class AboutExceptions < EdgeCase::Koan
|
||||
|
||||
assert_equal __, result
|
||||
|
||||
assert ex.is_a?(StandardError), "Failure message."
|
||||
assert ex.is_a?(RuntimeError), "Failure message."
|
||||
assert ex.is_a?(___), "Failure message."
|
||||
assert ex.is_a?(___), "Failure message."
|
||||
|
||||
assert RuntimeError.ancestors.include?(StandardError),
|
||||
"RuntimeError is a subclass of StandardError"
|
||||
@@ -57,4 +57,12 @@ class AboutExceptions < EdgeCase::Koan
|
||||
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
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutHashes < EdgeCase::Koan
|
||||
def test_creating_hashes
|
||||
empty_hash = Hash.new
|
||||
assert_equal Hash, empty_hash.class
|
||||
assert_equal __, empty_hash.class
|
||||
assert_equal({}, empty_hash)
|
||||
assert_equal __, empty_hash.size
|
||||
end
|
||||
@@ -25,7 +25,7 @@ class AboutHashes < EdgeCase::Koan
|
||||
hash[:one] = "eins"
|
||||
|
||||
expected = { :one => __, :two => "dos" }
|
||||
assert_equal expected, hash
|
||||
assert_equal __, expected == hash
|
||||
|
||||
# Bonus Question: Why was "expected" broken out into a variable
|
||||
# rather than used as a literal?
|
||||
@@ -35,7 +35,7 @@ class AboutHashes < EdgeCase::Koan
|
||||
hash1 = { :one => "uno", :two => "dos" }
|
||||
hash2 = { :two => "dos", :one => "uno" }
|
||||
|
||||
assert_equal hash1, hash2
|
||||
assert_equal __, hash1 == hash2
|
||||
end
|
||||
|
||||
def test_hash_keys
|
||||
@@ -43,24 +43,24 @@ class AboutHashes < EdgeCase::Koan
|
||||
assert_equal __, hash.keys.size
|
||||
assert_equal __, hash.keys.include?(:one)
|
||||
assert_equal __, hash.keys.include?(:two)
|
||||
assert_equal Array, hash.keys.class
|
||||
assert_equal __, hash.keys.class
|
||||
end
|
||||
|
||||
def test_hash_values
|
||||
hash = { :one => "uno", :two => "dos" }
|
||||
assert_equal __, hash.keys.size
|
||||
assert_equal __, hash.values.size
|
||||
assert_equal __, hash.values.include?("uno")
|
||||
assert_equal __, hash.values.include?("dos")
|
||||
assert_equal Array, hash.values.class
|
||||
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_not_equal hash, new_hash
|
||||
assert_equal __, hash != new_hash
|
||||
|
||||
expected = { "jim" => __, "amy" => 20, "dan" => 23, "jenny" => __ }
|
||||
assert_equal expected, new_hash
|
||||
assert_equal __, expected == new_hash
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutInheritance < EdgeCase::Koan
|
||||
class Dog
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutIteration < EdgeCase::Koan
|
||||
|
||||
@@ -12,7 +12,7 @@ class AboutIteration < EdgeCase::Koan
|
||||
array.each do |item|
|
||||
sum += item
|
||||
end
|
||||
assert_equal 6, sum
|
||||
assert_equal __, sum
|
||||
end
|
||||
|
||||
def test_each_can_use_curly_brace_blocks_too
|
||||
@@ -78,16 +78,26 @@ class AboutIteration < EdgeCase::Koan
|
||||
assert_equal __, result
|
||||
|
||||
# Files act like a collection of lines
|
||||
file = File.open("example_file.txt")
|
||||
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.
|
||||
ensure
|
||||
# Arg, this is ugly.
|
||||
# We will figure out how to fix this later.
|
||||
file.close if file
|
||||
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
|
||||
|
||||
139
koans/about_java_interop.rb
Normal file
139
koans/about_java_interop.rb
Normal file
@@ -0,0 +1,139 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
include Java
|
||||
|
||||
# Concepts
|
||||
# * Pull in a java class
|
||||
# * calling a method, Camel vs snake
|
||||
# * Resovling module/class name conflicts
|
||||
# * Showing what gets returned
|
||||
# * Ruby Strings VS Java Strings
|
||||
# * Calling custom java class
|
||||
# * Calling Ruby from java???
|
||||
|
||||
class AboutJavaInterop < EdgeCase::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_can_NOT_be_coerced_to_java
|
||||
[[], {}, Object.new].each do |ruby_object|
|
||||
assert_raise(___) do
|
||||
ruby_object.to_java_class
|
||||
end
|
||||
end
|
||||
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
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutMessagePassing < EdgeCase::Koan
|
||||
|
||||
@@ -49,7 +49,6 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
|
||||
class MessageCatcher
|
||||
def add_a_payload(*args)
|
||||
return :empty unless args
|
||||
args
|
||||
end
|
||||
end
|
||||
@@ -90,6 +89,18 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
#
|
||||
# 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 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 in Ruby
|
||||
# 1.9. normally.
|
||||
#
|
||||
# Thanks. We now return you to your regularly schedule Ruby
|
||||
# Koans.
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
def my_global_method(a,b)
|
||||
a + b
|
||||
@@ -10,14 +10,14 @@ class AboutMethods < EdgeCase::Koan
|
||||
assert_equal __, my_global_method(2,3)
|
||||
end
|
||||
|
||||
def test_calling_global_methods_without_parenthesis
|
||||
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_parenthesis_are_ambiguous
|
||||
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:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutModules < EdgeCase::Koan
|
||||
module Nameable
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutNil < EdgeCase::Koan
|
||||
def test_nil_is_an_object
|
||||
|
||||
56
koans/about_objects.rb
Normal file
56
koans/about_objects.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutObjects < EdgeCase::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_some_system_objects_always_have_the_same_id
|
||||
assert_equal __, false.object_id
|
||||
assert_equal __, true.object_id
|
||||
assert_equal __, nil.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
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutOpenClasses < EdgeCase::Koan
|
||||
class Dog
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
# Project: Create a Proxy Class
|
||||
#
|
||||
|
||||
159
koans/about_regular_expressions.rb
Normal file
159
koans/about_regular_expressions.rb
Normal file
@@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutRegularExpressions < EdgeCase::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+/]
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutUsingBlocks < EdgeCase::Koan
|
||||
class AboutSandwichCode < EdgeCase::Koan
|
||||
|
||||
def count_lines(file_name)
|
||||
file = open(file_name)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutScope < EdgeCase::Koan
|
||||
module Jims
|
||||
@@ -29,8 +29,8 @@ class AboutScope < EdgeCase::Koan
|
||||
assert_equal __, fido.identify
|
||||
assert_equal __, rover.identify
|
||||
|
||||
assert_not_equal fido.class, rover.class
|
||||
assert_not_equal Jims::Dog, Joes::Dog
|
||||
assert_equal __, fido.class != rover.class
|
||||
assert_equal __, Jims::Dog != Joes::Dog
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -74,6 +74,6 @@ class AboutScope < EdgeCase::Koan
|
||||
|
||||
def test_you_can_get_a_list_of_constants_for_any_class_or_module
|
||||
assert_equal __, Jims.constants
|
||||
assert_equal __, Object.constants.size
|
||||
assert Object.constants.size > _n_
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/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
|
||||
@@ -33,7 +33,7 @@ def score(dice)
|
||||
# You need to write this method
|
||||
end
|
||||
|
||||
class AboutScoringAssignment < EdgeCase::Koan
|
||||
class AboutScoringProject < EdgeCase::Koan
|
||||
def test_score_of_an_empty_list_is_zero
|
||||
assert_equal 0, score([])
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutStrings < EdgeCase::Koan
|
||||
def test_double_quoted_strings_are_strings
|
||||
@@ -146,12 +146,21 @@ EOS
|
||||
# Surprised?
|
||||
end
|
||||
|
||||
def test_single_characters_are_represented_by_integers
|
||||
in_ruby_version("1.8") do
|
||||
def test_in_ruby_1_8_single_characters_are_represented_by_integers
|
||||
assert_equal __, ?a
|
||||
assert_equal __, ?a == 97
|
||||
|
||||
assert_equal __, ?b == (?a + 1)
|
||||
end
|
||||
end
|
||||
|
||||
in_ruby_version("1.9") do
|
||||
def test_in_ruby_1_9_single_characters_are_represented_by_strings
|
||||
assert_equal __, ?a
|
||||
assert_equal __, ?a == 97
|
||||
end
|
||||
end
|
||||
|
||||
def test_strings_can_be_split
|
||||
string = "Sausage Egg Cheese"
|
||||
@@ -165,12 +174,20 @@ EOS
|
||||
assert_equal [__, __, __, __], words
|
||||
|
||||
# NOTE: Patterns are formed from Regular Expressions. Ruby has a
|
||||
# very powerful Regular Expression library. Unfortunately, time
|
||||
# does not permit us to explore it in detail in Ruby 101.
|
||||
# very powerful Regular Expression library. We will become
|
||||
# enlightened about them soon.
|
||||
end
|
||||
|
||||
def test_strings_can_be_joined
|
||||
words = ["Now", "is", "the", "time"]
|
||||
assert_equal __, words.join(" ")
|
||||
end
|
||||
|
||||
def test_strings_are_not_unique_objects
|
||||
a = "a string"
|
||||
b = "a string"
|
||||
|
||||
assert_equal __, a == b
|
||||
assert_equal __, a.object_id == b.object_id
|
||||
end
|
||||
end
|
||||
|
||||
100
koans/about_symbols.rb
Normal file
100
koans/about_symbols.rb
Normal file
@@ -0,0 +1,100 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutSymbols < EdgeCase::Koan
|
||||
def test_symbols_are_symbols
|
||||
symbol = :ruby
|
||||
assert_equal __, symbol.is_a?(Symbol)
|
||||
end
|
||||
|
||||
def test_symbols_can_be_compared
|
||||
symbol1 = :a_symbol
|
||||
symbol2 = :a_symbol
|
||||
symbol3 = :something_else
|
||||
|
||||
assert symbol1 == __
|
||||
assert symbol1 != __
|
||||
end
|
||||
|
||||
def test_identical_symbols_are_a_single_internal_object
|
||||
symbol1 = :a_symbol
|
||||
symbol2 = :a_symbol
|
||||
|
||||
assert_equal __, symbol1 == symbol2
|
||||
assert_equal __, symbol1.object_id == symbol2.object_id
|
||||
end
|
||||
|
||||
def test_method_names_become_symbols
|
||||
all_symbols = Symbol.all_symbols
|
||||
assert_equal __, all_symbols.include?(:test_method_names_become_symbols)
|
||||
end
|
||||
|
||||
# THINK ABOUT IT:
|
||||
#
|
||||
# Why do we capture the list of symbols before we check for the
|
||||
# method name?
|
||||
|
||||
in_ruby_version("mri") do
|
||||
RubyConstant = "What is the sound of one hand clapping?"
|
||||
def test_constants_become_symbols
|
||||
all_symbols = Symbol.all_symbols
|
||||
|
||||
assert_equal __, all_symbols.include?(__)
|
||||
end
|
||||
end
|
||||
|
||||
def test_symbols_can_be_made_from_strings
|
||||
string = "catsAndDogs"
|
||||
assert_equal __, string.to_sym
|
||||
end
|
||||
|
||||
def test_symbols_with_spaces_can_be_built
|
||||
symbol = :"cats and dogs"
|
||||
|
||||
assert_equal symbol, __.to_sym
|
||||
end
|
||||
|
||||
def test_symbols_with_spaces_can_be_built
|
||||
value = "and"
|
||||
symbol = :"cats #{value} dogs"
|
||||
|
||||
assert_equal symbol, __.to_sym
|
||||
end
|
||||
|
||||
def test_to_s_is_called_on_interpolated_symbols
|
||||
symbol = :cats
|
||||
string = "It is raining #{symbol} and dogs."
|
||||
|
||||
assert_equal __, string
|
||||
end
|
||||
|
||||
def test_symbols_are_not_strings
|
||||
symbol = :ruby
|
||||
assert_equal __, symbol.is_a?(String)
|
||||
assert_equal __, symbol.eql?("ruby")
|
||||
end
|
||||
|
||||
def test_symbols_do_not_have_string_methods
|
||||
symbol = :not_a_string
|
||||
assert_equal __, symbol.respond_to?(:each_char)
|
||||
assert_equal __, symbol.respond_to?(:reverse)
|
||||
end
|
||||
|
||||
# It's important to realize that symbols are not "immutable
|
||||
# strings", though they are immutable. None of the
|
||||
# interesting string operations are available on symbols.
|
||||
|
||||
def test_symbols_cannot_be_concatenated
|
||||
# Exceptions will be pondered further father down the path
|
||||
assert_raise(___) do
|
||||
:cats + :dogs
|
||||
end
|
||||
end
|
||||
|
||||
def test_symbols_can_be_dynamically_created
|
||||
assert_equal __, ("cats" + "dogs").to_sym
|
||||
end
|
||||
|
||||
# THINK ABOUT IT:
|
||||
#
|
||||
# Why is it not a good idea to dynamically create a lot of symbols?
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
# You need to write the triangle method in the file 'triangle.rb'
|
||||
require 'triangle.rb'
|
||||
|
||||
class AboutTriangleAssignment < EdgeCase::Koan
|
||||
class AboutTriangleProject < EdgeCase::Koan
|
||||
def test_equilateral_triangles_have_equal_sides
|
||||
assert_equal :equilateral, triangle(2, 2, 2)
|
||||
assert_equal :equilateral, triangle(10, 10, 10)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
# You need to write the triangle method in the file 'triangle.rb'
|
||||
require 'triangle.rb'
|
||||
|
||||
class AboutTriangleAssignment2 < EdgeCase::Koan
|
||||
class AboutTriangleProject2 < EdgeCase::Koan
|
||||
# The first assignment did not talk about how to handle errors.
|
||||
# Let's handle that part now.
|
||||
def test_illegal_triangles_throw_exceptions
|
||||
assert_raise(TriangleError) do triangle(0, 0, 0) end
|
||||
assert_raise(TriangleError) do triangle(3, 4, -5) end
|
||||
assert_raise(TriangleError) do triangle(1, 1, 3) end
|
||||
assert_raise(TriangleError) do triangle(2, 4, 2) end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutTrueAndFalse < EdgeCase::Koan
|
||||
def truth_value(condition)
|
||||
|
||||
3
koans/autotest/discover.rb
Normal file
3
koans/autotest/discover.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Autotest.add_discovery do
|
||||
"rubykoan" if File.exist? 'path_to_enlightenment.rb'
|
||||
end
|
||||
24
koans/autotest/rubykoan.rb
Normal file
24
koans/autotest/rubykoan.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
require 'autotest'
|
||||
|
||||
class Autotest::Rubykoan < Autotest
|
||||
def initialize
|
||||
super
|
||||
@exceptions = /\.txt|Rakefile|\.rdoc/
|
||||
|
||||
self.order = :alpha
|
||||
self.add_mapping(/^about_.*rb$/) do |filename, _|
|
||||
filename
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def make_test_cmd files_to_test
|
||||
"#{ruby} 'path_to_enlightenment.rb'"
|
||||
end
|
||||
|
||||
# quiet test/unit chatter
|
||||
def handle_results(results)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1 +1 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
@@ -3,45 +3,164 @@
|
||||
|
||||
require 'test/unit/assertions'
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Support code for the Ruby Koans.
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
class FillMeInError < StandardError
|
||||
end
|
||||
|
||||
def __(value="FILL ME IN")
|
||||
value
|
||||
def ruby_version?(version)
|
||||
RUBY_VERSION =~ /^#{version}/ ||
|
||||
(version == 'jruby' && defined?(JRUBY_VERSION)) ||
|
||||
(version == 'mri' && ! defined?(JRUBY_VERSION))
|
||||
end
|
||||
|
||||
def in_ruby_version(*versions)
|
||||
yield if versions.any? { |v| ruby_version?(v) }
|
||||
end
|
||||
|
||||
# Standard, generic replacement value.
|
||||
# If value19 is given, it is used inplace of value for Ruby 1.9.
|
||||
def __(value="FILL ME IN", value19=:mu)
|
||||
if RUBY_VERSION < "1.9"
|
||||
value
|
||||
else
|
||||
(value19 == :mu) ? value : value19
|
||||
end
|
||||
end
|
||||
|
||||
# Numeric replacement value.
|
||||
def _n_(value=999999, value19=:mu)
|
||||
if RUBY_VERSION < "1.9"
|
||||
value
|
||||
else
|
||||
(value19 == :mu) ? value : value19
|
||||
end
|
||||
end
|
||||
|
||||
# Error object replacement value.
|
||||
def ___(value=FillMeInError)
|
||||
value
|
||||
end
|
||||
|
||||
# Method name replacement.
|
||||
class Object
|
||||
def ____(method=nil)
|
||||
if method
|
||||
self.send(method)
|
||||
end
|
||||
end
|
||||
|
||||
in_ruby_version("1.9") do
|
||||
public :method_missing
|
||||
end
|
||||
end
|
||||
|
||||
class String
|
||||
def side_padding(width)
|
||||
extra = width - self.size
|
||||
if width < 0
|
||||
self
|
||||
else
|
||||
left_padding = extra / 2
|
||||
right_padding = (extra+1)/2
|
||||
(" " * left_padding) + self + (" " *right_padding)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module EdgeCase
|
||||
class Sensei
|
||||
attr_reader :failure, :failed_test
|
||||
class << self
|
||||
def simple_output
|
||||
ENV['SIMPLE_KOAN_OUTPUT'] == 'true'
|
||||
end
|
||||
end
|
||||
|
||||
module Color
|
||||
#shamelessly stolen (and modified) from redgreen
|
||||
COLORS = {
|
||||
:clear => 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 ENV['NO_COLOR']
|
||||
string
|
||||
else
|
||||
color(color_value) + string + color(COLORS[:clear])
|
||||
end
|
||||
end
|
||||
|
||||
def color(color_value)
|
||||
"\e[#{color_value}m"
|
||||
end
|
||||
end
|
||||
|
||||
class Sensei
|
||||
attr_reader :failure, :failed_test, :pass_count
|
||||
|
||||
in_ruby_version("1.8") do
|
||||
AssertionError = Test::Unit::AssertionFailedError
|
||||
end
|
||||
|
||||
in_ruby_version("1.9") do
|
||||
if defined?(MiniTest)
|
||||
AssertionError = MiniTest::Assertion
|
||||
else
|
||||
AssertionError = Test::Unit::AssertionFailedError
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@pass_count = 0
|
||||
@failure = nil
|
||||
@failed_test = nil
|
||||
@observations = []
|
||||
end
|
||||
|
||||
def accumulate(test)
|
||||
if test.passed?
|
||||
@pass_count += 1
|
||||
puts " #{test.name} has expanded your awareness."
|
||||
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
|
||||
puts " #{test.name} has damaged your karma."
|
||||
@failed_test = test
|
||||
@failure = test.failure
|
||||
@_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 :edgecase_exit
|
||||
end
|
||||
end
|
||||
@@ -54,62 +173,175 @@ module EdgeCase
|
||||
failure.is_a?(AssertionError)
|
||||
end
|
||||
|
||||
def report
|
||||
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 = EdgeCase::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
|
||||
puts "You have not yet reached enlightenment ..."
|
||||
puts failure.message
|
||||
end
|
||||
|
||||
def end_screen
|
||||
if EdgeCase.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 ,,::::::::::::,
|
||||
:::::::::::::: ,::::::::::::
|
||||
::::::::::::::, ,:::::::::::::
|
||||
::::::::::::, EdgeCase 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:"
|
||||
if assert_failed?
|
||||
puts find_interesting_lines(failure.backtrace)
|
||||
puts embolden_first_line_only(indent(find_interesting_lines(failure.backtrace)))
|
||||
else
|
||||
puts failure.backtrace
|
||||
puts embolden_first_line_only(indent(failure.backtrace))
|
||||
end
|
||||
puts
|
||||
end
|
||||
say_something_zenlike
|
||||
|
||||
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 =~ /test\/unit\/|edgecase\.rb/
|
||||
line =~ /test\/unit\/|edgecase\.rb|minitest/
|
||||
}
|
||||
end
|
||||
|
||||
# Hat's tip to Ara T. Howard for the zen statements from his
|
||||
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
|
||||
def say_something_zenlike
|
||||
puts
|
||||
def a_zenlike_statement
|
||||
if !failed?
|
||||
puts "Mountains are again merely mountains"
|
||||
zen_statement = "Mountains are again merely mountains"
|
||||
else
|
||||
case (@pass_count % 10)
|
||||
zen_statement = case (@pass_count % 10)
|
||||
when 0
|
||||
puts "mountains are merely mountains"
|
||||
"mountains are merely mountains"
|
||||
when 1, 2
|
||||
puts "learn the rules so you know how to break them properly"
|
||||
"learn the rules so you know how to break them properly"
|
||||
when 3, 4
|
||||
puts "remember that silence is sometimes the best answer"
|
||||
"remember that silence is sometimes the best answer"
|
||||
when 5, 6
|
||||
puts "sleep is the best meditation"
|
||||
"sleep is the best meditation"
|
||||
when 7, 8
|
||||
puts "when you lose, don't lose the lesson"
|
||||
"when you lose, don't lose the lesson"
|
||||
else
|
||||
puts "things are not what they appear to be: nor are they otherwise"
|
||||
"things are not what they appear to be: nor are they otherwise"
|
||||
end
|
||||
end
|
||||
puts Color.green(zen_statement)
|
||||
end
|
||||
end
|
||||
|
||||
class Koan
|
||||
include Test::Unit::Assertions
|
||||
|
||||
attr_reader :name, :failure
|
||||
attr_reader :name, :failure, :koan_count, :step_count, :koan_file
|
||||
|
||||
def initialize(name)
|
||||
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?
|
||||
@@ -126,6 +358,22 @@ module EdgeCase
|
||||
def teardown
|
||||
end
|
||||
|
||||
def meditate
|
||||
setup
|
||||
begin
|
||||
send(name)
|
||||
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
|
||||
failed(ex)
|
||||
ensure
|
||||
begin
|
||||
teardown
|
||||
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
|
||||
failed(ex) if passed?
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Class methods for the EdgeCase test suite.
|
||||
class << self
|
||||
def inherited(subclass)
|
||||
@@ -133,32 +381,7 @@ module EdgeCase
|
||||
end
|
||||
|
||||
def method_added(name)
|
||||
testmethods << name unless tests_disabled?
|
||||
end
|
||||
|
||||
def run_tests(accumulator)
|
||||
puts
|
||||
puts "Thinking #{self}"
|
||||
testmethods.each do |m|
|
||||
self.run_test(m, accumulator) if Koan.test_pattern =~ m.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def run_test(method, accumulator)
|
||||
test = self.new(method)
|
||||
test.setup
|
||||
begin
|
||||
test.send(method)
|
||||
rescue StandardError => ex
|
||||
test.failed(ex)
|
||||
ensure
|
||||
begin
|
||||
test.teardown
|
||||
rescue StandardError => ex
|
||||
test.failed(ex) if test.passed?
|
||||
end
|
||||
end
|
||||
accumulator.accumulate(test)
|
||||
testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
|
||||
end
|
||||
|
||||
def end_of_enlightenment
|
||||
@@ -200,17 +423,36 @@ module EdgeCase
|
||||
@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 = EdgeCase::Sensei.new
|
||||
each_step do |step|
|
||||
sensei.observe(step.meditate)
|
||||
end
|
||||
sensei.instruct
|
||||
end
|
||||
|
||||
def each_step
|
||||
catch(:edgecase_exit) {
|
||||
step_count = 0
|
||||
EdgeCase::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 {
|
||||
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
|
||||
EdgeCase::ThePath.new.walk
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
# 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'
|
||||
require 'about_constants'
|
||||
require 'about_control_statements'
|
||||
require 'about_true_and_false'
|
||||
require 'about_triangle_project'
|
||||
@@ -25,4 +31,7 @@ require 'about_scope'
|
||||
require 'about_class_methods'
|
||||
require 'about_message_passing'
|
||||
require 'about_proxy_object_project'
|
||||
in_ruby_version("jruby") do
|
||||
require 'about_java_interop'
|
||||
end
|
||||
require 'about_extra_credit'
|
||||
|
||||
33
rakelib/checks.rake
Normal file
33
rakelib/checks.rake
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace "check" do
|
||||
|
||||
desc "Check that the require files match the about_* files"
|
||||
task :abouts do
|
||||
about_files = Dir['src/about_*.rb'].size
|
||||
about_requires = `grep require src/path_to_enlightenment.rb | wc -l`.to_i
|
||||
puts "Checking path_to_enlightenment completeness"
|
||||
puts "# of about files: #{about_files}"
|
||||
puts "# of about requires: #{about_requires}"
|
||||
if about_files > about_requires
|
||||
puts "*** There seems to be requires missing in the path to enlightenment"
|
||||
else
|
||||
puts "OK"
|
||||
end
|
||||
puts
|
||||
end
|
||||
|
||||
desc "Check that asserts have __ replacements"
|
||||
task :asserts do
|
||||
puts "Checking for asserts missing the replacement text:"
|
||||
begin
|
||||
sh "egrep -n 'assert( |_)' src/about_* | egrep -v '__|_n_|project|about_assert' | egrep -v ' *#'"
|
||||
puts
|
||||
puts "Examine the above lines for missing __ replacements"
|
||||
rescue RuntimeError => ex
|
||||
puts "OK"
|
||||
end
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run some simple consistancy checks"
|
||||
task :check => ["check:abouts", "check:asserts"]
|
||||
8
rakelib/run.rake
Normal file
8
rakelib/run.rake
Normal file
@@ -0,0 +1,8 @@
|
||||
RUBIES = ENV['KOAN_RUBIES'] || %w(ruby-1.8.7-p299,ruby-1.9.2-p0,jruby-1.5.2,jruby-head)
|
||||
|
||||
task :runall do
|
||||
chdir('src') do
|
||||
ENV['SIMPLE_KOAN_OUTPUT'] = 'true'
|
||||
sh "rvm #{RUBIES} path_to_enlightenment.rb"
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutArrayAssignment < EdgeCase::Koan
|
||||
def test_non_parallel_assignment
|
||||
@@ -18,13 +18,19 @@ class AboutArrayAssignment < EdgeCase::Koan
|
||||
assert_equal __("Smith"), last_name
|
||||
end
|
||||
|
||||
def test_parallel_assignments_with_extra_variables
|
||||
def test_parallel_assignments_with_splat_operator
|
||||
first_name, *last_name = ["John", "Smith", "III"]
|
||||
assert_equal __("John"), first_name
|
||||
assert_equal __(["Smith","III"]), last_name
|
||||
end
|
||||
|
||||
def test_parallel_assignments_with_too_few_variables
|
||||
first_name, last_name = ["Cher"]
|
||||
assert_equal __("Cher"), first_name
|
||||
assert_equal __(nil), last_name
|
||||
end
|
||||
|
||||
def test_parallel_assignements_with_subarrays
|
||||
def test_parallel_assignments_with_subarrays
|
||||
first_name, last_name = [["Willie", "Rae"], "Johnson"]
|
||||
assert_equal __(["Willie", "Rae"]), first_name
|
||||
assert_equal __("Johnson"), last_name
|
||||
@@ -35,4 +41,11 @@ class AboutArrayAssignment < EdgeCase::Koan
|
||||
assert_equal __("John"), 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 __('Rob'), first_name
|
||||
assert_equal __('Roy'), last_name
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutArrays < EdgeCase::Koan
|
||||
def test_creating_arrays
|
||||
empty_array = Array.new
|
||||
assert_equal Array, empty_array.class
|
||||
assert_equal __(Array), empty_array.class
|
||||
assert_equal __(0), empty_array.size
|
||||
end
|
||||
|
||||
def test_array_literals
|
||||
array = Array.new
|
||||
assert_equal [], array
|
||||
assert_equal [], array # __
|
||||
|
||||
array[0] = 1
|
||||
assert_equal [1], array
|
||||
assert_equal [1], array # __
|
||||
|
||||
array[1] = 2
|
||||
assert_equal [1, __(2)], array
|
||||
@@ -45,9 +45,9 @@ class AboutArrays < EdgeCase::Koan
|
||||
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 __(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,2,3,4]), (1...5).to_a
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
# -*- ruby -*-
|
||||
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutAsserts < EdgeCase::Koan
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutBlocks < EdgeCase::Koan
|
||||
def method_with_block
|
||||
@@ -60,7 +60,7 @@ class AboutBlocks < EdgeCase::Koan
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_block_can_effect_variables_in_the_code_where_they_are_created
|
||||
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 __(:modified_in_a_block), value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutClassMethods < EdgeCase::Koan
|
||||
class Dog
|
||||
@@ -19,11 +19,11 @@ class AboutClassMethods < EdgeCase::Koan
|
||||
|
||||
def test_objects_have_methods
|
||||
fido = Dog.new
|
||||
assert_equal __(44), fido.methods.size
|
||||
assert fido.methods.size > _n_(30)
|
||||
end
|
||||
|
||||
def test_classes_have_methods
|
||||
assert_equal __(79), Dog.methods.size
|
||||
assert Dog.methods.size > _n_(40)
|
||||
end
|
||||
|
||||
def test_you_can_define_methods_on_individual_objects
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutClasses < EdgeCase::Koan
|
||||
class Dog
|
||||
@@ -22,7 +22,7 @@ class AboutClasses < EdgeCase::Koan
|
||||
assert_equal __([]), fido.instance_variables
|
||||
|
||||
fido.set_name("Fido")
|
||||
assert_equal __(["@name"]), fido.instance_variables
|
||||
assert_equal __(["@name"], [:@name]), fido.instance_variables
|
||||
end
|
||||
|
||||
def test_instance_variables_cannot_be_accessed_outside_the_class
|
||||
@@ -130,7 +130,7 @@ class AboutClasses < EdgeCase::Koan
|
||||
fido = Dog6.new("Fido")
|
||||
rover = Dog6.new("Rover")
|
||||
|
||||
assert_not_equal rover.name, fido.name
|
||||
assert_equal __(true), rover.name != fido.name
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -164,12 +164,12 @@ class AboutClasses < EdgeCase::Koan
|
||||
|
||||
def test_to_s_provides_a_string_version_of_the_object
|
||||
fido = Dog7.new("Fido")
|
||||
assert_equal "Fido", fido.to_s
|
||||
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}"
|
||||
assert_equal __("My dog is Fido"), "My dog is #{fido}"
|
||||
end
|
||||
|
||||
def test_inspect_provides_a_more_complete_string_version
|
||||
@@ -180,7 +180,7 @@ class AboutClasses < EdgeCase::Koan
|
||||
def test_all_objects_support_to_s_and_inspect
|
||||
array = [1,2,3]
|
||||
|
||||
assert_equal __("123"), array.to_s
|
||||
assert_equal __("123", "[1, 2, 3]"), array.to_s
|
||||
assert_equal __("[1, 2, 3]"), array.inspect
|
||||
|
||||
assert_equal __("STRING"), "STRING".to_s
|
||||
|
||||
87
src/about_constants.rb
Normal file
87
src/about_constants.rb
Normal file
@@ -0,0 +1,87 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
C = "top level"
|
||||
|
||||
class AboutConstants < EdgeCase::Koan
|
||||
|
||||
C = "nested"
|
||||
|
||||
def test_nested_constants_may_also_be_referenced_with_relative_paths
|
||||
assert_equal __("nested"), C
|
||||
end
|
||||
|
||||
def test_top_level_constants_are_referenced_by_double_colons
|
||||
assert_equal __("top level"), ::C
|
||||
end
|
||||
|
||||
def test_nested_constants_are_referenced_by_their_complete_path
|
||||
assert_equal __("nested"), AboutConstants::C
|
||||
assert_equal __("nested"), ::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 __(4), 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 __(4), 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 __(2), MyAnimals::Bird.new.legs_in_bird
|
||||
end
|
||||
|
||||
# QUESTION: Which has precedence: The constant in the lexical scope,
|
||||
# or the constant from the inheritance heirarachy?
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
class MyAnimals::Oyster < Animal
|
||||
def legs_in_oyster
|
||||
LEGS
|
||||
end
|
||||
end
|
||||
|
||||
def test_who_wins_with_explicit_scoping_on_class_definition
|
||||
assert_equal __(4), MyAnimals::Oyster.new.legs_in_oyster
|
||||
end
|
||||
|
||||
# QUESTION: Now Which has precedence: The constant in the lexical
|
||||
# scope, or the constant from the inheritance heirarachy? Why is it
|
||||
# different than the previous answer?
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutControlStatements < EdgeCase::Koan
|
||||
|
||||
@@ -8,10 +8,10 @@ class AboutControlStatements < EdgeCase::Koan
|
||||
else
|
||||
result = :false_value
|
||||
end
|
||||
assert_equal __, result
|
||||
assert_equal __(:true_value), result
|
||||
end
|
||||
|
||||
def test_if_then_else_statements
|
||||
def test_if_then_statements
|
||||
result = :default_value
|
||||
if true
|
||||
result = :true_value
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class DiceSet
|
||||
attr_reader :values
|
||||
@@ -7,7 +7,7 @@ class DiceSet
|
||||
end
|
||||
end
|
||||
|
||||
class AboutDiceSet < EdgeCase::Koan
|
||||
class AboutDiceProject < EdgeCase::Koan
|
||||
def test_can_create_a_dice_set
|
||||
dice = DiceSet.new
|
||||
assert_not_nil dice
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutExceptions < EdgeCase::Koan
|
||||
|
||||
@@ -22,10 +22,10 @@ class AboutExceptions < EdgeCase::Koan
|
||||
|
||||
assert_equal __(:exception_handled), result
|
||||
|
||||
assert ex.is_a?(StandardError), "Failure message."
|
||||
assert ex.is_a?(RuntimeError), "Failure message."
|
||||
assert ex.is_a?(___(StandardError)), "Failure message."
|
||||
assert ex.is_a?(___(RuntimeError)), "Failure message."
|
||||
|
||||
assert RuntimeError.ancestors.include?(StandardError),
|
||||
assert RuntimeError.ancestors.include?(StandardError), # __
|
||||
"RuntimeError is a subclass of StandardError"
|
||||
|
||||
assert_equal __("Oops"), ex.message
|
||||
@@ -57,4 +57,12 @@ class AboutExceptions < EdgeCase::Koan
|
||||
assert_equal __(:always_run), 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(___(MySpecialError)) do
|
||||
raise MySpecialError.new("New instances can be raised directly.")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/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 __(Hash), empty_hash.class
|
||||
assert_equal({}, empty_hash) # __
|
||||
assert_equal __(0), empty_hash.size
|
||||
end
|
||||
|
||||
@@ -25,7 +25,7 @@ class AboutHashes < EdgeCase::Koan
|
||||
hash[:one] = "eins"
|
||||
|
||||
expected = { :one => __("eins"), :two => "dos" }
|
||||
assert_equal expected, hash
|
||||
assert_equal __(true), expected == hash
|
||||
|
||||
# Bonus Question: Why was "expected" broken out into a variable
|
||||
# rather than used as a literal?
|
||||
@@ -35,7 +35,7 @@ class AboutHashes < EdgeCase::Koan
|
||||
hash1 = { :one => "uno", :two => "dos" }
|
||||
hash2 = { :two => "dos", :one => "uno" }
|
||||
|
||||
assert_equal hash1, hash2
|
||||
assert_equal __(true), hash1 == hash2
|
||||
end
|
||||
|
||||
def test_hash_keys
|
||||
@@ -43,24 +43,24 @@ class AboutHashes < EdgeCase::Koan
|
||||
assert_equal __(2), hash.keys.size
|
||||
assert_equal __(true), hash.keys.include?(:one)
|
||||
assert_equal __(true), hash.keys.include?(:two)
|
||||
assert_equal Array, hash.keys.class
|
||||
assert_equal __(Array), hash.keys.class
|
||||
end
|
||||
|
||||
def test_hash_values
|
||||
hash = { :one => "uno", :two => "dos" }
|
||||
assert_equal __(2), hash.keys.size
|
||||
assert_equal __(2), hash.values.size
|
||||
assert_equal __(true), hash.values.include?("uno")
|
||||
assert_equal __(true), hash.values.include?("dos")
|
||||
assert_equal Array, hash.values.class
|
||||
assert_equal __(Array), hash.values.class
|
||||
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
|
||||
assert_equal __(true), hash != new_hash
|
||||
|
||||
expected = { "jim" => __(54), "amy" => 20, "dan" => 23, "jenny" => __(26) }
|
||||
assert_equal expected, new_hash
|
||||
assert_equal __(true), expected == new_hash
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutInheritance < EdgeCase::Koan
|
||||
class Dog
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutIteration < EdgeCase::Koan
|
||||
|
||||
@@ -12,7 +12,7 @@ class AboutIteration < EdgeCase::Koan
|
||||
array.each do |item|
|
||||
sum += item
|
||||
end
|
||||
assert_equal 6, sum
|
||||
assert_equal __(6), sum
|
||||
end
|
||||
|
||||
def test_each_can_use_curly_brace_blocks_too
|
||||
@@ -78,16 +78,26 @@ class AboutIteration < EdgeCase::Koan
|
||||
assert_equal __([11, 12, 13]), result
|
||||
|
||||
# Files act like a collection of lines
|
||||
file = File.open("example_file.txt")
|
||||
File.open("example_file.txt") do |file|
|
||||
upcase_lines = file.map { |line| line.strip.upcase }
|
||||
assert_equal __(["THIS", "IS", "A", "TEST"]), upcase_lines
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
139
src/about_java_interop.rb
Normal file
139
src/about_java_interop.rb
Normal file
@@ -0,0 +1,139 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
include Java
|
||||
|
||||
# Concepts
|
||||
# * Pull in a java class
|
||||
# * calling a method, Camel vs snake
|
||||
# * Resovling module/class name conflicts
|
||||
# * Showing what gets returned
|
||||
# * Ruby Strings VS Java Strings
|
||||
# * Calling custom java class
|
||||
# * Calling Ruby from java???
|
||||
|
||||
class AboutJavaInterop < EdgeCase::Koan
|
||||
def test_using_a_java_library_class
|
||||
java_array = java.util.ArrayList.new
|
||||
assert_equal __(Java::JavaUtil::ArrayList), java_array.class
|
||||
end
|
||||
|
||||
def test_java_class_can_be_referenced_using_both_ruby_and_java_like_syntax
|
||||
assert_equal __(true), 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 __("constant"), 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.lang.String), java_string.class
|
||||
assert_equal __(java.lang.String), JString
|
||||
end
|
||||
|
||||
def test_can_directly_call_java_methods_on_java_objects
|
||||
java_string = JString.new("A Java String")
|
||||
assert_equal __("a java string"), java_string.toLowerCase
|
||||
end
|
||||
|
||||
def test_jruby_provides_snake_case_versions_of_java_methods
|
||||
java_string = JString.new("A Java String")
|
||||
assert_equal __("a java string"), 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 __(true), java_string.endsWith("String")
|
||||
assert_equal __(true), java_string.ends_with("String")
|
||||
assert_equal __(true), 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 __(true), java_string.is_a?(java.lang.String)
|
||||
assert_equal __(false), 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 __(false), ruby_string == java_string
|
||||
assert_equal __(true), 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 __(true), java_array.toString.is_a?(String)
|
||||
assert_equal __(false), java_array.toString.is_a?(java.lang.String)
|
||||
end
|
||||
|
||||
def test_some_ruby_objects_can_be_coerced_to_java
|
||||
assert_equal __(Java::JavaLang::String), "ruby string".to_java.class
|
||||
assert_equal __(Java::JavaLang::Long), 1.to_java.class
|
||||
assert_equal __(Java::JavaLang::Double), 9.32.to_java.class
|
||||
assert_equal __(Java::JavaLang::Boolean), false.to_java.class
|
||||
end
|
||||
|
||||
def test_some_ruby_objects_can_NOT_be_coerced_to_java
|
||||
[[], {}, Object.new].each do |ruby_object|
|
||||
assert_raise(___(NoMethodError)) do
|
||||
ruby_object.to_java_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_java_collections_are_enumerable
|
||||
java_array = java.util.ArrayList.new
|
||||
java_array << "one" << "two" << "three"
|
||||
assert_equal __(["ONE", "TWO", "THREE"]), 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 __(120), java_array.multiply_all
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutMessagePassing < EdgeCase::Koan
|
||||
|
||||
@@ -11,19 +11,19 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
def test_methods_can_be_called_directly
|
||||
mc = MessageCatcher.new
|
||||
|
||||
assert mc.caught?
|
||||
assert mc.caught? # __
|
||||
end
|
||||
|
||||
def test_methods_can_be_invoked_by_sending_the_message
|
||||
mc = MessageCatcher.new
|
||||
|
||||
assert mc.send(:caught?)
|
||||
assert mc.send(:caught?) # __
|
||||
end
|
||||
|
||||
def test_methods_can_be_invoked_more_dynamically
|
||||
mc = MessageCatcher.new
|
||||
|
||||
assert mc.send("caught?")
|
||||
assert mc.send("caught?") # __
|
||||
assert mc.send("caught" + __("?") ) # What do you need to add to the first string?
|
||||
assert mc.send("CAUGHT?".____(:downcase) ) # What would you need to do to the string?
|
||||
end
|
||||
@@ -49,7 +49,6 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
|
||||
class MessageCatcher
|
||||
def add_a_payload(*args)
|
||||
return :empty unless args
|
||||
args
|
||||
end
|
||||
end
|
||||
@@ -75,7 +74,7 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
exception = assert_raise(___(NoMethodError)) do
|
||||
typical.foobar
|
||||
end
|
||||
assert_match(/foobar/, exception.message)
|
||||
assert_match(/foobar/, exception.message) # __
|
||||
end
|
||||
|
||||
def test_calling_method_missing_causes_the_no_method_error
|
||||
@@ -84,12 +83,24 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
exception = assert_raise(___(NoMethodError)) do
|
||||
typical.method_missing(:foobar)
|
||||
end
|
||||
assert_match(/foobar/, exception.message)
|
||||
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 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 in Ruby
|
||||
# 1.9. normally.
|
||||
#
|
||||
# Thanks. We now return you to your regularly schedule Ruby
|
||||
# Koans.
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -111,7 +122,7 @@ class AboutMessagePassing < EdgeCase::Koan
|
||||
def test_catching_messages_makes_respond_to_lie
|
||||
catcher = AllMessageCatcher.new
|
||||
|
||||
assert_nothing_raised(NoMethodError) do
|
||||
assert_nothing_raised(NoMethodError) do # __
|
||||
catcher.any_method
|
||||
end
|
||||
assert_equal __(false), catcher.respond_to?(:any_method)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
def my_global_method(a,b)
|
||||
a + b
|
||||
@@ -10,19 +10,19 @@ class AboutMethods < EdgeCase::Koan
|
||||
assert_equal __(5), my_global_method(2,3)
|
||||
end
|
||||
|
||||
def test_calling_global_methods_without_parenthesis
|
||||
def test_calling_global_methods_without_parentheses
|
||||
result = my_global_method 2, 3
|
||||
assert_equal __(5), 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
|
||||
def test_sometimes_missing_parentheses_are_ambiguous
|
||||
#--
|
||||
eval "assert_equal 5, my_global_method(2, 3)" # REMOVE CHECK
|
||||
eval "assert_equal 5, my_global_method(2, 3)" # REMOVE CHECK # __
|
||||
if false
|
||||
#++
|
||||
eval "assert_equal 5, my_global_method 2, 3" # ENABLE CHECK
|
||||
eval "assert_equal 5, my_global_method 2, 3" # ENABLE CHECK # __
|
||||
#--
|
||||
end
|
||||
#++
|
||||
@@ -43,12 +43,15 @@ class AboutMethods < EdgeCase::Koan
|
||||
exception = assert_raise(___(ArgumentError)) do
|
||||
my_global_method
|
||||
end
|
||||
assert_match(/#{__("wrong number of arguments")}/, exception.message)
|
||||
#--
|
||||
pattern = "wrong (number|#) of arguments"
|
||||
#++
|
||||
assert_match(/#{__(pattern)}/, exception.message)
|
||||
|
||||
exception = assert_raise(___(ArgumentError)) do
|
||||
my_global_method(1,2,3)
|
||||
end
|
||||
assert_match(/#{__("wrong number of arguments")}/, exception.message)
|
||||
assert_match(/#{__(pattern)}/, exception.message)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutModules < EdgeCase::Koan
|
||||
module Nameable
|
||||
@@ -44,7 +44,7 @@ class AboutModules < EdgeCase::Koan
|
||||
|
||||
def test_module_methods_are_also_availble_in_the_object
|
||||
fido = Dog.new
|
||||
assert_nothing_raised(Exception) do
|
||||
assert_nothing_raised(Exception) do # __
|
||||
fido.set_name("Rover")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutNil < EdgeCase::Koan
|
||||
def test_nil_is_an_object
|
||||
|
||||
56
src/about_objects.rb
Normal file
56
src/about_objects.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutObjects < EdgeCase::Koan
|
||||
def test_everything_is_an_object
|
||||
assert_equal __(true), 1.is_a?(Object)
|
||||
assert_equal __(true), 1.5.is_a?(Object)
|
||||
assert_equal __(true), "string".is_a?(Object)
|
||||
assert_equal __(true), nil.is_a?(Object)
|
||||
assert_equal __(true), Object.is_a?(Object)
|
||||
end
|
||||
|
||||
def test_objects_can_be_converted_to_strings
|
||||
assert_equal __("123"), 123.to_s
|
||||
assert_equal __(""), nil.to_s
|
||||
end
|
||||
|
||||
def test_objects_can_be_inspected
|
||||
assert_equal __("123"), 123.inspect
|
||||
assert_equal __("nil"), nil.inspect
|
||||
end
|
||||
|
||||
def test_every_object_has_an_id
|
||||
obj = Object.new
|
||||
assert_equal __(Fixnum), obj.object_id.class
|
||||
end
|
||||
|
||||
def test_every_object_has_different_id
|
||||
obj = Object.new
|
||||
another_obj = Object.new
|
||||
assert_equal __(true), obj.object_id != another_obj.object_id
|
||||
end
|
||||
|
||||
def test_some_system_objects_always_have_the_same_id
|
||||
assert_equal __(0), false.object_id
|
||||
assert_equal __(2), true.object_id
|
||||
assert_equal __(4), nil.object_id
|
||||
end
|
||||
|
||||
def test_small_integers_have_fixed_ids
|
||||
assert_equal __(1), 0.object_id
|
||||
assert_equal __(3), 1.object_id
|
||||
assert_equal __(5), 2.object_id
|
||||
assert_equal __(201), 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 __(true), obj != copy
|
||||
assert_equal __(true), obj.object_id != copy.object_id
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutOpenClasses < EdgeCase::Koan
|
||||
class Dog
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
# Project: Create a Proxy Class
|
||||
#
|
||||
|
||||
159
src/about_regular_expressions.rb
Normal file
159
src/about_regular_expressions.rb
Normal file
@@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutRegularExpressions < EdgeCase::Koan
|
||||
def test_a_pattern_is_a_regular_expression
|
||||
assert_equal __(Regexp), /pattern/.class
|
||||
end
|
||||
|
||||
def test_a_regexp_can_search_a_string_for_matching_content
|
||||
assert_equal __("match"), "some matching content"[/match/]
|
||||
end
|
||||
|
||||
def test_a_failed_match_returns_nil
|
||||
assert_equal __(nil), "some matching content"[/missing/]
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_question_mark_means_optional
|
||||
assert_equal __("ab"), "abbcccddddeeeee"[/ab?/]
|
||||
assert_equal __("a"), "abbcccddddeeeee"[/az?/]
|
||||
end
|
||||
|
||||
def test_plus_means_one_or_more
|
||||
assert_equal __("bccc"), "abbcccddddeeeee"[/bc+/]
|
||||
end
|
||||
|
||||
def test_asterisk_means_zero_or_more
|
||||
assert_equal __("abb"), "abbcccddddeeeee"[/ab*/]
|
||||
assert_equal __("a"), "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 __("a"), "abbccc az"[/az*/]
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_character_classes_give_options_for_a_character
|
||||
animals = ["cat", "bat", "rat", "zat"]
|
||||
assert_equal __(["cat", "bat", "rat"]), animals.select { |a| a[/[cbr]at/] }
|
||||
end
|
||||
|
||||
def test_slash_d_is_a_shortcut_for_a_digit_character_class
|
||||
assert_equal __("42"), "the number is 42"[/[0123456789]+/]
|
||||
assert_equal __("42"), "the number is 42"[/\d+/]
|
||||
end
|
||||
|
||||
def test_character_classes_can_include_ranges
|
||||
assert_equal __("42"), "the number is 42"[/[0-9]+/]
|
||||
end
|
||||
|
||||
def test_slash_s_is_a_shortcut_for_a_whitespace_character_class
|
||||
assert_equal __(" \t\n"), "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"), "variable_1 = 42"[/[a-zA-Z0-9_]+/]
|
||||
assert_equal __("variable_1"), "variable_1 = 42"[/\w+/]
|
||||
end
|
||||
|
||||
def test_period_is_a_shortcut_for_any_non_newline_character
|
||||
assert_equal __("abc"), "abc\n123"[/a.+/]
|
||||
end
|
||||
|
||||
def test_a_character_class_can_be_negated
|
||||
assert_equal __("the number is "), "the number is 42"[/[^0-9]+/]
|
||||
end
|
||||
|
||||
def test_shortcut_character_classes_are_negated_with_capitals
|
||||
assert_equal __("the number is "), "the number is 42"[/\D+/]
|
||||
assert_equal __("space:"), "space: \t\n"[/\S+/]
|
||||
assert_equal __(" = "), "variable_1 = 42"[/\W+/]
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_slash_a_anchors_to_the_start_of_the_string
|
||||
assert_equal __("start"), "start end"[/\Astart/]
|
||||
assert_equal __(nil), "start end"[/\Aend/]
|
||||
end
|
||||
|
||||
def test_slash_z_anchors_to_the_end_of_the_string
|
||||
assert_equal __("end"), "start end"[/end\z/]
|
||||
assert_equal __(nil), "start end"[/start\z/]
|
||||
end
|
||||
|
||||
def test_caret_anchors_to_the_start_of_lines
|
||||
assert_equal __("2"), "num 42\n2 lines"[/^\d+/]
|
||||
end
|
||||
|
||||
def test_dollar_sign_anchors_to_the_end_of_lines
|
||||
assert_equal __("42"), "2 lines\nnum 42"[/\d+$/]
|
||||
end
|
||||
|
||||
def test_slash_b_anchors_to_a_word_boundary
|
||||
assert_equal __("vines"), "bovine vines"[/\bvine./]
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_parentheses_group_contents
|
||||
assert_equal __("hahaha"), "ahahaha"[/(ha)+/]
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_parentheses_also_capture_matched_content_by_number
|
||||
assert_equal __("Gray"), "Gray, James"[/(\w+), (\w+)/, 1]
|
||||
assert_equal __("James"), "Gray, James"[/(\w+), (\w+)/, 2]
|
||||
end
|
||||
|
||||
def test_variables_can_also_be_used_to_access_captures
|
||||
assert_equal __("Gray, James"), "Name: Gray, James"[/(\w+), (\w+)/]
|
||||
assert_equal __("Gray"), $1
|
||||
assert_equal __("James"), $2
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_a_vertical_pipe_means_or
|
||||
grays = /(James|Dana|Summer) Gray/
|
||||
assert_equal __("James Gray"), "James Gray"[grays]
|
||||
assert_equal __("Summer"), "Summer Gray"[grays, 1]
|
||||
assert_equal __(nil), "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"]), "one two-three".scan(/\w+/)
|
||||
end
|
||||
|
||||
def test_sub_is_like_find_and_replace
|
||||
assert_equal __("one t-three"), "one two-three".sub(/(t\w*)/) { $1[0, 1] }
|
||||
end
|
||||
|
||||
def test_gsub_is_like_find_and_replace_all
|
||||
assert_equal __("one t-t"), "one two-three".gsub(/(t\w*)/) { $1[0, 1] }
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutUsingBlocks < EdgeCase::Koan
|
||||
class AboutSandwichCode < EdgeCase::Koan
|
||||
|
||||
def count_lines(file_name)
|
||||
file = open(file_name)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutScope < EdgeCase::Koan
|
||||
module Jims
|
||||
@@ -29,8 +29,8 @@ class AboutScope < EdgeCase::Koan
|
||||
assert_equal __(:jims_dog), fido.identify
|
||||
assert_equal __(:joes_dog), rover.identify
|
||||
|
||||
assert_not_equal fido.class, rover.class
|
||||
assert_not_equal Jims::Dog, Joes::Dog
|
||||
assert_equal __(true), fido.class != rover.class
|
||||
assert_equal __(true), Jims::Dog != Joes::Dog
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -73,7 +73,7 @@ class AboutScope < EdgeCase::Koan
|
||||
end
|
||||
|
||||
def test_you_can_get_a_list_of_constants_for_any_class_or_module
|
||||
assert_equal __(["Dog"]), Jims.constants
|
||||
assert_equal __(122), Object.constants.size
|
||||
assert_equal __(["Dog"], [:Dog]), Jims.constants
|
||||
assert Object.constants.size > _n_(10)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/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
|
||||
@@ -54,7 +54,7 @@ def score(dice)
|
||||
#++
|
||||
end
|
||||
|
||||
class AboutScoringAssignment < EdgeCase::Koan
|
||||
class AboutScoringProject < EdgeCase::Koan
|
||||
def test_score_of_an_empty_list_is_zero
|
||||
assert_equal 0, score([])
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutStrings < EdgeCase::Koan
|
||||
def test_double_quoted_strings_are_strings
|
||||
@@ -141,17 +141,26 @@ EOS
|
||||
|
||||
def test_you_can_get_a_single_character_from_a_string
|
||||
string = "Bacon, lettuce and tomato"
|
||||
assert_equal __(97), string[1]
|
||||
assert_equal __(97, 'a'), string[1]
|
||||
|
||||
# Surprised?
|
||||
end
|
||||
|
||||
def test_single_characters_are_represented_by_integers
|
||||
assert_equal __(97), ?a
|
||||
assert_equal __(true), ?a == 97
|
||||
in_ruby_version("1.8") do
|
||||
def test_in_ruby_1_8_single_characters_are_represented_by_integers
|
||||
assert_equal __(97, 'a'), ?a
|
||||
assert_equal __(true, false), ?a == 97
|
||||
|
||||
assert_equal __(true), ?b == (?a + 1)
|
||||
end
|
||||
end
|
||||
|
||||
in_ruby_version("1.9") do
|
||||
def test_in_ruby_1_9_single_characters_are_represented_by_strings
|
||||
assert_equal __('a'), ?a
|
||||
assert_equal __(false), ?a == 97
|
||||
end
|
||||
end
|
||||
|
||||
def test_strings_can_be_split
|
||||
string = "Sausage Egg Cheese"
|
||||
@@ -165,12 +174,20 @@ EOS
|
||||
assert_equal [__("the"), __("rain"), __("in"), __("spain")], words
|
||||
|
||||
# NOTE: Patterns are formed from Regular Expressions. Ruby has a
|
||||
# very powerful Regular Expression library. Unfortunately, time
|
||||
# does not permit us to explore it in detail in Ruby 101.
|
||||
# very powerful Regular Expression library. We will become
|
||||
# enlightened about them soon.
|
||||
end
|
||||
|
||||
def test_strings_can_be_joined
|
||||
words = ["Now", "is", "the", "time"]
|
||||
assert_equal __("Now is the time"), words.join(" ")
|
||||
end
|
||||
|
||||
def test_strings_are_not_unique_objects
|
||||
a = "a string"
|
||||
b = "a string"
|
||||
|
||||
assert_equal __(true), a == b
|
||||
assert_equal __(false), a.object_id == b.object_id
|
||||
end
|
||||
end
|
||||
|
||||
100
src/about_symbols.rb
Normal file
100
src/about_symbols.rb
Normal file
@@ -0,0 +1,100 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutSymbols < EdgeCase::Koan
|
||||
def test_symbols_are_symbols
|
||||
symbol = :ruby
|
||||
assert_equal __(true), symbol.is_a?(Symbol)
|
||||
end
|
||||
|
||||
def test_symbols_can_be_compared
|
||||
symbol1 = :a_symbol
|
||||
symbol2 = :a_symbol
|
||||
symbol3 = :something_else
|
||||
|
||||
assert symbol1 == __(symbol2)
|
||||
assert symbol1 != __(symbol3)
|
||||
end
|
||||
|
||||
def test_identical_symbols_are_a_single_internal_object
|
||||
symbol1 = :a_symbol
|
||||
symbol2 = :a_symbol
|
||||
|
||||
assert_equal __(true), symbol1 == symbol2
|
||||
assert_equal __(true), symbol1.object_id == symbol2.object_id
|
||||
end
|
||||
|
||||
def test_method_names_become_symbols
|
||||
all_symbols = Symbol.all_symbols
|
||||
assert_equal __(true), all_symbols.include?(:test_method_names_become_symbols)
|
||||
end
|
||||
|
||||
# THINK ABOUT IT:
|
||||
#
|
||||
# Why do we capture the list of symbols before we check for the
|
||||
# method name?
|
||||
|
||||
in_ruby_version("mri") do
|
||||
RubyConstant = "What is the sound of one hand clapping?"
|
||||
def test_constants_become_symbols
|
||||
all_symbols = Symbol.all_symbols
|
||||
|
||||
assert_equal __(true), all_symbols.include?(__(:RubyConstant))
|
||||
end
|
||||
end
|
||||
|
||||
def test_symbols_can_be_made_from_strings
|
||||
string = "catsAndDogs"
|
||||
assert_equal __(:catsAndDogs), string.to_sym
|
||||
end
|
||||
|
||||
def test_symbols_with_spaces_can_be_built
|
||||
symbol = :"cats and dogs"
|
||||
|
||||
assert_equal symbol, __("cats and dogs").to_sym
|
||||
end
|
||||
|
||||
def test_symbols_with_spaces_can_be_built
|
||||
value = "and"
|
||||
symbol = :"cats #{value} dogs"
|
||||
|
||||
assert_equal symbol, __("cats and dogs").to_sym
|
||||
end
|
||||
|
||||
def test_to_s_is_called_on_interpolated_symbols
|
||||
symbol = :cats
|
||||
string = "It is raining #{symbol} and dogs."
|
||||
|
||||
assert_equal __('It is raining cats and dogs.'), string
|
||||
end
|
||||
|
||||
def test_symbols_are_not_strings
|
||||
symbol = :ruby
|
||||
assert_equal __(false), symbol.is_a?(String)
|
||||
assert_equal __(false), symbol.eql?("ruby")
|
||||
end
|
||||
|
||||
def test_symbols_do_not_have_string_methods
|
||||
symbol = :not_a_string
|
||||
assert_equal __(false), symbol.respond_to?(:each_char)
|
||||
assert_equal __(false), symbol.respond_to?(:reverse)
|
||||
end
|
||||
|
||||
# It's important to realize that symbols are not "immutable
|
||||
# strings", though they are immutable. None of the
|
||||
# interesting string operations are available on symbols.
|
||||
|
||||
def test_symbols_cannot_be_concatenated
|
||||
# Exceptions will be pondered further father down the path
|
||||
assert_raise(___(NoMethodError)) do
|
||||
:cats + :dogs
|
||||
end
|
||||
end
|
||||
|
||||
def test_symbols_can_be_dynamically_created
|
||||
assert_equal __(:catsdogs), ("cats" + "dogs").to_sym
|
||||
end
|
||||
|
||||
# THINK ABOUT IT:
|
||||
#
|
||||
# Why is it not a good idea to dynamically create a lot of symbols?
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
# You need to write the triangle method in the file 'triangle.rb'
|
||||
require 'triangle.rb'
|
||||
|
||||
class AboutTriangleAssignment < EdgeCase::Koan
|
||||
class AboutTriangleProject < EdgeCase::Koan
|
||||
def test_equilateral_triangles_have_equal_sides
|
||||
assert_equal :equilateral, triangle(2, 2, 2)
|
||||
assert_equal :equilateral, triangle(10, 10, 10)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
# You need to write the triangle method in the file 'triangle.rb'
|
||||
require 'triangle.rb'
|
||||
|
||||
class AboutTriangleAssignment2 < EdgeCase::Koan
|
||||
class AboutTriangleProject2 < EdgeCase::Koan
|
||||
# The first assignment did not talk about how to handle errors.
|
||||
# Let's handle that part now.
|
||||
def test_illegal_triangles_throw_exceptions
|
||||
assert_raise(TriangleError) do triangle(0, 0, 0) end
|
||||
assert_raise(TriangleError) do triangle(3, 4, -5) end
|
||||
assert_raise(TriangleError) do triangle(1, 1, 3) end
|
||||
assert_raise(TriangleError) do triangle(2, 4, 2) end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
class AboutTrueAndFalse < EdgeCase::Koan
|
||||
def truth_value(condition)
|
||||
|
||||
3
src/autotest/discover.rb
Normal file
3
src/autotest/discover.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Autotest.add_discovery do
|
||||
"rubykoan" if File.exist? 'path_to_enlightenment.rb'
|
||||
end
|
||||
24
src/autotest/rubykoan.rb
Normal file
24
src/autotest/rubykoan.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
require 'autotest'
|
||||
|
||||
class Autotest::Rubykoan < Autotest
|
||||
def initialize
|
||||
super
|
||||
@exceptions = /\.txt|Rakefile|\.rdoc/
|
||||
|
||||
self.order = :alpha
|
||||
self.add_mapping(/^about_.*rb$/) do |filename, _|
|
||||
filename
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def make_test_cmd files_to_test
|
||||
"#{ruby} 'path_to_enlightenment.rb'"
|
||||
end
|
||||
|
||||
# quiet test/unit chatter
|
||||
def handle_results(results)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1 +1 @@
|
||||
require 'edgecase'
|
||||
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||||
|
||||
368
src/edgecase.rb
368
src/edgecase.rb
@@ -3,45 +3,164 @@
|
||||
|
||||
require 'test/unit/assertions'
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Support code for the Ruby Koans.
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
class FillMeInError < StandardError
|
||||
end
|
||||
|
||||
def __(value="FILL ME IN")
|
||||
value
|
||||
def ruby_version?(version)
|
||||
RUBY_VERSION =~ /^#{version}/ ||
|
||||
(version == 'jruby' && defined?(JRUBY_VERSION)) ||
|
||||
(version == 'mri' && ! defined?(JRUBY_VERSION))
|
||||
end
|
||||
|
||||
def in_ruby_version(*versions)
|
||||
yield if versions.any? { |v| ruby_version?(v) }
|
||||
end
|
||||
|
||||
# Standard, generic replacement value.
|
||||
# If value19 is given, it is used inplace of value for Ruby 1.9.
|
||||
def __(value="FILL ME IN", value19=:mu)
|
||||
if RUBY_VERSION < "1.9"
|
||||
value
|
||||
else
|
||||
(value19 == :mu) ? value : value19
|
||||
end
|
||||
end
|
||||
|
||||
# Numeric replacement value.
|
||||
def _n_(value=999999, value19=:mu)
|
||||
if RUBY_VERSION < "1.9"
|
||||
value
|
||||
else
|
||||
(value19 == :mu) ? value : value19
|
||||
end
|
||||
end
|
||||
|
||||
# Error object replacement value.
|
||||
def ___(value=FillMeInError)
|
||||
value
|
||||
end
|
||||
|
||||
# Method name replacement.
|
||||
class Object
|
||||
def ____(method=nil)
|
||||
if method
|
||||
self.send(method)
|
||||
end
|
||||
end
|
||||
|
||||
in_ruby_version("1.9") do
|
||||
public :method_missing
|
||||
end
|
||||
end
|
||||
|
||||
class String
|
||||
def side_padding(width)
|
||||
extra = width - self.size
|
||||
if width < 0
|
||||
self
|
||||
else
|
||||
left_padding = extra / 2
|
||||
right_padding = (extra+1)/2
|
||||
(" " * left_padding) + self + (" " *right_padding)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module EdgeCase
|
||||
class Sensei
|
||||
attr_reader :failure, :failed_test
|
||||
class << self
|
||||
def simple_output
|
||||
ENV['SIMPLE_KOAN_OUTPUT'] == 'true'
|
||||
end
|
||||
end
|
||||
|
||||
module Color
|
||||
#shamelessly stolen (and modified) from redgreen
|
||||
COLORS = {
|
||||
:clear => 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 ENV['NO_COLOR']
|
||||
string
|
||||
else
|
||||
color(color_value) + string + color(COLORS[:clear])
|
||||
end
|
||||
end
|
||||
|
||||
def color(color_value)
|
||||
"\e[#{color_value}m"
|
||||
end
|
||||
end
|
||||
|
||||
class Sensei
|
||||
attr_reader :failure, :failed_test, :pass_count
|
||||
|
||||
in_ruby_version("1.8") do
|
||||
AssertionError = Test::Unit::AssertionFailedError
|
||||
end
|
||||
|
||||
in_ruby_version("1.9") do
|
||||
if defined?(MiniTest)
|
||||
AssertionError = MiniTest::Assertion
|
||||
else
|
||||
AssertionError = Test::Unit::AssertionFailedError
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@pass_count = 0
|
||||
@failure = nil
|
||||
@failed_test = nil
|
||||
@observations = []
|
||||
end
|
||||
|
||||
def accumulate(test)
|
||||
if test.passed?
|
||||
@pass_count += 1
|
||||
puts " #{test.name} has expanded your awareness."
|
||||
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
|
||||
puts " #{test.name} has damaged your karma."
|
||||
@failed_test = test
|
||||
@failure = test.failure
|
||||
@_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 :edgecase_exit
|
||||
end
|
||||
end
|
||||
@@ -54,62 +173,175 @@ module EdgeCase
|
||||
failure.is_a?(AssertionError)
|
||||
end
|
||||
|
||||
def report
|
||||
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 = EdgeCase::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
|
||||
puts "You have not yet reached enlightenment ..."
|
||||
puts failure.message
|
||||
end
|
||||
|
||||
def end_screen
|
||||
if EdgeCase.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 ,,::::::::::::,
|
||||
:::::::::::::: ,::::::::::::
|
||||
::::::::::::::, ,:::::::::::::
|
||||
::::::::::::, EdgeCase 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:"
|
||||
if assert_failed?
|
||||
puts find_interesting_lines(failure.backtrace)
|
||||
puts embolden_first_line_only(indent(find_interesting_lines(failure.backtrace)))
|
||||
else
|
||||
puts failure.backtrace
|
||||
puts embolden_first_line_only(indent(failure.backtrace))
|
||||
end
|
||||
puts
|
||||
end
|
||||
say_something_zenlike
|
||||
|
||||
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 =~ /test\/unit\/|edgecase\.rb/
|
||||
line =~ /test\/unit\/|edgecase\.rb|minitest/
|
||||
}
|
||||
end
|
||||
|
||||
# Hat's tip to Ara T. Howard for the zen statements from his
|
||||
# metakoans Ruby Quiz (http://rubyquiz.com/quiz67.html)
|
||||
def say_something_zenlike
|
||||
puts
|
||||
def a_zenlike_statement
|
||||
if !failed?
|
||||
puts "Mountains are again merely mountains"
|
||||
zen_statement = "Mountains are again merely mountains"
|
||||
else
|
||||
case (@pass_count % 10)
|
||||
zen_statement = case (@pass_count % 10)
|
||||
when 0
|
||||
puts "mountains are merely mountains"
|
||||
"mountains are merely mountains"
|
||||
when 1, 2
|
||||
puts "learn the rules so you know how to break them properly"
|
||||
"learn the rules so you know how to break them properly"
|
||||
when 3, 4
|
||||
puts "remember that silence is sometimes the best answer"
|
||||
"remember that silence is sometimes the best answer"
|
||||
when 5, 6
|
||||
puts "sleep is the best meditation"
|
||||
"sleep is the best meditation"
|
||||
when 7, 8
|
||||
puts "when you lose, don't lose the lesson"
|
||||
"when you lose, don't lose the lesson"
|
||||
else
|
||||
puts "things are not what they appear to be: nor are they otherwise"
|
||||
"things are not what they appear to be: nor are they otherwise"
|
||||
end
|
||||
end
|
||||
puts Color.green(zen_statement)
|
||||
end
|
||||
end
|
||||
|
||||
class Koan
|
||||
include Test::Unit::Assertions
|
||||
|
||||
attr_reader :name, :failure
|
||||
attr_reader :name, :failure, :koan_count, :step_count, :koan_file
|
||||
|
||||
def initialize(name)
|
||||
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?
|
||||
@@ -126,6 +358,22 @@ module EdgeCase
|
||||
def teardown
|
||||
end
|
||||
|
||||
def meditate
|
||||
setup
|
||||
begin
|
||||
send(name)
|
||||
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
|
||||
failed(ex)
|
||||
ensure
|
||||
begin
|
||||
teardown
|
||||
rescue StandardError, EdgeCase::Sensei::AssertionError => ex
|
||||
failed(ex) if passed?
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Class methods for the EdgeCase test suite.
|
||||
class << self
|
||||
def inherited(subclass)
|
||||
@@ -133,32 +381,7 @@ module EdgeCase
|
||||
end
|
||||
|
||||
def method_added(name)
|
||||
testmethods << name unless tests_disabled?
|
||||
end
|
||||
|
||||
def run_tests(accumulator)
|
||||
puts
|
||||
puts "Thinking #{self}"
|
||||
testmethods.each do |m|
|
||||
self.run_test(m, accumulator) if Koan.test_pattern =~ m.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def run_test(method, accumulator)
|
||||
test = self.new(method)
|
||||
test.setup
|
||||
begin
|
||||
test.send(method)
|
||||
rescue StandardError => ex
|
||||
test.failed(ex)
|
||||
ensure
|
||||
begin
|
||||
test.teardown
|
||||
rescue StandardError => ex
|
||||
test.failed(ex) if test.passed?
|
||||
end
|
||||
end
|
||||
accumulator.accumulate(test)
|
||||
testmethods << name if !tests_disabled? && Koan.test_pattern =~ name.to_s
|
||||
end
|
||||
|
||||
def end_of_enlightenment
|
||||
@@ -200,17 +423,36 @@ module EdgeCase
|
||||
@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 = EdgeCase::Sensei.new
|
||||
each_step do |step|
|
||||
sensei.observe(step.meditate)
|
||||
end
|
||||
sensei.instruct
|
||||
end
|
||||
|
||||
def each_step
|
||||
catch(:edgecase_exit) {
|
||||
step_count = 0
|
||||
EdgeCase::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 {
|
||||
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
|
||||
EdgeCase::ThePath.new.walk
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
# 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'
|
||||
require 'about_constants'
|
||||
require 'about_control_statements'
|
||||
require 'about_true_and_false'
|
||||
require 'about_triangle_project'
|
||||
@@ -25,4 +31,7 @@ require 'about_scope'
|
||||
require 'about_class_methods'
|
||||
require 'about_message_passing'
|
||||
require 'about_proxy_object_project'
|
||||
in_ruby_version("jruby") do
|
||||
require 'about_java_interop'
|
||||
end
|
||||
require 'about_extra_credit'
|
||||
|
||||
Reference in New Issue
Block a user