“God I hope I get it… I hope I get it! I really need this job… Oh, God I need this job…” -from “A Chorus Line,” music by Marvin Hamlisch, lyrics by Edward Kleban

A Class Act

Understanding Classes in Ruby… through Acting!

Jason Tseng
Published in
6 min readJul 7, 2017

--

Ruby is an object-oriented programming language, which means that the language paradigm is centered around the idea and use of “Objects” — containers of data, attributes, and methods. When first starting out, you learn about strings, arrays, integers, hashes, etc.:

string = "hello"array = [1, 2, 3]integer = 1234hash = {foo: "bar", sunday: "funday"}

These are all objects which you manipulate to construct the complex logic and actions that make up your code. All these objects belong to their corresponding classes (i.e. String, Array, etc.) with all their corresponding methods. All these classes are built into the core of Ruby’s language, but they aren’t the only classes you have access to...

You can write your own classes.

I know. Me too.

A class is made of two main portions. The actual class itself — which defines what the class looks like i.e. its relationships to other classes or the special methods only it can call — and the instances of that class. It’s helpful to think of a class as a factory that is responsible for producing all the different possible versions of that product or thing:

Fly my pretties! Fly!

The Bird class has five attributes: name, status, songs, species, and can_fly?. Whenever it initializes a new instance, it needs to know if the bird being created can fly, what it’s name is, and what species it is. It also automatically sets the bird’s status to hungry. This Bird class also has a special methods that only birds can use, #learn_song #mating_call , which allows a bird to learn new songs and then output them. If there was a Dog class or a Cat class, they wouldn’t be able to use these methods (although they probably have their own methods like Dog.bark or Cat.meow)

But you didn’t come to this blog to learn about no ornithology… you came for…

Thanks, Sir Patrick Stewart!

Classes as Actors

Classes aren’t just good repositories of information and methods, but one of the important benefits of creating your own class is you get to design how information enters, exits, and interacts with your new objects. I understood this from a theoretical level, but it was difficult for me to grasp the utility of this functionality until I realized classes behave a lot like actors.

Actors before a staged reading at Judson Memorial Church in New York City.

Over the years I’ve been writing plays, I’ve had the pleasure of working with a host of wonderful, thoughtful, and remarkable actors. But no matter how many times I get to hear actors read my work, I’m reminded of how much the same dialogue can vary from actor to actor. Each actor has their own unique blend of training, experience, and interpretation that produces widely varied interpretations of the text. I’m always surprised how actors can find new meanings, motivations, and questions in the text that I, as the author, had not even considered. Of course an actor’s performance is subject to some inputs that can change the outcome… usually from a director’s instructions. But there’s only so much that directorial input can do to alter a performance. Much more of that is reliant on an actor’s internal work in exploring and understanding their character — much of which is completely invisible to outside observers.

Consider this Actor class that I wrote to describe how an actor works in code:

Now this is a very simplified representation of the pain-staking work an actor does to perform. But it’s a useful model to help understand how a class and its instances control what data it takes in, what data it outputs, and what data and methods occur completely within the instance, and are invisible to those outside of the class.

Attributes: Accessors, Readers, and Writers… Oh my!

Cate Blanchett, serving face. via Giphy

Classes have attributes. Some can be altered by outside inputs, while others can be called and outputted for the whole world to see. Some attributes can do both, and some can do none of the above. Thanks to the wonder of ruby macros, attr_accessor attr_reader and attr_writer automatically create setter and getter methods that allow these attributes to be written, read or both.

Let’s take a look at the macros used in my Actor class.

Attributes that have an attr_accessor have both getter and setter methods. This means that these attributes like :stage_name or :training will have methods like:

#getter method
def stage_name
@stage_name
end
#setter method
def stage_name=(name)
@stage_name = name
end

An actor can choose a stage name whenever they like, and this name is widely available to the public. Similarly, an actor can get training in specific acting techniques, and this information is often shared with casting directors, and the like. This allows an instance of the Actor class to be able to output its accessor attribute and set it:

lady_gaga = Actor.new(birth_name: "Stefani Germanotta")lady_gaga.stage_name = "Lady Gaga"
-> sets @stage_name to "Lady Gaga"
lady_gaga.stage_name
-> returns "Lady Gaga"

Attributes that only have a attr_reader like birth_name can only be read, not set:

lady_gaga.birthname
-> returns "Stefani Germanotta"
lady_gaga.birthname = "Something Else"
-> returns NoMethodError

Unlike a @stage_name an actor’s birth_name is not something they can control or set themselves… but it is often public knowledge.

Similarly, if an attribute only has a attr_writer like motivations, they can only be assigned, not read:

lady_gaga.motivations = ["eternal life", "lust", "vanity"]
-> sets @motivations to the above array
lady_gaga.motivations
-> returns NoMethodError

An actor can be given motivations from the director, but when an actor performs their roles, these motivations are typically not known to any one else, other than the performer. So, Lady Gaga, in her emmy-award winning turn as The Countess on American Horror Story: Hotel, she might have been given motivations for her character, but only she knows what motivations she drew from to produce her performance.

The (Instance and Class) Methods to This Madness

Similarly, classes can have instance variables, @instance , that are available within an instance, or class variables, @@class , that are available to all instances of that class. If these variables don’t have getter/setter methods, then they cannot be accessed from outside the class or instance.

For example, the Actor class has an instance variable called @lines , which does not have a getter or a setter method. So, if you called lady_gaga.lines , you wouldn’t return anything. Only Lady Gaga knows which lines she has committed to memory. In fact, she uses @lines , which she is initialized with as an empty array [] , when we called the method lady_gaga.memorize_lines(american_horror_story, "The Countess"). In this method, Lady Gaga is assigned a character name, she goes through the script, and any line that is assigned to her character name, she adds to her @lines array.

These @lines only see the light of day when we ask her to lady_gaga.perform_lines which iterates over her lines and adds @@expressions (a class variable!) that she chooses based off of the @sense_memories that she draws upon.

In this way, we’ve designed actors to be able to take direction, to receive scripts to memorize, choose a stage name, get acting training, etc. However, our actors can do a variety of things independently, like choosing the memories they draw on to generate the performances on stage, or their character motivations.

I’ll be blogging more about my journey from putting plays on the stage to staging development environments, and how theatre and art can help us understand coding concepts in a more intuitive and human way.

--

--