May 27, 2015
Table of contents:
An important part of object-oriented programming is writing classes. Classes are the blueprints of objects and allow us to define the properties and behaviour that the objects of our applications should posses.
Last week we looked at working with Ruby methods. Writing reusable methods is a big step towards becoming a programmer in the early days of learning to code.
In today’s tutorial we’re going to be looking at writing classes in Ruby.
As Ruby is a typical object-oriented language, many of the characteristics of classes and objects are similar to other languages.
However, we’ll also look at some of the unique aspects that make Ruby a fantastic choice of programming language to use for your next project.
Before we get into writing classes in Ruby, first it is important to understand why we need classes in the first place.
A class represents an important concept in the application we are building.
By writing classes, we can encapsulate the behaviour and properties of these important concepts.
Object-oriented programming is all about modelling important concepts as objects and then using those objects to send messages to other objects.
Hopefully you are familiar with why we need to write classes. I know that the concept can seem foreign at first, but if you just keep persisting things will fall in to place.
To define a new class you use the
class keyword, give the class a name, and then finish with an
class Book end
The name of the class is a constant and by convention should begin with a capital letter. If your class name is two words it should use Camel Case, (e.g
I’ve saved the class definition in a file called
If you fire up irb we can load the class file so we can play around with it:
Every time you make a change to the
book.rb file you will need to reload the class.
An interesting thing to notice about ruby objects is that all objects inherit from something.
In our definition of our
Book class you will notice that we didn’t inherit from any class. But have a look what you get returned when you check the superclass:
Book.superclass # => Object
If you don’t specify a class to inherit from, your class will automatically inherit from
Object. This is really useful because it means your class will already have some of the basic object methods of the ruby language.
If you are new to object inheritance, don’t worry about it. We’ll be looking at inheritance in a future tutorial.
Now that we have our
Book class defined we can instantiate it like you would with any other typical Ruby class:
book = Book.new book.class # => Book
Whenever we create a new instance of a Ruby object, the
initialize method will be automatically called. We can see this in action by modifying the
class Book def initialize puts 'You just initialized the Book class!' end end
Now when you create a new instance of the
Book class you should see string of text printed to the screen:
Book.new # => "You just initialized the Book class!"
initialize is used for setting the object up when it is first created. This is typically setting instance variables, but it could be whatever you need to do when the new object is created.
One of the most common things to do in the
initialize method is to set the instance variables of the object. For example, when we create a book, we also need to set it’s title:
class Book def initialize(title) @title = title end end
Notice how we write
@ means that this is an instance variable, rather than a local variable that would be scoped to the
To set the
title, you pass in the value when creating the new object:
book = Book.new('The Wolf of Wall Street')
Now that we have a book object to work with, we are probably going to want to be able to access the
title property, right?
Well, normally to access an object’s property you would call the getter method, like this:
book.title # => NoMethodError: undefined method 'title' for #<Book:0x007fe9c32f2ab0 @title="hello">
Uh oh, looks like our
Book object doesn’t have a getter method and so we will need to implement that ourselves:
class Book def initialize(title) @title = title end def title @title end end
title method we simply need to return the
@title instance variable. Remember as we saw last week, you don’t need to write the
return keyword to return a value from a ruby method.
Another common thing you will want to do with an object is change it’s properties. For example, we might want to change the title of the book:
book.title = "Zen and the Art of Motorcycle Maintenance" # => book.title = "Zen and the Art of Motorcycle Maintenance" # NoMethodError: undefined method 'title=' for #<Book:0x007fe9c32cc130 @title="The Wolf of Wall Street">
Once again, in order to set a property, we first need to create the setter method:
class Book def initialize(title) @title = title end def title @title end def title=(title) @title = title end end
Now if you try to reset the title of a book object it should work correctly.
Getting and setting properties on an object is such a common thing Ruby has a shortcut in the
class Book attr_accessor :title def initialize(title) @title = title end end
If you reload the class in IRB you will see that it works in exactly the same way, but we didn’t have to define the getter and setter methods.
attr_accessor accepts a Symbol and will automatically create the getter and setter methods for you. This might not seem like a big deal, but it does save you from repeating the same boilerplate code every time you want to create a new class!
Imagine we modify the
Book class to automatically set a
created_at timestamp when the object was instantiated:
class Book attr_accessor :title, :created_at def initialize(title) @title = title @created_at = Time.new end end
We can, once again, use
attr_accessor to automatically create the getter and setter methods:
book.created_at # => 2015-04-29 18:21:51 +0100
However, once the object is created, we shouldn’t be able to set a new timestamp:
book.created_at = Time.now # => 2015-04-29 18:24:03 +0100
To get around this problem we can use
attr_reader instead of
class Book attr_accessor :title attr_reader :created_at def initialize(title) @title = title @created_at = Time.new end end
Now when we try to use the setter method, we should get an Exception:
book = Book.new('The 4-Hour Work Week') # => #<Book:0x007ff609827150 @title="The 4-Hour Work Week", @created_at=2015-04-29 18:26:36 +0100> book.created_at # => 2015-04-29 18:26:36 +0100 book.created_at = Time.now # NoMethodError: undefined method 'created_at=' for #<Book:0x007ff609827150>
So if you only want to create the getter methods you can use
attr_reader instead of
So you can create both the setter and getter methods, or just the getter methods, how would you create just the setter methods?
Well, Ruby also has the
attr_writer method for doing just that:
class Book attr_accessor :title attr_reader :created_at attr_writer :owner def initialize(title) @title = title @created_at = Time.new end end
In the class above you can set the
owner property but you can’t read it:
book = Book.new('Fooled By Randomness') # => #<Book:0x007fbb4a026ec8 @title="Fooled By Randomness", @created_at=2015-04-29 18:30:36 +0100> book.owner = 'Jane' # => "Jane" book.owner # NoMethodError: undefined method 'owner' for #<Book:0x007fbb4a026ec8>
There’s a lot to cover when looking a Ruby classes so I’m going to break this out into a few different tutorials.
Today we looked at defining a class and creating the getter and setter methods.
I really love some of the unique characteristics of Ruby. For example, the fact that all objects inherit from
Object. Or the fact we can use
attr_accessor instead of having to write out getter and setter methods.
Its stuff like this that makes Ruby a lovely language to work with.