Ruby is an object-oriented programming language; that much, everyone knows. But Ruby's objects work very differently from many other languages, especially if you're coming from a complied, statically typed language. In this lecture, I'll review the surprisingly simple rules that govern Ruby's objects. I discuss methods, classes, instances, and modules, and how these various pieces fit together into an integrated whole -- including such topics as inheritance, instance variables, class variables, mixins, and "include" vs. "extend".
3. Warning!
• We’ll be getting into seriously hairy object
stuff today.
• This is both fun and useful, but can be
tricky.
• Please stop me and ask questions whenever
you need!
4. Dynamic typing
• Ruby uses dynamic typing — meaning that
variables don’t have types
>> a = [1,2,3]
=> [1, 2, 3]
>> a = 'hello'
=> "hello"
>> a = {:a => 5, :b => 6}
=> {:b=>6, :a=>5}
5. Strict typing
• But data does have types, and those types are
strictly enforced:
>> puts 5 + "5"
TypeError: String can't be coerced
into Fixnum
from (irb):30:in `+'
from (irb):30
from :0
6. Everything is an object!
• Numbers and Strings
• Arrays and hashes
• true, false, nil
• Regular expressions, ranges
• Files
• Classes, Modules
7. Object ID?
• Every object in Ruby has an object ID, a
unique integer
• Builtin objects have permanent IDs
• Objects that you define or create have
their own object IDs
• If the object IDs are equal, then we’re
talking about the same object
9. What is equality?
• You can compare them with ==, to see if
their contents are equal
• You can compare their object IDs, to see if
they point to the same piece of data
10. That’s nice —
but what is an object?
• Every object is two things:
• Some state (instance variables)
• A pointer to a class
11. OK, and what is a class?
• A class is four things:
• Some state (because they’re objects)
• A pointer to a class (because they’re
objects)
• A parent class
• Zero or more methods
12. Whoa!
• A class is an object?
• Yes.
• What is its class?
• Class. (Which is a constant identifier.)
• So a class is an instance of Class?
• Yes.
13. Examples
>> 60.class
=> Fixnum
>> 60.class.class
=> Class
>> "hello".class
=> String
>> "hello".class.class
=> Class
14. Let’s define a class
>> class MyClass
>> end
>> o = MyClass.new
=> #<MyClass:0x102ffdef0>
>> o.class
=> MyClass
15. Let’s define a class
>> class MyClass
Class definition
>> end
>> o = MyClass.new
=> #<MyClass:0x102ffdef0>
>> o.class
=> MyClass
16. Let’s define a class
>> class MyClass
Class definition
>> end
>> o = MyClass.new New object, class is MyClass
=> #<MyClass:0x102ffdef0>
>> o.class
=> MyClass
17. Let’s define a class
>> class MyClass
Class definition
>> end
>> o = MyClass.new New object, class is MyClass
=> #<MyClass:0x102ffdef0>
>> o.class
See? I told you!
=> MyClass
18. Methods
• Methods are defined on classes
• When we invoke a method on object o,
Ruby looks in o’s class (MyClass) for a
method of that name
• Ruby does this at runtime
• Ruby has only one type of method!
• No static, virtual, final, abstract, etc.
19. Method example
>> class MyClass
>> def say_hello
>> puts "hello"
>> end
>> end
=> nil
>> o = MyClass.new
=> #<MyClass:0x1036160d0>
>> o.say_hello
hello
=> nil
20. Method example
>> class MyClass
>> def say_hello
>> puts "hello" Method definition
>> end
>> end
=> nil
>> o = MyClass.new
=> #<MyClass:0x1036160d0>
>> o.say_hello
hello
=> nil
21. Method example
>> class MyClass
>> def say_hello
>> puts "hello" Method definition
>> end
>> end
=> nil
>> o = MyClass.new New instance
=> #<MyClass:0x1036160d0>
>> o.say_hello
hello
=> nil
22. Method example
>> class MyClass
>> def say_hello
>> puts "hello" Method definition
>> end
>> end
=> nil
>> o = MyClass.new New instance
=> #<MyClass:0x1036160d0>
>> o.say_hello
hello Invocation of method
=> nil
23. No such method?
• What if we invoke a method that doesn’t
exist?
>> o.say_goodbye
NoMethodError: undefined method
`say_goodbye' for #<MyClass:
0x1036160d0>
from (irb):17
24. method_missing
>> class MyClass
>> def method_missing(name)
>> puts "You tried to execute the '#{name}'
method. Too bad!"
>> end
>> end
=> nil
>> o.say_goodbye
You tried to execute the 'say_goodbye' method. Too
bad!
=> nil
25. method_missing
>> class MyClass Symbol with method name
>> def method_missing(name)
>> puts "You tried to execute the '#{name}'
method. Too bad!"
>> end
>> end
=> nil
>> o.say_goodbye
You tried to execute the 'say_goodbye' method. Too
bad!
=> nil
26. Method search order
• Ruby looks for a matching method in the
object’s class
• If none is found, it tries in each ancestor
class, in order
• The class method “ancestors” returns an
array of classes (and modules)
• If no match found, invokes method_missing
27. self
• The “self” keyword is built into Ruby
• It refers to the current object — the
default receiver of messages
• Knowing the value of “self” during each
point of execution is important
28. Inheritance!
• Every object has a class
• Every class (other than Object) has a
superclass
• We’ll look for methods in every parent
class until we find a match or die trying
29. Inheritance
• When a class is defined, we can set its
superclass
class MyClass # inherits from Object
class MyClass < YourClass
• Single inheritance only!
30. Superclass
• How can we find the superclass? Ask the
class!
>> o.class
=> MyClass
>> o.class.superclass
=> Object
>> o.class.superclass.superclass
=> nil
31. Methods are messages
• When you say
o.say_hello
• You’re really invoking the “send” method:
o.send(:say_hello)
32. Mini-message sender
>> def send_a_message
>> s = ('a'..'z').to_a.join
>> print "Enter a method name: "
>> puts s.send(gets.chomp.to_sym)
>> end
33. Method protection
• Methods are public by default
• You may declare a method “public,”
“private,” or “protected”
34. public
• Anyone can invoke the method
o.a_method
o.send(:a_method) # same thing
o.a_method(an_argument)
o.send(:a_method, an_argument)
35. private
• Only self can invoke private methods
• In other words, you cannot invoke
self.method_name or o.method_name
• And certainly not o.send(:method_name)
• Only instances of the class in which it was
defined (and its subclasses) may use it
36. protected
• Not commonly used, in part because it’s
hard to understand
• Only if the sender and receiver of the
message inherit the method from a
common ancestor, may it be invoked
37. Listing methods
• Get an array of method names (as strings)
o.methods
o.public_methods # same as o.methods
o.protected_methods
o.private_methods
38. What about data?
• We said earlier that an object is data and a
class pointer, and that methods sit on the
class
• So, where’s the data?
39. Instance variables
• Variables that are defined on an object, or
“instance variables,” have a @ prefix
• Often defined in the constructor
• If accessed without being set, equal to nil
• Unavailable outside of the object itself!
• You must use methods to set or retrieve
instance variables
42. Using instance variables
class Person
def initialize(first_name='', last_name='')
@first_name = first_name
@last_name = last_name
end
def name
@first_name + ' ' + @last_name
end
end
43. Defining getters
• Define VARNAME as a method:
def first_name
@first_name
end
puts o.first_name
44. Defining setters
• Define VARNAME= as a method:
def first_name=(value)
@first_name = value
end
o.first_name = 5
45. Attributes
• Instead of defining setters and getters for all
of your instance variables, create “attributes”
attr_reader :foo # read-only
attr_writer :bar # write-only
attr_accessor :baz # read-write
46. Attribute example
class Person
attr_accessor :first_name
attr_accessor :last_name
def initialize(first_name='', last_name='')
@first_name = first_name
@last_name = last_name
end
end
47. Attribute example
class Person
attr_accessor :first_name
attr_accessor :last_name
def initialize(first_name='', last_name='')
@first_name = first_name
Still need to initialize
@last_name = last_name our variables
end
end
48. The story so far
• Data is private, stored in @variables
• Access to data is handled via methods
• Methods are on an object’s class, and can
be kept private (but are public by default)
• The first matching method in the class’s
ancestry is invoked
• No match? method_missing or exception
49. Wait a second...
• When I use ActiveRecord, I say
Person.find(:all)
• What does that mean? How does that fit
into what we already know?
50. Person.find
• Person.find is a method
• Methods are defined on the class of the
object
• Person is an instance of Class
• So Person’s methods are defined in Class?
• No, but that’s not a bad guess!
51. Indeed, we can do that
>> class Person
>> end
=> nil
>> Person.blah
NoMethodError: undefined method `blah' for
Person:Class
from (irb):33
from :0
52. Class methods
>> class Class
>> def blah
>> puts "blah!"
>> end
>> end
=> nil
>> Person.blah
blah!
=> nil
>> Fixnum.blah
blah!
=> nil
53. Class methods
>> class Class
Open up the Class class
>> def blah
>> puts "blah!" and define a method
>> end
>> end
=> nil
>> Person.blah
blah!
=> nil
>> Fixnum.blah
blah!
=> nil
54. Class methods
>> class Class
Open up the Class class
>> def blah
>> puts "blah!" and define a method
>> end
>> end
=> nil
>> Person.blah Yes, we can define
blah! class methods this way
=> nil
>> Fixnum.blah
blah!
=> nil
55. Class methods
>> class Class
Open up the Class class
>> def blah
>> puts "blah!" and define a method
>> end
>> end
=> nil
>> Person.blah Yes, we can define
blah! class methods this way
=> nil
>> Fixnum.blah
blah!
Er, if we want them
=> nil to be defined for all classes
56. Hmm.
• Class methods exist.
• Methods are defined on an object’s class.
• We know that Person.find isn’t defined on
Class, because it is specific to ActiveRecord.
• So where are class methods for Person
defined?
57. Singleton methods
• Not the “singleton” design pattern!
• A very unfortunate term
• Define a method on an object, not a class
• Occasionally useful for specific needs
• Important as a tool for understanding
58. Singleton example
>> a = "hello"
>> def a.say_something(something)
>> puts something
>> end
>> a.say_something("hello")
=> hello
>> "another_string".say_something("hello")
NoMethodError: undefined method `say_something'
for "another_string":String
from (irb):47
from :0
59. Singleton example
>> a = "hello"
>> def a.say_something(something)
>> puts something
>> end
>> a.say_something("hello")
=> hello
>> "another_string".say_something("hello")
NoMethodError: undefined method `say_something'
for "another_string":String
from (irb):47
from :0
60. Singleton example
>> a = "hello"
>> def a.say_something(something)
>> puts something
>> end Notice the method is
>> a.say_something("hello") defined on a!
=> hello
>> "another_string".say_something("hello")
NoMethodError: undefined method `say_something'
for "another_string":String
from (irb):47
from :0
61. Singleton methods
• We add a method to a particular instance
• But wait — methods only exist on classes
• What the heck is going on here?
65. Eigenclasses
Object
String Class is String
Eigenclass New class in the middle
a
66. Eigenclasses
Object
String Class is String
Eigenclass New class in the middle
a
Singleton method is defined here
67. Eigenclasses?!?
• Also known as “ghost classes” — each
object has its own eigenclass for private
method storage
• When you define a singleton method on an
object, you’re creating an eigenclass that
sits between the object and its class
• Singleton methods are defined in the
eigenclass
68. Singleton override
>> a = 'abc' >> a.reverse
=> "abc" => "ABC"
>> b = 'def' >> b.reverse
=> "def" => "fed"
>> def a.reverse >> b = a
>> upcase => "abc"
>> end >> b.reverse
=> nil => "ABC"
69. Defining class methods
• We define a singleton method by naming
both the object and the method name:
>> a = "hello"
>> def a.say_something(something)
>> puts something
>> end
70. Defining class methods
• So we can define a class method by naming
both the object and the method name:
>> def Person.say_something(something)
>> puts something
>> end
=> nil
>> Person.say_something('hello')
hello
=> nil
71. Instance vs. class
methods
• Rule: Methods for an object reside in its
class
• Methods for p (an instance of Person) are
defined in Person
• Methods for Person — class methods —
are defined in Person’s eigenclass
• Eigenclass of a class == “metaclass”
72. Define class methods, 1
>> class Person
>> def Person.foo
>> "foo"
>> end
>> end
=> nil
>> Person.foo
=> "foo"
73. Define class methods, 1
>> class Person
>> def Person.foo Defining on Person
>> "foo"
>> end
>> end
=> nil
>> Person.foo
=> "foo"
74. Define class methods, 2
>> class Person
>> def self.bar
>> "bar"
>> end
>> end
=> nil
>> Person.bar
=> "bar"
75. Define class methods, 2
>> class Person
>> def self.bar “self” in this
>> "bar"
context is Person
>> end
>> end
=> nil
>> Person.bar
=> "bar"
76. Define class methods, 3
>> class Person
>> class << self
>> def baz
>> "baz"
>> end
>> end
>> end
=> nil
>> Person.baz
=> "baz"
77. Define class methods, 3
>> class Person
>> class << self Add to Person’s
>> def baz eigenclass!
>> "baz"
>> end
>> end
>> end
=> nil
>> Person.baz
=> "baz"
78. Why do we care?
• Why should I care about eigenclasses?
• How does this affect me?
• Couldn’t you think of a sillier name?
79. It’s really important
• Once you understand eigenclasses, you
basically understand the entire Ruby object
model
• There are no exceptions to this rule of
how things work
• Suddenly, instance methods, class methods,
and modules fall into place
80. Oh, and by the way
• We still haven’t explained how we defined
Person.find
• We know how to define a class method
on a single class
• But how do we define a class method for
anything inheriting from
ActiveRecord::Base?
81. Answer
• Define it on the eigenclass of the base class!
>> class A
>> class << self
>> def boo
>> "boo"
>> end
>> end
>> end
=> nil
82. That’s right:
• OK, I admit it: Ruby is a bit sneaky here.
• The superclass of the eigenclass of an
object is the object’s class.
• But the superclass of a class’s eigenclass is
the eigenclass of the class’s superclass.
• Or if you prefer: The superclass of the
metaclass is the metaclass of the superclass.
83. More on Eigenclasses
Class
Eigenclass of
ActiveRecord::Base
Class is Class
Person
84. More on Eigenclasses
Class Class is Class
Eigenclass of
ActiveRecord::Base
Person
85. More on Eigenclasses
Class Class is Class
Eigenclass of
ActiveRecord::Base
Eigenclass
Person
86. More on Eigenclasses
Class Class is Class
Eigenclass of
ActiveRecord::Base
Eigenclass New class in the middle
Person
87. More on Eigenclasses
Class Class is Class
Eigenclass of
ActiveRecord::Base
Eigenclass New class in the middle
Person
“find” is defined here
88. More on Eigenclasses
Class Class is Class
Eigenclass of
ActiveRecord::Base
Eigenclass New class in the middle
Person
“find” is defined here
91. Modules
• Modules have two uses:
• Namespaces
• Keep related code together, for inclusion
as a whole later on
92. Modules vs. Classes
• Both are capitalized (i.e., they’re constants)
• Both can contain methods
• Classes are modules (but modules are not
classes)
• They may contain constants
• “self” inside refers to the module/class
93. Modules vs. classes
• Regular (instance) methods defined in a
module cannot be accessed directly
• Module methods defined in a module may
be accessed just like class methods
• They’re also defined just like class
methods
97. Module methods, 2
>> module Blah
>> def self.foo self here is Blah
>> "foo"
>> end
>> end
=> nil
>> Blah.foo
=> "foo"
98. Module methods, 3
>> module Blah
>> class << self
>> def baz
>> "baz"
>> end
>> end
>> end
=> nil
>> Blah.baz
=> "baz"
99. Module methods, 3
>> module Blah
>> class << self Add to Blah’s
>> def baz eigenclass!
>> "baz"
>> end
>> end
>> end
=> nil
>> Blah.baz
=> "baz"
100. Constants in modules
>> Math::E
=> 2.71828182845905
>> Math::PI
=> 3.14159265358979
>> Math.PI
NoMethodError: undefined method
`PI' for Math:Module
from (irb):99
101. Constants in modules
>> Math::E
=> 2.71828182845905
>> Math::PI
=> 3.14159265358979
>> Math.PI Methods aren’t constants!
NoMethodError: undefined method
`PI' for Math:Module
from (irb):99
102. Nested modules
>> module Stuff
>> module Things
>> def self.hello
>> "hello"
>> end
>> end
>> end
=> nil
>> Stuff::Things.hello
=> "hello"
103. Nested modules
>> module Stuff
>> module Things
>> def self.hello
>> "hello"
>> end
>> end
>> end
=> nil
>> Stuff::Things.hello
=> "hello"
104. Nested modules
>> module Stuff
>> module Things
>> def self.hello
>> "hello"
>> end
>> end
>> end :: is for namespaces,
=> nil . is for method calls
>> Stuff::Things.hello
=> "hello"
105. include
• Modules are truly useful because you can
“include” their methods into a class
• This is sometimes known as “mix-ins” —
you mix the methods from a module into
your current class
106. Inclusion rules
• “include” any defined module into any class
• The module is added to the ancestor
chain, right above the class itself
• The module’s methods become instance
methods for your class — because
instances get methods from their class!
107. include
>> module Foo
>> def yikes
>> "yikes!"
>> end
>> end
>> class Person
>> include Foo
>> end
>> Person.new.yikes
=> "yikes!"
108. include
>> module Foo
>> def yikes
>> "yikes!"
>> end
>> end
>> class Person
>> include Foo
>> end
>> Person.new.yikes Modules are objects,
=> "yikes!"
so you don’t use quotes
109. How does this work?
>> class Person
>> end >> class Person
=> nil >> include MyModule
>> end
>> Person.ancestors => Person
=> [Person, Object,
Wirble::Shortcuts, >> Person.ancestors
PP::ObjectMixin,
=> [Person, MyModule,
Kernel]
Object,
Wirble::Shortcuts,
>> module MyModule PP::ObjectMixin,
>> end Kernel]
=> nil
110. How does this work?
>> class Person
>> end >> class Person
=> nil >> include MyModule
>> end
>> Person.ancestors => Person
=> [Person, Object,
Wirble::Shortcuts, >> Person.ancestors
PP::ObjectMixin,
=> [Person, MyModule,
Kernel]
Object,
Wirble::Shortcuts,
>> module MyModule PP::ObjectMixin,
MyModule was added to
>> end Kernel]
Person’s ancestors
=> nil
111. More than one include
>> Person.ancestors
=> [Person, MyModule, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel]
>> module MyModule2
>> end
=> nil
>> class Person
>> include MyModule2
>> end
=> Person
>> Person.ancestors
=> [Person, MyModule2, MyModule, Object,
Wirble::Shortcuts, PP::ObjectMixin, Kernel]
112. More than one include
>> Person.ancestors
=> [Person, MyModule, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel]
>> module MyModule2
>> end
=> nil
>> class Person include adds the new module
>>
>>
include MyModule2
end
right after the class itself
=> Person
>> Person.ancestors
=> [Person, MyModule2, MyModule, Object,
Wirble::Shortcuts, PP::ObjectMixin, Kernel]
113. Implications
• Import as many modules as you want!
• Sort of like multiple inheritance
• The most recent import “wins”
• Modules cannot override previously
defined methods in a class
114. Enumerable
• The most popular mixin module is
Enumerable
• If your class defines “each”, then you get a
huge number of methods as a result
115. Class methods
• What if you want a module to define class
methods?
• Import the module into the eigenclass, of
course!
116. Regular include
>> module MyModule >> i =
InstanceInclude.new
>> def hello
=> #<InstanceInclude:
>> "hello"
0x103624220>
>> end
>> i.hello
>> end
=> "hello"
=> nil
>> InstanceInclude.hello
>> class InstanceInclude
NoMethodError: undefined
>> include MyModule
method `hello' for
>> end InstanceInclude:Class
=> InstanceInclude from (irb):11
117. Eigenclass include
>> class ClassInclude
>> class << self
>> include MyModule
>> end
>> end
=> #<Class:ClassInclude>
>> c = ClassInclude.new
=> #<ClassInclude:0x1035f20e0>
>> c.hello
NoMethodError: undefined method `hello' for
#<ClassInclude:0x1035f20e0>
>> ClassInclude.hello
=> "hello"
118. Eigenclass include
>> class ClassInclude
>> class << self
>> include MyModule
>> end
>> end
=> #<Class:ClassInclude>
Here the eigenclass is “self,” so
>> c = ClassInclude.new
=> #<ClassInclude:0x1035f20e0>
>> c.hello include imports the module as
class methods
NoMethodError: undefined method `hello' for
#<ClassInclude:0x1035f20e0>
>> ClassInclude.hello
=> "hello"
119. extend
• Alternatively, we can use “extend,” which
does the same thing — it includes the
module on the eigenclass
• In other words, it imports the methods
from the module as class methods
120. extend
>> class ExtendInclude
>> extend MyModule
>> end
=> ExtendInclude
>> e = ExtendInclude.new
=> #<ExtendInclude:0x1035d22b8>
>> e.hello
NoMethodError: undefined method `hello' for
#<ExtendInclude:0x1035d22b8>
from (irb):24
>> ExtendInclude.hello
=> "hello"
121. Including and extending
• This is pretty magical, but useful
• When you include a module, the module’s
“included” method is invoked — and the
class that invoked it is passed as a
parameter
122. included
>> module Squealer
>> def self.included(base)
>> puts "Squealer was just included by
'#{base.to_s}'!"
>> end
>> end
=> nil
>> class Includer
>> include Squealer
>> end
Squealer was just included by 'Includer'!
=> Includer
123. module Foo
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def bar
puts 'class method'
end
end
def foo
puts 'instance method'
end
end
124. lib in Rails
• All of the files in the “lib” directory are
evaluated at startup time
• This means that you can define any number
of modules in those files
• Put common functionality there — put
common methods into a module, and then
include those methods in your classes
125. irb addon: looksee
• gem install looksee
• require 'looksee/shortcuts' # in .irbrc
• “lp varname” shows you which methods
came from which modules and classes
• Shows eigenclasses and metaclasses
• Useful and fun... but it apparently doesn’t
work with jruby. Sigh.
126. Contacting me
• Call me in Israel: 054-496-8405
• Call me in the US: 847-230-9795
• E-mail me: reuven@lerner.co.il
• Interrupt me: reuvenlerner (Skype/AIM)