-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathabout_class_methods.rb
More file actions
228 lines (191 loc) · 6.53 KB
/
about_class_methods.rb
File metadata and controls
228 lines (191 loc) · 6.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
require File.expand_path(File.dirname(__FILE__) + '/neo')
class AboutClassMethods < Neo::Koan
class Dog
end
def test_objects_are_objects
fido = Dog.new
assert_equal true, fido.is_a?(Object)
end
def test_classes_are_classes
assert_equal true, Dog.is_a?(Class)
end
def test_classes_are_objects_too
assert_equal true, Dog.is_a?(Object)
end
def test_objects_have_methods
fido = Dog.new
assert fido.methods.size > 0
end
# The pedagogical point is that instances and classes have different sets of
# methods, and you can see what instance methods and class methods are by
# calling methods. We already know that classes themselves are just
# Class instances.
def test_classes_have_methods
assert Dog.methods.size > 0
end
# Learned something new! You can add a method onto just one particular
# instance of a class. These are known as singleton methods. It seems to
# have limited utility at first glance.
def test_you_can_define_methods_on_individual_objects
fido = Dog.new
def fido.wag
:fidos_wag
end
assert_equal :fidos_wag, fido.wag
end
def test_other_objects_are_not_affected_by_these_singleton_methods
fido = Dog.new
rover = Dog.new
def fido.wag
:fidos_wag
end
assert_raise(NoMethodError) do
rover.wag
end
end
# ------------------------------------------------------------------
class Dog2
def wag
:instance_level_wag
end
end
def Dog2.wag
:class_level_wag
end
def test_since_classes_are_objects_you_can_define_singleton_methods_on_them_too
assert_equal :class_level_wag, Dog2.wag
end
def test_class_methods_are_independent_of_instance_methods
fido = Dog2.new
assert_equal :instance_level_wag, fido.wag
assert_equal :class_level_wag, Dog2.wag
end
# ------------------------------------------------------------------
class Dog
attr_accessor :name
end
def Dog.name
@name
end
# Somewhat counterintuitively, you can create instance variables inside a
# class object. Having Dog.name return @name has it return the instance
# variable defined in the class object itself
def test_classes_and_instances_do_not_share_instance_variables
fido = Dog.new
fido.name = "Fido"
assert_equal 'Fido', fido.name
assert_equal nil, Dog.name
end
# ------------------------------------------------------------------
class Dog
def Dog.a_class_method
:dogs_class_method
end
end
# Can define it as either Dog.a_class_method or self.a_class_method
def test_you_can_define_class_methods_inside_the_class
assert_equal :dogs_class_method, Dog.a_class_method
end
# ------------------------------------------------------------------
# I'm uncertain what the deeper meaning of this koan is...
LastExpressionInClassStatement = class Dog
21
end
def test_class_statements_return_the_value_of_their_last_expression
assert_equal 21, LastExpressionInClassStatement
end
# ------------------------------------------------------------------
SelfInsideOfClassStatement = class Dog
self
end
def test_self_while_inside_class_is_class_object_not_instance
assert_equal true, Dog == SelfInsideOfClassStatement
end
# ------------------------------------------------------------------
class Dog
def self.class_method2
:another_way_to_write_class_methods
end
end
def test_you_can_use_self_instead_of_an_explicit_reference_to_dog
assert_equal :another_way_to_write_class_methods, Dog.class_method2
end
# ------------------------------------------------------------------
# Boggling my mind a little bit here. First time running into this way of
# defining a class method. Did some research, came up with this:
#
# http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self
#
# In a nutshell:
#
# Every object belongs to its own invisible metaclass. This is what allows
# us to define methods directly on instances of an object that are not
# available to instances of the same class: the methods are defined on the
# metaclass of the instance.
#
# When we open up a class, self is defined as the instance of the class
# itself. Thus, doing "def some_method" will add some_method to instances
# of that class, but doing "def self.some_method" will add some_method to
# the class instance itself.
#
# class_eval allows you to pass in a string or block and defines self as the
# class instance. Thus, passing in "def some_method" to class_eval would
# define it for instances of that class. (Note: class_eval is an alias for
# module_eval, and it's only available in Modules and in the Class class
# instance).
#
# We don't even need to open the class to define a class method on it. We can
# add a method to the class by adding it to its metaclass:
# def Person.some_method ; 'blahblah'; end;
#
# class << Person is Ruby shorthand syntax for accessing an object's
# metaclass directly. That means we can do this:
# class << Person
# def some_method...
#
# and now Person will respond to some_method, but semantically, that method
# is actually defined in the class object's metaclass and not the class
# object itself.
#
# Finally, instance_eval splits self into two selves:
# 1. When defining a method, self is the metaclass. Therefore, methods
# are defined as "class methods"
# 2. Otherwise, self refers to the class instance itself.
class Dog
class << self
def another_class_method
:still_another_way
end
end
end
def test_heres_still_another_way_to_write_class_methods
assert_equal :still_another_way, Dog.another_class_method
end
# THINK ABOUT IT:
#
# The two major ways to write class methods are:
# class Demo
# def self.method
# end
#
# class << self
# def class_methods
# end
# end
# end
#
# Which do you prefer and why?
# Are there times you might prefer one over the other?
#
# Answer:
# I prefer self.method because:
# 1. It's more readable. I can't mistake self.method as being an instance
# method, but I can with class << self.
# 2. Less indentation/nesting.
#
# ------------------------------------------------------------------
def test_heres_an_easy_way_to_call_class_methods_from_instance_methods
fido = Dog.new
assert_equal :still_another_way, fido.class.another_class_method
end
end