Advanced Rails - Building Industrial-Strength Web Apps in Record Time

(Tuis.) #1

30 | Chapter 1: Foundational Techniques


As always, with great power comes great responsibility. Although these methods can
be useful, they can also be dangerous. Use them judiciously.


An example of the proper use ofObjectSpaceis found in Ruby’sTest::Unitframe-
work. This code usesObjectSpace.each_objectto enumerate all classes in existence
that inherit fromTest::Unit::TestCase:


test_classes = []
ObjectSpace.each_object(Class) {
| klass |
test_classes << klass if (Test::Unit::TestCase > klass)
}

ObjectSpace, unfortunately, greatly complicates some Ruby virtual machines. In par-
ticular, JRuby performance suffers tremendously when ObjectSpace is enabled,
because the Ruby interpreter cannot directly examine the JVM’s heap for extant
objects. Instead, JRuby must keep track of objects manually, which adds a great
amount of overhead. As the same tricks can be achieved with methods like
Module.extendedandClass.inherited, there are not many cases whereObjectSpace
is genuinely necessary.


Delegation with Proxy Classes


Delegationis a form of composition. It is similar to inheritance, except with more con-
ceptual “space” between the objects being composed. Delegation implies a “has-a”
rather than an “is-a” relationship. When one object delegates to another, there are
two objects in existence, rather than the one object that would result from an inherit-
ance hierarchy.


Delegation is used in ActiveRecord’s associations. TheAssociationProxyclass dele-
gates most methods (includingclass) to its target. In this way, associations can be
lazily loaded (not loaded until their data is needed) with a completely transparent
interface.


DelegateClass and Forwardable


Ruby’s standard library includes facilities for delegation. The simplest is
DelegateClass. By inheriting fromDelegateClass(klass)and callingsuper(instance)
in the constructor, a class delegates any unknown method calls to the provided
instance of the classklass. As an example, consider aSettingsclass that delegates to
a hash:


require 'delegate'
class Settings < DelegateClass(Hash)
def initialize(options = {})
super({:initialized_at => Time.now - 5}.merge(options))
end
Free download pdf