PreviousNext…

Starting with Ruby - part 3

Before we get into how Ruby works with classes, its type model and the philosophy of form and being (oh yes), let’s take a tiny gander at the syntactical basics: (1) how do you define a class in Ruby, and (2) how do you instantiate it?

It won’t take long—this stuff is nice ’n’ easy, so feel free to follow in your text editor of choice (or even irb if you like. See my previous posts if you need to catch up).

First, let’s define a simple class called BlogPost (as you might guess from the class name, I’m keen to work with an example that might be relevant—or at least familiar). In addition to declaring our class, we shall also add some of the typical attributes one might associate with a blog post:

class BlogPost
	attr_reader :status, :created

	def initialize(title, content)
		@title = title
		@content = content
		@created = Time.new
		@status = "draft"
	end
	
	def append_to_file(file_ref)
		file_ref << @title
	end
end

Seems pretty straightforward I know, but let’s walk through some of the code, to tease out the details. First, any module, class or definition in Ruby starts with an appropriate lead-in (here, it’s the class keyword), and ends with, er, end.

You will also see that I have included a funky wee line that starts with the text attr_read. This is a short-cut for dealing with accessors, and it’s nifty! You may be used to writing (or maybe just generating) getters and setters for your well-encapsulated code in Java and wot-not. Well, Ruby allows for this too—you can define methods to read and write attributes—but Ruby also provides this stuff automatically via the attr_reader, attr_writer and attr_accessor keywords, which respectively provide getters (only), setters (only), or both. Nice!

Next we come to the bits of text that those accessors are being defined for, the things that look like variable names preceded with a colon. These are called symbols and effectively become key-value pairs, held within the class.

Moving on, the initialize method can be thought of as a “constructor”, a nugget of code that is triggered when the object is instantiated. You wrap a method definition with the def and end keywords respectively. In my code example here, the constructor requires two parameters (title and content, which are passed through in a BlogPost.new(…) statement), and tucks them away in a new instance of a blog post, along with a couple of other “default” values. You will notice that the BlogPost object has all these attributes set with @ preceding each one’s name. In Ruby, @ indicates an instance variable, i.e. an attribute peculiar to each object instantiated from the BlogPost class.

A few notes at this juncture:
  1. The attached Ruby source file (available in the permalink for this page) contains the full BlogPost file, for your reference
  2. I mentioned “modules” in the section above. I won’t come back to these just yet. For now, think of a module as a class that cannot be instantiated
  3. Keen observers will note that I’ve defined a second method in the BlogPost class; we’ll come to that momentarily

Types

If you’re coming from a Java / C# background (or similar), throw away a little of what you know. Languages like Java are “strongly-typed”, whereby a given object’s “type” is stated up-front, and is analogous to its class. An object’s class drives what it can and can’t do, and how it behaves. The compiler knows this, and checks for type, to the point that it will only let you do something like this:

String wibble = "Wobble";

… and not something like this:

String wibble = new Winkler();

(Quite right too!)

In the first snippet above, you’re telling the compiler that ‘wibble’ has a type of ‘String’. This means that right from the get-go, wibble can only do String-y things. If you try to do something else, as in the second snippet, the compiler will throw a hissy fit. Of course, we have interfaces and inheritance too, but this still all gets checked out at compile-time.

Ruby doesn’t do things that way. Instead, a type is more readily defined as comprising the capabilities of an object, but it doesn’t necessarily tie that object down to a string’s life (or a file’s, or an array’s…). Whilst a class definition can be used to give an object its initial set of capabilities and behaviours, it’s just a kick-start. From there on in, an object’s “class” becomes less relevant—type is something that becomes inferred “after the fact”, rather than up-front as in Java and co.

Here’s a fragment of Ruby to illustrate the point. As in the first Java snippet above, I’m going to instantiate an object called wibble, and I’m going to assign it a value of “wobble”:

wibble << "wobble"

The important distinction here is that we don’t care whether wibble is an actual string, another object entirely (a Winkler, or a DooDad perhaps)—it simply doesn’t matter. All wibble has to do is understand the << method (it’s an “append” operation, in case you were wondering). Now, straight off the bat in Ruby, things like strings, arrays, files and wot-not all know about <<, so wibble could actually “be” any one of those things. I say “be”, because of course, it isn’t a strong type as such.

Oh, it’s all getting a bit Platonic isn’t it!

If you come from a traditional strong-typing background (as I do), this is kind of funky, and you’ll still be getting your head around it for months to come. Don’t fret: where it’s (initially) very useful, is in the realm of unit testing, which is just as well. In this crazy new world, you definitely want to be testing a hell of a lot, and we’ll be looking at testing very soon!

Further reading

Downloads


Comments on this post are now closed.

About

I’m a software architect / developer / general IT wrangler specialising in web, mobile web and middleware using things like node.js, Java, C#, PHP, HTML5 and more.

Best described as a simpleton, but kindly. You can read more here.

";