Ok, so there seems to be three basic methods of doing this, the most hairy 
involves using class_eval to interpose an aspect on function call 
and return.

Cute.

Scary.

Anywhoo, the other two are basically to make the invariant method empty or 
to only invoke the invariant method if a global or const is true....

require 'benchmark'

DEBUG = false
$foo = false

class A

   attr_reader :inv

   alias_method :in_very_ant, :object_id

   def invariant
   end

   def very
     in_very_ant
     in_very_ant
   end

   def empty_method
     invariant
     invariant
   end

   def if_const
     invariant if DEBUG
     invariant if DEBUG
   end

   def if_global
     invariant if $foo
     invariant if $foo
   end

   def attr_thing
     inv
     inv
   end

   def no_code
   end
end

n = 1000000
a = A.new

Benchmark.benchmark(" "*7 + Benchmark::CAPTION, 7, Benchmark::FMTSTR, ">total:", ">avg:") do |x|
   tf = x.report("empty_method:")   { n.times do   ; a.empty_method; end }
   tt = x.report("if_const:") { n.times do   ; a.if_const    ; end }
   tg = x.report("if_global:") { n.times do   ; a.if_const    ; end }
   tu = x.report("no_code:")  { n.times do   ; a.no_code     ; end }
   tv = x.report("inv:")  { n.times do   ; a.attr_thing ; end }
   tw = x.report("very:")  { n.times do   ; a.very ; end }
   x.report("nothin:")  { n.times do   ; a ; end }
   [tf+tt+tg+tu+tv+tw, (tf+tt+tg+tu+tv+tw)/6]
end

The results are using Ruby 1.8.2...

ruby-1.8.2 binch.rb;ruby binch.rb
              user     system      total        real
empty_method:  1.210000   0.000000   1.210000 (  1.218951)
if_const:  1.010000   0.000000   1.010000 (  1.016450)
if_global:  1.020000   0.000000   1.020000 (  1.021371)
no_code:  0.580000   0.000000   0.580000 (  0.584446)
inv:     0.850000   0.000000   0.850000 (  0.852560)
very:    0.870000   0.000000   0.870000 (  0.863922)
nothin:  0.260000   0.010000   0.270000 (  0.261910)
>total:  5.540000   0.000000   5.540000 (  5.557700)
>avg:    0.923333   0.000000   0.923333 (  0.926283)

And a fairly recent version from CVS....
              user     system      total        real
empty_method:  1.160000   0.000000   1.160000 (  1.159409)
if_const:  0.950000   0.000000   0.950000 (  0.953207)
if_global:  0.970000   0.000000   0.970000 (  0.972160)
no_code:  0.570000   0.000000   0.570000 (  0.569933)
inv:     0.790000   0.000000   0.790000 (  0.789721)
very:    0.800000   0.000000   0.800000 (  0.796247)
nothin:  0.260000   0.000000   0.260000 (  0.266906)
>total:  5.240000   0.000000   5.240000 (  5.240677)
>avg:    0.873333   0.000000   0.873333 (  0.873446)

notes:

1) I was surprised to find the if const and if global so close. Obviously 
no constant folding is being done on branches.

2) Of course, no code is faster than no code. So the hairy class_eval 
tricks would be faster. Although not tremendously faster than the 
attr_reader trick.

3) Ruby 1.9 is noticably faster than 1.8.2

Thanks for all the responses and wild creativity.

4) Ruby-contract, Awesome and ruby-dbc are very very very scary modules.

I'm not sure I can cope with anything quite that clever near my code. It 
has all (and more, much more) the horror of the C preprocessor, without 
the gcc -E option to see the what it actually did.


John Carter                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : john.carter / tait.co.nz
New Zealand

Carter's Clarification of Murphy's Law.

"Things only ever go right so that they may go more spectacularly wrong later."

From this principle, all of life and physics may be deduced.