# Ruby for Newbies: Operators and their Methods

This post is part of a series called Ruby for Newbies.
Ruby for Newbies: Iterators and Blocks
Ruby for Newbies: Working with Directories and Files

Ruby is a one of the most popular languages used on the web. We've started a new screencast series here on Nettuts+ that will introduce you to Ruby, as well as the great frameworks and tools that go along with Ruby development. In this lesson, we’ll be taking a deeper look at operators in Ruby, and why they are different from anything you’ve ever seen before.

## Operators

You’ve familiar with operators.

 1 1 + 2 # 3  2   3 person[:name] = "Joe" 

Operators are things like the plus sign (one of the arithmetic operators), or the equal sign (the assignment operator). These things don’t look to much different from the ones you use in JavaScript, PHP, or any other language. But—like most of Ruby—there’s a lot more than meets the eye going on here.

Here’s the secret: operators in Ruby are really method calls. Try this:

 1 1.+(2) # 3 

Here, we’re calling the + operator on the object 1, passing in the object 2 as a parameter. We get back the object 3. We can do this with strings too:

 1 name = "Joe"  2   3 name.+(" Smith") # "Joe Smith", but name is still "Joe"  4   5 name += " Smith" # name is now "Joe Smith" 

As you can see, we can do string concatenation with the + method. As a bonus here, ruby defines the += operator based on the + operator (note: you can’t use += as a method).

As you might realize, this gives us incredible power. We can customize the meaning of adding, subtracting, and assigning objects in our custom classes. We saw how this works with properties on objects in our lesson on classes (we defined a property and property= method in the class, and got the expected syntax sugar for using them). What we’re looking at here is taking that a step further.

## Building our own Operator Methods

Let’s try to create one of these methods ourselves. For this example, let’s create a refrigerator object, that we can add things to via the + operator and take things out of via the - operator.

Here’s the start of our class:

 1 class Fridge  2  def initialize (beverages=[], foods=[])  3  @beverages = beverages  4  @foods = foods  5  end  6   7  def + (item)  8   9  end  10   11  def - (item)  12   13  end  14 end 

Our initialize function is pretty simple: we take two parameters (that fall back to empty arrays if nothing is given), and assign them to instance variables. Now, let’s build those two functions:

 1 def + (item)  2  if item.is_a? Beverage  3  @beverages.push item  4  else  5  @foods.push item  6  end  7 end 

This is pretty simple. Every object has an is_a? method that takes a single parameter: a class. If the object is an instance of that class, it will return true; otherwise, it will return false. So, this says that if the item we’re adding to the fridge is a Beverage, we’ll add it to the @beverages array. Otherwise, we’ll add it to the @food array.

That’s good; now, how about taking things out of the fridge? (Note: this method is different from the one shown in the video; this shows you that these operator method give us a great deal of flexibility; they are really just normal methods that you can do anything with. Also, I think this is a better version of the method; however, it’s more complex.)

 1 def - (item)  2  ret = @beverages.find do |beverage|  3  beverage.name.downcase == item.downcase  4  end  5   6  return @beverages.delete ret unless ret.nil?  7   8  ret = @foods.find do |food|  9  food.name.downcase == item.downcase  10  end  11   12  @foods.delete ret  13 end 

Here’s what’s going on when we use the minus operator. The parameter that it takes is a string, with the name of the item we’re looking for (By the way, we’ll create the Beverage and Food classes soon). We start by using the find method that arrays have. There are a few ways to use this method; we’re passing it a block; this block says that we’re trying to find the item in the array which has a name property that’s the same as the string we passed in; note that we’re converting both strings to lowercase, to be safe.

If there’s an item that matches in the array, that will be stored in ret; otherwise, ret will be nil. Next, we’ll return the result of @beverage.delete ret, which removes the item from the array and returns it. Notice we’re using a statement modifier at the end of that line: we do this unless ret is nil.

You might wonder why we’re using the keyword return here, since it’s not required in Ruby. If we didn’t use it here, the function wouldn’t return yet, since there’s more code to the function. Using return here allows us to return a value from a place the function wouldn’t normally return.

If we don’t return, that means the item wasn’t found in @beverages. Therefore, we’ll assume it’s in @foods. We’ll do the same thing to find the item in @foods and then return it.

Before testing this out, we’ll need our Food and Beverages classes:

 1 class Beverage  2  attr_accessor :name  3   4  def initialize name  5  @name = name  6  @time = Time.now  7  end  8 end  9 class Food  10  attr_accessor :name  11   12  def initialize name  13  @name = name  14  @time = Time.now  15  end  16 end 

Note that in the video, I didn’t make @name accessible from outside the object. Here, I’m doing that with attr_accessor :name, so that we can check the name of these object when they’re inside a fridge.

So, let’s test it out in irb; we’ll start by requiring the file that holds the code; then, give the classes a try; note that I’ve added line breaks to the output for easier reading.

 1 > require './lesson_6'  2 => true  3   4 > f = Fridge.new  5 => #  6   7 > f + Beverage.new("water")  8 => [#]  9   10 > f + Food.new("bread")  11 => [#]  12   13 > f + Food.new("eggs")  14 => [  15  #,  16  #  17  ]  18   19 > f + Beverage.new("orange juice")  20 => [  21  #,  22  #  23  ]  24   25 > f  26 => #,  29  # ],  30  foods[  31  #,  32  # ]  33 > f - "bread"  34 => #  35 > f  36 => #,  39  #],  40  foods[#] 

As we go along, you can see things being added to the @beverages and @foods arrays, and then subsequently removed.

## Get and Set Operators

Now let’s write methods for the get and set operators used with hashes. You’ve seen this before:

 1 person = {}  2   3 person[:name] = "Joe" 

But, since these operators are methods, we can do it this way:

 1 person.[]=(:age, 35) # to set  2   3 person.[](:name) # to get 

That’s right; these are normal methods, with special sugar for your use.

Let’s give this a try; we’ll make a Club class. Our club with have members with different roles. However, we may want to have more than one member with a given role. So, our Club instance will keep track of members and their roles with a hash. If we try to assign a second member to a role, instead of overwriting the first one, we’ll add it.

 1 class Club  2  def initialize  3  @members = {}  4  end  5   6  def [] (role)  7  @members[role]  8  end  9   10  def []= (role, member)  11   12  end  13 end 

The get version is pretty simple; we just forward it to the @members array. But set is a little more complicated:

 1 def []== (role, member)  2  if @members[role].nil?  3  @members[role] = member  4  elsif @members[role].is_a? String  5  @members[role] = [ @members[role], member ]  6  else  7  @members[role].push member  8  end  9 end 

If that role has not been set, we’ll just set the value of that key to our member hash. If it has been set as a string, we want to convert that to an array, and put the original member and the new member in that array. Finally, if neither of those options are true, it’s already an array, and so we just push the member into the array. We can test this class this way:

 1 c = Club.new  2   3 c[:chair] = "Joe"  4   5 c[:engineer] = "John"  6   7 c[:engineer] = "Sue"  8   9 c[:chair] # "Joe"  10   11 c[:engingeer] # [ "John", "Sue" ] 

There you go!

## Other Operators

These aren’t the only operators that we can do this with, of course. Here’s the whole list:

• Arithmetic Operators: + - * \
• Get and Set Operators: [] []=
• Shovel Operator: <<
• Comparison Operators: == < > <= >=
• Case equality Operator: ===
• Bit-wise Operator: | & ^