Roblog

home about me text processing with ruby

Decoding "Almost Sinatra"

13 December 2013

Science! true daughter of Old Time thou art! Who alterest all things with thy peering eyes. Why preyest thou thus upon the poet's heart, Vulture, whose wings are dull realities? — Edgar Allen Poe

In 2010 Konstantin Haase, the lead developer of Sinatra, set about writing as minimal a version of the web framework as he could, using all the obfuscation and minification tricks in the Ruby book. The result was Almost Sinatra, a Sinatra-compatible framework in a frankly microscopic 999B of code, spread across a mere 7 lines.

It’s an impressive feat; the full code for Almost Sinatra is below:

%w.rack tilt date INT TERM..map{|l|trap(l){$r.stop}rescue require l};$u=Date;$z=($u.new.year + 145).abs;puts "== Almost Sinatra/No Version has taken the stage on #$z for development with backup from Webrick"
$n=Module.new{extend Rack;a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m
%w[get post put delete].map{|m|D.(m){|u,&b|a.map(u){run->(e){[200,{"Content-Type"=>"text/html"},[a.instance_eval(&b)]]}}}}
Tilt.mappings.map{|k,v|D.(k){|n,*o|$t||=(h=$u._jisx0301("hash, please");File.read(caller[0][/^[^:]+/]).scan(S){|a,b|h[a]=b};h);v[0].new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})}}
%w[set enable disable configure helpers use register].map{|m|D.(m){|*_,&b|b.try :[]}};END{Rack::Handler.get("webrick").run(a,Port:$z){|s|$r=s}}
%w[params session].map{|m|D.(m){q.send m}};a.use Rack::Session::Cookie;a.use Rack::Lock;D.(:before){|&b|a.use Rack::Config,&b};before{|e|q=Rack::Request.new e;q.params.dup.map{|k,v|params[k.to_sym]=v}}}

Always keen to explore the darker arts of languages that I program in, I thought I’d break Almost Sinatra down, statement-by-statement, and figure out the tricks Konstantin has used. I hope by doing so I’m not somehow “unweaving the rainbow”, destroying the beauty in what is basically the coding equivalent of symbolist poetry, abstruse and thick with meaning; I hope, in other words, that it’s possible to have respect for a work of art while still inspecting its brush-strokes.

Setting up

Without further ado, let’s begin at the beginning with the first statement in the script.

%w.rack tilt date INT TERM..map{|l|trap(l){$r.stop}rescue require l};

There’s already some syntax here that might be unfamiliar to the Ruby beginner; so let’s reformat it to perhaps make it slightly clearer:

%w{rack tilt date INT TERM}.map do |l|
  trap(l) do
    $r.stop
  end rescue require l
end

The first space-saving tip is a basic one: you can use the %w literal to create arrays of strings easily. So this:

["rack", "tilt", "date", "INT", "TERM"]

…can be written as:

%w{rack tilt date INT TERM}

You can use any character you like for the delimiters; Konstantin has used full-stops (.), but it’s probably more common in the wild to see some variety of brackets ([], (), {}, and so on).

The other technique on display here is the reuse of loops. Two things need to happen at the start of the script: first, Almost Sinatra’s modest dependencies need to be required; and secondly, AS needs to tell Ruby that it’s interested in trapping signals sent to the Ruby process. In this case, the signals are SIGINT (the signal sent when you hit Ctrl + C in the terminal) and SIGTERM (the signal sent when the process is killed).

This could be done in two steps: loop over the dependencies, requiring each one; then loop over the signals to be trapped, passing each of them to trap. But when space is at a premium, two loops are a luxury.

So we can see the technique: the script is passing "rack", "tilt", and "date" to trap, which aren’t valid signals. What happens if we pass something to trap that isn’t a valid signal? We can see from a REPL session:

