Self and Other Objects
In my previous article about trip-ups, we saw that class-level methods are defined like this:
class MyClass
def self.hello
"hello"
end
end
MyClass.hello # => "hello"
Developers coming from other languages might be tempted to assume that def self.method_name
is some kind of special syntax for creating class methods. However, this is also possible:
class MyClass
def MyClass.hello
"hello"
end
end
MyClass.hello # => "hello"
And much more importantly, this is also possible:
MyClass = Class.new
def MyClass.hello
"hello"
end
MyClass.hello # => "hello"
So, you can see that Ruby lets you define methods on classes. But classes are just objects of class Class
, so logically you should be able to define methods on other kinds of objects, like a string.
a_string = "blerp"
def a_string.hello
"hello"
end
a_string.hello # => "hello"
As it turns out, def self.whatever
isn’t special syntax. Ruby allows you to define methods on objects, including classes, and on some kind of object named self
.
But what is self
?
For any line of Ruby code, there is a reference called self
. self
always refers to the current object at that point in the code, which is important because many other aspects of Ruby rely on it. For example, instance variables are just variables stored in self
.
# At the top level, self is a special object called "main"
self # => main
self.class # => Object
# class definitions change the value of self
class SelfClass
self # => SelfClass
# Will be called on instances
def self
self
end
# Same as SelfClass.self
def self.self
self
end
end
SelfClass.self # => SelfClass
SelfClass.new.self # => Instance of SelfClass
So who cares, right? Well, the fact that self
is always something has some interesting consequences:
- Instance variables can be assigned and resolved anywhere (although it might not make sense semantically, it’s always syntactically possible)
- Methods can be defined anywhere
This is markedly different from other languages, where there are strict rules about where certain things can happen.
Lazy Initialization
Developers who are accustomed to languages that require instance variables to be declared in classes might be tempted to “define” instance variables in constructors like this:
class Item
attr_accessor :title
def initialize
@title = ""
end
end
There are some problems that come with doing things this way:
- Subclasses might need to worry about calling
super
. - Objects get allocated regardless of whether they are used.
- It’s not possible to tell what a getter is going to return just by looking at it.
Part 1 showed that an unassigned instance variable returns nil
instead of raising an error. This can be combined with the ||=
operator to set default values without a constructor. It performs the assignment only if the left-hand operand is falsy, meaning nil
or false
.
class Item
attr_writer :title
def title
@title ||= ""
end
end
Now, it is obvious that when an Item
instance receives the #title
message, it will either return an empty string or the most recently assigned title. Even better, if we don’t care what an item’s title is, memory for it never gets allocated.
Scope Resolution Operator (::)
A problem can occur when there is a module or class in your code that has the same name as a core Ruby module that you want to use.
module MyModule
class File
end
class Thing
def exist?(path)
File.exist?(path)
end
end
end
thing = MyModule::Thing.new
thing.exist?('/etc/hosts') # => udefined method `exist?' for MyModule::File:Class
The easiest way to fix this is to add the ::
operator to the beginning of the colliding constant.
module MyModule
class File
end
class Thing
def exist?(path)
::File.exist?(path)
end
end
end
thing = MyModule::Thing.new
thing.exist?('/etc/hosts') # => true
Everything is Run Time
Historically, languages have made a distinction between what is declared and what is run. For example, in C++, classes are declared at compile-time, and instances are created and used at run-time. This can be frustrating, since compile-time code can be limited by a lack of information access.
In Ruby’s world, everything is run-time.
class Hello
puts "Hello World"
end
This example prints the string because Ruby class “declarations” are just code that is being run like anywhere else. That opens a window into all kinds of possibilities – not the least of which is writing code that writes code. It isn’t necessary to have a special plugin container for your app when the code can change the app itself.
For example, sometimes it might be useful to define methods dynamically:
print "What noise does the animal make?: "
speak_noise = gets
Animal = Class.new do
define_method :speak do
puts "Animal: #{speak_noise}"
end
end
Animal.new.speak
It’s also possible to inherit dynamically (mostly for impressing friends, but who knows?)
class RandomClass < [Array, Hash].sample
end
RandomClass.superclass # => either Array or Hash
Internal DSLs
Domain-Specific Languages enable developers to write elegant code that is tailored for particular situations. Example DSLs include RSpec, Cucumber, Prawn, and Shoes.
DSLs come in two flavors:
- internal – are based on a subset of the host language
- external – use a full-blown parser
Since Ruby is a flexible, syntax-noise free language, it is well-suited for developing internal DSLs. These are DSLs that are actually just Ruby, but tailored for a specific domain.
So, how does one go about creating one of these things?
In part 1, Ruby modules were shown to be packages for methods that could be mixed into classes. If you check a class’s #ancestors
, you will see any modules that have been included.
Looking at the ancestors of Object
, you will notice an odd module:
Object.ancestors #=> [Object, PP::ObjectMixin, Kernel, BasicObject]
Every object in Ruby ships with the Kernel
module included. Kernel
contains methods like #puts
and #lambda
. One way that DSLs are often created in Ruby is by adding methods to Kernel so that they will work at the top level.
module Kernel
def task(name, &block)
tasks << block
end
def tasks
@tasks || @tasks = []
end
def run_task
tasks.each { |t| t.call }
end
end
Now, in a file using our DSL, we can run:
task :hello do
puts "Hello world"
end
task :goodbye do
puts "Later world"
end
run_tasks
Of course, run_tasks
is superfluous and would probably be called underneath the hood in a real-world DSL.
instance_eval
and class_eval
DSLs would be pretty limited if defining methods in Kernel
was the only way to make them. Fortunately, Ruby provides #instance_eval
. When provided a block or a string, the contents will be evaluated within the context of the instance.
a_string = "word"
a_string.instance_eval("reverse") # => "drow"
Let’s make a more portable version of our task running DSL. When using #instance_eval
in this way, the methods defined form the keywords of the DSL. We’ll just make one DSL keyword: task
.
module TaskList
# Make every instance method a class method
# Shorter than putting "self" in front of every method
extend self
def run(&block)
instance_eval(&block)
process_tasks
end
private
def task(name, &block)
# Leaving the "&" off converts it to a storeable proc
tasks << block
end
def tasks
@tasks || @tasks = []
end
def process_tasks
tasks.each { |t| t.call }
end
end
TaskList.run do
task :hello do
puts "Hello"
end
task :goodbye do
puts "Goodbye"
end
end
That version uses #instance_eval
on a module. Don’t let that confuse you – modules are indeed instances of class Class
. Here is the instance in a typical sense version (i.e. an instance of a class that’s not Class
):
class TaskList
def run(&block)
instance_eval(&block)
process_tasks
end
private
def task(name, &block)
tasks << block
end
def tasks
@tasks || @tasks = []
end
def process_tasks
tasks.each { |t| t.call }
end
end
TaskList.new.run do
task :hello do
puts "Hello"
end
. . .
end
Unlike #instance_eval
, #module_eval
/ #class_eval
only works on modules and classes, respectively. An instance method defined in a #class_eval
block will be defined for all objects of that class. This can be useful for monkey-patching core classes.
String.class_eval do
def spaceify
self.split("").join(" ")
end
end
"hello".spaceify # => "h e l l o"
Privacy
Let’s say you have a class with some instance variables and a private method like this:
class Secret
def initialize
@hidden = 123
end
private
def hidden_method
"You can't call me!"
end
end
Since instance variables need methods to be manipulated from outside the class, they are private, right? Well…
secret = Secret.new
secret.instance_eval { @hidden } # => 123
secret.instance_eval { hidden_method } # => "You can't call me!"
Ok, but even with #instance_eval
, someone would need to know the names of instance variables and methods, right?
secret.instance_variables # => [:@hidden]
secret.private_methods # => [:initialize, :hidden_method]
It’s pretty easy to look inside a Ruby object and see what it owns and what it can do. This is known as reflection.
secret.hidden_method # => NoMethodError: private method `hidden_method'...
Calling private methods on an object won’t work. But what if we send a message to the object?
secret.send(:hidden_method) # => "This method is private - you can't call me!"
The way private methods actually work in Ruby is interesting in its simplicity. Private methods are just methods that cannot be called with an explicit receiver. This means that private methods can’t be called with an explicit receiver even if the explicit receiver is self
.
class Secret
def initialize
self.private_method
end
private
def private_method
"You can't call me!"
end
end
secret = Secret.new # => private method `private_method' called for...
As such, encapsulation in Ruby pretty much works on the honor system. There is no real privacy, only pseudo-privacy. Does that mean it’s acceptable to just go around breaking encapsulation? Well, no. If a developer made something private, it was probably for a good reason. Nevertheless, you will often see it, and it’s good to know why a developer might be sending a message to an object instead of just calling the method.
Murky Methods
Many programming languages rely on a strong relationship between what a class is and what it can do. In Ruby, this relationship breaks down. Rather than being concerned where an object comes from, it’s more important to think about what kind of messages it can receive.
In Ruby, if an object knows how to handle a message we send it, then it is probably the right kind of object. This way of thinking is commonly referred to as duck typing.
For example, this would not be good Ruby code:
if user.is_a? EmailUser
"Email:" #{user.email}"
end
Although such type checks can improve the robustness of our code, they completely destroy the decoupling benefits of duck typing. Instead, it’s better to ask whether a class can respond to the messages you #send
it.
if user.respond_to?(:email)
"Email: #{user.email}"
end
So far, nothing too strange. However, this is where things take a turn for the awesome. Since objects receive messages in order to call methods, it’s possible to intervene when a message is received. This allows calls to methods that don’t even exist. Such an ability enables the development of flexible classes that can include new features without the modification of a single line of code (for example, the client of a web API).
To see how we can do this, let’s look at the steps Ruby takes when an object receives a message:
- Is the method defined in the receiver’s class or anywhere in its ancestors?
- Is
#method_missing
defined in the receiver’s class or anywhere in its ancestors? - Raise
NoMethodError
So, to handle undefined methods, just define #method_missing
.
class MethodHandler
def method_missing(method_name, *args)
puts "Handling instance method #{method_name}"
puts "The arguments were: #{args.join(', ')}"
end
end
handler = MethodHandler.new
handler.iammadeup!(1, 2, "cat")
A common pattern is to check the method name against a regular expression, and otherwise Raise NoMethodError
by calling super
(unless the parent class has defined method_missing
). Note that the method name is a symbol, not a string. This is important to keep in mind when creating regular expressions, since symbols start with a colon. Some developers prefer to call it method_sym
to be more descriptive.
Unfortunately, Even if #method_missing
responds to a method, it does not mean that #respond_to?
or #method
will. Therefore, it’s important to always define #respond_to_missing
when defining #method_missing
.
Thoughtbot has a good post on the subject here.
Singleton Classes / Eigenclasses
It is difficult to explain what comes next. I think it is best introduced with a couple of statements:
- Methods are really defined on classes, not objects.
- Any Ruby object can have its own methods.
Sound contradictory? Let’s prove the second statement with some code.
word = "Hello"
def word.double
self * 2
end
puts word.double # => "HelloHello"
Both of these statements are true for Ruby. And yet – how? If methods live inside classes, how could an object have methods that are not defined for its class or its ancestors? There must be…another class. Somewhere, there must be a hidden class in which such methods live.
puts word.method(:length).owner # => "String"
puts word.method(:double).owner # => "#<Class:#<String:0x00000001bf4680>>"
And this is, indeed, the case.
It’s best to not get attached to any particular name for this hidden class, as several have been used over the years: metaclass, eigenclass, ghost class, singleton class, anonymous class. Officially, Ruby has adopted #singleton_class
in 1.9.2. Note that the word “singleton” is not being used in the singleton pattern sense. Since this can be confusing, the term “eigenclass” will be used in this article from here on out.
The canonical way to define things in an object’s eigenclass is to use
class << [some object]
notation.
letters = ['a', 'b', 'c']
class << letters
def capitalize
self.map do |letter|
letter.upcase
end
end
end
letters.capitalize #=> ['A', 'B', 'C']
Earlier we saw that private class methods are not so private. This can be fixed by defining the methods on the eigenclass.
class Secretive
class << self
private
def hidden
"This method is private - You can't call me!"
end
end
end
Secretive.hidden #=> private method `hidden' called for Secretive:Class (NoMethodError)
Making attr_accessor-like Methods
In part 1, we saw how Ruby lets you define getters and setters with the helper methods attr_reader
, attr_writer
, and attr_accessor
.
class Foo
attr_accessor :bar, :baz
end
What if we want to make your own helper methods like this? It makes sense to define a class-level method since self
is the class inside the class definition.
class Foo
def self.attr_printer(*properties)
puts "attr_printer called with #{properties}"
end
attr_printer :bar, :baz
end
This works, but defining the method inside each class that uses attr_printer
kind of defeats the purpose. We need to find another place to define it that will apply to all classes.
class Class
def attr_printer(*properties)
puts "attr_printer called with #{properties}"
end
end
class Something
attr_printer :attr_one, :attr_two, :attr_three
end
Now all classes have the new method, but we might not want that, either. Instead, it’s probably better to put the code into a module that can be included into classes individually, especially if there are varying implementations of the helper method. Here is a chance for us to leverage eigenclasses again.
module AttrPrinterable
def self.included(klass)
class << klass
def attr_printer(*properties)
puts "attr_printer called with #{properties}"
end
end
end
end
class Something
include AttrPrinterable
attr_printer :attr_one, :attr_two, :attr_three
end
Alternatively, you can use #instance_eval
on the Class
instance. Note that def attr_printer
and not def self.attr_printer
is used.
module AttrPrinterable
def self.included(klass)
klass.instance_eval do
def attr_printer(*properties)
puts "attr_printer called with #{properties}"
end
end
end
end
Summary
self
is always the current object.- Rather than declaring instance variables in constructors as in static languages, it’s sometimes better to take advantage of lazy initialization methods.
#instance_eval
runs a block or string in the context of an instance, including instances ofClass
- Use
#class_eval
to define methods for all instances of a class. - There is no difference between compile-time and run-time code in Ruby.
- In Ruby, a constant is any word that begins with a capital letter.
- Constants are mostly used for class and module names, never as enums like in other languages.
- The scope-resolution operator
::
is used to specify constants - Put
::
at the front of a constant to specify a root level constant. - Private methods are methods that cannot have an explicit receiver, including
self
. - Objects receive messages to call methods.
- The
.
operator is shorthand for#send
to send a message to an object #method_missing
can intervene when an object does not know how to respond to a message.- It’s important to define
#respond_to_missing
when defining#method_missing
. - An eigenclass or singleton class is a unique class an object is linked to.
- The
class <<
syntax can be used to open an object’s eigenclass, including a class object. - Class methods cannot be private, but eigenclass methods defined on classes can.
- Methods like
attr_accessor
can be defined as class-level methods of classes or instance-level methods ofClass
.
Robert is a voracious reader, Ruby aficionado, and other big words. He is currently looking for interesting projects to work on and can be found at his website.