structured_warnings Highlights

Posted by schmidt
(2008-02-22 12:00:00)

After my short Announcement yesterday, I’d like to go on an name some of the highlights.

Besides the basic functionality proposed in the original article, it has some more neat features, that make the life of its users enjoyable and fun.

So you better start using structured_warnings now.

Dynamic scoping

Using the block syntax to disable warnings provides a dynamic scope of deactivation. Not only everything within the block will raise no warning, but every piece of code, that is accessed from there on. This can become very powerful for certain uses.

Last one wins

You never know exactly, so deactivating a warning for a dynamic scope may be overridden by enabling it again in a deeper scope and the other way round.

Warner architecture

Each warning is passed to a warner instance, which it there to format the information, which is put to stdout. This warner may be changed for –again – a dynamic scope. This way you may instruct your favourite web framework to use a specific warner, that fetches all the information and writes it to a database instead of stdout.

Perhaps you want to have warnings in your merchant library to be to you via twitter message. Who knows? It will be pretty easy. Just have a look at the warner that is used for the test assertions. It will give you a good start.

Inheritance done right

Instead of redefining the Kernel#warn method directly, structured_warnings simply defines its own warn. The module is mixed into Object, so that every warn will go to this implementation first. After collecting all the necessary information, checking if the current warning is not disabled and formatting the output, the resulting message is passed on to the base implementation.

This way it is possible to add other extensions to you warning mechanism, you may freely redirect stderr to whatever you want. structured_warnings warn will just do, what it is supposed to.

Fully documented sources

Today I finished the documentation part. Now each and every method, public or not is documented. Just have a look at the RDoc generated API site.

Performance

Come on. The main goal is to avoid the use of code, that raises warnings. With the help of this library it is as easy as it gets to do that. A warning should always be an exception (not in the sense of the Ruby class, but in the sense of “not common”), so who will be concerned about performance.

The only thing I can tell you is, that structured_warnings will slow down calls to warn but it will not slow down your whole code and that is what counts.

[Ann] structured_warnings 0.1.0 released

Posted by schmidt
(2008-02-21 12:00:00)

Read this article by Daniel Berger first.

I just implemented his suggestions and made them available via RubyGems and our rug-b Rubyforge account. Have a look at the gems website and leave me a comment, if you like.

A simple gem install structured_warnings will bring it to your machine, another require 'structured_warnings' brings it to your project and tests.

The implementation relies heavily on Christian Neukirchen's dynamic.rb.

Disclaimer: I cannot manipulate the rb_warn method, so all the "method redefined", "void context" and "parenthesis" warnings will remain active.

Introducing cache_annotations the other way round 0

Posted by schmidt
(2007-06-01 11:17:00)

In one of my last articles I introduced my lasted gem cache_annotations. Today I think it was explained as black vodoo, which might be uncomfortable to most readers. Therefore, I'll start again, but the otherway round and everybody will love its simplicity.

cache_annotations

With CacheAnnotation you may easily provide your methods with an often needed caching. Suppose you are using the following piece of code:

class A
  def a
    @a ||= "some heavy computing that should be" +
        " done only once"
  end
end

This could look so much better:

class A
  include CacheAnnotation

  cached
  def a
    "some heavy computing that should be done only once"
  end
end

Or even better for single argumented methods:

class A
  def b(arg0)
    @b ||= {}
    @b[arg0] ||= "some heavy computing in " +
                 "respect to #{arg0} " +
                 "that should be done only once"
  end
end

vs.

class A
  include CacheAnnotation

  cached
  def b(arg0)
    "some heavy computing in respect to #{arg0} " +
    "that should be done only once"
  end
end

Behind the scenes, CacheAnnotation replaces the method body with the caching code. So the two versions are equal concerning behaviour and speed. If you don't want CacheAnnotation to derive the instance variable's name from the method name, you may supply a custom one:

class A
  include CacheAnnotation

  cached :in => :@b_cache
  def b(arg0)
    "some heavy computing in respect to #{arg0} " +
    "that should be done only once"
  end
end

If you want to use CacheAnnotation on the class side, you have to use a special technique to add these methods. It is described pretty good on http://www.dcmanges.com/blog/27

class A
  module ClassMethods
    include CacheAnnotation

    cached
    def c
      "some heavy computing that should " +
            "be done only once"
    end
  end
  self.extend(ClassMethods)
end

Final Remarks

That's all the voodoo. Generating a simple method for you, basically the same thing attr_* does.

You will find other chunks of repeated code and you should think, if this could be stripped of and be used in a declarative way.

cache_annotations release to the wild 0

Posted by schmidt
(2007-05-28 12:40:00)

Ruby is all about keeping things simple, letting others do the simple work and being creative and not occupied. Have you ever thought about how often you are writing down the same idioms. Although they may look cleaner than in other languages it is cumbersome to write the same patterns over and over.

cache_annotations tries to eliminate one of these. And I encourage everybody to spot others and ged rid of these to. This is Ruby and it should look as lean and mean as possible. cache_annotations is a simple annotation-like, aop-inspired api that allows you to mark methods as functional. These will always return the same values, given the same parameters and do not produce any side effects. Examples would be any mathematical computation, database selects on a read-only db or between to updates.

Fibonacci example

I have a pretty easy and striking example to show the use and beauty of cache_annotations. The Fibonacci sequence. The following code will compute every fibonacci value corresponding to the recursive defintion on Wikipedia.

class Fibonacci
  module ClassMethods
    def compute(number)
      if number < 0
        raise ArgumentError.new(
            "Fibonacci is not defined for numbers < 0")
      elsif number < 2
        number
      else
        Fibonacci.compute(number - 2) + 
             Fibonacci.compute(number - 1)
      end
    end
  end
  self.extend(ClassMethods)
end

Fibonacci.compute(10) # => 55

But after looking closely to the code you may see, that esp. for larger numbers, this defintion is a killer. For each call it executes two recursive other calls and this eats stack levels. Besides that Fibonacci.compute(number - 2) is computed twice, one time in each recursive call. Finally computing Fibonacci.compute(100) takes ages - or at least longer than 10 seconds.

We have got two options here. Switch to a non-recursive function definition or avoid the double computation by caching results.

Enter cache_annotations

require 'rubygems'
require 'cache_annotations'

class Fibonacci
  module ClassMethods
    include CacheAnnotation

    cached :in => :@fibonacci_cache
    def compute(number)
      if number < 0
        raise ArgumentError.new(
            "Fibonacci is not defined for numbers < 0")
      elsif number < 2
        number
      else
        Fibonacci.compute(number - 2) + 
             Fibonacci.compute(number - 1)
      end
    end
  end
  self.extend(ClassMethods)
end

That's it. No need to rewrite, not much more code. The compute function is automagically cached into an instance variable Hash named @fibonacci_cache. The performance inprovements are tremendous with two additional lines of code and no additional brain work.

The gem

Want to use it on your own? All you need to do is

sudo gem install cache_annotations

I just released it, so it may take a day to reach all rubyforge mirrors. Detailed information and other examples may be found in the API doc. If you have any problems, ideas or comments, feel free to leave a comment or drop me a line via mail.