1 require File.expand_path(File.dirname(__FILE__) + '/edgecase')
3 class AboutMessagePassing < EdgeCase::Koan
11 def test_methods_can_be_called_directly
12 mc = MessageCatcher.new
17 def test_methods_can_be_invoked_by_sending_the_message
18 mc = MessageCatcher.new
20 assert mc.send(:caught?)
23 def test_methods_can_be_invoked_more_dynamically
24 mc = MessageCatcher.new
26 assert mc.send("caught?")
27 assert mc.send("caught" + __ ) # What do you need to add to the first string?
28 assert mc.send("CAUGHT?".____ ) # What would you need to do to the string?
31 def test_send_with_underscores_will_also_send_messages
32 mc = MessageCatcher.new
34 assert_equal __, mc.__send__(:caught?)
38 # Why does Ruby provide both send and __send__ ?
41 def test_classes_can_be_asked_if_they_know_how_to_respond
42 mc = MessageCatcher.new
44 assert_equal __, mc.respond_to?(:caught?)
45 assert_equal __, mc.respond_to?(:does_not_exist)
48 # ------------------------------------------------------------------
51 def add_a_payload(*args)
56 def test_sending_a_message_with_arguments
57 mc = MessageCatcher.new
59 assert_equal __, mc.add_a_payload
60 assert_equal __, mc.send(:add_a_payload)
62 assert_equal __, mc.add_a_payload(3, 4, nil, 6)
63 assert_equal __, mc.send(:add_a_payload, 3, 4, nil, 6)
66 # ------------------------------------------------------------------
71 def test_sending_undefined_messages_to_a_typical_object_results_in_errors
72 typical = TypicalObject.new
74 exception = assert_raise(___) do
77 assert_match(/foobar/, exception.message)
80 def test_calling_method_missing_causes_the_no_method_error
81 typical = TypicalObject.new
83 exception = assert_raise(___) do
84 typical.method_missing(:foobar)
86 assert_match(/foobar/, exception.message)
90 # If the method :method_missing causes the NoMethodError, then
91 # what would happen if we redefine method_missing?
95 # In Ruby 1.8 the method_missing method is public and can be
96 # called as shown above. However, in Ruby 1.9 the method_missing
97 # method is private. We explicitly made it public in the testing
98 # framework so this example works in both versions of Ruby. Just
99 # keep in mind you can't call method_missing like that in Ruby
102 # Thanks. We now return you to your regularly scheduled Ruby
106 # ------------------------------------------------------------------
108 class AllMessageCatcher
109 def method_missing(method_name, *args, &block)
110 "Someone called #{method_name} with <#{args.join(", ")}>"
114 def test_all_messages_are_caught
115 catcher = AllMessageCatcher.new
117 assert_equal __, catcher.foobar
118 assert_equal __, catcher.foobaz(1)
119 assert_equal __, catcher.sum(1,2,3,4,5,6)
122 def test_catching_messages_makes_respond_to_lie
123 catcher = AllMessageCatcher.new
125 assert_nothing_raised(NoMethodError) do
128 assert_equal __, catcher.respond_to?(:any_method)
131 # ------------------------------------------------------------------
133 class WellBehavedFooCatcher
134 def method_missing(method_name, *args, &block)
135 if method_name.to_s[0,3] == "foo"
138 super(method_name, *args, &block)
143 def test_foo_method_are_caught
144 catcher = WellBehavedFooCatcher.new
146 assert_equal __, catcher.foo_bar
147 assert_equal __, catcher.foo_baz
150 def test_non_foo_messages_are_treated_normally
151 catcher = WellBehavedFooCatcher.new
154 catcher.normal_undefined_method
158 # ------------------------------------------------------------------
160 # (note: just reopening class from above)
161 class WellBehavedFooCatcher
162 def respond_to?(method_name)
163 if method_name.to_s[0,3] == "foo"
171 def test_explicitly_implementing_respond_to_lets_objects_tell_the_truth
172 catcher = WellBehavedFooCatcher.new
174 assert_equal __, catcher.respond_to?(:foo_bar)
175 assert_equal __, catcher.respond_to?(:something_else)