Seven Languages in Seven Weeks: Day 2

A grep work-a-like which despite being much shorter then the previous exercise actually has the possibility of being useful:

pattern = Regexp.new(ARGV[0])
path = ARGV[1]

File.open(path, "r").each_with_index do |line, i|
    puts "#{path}:#{i + 1}\t#{line}" if line.index(pattern)
end

Seven Languages in Seven Weeks: Day 1

I picked up Seven Languages in Seven Weeks after Martin Fowler recommended it. After half an hour of reading I wrote one whole Ruby program!

i = rand(10)

puts "I'm thinking of a number x ∈ [0,9]. Can you guess it?"

while true do
    print ">> "
    guess = gets.to_i

    break if guess == i
    puts "Too low." if guess < i
    puts "Too high." if guess > i
end
puts "Good job :-)"

(I may have known a little bit of Ruby before I started ;-).

Eventlet is Awesome!

Eventlet is an asynchronous networking library for Python which uses coroutines to allow you to write non-blocking code without needing to perform the normal mental gymnastics that usually go along with asynchronous programming. There are a bunch of async/event-driven networking framworks (eventmachine, node.js, tornado, and twisted to name a few), and their defining characteristic is that they use select/epolll/kqueue/etc. to do I/O asynchronously. Asynchronous I/O is cool because when done correctly it allows your server to handle a much greater number of concurrent clients than the one-OS-thread-per-connection approach. Furthermore, since you're using co-operative rather than preemptive multitasking, you can safely update shared data structures without locks, which makes it a lot easier to write correct code.

Anyway, eventlet is pretty cool. If you like Python and you're interested in async programming, you should check it out. After all, anything that reduces the incidence of Heisenbugs is worth a look ;)

Proxymachine in 55 Lines

Since they're so specialized, playing with this kind of library requires a specific kind of project. Consequently, I decided to put together a "Hello Word!" version of Proxymachine. Proxymachine is a proxy built on eventmachine that lets you configure it's routing logic using Ruby. Don't get me wrong, Proxymachine is awesome and way more production ready than this. That being said, it's still friggin' cool that I could put togther a pale imitation of Proxymachine in less than 100 lines thanks to eventlet:

import functools
import eventlet

CHUNK_SIZE = 32384

class Router(object):
    def route(self, addr, data):
        raise NotImplemented

def merge(*dicts):
    result = {}
    for d in dicts:
        result.update(d)
    return result

def forward(source, dest):
    while True:
        d = source.recv(CHUNK_SIZE)
        if d == '':
            break
        dest.sendall(d)

def route(router, client, addr):
    blocks = []
    while True:
        block = client.recv(CHUNK_SIZE)
        if block == '':
            raise Exception('Failed to route request: "{0}"'.format("".join(blocks)))

        blocks.append(block)

        route = router.route(addr, "".join(blocks))
        if route is not None:
            print "Forwarded connection from {0} to {1}".format(addr, route)

            server = eventlet.connect(route)
            for block in blocks:
                server.sendall(block)

            eventlet.spawn_n(forward, server, client)
            forward(client, server)
            return


def start(router, **kwargs):
    defaults = {
        "listen": ('localhost', 8080),
    }

    config = merge(defaults, kwargs)
    print "Listening on:", config['listen']

    eventlet.serve(
        eventlet.listen(config['listen']),
            functools.partial(route, router())
    )

Roulette: A router in ten lines

The logic here is laughable, route inbound connections to either localhost:9998 or localhost:9999 depending on whether the remote port is divisible by two, but the point is that the routing logic could be anything. We're writing Python here. We could look stuff up in a database, or check the phase of the moon or y'know, do something useful.

import roulette

class Router(roulette.Router):
    def route(self, addr, data):
        if addr[1] % 2 == 0:
            return ("localhost", 9999)
        return ("localhost", 9998)

roulette.start(Router,
    listen = ("localhost", 80)
)

Scala Templates

I've recently been messing around with Scala and figured I'd tackle a problem I've already solved in other languages. Here is the template kata implemented using a regex to match the placeholders:

class Template(val tmpl: String) {
    def render(context: Map[String, Object]): String = {
        return """\$(\w+)""".r.replaceAllIn(tmpl, m => {
            context.getOrElse(m.group(1), "").toString()
        })
    }
}

