Advertisement
Ruby

Ruby for Newbies: Operators and their Methods

by

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.

Press the HD button for a clearer picture.

Subscribe to our YouTube page to watch all of the video tutorials!


Operators

You’ve familiar with operators.

1 + 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.+(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:

name = "Joe"
 
name.+(" Smith") # "Joe Smith", but `name` is still "Joe"
 
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:

class Fridge
    def initialize (beverages=[], foods=[]) 
        @beverages = beverages
        @foods     = foods
    end
 
    def + (item)
 
    end
 
    def - (item)
 
    end
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:

def + (item) 
    if item.is_a? Beverage
        @beverages.push item
    else
        @foods.push item
    end
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.)

def - (item)
    ret = @beverages.find do |beverage|
        beverage.name.downcase == item.downcase
    end
 
    return @beverages.delete ret unless ret.nil?
 
    ret = @foods.find do |food|
        food.name.downcase == item.downcase
    end
 
    @foods.delete ret
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:

class Beverage
    attr_accessor :name
 
    def initialize name
        @name = name
        @time = Time.now
    end
end
class Food
    attr_accessor :name
 
    def initialize name
        @name = name
        @time = Time.now
    end
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.

> require './lesson_6'
=> true
 
> f = Fridge.new
=> #<Fridge:0x00000100a10378 @beverages=[], @foods=[]> 
 
> f + Beverage.new("water")
=> [#<Beverage:0x000001009fe8d0 @name="water", @time=2011-01-15 13:20:48 -0500>] 
 
> f + Food.new("bread")
=> [#<Food:0x000001009d3c98 @name="bread", @time=2011-01-15 13:20:59 -0500>] 
 
> f + Food.new("eggs")
=> [
    #<Food:0x000001009d3c98 @name="bread", @time=2011-01-15 13:20:59 -0500>, 
    #<Food:0x000001009746a8 @name="eggs", @time=2011-01-15 13:21:04 -0500>
   ] 
 
> f + Beverage.new("orange juice")
=> [
    #<Beverage:0x000001009fe8d0 @name="water", @time=2011-01-15 13:20:48 -0500>,
    #<Beverage:0x00000100907cd8 @name="orange juice", @time=2011-01-15 13:21:16 -0500>
   ]
 
> f
=> #<Fridge:0x00000100a10378 
        @beverages=[
            #<Beverage:0x000001009fe8d0 @name="water", @time=2011-01-15 13:20:48 -0500>,
            #<Beverage:0x00000100907cd8 @name="orange juice", @time=2011-01-15 13:21:16 -0500> ], 
        foods[
            #<Food:0x000001009d3c98 @name="bread", @time=2011-01-15 13:20:59 -0500>, 
            #<Food:0x000001009746a8 @name="eggs", @time=2011-01-15 13:21:04 -0500> ] 
> f - "bread"
=> #<Food:0x000001009d3c98 @name="bread", @time=2011-01-15 13:20:59 -0500> 
> f
=> #<Fridge:0x00000100a10378 
        @beverages=[
            #<Beverage:0x000001009fe8d0 @name="water", @time=2011-01-15 13:20:48 -0500>,
            #<Beverage:0x00000100907cd8 @name="orange juice", @time=2011-01-15 13:21:16 -0500>], 
        foods[#<Food:0x000001009746a8 @name="eggs", @time=2011-01-15 13:21:04 -0500>]

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:

person = {}
 
person[:name] = "Joe"

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

person.[]=(:age, 35) # to set
 
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.

class Club
    def initialize
        @members = {}
    end
 
    def [] (role)
        @members[role]
    end
 
    def []= (role, member)
 
    end
end

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

def []== (role, member)
    if @members[role].nil?
        @members[role] = member
    elsif @members[role].is_a? String
        @members[role] = [ @members[role], member ]
    else 
        @members[role].push member
    end
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:

c = Club.new
 
c[:chair] = "Joe"
 
c[:engineer] = "John"
 
c[:engineer] = "Sue"
 
c[:chair] # "Joe"
 
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: | & ^

Thanks for Reading!

If you’ve got any questions about this lesson, or anything else we’ve discussed in Ruby, ask away in the comments!

Related Posts
  • Computer Skills
    App Training
    Writing Destinations for DropzoneDropzone400
    Dropzone is not just another FTP upload client, but a great utilities platform for processing files and text. Dropzone is a collection of destinations that can have two types of actions: drag and drop action and/or a click action. There are a lot of pre-programmed destinations to use, but you can also write your own destinations in Ruby!Read More…
  • Code
    Theme Development
    Creating a WordPress Theme From Static HTML: The Footer FileCreating wordpress theme from html 400
    In this series, you've been learning how to create a WordPress theme form static HTML. Up to this point, you have: prepared your markup for WordPress converted your HTML to PHP and split your file into template files edited the stylesheet and uploaded your theme to WordPress added a loop to your index file added meta tags, the wp_head hook and the site title and description to your header file added a navigation menu added widget areas to the header and sidebar. Read More…
  • Code
    PHP
    Validation and Exception Handling: From the UI to the BackendProcedural to oop php retina preview
    Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached a point when we had to take architectural actions to accommodate all the exceptional cases our quite large software project needed to handle. Below is a list of practices we came to value and apply when it comes to validation and exception handling.Read More…
  • Web Design
    UX
    Walk Users Through Your Website With Bootstrap TourTour retina
    When you have a web application which requires some getting used to from your users, a walkthrough of the interface is in order. Creating a walkthrough directly on top of the interface makes things very clear, so that's what we're going to build, using Bootstrap Tour.Read More…
  • Code
    Theme Development
    How to Integrate Bootstrap Navbar Into WordPress ThemeBootstrapd 400
    Have you ever wanted to speed-up the process of theme development? I assume the answer is "yes" and you already know about Bootstrap and use it in mock-ups for development. This raises the question: "How can you integrate Bootstrap components into a WordPress theme?"Read More…
  • Code
    PHP
    How to Accept Payments With StripeCode
    Processing credit cards is unfortunately far more difficult than we might hope, as developers. Given that it’s such a common task, is it really necessary that we jump through countless hoops (surrounded by fire, of course) for the sole purpose of processing a payment? Merchants? Gateways? SSL? Security? Very quickly, a seemingly simple operation can become an overwhelmingly confusing and, more importantly, dangerous task. Any time that you find yourself handling a user’s sensitive data, you better be on your toes. Read More…