Say that for any Person we wanted to be able to display a greeting that would include the person's name and the explicit class of the person. Ruby is a single inheritance language, and all our Person classes implicitly inherit from Object, so there's no obvious parent class that we can use to provide this functionality (in principle we could implement it on Object, but we haven't talked about that yet and it would also be a rather messy solution). Mix ins to the rescue! Here's the implementation of the mixin:
module PersonUtilities
def greeting
"Greetings, #{name} (#{self.class.name})"
end
end
and here's the altered implementations of our three Person classes:
class Person
include PersonUtilities
def name
'Steve Hayes'
end
end
module Distinguished
class Person
include PersonUtilities
def name
'Shakespeare'
end
end
end
module Very
module Distinguished
class Person
include PersonUtilities
def name
'Einstein'
end
end
end
end
Notice that the implementation of #greeting referred to a method that the module expected the including class to provide (#name), and to self, which resolves to the including class at runtime.
Here's the results of invoking #greeting on each of our three classes:
Person.new.greeting # => "Greetings, Steve Hayes (Person)"
Distinguished::Person.new.greeting # => "Greetings, Shakespeare (Distinguished::Person)"
Very::Distinguished::Person.new.greeting # => "Greetings, Einstein (Very::Distinguished::Person)"
Mix ins are a key part of very common Ruby classes. Object mixes in the Kernel module (and performs some magic to make the Kernel methods private), and most of the iterators we described in the previous column are provided by the Enumerable module. Enumerable's only expectation is that the including class will provide an implementation of #each -- all the other methods in Enumerable ultimately rely only on that. So if you implement a new class with an appropriate #each, you can mix in Enumerable and get all the other iteration methods for free.
As an example, here's a simple class that takes a brute force approach to converting a number to a set of digits -- not elegantly, but it does demonstrate a number of Ruby features:
class Digits
attr_reader :digits
def initialize(number)
string = number.to_s
@digits = (0..string.size-1).inject([]) {|digits, index| digits << string[index,1].to_i}
end
end
You can use this to iterate over the digits in a number by returning the digits array and iterating over that. So to get the squares of the digits, we could do this:
Digits.new(123).digits.collect{|each| each*each} # => [1, 4, 9]
However, exposing the digits attribute isn't very object-oriented; it would be much better if the Digits class handled iteration directly, and Enumerable makes this very easy:
class Digits
include Enumerable
def initialize(number)
string = number.to_s
@digits = (0..string.size-1).inject([]) {|digits, index| digits << string[index,1].to_i}
end
def each(&block)
@digits.each(&block)
end
end
Now we can iterate directly over the Digits object, and we're free to change the implementation any way we like:
Digits.new(123).collect{|each| each*each} # => [1, 4, 9]
In addition to modules, Ruby has one other feature that makes it very flexible and extensible -- in contrast to many other languages, all the Ruby classes are open to extension.
This means that you are free to add new methods to an existing Ruby class, and to change the behaviour of existing methods. Java projects, for example, commonly build up an extensive StringUtils class to hold all methods that the developers would like to invoke on String instances, but which cannot be implemented directly on String since in Java the class is closed.
One concrete example is the Apache Commons StringUtils class providing isBlank() to check whether a string is white space, empty, or null. The invocation is:
StringUtils.isBlank(aString)
In Ruby, we can implement this method directly on String (for simplicity we'll just look at empty or null):
class String
def blank?
self.strip.empty?
end
end
This handles the String case, but doesn't check for null. That's no problem as the Ruby equivalent, nil, is an instance of NilClass, which is also open, so we can therefore implement:
class NilClass
def blank?
true
end
end
and we get an expression that's more natural for an object-oriented language:
's'.blank? # => false
''.blank? # => true
nil.blank? # => true
As you can see, the Ruby language has a number of features that make it easy to organise code in ways that avoid duplication and makes your expressions clear and easy to understand. Ruby also has tools for introspection built in as natural parts of the language, so you're free to explore existing structures and implementations to improve your understanding of the language and help you find better ways of solving your particular problems.
Do you need help with Ruby? 





1
nona - 14/09/07
Articles and content in this section of the website are really amazing.
» Report offensive content
2
Shallu - 15/11/07
I really enjoyed my visit to this website.
» Report offensive content