Beauty is in the eye of the beholder

In my experience, there are a few different attitudes when it comes to beautiful design. Many consider Rails to be a beautiful framework to work with. In it’s own right, I certainly would agree with these folks. If you’re willing to accept the 80/20 divide and the tool fits the job, it could be dreamy to work with Rails. But in my limited experience, I sort of feel like a decision to work with Rails is more or less an all-or-nothing decision.

Like a train that wants to go east but only has tracks running North to South, a change in course is going to create some major problems. I don’t think this is a bad design, that’s what a train is meant to do. But maybe for some jobs, you need a dune buggy. That’s where the little wheels come in.

The camping framework is designed in light of another definition of beauty, and that definition is closer to the austere. When I started a job in it, I asked the usual questions one might run into when dealing with a small web app. “Does camping support sessions|file uploads|static files”

The answers were, ’sort of’,'nope’,'nope’. But all three were also appended with a “but it’s easy to roll your own”. The rest of this article will show you one way to do all three, and hopefully show some appreciation for the simplicity of the task.

Sessions with Camping

There is optional support for sessions. To me, it totally rocks how easy these are to set up. Camping lets you use SQLite3 with zero config, so all you need to do is use the session library to create a schema, and you’re right off the ground in no time.

Straight from an actual job of mine, the relevant parts look like this.

  require "camping/session
  module Importer
    include Camping::Session
  end
  def Importer.create
    Camping::Models::Session.create_schema
  end

Now in the rest of my app, the @state variable will hold a unique session that I can just stuff things in for the user, no further thoughts needed.

for example:

  @state.my_attribute = "Chunky Bacon"

could be accessed throughout my app, maintaining state. Very cool.

The thing is, to a Rubyist with minimal rails experience, this kind of thing might make more sense. I *see* where the schema is being created, I see the library that’s in charge of my sessions, etc. And if I really wanted to, I can hack on those things without digging much deeper. This to me is a valuable tradeoff from ‘just works’ to ‘works easily and you can see how’. Now yeah, I’d be annoyed if I was looking for more high level support, but for what I was looking for, I saw this as ’super cool’

File Uploads

Note that the way I do this is a hack, but the joy of Camping is that’s okay! This particular app is meant to run locally, and all users to be considered equal, so I had no need for ACLs or any other constraints, which others may.

Here is the controller:

    class Upload < R '/upload'
      def get
        render :upload
      end
      def post
        file = @input.File.tempfile.read
        @state.mi.load(file) #does something with the file, using sessions
        redirect Import
      end
    end

And the relevant bits of the view:

  def upload
    form :action => "?upload_id=#{Time.now.to_f}", :method => 'post',
         :enctype => 'multipart/form-data' do
      p do
        input({:name => "File", :id => "File", :type => 'file'})
        input.newfile! :type => "submit", :value => "Upload"
      end
    end
  end

Nothing particularly magical here, and that’s the way I like it. This just creates the appropriate form, lets me select a file from my machine, whack the ‘Upload’ button, and then handle that in my controller. There are a ton of other ways to deal with this, and the more complicated they get, the more likely you’ll be to say something like “Maybe I should be using Rails”. But for simple needs, I like having this kind of “roll your own” power.

Static Files

I copied and pasted this code from the camping wiki, but again, I was awestruck by the coolness of the ‘if I need to tweak that, it should be easy’ effect.

 require 'pathname'

 module Camping::Controllers
  class Static < R '/static/(.+)'
    MIME_TYPES = {'.css' => 'text/css', '.js' => 'text/javascript',
                  '.jpg' => 'image/jpeg'}
    PATH = File.expand_path(File.dirname(__FILE__))

    def get(path)
      @headers['Content-Type'] = MIME_TYPES[path[/.w+$/, 0]] || "text/plain"
      unless path.include? ".." # prevent directory traversal attacks
        @headers['X-Sendfile'] = "#{PATH}/static/#{path}"
      else
        @status = "403"
        "403 - Invalid path"
      end
    end
  end

You can happily ride the little wheels too!

I realized that this was the kind of tool I was looking for. Something simple, basic, and super extendable. I’m not afraid of rolling my sleeves up, and sparse documentation isn’t enough to keep me away from code that seems cool. If you’re in the same boat, be sure to check Camping out. Most of my code here is either taken directly from the wiki page or via suggestions from the kind folks in #camping on Freenode.

A slight warning is that if you plan on working with the framework, give yourself some extra exploration time. The little wheels are *awesome*, but I can promise you you’ll spend more time on IRC and sifting blogs and wikis at first then you might for the equivalent Rails job. I personally think it’s well worth it for the minimal web needs I have, and even if you don’t have a pressing need for a minimal tool like this, it’s great eye opener to the benefits of a simple design.