• 20 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 10/4/17

Anatomy of a Ruby Object

Log in or subscribe for free to enjoy all this course has to offer!

 

"In Ruby, everything is an object."
-- https://www.ruby-lang.org/en/about/

One of the interesting features of Ruby is that every type of variable is actually an object, even things like numbers or the values True and False. To understand Ruby, it helps to understand objects.

First off, what is an object, even?

Objects are essentially collections of variables and methods.

This course assumes you've worked with variables before. More on them in a bit.

Methods

You may also be familiar with methods. They are little pieces of code that run when the method's name is sent as a *message* to an object.

Let's imagine that we have an object calledmy_object

We can "send a message" to it like this:

my_object.this_is_my_message

So, in this case, we callthis_is_my_messagethe "message", andmy_objectthe "receiver".

Ifmy_objecthas a method calledthis_is_my_message, it will run that method here. The value that results from running that method becomes the meaning of the expressionmy_object.this_is_my_message. We call that the return value of the method.

You create methods by using the special def keyword, followed by the receiver (ie the object), a period, and the method name:

def my_object.this_is_my_message
  "Hi, thanks for your message."
end

my_object.this_is_my_message
=> "Hi, thanks for your message."

 

Classes

In the examples above, it seems like the methods belong to the object that is using them, but that's misleading. In a sense, objects don't actually ever have methods of their own.

All of an object's methods are actually not defined on that object, but on another object that the object is linked to, called aclassobject. A class is just an object with a special ability to hold methods for use by other objects.

That's useful because it means that many objects can use the same methods without duplicating them over and over again.

This is pretty abstract, so let's try to picture it with an analogy.

Let's imagine a group of young scouts on a camping trip with their scout leaders. The scouts are all children — let's say they range in age from 8-10 — and their scout leaders are all adults. Each scout leader has their own troop of child scouts, and all the child scouts belong to only one troop. Each troop is responsible for a single thing. Let's say one troop is responsible for building fires, another troop is responsible for pitching tents, and a third troop is responsible for cooking the food.

Now, to handle these different responsibilities requires special equipment, which each of the scout leaders for each group holds in a huge pack on their backs. The scout leader for the fire building troop wears a big backpack with all the equipment in it for building a fire. The leader for the cooks wears a different big backpack with all the equipment in it for cooking food. And so on.

Next, let's imagine that the scout leaders never, ever want to take their backpacks off, because they're so heavy to pick up and put back on again. This means that the scout leader with the firebuilding gear in their pack can't actually build a fire. Instead they crouch down, and one of the young scouts in their troop goes in their bag, gets the tools, and builds the fire.

Ruby classes are the scout leaders. They carry methods that they can't actually make use of on their own. Instead, they enable other objects, their instances, to use them. The class's instances are the little scouts that belong to the leader's troop.

I said a moment ago that objects were essentially collections of variables and methods. Now you can see that that's not quite right. Actually, objects are collections of variables, and they can use methods that are being held by a class object that they "belong to".

Just as the scouts can only belong to one troop, objects can only "belong" to one class. They are said to be "instances" of their class.

Now, just as scout leaders are special because they are adults and carry backpacks, class objects in ruby are special in a number of ways.

  • To remind us of their special status, they usually have a name that starts with a capital letter.

  • Just as scout leaders might be able to sign up new scouts to their troops, Class objects in Ruby can create new instances using their special *new method.

  • Ruby provides a distinct way of creating class objects, by using the "class" keyword and giving it a name, like this:

class FireBuilderClassObject
end

Inside this class definition is where you can define the class' methods, ie, the contents of its backpack that others can use. You declare these methods as we declaredmy_object.this_is_my_messageabove, with one important difference: inside a class definition area, you can leave off the "receiver" part of the method definition to tell Ruby that the method goes in the class object's "backpack". Here's how that looks:

class FireBuilderClassObject
  def start_a_fire
    "it's burning now"
  end
end

So now we have a new class calledFireBuilderClassObject, and it has one method.

Just as theclasskeyword is paired with anendkeyword, so thedefkeyword opens the method definition, and the correspondingendstates that the method is over. We usually indent these pairs of opening/closing keywords so it's easy to see how they're nested inside each other, like an outline format for a document.

A note on return values

Note that unless you specify otherwise by using the wordreturnsomewhere in your method definition, the return value of the method is just going to be whatever the last line in the method definition is. If you want to return something else (and exit the method at that point) you can do it like this:

def hoberman_switch_pitch
  return :blue # because of the word "return" here, :blue is always the return value of this method
  [:blue, :yellow].sample # this line never gets run
end

Now, I said before that all Ruby objects have a class, and only one class. We also said that classes are objects too. It follows that class objects must themselves belong to a class. In our camping analogy, it means that there's a special scout leader, the "leader of the scout leaders", you could say. This chief scout leader has a backpack of tools that the other scout leaders can use to do their responsibilities, like signing up new scouts.

In Ruby terms, this chief scout leader is a class that contains useful methods for the class objects themselves to use. It is the class that all the other classes belong to. Just as each of the class objects has a capitalized name, the chief scout leader's name isClass.

