Ruby: create your code on the fly

Ruby is so powerful it scares me. I’m far from even grasping how powerful it is. Whenever I discover something cool to do with it, I have the urge to tell it to the world.

First, here’s the context:

I’m currently working my way through building a Rails browser game in the style of these ones. For simplicity’s sake, think of it as a RPG-style game.
I have an Avatar object that belongs_to Race and Occupation.
The avatar, race and occupation have several common properties: mana, health, reactivity, initiative, etc. The avatar has its own health, mana or reflexes value, but when I access them I want to sum them up with the corresponding values stored in the Race and Occupation; this means that the race and occupation you chose when creating the avatar will impact the character’s features throughout the game.

What I could do was to define accessor methods for each property:


  def get_mana
    mana+race.mana+occupation.mana
  end

  def get_health
    health+race.health+occupation.health
  end

While it works just fine, the problem of this approach is that it’s not DRY(Don’t Repeat Yourself) – if I have tens of such common properties, I end up having tens of similar methods which differ only by the name of the property. So what I wanted to do was to have a method similar to the attr_reader one, with my own body instead.

After lots of head banging and looking through the ruby Object and Module classes, today I finally discovered Creating DSLs with Ruby which contains exactly what I need – a method for creating your custom attr_accessor-like methods, receiving as parameters a list of symbols and generating, in return, corresponding object methods. I’m not going to reproduce their code, but show you directly mine:


class Module
  # race_occupation accessor : for x, returns x+race.x+occupation.x
  def ro_accessor(*symbols)
    symbols.each { |sym|
      class_eval %{
        def get_#{sym}
            return #{sym} + race.#{sym} + occupation.#{sym}
        end
      }
    }
  end
end

Now, I’m able to write

  
    class Avatar < ActiveRecord::Base
      belongs_to :race
      belongs_to :occupation
      ro_accessor :mana, :health, :reactivity, :initiative
    ....
    end
  

which further allows me to invoke the accessor methods like this:

  
    >> a=Avatar.find 1
    => #<Avatar:0x35aaee4 @attributes={"mana"=>"10", "initiative"=>"1", "reactivity"=>"0.2", "race_id"=>"2", "occupation_id"=>"3", "id"=>"1", "health"=>"10"}>
    >> a.get_mana
    => 20
  

I obviously left out irrelevant accessors and methods, but you get the idea – custom code, generated by Ruby; in other words, turning the standard language into a DSL – Domain Specific Language.


Similar Posts:

Leave a Reply