I try to expand my knowledge of Ruby by reading code from other developers I admire. In the past few weeks, that has meant reading the meager source of Camping, Why the Lucky Stiff’s tiny website framework.
It also helped to learn by writing a test framework for Camping. It works pretty well so far and has taught me a lot about Ruby, testing, and Camping. I hope to package it as a gem in the next few weeks.
Last night at the Seattle.rb, Evan Webb answered a few questions about the finer points of Ruby and metaprogramming. (Note: This is not a Camping tutorial.)
Metaprogramming via Search and Replace
I was initially confused by the way Camping controllers are constructed. Your controllers are all in a module and don’t inherit from anything. How does it work?
In camping, you start an application by sending it the namespace of your app:
Camping.goes :Blog
The practical result of this is that your app is mounted at a specific directory, such as http://my_camping_app.com/blog.
Behind the scenes, Camping actually reads its own source file (with the __FILE__ handle) and does a search and replace on all instances of Camping. It then evals the result and runs your app with the modified code!
If you read the source of camping-unabridged.rb, you will see something like this:
module Camping
module Base
def render(m)
# etc.
After calling Camping.goes :Blog you get this (in memory only, not on disk):
module Blog
module Base
def render(m)
# etc.
As an interesting side effect, this technique also performs the replacement on the comments in the source file. As an exercise for the reader, you could add a method to Camping that generates rdoc documentation based on the newly modified comments.
Honestly, if I were clever enough to think of this technique, I would immediately dismiss it as a hack. But if it’s good enough for why…
(I seem to remember that Perl has a preprocessor hook that makes this possible there as well.)
Class, or not a Class?
Another thing Camping does is this:
class Page < R '/(w+)'
def get(page_name)
# ...
What’s happening here?
Practically, we’re defining a controller and matching it up with a regular expression. If a URL matches that regular expression, it will be passed as an argument to the get or post method defined by that controller.
Technically, it gets a little weird. To start with, the left side sets up a new class called Page that inherits from…something. The right side turns out to be a method named R that takes a string as a parameter. The R method actually creates a new class and returns it back as the value for the right side.
So Page inherits from a dynamically generated class, based on a regular expression.
To get even more weird, Camping defines a class named R and also defines a method named R. Ruby is smart enough to tell that the method R is being called, not the class R.
Wha?
I honestly have no idea how I would use these concepts in a production application. But, it does show how dynamic the Ruby language is.
Now you know! Let’s go Camping!


In Perl, you've got the DATA filehandle, that you can (ab)use to read in the contents of the source file that's currently executing. You can probably do the same thing with it...
Continuing the Camping discussion, I'm wondering why method_missing is used again and again in related modules (like Markaby) to implement the HTML etc. entities repeatedly.
From a neophyte's perspective, I can understand this happening the very first time, but to call method missing a hundred times if the B tag appears a hundred times seems a violation of the DRY principle.
(Is my understanding correct?)
Is there a way to keep the dynamic nature of Markaby/Builder/Camping, but reduce the subsequent calls to method_missing ??
As others have noted elsewhere (Atrus for example) Markaby seems to take a sizable portion of the time making the method_missing calls..
I think the dynamic nature of Camping is brilliant, but I'm just wondering if there are ways to optimize the bits which seem to be resource/time hogs?
the DATA filehandle is available in ruby too, just stick __END__ in your file, but I don't think Geoffrey was thinking of that..
Anyway, I for one found the gsub+eval a dirty hack but quite.. _whyish..
Subclassing a runtime generated class on the other hand is quite useful, especially if you think of the "standard" class builders like Struct, DelegateClass and the likes.