Ruby Day 3

My experience with day three of exploring Ruby through Seven Languages in Seven Weeks.

The third day takes you past the standard features to look at solving non trivial problems with Ruby. It focuses on Metaprogramming - writing programs that write programs.

An example of the use of metaprogramming is in ActiveRecord, a piece of rails used to connect business objects and database tables.

The has_many method adds all the instance variables and methods needed to establish a many to one relationship. has_one adds all the instance variables and methods needed to establish a one to one relationship. The methods read like English making them easy to understand.

Open Classes

You have the ability to change the definition of any class at any time e.g. to add behaviour. For example we might want to add a common blank? method across two classes to allow us to check strings.

The classes have already been defined so this invocation modifies the classes adding a blank? method. We then make use of ducktyping as we check an array for blank elements. It doesn’t matter if the element is a nil or a String as both support the blank? behaviour. The ability to redefine can make for very readable code but you need to use it responsibly.

You may have heard of DSL, Domain Specific Languages, a language created to represent information for a particular domain. Open classes in Ruby can be useful in creating a DSL e.g. expressing units in a language relevant to your business domain.

method_missing

If Ruby goes to call a method and finds it doesn’t exist it calls a debug method method_missing which prints out some diagnostic information. It can aid with debugging but it can also be overridden to provide some very expressive syntax. e.g. representing Roman numerals Could create a method and have syntax like

However you can create even easier read syntax overriding the method_missing method

We take the name of the method called and convert it to a string. We then handle special cases like 4, 9 etc. by converting from the 5 - 1 syntax (IV) or 10 -1 syntax (IX) to one which is easily added up i.e. 4 value 1’s and a 5 value and 4 value 1s. We then calculate the total amount converting characters to their numeric equivalent. It comes at price because now if we are missing a method Ruby won’t be able to tell us. The class would need to be enhanced to make sure it was accepting valid Roman numerals.

Modules

I found it hard understanding the Modules part of the chapter so I looked elsewhere for some examples to help build my understanding. I’ve taken some examples from ‘Ruby Best Practices’

Modules can be used for namespacing. e.g. in the example below both XML and PDF have document classes.

As previously discussed in my Ruby Day 2 blog modules can be used to add behaviour to classes by including a module. E.g. providing methods to allow objects to be ordered, comparable and enumerable modules.

You can also extend the behaviour of the class itself by extending it with a module. include makes the module’s methods available to the instance of a class, while extend makes these methods available to the class itself.

The following bit of code mixes the methods defined by Greeter directly into the Greeter object itself.

Self Study

Modify the CSV application to support an each method to return a CsvRow object. Use method_missing on that CsvRow to return the value for the column for a given heading. For example, for the file: one, two lions, tigers allow an API that works like this: csv = RubyCsv.new csv.each {|row| puts row.one} This should print “lions”.

Check out my code at github