You've done a lot of work with Ruby and that's great! Forget everything you've learned.
Just kidding. We'll be using quite a few of the concepts you've learned from the past lessons, but keep an open mind because we're about to dive into the fundamentals of Ruby.
Today we're going to learn about Object-Orientation in Ruby. Specifically, you'll be learning the following:
We'll start off with this high quality video with state-of-the-art animation:
That was a pretty old school video, but it was packed with a lot of information that's crucial for what we're doing today.
Every piece of data you've seen and worked with - they're all objects. That includes all of these:
:wuddup # Symbols
"Hello" # Strings
42 # Integers
3.14 # Floats
[1,2,3] # Arrays
{ x: 'y' } # Hash
Remember that in Ruby, when we pass methods to objects, we are really using .send to "send" it a message. But there's a shortcut out there that you may know of. When you send an object a message, the first part of the message is always telling it what to do. In the examples above we told the objects to add
, reverse
, split
and join
. The rest of the message always varied.
To make message passing easy, you can take the action out of the message and replace send with that action.
"hello".send(:reverse)
becomes "hello".reverse
Translate the following into the new format and make sure you get the same results:
{ ruby: 'backend', html: 'frontend' }.send(:invert)
10.send(:/, 3)
['a', 'b', 'c', 'd'].send(:slice, 1, 2)
As we said before, everything that you work with in Ruby is an object and every action that you take is a method. Breaking down this example:
['a', 'b', 'c', 'd'].send(:slice, 1, 2)
send
is the method you're performing on the object:slice
, 1
, and 2
are each objects - they are called arguments. Arguments are pieces of additional data being sent to the object for it to perform the task.["b", "c"]
In the new format, the method is now slice
instead of send
and the arguments are just 1
and 2
.
All objects have a class associated with them. Classes determine the structure of the object you're looking at and what methods you can call on it.
Call the class method on the following objects:
"hello"
42
72.2
:apple
[1, 2, 3]
{a: "b"}
As a response, you'll get the name of it's class. Knowing the class helps you figure out what methods you can and can't use on the object. You can go to Ruby Docs, or use the Dash app if you're on a Mac, and search for the class name to find a list of methods available along with documentation. Look up Array
and check out a new method or two.
Ruby gives you a few objects right off the bat, so that you're not left wandering alone into emptiness. The subset we're going to look at are the classes that are given. Yes, even classes are objects... everything is an object. (No really. You can even check the class of a class.)
Here are a few of the classes given to you:
String
Symbol
FixNum
Array
Hash
Float
There are more, but these are a few that are familiar to you. These are objects of the type Class which gives them the ability to use a method called new.
A class is essentially a template or a factory that tells you what kinds of objects it can create, and using the new
method, the factory produces the object. For example:
Array.new
Hash.new
The objects that these return are instances of the classes Array
and Hash
respectively. The array factory will give us an array and the hash factory will do the same.
However, this is quite pointless unless you label the instance that this returns. You label an object by picking a name and equating it to the object:
my_special_array = Array.new
The whole concept of variables is based on labeling objects in order to reuse them.
Very cool. Now we know about how method calls really work, how objects are truly created at the root and the general idea of classes. All of this was a precursor for us to be able to... drumroll... create our own classes!
Let's put some thoughts together
Class
Seems like we're trying to create an instance of the class Class. (Insert xzibit meme here)
Yeah... I'm confused, too. We'll go through an example together. First, we'll learn to run a Ruby program through a text file rather than in IRB.
Open up Sublime Text and create a new file and save it as class.rb
on the desktop.
In terminal, navigate to the desktop and type the command ruby class.rb
.
This will execute all of the Ruby code in the file class.rb
. This is an easier way to run code so that we don't have to rewrite our code in IRB every single time. Nothing will happen right now because we haven't written any code.
In class.rb
, type 1.+(7)
and save. In the terminal run the command ruby class.rb
again.
Still nothing. That's because executing a method does not print anything to the screen. IRB prints out the result of the method execution because it's meant to be used as a tool to help you code, but in regular execution, the code will NOT be printed unless you tell it to.
One of the objects we didn't mention earlier is Kernel
. Kernel
is the computer system and you can do interesting things to it like print to the screen. You may or may not have already been doing this...
In class.rb
type Kernel.puts("Hello World")
. Run the Ruby code in terminal again and watch the magic work.
Great! Now that we are safe from rewriting our code a million times we can start creating classes. To create a class we create an instance of Class
.
Pet = Class.new # put this in your class.rb file
Bam! We got ourselves a new class! Congrats!
So...what can we do now? You can create a new Pet
now since the class Pet
exists now. Try adding this to the end of your class.rb
file:
dog = Pet.new
Kernel.puts(dog)
Run the Ruby code and check out the results. We've created an instance of a Pet.
Pat yourself on the back!
Within terminal, instead of running ruby class.rb
, we can get the code working in IRB.
load 'class.rb'
dog = Pet.new
Kernel.puts(dog)
load 'class.rb'
loads all class and method definitions into your IRB session, so you can interact with them on the spot.
Remember that you have to load the Ruby file everytime you change it. It won't pick up the new changes automatically.
Back to the Pet class. Unfortunately, you can't do much with this pet object. For example, try creating a new dog in your IRB session and making the dog bark by typing the line dog.speak
in your IRB session.
Yeah, Ruby just exploded. Actually, Ruby gave you a super helpful error message.
NoMethodError: undefined method `speak' for #<Pet:0x007fa1198b6228>
When you read this error message, you can see that it's complaining, because you don't have the method speak
defined. The message is pretty much saying:
This method is not defined. The Pet class doesn't have a method speak defined for it
Sounds like we should define this method! Add the following code to the class definition. Type it. Don't copy it. You'll understand it better by typing it out.
Pet = Class.new do
def speak
Kernel.puts("Woof Woof")
end
end
# in IRB
load 'class.rb'
dog = Pet.new
dog.speak
Using the do end block we added a new functionality for the Pet class so that it can speak. Note the anatomy of a method definition:
def
and end
def
is followed by the method name, in this case speak
def
and end
are indented over and is the code that's run when you call the method.Write 2 other methods that a Pet
can use. Both methods must use an argument. Use Google to help you out. It will look similar to something below:
def your_age(years)
puts "You are only #{years} years old?! You child."
end
Try out the methods in IRB.
You've just created your first class with 3 methods. Awesome!
Now create a class of your own with 2 methods of your choice.
Pet
and one of your choice). Each one of the classes should have various methods that take arguments.
Let's reorganize this using some ruby shortcuts!
Instead of typing:
Pet = Class.new do
...
...
end
Ruby let's you make that look slightly cleaner and simply type:
class Pet
...
...
end
Go ahead and convert the code for the Pet class and your own class.
Classes allow you to have instance variables. If we have a class Cat
, instances of that class represent individual cats, and instance variables are the properties (height
, weight
, name
) that describe that cat.
Comment out the current code in class.rb
. You can do this highlighting the code and hitting command + /
.
Write the following code in the class.rb
file:
# class.rb
class Marker
def set_color(my_color)
@color = my_color
end
def write
Kernel.puts("I am writing with a #{@color} marker!")
end
end
See what is returned after executing the following in IRB code. Don't forget to load class.rb
in IRB.
#IRB code
red_marker = Marker.new
green_marker = Marker.new
red_marker.set_color("red")
green_marker.set_color("green")
red_marker.write
green_marker.write
Whoa there! That's quite a bit of code. Let's break it down.
In the set_color
method, we assign the input my_color
to the variable @color
. Because of the @
sign, Ruby recognizes that it's an instance variable. The variable is tied with just the instance of Marker
that calls the method set_color
.
When we create instances of the Marker
class, we call set_color
with both Markers
using different colors. When we call the write method on each Marker
, they print out different messages based on the color set.
Now go back to the Pet
class, allow a user to set what kind of noise the pet makes and when the pet speaks, have it make that sound.
For the class you created, think of a use for an instance variable and implement it.
Also, think of 3 other use cases for instance variables. No need to implement!
Convert the following methods to use the send method. Put your results in class.rb
5 * 5
"omg".upcase
['a', 'b', 'c'].at(1)
['a', 'b', 'c'].insert(2, 'o', 'h', 'n', 'o')
{}.size
{character: "Mario"}.has_key?(:character)
Convert the following methods to not use the send
method. Put these results in class.rb
6.send(:-, 32)
{html: true, json: false}.send(:keys)
"MakerSquare".send(:*, 6)
"MakerSquare".send(:split, 'a')
['alpha', 'beta'].send(:[], 3)
If you've gone through Git lessons/tutorials then run through this step. It's not required as part of the technical interview prep, but it's useful to know.
mkdir ruby_classes-1
cd ruby_classes-1
touch class.rb
git init
git add .
git commit -m "Initial commit"
git remote add origin ((paste your git url here))
git push origin master
These are basic steps you run in order to get a git repo started.
Here's a refresher on the Git process that you see above.
git init
- initiating "git" on your local computergit add .
- adding any changes you made to "staging"git commit -m "Initial commit"
- committing everything that is stagedgit remote add origin <url>
... - adding the ability to push your local directory to your personal remote from github.comgit push origin master
- pushing your local directory to your Github repo