Object Relationships in Basic Ruby

Marcella Maki
9 min readJul 13, 2017

--

When learning about object relationships in Ruby, it can be difficult to remember not only what the types of relationships are, but also how each line of code, and even specific variables, are working together to build that relationship in the program.

Here, I’m going to build out step-by-step example to show how the relationships work abstractly and how each element of the code works to build this relationship into the code that we’re writing.

For example, let’s think about your local cinema. Unless you are living in a tiny town (as I did for a while) that theater is probably showing more than one movie. The theatre has many movies. While it’s true that any given movie is probably playing in multiple theaters on any given day, let’s think about the movie as this particular copy of the movie which that theater has permission to show; a while ago it might have been because someone was literally in possession of a roll of film, but now it’s often a hard copy of a digital file and all sorts of licensing agreements that go along with it.

Cobble Hill Cinema Website Home Page (http://www.cobblehilltheatre.com/)

So, I’ve chosen to use Cobble Hill Cinemas in Brooklyn as my example, and right now, it’s screening Spider-Man Homecoming, The Beguiled, Despicable Me 3, and The Big Sick.

To think about how the cinema and the movies are related, I want to draw out the relationship, and it would probably look something like this:

Made with draw.io

Now that we can see the relationships between the objects, we can start talking about how those relationships are organized, and what our different levels are all about. In this flow chart, you can imagine the levels and colors as different classes.

Cobble Hill Cinema, in blue, is our Cinema Class, and it has it’s own unique set of attributes specific to that class. What do I mean by that? Well, what does any cinema have (other than movies)? It has a name and location. It can’t really exist as a business without those things. So this cinema is one example (or instance) of a cinema with those properties (or attributes) of name (Cobble Hill Cinema) and location (265 Court St., Brooklyn, NY 11231).

The next level of the flowchart is our Movie class, in green, and it shows how all of the movies belong to the previous class. So, movies belong to a cinema, and these four particular instances of the movie class (remember, imagine the film reels or DVDs that the cinema currently has!) belong to this instance of the cinema class (Cobble Hill Cinema). Again, we need to think about what properties or qualities the movie has, especially those qualities that are relevant to the relationships modeled here. They are connected horizontally because all of the elements in the class will have the same attribute types, but the values or specifics of those attributes will change based on the movie. Each movie has a name (couldn’t really be released without one!) and a showtime. For the sake of simplicity in this example, let’s say this cinema only shows each movie once per day.

class Cinema  attr_accessor :name, :location  def initialize(name, location)
@name = name
@location = location
@movies = []
end
end

We’ve started to build functionality into our Cinema classes to allow it to communicate the other classes. How? Look at the @movies = [] in the initialize function. What is that doing? Every time a new cinema is initialized (let’s say, Cobble Hill Cinema is now open for business!), the movies array is a place to hold each movie object. This object bit is an important point — it’s not just holding the name of the movie. Imagine the array as the little room up at the top of the theatre where the movies live and from where they play, at a specific time. So, from that movie room, we can play Despicable Me 3 at 3:45 and Spider-Man at 7:30.

http://kordastudio.hu/wp-content/gallery/screening-room/Korda-Studios-Screening-room-3.jpg

Okay, so we’ve built a cinema, and an important part of that build included having a place to store the movies so we can play them at the correct time, but we don’t have any movies yet! Back to our Movie class.

class Movie  attr_accessor :title, :showtime, :cinema  @@all = []  def initialize(title, showtime)
@title = title
@showtime = showtime
@@all << self
endend

Take a look at the code and you will see that the Movie class has a cinema attribute, which in turn has a setter and getter method. But, it hasn’t been included in initialize. Why’s that? Well, let’s consider the attributes and how the objects relate to one another to try to figure out why.

What is cinema all about in this class? Well, it’s the particular instance of the cinema class that currently owns the rights to a given movie object. Imagine cinemas can bid on the rights to screen The Beguiled daily at 9:30 PM for twelve weeks. That package deal is an example of an initialized instance of Movie class — the studio has gotten the movie all ready to go, but no cinema has leased it yet. The exclusion of cinema from our initialize method allows us to say “This package for The Beguiled hasn’t been leased yet.” Then, when Cobble Hill Cinema pays the money and signs the paperwork, they will get their own lovely delivery of the movie and the agreement that goes along with The Beguiled (a movie object).

How do we do that with code? Let’s go back to the Cinema class, and build an instance method to do just that:

def add_movie(movie)
@movies << movie
movie.cinema = self
end

Okay, so, what exactly is going on here? It looks like basically the same word over and over. How is it even doing anything?

def add_movie(movie)

We have our method that we’re building “add_movie,” and that takes the argument movie. Hmm…so what is movie here? Think back to the example…It’s the movie object! Make sure you are always passing in your object here, not the title of the movie. It’s easy to get confused, but thinking back to a literal example can help. If a Cinema is adding a movie to it’s collection, it doesn’t want just the name of the movie! It wants a copy and the rights to show it for a certain time.

On the next line we have

@movie << movie

So, we talked about this a little bit earlier. @movie is the little room for us to store all of the movies. So, when we get the movie from the distributor, we need to store it away safely.

Finally, we get to:

movie.cinema = self

Let’s break this down one word at a time. Remember that movie is the same as the argument we passed in which is what we are filing away, namely, the movie object. Remember cinema from our Movies class? When we initialized the movie, we didn’t set it, because no one had leased the movie. Now, in our example, we’re saying that Cobble Hill Cinemas has leased it. So movie.cinema is saying “the cinema that has lease this particular instance of the movie The Beguiled.” Okay, so what cinema is that? Cobble Hill Cinema. Then what is “self” all about?

Here are two different ways to think about it.

  1. For folks who are into programming because they are linguaphiles, let’s use grammar. In English (and many other spoken languages), we have transitive verbs (“action words”) and direct objects. For example, in the sentence “I threw the ball”, “threw” is the “action” and “ball” is the direct object, or the object that the action is happening to or on. If I say, “I threw the ball. She caught it,” we know “it” is referring to “ball.” In code, we have objects and methods, and they are pretty close to direct objects and verbs! Methods act on objects, and action verbs act on direct objects. It even sorta sounds the same, right?! Self is similar to “it.” If you’re following along in your code, much like if you’re reading a paragraph, you probably know what object is being acted on. If you don’t, that usually means either the writing (code or English) is ambiguous, questionable or otherwise bad, or you got lost and can quickly just go back a few lines to figure out what the object is.
  2. For those who just want to speak code: Self is the object receiving the method call. If the method is an instance method, self is always the instance that we are working with right now. If the method is a class method, self is going to be the class that the method is acting on.

Taking this information back to our example

movie.cinema = self

First question is, where are we? We are in the Cinema class, and we are reading a method we just built that is an instance method. We don’t want this method to work for the Cinema class overall, because we don’t want to try to give the same copy of Spider-Man to every theatre in the U.S. — that would be a disaster! We are trying to add the movie to the control room at Cobble Hill Cinema, which is one instance of the movie class. Therefore, self, here, is referring to Cobble Hill Cinema.

We know what each word represents, but what is really happening? Essentially, now that Cobble Hill Cinema has gotten a copy of The Beguiled, they’re labeling it as theirs. But remember, even though this is happening in an instance method in the Cinema Class, the actual label is going onto the movie object. This method is building the relationship between the copy of the movie and Cobble Hill Cinema into our code. Imagine someone in the theater control room sticking a label on the movie as it comes in the door. The room isn’t keeping track of that movie, even though it’s storing it. The movie needs to keep a record the cinema to which it belongs.

http://img.hisupplier.com/var/userImages/2013-10/15/165324584669.jpg

To wrap up, we can look at this code tested in the terminal line by line to see it in action and make sure our method works.

In line 1, we’re running the environment file, which requires the relative files cinema.rb (where the cinema class has been saved) and movie.rb (where the movie class has been saved), which allows us to access both files at once.

In line 2, we’re creating an instance of a cinema with cobble_hill. This is the name of the new cinema object, which has the name “Cobble Hill Cinema” and the location “Court St, Brooklyn”. This name gives us an easier way to access the object in terminal rather than keeping track of the id created by the terminal.

Lines 3–6 show that we’ve successfully created a cinema object — check out the <> in green that contain the object, its long generated id, and the three instance variables @location, @movies, and @name, which are the attributes of Cobble Hill Cinema.

In line 7, we’re creating an instance of a movie with spider_man. Just like with the cinema object, we’re using this syntax to both pass in arguments to the initialize method and give ourselves an easy way to access this particular object later on in the code.

Line 8 shows we’ve successfully created our spider_man object with attributes.

Now, in line 9, we’re testing out our method add_movie. We are calling add_movie on a particular instance of a the theater class (cobble_hill). We are passing in the spider_man object as an object. Remember, we’re essentially leasing Cobble Hill Cinema a copy of Spider-Man.

Did it work?

Line 10: We’re getting back our cinema object that is cobble_hill

Line 11 and 17: This object still has the instance variables for location and name.

Line 12–16: Check it out — now our movie array contains the spider_man movie object, AND that object within the array has been updated with the instance variable cinema, which matches cobble_hill (line 13).

So, yes, our method worked! Now, we know the basics of building a has-many and belongs-to relationships between two objects in Ruby.

--

--