Section 11.3. Working with Dynamic Features


11.2. More Advanced Techniques

Not everything in Ruby OOP is straightforward. Some techniques are more complex than others, and some are rarely used. The dividing line will be different for each programmer. We've tried to put items in this part of the chapter that were slightly more involved or slightly more rare in terms of usage.

From time to time, you might ask yourself whether it's possible to do some task or other in Ruby. The short answer is that Ruby is a rich dynamic OOP language with a good set of reasonably orthogonal features, and if you want to do something that you're used to in another language, you can probably do it in Ruby.

As a matter of fact, all Turing-complete languages are pretty much the same from a theoretical standpoint. The whole field of language design is the search for a meaningful, convenient notation. The reader who doubts the importance of a convenient notation should try writing a LISP interpreter in COBOL or doing long division with Roman numerals.

Of course, we won't say that every language task is elegant or natural in Ruby. Someone would quickly prove us wrong if we made that assertion.

This section also touches on the use of Ruby in various advanced programming styles such as functional programming and aspect-oriented programming. We don't claim expertise in these areas; we are only reporting what other people are saying. Take it all with a grain of salt.

11.2.1. Sending an Explicit Message to an Object

In a static language, you take it for granted that when you call a function that function name is hard-coded into the program; it is part of the program source. In a dynamic language, we have more flexibility than that.

Every time you invoke a method, you're sending a message to an object. Most of the time, these messages are hard-coded as in a static language, but they need not always be. We can write code that determines at runtime which method to call. The send method will allow us to use a Symbol to represent a method name.

For an example, suppose that we had an array of objects we wanted to sort, and we wanted to be able to use different fields as sort keys. That's not a problem; we can easily write customized sort blocks. But suppose that we wanted to be a little more elegant and write only a single routine that could sort based on whatever key we specified. Listing 11.9 shows an example.

This example was written for the first edition of this book. The sort_by method is now standard in Ruby, and it is more efficient than this one because it implements a Schwartzian transform (named for Randal Schwartz of Perl fame), saving the transformed values so as to avoid recomputing them. Listing 11.9, however, is still a valid example of using send.

Listing 11.9. Sorting by Any Key

class Person   attr_reader :name, :age, :height   def initialize(name, age, height)     @name, @age, @height = name, age, height   end   def inspect     "#@name #@age #@height"   end end class Array   def sort_by(sym)   # Our own version of sort_by     self.sort {|x,y| x.send(sym) <=> y.send(sym) }   end end people = [] people << Person.new("Hansel", 35, 69) people << Person.new("Gretel", 32, 64) people << Person.new("Ted", 36, 68) people << Person.new("Alice", 33, 63) p1 = people.sort_by(:name) p2 = people.sort_by(:age) p3 = people.sort_by(:height) p p1   # [Alice 33 63, Gretel 32 64, Hansel 35 69, Ted 36 68] p p2   # [Gretel 32 64, Alice 33 63, Hansel 35 69, Ted 36 68] p p3   # [Alice 33 63, Gretel 32 64, Ted 36 68, Hansel 35 69]

We'll also mention the alias __send__, which does exactly the same thing. It is given this peculiar name, of course, because send is a name that might be used (purposely or accidentally) as a user-defined method name.

11.2.2. Specializing an Individual Object

I'm a Solipsist, and I must say I'm surprised there aren't more of us.

Letter received by Bertrand Russell

In most object-oriented languages, all objects of a particular class share the same behavior. The class acts as a template, producing an object with the same interface each time the constructor is called.

Although Ruby acts the same way, that isn't the end of the story. Once you have a Ruby object, you can change its behavior on-the-fly. Effectively, you're giving that object a private, anonymous subclass: All the methods of the original class are available, but you've added additional behavior for just that object. Because this behavior is private to the associated object, it can only occur once. A thing occurring only once is called a singleton, as in singleton methods and singleton classes.

The word "singleton" can be confusing because it is also used in a different sense, as the name of a well-known design pattern for a class that can only be instantiated once. For this usage, refer to the singleton.rb library.

