Dr Nic

Meta-Magic in Ruby: Dr Nic Unplugged in Stockholm

Last nights’ Ruby meeting in Stockholm had a great turn out and starred Ola Bini sharing the latest and greatest about JRuby, and myself giving an overview on the wonders of Meta-Magic in Ruby.

I’ll write a separate post on Ola’s presentation shortly. It was awesome and I videoed it. Hehehe.

But first and foremost, lets talk about me. Or rather, let’s talk about my talk, which was also videoed.

Meta-magic in a programming language is as important to programmers as changeable ring tones are to teenagers. Authors of programming languages cannot provide every feature to everyone, so it is so wonderful to be able to add new language features and extensions that you want. Everyone knows you can add Jessica Simpson as your mobile ring tone, but not all programmers know that you can add new features to their programming world.

So here is an overview to a new world of happiness. It also overviews how the Magic Models work, and introduces a new gem I’m working on – the Magic Wiggly Lines – described as “genius or insane

Turn-based game DSL

Late in the night, whilst the baby feeds, I continue to develop Dr Nic’s Civilizations game. This led me to develop a DSL for the specification of game rules. You need to see this.

Here’s an example definition of some terrain:

  class Desert < Terrain
    title           "Desert"
    letter          "d"
    graphic         "desert"
    movement_cost   1
    defense_bonus   10
    food            0
    shield          1
    trade           0
  end
  class Oasis < Desert
    title           "Oasis"
    special_code    1
    food            3
    shield          1
    trade           0
    graphic         "oasis"
  end

An Oasis is a special version of the Desert terrain, so Ruby subclasses offer a compatible relationship.

This is seriously cool. All in Ruby.

Why write a DSL for something trite like game rules?

The descriptions of terrain (plains, hills, ocean), the improvements (roads, irrigation, mines), etc. all need defining somewhere. Here are the standard options:

  1. Rows in a database, imported into ActiveRecords at startup
  2. Files that are loaded at startup, parsed and converted into an internal data model
  3. Ruby DSL

The sort of configuration we're dealing with is static: its a part of the game design to a certain extent. Yes, I could put the configuration in a database and access/modify it via an admin console, though version control becomes an additional administrative hassle.

Storing game rule configuration in external files makes version control trivial, though there is a one-time cost of implementing special purpose syntax, parsers, internal game data models and testing.

A Ruby DSL is exactly the same as implementing option 2 - the configuration is stored in files, though as the syntax is pure Ruby, there is no need to implement a parser and internal data model.

There was a one time cost for supporting the syntax. But why the lucky stiff paid this cost for me with his traits definition for slaying dragons [1]. So I was free and clear of any actual effort.

"Yes, smart arse, but you still had to write up all those Ruby classes"

An irrelevant argument, you'd have to enter the configuration for any of the 3 options, but...

I wrote a generator to build the Ruby class definitions for me from the Freeciv's GPL rule sets. Sweet.

[1] The definition of the Terrain class, using the traits mechanism, is simple:

  class Terrain
    traits :title,
      :letter,
      :graphic,
      :movement_cost,
      :defense_bonus,
      :food,
      :shield,
      :trade,
      :special_code
  end

Translation of RSS feeds a failure (part 3)

My path to spying on the Japanese wouldn’t be complete without support for translating RSS feeds. The translation needs to be performed on the feed itself before it hits the feedreader (unless you have non-Ajaxy webreaders that you can translate).

Unfortunately, Google Translate seems to ignore RSS altogether, and just redirects you back to the original RSS feed.

For example, take sample feed: http://d.hatena.ne.jp/hanazukin/rss2

Press the Spy on Japanese bookmarklet (cheat: click here for translation) and you’ll see the original RSS feed in the lower frame. Its not that its been poorly translated – Google just redirects you back to the original page instead of translating it.

Two solutions:

  1. Use a HTML -> RSS feed converter and use the direct translation url (use translate_c? instead of translate? in the url)
  2. Google Translate should add RSS feed translation. Really it cannot be more difficult than HTML translation. Add some caching (which google loves to do anyway) so its not translating each time my feed reader polls the feed, and you’re off and running.
  3. NEW – create a translator proxy that accepts RSS feed urls, converts the feed to a primitive HTML format, translate on google, convert results back to RSS feed

I prefer #2. So, if any Googlers read this blog, or if any friends of Larry & Co. are reading this blog, or if anyone remotely interested in popping over to the Googleplex to pass on a message from me, could ask the Google Translate ppl to add RSS feed support, I’d be a very very happy man.

Extending _why’s Creature class

