Odyssey Through Three Web Frameworks: Rails, Clojure, Django

A while ago, I bought the book Learning PHP, MySQL, and Javascript and messed around with PHP on Apache for websites. It was a pain, for three reasons: I didn’t really know what I was doing (the main reason), cross-browser incompatibility, and PHP is just not a very nice language. It reminds me a lot of Matlab, but specialized for websites instead of matrix calculations: it has lots of shortcuts for web-related stuff, but they’re implemented in weird, hacky ways that make everything seem like a code golf voodoo spell. For example, PHP has built-in global variables that hold request parameters. This makes it quick and easy to handle request parameters, but then there’s just this weird variable coming out of nowhere in all your functions. The standard library is similar: it doesn’t use any kind of module system, so you just have all these functions that pop up from nowhere. It’s like if every single library function in Python was a built-in, like len, and you could just call it from anywhere in your program without referring to anything. Which is fine if you just want to hack together a quick website using a few primitive operations, but think about how many libraries Python has: you’d have built-ins for obscure libraries like ctypes and curses, and you wouldn’t be able to name your own functions simple things like raw or wrapper because those names are already taken by curses, unless we had some awful system like naming those functions __curses_raw____ (oh, wait…) By the time I was using it, PHP had grown well beyond a simple web hack and had over 500 built-in functions, with long, unintuitive names like mysql_real_escape_string and no consistent style standards, so I constantly had to look up whether it was query_mysql or query_my_sql or queryMySQL or…

(I actually like Javascript, even though I acknowledge it also has some guilt in the unpleasantness of this kind of web development. This was also before the answer to every problem was “use JQuery”. The book introduces YUI, which isn’t bad; it’s not quite as nice as JQuery, although I like how its last name could be Hirasawa. But YUI didn’t help, because I had no idea what I was doing.)

But, like I said, the main problem wasn’t PHP; it was that I had no idea what I was doing. So last year, I decided to test if my schooling had taught me anything by trying out web programming again. Since I was pretty sure PHP hadn’t gotten any better, I decided to try out a framework, but as a consummate language dabbler, I couldn’t stick with just one, so I ended up trying out three of them, and even though I ended up sort of picking one, I’ll probably change my mind three or four times.

Below, I’ll go over my experience with each framework, which is totally biased and not at all objective, and discuss some of the facets of my experience that influenced my opinion of the framework.

Ruby on Rails: The Web Framework of Heroes

The first framework I tried was Ruby on Rails. For some reason I keep on coming back to Ruby, even though I never end up seriously learning it because it feels so redundant when I know Python. Steve Yegge loved Ruby for a while, and he wrote all about how great Rails was and how easy it made getting started, and lots of other people and companies were using it, including companies I might want to work at someday like Scribd, so I decided to give Rails a try first.

I followed Michael Hartl’s Rails tutorial, which is an impressive achievement in itself and seems to be the most commonly used tutorial for people starting out with Rails. One of the nice things about learning Rails, especially with Hartl’s tutorial, is that it had so many people trying it in recent history that almost every beginner problem has been discussed on Stack Overflow. In fact, half the problems I had while following the tutorial had already been exactly answered for the benefit of other people following Hartl’s tutorial. Eventually, I could just Google “Rails tutorial Hartl Chapter 7.3”, and it would turn out that whatever I’d done wrong in that section was something lots of other people had also done wrong, or something that had changed in a newer release of Rails or one of the other components. So that part was pretty nice.

But obviously, I wouldn’t have tried the other two frameworks if Rails had been exactly what I was looking for.

Installation

Installing Rails was an unmitigated nightmare. It was absolutely one of the most frustrating installation procedures I’ve ever gone through.

Hartl’s tutorial very patiently explains how to install everything. Hartl does his best to make it easy for you, and I think with any other software on the planet, Hartl would have succeeded at making it easy enough that someone in a coma can do it, because he just is that good at explaining things. But Rails’s installation is so vile, so evil, so pernicious, that not even Hartl could actually make it pain-free.