In the following example we see a pair of objects, both of which are strings. For the second one, we will add a method upcase that will override the existing method of that name.

a = "hello" b = "goodbye" def b.upcase      # create single method   gsub(/(.)(.)/) { $1.upcase + $2 } end puts a.upcase   # HELLO puts b.upcase   # GoOdBye


Adding a singleton method to an object creates a singleton class for that object if one doesn't already exist. This singleton class's parent will be the object's original class. (This could be considered an anonymous subclass of the original class.) If you want to add multiple methods to an object, you can create the singleton class directly.

b = "goodbye" class << b   def upcase      # create single method     gsub(/(.)(.)/) { $1.upcase + $2 }   end   def upcase!     gsub!(/(.)(.)/) { $1.upcase + $2 }   end end puts b.upcase  # GoOdBye puts b         # goodbye b.upcase! puts b         # GoOdBye


As an aside, note that the more "primitive" objects (such as a Fixnum) cannot have singleton methods added. This is because an object of this nature is stored as an immediate value rather than as an object reference. However, this functionality is planned for a future revision of Ruby (though the values will still be immediate).

If you read some of the library code, you're bound to come across an idiomatic use of singleton classes. Within class definitions, you might see something like this:

class SomeClass   # Stuff...   class << self     # more stuff...   end   # ... and so on. end


Within the body of a class definition, self is the class you're defining, so creating a singleton based on it modifies the class's class. At the simplest level, this means that instance methods in the singleton class are class methods externally.

class TheClass   class << self     def hello       puts "hi"     end   end end # invoke a class method TheClass.hello            # hi


Another common use of this technique is to define class-level helper functions, which we can then access in the rest of the class definition. As an example, we want to define several accessor functions that always convert their results to a string. We could do this by coding each individually. A neater way might be to define a class-level function accessor_string that generates these functions for us (as shown in Listing 11.10).

Listing 11.10. A Class-Level Method accessor_string

class MyClass   class << self     def accessor_string(*names)       names.each do |name|         class_eval <<-EOF           def #{name}             @#{name}.to_s           end         EOF       end     end   end   def initialize     @a = [ 1, 2, 3 ]     @b = Time.now   end   accessor_string :a, :b end o = MyClass.new puts o.a           # 123 puts o.b           # Mon Apr 30 23:12:15 CDT 2001

More imaginative examples are left up to you.

The extend method will mix a module into an object. The instance methods from the module become instance methods for the object. Let's look at Listing 11.11.

Listing 11.11. Using extend

module Quantifier   def any?     self.each { |x| return true if yield x }     false   end   def all?     self.each { |x| return false if not yield x }     true   end end list = [1, 2, 3, 4, 5] list.extend(Quantifier) flag1 = list.any? {|x| x > 5 }        # false flag2 = list.any? {|x| x >= 5 }       # true flag3 = list.all? {|x| x <= 10 }      # true flag4 = list.all? {|x| x % 2 == 0 }   # false

In this example, the any? and all? methods are mixed into the list array.

11.2.3. Nesting Classes and Modules

It's possible to nest classes and modules arbitrarily. The programmer new to Ruby might not know this.

Mostly this is for namespace management. Note that the File class has a Stat class embedded inside it. This helps to "encapsulate" the Stat class inside a class of related functionality and also allows for a future class named Stat, which won't conflict with that one (perhaps a statistics class, for instance).

The Struct::Tms class is a similar example. Any new Struct is placed in this namespace so as not to pollute the one above it, and Tms is really just another Struct.

It's also conceivable that you might want to create a nested class simply because the outside world doesn't need that class or shouldn't access it. In other words, you can create classes that are subject to the principle of "data hiding" just as the instance variables and methods are subject to the same principle at a lower level.

class BugTrackingSystem   class Bug     #...   end   #... end # Nothing out here knows about Bug.


You can nest a class within a module, a module within a class, and so on. If you find interesting and creative uses for this technique, please let us all know about it.

