In this lesson, we'll cover the basic concepts of working with objects and methods as well as some things about classes in Ruby. These are all topics we'll dive deeper into in later lessons, so consider this a crash course.
"Everything in Ruby is an Object" is something you'll hear rather frequently. "Pretty much everything else is a method" could also be said. The goal here is for you to see the Matrix... that everything in Ruby is an Object, every object has a class, and being a part of that class gives the object lots of cool methods that it can use to ask questions or do things. Being incredibly object-oriented gives Ruby lots of power and makes your life easier.
Think of every "thing" in Ruby as a having more than meets the eye. The number
12 is more than just a number... It's an object and Ruby lets you do all kinds of interesting things to it like adding and multiplying and asking it questions like:
> 12.class #=> Fixnum > 12+3 #=> 15
Ruby gives all objects a bunch of neat methods. If you ever want to know what an object's methods are, just use the
methods method! Asking
12345.methods in IRB will return a whole bunch of methods that you can try out on the number 12345.
You'll also see that the basic operators like
/ are all methods too (they're included in that list). You can call them using the dot, dot and parenthesis, or just "naked" like you normaly would:
> 1+2 # the "normal" way #=> 3 > 1.+2 # another option #=> 3 > 1.+(2) # explicitly speaking... #=> 3
You wouldn't ever use the second or third option, but remembering that EVERYTHING in Ruby is an object or a method is a useful mental model to have.
It's also useful to note that the parentheses around the argument for the method are optional (as you see in the example above). This makes a lot of beginners uncomfortable (especially those coming from strict languages), but you'll get used to it. Remember -- just because it doesn't have parentheses doesn't mean it's not a method!
Some methods ask true/false questions, and are usually named with a question mark at the end like
is_a?, which asks whether an object is a type of something else, for example:
> 1.is_a?(Integer) #=> true "hihi".is_a?(Integer) #=> false
You'll get used to the question-mark-for-questions naming convention.
is_a?, which tell you something about the object itself, are called Reflection Methods (as in, "the object quietly reflected on its nature and told me that it is indeed an Integer"). The method
class was another one we saw, where the object will tell you what class it is:
> "howdy!".class #=> String
What is a method? A method is just a function or a black box. You put in the thing on the left with some arguments to the right and it spits out the result afterward. Every method returns something, even if it's just
Some methods are more useful for their Side Effects than the thing they actually return, like
puts. That's why when you say
puts "hi" in IRB, you'll see a little
=> nil down below... the method prints out your string as a "side effect" and then returns
nil after it's done:
> puts "Howdy!" Howdy! #=> nil
When you write your own methods, if you forget to think about the return statement, sometimes you'll get some weird behavior so always think about what's going in and what's coming out of a method.
Methods can take arguments too, which are included in parentheses to the right of the method name. As we said above, these parentheses are optional, so you could say either of these:
> puts "hi" hi #=> nil > puts("hi") hi #=> nil
For another example, you can take an arithmetic expression and write it the long way like we described before, which makes each method's arguments much clearer:
> 1+2==3 # Short form #=> true > 1.+(2).==(3) # Long form (never actually used!) #=> true
...but why would you ever write it out the long way?
That's why Ruby is great -- there's a shorter and simpler way for everything.
The addition example above also shows Method Chaining, which is when you stick a bunch of methods onto each other. It behaves like you'd expect -- evaluate the thing on the left first, pass whatever it returns to the method on the right and keep going. So
1+2==3 first evaluates
1+2 to be
3 and then evaluates
3==3 which is
This is great because it lets you take what would normally be many lines of code and combine them into one elegant expression.
Bang Methods are finished with an exclamation point
sort!, and they actually modify the original object. The exclamation point lets you know you're in dangerous territory.
When you run a normal method in IRB, it will normally output whatever the method returns but it preserves the original object. Bang methods save over the original object (they are "destructive"):
> my_numbers = [1,5,3,2] #=> [1, 5, 3, 2] > my_numbers.sort #=> [1, 2, 3, 5] > my_numbers #=> [1, 5, 3, 2] # still unsorted > my_numbers.sort! #=> [1, 2, 3, 5] > my_numbers #=> [1, 2, 3, 5] # overwrote the my_numbers object!
To Write Your Own Methods, just use the syntax
def methodname(argument1, argument2, argumentN), though the parentheses around the arguments are optional. The method will return ("spit out") either whatever follows the
return statement or the result of the last piece of code that was evaluated (an Implicit Return statement). You call the inputs by whatever name you defined them at the top.
You can write methods in IRB... it will let you use multiple lines if it detects that you have unfinished business (a
def without an
end or unclosed parentheses):
> # IRB > def speak(words) # Create the method first > puts words > return "fuzzy-wuzzy!" > end #=> :speak # Ignore this > speak("hello!") # Then run the method hello! #=> "fuzzy-wuzzy!" # Return value
What if you want to assume that the input to a method is some default value if there otherwise hasn't been one supplied? That's easy, just specify the Default Input by assigning the input variable to something in the parentheses:
> def speak(words="shhhhh") > puts words > end # implicitly returns same as puts does #=> :speak # ignore this > speak # no input shhhhh #=> nil > speak("this is an input") this is an input #=> nil
Let's answer the question, "Where did all those methods come from?" Classes are like umbrellas that let us give an object general behaviors just based on what it is.
An object is an instance of a class -- you (yes, you) are an instance of the
Person class. There are lots of behaviors (methods) that you can do just by virtue of being a
Person... you can
This is really useful in programming because you often need to create lots of instances of something and it's silly to have to rewrite all the methods you want all of them to have anyway, so you write them at the class level and all the instances get to use them.
Everything in Ruby has some sort of class:
3.class #=> Fixnum 3.0.class #=> Float "Hello".class #=> String 'hi'.class #=> String # Special values are objects too nil.class #=> NilClass true.class #=> TrueClass false.class #=> FalseClass
Classes are the secret sauce of object-oriented programming so we'll spend a lot more time covering them in future lessons. This is just a teaser.
Individual instances of a class get to inherit the behaviors of the class they belong to. Inheritance works for classes too! Your class
Person has lots of methods but many of them are inherited just by virtue of you also being a
Mammal or even just a
LivingThing You get to use all the methods of your ancestor classes.
An interesting exercise to try in Ruby is to use the method
superclass to ask a class what its parent is. If you just keep on going and going, you'll see that everything eventually inherits from
BasicObject, which originates most of the methods you have access to in the original object:
> 1.class.superclass.superclass.superclass => BasicObject > BasicObject.methods => # giant list of methods
The following are more points of interest than things you really need to know but...
methods method on a class only returns the class methods, whereas running
instance_methods on it will return all methods available to any instance of that class. For example:
> String.methods.count #=> 100 # All the class methods on String > "Hello".methods.count #=> 164 # Also with additional instance methods > String.instance_methods.count #=> 164 # Yep, the world makes sense. For now.
Every object is really just a pointer to somewhere in your computer's memory where Ruby can find the actual object (though it's slightly different for FixNums, Booleans and
> "I am a string".object_id #=> 70226360259380 # Yours will differ > 1.object_id #=> 3 # Yours will be the same because integers are special > str = "another string" #=> "another string" > str.object_id #=> 70226376922740 # Yours will differ
object_id method to see an object's id, and this can be useful if you're running into odd errors where you thought you were modifying and object but it's not changing. If you debug and look at the IDs along the way, you may find that you're actually only modifying a COPY of that object. We'll get into the specifics of when you might be modifying a copy of an object (e.g. inside a block) instead of the object itself.