Since I was working on Windows 8, I started by following Hartl’s advice to use RailsInstaller. Unfortunately, it turns out that RailsInstaller uses an outdated version of Rails that doesn’t work with newer versions of Ruby. I had just finished (stupidly) compiling Ruby 2.1 from source, but it wouldn’t work with the version that Rails Installer included, so I had to go get Ruby 1.9 from somewhere and install it. After that, Rails would start and generate code, but it wouldn’t run anything because there was a problem with the Javascript runtime not working on Windows. I looked up this problem and found that you have to change some obscure setting in some obscure config file buried somewhere deep inside Rails to make it work, or you can install Node.js, which I didn’t want to do, but I couldn’t figure out where the file you needed to edit was because the Stack Overflow answer forgot to mention that. I was going to just install Node.js, but then I read somewhere else that the version of Rails installed by RailsInstaller had some pretty serious security holes and shouldn’t be used. For learning, it wouldn’t have mattered that much, except that I didn’t want to learn something that was already obsolete. Plus, Hartl’s tutorial was using a newer version, and I knew there was going to be trouble if I didn’t have the same version.

I ended up installing a Lubuntu virtual machine on VMWarePlayer and running Rails from that, because getting it running on Windows was such a nightmare. I tried using RVM, but it didn’t work. By this point I’d already spent almost six hours trying to get Rails installed, so I gave up on RVM and used the version from the Ubuntu repositories, which was also outdated, although not as severely as the RailsInstaller version. It still only worked with Ruby 1.9. By the time I got my first “Hello, world” app running, I’d spent around seven hours getting Rails up and running.

The Languages of Magic

Rails felt like magic to me, and I mean that in the worst possible sense. I’m sure it’s wonderful for people like Steve Yegge who spent years building all this crap it provides (ORM, HTML templates, unit testing, routing, the MVC architecture) and stitching it together. But since I don’t really know what I’m doing, I like to be able to see the wiring. I don’t like things to magically apparate in global variables, or for my methods to conjure up libraries based on a complex ritual of gobbledegook, or suddenly gain the ability to hurl lightning bolts because I inherited from ActiveRecord::ComfySandals, and I felt like Rails was doing too much of that.

Rails also made the whole language overflow of web development even more complicated than it already was, at least as it was presented in Hartl’s tutorial. You have Ruby, the implementation language. You have HTML, the markup. You have CSS, the stylesheet language. You have Javascript, which Rails does its best to hide from you, although it must use Javascript because you have to install a Javascript runtime to use Rails. Then you have RSpec and Capybara, the unit testing language. You have Bootstrap and SASS, the extended styling language. You have ActiveRecord, which is a weird sort of domain specific language built on top of Ruby (as is RSpec, actually). There’s just too many of them. It’s so confusing that I don’t even know if I used the right names for these things.

Individually, I have no problem with any of these languages. RSpec is actually really cool, because it makes your tests read almost like an English description of what the test should do and see. It lets the tests act like a human-readable, machine-verifiable formal specification of behavior. On the other hand, I consistently had problems with the FactoryGirl gem for mock objects. I would have rather had some way to write a mock object factory in pure Ruby and call it from RSpec, instead of yet another library. SASS is a nice set of extensions to CSS that maybe should have been considered for inclusion in CSS3. I like Javascript, like I said. HTML and CSS are what they are. Ruby is a very nice language; although some parts of it aren’t totally to my taste, it pretty much does everything right and was a good choice as the backbone for Rails. But there’s just so many of them, it gets overwhelming. And they don’t always play nice with each other or with the Rails framework itself.