11.2.4. Creating Parametric Classes

Learn the rules; then break them.

Basho

Suppose that we wanted to create multiple classes that differed only in the initial values of the class-level variables. Recall that a class variable is typically initialized as a part of the class definition.

class Terran   @@home_planet = "Earth"   def Terran.home_planet     @@home_planet   end   def Terran.home_planet=(x)     @@home_planet = x   end   #... end


That is all fine, but suppose that we had a number of similar classes to define. The novice will think, "Ah, I'll just define a superclass" (see Listing 11.12).

Listing 11.12. Parametric Classes: The Wrong Way

class IntelligentLife   # Wrong way to do this!   @@home_planet = nil   def IntelligentLife.home_planet     @@home_planet   end   def IntelligentLife.home_planet=(x)     @@home_planet = x   end   #... end class Terran < IntelligentLife   @@home_planet = "Earth"   #... end class Martian < IntelligentLife   @@home_planet = "Mars"   #... end

But this won't work. If we call Terran.home_planet, we expect a result of "Earth"but we get "Mars"!

Why would this happen? The answer is that class variables aren't truly class variables; they belong not to the class but to the entire inheritance hierarchy. The class variables aren't copied from the parent class but are shared with the parent (and thus with the "sibling" classes).

We could eliminate the definition of the class variable in the base class, but then the class methods we define no longer work!

We could fix this by moving these definitions to the child classes, but now we've defeated our whole purpose. We're declaring separate classes without any "parameterization."

We'll offer a different solution. We'll defer the evaluation of the class variable until runtime by using the class_eval method. Listing 11.13 shows a complete solution.

Listing 11.13. Parametric Classes: A Better Way

class IntelligentLife   def IntelligentLife.home_planet     class_eval("@@home_planet")   end   def IntelligentLife.home_planet=(x)     class_eval("@@home_planet = #{x}")   end   #... end class Terran < IntelligentLife   @@home_planet = "Earth"   #... end class Martian < IntelligentLife   @@home_planet = "Mars"   #... end puts Terran.home_planet            # Earth puts Martian.home_planet           # Mars

It goes without saying that inheritance still operates normally here. Any instance methods or instance variables defined within IntelligentLife will be inherited by Terran and Martian just as you would expect.

Listing 11.14 is perhaps the best solution. Here we don't use class variables at all but class instance variables.

Listing 11.14. Parametric Classes: The Best Way

class IntelligentLife   class << self     attr_accessor :home_planet   end   #... end class Terran < IntelligentLife   self.home_planet = "Earth"   #... end class Martian < IntelligentLife   self.home_planet = "Mars"   #... end puts Terran.home_planet            # Earth puts Martian.home_planet           # Mars

Here we open up the singleton class and define an accessor called home_planet. The two child classes call their own accessors and set the variable. These accessors work strictly on a per-class basis now.

As a small enhancement, let's also add a private call in the singleton class:

private :home_planet=


Making the writer private will prevent any code outside the hierarchy from changing this value. As always, using private is an "advisory" protection and is easily bypassed by the programmer who wants to. Making a method private at least tells us we are not meant to call that method in this particular context.

I should mention that there are other ways of implementing these techniques. Use your creativity.

11.2.5. Using Continuations to Implement a Generator

One of the more abstruse features of Ruby is the continuation. This is a structured way of handling a nonlocal jump and return; a continuation object stores a return address and an execution context. It is somewhat analogous to the setjmp/longjmp feature in C, but it stores more context.

The Kernel method callcc takes a block and returns an object of the Continuation class. The object returned is also passed into the block as a parameter, just to keep things confusing.

The only method of Continuation is call, which causes a nonlocal return to the end of the callcc block. The callcc can be terminated either by falling through the block or by calling the call method.

Think of a continuation as being like the "save game" feature in a classic adventure game. You save the game at a well-known place where all is calm; you go off and do something dangerous and experimental; and when you die, you go back and restore the game so you can try something else.

