Wednesday, February 27, 2008

'ey! Markio!

Mark-ee-oh. Whenever I say that out loud, I think of some generic movie from the early 90s in which Markio and his “crew” “hang out” and shoot “ball”. It goes something like this…

Friend: “Ey Markio! Lets go shoot some ball!”
Markio: “Alright…”

...Others disagree.

Anyway. _why wrote a couple times about a language called Io. Those posts turned me on to a great language. In his second post he wrote an incredibly small DSL for generating basic HTML. It did that pretty well. Not long after that I started working on a little web framework in Io, deemed “Iota”. Am I not clever? Because, really, what does the world need more than another web framework?

I wrote the first version of the framework. And it works. It has a tiny little ORM and a tiny little session manager and a tiny little html generator, not much different from _why’s. However, the whole thing was rather fragile, and written is a very Ruby-esque style. Ruby-style is just not a very good fit for Io.

So, I took it all apart, am rewriting parts of it, and have decided to release it bit by bit. The first bit is Markio. If you can’t tell, I blatantly poached the name from Markaby (Markup as Ruby). I prefer to think of Markio as… Markaby in Io. Markio.

It comes in at well under 100 lines of code, but handles most of the basic things that a markup tool should handle. Lets look at how to use it…

builder := Markio Env clone
builder build(
  html(
    head( title( >"My Spiffy Web Page" ) )
    body(
      div_header( >"Some header crap" )
      div_content(
        span.spiffy( >"Spiffy span" )
        span.spiffy( >"Another spiffy span" )
        a({"href":"/there"}, >"Link to there" )
      )
    )
  )
)

Its pretty easy to see the similarity to Markaby. There are a few significant differences to the syntax though.

First of all, in Markaby if you want a div with the class “foo” and the id “bar”, you’d type something: div.foo.bar!. The equivalent in Markio is div.foo_bar. Why the divergence? Honestly, I can’t stand the exclamation point thing. Thats really it. I originally intended to use a hash instead of an underscore, however it appears that Io isn’t a fan of that. CamelCase is the way things are done in the Io world, so the use of an underscore shouldn’t be too confusing.

Markio was designed to be easily dropped into a framework or some other such thing. Which means that there needs to be an easy way to drop paramaters into it. (The code is evaluated in the context of the Markio Env. I suppose that could change…) If we were going to do some such, it might look like this…

params := {"id":1234, "flash":"Victory is mine!"}
builder := Markio makeEnv(params)
builder build(
  div.flash( >flash )
  span.your-id( >("Your id is: " .. id) )
)

In short, Markio makeEnv takes a Map and clones Env. It then sets new members on it using the keys and values of the Map passed to it.

The last major difference is that you cannot simply output strings and expect them to be appended to the output. There is an internal string which is built up. So, all literal output needs to be added to it. That is why the “>” operator is used. It simply appends the argument to the stream.

You can grab Markio right here!

A few notes: First, you may have noticed the strange Map literal syntax I was using. This isn’t built into Io. But its dead easy to add it. I love Io. Grab the code for that here. Secondly, there are a couple known bugs with Markio. First of all, it does not make self-closing tags at the moment. There is a TODO in the code to make that work. Secondly, setting attributes is a bit odd. If you make the first argument to the tag a Map, it will work… as long as there is another argument. If you pass just a Map (a({"href":"blah"})) it will fail. The simple workaround at the moment is just to pass a nil as a second argument.

Have fun. Let me know if you love it… or hate it… or hate me.

Friday, December 28, 2007

Metaprogramming MUD Commands

About a year and a half ago I threw together a “mud” in the space of a few days. I use the word mud loosely. Really, I just threw together the parts to make a basic world (editable only by changing the database entries the program used by hand), allow multiple people to log on, and talk. It worked and I was proud of my Ruby skills, as I hadn’t done too much with Ruby outside of the Rails context at that point.

Of course the problem with just throwing the parts in a bin and shaking it is that when you want to add new things, it gets harder and harder as you go. So, I quickly lost interest, as doing it right would have required a complete rewrite.

Fast forward to a few days ago, and I’ve decided to start working on a MUD again… and do it right. I’ve spent the last couple years doing a ton of Ruby work. So, I’m more confident now that I can do it right.

One thing that is now pretty thoroughly etched into my head, that wasn’t a year ago, is metaprogramming with Ruby. I understood how to do it back then… and how to use DSLs in previous years, however, now I have a better grip on just how freaking great it is. (Partially thanks to Paul Graham’s writings convincing me that bottom-up is awesome.)

I worked on the mud code quite a bit on the days I had off for the holidays, and then most everything evening since. So, I’ve got a pretty good base built up. One part of that base is the “command system”.