By the way, I completely underestimated how much of a pain it would be to learn Ruby and Rails at the same time. I thought it would be pretty simple since Ruby is so much like Python, and was purposely designed not to be too surprising. I already knew a little Ruby from doing Seven Languages in Seven Weeks, but it was nowhere near enough. My lack of knowledge of Ruby contributed to that feeling of mysticism as I was using Rails; I was never sure what was Ruby and what was Rails and what was something else. If you don’t know Ruby, I would recommend working through at least one book on Ruby, and probably a few Project Euler or Elements of Programming Interviews problems, before you ever look at Rails.

Conclusion

I made it much of the way through Hartl’s tutorial, to Chapter 10. It took me over a month, even though other people were saying they finished it in two or three days. The problems I mentioned, of language and library overflow and magical incantation-oriented programming, bothered me the whole way through.

The reason I call Ruby on Rails “the web framework of heroes” is that it seems like a design that appeals to battle-scarred veterans of past web programming wars, like Steve Yegge. These people, after they die, will have their spirits enshrined in the Throne of Programmers outside of time, and return in the year 2199 to argue on FutuReddit over whether to program the Matrix in Lisp or Haskell. As heroes, they don’t have problems with installations, and most of them don’t care about Windows anyway. They struggled for years writing ORMs and template systems and routing in PHP, or Java, or Perl with CGI, and achieved legendary status, and now they’d like to use Ruby on Rails, because it does all that for them. They don’t get that feeling I get of invoking magical forces far beyond my understanding, because they’ve already found the Source of All Magic on the web.

Also, not knowing Ruby made it much harder for me to use Rails than I thought it would. That partially motivated my next two choices.

Clojure, Ring, Compojure, Enlive: Unlimited Dependency Works

Just as I was about to give up on web programming again, I downloaded the Clojure Cookbook from GitHub. It had a section on using Ring and Compojure. I wasn’t convinced until I saw Adam Bard’s tutorial on building a Clojure web app in fifteen minutes.

Clojure is probably my favorite programming language right now. Ruby (when you know it) and Python are like Excalibur: you pick them up, and you can shoot a big beam that blows things up. Very effective, and once you have it, you have it. Clojure is like Ionioi Hetairoi: you have to work your whole life to accumulate all those loyal companions (absorb all those complex concepts like lazy sequences, STM, macros, and functional and concurrent programming in general), but once you have them, you can get things done really quickly and easily. They can just cut through a whole tribe of assassins for you. Additionally, a well-turned Clojure program is a thing of beauty, and writing one feels like a ballet.

So I did Adam Bard’s tutorial, except when it came to the part with Moustache, I just used Enlive, because I already know it from my document preparer. I really did have something working and communicating on a local socket within fifteen minutes, not counting the time it took to download 19 megabytes of dependencies. Within about two hours I already had something that could read from a MySQL database and populate an index page.

Do it yourself

Clojure does have a web framework, Luminus, but it’s really easy to just snap together Ring (a general web and HTTP library), Compojure (a routing library), Enlive (for templates), and JDBC (for databases) into a simple app without any kind of boilerplate generator like what Rails provides. Doing this was fun and very informative; I learned all sorts of things that had never been totally clear to me in Rails, like why the MVC architecture is advantageous, and what kinds of security problems you have to watch out for, and working with the database on a lower level helped me sharpen my SQL. Additionally, a lot of these libraries are built on well-known Java libraries, with the benefits that brings; JDBC is a Clojure wrapper for Java’s JDBC, and Ring uses Jetty.

Because Clojure has mechanisms like macros, higher-order functions, and keywords that let you keep your code size down, my Clojure apps were actually smaller than my similar apps in the other two frameworks. But there were some things that you have to do yourself in Clojure that I really did want someone else to do for me, mainly security.

For my applications, it doesn’t really matter if my site is secure. I was planning to write a simple blog app. At first, I wasn’t going to have users at all; I just wouldn’t have comments, and then I would be the only user. But I didn’t want to have to modify the source code or log in to a remote shell just to add a new post, so I clearly needed some kind of user interface system. Besides, since I’m doing this to learn, I’d be doing myself a disservice if I didn’t learn how to handle users, since it’s one of the most common things that websites do nowadays.

