Feedburner-izing typo in Fifteen Minutes

I finally got around to signing up with FeedBurner for the main Atom feed for mult.ifario.us; the new preferred feed is http://feeds.feedburner.com/Multifarious, but I'm going to hold off on 301ing aggregators for the time being. It was easier than I thought to get the FeedBurner feed and some auto-subscribe chicklets displayed in typo, thanks to a well-designed sidebar API. In fact, it took all of 15 minutes to read the README on sidebar plugins, fire up a text editor, write the 16 lines of ruby and 46 lines of rhtml for the sidebar, fix some typos, bounce lighttpd, et voilá...

On the downside, the code for autodiscovery is not cleanly modularized in typo, so I stopped at the sidebar. A more thorough integration would require a more thorough refactor (or at least a hack to hard-code the FeedBurner URL), as the autodiscovery feed code is spread between a superclass and a base class in the controllers. (Yuck.)

Rails Presentations from ApacheCon ‘05

Copies of my Rails presentation from ApacheCon US '05 are up (finally). My apologies for having the wrong one on the CD from the conference. I think the CD makes up for it though by including a presentation on Drools under the Rails talk demonstration, along with the older version (and probably more useful to a non-live audience) of the Rails slides.

This was a fun, though somewhat scary, talk to give. It was fun because the subject is dear to my heart (I do believe that Ruby (and Rails)) is better for a lot (not all) of the development being done right now). It was scary because Rails is such a hot topic that it lead to a really big audience and probably high expectations. Then Craig and Craig started asking questions. Craig and Craig being rather eminent people in their respective fields, those fields being object/relational mapping and web frameworks respectively (rather relevant to Rails).

Feedback aftward indicated that the presentation was useful and enjoyable though. Phew!

If I can figure out a way to post the presentation complete with the undead (not live, but it looks live) code I will. Right now that would mean posting a 70 meg keynote file though and I don't want to inflict that on my bandwidth consumption =(

ps: an interesting conversation occured right before the talk. It was interesting for two reasons. First, the guy I was talking to (at ApacheCon) responds to an @microsoft.com email address. Second, he was looking for someone to give a talk about Rails (not for Microsoft directly, though).

Ruby’s executable class definitions

If you're coming to Ruby from Java, remember that class definitions are executable code. I can think of at least one situation where this has practical implications and if you're not clear about it you'll spend a couple of hours banging your head against the wall.

Consider this simple Java class:

class A
{
  int a = 10;

  public int getA()
  {
    return a;
  }
}


If you create an object of type A and call it's method getA(), you'll get 10. No surprises here.

Now, knowing that member variables in Ruby are prefixed with @ we go ahead and translate our little java class to Ruby like so:


class A
  @a = 10

  def a
    return @a
  end
end


We then create an object of class A and call its a() method:

irb(main):001:0> class A
irb(main):002:1> @a = 10
irb(main):003:1> def a
irb(main):004:2> return @a
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> A.new.a
=> nil


"WTF!!" you say. Well, if you remembered what I said, class definitions are executable code. In our case, this means that the @a = 10 is executed when the class is defined, not when an instance of the class is created. As a result, this statement defines a member variable of the instance of class Class that represents class A.

To prove the last statment is true, let's make a() a class method:

class A
  @a = 10

  def A.b
    return @a
  end
end


When we call it we get what we expected:

irb(main):001:0> class A
irb(main):002:1> @a = 10
irb(main):003:1> def A.a
irb(main):004:2> return @a
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> A.a
=> 10

ANTLR for Ruby

The latest release of ANTLR v3 adds support for Ruby as a target language, courtesy of yours truly. Let's look at an example to see what's possible.

The following grammar generates a parser that can evaluate simple arithmetic expressions involving integer numbers, +, -, *, /, ( and ).


grammar Calculator;
options {
  language = Ruby;
}

@members {
  @stack = []

  def result
    @stack[0]
  end
}

parse: expression;

expression: mult (
    '+' mult {
      @stack.push(@stack.pop + @stack.pop)
    }
  | '-' mult {
      a = @stack.pop
      b = @stack.pop
      @stack.push(b - a)
    }
  )* ;

mult: atom (
    '*' atom {
      @stack.push(@stack.pop * @stack.pop)
    }
  | '/' atom {
      a = @stack.pop
      b = @stack.pop
      @stack.push(b / a.to_f)
    }
  )* ;


atom: n=NUMBER { @stack.push($n.text.to_i) }
  | '(' expression ')';

NUMBER: ('0'..'9')+;

WS: (' ' | '\n' | '\t')+ { channel = 99 };


First, we need to process the grammar with ANTLR. You'll need to have Java installed in order to do this. ANTLR will generate two files: Calculator.rb and CalculatorLexer.rb.

You'll also need the ANTLR ruby runtime library (just a small .rb file) which you can get here. Just drop into the same directory as the two .rb files created above (in the future I may package it as a gem).

We got our parser, so let's try it out. Create another ruby file with the following:


require 'Calculator'
require 'CalculatorLexer'

lexer = CalculatorLexer.new(ANTLR::CharStream.new(STDIN))
parser = Calculator.new(ANTLR::TokenStream.new(lexer))
parser.parse

puts parser.result


Now, run the program, type an expression and press CTRL-D (or CTRL-Z if in Windows) to see the results, like so:


(5 + 4) * (10 - 2) / 3
^Z
24.0


Simple... but cool, eh?

ANTLR v3 is still in pre-alpha stage, and the Ruby backend is a work in progress. So far, it can handle syntactic and semantic predicates, token labels, etc. and I'm planning to add support for scopes and token/rule parameters and return values next. AST creation will come after that.

WordPress to Typo Migration, Part II

The initial migration (and a subsequent upgrade from 2.6.0 to svn trunk) was pretty much painless, but the database migration didn't take care of mapping permalinks or date queries from the WordPress scheme to the typo scheme. Enter a little mod_rewrite and Ruby (at which I'm a newbie).

The first step is to grab the query string on the old server, e.g., to grab the WordPress-style permalink:

http://oldblog/?p=69

to a new entry point in typo like:

http://newblog/wp/69

the required bit of mod_rewrite script is:

RewriteCond %{QUERY_STRING} p=([^&;]+)
RewriteRule ^/$ http://newblog/wp/%1? [R=301,L]

(The trailing ? drops the query string in the redirected URL, and back references use the % in place of the $.) The 301 response code is "moved permanently", so well-behaved clients should get the idea. The same technique applies to the query string-defined syndication protocols that WordPress uses for the RSS and Atom feeds.

The next bit of work in Ruby is a bit painful because of the way that the database migration script maps the IDs. (If I was in the mood, I could have modified the migration script to dump an id cross-references, but I wasn't and didn't.) The first piece is a new route in config/routes.rb:

map.connect 'wp/:wpid',
  :controller => 'articles', :action => 'wp'

And then a bit of Ruby in app/controllers/articles_controller.rb:

def wp
  begin
    wpid = params[:wpid].to_i
    case wpid
      when (109..109) then wpid = 97
      when (100..101) then wpid -= 15
      # etc.
    end
    # imitate the "read" method here...
  end

And that should do it. (So far, the most difficult part of Ruby is not typing a ; at the end of a line...)

The Problem With Ruby

I've been writing a lot of Ruby code lately, and I think that my friend Ryan best expressed the biggest problem that I've seen with it so far:

The problem is that I get something done in two lines of code that I normally get done in about 10 in another language, but I always seem to sit there thinking that there must be a way to do it in one

Not the worst problem in the world to have.