The command system is what the mud uses to identify and dispatch commands sent by the players. Things like:

say Hey whats up man
eq sword
score
area list
area delete 1
area create New Area
area set 1 room_start=1000 room_end=1100

Theres a few things that are probably immediately noticeable about this list of commands. First, some of these commands should not be usable by the average player. (Allowing ‘area delete’ for normal players would be a disaster.) Secondly, some commands have sub commands.

The naive (and sad) way to handle this would just be nested case statements. Which, of course, would lead to a tremendous amount of boilerplate code and repetition. Not acceptable… and certainly not worthy of a blog post. However, I think I’ve come up with a pretty slick way of handling this situation, and since it involves metaprogramming, which I love, I thought I’d share it. So, first off, what does the code need to do? It needs to…

  1. Record a block of code to be run when the command is dispatched
  2. Exclude the command based upon roles of players
  3. Facilitate nested (or namespaced) commands
  4. Easily create reusable methods (helpers)

So what would code that accomplishes all those goals look like? Well… Heres what I came up with:

module Roles
  extend RoleBuilder

  role(:player) { |c| true }
  role(:immortal) { |c| c.level > 100 }
  role(:admin) { |c| c.level > 105 }
end

class Command < CommandBase
  player(:say) { |input| message_room input }

  namespace(:area) do |area|
    area.admin(:create) do |i|
      # blah blah blah
    end

    area.immortal(:info) do |i|
      # etc etc etc
    end

    area.player(:list) do |i|
      # pretend theres real code here
    end
  end
end

module Helpers
  def current_char
    @client.character
  end
end


Hey! Thats not too bad. So in the previous code we’ve defined three roles (player, immortal, admin) and four commands (say, area create, area info, area list). In the Roles module, we define the different roles… which are then used in the Command class. Those roles are defined as methods in the Roles module. That module is included in CommandBase (which Command inherits from). Those dynamically generated methods, in turn, define methods which are called by a dispatch method (which is outside the scope of this article, but suffice it to say dispatch looks at “area info 1” and says “okay, call the ‘area’ command”... more on this later). The way namespacing works is pretty self explanatory as well. You might guess that the code that lets all this happen is a bit gnarly. Actually, its not bad. Its been through several revisions, the first of which were fairly nasty… but the “final” version is short and pretty simple. This was aided, in part, by Ruby 1.9. (Bonus points if you can point out the 1.9 feature in use in the code below.)

module RoleBuilder
  def role(name, &role_block)
    define_method(name) do |command,&command_block|
      define_method(command) do |input|
        command_block.call(input) if role_block.call(current_char)
      end
    end
  end
end

So, lets start with the RoleBuilder module. Really quite simple, we have defined a method “role” which creates a function, which in turn will create the functions which actually run commands. So in this case, use of the RoleBuilder module might look like this:

module Roles
  extend RoleBuilder

  role(:player) { |c| true }
  role(:admin) { |c| c.level > 100 }
end

In the above example, we end up defining two new methods: player and admin. The “c” being passed into each of the blocks is the logged in character we’re dealing with. That code is outside the scope of this post (and not interesting). So, just pretend that its obvious that a Character object should be passed into those. Then as long as the role block evaluates to true eventually the resulting command will be called (line 5 of the previous code). Moving on…

Alright, it gets a bit more interesting here. If we want to namespace a command (area list, area create, area delete) we use the “namespace” method defined here. Looking back at the code that uses this code (the first code sample) we see that namespace is used like this:

namespace(:area) do |area|
  area.admin(:create) do |i|
    # code to create a new area here
  end

  area.immortal(:info) do |i|
    # code to display area info here
  end
end

class CommandBase
  extend Roles
  extend Namespace
  include Helpers
end

module Namespace
  def namespace(name,&block)
    ns_class = Class.new(CommandBase)
    yield ns_class
    ns = ns_class.new

    define_method(name) do |input|
      m = input.match(/([^\s]+)\s/)
      ns.send(m[1],m.post_match)
    end
  end
end

So what happens is that “namespace” creates a temporary class which inherits from CommandBase, just like Command, as well as a typical command method, like player or admin. When that method gets called (in this case “area”), it then re-dispatches the command, using the next argument. So, its like this…

  1. input: “area delete 1”
  2. dispatch calls “area”
  3. input: “delete 1”
  4. area command calls its own dispatch
  5. dispatch calls “delete”
  6. input: “1”

Pretty neat. Its almost, sorta, recursive. (Except that its not calling the same code, but rather, very similar code)

Theres at least one other good way I can think of to deal with writing a command system. That way involves pattern matching. If I was going to go back and do this again, I might try that, just for my own amusement, but I think the way I just described wins in practicality and “coolness”. And really… whats programming without coolness?