diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76d2d5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +dist +.project_env.rc +.path_progress +*.rbc +.idea diff --git a/koans/path_to_enlightenment.rb b/koans/path_to_enlightenment.rb deleted file mode 100644 index edeb34a..0000000 --- a/koans/path_to_enlightenment.rb +++ /dev/null @@ -1,3 +0,0 @@ -# The path to Ruby Enlightenment starts with the following: - -$LOAD_PATH << File.dirname(__FILE__) \ No newline at end of file diff --git a/koans/Rakefile b/src/Rakefile similarity index 100% rename from koans/Rakefile rename to src/Rakefile diff --git a/src/about_binding.rb b/src/about_binding.rb new file mode 100644 index 0000000..ea6c2b0 --- /dev/null +++ b/src/about_binding.rb @@ -0,0 +1,62 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutBinding < EdgeCase::Koan + + class Foo + def initialize + @ivar = 22 + end + + def bar(param) + lvar = 11 + binding + end + end + + def test_binding_binds_method_parameters + binding = Foo.new.bar(99) + assert_equal 99, eval("param", binding) + end + + def test_binding_binds_local_vars + binding = Foo.new.bar(99) + assert_equal 11, eval("lvar", binding) + end + + def test_binding_binds_instance_vars + binding = Foo.new.bar(99) + assert_equal 22, eval("@ivar", binding) + end + + def test_binding_binds_blocks + binding = Foo.new.bar(99) { 33 } + assert_equal 33, eval("yield", binding) + end + + def test_binding_binds_self + foo = Foo.new + binding = foo.bar(99) + assert_equal foo, eval("self", binding) + end + + def n_times(n) + lambda {|value| n * value} + end + + def test_lambda_binds_to_the_surrounding_context + two_times = n_times(2) + assert_equal 6, two_times.call(3) + end + + def count_with_increment(start, inc) + lambda { start += inc} + end + + def test_lambda_remembers_state_of_bound_variables + counter = count_with_increment(7, 3) + assert_equal 10, counter.call + assert_equal 13, counter.call + assert_equal 16, counter.call + end + +end diff --git a/src/about_blocks.rb b/src/about_blocks.rb new file mode 100644 index 0000000..5a12272 --- /dev/null +++ b/src/about_blocks.rb @@ -0,0 +1,60 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutBlocks < EdgeCase::Koan + + def test_calling_a_lambda + l = lambda {|a| a + 1} + assert_equal 100, l.call(99) + end + + def test_calling_a_proc + p = Proc.new {|a| a + 1} + assert_equal 100, p.call(99) + end + + def convert(&block) + block + end + + def test_block_is_proc + b = convert {|a| a + 1} + assert_equal Proc, b.class + assert_equal 100, b.call(99) + end + + def test_proc_takes_fewer_or_more_arguments + p = Proc.new {|a, b, c| a.to_i + b.to_i + c.to_i} + assert_equal 3 , p.call(1,2) + assert_equal 6, p.call(1,2,3,4) + end + + def test_lambda_does_not_take_fewer_or_more_arguments + l = lambda {|a, b, c| a.to_i + b.to_i + c.to_i} + assert_raises(ArgumentError) do + l.call(1, 2) + end + + assert_raises(ArgumentError) do + l.call(1,2,3,4) + end + end + + def method(lambda_or_proc) + lambda_or_proc.call + :from_method + end + + def test_return_inside_lambda_returns_from_the_lambda + l = lambda { return :from_lambda } + result = method(l) + assert_equal :from_method, result + end + + def test_return_inside_proc_returns_from_the_context + p = Proc.new { return :from_proc } + result = method(p) + # The execution never reaches this line because Proc returns + # outside the test method + assert_equal __, p.call + end +end diff --git a/src/about_class_as_constant.rb b/src/about_class_as_constant.rb new file mode 100644 index 0000000..4ca8504 --- /dev/null +++ b/src/about_class_as_constant.rb @@ -0,0 +1,53 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutClassAsConstant < EdgeCase::Koan + + class Foo + def say_hello + "Hi" + end + end + + def test_defined_tells_if_a_class_is_defined_or_not + assert_not_nil defined?(Foo) + assert_nil defined?(Bar) + end + + def test_class_is_a_constant + assert_equal "constant", defined?(Foo) + end + + def test_class_constant_can_be_assigned_to_var + my_class = Foo + assert_equal "Hi", my_class.new.say_hello + end + + @@return_value_of_class = + class Baz + def say_hi + "Hello" + end + 99 + end + + def test_class_definitions_are_active + assert_equal 99, @@return_value_of_class + end + + @@self_inside_a_class = + class Baz + def say_hi + "Hi" + end + self + end + + def test_self_inside_class_is_class_itself + assert_equal Baz, @@self_inside_a_class + end + + def test_class_is_an_object_of_type_class_and_can_be_created_dynamically + cls = Class.new + assert_match /Class/, cls.to_s + end +end diff --git a/src/about_class_inheritance.rb b/src/about_class_inheritance.rb new file mode 100644 index 0000000..6f94a0b --- /dev/null +++ b/src/about_class_inheritance.rb @@ -0,0 +1,27 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutClassInheritance < EdgeCase::Koan + + def test_singleton_class_can_be_used_to_define_singleton_methods + animal = "cat" + class << animal + def speak + "miaow" + end + end + assert_equal "miaow", animal.speak + end + + class Foo + class << self + def say_hello + "Hello" + end + end + end + + def test_singleton_class_can_be_used_to_define_class_methods + assert_equal "Hello", Foo.say_hello + end + +end diff --git a/src/about_class_methods.rb b/src/about_class_methods.rb new file mode 100644 index 0000000..ef9b4d2 --- /dev/null +++ b/src/about_class_methods.rb @@ -0,0 +1,23 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutClassMethods < EdgeCase::Koan + + class Foo + def self.say_hello + "Hello" + end + end + + def test_class_is_an_instance_of_class_Class + assert_equal true, Foo.class == Class + end + + def test_class_methods_are_just_singleton_methods_on_the_class + assert_equal "Hello", Foo.say_hello + end + + def test_classes_are_not_special_and_are_just_like_other_objects + assert_equal true, Foo.is_a?(Object) + assert_equal true, Foo.superclass == Object + end +end diff --git a/src/about_define_method.rb b/src/about_define_method.rb new file mode 100644 index 0000000..11d9b69 --- /dev/null +++ b/src/about_define_method.rb @@ -0,0 +1,87 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutDefineMethod < EdgeCase::Koan + + class Example + def start + def stop + :stopped + end + :started + end + end + + def test_methods_can_define_other_methods + o = Example.new + assert_raises(NoMethodError) do + o.stop + end + + o.start + + assert_equal :stopped, o.stop + end + + class Example + def foo + def foo + :new_value + end + :first_value + end + end + + def test_methods_can_redefine_themselves + o = Example.new + assert_equal :first_value, o.foo + assert_equal :new_value, o.foo + end + + class Multiplier + def self.create_multiplier(n) + define_method "times_#{n}" do |val| + val * n + end + end + + 10.times {|i| create_multiplier(i) } + end + + def test_define_method_creates_methods_dynamically + m = Multiplier.new + assert_equal 30, m.times_3(10) + assert_equal 60, m.times_6(10) + assert_equal 90, m.times_9(10) + end + + module Accessor + def my_writer(name) + ivar_name = "@#{name}" + define_method "#{name}=" do |value| + #Write code here to set value of ivar + instance_variable_set(ivar_name, value) + end + end + + def my_reader(name) + ivar_name = "@#{name}" + define_method name do + #Write code here to get value of ivar + instance_variable_get(ivar_name) + end + end + end + + class Cat + extend Accessor + my_writer :name + my_reader :name + end + + def test_instance_variable_set_and_instance_variable_get_can_be_used_to_access_ivars + cat = Cat.new + cat.name = 'Fred' + assert_equal 'Fred', cat.name + end +end + diff --git a/src/about_hook_methods.rb b/src/about_hook_methods.rb new file mode 100644 index 0000000..18258c3 --- /dev/null +++ b/src/about_hook_methods.rb @@ -0,0 +1,92 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutHookMethods < EdgeCase::Koan + + module Bar + def self.included(klass) + @included = true + end + + def self.included? + @included + end + end + + class Foo + include Bar + end + + def test_included_hook_method_is_called_when_module_is_included_in_class + assert_equal true, Bar.included? + end + + class Parent + def self.inherited(klass) + @inherited = true + end + + def self.inherited? + @inherited + end + end + + class Child < Parent + end + + def test_inherited_hook_method_is_called_when_class_is_subclassed + assert_equal true, Parent.inherited? + end + + class ::Struct + @children = [] + + def self.inherited(klass) + @children << klass + end + + def self.children + @children + end + end + + Cat = Struct.new(:name, :tail) + Dog = Struct.new(:name, :legs) + + def test_inherited_can_track_subclasses + assert_equal [Cat, Dog], Struct.children + end + + class ::Module + def const_missing(name) + if name.to_s =~ /(X?)(IX|IV|(V?)(I{0,3}))/ + to_roman($~) + end + end + end + + def test_const_missing_hook_method_can_be_used_to_dynamically_evaluate_constants + assert_equal 8, VIII + end + + class Color + def self.const_missing(name) + const_set(name, new) + end + + end + + def test_const_set_can_be_used_to_dynamically_create_constants + Color::Red + assert_equal 'constant', defined?(Color::Red) + end +end + +def to_roman(match) + value = 0 + value += 10 if match[1] == 'X' + value += 9 if match[2] == 'IX' + value += 4 if match[2] == 'IV' + value += 5 if match[3] == 'V' + value += match[4].chars.count if match[4] + value +end diff --git a/src/about_instance_eval_and_class_eval.rb b/src/about_instance_eval_and_class_eval.rb new file mode 100644 index 0000000..f4a35e2 --- /dev/null +++ b/src/about_instance_eval_and_class_eval.rb @@ -0,0 +1,166 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutInstanceEvalAndClassEval < EdgeCase::Koan + + def test_instance_eval_executes_block_in_the_context_of_the_receiver + assert_equal "CAT", "cat".instance_eval { upcase } + end + + class Foo + def initialize + @ivar = 99 + end + end + + def test_instance_eval_can_access_instance_variables + assert_equal 99, Foo.new.instance_eval { @ivar } + end + + class Foo + private + def secret + 123 + end + end + + def test_instance_eval_can_access_private_methods + assert_equal 123, Foo.new.instance_eval { secret } + end + + def test_instance_eval_can_be_used_to_define_singleton_methods + animal = "cat" + animal.instance_eval do + def speak + "miaow" + end + end + assert_equal "miaow", animal.speak + end + + class Cat + end + + def test_instance_eval_can_be_used_to_define_class_methods + Cat.instance_eval do + def say_hello + "Hello" + end + end + + assert_equal "Hello", Cat.say_hello + end + + def test_class_eval_executes_block_in_the_context_of_class_or_module + assert_equal Cat, Cat.class_eval { self } + end + + def test_class_eval_can_be_used_to_define_instance_methods + Cat.class_eval do + def speak + "miaow" + end + end + assert_equal "miaow", Cat.new.speak + end + + def test_module_eval_is_same_as_class_eval + Cat.module_eval do + def miaow + "miaow" + end + end + assert_equal "miaow", Cat.new.miaow + end + + module Accessor + def my_eval_accessor(name) + # WRITE code here to generate accessors + class_eval %{ + def #{name} + @#{name} + end + + def #{name}=(val) + @#{name} = val + end + } + end + end + + class Cat + extend Accessor + my_eval_accessor :name + end + + def test_class_eval_can_be_used_to_create_instance_methods_like_accessors + cat = Cat.new + cat.name = 'Frisky' + assert_equal 'Frisky', cat.name + end + + module Hello + def say_hello + "hi" + end + end + + def test_class_eval_can_be_used_to_call_private_methods_on_class + String.class_eval { include Hello } + assert_equal "hi", "hello".say_hello + end + + class Turtle + attr_reader :path + def initialize + @path = "" + end + + def right(n=1) + @path << 'r' * n + end + + def up(n=1) + @path << 'u' * n + end + end + + class Turtle + def move_yield(&block) + yield + end + end + + def test_yield_executes_block_with_self_as_caller + t = Turtle.new + here = :here + assert_equal :here, t.move_yield { here } + end + + class Turtle + def move_eval(&block) + instance_eval(&block) + end + end + + def test_instance_eval_executes_block_with_self_as_called_object + t = Turtle.new + t.move_eval do + right(3) + up(2) + right(1) + end + assert_equal 'rrruur', t.path + end + + class Turtle + def move_eval_yield(&block) + instance_eval { yield } + end + end + + def test_yield_inside_instance_eval_executes_block_with_self_as_caller + still_here = :still_here + t = Turtle.new + assert_equal :still_here, t.move_eval_yield { still_here } + end +end diff --git a/src/about_metaclass.rb b/src/about_metaclass.rb new file mode 100644 index 0000000..0b30469 --- /dev/null +++ b/src/about_metaclass.rb @@ -0,0 +1,335 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +# Based on _why's article: Seeing Metaclasses clearly +# http://dannytatom.github.com/metaid/ + +class AboutMetaclass < EdgeCase::Koan + + class MailTruck + attr_accessor :driver, :route + def initialize(driver = nil, route = nil) + @driver, @route = driver, route + end + end + + def setup + @truck = MailTruck.new("Harold", ['12 Corrigan way', '23 Antler Ave']) + end + + def test_class_of_an_object + assert_equal MailTruck, @truck.class + end + + def test_class_of_a_class + assert_equal Class, MailTruck.class + end + + def test_object_is_a_storage_for_variables + assert_equal "Harold", @truck.driver + end + + def test_object_can_hold_any_other_instance_variables + @truck.instance_variable_set("@speed", 45) + assert_equal 45, @truck.instance_variable_get("@speed") + end + + def test_attr_accessor_defines_reader_and_writer + @truck.driver = 'Kumar' + assert_equal 'Kumar', @truck.driver + end + + def test_classes_store_methods + assert_equal true, MailTruck.instance_methods.include?(:driver) + end + +=begin + +BasicObject + | + Object + | + Module + | + Class + +=end + + def test_class_is_an_object + assert_equal true, Class.is_a?(Object) + assert_equal Module, Class.superclass + assert_equal Object, Class.superclass.superclass + end + + def test_class_has_object_id + assert_equal true, @truck.object_id > 0 + assert_equal true, MailTruck.object_id > 0 + end + + def test_Object_class_is_Class + assert_equal Class, Object.class + end + + def test_Object_inherits_from_Basic_Object + assert_equal BasicObject, Object.superclass + end + + def test_Basic_Object_sits_at_the_very_top + assert_equal nil, BasicObject.superclass + end + + class MailTruck + def has_mail? + !(@mails.nil? || @mails.empty?) + end + end + + def test_metaclass_is_a_class_which_an_object_uses_to_redefine_itself + assert_equal false, @truck.has_mail? + end + + class ::Class + def is_everything_an_object? + true + end + end + + def test_metaclass_is_a_class_which_even_Class_uses_to_redefine_itself + assert_equal true, Class.is_everything_an_object? + assert_equal true, MailTruck.is_everything_an_object? + end + + def test_singleton_methods_are_defined_only_for_that_instance + red_truck = MailTruck.new + blue_truck = MailTruck.new + def red_truck.honk + "Honk!Honk!" + end + + assert_equal "Honk!Honk!", red_truck.honk + assert_raises NoMethodError do + blue_truck.honk + end + end + +=begin + + MailTruck + | + Metaclass + | + @truck + +=end + + def test_metaclass_sits_between_object_and_class + assert_equal MailTruck, @truck.metaclass.superclass + end + + def test_singleton_methods_are_defined_in_metaclass + def @truck.honk + "Honk" + end + assert_equal "Honk", @truck.honk + assert_equal true, @truck.metaclass.instance_methods.include?(:honk) + assert_equal true, @truck.singleton_methods.include?(:honk) + end + + class ::Object + def metaclass + class << self ; self ; end + end + end + + def test_class_lt_lt_m_opens_up_metaclass + klass = class << @truck ; self ; end + assert_equal true, klass == @truck.metaclass + end + + def test_metaclass_can_have_metaclass_ad_infinitum + assert_equal false, @truck.metaclass.metaclass.nil? + assert_equal false, @truck.metaclass.metaclass.metaclass.nil? + end + + def test_metaclass_of_a_metaclass_does_not_affect_the_original_object + def @truck.honk + "Honk" + end + + metaclass = @truck.metaclass + def metaclass.honk_honk + "Honk Honk" + end + + assert_equal "Honk", @truck.honk + assert_equal "Honk Honk", @truck.meta_eval { honk_honk } + assert_raises NoMethodError do + @truck.honk_honk + end + end + +=begin + MailTruck + | + Metaclass -> Metaclass -> Metaclass ... + | + @truck + +=end + class MailTruck + @@trucks = [] + + def MailTruck.add_truck(truck) + @@trucks << truck + end + + def MailTruck.count_trucks + @@trucks.count + end + end + + def test_classes_can_have_class_variables + red_truck = MailTruck.new + blue_truck = MailTruck.new + MailTruck.add_truck(red_truck) + MailTruck.add_truck(blue_truck) + + assert_equal 2, MailTruck.count_trucks + end + + class MailTruck + @trucks = [] + + def MailTruck.add_a_truck(truck) + @trucks << truck + end + + def MailTruck.total_trucks + @trucks.count + end + end + + def test_classes_can_have_instance_variables + red_truck = MailTruck.new + blue_truck = MailTruck.new + green_truck = MailTruck.new + MailTruck.add_a_truck(red_truck) + MailTruck.add_a_truck(blue_truck) + MailTruck.add_a_truck(green_truck) + + assert_equal 3, MailTruck.total_trucks + end + + def test_class_variable_and_class_instance_variable_are_not_the_same + assert_equal false, MailTruck.count_trucks == MailTruck.total_trucks + end + + class MailTruck + def say_hi + "Hi! I'm one of #{@@trucks.length} trucks" + end + end + + def test_only_class_variables_can_be_accessed_by_instances_of_class + MailTruck.add_truck(@truck) + assert_equal "Hi! I'm one of 3 trucks", @truck.say_hi + end + + def test_class_methods_are_defined_in_metaclass_of_class + assert_equal true, MailTruck.metaclass.instance_methods.include?(:add_truck) + assert_equal true, MailTruck.metaclass.instance_methods.include?(:add_a_truck) + end + + class MailTruck + def self.add_another_truck(truck) + @@trucks << truck + end + end + + def test_class_methods_can_also_be_defined_using_self + MailTruck.add_another_truck(MailTruck.new) + assert_equal 4, MailTruck.count_trucks + end + + def test_all_class_methods_are_defined_in_metaclass_of_class + assert_equal true, MailTruck.metaclass.instance_methods.include?(:add_another_truck) + end + + class ::Object + def meta_eval(&block) + metaclass.instance_eval(&block) + end + # Add methods to metaclass + def meta_def name, &block + meta_eval { define_method name, &block } + end + end + + class MailTruck + def self.made_by(name) + meta_def :company do + name + end + end + end + + class ManualTruck < MailTruck + made_by "TrucksRUs" + end + + class RobotTruck < MailTruck + made_by "Lego" + end + + def test_meta_def_can_be_used_to_add_methods_dynamically_to_metaclass + assert_equal "TrucksRUs", ManualTruck.company + assert_equal "Lego", RobotTruck.company + end + + class ::Object + # Defines an instance method within a class + def class_def name, &block + class_eval { define_method name, &block } + end + end + + class MailTruck + def self.check_for(attr) + class_def :can_drive? do + instance_variable_get("@#{attr}") != nil + end + end + end + + class ManualTruck < MailTruck + check_for :driver + end + + class RobotTruck < MailTruck + check_for :route + end + + def test_class_def_can_be_used_to_add_instance_methods_dynamically + assert_equal false, ManualTruck.new.can_drive? + assert_equal false, RobotTruck.new.can_drive? + + assert_equal true, ManualTruck.new('Harold', nil).can_drive? + assert_equal true, RobotTruck.new(nil, ['SF']).can_drive? + end + + class ::Object + def meta_eval(&block) + metaclass.instance_eval(&block) + end + + # Add methods to metaclass + def meta_def name, &block + meta_eval { define_method name, &block } + end + + # Defines an instance method within a class + def class_def name, &block + class_eval { define_method name, &block } + end + end + +end diff --git a/src/about_method_added.rb b/src/about_method_added.rb new file mode 100644 index 0000000..a959122 --- /dev/null +++ b/src/about_method_added.rb @@ -0,0 +1,26 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutMethodAdded < EdgeCase::Koan + + class Cat + @num_of_methods = 0 + + def self.method_added(name) + @num_of_methods += 1 + end + + def miaow + end + + def speak + end + + def self.num_of_methods + @num_of_methods + end + end + + def test_method_added_hook_method_is_called_for_new_methods + assert_equal 2, Cat.num_of_methods + end +end diff --git a/src/about_method_missing.rb b/src/about_method_missing.rb new file mode 100644 index 0000000..e826c51 --- /dev/null +++ b/src/about_method_missing.rb @@ -0,0 +1,8 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutMethodMissing < EdgeCase::Koan + + def test_foo + + end +end diff --git a/src/about_modules.rb b/src/about_modules.rb new file mode 100644 index 0000000..cecae02 --- /dev/null +++ b/src/about_modules.rb @@ -0,0 +1,87 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutModules < EdgeCase::Koan + + module Greeting + def say_hello + "Hello" + end + end + + class Foo + include Greeting + end + + module Greeting + def say_hello + "Hi" + end + end + + def test_module_methods_are_active + assert_equal "Hi", Foo.new.say_hello + end + + def test_extend_adds_singleton_methods + animal = "cat" + animal.extend Greeting + + assert_equal "Hi", animal.say_hello + end + + def test_another_way_to_add_singleton_methods_from_module + animal = "cat" + class << animal + include Greeting + end + + assert_equal "Hi", animal.say_hello + end + + class Bar + extend Greeting + end + + def test_extend_adds_class_methods_or_singleton_methods_on_the_class + assert_equal "Hi", Bar.say_hello + end + + module Moo + def instance_method + :instance_value + end + + module ClassMethods + def class_method + :class_value + end + end + end + + class Baz + include Moo + extend Moo::ClassMethods + end + + def test_include_instance_methods_and_extend_class_methods + assert_equal :instance_value, Baz.new.instance_method + assert_equal :class_value, Baz.class_method + end + + module Moo + def self.included(klass) + #WRITE CODE HERE + klass.extend(ClassMethods) + end + end + + class Foo + include Moo + end + + def test_included_is_a_hook_method_that_can_be_used_to_extend_automatically + assert_equal :instance_value, Foo.new.instance_method + assert_equal :class_value, Foo.class_method + end + +end diff --git a/src/about_prototype_inheritance.rb b/src/about_prototype_inheritance.rb new file mode 100644 index 0000000..fbdd689 --- /dev/null +++ b/src/about_prototype_inheritance.rb @@ -0,0 +1,42 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutPrototypeInheritance < EdgeCase::Koan + + def test_clone_copies_singleton_methods + animal = "cat" + def animal.speak + "miaow" + end + other = animal.clone + assert_equal "miaow", other.speak + end + + def test_dup_does_not_copy_singleton_methods + animal = "cat" + def animal.speak + "miaow" + end + other = animal.dup + assert_raises(NoMethodError) do + other.speak + end + end + + def test_state_is_inherited_in_prototype_inheritance + animal = Object.new + def animal.num_of_lives=(lives) + @num_of_lives = lives + end + + def animal.num_of_lives + @num_of_lives + end + + cat = animal.clone + cat.num_of_lives = 9 + + felix = cat.clone + assert_equal 9, felix.num_of_lives + end + +end diff --git a/src/about_singleton_methods.rb b/src/about_singleton_methods.rb new file mode 100644 index 0000000..d29dfd9 --- /dev/null +++ b/src/about_singleton_methods.rb @@ -0,0 +1,33 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutSingletonMethods < EdgeCase::Koan + + def test_instance_method_calls_method_on_class + animal = "cat" + assert_equal "CAT", animal.upcase + end + + def test_instance_method_calls_method_on_parent_class_if_not_found_in_class + animal = "cat" + assert_equal false, animal.frozen? + end + + def test_singleton_method_calls_method_on_anonymous_or_ghost_or_eigen_class + animal = "cat" + def animal.speak + "miaow" + end + assert_equal "miaow", animal.speak + end + + def test_singleton_method_is_available_only_on_that_instance + cat = "cat" + def cat.speak + "miaow" + end + dog = "dog" + assert_raises(NoMethodError) do + dog.speak + end + end +end diff --git a/src/about_template.rb b/src/about_template.rb new file mode 100644 index 0000000..8f92387 --- /dev/null +++ b/src/about_template.rb @@ -0,0 +1,8 @@ +require File.expand_path(File.dirname(__FILE__) + '/edgecase') + +class AboutTemplate < EdgeCase::Koan + + def test_foo + assert_equal __, true + end +end diff --git a/koans/edgecase.rb b/src/edgecase.rb similarity index 100% rename from koans/edgecase.rb rename to src/edgecase.rb diff --git a/src/path_to_enlightenment.rb b/src/path_to_enlightenment.rb new file mode 100644 index 0000000..96cb226 --- /dev/null +++ b/src/path_to_enlightenment.rb @@ -0,0 +1,18 @@ +# The path to Ruby Metaprogramming Enlightenment starts with the following: + +$LOAD_PATH << File.dirname(__FILE__) + +require 'about_metaclass' +require 'about_singleton_methods' +require 'about_class_as_constant' +require 'about_class_methods' +require 'about_prototype_inheritance' +require 'about_class_inheritance' +require 'about_modules' +require 'about_blocks' +require 'about_binding' +require 'about_define_method' +require 'about_instance_eval_and_class_eval' +require 'about_hook_methods' +require 'about_method_added' +require 'about_method_missing' diff --git a/koans/test_helper.rb b/src/test_helper.rb similarity index 100% rename from koans/test_helper.rb rename to src/test_helper.rb