Archive for the ‘metaprogramming’ Category

blocks parameters list and metaprogramming

I have been playing with a Aquarium, the new AOP framework in Ruby. A point advice is create for example :

before :type=>A, :method=>:foo do |execution_point, *args|
  self.some_method() # call a class method from A
  other_method()     # call other class method from A
  # call a method from the caller object
  execution_point.context.advised_object.bar()  
end 

The DSL is nice but i found the access to the caller object is a tedious. I prefer something like this :

before :type=>A, :method=>:foo do |execution_point,this , *args|
  self.some_method() # call a class method from A
  other_method()     # call other class method from A
  # call a method from the caller object
  this.bar()  
end 

Dean Wampier think thats would be great if the user could specify combinations
like these:

|join_point, the_self, *method_args|
|join_point|
|the_self|
|the_self, *method_args|
|join_point, the_self|
|join_point, *method_args|

But the point is that the advices is create in the “definition time class” using some code generation at
runtime. How can i know the arity of the argument list.

module Kernel  
  def before (name, &block) 
    define_method(name){ 
      if block.arity == 1
        yield :only_one
      elsif  block.arity == 2
        yield :one,:two
      elsif block.arity == -2
        yield :one, :a , :splater, :argument
      end
    }
  end
end

class A  
  before :foo do |first|   
    puts first
  end
end

a = A.new 
a.foo

class A
  before :foo do |first , second|
    puts second
  end
end

a2 = A.new 
a2.foo

class A
  before :foo do |first , *splat_arg| 
    puts splat_arg
  end
end

a3 = A.new 
a3.foo

Thats great, the block can be acceded because its a Proc class. Now we can predict the number of parameter of that the user has write and create a more friendly DSL.

How to define a attribute using metaprogramming.

Just for fun and learning. We can add an attribute using meta-programming :

class MyClass
   attr_accessor    :id, :diagram, :telegram
end

We can define a custom function :

class Class
  def my_attr_accessor( *args )
     args.each do |name|
       self.class_eval do
          attr_accessor :"#{name}"
       end
     end
  end
end

class MyNewClass
  my_attr_accessor :id, :diagram, :telegram
end

We can create a set of attributes using class_eval :

class Another_class
  def initialize
    ["id","diagram","telegram"].each do |name|
       self.class.class_eval do
          attr_accessor :"#{name}"
       end
     end
  end
end

Or more ruby idiomatic

class Yet_another_class
  def initialize
    self.class.class_eval do
        attr_accessor *[:id,:diagram,:telegram]
    end
  end
end

Metaprogramming and reflection for optional parameters.

In the previous post, the example use reflection via method missing, to emulate some getters and setters.

Another option is to define the setters and getters methods on the fly, refactoring the Mult module using metaprogramming

module Mult

  def create_method(name, &block)
     self.class.send(:define_method, name, &block)
  end

  def initialize(properties = {})
      props.each do |k, v|
  	create_method(k) { @props[k] }
	create_method("#{k}=") { |value| @props[k]=value}
      end

      properties.each do |k, v|
          #k = k.to_s
          raise ArgumentError, "unknown property: #{k}" unless @props.key?(k)
          self.send("#{k}=",v)
      end
  end
end

The iteration over the hash defaults value defines a setter and a getter for each of the properties we define in the book class.

In a second refactoring round, i replace the definitions of the getters/setters with a call to class_eval and defining inside the attribute accessor

module Mult
  def initialize(properties = {})
      props.each do |k, v|
        self.class.class_eval do
           attr_accessor :"#{k}"
        end
	self.send("#{k}=",v)
      end

      properties.each do |k, v|
          k = k.to_s
          raise ArgumentError, "unknown property: #{k}" unless @props.key?(k)
          self.send("#{k}=",v)
      end
  end
end

Ruby : module callbacks

We can override some callbacks methods in a module to modify the behaivor of a class, or the methods of a class:

If we wan to preform an action when a class “include” a module we need to override the method included in our module.

module Foo

  def self.included(mod)
    puts "and now  CallbackTest included has been used by #{mod.inspect} form Foo..."
  end

  def self.method_added(method)
    puts "Added #{method}"
  end 

end

If we want , for example, log the methods added to our class we must override method_added method.

class Bar
  include Foo

  def self.method_added(method)
    puts "Added #{method}"
  end 
end

class Bar
  def another_method
  end
end

How to extends ruby classes

Add the quickshort algorithm to the array class.

     class Array
        def qs
            return self if size <=1
            pivot = shift
            less, more = partition{ |y| y < pivot }
            [*less.qs, pivot, *more.qs]
        end
    end

Add the factorial operation to the integer class

    class Integer
        def factorial
            return 1 if self <= 1
            self * (self-1).factorial
        end
    end