The best way to understand continuations is to watch the movie Run, Lola, Run (or the original German version, Lola Rennt).

There are a few good examples of how to use continuations. Some of the best ones we have seen come from Jim Weirich. In this next example, we see how Jim implemented a "generator" as a result of his discussion with another Ruby programmer, Hugh Sasse.

A generator is made possible by the suspend of Icon (also found in Prolog), which allows a function to resume execution just after the last place it returned a value. Hugh describes it as like an "inside-out yield."

A generator library is now a part of Ruby. For more on this topic, refer to section 8.3.7, "Using Generator Objects."

Listing 11.15, then, is Jim's implementation of a generator that generates Fibonacci numbers one after another. Continuations are used to preserve the call state from one invocation to the next.

Listing 11.15. Fibonacci Generator

class Generator   def initialize     do_generation   end   def next     callcc do |here|       @main_context = here;       @generator_context.call     end   end   private   def do_generation     callcc do |context|       @generator_context = context;       return     end     generating_loop   end   def generate(value)     callcc do |context|       @generator_context = context;       @main_context.call(value)     end   end end # Subclass this and define a generating_loop class FibGenerator < Generator   def generating_loop     generate(1)     a, b = 1, 1     loop do       generate(b)       a, b = b, a+b     end   end end # Now instantiate the class... fib = FibGenerator.new puts fib.next            # 1 puts fib.next            # 1 puts fib.next            # 2 puts fib.next            # 3 puts fib.next            # 5 puts fib.next            # 8 puts fib.next            # 13 # And so on...

There are certainly practical applications for continuations. One example is the web framework Borges (named for the author Jorge Luis Borges), which takes its inspiration from Seaside. In this paradigm, the traditional flow of control of a web application is "re-inverted" so that the logic appears "normal." For example, you display a page, get a result from a form, display the next page, and so on with intuitive logic.

The problem with this approach is that continuations in general are "expensive" operations. They save quite a bit of state and require significant time to switch contexts. Use them with caution if you're concerned about performance.

11.2.6. Storing Code As Objects

Not surprisingly, Ruby gives you several alternatives when it comes to storing a chunk of code in the form of an object. In this section, we'll take a look at Proc objects, Method objects, and UnboundMethod objects.

The built-in class Proc is used to wrap Ruby blocks in an object. Proc objects, like blocks, are closures, and therefore carry around the context in which they were defined.

myproc = Proc.new { |a| puts "Param is #{a}" } myproc.call(99)         # Param is 99


Proc objects are also created automatically by Ruby when a method defined with a trailing & parameter is called with a block:

def take_block(x, &block)   puts block.class   x.times {|i| block[i, i*i] } end take_block(3) { |n,s| puts "#{n} squared is #{s}" }


This example also shows the use of brackets ([]) as an alias for the call method. The output is shown here:

Proc 0 squared is 0 1 squared is 1 2 squared is 4


If you have a Proc object, you can pass it to a method that's expecting a block, preceding its name with an &, as shown here:

myproc = proc { |n| print n, "... " } (1..3).each(&myproc)                    # 1... 2... 3...


Ruby also lets you turn a method into an object. Historically, this is done using Object#method, which creates a Method object as a closure in a particular object.

str = "cat" meth = str.method(:length) a = meth.call               #  3  (length of "cat") str << "erpillar" b = meth.call               # 11  (length of "caterpillar") str = "dog" # Note the next call! The variable str refers to a new object # ("dog") now, but meth is still bound to the old object. c = meth.call               # 11  (length of "caterpillar")


As of Ruby 1.6.2, you can also use Module#instance_method to create UnboundMethod objects. These represent a method associated with a class rather than one particular object. Before calling an UnboundMethod object, you must first bind it to a particular object. This act of binding produces a Method object, which you call normally.

umeth = String.instance_method(:length) m1 = umeth.bind("cat") m1.call                         # 3 m2 = umeth.bind("caterpillar") m2.call                         # 11


