Sunday, 10. December 2006

ruby metaprogramming I

just played a little with some basic groundwork of ruby's potentials upon metaprogramming. metaprogramming still is and even will become more important in regard to the increasing relevance due to domain specific languages (dsl). in the context of modeling such a dsl and expressing domain specific concepts within it, metaprogramming is a powerful and very flexible option, since it will provide the full power of ruby's features when it comes to modeling domain concepts.

with the rise of java 6 and especially the support for scripting languages (jsr 223 - scripting for the java platform) such potentials could become strategic, since half typed languages like java are quite inflexible for extensions due to domain specific needs. scripting languages like ruby could play an important role in the next future, since it will be very easy to interoperate. given this, one could take the power of both sides java and ruby, when it comes to the implementation of applications. one could apply the right instrument to the approriate parts of an application in order to benefit from the strength of each particular language. for example, web application layer could be done using rails, but java provides its full power under the hood when asking for security, transactionional services, distribution or integration of legacy systems. the core business logic (especially where the domain drives complexity) could be expressed using a domain specific language, again falling back to ruby's above mentioned potentials ...

so much for the motivation ... now to the action ...

come in - we're open, too

one basic feature of ruby's possibilities due to metaprogramming is its facility of extending almost everything, even 'build in' classes. so its very easy to enrich a given class with new features.

if we would ask a number if it's an odd one, we would receive a reaction like the following:


> 23.odd?
NoMethodError: undefined method `odd?' for 23:Fixnum

this is normal behaviour since class Fixnum doesn't provide such a method.
but thanks to the openness of classes, we can enhance Fixnum at any time and pay for a new method:


class Fixnum
   def odd?
     self % 2 == 1
  end
end

if we now ask the number a second time, the sun will shine again ...


> 23.odd?
=> true



enhancing class features

what about enhancing the features of class itself?
you may know about 'attr_reader' or 'attr_writer' in order to define instance properties without explicitly defining 'getters' and 'setters' (sorry, i'm originally a java guy).


class Person
  attr_reader :firstname, :lastname, :birthday
  attr_writer :firstname, :lastname, :birthday
end


we have to do so - declaring attr_reader for offering 'getter' and declaring attr_writer for offering 'setter' functionality - because there's no feature of declaring both 'setter' and 'getter' in one go.
now, if we would consider it nice to have such a feature - let's enhance ...

since everything is an object in Ruby world, also Class is nothing more than a specific class that - among others - inherits from Object, you can of course also enhance the definition of class and let all class definitions (which 'uses' Class) benefit from that enhancements:


class Class
  def attr_rw( *attrib_names )
    attrib_names.each do |name|
      attr_reader name
      attr_writer name
    end
 end
end


as you can see, Class now owns a new method attr_rw, which delegates the passed property names to both attr_reader and attr_writer, means that both 'getter' and 'setter' functionality will be assigned.

we now could use this new feature as it would be a normal 'keyword' of the language, like attr_reader or attr_writer:


class Person
  attr_rw :firstname, :lastname, :birthday
end

admitted, this is a very simple example, but it shows the power of enhancing the 'core' language with new 'concepts' quite well.
it's a first step towards the way of metaprogramming ...

 

Posted by mario.gleichmann at 02:09:13 | Permanent Link | Comments (7) |