i=rand(10)puts"I'm thinking of a number x ∈ [0,9]. Can you guess it?"whiletruedoprint">> "guess=gets.to_ibreakifguess==iputs"Too low."ifguess<iputs"Too high."ifguess>iendputs"Good job :-)"
(I may have known a little bit of Ruby before I started ;-).
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:
importfunctoolsimporteventletCHUNK_SIZE=32384classRouter(object):defroute(self,addr,data):raiseNotImplementeddefmerge(*dicts):result={}fordindicts:result.update(d)returnresultdefforward(source,dest):whileTrue:d=source.recv(CHUNK_SIZE)ifd=='':breakdest.sendall(d)defroute(router,client,addr):blocks=[]whileTrue:block=client.recv(CHUNK_SIZE)ifblock=='':raiseException('Failed to route request: "{0}"'.format("".join(blocks)))blocks.append(block)route=router.route(addr,"".join(blocks))ifrouteisnotNone:print"Forwarded connection from {0} to {1}".format(addr,route)server=eventlet.connect(route)forblockinblocks:server.sendall(block)eventlet.spawn_n(forward,server,client)forward(client,server)returndefstart(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.
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:
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:
importorg.scalatest.FlatSpecimportorg.scalatest.matchers.ShouldMatchersclassTemplateSpecextendsFlatSpecwithShouldMatchers{valEMPTY=Map[String, Object]()privatedeftest(tmpl:String,expected:String,ctx:Map[String, Object]=EMPTY){newTemplate(tmpl).render(ctx)shouldequal(expected)}"A template with no tokens"should"return a blank string if the given string is blank"in{test("","")}itshould"return a non-blank string unchanged"in{validentity="hello, world"test(identity,identity)}"A template with one token"should"replace missing values with the empty string"in{test("$name","")}itshould"replace the token with value from the context"in{test("$name","Aaron",Map("name"->"Aaron"))}itshould"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"))}itshould"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"))}itshould"replace adjacent tokens with their respective values"in{test("$me$you","AaronJohn",Map("me"->"Aaron","you"->"John"))}itshould"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
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:
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.