But if you have users, you need to have security. I knew from my PHP days that you don’t store passwords in the database as plain text; you first salt them, then hash them, then store them, so that if someone manages to steal your database, they have to unscramble the hash before they can use your users’ passwords. So I looked up some articles, figuring there would be some kind of library to handle this.

There isn’t. Well, you can use Java interop, along with Java’s java.security.SecureRandom class for generating random salt strings, and Java’s PBKDF2 hash function. It’s kind of a pain, because you have to use arrays (which you can from Clojure, although like Java arrays, they’re basically pariahs from the language and can’t participate in any of the cool stuff you can do with real Clojure sequences). There’s also Friend, a Clojure authentication library, which I was going to use. But it didn’t seem as sleek and cool as Enlive or Compojure; you couldn’t just wrap your Ring handler in some authentication middleware and have Friend do everything for you, at least as far as I could tell. Friend includes bcrypt for hashing passwords, but I couldn’t figure out if it makes salt strings or not, and if so, how to get them so you can store them in your database. I couldn’t even tell if you need salt strings with bcrypt. The documentation is pretty minimal, too. There are some nice examples on the GitHub page that show you how to use Friend for various situations, but I couldn’t tell if they were real examples of how a live site could look, or if they were simplified for pedagogical reasons.

Also, like I said, I don’t really know what I’m doing. I couldn’t figure out whether I needed to set up SSL or not, and how one would go about doing that. I checked what some real sites do and found that most of them use SSL to transmit user credentials, but I couldn’t find any advice on whether this is only necessary if you’re a Google-scale site, or if transmitting your user credentials without using SSL is the equivalent of wiring your friend some money for a plane ticket by entrusting an envelope of cash to random hobos who happen to be jumping on a train towards the town where your friend is. I couldn’t even figure out if SSL became obsolete sometime yesterday.

Since I really wanted to get a site up, and not do a Ph.D. in information security, I decided that Clojure’s web stack wasn’t really for me at this point. Using it was a great learning experience, but I just spent three years in school not finishing any projects, and I really wanted to finish a project. So “do it yourself” was pretty much the reason for both the rise and fall of Clojure.

I could have just gone ahead and written something without any security beyond escaping user input, using prepared SQL statements instead of concatenating strings, and hashing and salting passwords, all of which I had already gotten working. But I do hope this experience can lead to a job someday, and I really don’t want to show up on The Daily WTF, or worse, the nightly news, as the guy whose ignorance of security issues lost thousands of people’s credit card numbers to identity thieves.

Postscript: Enlive is awesome

Enlive is the coolest templating library out there. Other template systems talk about separating content from presentation, but you still have to write weird HTML files full of control codes inside <%= > or {% %}. They still have templating languages with their own versions of for-loops and if-statements that have to be parsed and interpreted by the host language. And you can’t use the same mechanism to send static and dynamic pages; you have to stick static pages in some special folder where the framework can find them, so it won’t confuse them for templates.

Enlive takes separation of content and presentation to a whole new level. There is absolutely not one single programming language-like thing in your templates. They’re just empty HTML. The templates and snippets are completely inside your program. They’re also not some kind of special object; they’re just regular functions, so you can apply them, you can pass them around, you can give them whatever extra arguments you want in whatever form you want (it doesn’t have to be a hash map), and inside a template or snippet, you can use anything the Clojure language has available. There are various ways you can do something like template inheritance; one obvious way is to define major parts of the page (like the navigation bar, footer, log-in form, etc.) as snippets, then use templates to combine them in different ways with other components.