This explicit binding makes the UnboundMethod object a little more intuitive than Method.

11.2.7. How Module Inclusion Works

When a module is included into a class, Ruby in effect creates a proxy class as the immediate ancestor of that class. You may or may not find this intuitive. Any methods in an included module are "masked" by any methods that appear in the class.

module MyMod   def meth     "from module"   end end class ParentClass   def meth     "from parent"   end end class ChildClass < ParentClass   include MyMod   def meth     "from child"   end end x = ChildClass.new p x.meth              # from child


This is just like a real inheritance relationship: Anything the child redefines is the new current definition. This is true regardless of whether the include is done before or after the redefinition.

Here's a similar example, where the child method invokes super instead of returning a simple string. What do you expect it to return?

# MyMod and ParentClass unchanged class ChildClass < ParentClass   include MyMod   def meth     "from child: super = " + super   end end x = ChildClass.new p x.meth              # from child: super = from module


This previous example shows that the module is really the new parent of the class. What if we also let the module invoke super in the same way?

module MyMod   def meth     "from module: super = " + super   end end # ParentClass is unchanged class ChildClass < ParentClass   include MyMod   def meth     "from child: super = " + super   end end x = ChildClass.new p x.meth       # from child: super = from module: super = from parent


The meth from MyMod can call super only because there actually is a meth in the superclass (that is, in at least one ancestor). What would happen if we called this in another circumstance?

module MyMod   def meth     "from module: super = " + super   end end class Foo      include MyMod end x = Foo.new x.meth


This code would result in a NoMethodError (or a call to method_missing if one existed).

11.2.8. Detecting Default Parameters

In 2004, Ian Macdonald asked on the mailing list: "How can I detect whether a parameter was specified by the caller, or the default was taken?" It was an interesting question; not something you would use every day, but still interesting.

At least three solutions were suggested. By far the simplest and best was by Nobu Nakada. This trick is shown in the following code:

def meth(a, b=(flag=true; 345))   puts "b is #{b} and flag is #{flag.inspect}" end meth(123)        # b is 345 and flag is true meth(123,345)    # b is 345 and flag is nil meth(123,456)    # b is 456 and flag is nil


As the preceding example shows, this works even if the caller explicitly supplies what happens to be the default value. The trick is obvious when you see it: The parenthesized expression sets a local variable called flag but then returns the default value 345. This is a tribute to the power of Ruby.

11.2.9. Delegating or Forwarding

Ruby has two libraries that offer solutions for delegating or forwarding method calls from the receiver to another object. These are delegate and forwardable; we'll look at them in that order.

The delegate library gives you three ways of solving this problem.

The SimpleDelegator class can be useful when the object delegated to can change over the lifespan of the receiving object. The __setobj__ method is used to select the object to which we're delegating.

However, I find the SimpleDelegator technique to be a little too simple. Since I am not convinced it offers a significant improvement over doing the same thing by hand, I won't cover it here.

The DelegateClass top-level method takes a class (to be delegated to) as a parameter. It then creates a new class from which we can inherit. Here's an example of creating our own Queue class that delegates to an Array object:

require 'delegate' class MyQueue < DelegateClass(Array)   def initialize(arg=[])     super(arg)   end   alias_method :enqueue, :push   alias_method :dequeue, :shift end mq = MyQueue.new mq.enqueue(123) mq.enqueue(234) p mq.dequeue        # 123 p mq.dequeue        # 234


It's also possible to inherit from Delegator and implement a __getobj__ method; this is the way SimpleDelegator is implemented, and it offers more control over the delegation.

But if you want more control, you should probably be doing per-method delegation rather than per-class anyway. The forwardable library enables you to do this. Let's revisit the queue example:

require 'forwardable' class MyQueue   extend Forwardable   def initialize(obj=[])     @queue = obj    # delegate to this object   end   def_delegator :@queue, :push,  :enqueue   def_delegator :@queue, :shift, :dequeue   def_delegators :@queue, :clear, :empty?, :length, :size, :<<   # Any additional stuff... end