The chief scout leader's backpack probably contains some registration forms, plus scout bandanas and patches and things, that allow it register new scouts in its troop. In the same way, the class calledClassallows its instances (who themselves are all classes, likeFireBuilderClassObjectin the example below) to access thenewmethod to create new instances of themselves:

fire_builder = FireBuilderClassObject.new

# fire_builder's class is FireBuilderClassObject, so like all instances of FireBuilderClassObject, fire_builder will have access to the "light_a_fire" method:

fire_builder.light_a_fire
=> "it's burning now"

You could create as many fire builders as you want:

fire_builder_2 = FireBuilderClassObject.new
fire_builder_3 = FireBuilderClassObject.new

and they can all call *light_a_fire.

Variables

A variable is a name that represents a bit of data, a value. In Ruby, variables always belong to some object. They can't float around on their own. There are a few types of variables:

Constant
Local
Instance

For now we're just interested in instance variables. They start with a @, and live internally inside objects. In fact, you have to be "inside" an object to use them.

Self

Just like in real life, when you're writing Ruby code there's always an invisible presence, the self.

self in Ruby is a special variable that changes meaning as you move around in your code. It means something like "the object I am inside of right now".

We said that everything in Ruby is an object. That also means that wherever you are, you're in an object. What object that is, at any given time, is indicated by the variable self.

By default in Ruby, self is set to a weird object called "main":

self
=> main

But once you're inside a method definition, self will change to mean the object that is currently calling the method:

class FireBuilderClassObject
  def who_am_i
    self
  end
end

fire_builder = FireBuilderClassObject.new
fire_builder.who_am_i
=> #<FireBuilderClassObject:0x007fdb67911f50>

Getting back to instance variables: when you create instance variables like this 

@my_var="some value"

they get automatically assigned to self. If you were inside a method definition when you declared your variable, like so:

def my_method
@my_var = "some_value"
end

then@my_varwill belong to the instance that is executing this method whenever it's called. In fact, that's why they are called "instance variables". "instance" essentially just means "object", and instance variables are the variables that belong to an object.

This behavior is the magic glue that binds together methods and variables on an object. Even though instances share their methods with other instances of their class, their variables are theirs alone.

For example: if we create a variable inside a method definition, it will now be part of the object that called the method:

class FireBuilderClassObject
  def fill_up_pockets_with_dry_sticks(how_many_sticks)
    @num_dry_sticks = how_many_sticks
  end
end

first_fire_builder = FireBuilderClassObject.new
=> #<FireBuilderClassObject:0x007fdb67911f50>
first_fire_builder.fill_up_pockets_with_dry_sticks(5)
=> 5

second_fire_builder = FireBuilderClassObject.new
=> #<FireBuilderClassObject:0x007f80ea89d3f8>
second_fire_builder.fill_up_pockets_with_dry_sticks(13)

first_fire_builder
=> #<FireBuilderClassObject:0x007fdb67911f50 @num_dry_sticks="5">
second_fire_builder
=> #<FireBuilderClassObject:0x007f80ea89d3f8 @num_dry_sticks="13">

Even though both fire builders are using the same methodfill_up_pockets_with_dry_sticks, the variable@num_dry_sticksdoes not refer to a shared collection of sticks. Instead, like all instance variables,@num_dry_sticksrefers to "however many sticks the object using this method has on them".

In Ruby's terms, the word "self" is used to refer to "the object using this method." The first time we calledfill_up_pockets_with_dry_sticksin the example above,selfwasfirst_fire_builder. The second time,selfwassecond_fire_builder.

Accessing variables in objects

You can't get to the@num_dry_sticksvariable insidefire_builderdirectly by trying to message the object:

fire_builder.@num_dry_sticks
SyntaxError: (irb):134: syntax error, unexpected tIVAR, expecting '('

To access an instance variable, you have to be somewhere in your code where "self" means "that variable's object". As I mentioned above, that happens inside method definitions. Since the Ruby interpreter changes the meaning of "self" to be "the calling object", we can access variables for an object inside methods it is calling:

class FireBuilderClassObject
  def report_on_stick_supply
    @num_dry_sticks
  end
end

first_fire_builder.report_on_stick_supply
=> 5

In terms of our analogy, then,first_fire_builderis a little scout who belongs to scout leaderFireBuilderClassObject's troop. When that scout is ordered toreport_on_stick_supplyorfill_up_pockets_with_dry_sticks(8), it looks in its leader's backpack. Let's say the "tools" in this case are a little manual on collecting sticks for firewood, and another manual on how to count sticks and report their number (after all, do broken sticks count as one stick or two? Maybe the scouts believe that these things have to be reported in a standardized manner...). These little manuals tell them just what to do in each case, using @num_dry_sticks to refer to the contents of the scout's pockets.

This description of objects and their instance variables, along with their classes, and their classes' instance methods, forms the core of how objects work in Ruby. In the next chapter, we'll take a look in greater depth at class objects.

Example of certificate of achievement
Example of certificate of achievement