Enlive is also useful for scraping and transforming existing HTML. In fact, with Enlive, you could just steal the HTML source from some page whose layout you like, and replace all the content with your own content. You don’t have to manually delete it all, or write a script to delete it all, or fill the file with control statements; just tell Enlive what to put and where to put it. (Not that I condone that kind of behavior…But, imagine that, in your younger days, you spent hours writing HTML by hand to create a layout, and you wanted to reuse that layout, but it would be a ton of work to go strip out all the content and replace it with ERB or Jinja2 control tags. With Enlive, it’s simple and quick.)

Enlive is awesome, but it’s also evil, because now I find ERB and other template languages extremely painful, even though they really aren’t that bad. When I decided not to use Clojure for my project, I kept thinking how much I would miss Enlive.

Django: Web Frameworks Unchained

With my head spinning from all the confusing information about security that I’d looked up for Clojure, I left the room to recover. I came back and found that at some point, I’d typed “Django tutorial” into my search bar.

I already know and like Python, and so ever since the Rails fiasco I’d been thinking about looking into one of the Python web frameworks. I saw some demos of Flask a while ago and thought it looked nice, but it also seemed like kind of a niche framework, something for which help and jobs would be hard to come by. Plus, it didn’t have Enlive. But at this point, I just had to face facts: no one except Clojure has Enlive, and Clojure doesn’t have security. Enlive is cool and security is boring, but security is necessary and Enlive isn’t.

I tried out the Django tutorial in the official docs, where you build a polling app.

Timezones are better in chains

You can install Django through Pip, Python’s normal package manager. It took about one minute to download and install. It took another few minutes to download and install psyco-pg, the Django backend to PostgreSQL.

Where Ruby on Rails tries to abstract away everything, including Ruby, by building extra layers on top of it, Django embraces its host language. Projects are organized using Python’s package and module system. Rather than having a separate command line app, Django includes a Python script called manage.py in every new project which can be used to start up a server, generate database migrations, and other things done by the rails command for Rails and the lein ring command for Clojure. Django also has a settings.py file where you can configure settings for your project. Rails has similar files, but it has like fifty of them, and they’re scattered all over the project directory. Also, Django does not impose a particular directory structure; you can set up your project however you want, as long as you also set up the settings.py file so Django knows where everything is.

One thing that I find a little bizarre about Django is its fetish for timezones. There’s a section about timezones in the documentation, and a USE_TIMEZONES option in the settings, and you have to set what timezone you want and install an extra library, pytz, dedicated completely to timezones. The Django tutorial, which is not as good as Hartl’s for Rails (though it is pretty good), tells you towards the beginning to set your timezone. I looked up the list of supported timezones and naively set mine to “America/Los_Angeles”, since that’s the timezone I live in.

It turns out that Django handles timezones in some ridiculously complex manner for some reason that relates to users from different timezones using an app. The consensus among the community seems to be to just use UTC, so whenever you deal with times, they’ll be wrong, no matter where you live, but they’ll be wrong in a consistent way, so you can still use relative dates and times.

Object-Orientedness Unchained

In general, Django feels more code-y and bigger than Rails or Clojure. A lot of things that were handled in Rails by yet another DSL, and in Clojure with a macro or function, require making classes or objects in Django. I’ve recently started to think that Python and Java are long-lost cousins, with Python taken in from birth by dynamically-typed monks, and the fact that Django’s code looks so much more like Java than Rails’s does supports this belief.

But because of this, Django does a better job satisfying my desire to see the man behind the curtain than Rails does, while not being as relentlessly do it yourself as Clojure. For instance, your routes are represented in Django as lists of calls to the url function, with a regex to match the actual URL string, a view function that’s called to render a response from that URL, and a name, so that you can refer to your routes independent of their address, allowing you to change URL structure later on if you want. View functions are just functions that return some kind of HTTP response. They can use Django templates to render a page, or they can send back a response with no HTML in it, e.g. for a web service API. In Django, given my familiarity with Python, I feel like I actually understand the code, at least superficially, whereas in Rails I always felt like my code was summoning some kind of spirit that I might not be able to control. For instance, I didn’t see the idea of building a web service API on top of views that return plain HTTP responses in any tutorial; it was a leap I made myself, given my understanding of how URLs and views work in Django. It’s a small leap, but it’s not one I could have made in Rails, because of how little I understood the underlying system.