[1] pry(main)> trap("rack") {}
ArgumentError: unsupported signal SIGrack
from (pry):1:in `trap'

The first three elements in the array, then, will trigger an ArgumentError. rescue, when called as the modifier to a line line this, will recover all exceptions; so, instead of trapping a signal, the script will drop through to the rescue call require with the array element instead. Bingo: one loop, two purposes.

The actual handler in trap stops the server cleanly. We’ll find out more about $r later, when it’s actually created: there’s a long way to go first.


$u=Date;

An even simpler statement for number two, but an important concept: here, a constant that’s used multiple times in the script is assigned to a one-letter variable. Although this seems like it might be a waste, the characters are made up if it’s even used once or twice more — or even quicker, if the constant has a particularly long name.


$z=($u.new.year + 145).abs;

This is a curious technique. $z is used as the port that Sinatra will run on; in this case, Date.new.year is called (which always returns -4712, the default value for years). 145 is added, taking us to -4567; finally, the sign is ignored by calling abs, leaving us with 4567 — the default Sinatra port.

This of course is identically and always equivalent to:

$z=4567

It seems that this shorter option was rejected, tongue-in-cheek, to discourage the use of “magic numbers”; it seems that sometimes even the principles of Golf can play second fiddle to the church of DRY.


puts "== Almost Sinatra/No Version has taken the stage on #$z for development with backup from Webrick"

A simple puts statement, this does contain one neat trick: if you’re accessing a sigiled variable (that is, a global that begins $ or an instance variable that beings @), you don’t need the curly braces in the interpolation operator. So the commonplace:

@foo = "bar"
$foo = "bar"
puts "@foo is #{@foo} and $foo is #{$foo}"

can be expressed as:

@foo = "bar"
$foo = "bar"
puts "@foo is #@foo and $foo is #$foo"

Readability takes a hit, but a vital two characters are saved.

The main logic

$n=Module.new

The first line, which was mostly setup, is over; now we’re into the bulk of the logic. A new module is defined; this module will contain the Sinatra-related functionality.


extend Rack;

Rack is mixed into the new module; this allows Rack methods to be used without namespacing them. AS will go onto use Rack::Builder to create the app, which offers with its map method a space-efficient way to do Sinatra’s URL mapping.


a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m

There are quite a few things going on in this patch of multiple assignment, but they’re simple enough individually.

First, a new instance of Rack::Builder is initialised and put into a:

a = Rack::Builder.new

(It might help to think of a as app.)

Then, a shortcut is defined. The script will define a lot of methods dynamically, so D becomes an alias of define_method to save lots of characters later:

D = Object.method(:define_method)

Strictly speaking it’s actually set to the method object of define_method, since Ruby doesn’t allow us to pass around functions as values; but in practical terms it’s similar. After this shortcut is defined, D.call(*) {} is equivalent to define_method(*) {}.

Finally, S is set to the regular expression needed to parse Sinatra’s inline templates:

S = /@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m

Handling requests

%w[get post put delete].map{|m|D.(m){|u,&b|a.map(u){run->(e){[200,{"Content-Type"=>"text/html"},[a.instance_eval(&b)]]}}}}

Here’s where we start to see elements that are recognisably “Sinatra”: this block defines the global methods for defining routes (get "/foo" do and so on). Let’s format it, expand some aliases, and rename some of those one-letter variables, so we can see a bit more clearly what’s going on:

%w{get post put delete}.map do |method|
  define_method(method) do |path, &block|
    a.map(path) do
      run ->(e) { [200, {"Content-Type" => "text/html"}, [a.instance_eval(&block)]] }
    end
  end
end

Things start to look a bit simpler now. For each of the HTTP request methods (using map rather than each, since that saves a character), a global method is defined — each of which takes a path as its first argument and then a block. These methods are defined using the define_method shortcut we saw before; it’s worth noting that, instead of using D.call("method_name"), D.("method_name") is used — which is shorter. You can actually go shorter still, since [] is an alias of call, and use D["method_name"].

When called, this created method uses the map method of Rack::Builder to add a new route into the Rack app that matches the path given. When executed, this handler will set the response status to 200 (“OK”), the content type to HTML, and the response body to the return value of the block (which is executed in the context of the app using instance_eval).

This is, fundamentally, exactly the same way that classic Sinatra apps work.

Parsing templates

Tilt.mappings.map{|k,v|D.(k){|n,*o|$t||=(h=$u._jisx0301("hash, please");File.read(caller[0][/^[^:]+/]).scan(S){|a,b|h[a]=b};h);v[0].new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})}}

The next line concerns itself entirely with templates; it’s what allows you to call, for example, haml :foo and have the template foo.haml rendered as the response body.

Again, this complicated block is much easier to understand if we let it breathe and give the variables some more verbose names. Starting with the first two statements:

Tilt.mappings.map{|k,v|D.(k){|n,*o|$

We can expand them to:

Tilt.mappings.map do |template_type, constant|
  define_method(template_type) do |n, *o|

Tilt is a library that acts as a frontend for many different templating languages; it’s used by regular Sinatra, too.

For each of the template languages that Tilt supports, this code defines a global method (called e.g. erb or haml). This method does the following:

$t||=(h=$u._jisx0301("hash, please");File.read(caller[0][/^[^:]+/]).scan(S){|a,b|h[a]=b};h);v[0].new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})

The first part of this code extracts the inline templates from the current file and builds them up into a hash.

Amusingly, rather than creating an empty hash with {}, as you’d expect, AS calls:

Date._jisx0301("hash, please")

_jisx0301 is a semi-private method of the Date class that expects a string in the JIS X 0301-2002 format — the standard used in Japan. If it’s given one, it returns a hash with the year, month, and day of the given date; but if the input is invalid, it returns an empty hash. So, passing it “hash, please” is possibly the strangest and most brilliant way to get an empty hash in Ruby — another odd joke, to go with the port number, that only makes the final size of the script even more impressive.

Next, AS fetches the inline templates that exist in the script that’s calling Sinatra. It can’t rely on __FILE__ to figure out what this file is, since that refers to the file that AS is being defined in which might be different to the file where the Sinatra methods are actually called. It also can’t use $0, which refers to the script that’s being executed, since that might be different again.

So it uses caller[0] to look at the most recent entry in the call stack, then parses out the filename. It then uses scan with the regex defined above to extract all of the inline templates and build them into the h hash, which is memoised into $t; this memoisation means that parsing will only happen once, rather than every time a template is parsed.

Expanding it and using clearer variable names, we might rewrite this section as follows:

$templates ||= begin
  templates = {}
  File.read(caller[0][/^[^:]+/]).scan(S) do |template_name, template|
    templates[template_name] = template
  end
  templates
end

Next, the template is parsed:

v[0].new(*o){n=="#{n}"?n:$t[n.to_s]}.render(a,o[0].try(:[],:locals)||{})

Tilt allows you to ask for a particular type of template — ”erb”, for example — and get back the class that you need to initialise to parse those templates. In AS’s case, that class is in v[0]; it’s initialised, and a block is passed to it containing the template data. The render method is called, passed the app as its evaluation context, and any locals that have been assigned are passed to it (or an empty hash if there are no locals).

There’s nothing too interesting there.


%w[set enable disable configure helpers use register].map{|m|D.(m){|*_,&b|b.try :[]}};

Here more global methods are defined; we’re used to the D.(m) trick by now. You’ll notice, though, that the methods here are actually all the same, and that none of them do anything other than executing the blocks passed to them; this means that there’s no practical difference between:

configure do
  enable :sessions
end

and:

helpers do
  enable :sessions
end

This is presumably not what Sinatra itself does, but provides enough compatibility with it to get by. But it means you couldn’t do things like configure blocks based on environments in Almost Sinatra, but really — that would be asking a bit much!


END{Rack::Handler.get("webrick").run(a,Port:$z){|s|$r=s}}

An END block is specified, since this is shorter than the perhaps more standard at_exit; this is what actually runs the webserver. We see that $r is set to the running server instance, as we saw in our trap call in the very first line.

A couple of tiny tricks are used here: the second argument to run is a hash, so the braces are omitted (perfectly valid and indeed fairly common) and the Ruby 1.9 hash syntax is used, where Port: $z is equivalent to :Port => $z.


%w[params session].map{|m|D.(m){q.send m}};

The params and session methods are defined; these are passed through to q, which isn’t defined at this point but will later be set to the request object. You might have noticed that, way back up in line two, we saw a lopsided assignment, where four variables were given three values:

a,D,S,q=Rack::Builder.new,Object.method(:define_method),/@@ *([^\n]+)\n(((?!@@)[^\n]*\n)*)/m

This has the effect of setting q to nil, defining it in this scope without actually having to assign a value to it. It allows you to scope a variable in two characters, rather than the minimum of three or four you’d need to assign an actual value to it.


a.use Rack::Session::Cookie;a.use Rack::Lock;

Sinatra includes Rack sessions that used cookies, and runs requests synchronously using Rack::Lock.


D.(:before){|&b|a.use Rack::Config,&b};b

The before method is defined; this delegates the logic to Rack::Builder, calling use to load the middleware.

It then uses this before handler to load middleware of its own:

before{|e|q=Rack::Request.new e;q.params.dup.map{|k,v|params[k.to_sym]=v}}}

…which sets q to the current request, and then converts the keys in the params hash to symbols.

Although this is technically the end of the script, you’ll recall that the server actually runs in an END block; so at this point, if there’s no further script, the END block will trigger and the server will start.


That’s it; our journey through Almost Sinatra is complete. I don’t know about you, but I learned quite a few things. I learned how to use Rack::Builder; that combining loops shortens code dramatically; that method objects can be called with the shortened method.() syntax as well as method.call; that "#$foo" is equivalent to "#{$foo}"; and lots more besides.

But it’s not just about learning little tricks. Almost Sinatra’s GitHub page opens with a quite from _why:

Until programmers stop acting like obfuscation is morally hazardous, they’re not artists, just kids who don’t want their food to touch.

I think that really nails the feeling I get from reading through this code and from deciphering it. Obfuscation is sometimes a virtue: allusion is often more powerful than an outright statement. More than anything, though: it’s really quite fun.