45541a015325e58a558d8b90c21f7f70495861bb
[ruby_koans.git] / koans / about_message_passing.rb
1 require File.expand_path(File.dirname(__FILE__) + '/edgecase')
2
3 class AboutMessagePassing < EdgeCase::Koan
4
5   class MessageCatcher
6     def caught?
7       true
8     end
9   end
10
11   def test_methods_can_be_called_directly
12     mc = MessageCatcher.new
13
14     assert mc.caught?
15   end
16
17   def test_methods_can_be_invoked_by_sending_the_message
18     mc = MessageCatcher.new
19
20     assert mc.send(:caught?)
21   end
22
23   def test_methods_can_be_invoked_more_dynamically
24     mc = MessageCatcher.new
25
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?
29   end
30
31   def test_send_with_underscores_will_also_send_messages
32     mc = MessageCatcher.new
33
34     assert_equal __, mc.__send__(:caught?)
35
36     # THINK ABOUT IT:
37     #
38     # Why does Ruby provide both send and __send__ ?
39   end
40
41   def test_classes_can_be_asked_if_they_know_how_to_respond
42     mc = MessageCatcher.new
43
44     assert_equal __, mc.respond_to?(:caught?)
45     assert_equal __, mc.respond_to?(:does_not_exist)
46   end
47
48   # ------------------------------------------------------------------
49
50   class MessageCatcher
51     def add_a_payload(*args)
52       args
53     end
54   end
55
56   def test_sending_a_message_with_arguments
57     mc = MessageCatcher.new
58
59     assert_equal __, mc.add_a_payload
60     assert_equal __, mc.send(:add_a_payload)
61
62     assert_equal __, mc.add_a_payload(3, 4, nil, 6)
63     assert_equal __, mc.send(:add_a_payload, 3, 4, nil, 6)
64   end
65
66   # ------------------------------------------------------------------
67
68   class TypicalObject
69   end
70
71   def test_sending_undefined_messages_to_a_typical_object_results_in_errors
72     typical = TypicalObject.new
73
74     exception = assert_raise(___) do
75       typical.foobar
76     end
77     assert_match(/foobar/, exception.message)
78   end
79
80   def test_calling_method_missing_causes_the_no_method_error
81     typical = TypicalObject.new
82
83     exception = assert_raise(___) do
84       typical.method_missing(:foobar)
85     end
86     assert_match(/foobar/, exception.message)
87
88     # THINK ABOUT IT:
89     #
90     # If the method :method_missing causes the NoMethodError, then
91     # what would happen if we redefine method_missing?
92     #
93     # NOTE:
94     #
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
100     # 1.9. normally.
101     #
102     # Thanks.  We now return you to your regularly schedule Ruby
103     # Koans.
104   end
105
106   # ------------------------------------------------------------------
107
108   class AllMessageCatcher
109     def method_missing(method_name, *args, &block)
110       "Someone called #{method_name} with <#{args.join(", ")}>"
111     end
112   end
113
114   def test_all_messages_are_caught
115     catcher = AllMessageCatcher.new
116
117     assert_equal __, catcher.foobar
118     assert_equal __, catcher.foobaz(1)
119     assert_equal __, catcher.sum(1,2,3,4,5,6)
120   end
121
122   def test_catching_messages_makes_respond_to_lie
123     catcher = AllMessageCatcher.new
124
125     assert_nothing_raised(NoMethodError) do
126       catcher.any_method
127     end
128     assert_equal __, catcher.respond_to?(:any_method)
129   end
130
131   # ------------------------------------------------------------------
132
133   class WellBehavedFooCatcher
134     def method_missing(method_name, *args, &block)
135       if method_name.to_s[0,3] == "foo"
136         "Foo to you too"
137       else
138         super(method_name, *args, &block)
139       end
140     end
141   end
142
143   def test_foo_method_are_caught
144     catcher = WellBehavedFooCatcher.new
145
146     assert_equal __, catcher.foo_bar
147     assert_equal __, catcher.foo_baz
148   end
149
150   def test_non_foo_messages_are_treated_normally
151     catcher = WellBehavedFooCatcher.new
152
153     assert_raise(___) do
154       catcher.normal_undefined_method
155     end
156   end
157
158   # ------------------------------------------------------------------
159
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"
164         true
165       else
166         super(method_name)
167       end
168     end
169   end
170
171   def test_explicitly_implementing_respond_to_lets_objects_tell_the_truth
172     catcher = WellBehavedFooCatcher.new
173
174     assert_equal __, catcher.respond_to?(:foo_bar)
175     assert_equal __, catcher.respond_to?(:something_else)
176   end
177
178 end