This example shows that the def_delegator method associates a method call (for example, enqueue) with a delegated object @queue and the correct method to call on that object (push). In other words, when we call enqueue on a MyQueue object, we delegate that by making a push call on our object @queue (which is usually an array).

Notice how we say :@queue rather than :queue or @queue; this is simply because of the way the Forwardable class is written. It could have been done differently.

Sometimes we want to pass methods through to the delegate object by using the same method name. The def_delegators method allows us to specify an unlimited number of these. For example, as shown in the preceding code example, invoking length on a MyQueue object will in turn call length on @queue.

Unlike the first example in this chapter, the other methods on the delegate object are simply not supported. This can be a good thing. For example, you don't want to invoke [] or []= on a queue; if you do, you're not using it as a queue anymore.

Also notice that the previous code allows the caller to pass an object into the constructor (to be used as the delegate object). In the spirit of duck-typing, this means that I can choose the kind of object I want to delegate to; as long as it supports the set of methods that I reference in the code.

For example, these calls are all valid. (The last two assume that we've done a require 'thread' previously.)

q1 = MyQueue.new                    # use an array q2 = MyQueue.new(my_array)          # use one specific array q3 = MyQueue.new(Queue.new)         # use a Queue (thread.rb) q4 = MyQueue.new(SizedQueue.new)    # use a SizedQueue (thread.rb)


So for example, q3 and q4 in the preceding examples are now magically thread-safe because they delegate to an object that is thread-safe. (That's unless any customized code not shown here violates that.)

There is also a SingleForwardable class that operates on an instance rather than on an entire class. This is useful if you want one certain object of a class to delegate to another object, but the other objects of that class will not delegate.

You might ask: Is delegation better than inheritance? But it's the wrong question in a sense. Sometimes the situation calls for delegation rather than inheritance; for example, suppose that a class already has a parent class. We can still use delegation (in some form), but we can't inherit from an additional parent (because Ruby doesn't allow multiple inheritance).

11.2.10. Automatically Defining Class-level Readers and Writers

We have seen the methods attr_reader, attr_writer, and attr_accessor, which make it a little easier to define readers and writers (getters and setters) for instance attributes. But what about class-level attributes?

Ruby has no similar facility for creating these automatically. But we could create something similar on our own.

In the first edition of this book, we showed you an elaborate scheme involving class_eval. We created new methods such as cattr_reader and cattr_writer.

There is an easier, better way. Open the singleton class and just use the ordinary attr family of methods. The resulting instance variables in the singleton class will be class instance variables. These are often better for our purposes than class variables because they are strictly per-class and are not shared up and down the hierarchy.

class MyClass   @alpha = 123                # Initialize @alpha   class << self     attr_reader :alpha          # MyClass.alpha()     attr_writer :beta           # MyClass.beta=()     attr_accessor :gamma        # MyClass.gamma() and   end                           #   MyClass.gamma=()   def MyClass.look     puts "#@alpha, #@beta, #@gamma"   end   #...   end   puts MyClass.alpha             # 123   MyClass.beta = 456   MyClass.gamma = 789   puts MyClass.gamma             # 789   MyClass.look                   # 123, 456, 789


Most classes are no good without instance level data. We've only omitted it here for clarity.

11.2.11. Working in Advanced Programming Disciplines

Brother, can you paradigm?

Grafitti seen at IBM Austin, 1989

Many philosophies of programming are popular in various circles. These are often difficult to characterize in relation to object-oriented or dynamic techniques, and some of these styles can be actually independent of whether a language is dynamic or object-oriented.

Because we are far from experts in these matters, we are relying mostly on hearsay. So take these next paragraphs with a grain of sodium chloride.

Some programmers prefer a flavor of OOP known as prototype-based OOP (or classless OOP). In this world, an object isn't described as a member of a class; it is built from the ground up, and other objects are created based on the prototype. Ruby has at least rudimentary support for this programming style because it allows singleton methods for individual objects, and the clone method will clone these singletons. Interested readers should also look at the simple OpenStruct class for building Python-like objects; you should also be aware of how method_missing works.

One or two limitations in Ruby hamper classless OOP. Certain objects such as Fixnums are stored not as references but as immediate values so that they can't have singleton methods. This is supposed to change in the future, but at the time of this writing, it's impossible to project when it will happen.

In functional programming (FP), emphasis is placed on the evaluation of expressions rather than the execution of commands. An FP language is one that encourages and supports functional programming, and as such, there is a natural gray area. Nearly all would agree that Haskell is a pure functional language, whereas Ruby certainly isn't one.

But Ruby has at least some minimal support for FP; it has a fairly rich set of methods for operating on arrays (lists), and it has Proc objects so that code can be encapsulated and called over and over. Ruby allows the method chaining that is so common in FP, although it is easy to be bitten by the phenomenon of a bang method (such as sort! or gsub!) that returns nil when the receiver doesn't actually change.

There have been some initial efforts at a library that would serve as a kind of FP "compatibility layer," borrowing certain ideas from Haskell. At the time of this writing, these efforts aren't complete.

The concept of aspect-oriented programming (AOP) is an interesting one. In AOP, we try to deal with programming issues that crosscut the modular structure of the program. In other words, some activities or features of a system will be scattered across the system in code fragments here and there, rather than being gathered into one tidy location. In other words, we are attempting to modularize things that in traditional OOP or procedural techniques are difficult to modularize. We are working at right angles to our usual way of thinking.

Ruby certainly wasn't created specifically with AOP in mind. But it was designed to be a flexible and dynamic language, and it is conceivable that these techniques can be facilitated by a library. In fact, a library called AspectR is an early effort at implementing AOP; see the Ruby Application Archive for the most recent version.

The concept of Design by Contract (DBC) is well known to Eiffel devotees, although it is certainly known outside those circles also. The general idea is that a piece of code (a method or class) implements a contract; certain preconditions must be true if the code is going to do its job, and the code guarantees that certain post-conditions are true afterward. The robustness of a system can be greatly enhanced by the ability to specify this contract explicitly and have it automatically checked at runtime. The usefulness of the technique is expanded by the inheritance of contract information as classes are extended.

The Eiffel language has DBC explicitly built in; Ruby does not. There are at least two usable implementations of DBC libraries, however, and we recommend that you choose one and learn it.

Design patterns have inspired much discussion over the last few years. These, of course, are highly language-independent and can be implemented well in many languages. But again, Ruby's unusual flexibility makes them perhaps more practical than in some other environments. Well-known examples of these are given elsewhere; the Visitor pattern is essentially implemented in Ruby's default iterator each, and other patterns are part of Ruby's standard distribution (the standard libraries delegator.rb and singleton.rb).

The Extreme Programming (XP) discipline is gaining devotees daily. This methodology encourages (among other things) early testing and refactoring on-the-fly.

XP isn't language specific, although it might be easier in some languages than others. Certainly we maintain that Ruby makes refactoring easier than many languages would, although that is a highly subjective claim. But the existence of the Test::Unit library (and others) makes for a real blending of Ruby and XP. This library facilitates unit testing; it is powerful, easy to use, and has proven useful in developing other Ruby software in current use. We highly recommend the XP practice of testing early and often, and we recommend Test::Unit for those who want to do this in Ruby. (ZenTest is another excellent package with several features not found in Test::Unit.)

By the time you read this, many of the issues we talk about in this section will have been fleshed out more. As always, your four best resources for the latest information are

The comp.lang.ruby newsgroup

The Ruby Application Archive

rubyforge.org

ruby-doc.org

Other useful resources also are available, especially if you speak Japanese. It's difficult to list online resources in print because they are in constant flux. The search engine is your friend.




The Ruby Way(c) Solutions and Techniques in Ruby Programming
The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)
ISBN: 0672328844
EAN: 2147483647
Year: 2004
Pages: 269
Authors: Hal Fulton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net