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.
[tags]Ruby, DSL, Rails, implementing a game, custom made accessors[/tags]
Sponsored link – Unicul magazin online de sapte stele din Romania for the benefit of a Romanian ONG


Similar Posts:

0 Comments

Instead of reopening the Module class, you could create a distinct module RaceOccupation, define the ro_accessor method there, and include RaceOccupation in your model class.

But otherwise… Hell yeah! Metaprogramming ftw.

Jeleu says:

Kool.
Other way is to send in a bag of goodies and via reflection call the setters for the appropriate attributes.
You could move that stuff into a builder (which ensure the creation is valid).

Alex says:

I had tried this approach, using Object and Model methods, but it looked so ugly (and didn’t work, on top of that) compared to this elegant meta-programming approach…

Jeleu says:

Dunno about u , but to me flexibility wins over beauty 🙂

So for example, u want to populate another attribute which is not in the above declaration? Change source code? One way..Use reflection? No change in code.

But indeed, its an elegant solution 🙂

Jeleu says:

That big a$$ list of params could be refactored using parameter object.

I have not done anything in Ruby yet but from what Ive been reading im liking it a lot.

I m not sure whether it is worth investing my time in it since most of the jobs on the market are still dotnet or java.

Alex says:

Jeleu, I didn’t need this kind of method for *all* accessors, but only for a subset of it, so reflection wasn’t possible.

NB says:

Uh-huh. And now imagine how excited you’ld be if you looked into any functional programming language :)).

Jeleu says:

It’s ok, different way of skinning the cat:) I was looking at if from the perspective of the api writer/coder usage

We develop using this approach (meta) quite heavily. Entire modules are metadata driven with tons of factories and the codebase (BL) rarely changes.

The only drawback of reflection is the slowness but caching compensate for the lack of speed.

what IDE would you recommend for Ruby?

Alex says:

NB, who said Ruby wasn’t a functional language? Not a purely functional one, I give you that, but actually even better for productive purposes..

Jeleu, if your language and libraries aren’t as cluttered as Visual * or J2EE, I wouldn’t recommend using IDEs – they stand in the way instead of helping…

I’m coding in Textmate on Mac, while on Windows I was coding in VIM or JEdit. There’s actually some free/open source Textmate Windows clone out there that looks promising. If you want to be impressed with Textmate, take a look at the screencasts on rubyonrails.org…

I think you can take a look at Ara’s attribute(s) library. I don’t know if it covers all but it may at least give you an idea how it can be done.

cheers,

./alex

.w( the_mindstorm )p.

Leave a Reply

Your email address will not be published.