I can’t decide whether Django’s system of using regexes to match routes in URLs is genius or chintzy. When I first saw it, I thought it was pretty rinky-dink, but after I finished the tutorial and saw how easy it made it to match a URL like “/polls/1/details” to the detail page for the item with ID number 1 in your database, I revised my opinion.

It is helping me a lot that Django uses Python, which I already know, but it is definitely a loser when it comes to unit testing. Instead of RSpec, you get Python’s unittest module, which is based on JUnit. It’s a big, fat, object-oriented library where you have to make a million classes and methods with long, insanely specialized names like test_view_question_model_when_question_was_posted_within_the_last_five_days_in_the_past_not_in_the_future_because_thats_incorrect_by_the_way_this_should_return_True(self). I intend to look into what other testing solutions exist for Django.

The template language is pretty similar to ERB. It’s really just a template library, not an HTML-genetic engineering library like Enlive is. It’s not bad, but Django makes it really easy to switch template languages, so if I can find something in Python that looks more like Enlive, I might switch.

But what about Flask?

I actually like the look of Flask. It’s even more object-oriented than Django, but it’s also a lot simpler, and has some of Clojure’s do-it-yourself nature and less of Django’s magic. I haven’t tried anything in it yet, but I think I just might one of these days.

A Certain Magical Rails, A Certain Scientific Clojure

All of these frameworks had things I loved and things I hated, but I think it’s going to be Django for now. For me, it seemed like the best balance between magic and science. (Rails was too magical, and Clojure was too scientific.)

Really, I wish I could have a framework that combined Django’s straightforwardness, Clojure’s concision and composability, and Rails’s high level of abstraction, but those three features are all directly at odds with one another. Rails’s high level of abstraction makes it not straightforward. Django’s straightforwardness comes partially from being at a lower level of abstraction, where we’re passing around classes and objects inside modules and packages instead of writing in DSLs that all do different things and magically integrate somehow into a complete website, and being at a lower level of abstraction makes Django less concise. Since Rails needs magic to integrate its domain specific languages, it can’t be as composable as Clojure and Django.

Anyway, as I mentioned, this is totally biased and subjective, and this is all the opinion of someone who has no idea what he’s doing, so please keep that in mind when you get angry and write comments. I pretty much trashed and lauded all three for various reasons. If you think I totally missed the beauty of your favorite framework, let’s have a civil conversation about it. After all, I’m still learning, and I’m not set on one framework, so if you have some super compelling reasons why I should give Rails or Clojure another look, I’d like to hear it.

By the way, I never really considered other frameworks like Symfony or CakePHP for PHP, or Java’s Spring MVC or Struts. I don’t really know PHP, and what I saw of it, I didn’t like, so I wasn’t going to go for a PHP framework, even though I’m sure they’re nicer than using straight PHP. If you already know PHP, I can certainly see why you’d choose Symfony or CakePHP: because it’s hard to learn Ruby and Rails at the same time. I do know Java, and if you do too, you probably know why I decided not to start my web programming experience from square one with a giant scalable enterprise framework based on it. Java’s great for big teams building business critical software; it’s not so great for guy at home experimenting with web frameworks. I acknowledge there are good reasons for choosing one of those over the more hip options like Rails and Django, but I didn’t feel those reasons applied to me. Maybe I was wrong! Let me know.

Advertisements

4 thoughts on “Odyssey Through Three Web Frameworks: Rails, Clojure, Django

  1. Pingback: Three Haiku on Clojure | The Poetry of Computer Science

  2. That’s a nice comparison! I have experienced PHP, Django and Clojure web stack, hated PHP, totally agree with that “security science” part of Clojure 😀

Comments are closed.