Here's a couple things I like:

  • Scala's Maps have a getOrElse method, yay! It's a little more verbose than Python's get method, but at least I don't have to write it my self.
  • It's actually shorter than my first implementation in JavaScript, including the type annotations.
  • I actually really like the class parameter, implicit field, thing (val tmpl: String). It handles the "The constructor takes and argument and binds it to a field." use-case very nicely.
  • Scala's lambda expressions are very concise, and it's hard not to like that.

Here are the tests written in a BDD style using ScalaTest:

import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers

class TemplateSpec extends FlatSpec with ShouldMatchers {
    val EMPTY = Map[String, Object]()

    private def test(tmpl: String, expected: String, ctx: Map[String, Object] = EMPTY) {
        new Template(tmpl).render(ctx) should equal (expected)
    }

    "A template with no tokens" should "return a blank string if the given string is blank" in {
        test("", "")
    }

    it should "return a non-blank string unchanged" in {
        val identity = "hello, world"
        test(identity, identity)
    }

    "A template with one token" should "replace missing values with the empty string" in {
        test("$name", "")
    }

    it should "replace the token with value from the context" in {
        test("$name", "Aaron", Map("name" -> "Aaron"))
    }

    it should "leave the rest of the string unchanged" in {
        test("Hello $name!", "Hello Aaron!", Map("name" -> "Aaron"))
    }

    "A template with two tokens" should "replace each token its value from the context" in {
        test("Hello $name! It is a $attitude day.", "Hello Aaron! It is a wonderful day.", Map(
            "name" -> "Aaron", "attitude" -> "wonderful"
        ))
    }

    it should "replace each missing value with the empty string" in {
        test("Hello $name! It is a $attitude day.", "Hello Aaron! It is a  day.", Map(
            "name" -> "Aaron"
        ))
    }

    it should "replace adjacent tokens with their respective values" in {
        test("$me$you", "AaronJohn", Map("me" ->"Aaron", "you" -> "John"))
    }

    it should "replace every occurrance of each placeholder" in {
        test("$a-$b-$a-$b", "AAA--AAA-", Map("a" -> "AAA"))
    }
}

They're a little verbose, but as I mentioned in a previous post, you can extract documentation from tests like these:

TemplateSpec:
A template with no tokens
- should return a blank string if the given string is blank
- should return a non-blank string unchanged

A template with one token
- should replace missing values with the empty string
- should replace the token with value from the context
- should leave the rest of the string unchanged

A template with two tokens
- should replace each token its value from the context
- should replace each missing value with the empty string
- should replace adjacent tokens with their respective values
- should replace every occurrance of each placeholder

JavaScript Templates (redux)

In the spirit of code kata (the origional problem was a kata) here is an alternative solution to the JavaScript templating problem:

var template = (function(){
    var grammar = parsec.grammar([
        parsec.token("placeholder", "[$]\\w+"),
        parsec.token("literal", "[^$]+")
    ]);

    with(grammar){
        root("template", placeholder.or(literal).repeated().then(eof));
    }

    return {
        render: function render(source, context){
            var handlers = {
                literal: function(node){
                    return node.v;
                },

                placeholder: function(node){
                    var key = node.v.substring(1);
                    return context[key] || "";
                },

                template: function(node){
                    return $.map(node.children, evaluate).join("");
                }
            };

            function evaluate(node){
                return handlers[node.type](node);
            }

            var root = grammar.parse(source);
            return evaluate(root);
        }
    };
})(parsec.util);

It's a bunch longer. It's not quite ten time's longer, but it's kind of close... and it passes exactly the same test suite and has the same features, so what, if anything is good about it?

For one thing, it uses a completely different approach. It builds a recursive descent parser using a parsing library that I put together that's based on Parsec. The basic idea of Parsec, and other parser combinator libraries, is that you define simple parsers and then compose them into more complicated parsers using combinators. What this means is that, while it is not necessary here, this approach can handle much more complicated languages than the regex based approach.

Oh, and there was one little nugget of joy in there that deserves special mention. You, might have missed it, so I'll show it again:

with(grammar){
    root("template", placeholder.or(literal).repeated().then(eof));
}

That's right, a with statement. I bet you never though you would see somebody actually try to use one of those. Sure, with statements are widely reviled, they are not one of the good parts, and they're dead in EC5 strict mode... but embedded DSLs are one of the places where you're allowed to cheat like there's not tomorrow. 'sides, $.extend(window, grammar); is your other option for scope manipulation and that's just plain evil.