February 21, 2006

Design by contract for Ruby

Here's a nice little module Brian and I hacked together a few weeks ago. It adds basic design by contract capabilities to classes with a fairly clean syntax (yay for Ruby's flexibility!)

Take a look for yourself:

class Foo
   include DesignByContract

   pre(:sqrt, "value must be >= 0") { |v| v >= 0 }

   pre("divisor must be != 0") { |dividend, divisor| divisor != 0 }
   post { |result, a, b| a - b * result < 1e-3 }
   def div(dividend, divisor)
      dividend / divisor
   end

   def sqrt(value)
      Math.sqrt value
   end

   post(:sqrt, "error is greater than expected") { |result, value|
         result * result - value < 1e-3
   }
end


When the DesignByContract module is included in a class, the pre and post methods become available. The parameters to these methods are a symbol representing the method to add the pre or post condition to, the message to print if the condition fails and a block that implements the condition.

The method name and message are optional, though. If the method name is missing, the condition will applied to the first method definition following the pre/post declaration. Note that if the method name is present, the condition can be defined anywhere in the class definition, as shown in the example above.

The condition block receives all the arguments passed to the method it applies to. They must return a boolean value indicating whether the condition succeeded. A minor difference between pre and post blocks is that the post block receives the result returned by the method as the first argument. This makes it possible to validate the results against the inputs such as in:

post { |result, dividend, divisor| dividend - divisor * result < 1e-3 }


Get it here!

How it works


In a nutshell, the pre and post methods intercept the existing method using the technique I described a few weeks ago (i.e., no poluting the class' namespace with aliased methods). The new methods test the provided condition before or after delegating the call to the original method and raise an exception if the condition fails.

There is a small caveat, though. If pre/post are called before the method is first created, it cannot be intercepted. To get around this the module uses an alternative trick. Whenever the module is included, it hooks into the method_added callback of class it's included into. If pre/post are called and the corresponding method does not exist, the interception is scheduled until the method is added.

Labels:

10 Comments:

Blogger Martin said...

Hey Ian,
It's good to hear somebody found this code helpful. And yeah, feel free to use it however you like.

April 15, 2006 11:31 AM  
Blogger Martin said...

BTW, I just made the license for that code explicit. I'm making it available under the MIT license. Let me know if that's not suitable for your use and I'll change it.

April 15, 2006 11:39 AM  
Blogger chicoary said...

Very good! But I noticed that the pre/post invocation order is not guaranteed.

June 01, 2006 6:31 AM  
Blogger bparanj said...

where is the invariant?

July 05, 2006 8:57 PM  
Blogger Martin said...

Erik,

I just uploaded a new version that fixes that issue.

Get it here: http://utils.ning.com/ruby/dbc.rb

August 19, 2006 6:43 PM  
Blogger Twm said...

Thanks for this. It looks like a very clean, natural implementation.
This doesn't seem to work for private methods. Nothing happens at all when i put a pre in front of a private.
I'd like to use this to state contracts between internal private methods as well as for public interfaces. How do i do that?

Ditto the comment about disabling it for release code.

October 08, 2006 5:50 PM  
Blogger legionnaired said...

This is a great module for ruby code, I think I want to add some functionality to a rails addon for rails developers, to give controllers, models, and views some basic requires/ensures capability. There'll probably be a tool as well that'll allow you to generate an app-specific cheat sheet of what each component makes use of... Would it be possible for me to commit some stuff to rdbc?

March 11, 2008 12:25 PM  
Blogger bestonline323 said...

That's a helpful code, thanks.

------------------------------------------
Interior Design Furniture Scotland

June 16, 2008 12:59 PM  
Blogger Adi said...

Pandeglang is one of many places in Indonesia are eligible for tourist destination.
Kenali dan Kunjungi Objek Wisata di Pandeglang Objek Wisata di Pandeglang Kenali dan Kunjungi Objek Wisata di Pandeglang Pantai Carita Seni Saman Rampak Bedug

October 29, 2009 11:07 PM  
Anonymous Anonymous said...

araç sorgulama
sorgulama
ehliyet
açıköğretim
bağkur sorgulama
ssk sorgulama
emekli sandığı
cinsellik
radyo dinle
korku
evlilik
hikaye
gazeteler
ilan
ssk
iş ilanları
bağkur
gazete oku

January 17, 2010 1:49 PM  

Post a Comment

<< Home