A Ruby Newb’s Intro to Classes and Object-Orientedness

posted in: ones and zeros | 0

Dearth of beginner-friendly info on the web got you down? Don’t worry, I’m going to explain Ruby’s classes and this crazy object-oriented thing for you.

The Beginner’s Path

So you’re just starting out learning a programming language, and you are introduced to everyone’s favorite starter script, Hello World.

puts "Hello World"

You quickly move on to writing little scripts that reinvent the mathematical wheel, because maths are bits of logic that are easy to reproduce for yourself, and, frankly, because you’re not capable of much more. (Don’t worry! Everyone starts out this way.)

def multiply(x, y)
  x * y
end

Then you’re writing stuff that’s actually kinda useful, like this script that turns a number into a string and inserts commas in the appropriate places:

def separate_comma(number)
  if number < 1000     
    number.to_s   
  elsif number > 999 && number < 1000000
    number.to_s.insert(-4, ",")
  else
    number.to_s.insert(-4, ",").insert(-8, ",")
  end
end

YOU ARE ROCKSTAR NOOB CODER. But… there’s something nobody told you.

This isn’t really what Ruby programming looks like.

Object-Oriented Languages

What you’ve been writing is in a “procedural” format, where you have some data (the numbers 1 and 2) and then you do things to it ( + ). You think that Ruby has special keywords and operators that you can use to do things to your Fixnum data.

Well, I can’t tell you that this is 100% wrong. There is proceduralness in Ruby. But what’s really going on?

See that fixnum of yours? It isn’t data. It is an object. And that means: your Fixnum object not only contains the data, 2, but it also knows what methods will work on it.

Did you catch that? If you have an instance of Fixnum, an occurance of a number, it knows not only the number but also +, -, *, and every other action that can be performed on a Fixnum.

(My response to this when I first learned it: Wow, OOP is magic!)

Classes

You see what’s going on is that instead of data types (string, integer, array, etc), Ruby has classes.

When Yukihiro Matsumoto wrote Ruby, he defined the classes Fixnum, String, etc., and made it so that every time you create a new string, that specific string has a built-in knowledge of concatenation, splitting, etc.

Well, you might say, so what? puts “Hello World” still looks and works the same whether or not you understand what’s going on under the hood. Why is this important to know? And what the heck are the implications of this crazy object-oriented thing?? (“Why?” and “What are the implications?” are the two least-answered questions in beginner’s tutorials, and yet sometimes the most important to answer, IMHO.)

If you have classes that define objects and the actions associated with them, well then guess what? You can create your own classes too! Yes, we’re no longer limited to Fixnums, Strings, Hashes, etc. In fact, we can create:

The Puppy Class

A lot of examples of how to write and use classes involve a Person class, or a Car class or something similar. For MY example, I’m going to use puppies. Because… puppies.

class Puppy
end

Well, I guess we’ve established that there’s a Puppy class, but it’s not very interesting or useable. Let’s define what an instance of this Puppy class looks like, and then create a new puppy.

class Puppy
  def initialize(age) #age in months
    @age = age
  end
end

Puppy.new(2)

A two month old puppy! Congratulations, you must be very proud.

So what is this initialize stuff? That is where you specify what your new object will be like when you .new it to create a new one. This particular class takes a single argument, age (in months). Then it creates an instance variable @age, to make its age available throughout the rest of the class.

Wait – have you learned about scope yet? If not: there’s a rule that says you can only access variables within their own “scope.”" I think the simplest way to explain scope is that if a variable is defined in a def…end block or a do…end block or {…}, you can only use it inside that block and it’s NOT accessible outside that block.

An instance variable, that version with @ in front, can be used anywhere inside that instance of the class. So:

class Puppy
  def initialize(age)
    @age = age
  end

  def dire_warning  #we don't have to pass an argument here because 
    if @age <= 6    #we're using an instance variable!
      puts "Goodbye, free time!"
    elsif @age < 12
      puts "Goodbye, new shoes!"
    else
      puts "Hello, best friend!"
    end
  end
end

and

Puppy.new(2).dire_warning #=> Goodbye, free time!

And now we can put all sorts of puppy-related methods inside of this class, for when we need our instance of puppy to have some kind of action going on with it.

So Did You Get All That?

In review:

  • Ruby has objects, not data types
  • Objects know their data and the methods associated with them
  • Instance variables are great for allowing different methods within a class to get to variables that were assigned elsewhere within that class
  • Two month old puppies are a lot of work. Make sure you buy that special pet stain remover. Really.