Many Rubist’s first explanation of metaprogramming is by why the lucky stiff (_why)’s Why’s (Poignant) Guide to Ruby, chapter 6, section 3.

You go on a dragon-hunting, adventure game using sexy Ruby syntax (a domain-specific language/DSL for adventure games?). Here is some sample syntax for defining a monster class:

 class Dragon < Creature
   life 1340     # tough scales
   strength 451  # bristling veins
   charisma 1020 # toothy smile
   weapon 939    # fire breath
 end

The life, strength, charisma and weapon class methods are generated by a traits class method called against the Creature class (read the chapter).

class Creature
  traits :life, :strength, :charisma, :weapon
end

Read this chapter section many times and admire the beauty of the idea (and amuse yourself with his writing style!).

But there is one small improvement that could be made: currently, after setting the trait methods (e.g. life 1340 sets the life trait to 1340), you cannot access the class's trait values directly via their original trait method. That is, you cannot call Dragon.life to retrieve the value 1340.

This is due to a limitation of the define_method method being used. The relevant code from _why's book is:

   def self.traits( *arr )
     # 2. Add a new class method to for each trait.
     arr.each do |a|
       metaclass.instance_eval do
         define_method( a ) do |val|
           @traits ||= {}
           @traits[a] = val
         end
       end
     end

The method creator define_method uses a block to define the generated method body. The parameters for the block (val in the example above) become the arguments of the method once its been added to the class. That is, if we call traits :life on our Creature class, then a class method will be generated that requires one argument - the value of the trait. That is, it will generate the following method:

class Creature
  def life(val)
    @traits ||= {}
    @traits[:life] = val
  end
end

Now, back to the problem. How do we support the syntax Dragon.life? To achieve this, the generated method would need to look like:

class Creature
  def life(val = nil)
    @traits ||= {}
    return @traits[:life] if not val
    @traits[:life] = val
  end
end

That is, we need a default value for our method argument. But... blocks don't allow parameters to have default values. We cannot do the following:

         define_method( a ) do |val = nil|
           @traits ||= {}
           return @traits[a] if not val
           @traits[a] = val
         end

A pity, yes.

So, we need to generate our methods differently. The solution is as follows:

      metaclass.class_eval <<-EOS
        def #{a}(val=nil)
          @traits ||= {}
          return @traits[:#{a}] if not val
          @traits[:#{a}] = val
        end
      EOS

The eval methods allow a string to be passed to them. So, we shall pass it a string that defines a new method the old fashioned way: using the def method constructor. Thus it allows us to have default values for our arguments.

QED.

ALTERNATIVE from Chris @ Errtheblog.com

Use the splat! (*) to allow zero or more arguments. Pluck the first one off to represent the incoming argument or nil.

def self.traits( *arr )
  arr.each do |a|
    metaclass.instance_eval do
       define_method( a ) do |*val|
         val = val.first
         @traits ||= {}
         return @traits[a] if not val
         @traits[a] = val
       end
     end
  end
end

There are unanswered questions about its support for 2+ arguments (where we only need support for 0 or 1) that shall remain unanswered for the sake of this simple hack. But feel free to start throwing exceptions around if your users need protection from themselves.

So, cattr_accessor doesn’t work like it should?

Rails’ active_support library adds some wonderful functions into standard Ruby classes. Some we all use day-in-day out are attr_accessor and its class-level equivalent, cattr_accessor.

But cattr_accessor doesn’t work the way you (read, “me”) thought at first glance when you use subclasses. I thought if I declared a class accessor in the superclass, then I would have independent class attributes for all my subclasses. Apparently not…

>> class Parent; cattr_accessor :val; end
=> [:val]
>> class Child1 < Parent; end
=> nil
>> class Child2 < Parent; end
=> nil
>> Child1.val = 4
=> 4
>> Child2.val
=> 4
>> Child2.val = 5
=> 5
>> Child1.val
=> 5

Child1.val and Child2.val seem to be the same value. Not very independent at all. Internally, the classes share a common class attribute. This is useful in certain circumstances, but not what I was looking for.

Instead, I found class_inheritable_accessor.

>> class Parent; class_inheritable_accessor :val; end
=> [:val]
>> class Child1 < Parent; end
=> nil
>> class Child2 < Parent; end
=> nil
>> Child1.val = 4
=> 4
>> Child2.val = 4
=> 4
>> Child2.val = 5
=> 5
>> Child1.val
=> 4

Lovely. Each subclass will have an independent value once you’ve assigned it a value explicitly, else it will pick up the value from its superclass.

UPDATE: class_inheritable_accessor and co. actually clone the superclass’s inherited attributes, rather than just referencing them.