Ruby for Newbies: Operators and their Methods
videos

Ruby for Newbies: Operators and their Methods

Tutorial Details
  • Topic: Ruby
  • Difficulty: Beginner - Intermediate
  • Format: Video
This entry is part 6 of 13 in the Ruby for Newbies Session
« PreviousNext »

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 + 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!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.dreamdezigns.com dream dezigns

    hi its great explanation you have given i am search for this code bug i got it in your blog

  • Nick

    Typo :
    def []== (role, member)
    should be
    def []= (role, member)

    • http://andrewburgess.ca Andrew Burgess
      Author

      Good Catch! Thanks!

  • Gavin

    Really enjoying this series. As this is 6/6 is this it? I was wondering about GUI and building apps etc…
    Thanks again for the great series.

    • Gavin

      Just to clarify, I realise there are some great full app tutorials on this site, but the jump from the beginners to these can sometimes be over looked. As a beginner, I guess I still need someone to hold my hand! lol

    • Mike Schneider

      @Gavin, Look up ruby shoes. I think that is still the most popular way to write a GUI with Ruby.
      http://shoesrb.com/

      • Gavin

        Thanks Mike, as I mentioned above, the leap to using these methods is a little beyond me at this point. I understand how to install them etc.. but actually tying it all together is a bit harder.

    • http://andrewburgess.ca Andrew Burgess
      Author

      I’m glad you’re enjoying the series; don’t worry, this isn’t the end of it. That’s just saying that six episodes have been published. There will definitely be more. However, I’m not sure we’ll get into GUI stuff; after all, this is Nettuts+ :). But do check out shoes. I haven’t tried it very much, but I think it’s pretty good! Also, for Mac programming, you might look at MacRuby, a Ruby library for Cocoa programming.

      • Gavin

        Thanks Andrew. Look forward to then next tutorial.

  • David

    Is there a way I can download this video? I’m a premium member already but it doesn’t seem to be available there.

  • http://www.weareprimate.co.uk/ Gordon @ Primate

    Nice tutorial, thanks Andrew!

    I’ve been working with PHP for over 6 years now but have just recently started learning Ruby (our developers are all Ruby based so figured it was time to get with them!) and am enjoying it immensely. Obviously being so familiar with PHP makes it a bit frustrating and confusing at times but I’m taking it slow. Anyway I’m very glad I discovered these tutorials… going to jump back to #1 now and start from the begining!

  • Potado

    Interesting, but how would you incorporate this into a webpage?

    • Potato

      I was asking the same question myself.. I as a PHP developer, you tend to look at things in terms of views and controllers, and HTML stuff..

      I wish he will show us more of real world applications of Ruby/RoR into web development environment.

  • http://www.BlaineSch.com BlaineSch

    The next tutorial in this series “Working with Directories and Files” gives a 404.

  • Adam

    FYI c++ and I believe java as well have operator overloading as well. Obviously a different category from net languages entirely but figured I’d pipe up anyway. Coming from an application based programing background and now trying to learn the different web technologies has it’s benefits and it’s issues. These tutorials have been helpful in learning a new language that’s just different enough to be challenging.

    Thanks

    • Michael Schueler

      Yeah I found that annoying too.

      These screencasts could be really great if the author did some more research and fact checking. Don’t get me wrong, these screencasts are mostly pretty good.. but if you’re serious about learning Ruby I’d go with another resource.

      On the other hand, if you don’t really care about the difference between between functions and methods, message passing, etc.

      Note: In the video the point that operators are really just methods isn’t really stressed, but it looks like in the writeup below the video he explains it properly.

      Don’t take this post the wrong way, trying to be constructive.

  • http://www.newtnetnews.com Christoph Rumpel

    Hi,

    ruby is really great. Great tutorial, thx a lot! Just please do not say “this is not a good example” oder “this is not how its done usually” etc. :-)
    Greets

  • Drew

    You forgot the >>, +@, -@ and !@ operators.

  • http://www.marketplaso.com markiee

    You have well crafted your ruby knowledge in to this ruby tutorial,its very nice of you, I appreciate it