I've written here and here about serving static files with Camping, but each time I've set up a new route for each resource type. You can serve all static files with a single route by following the instructions on the wiki.

Here's the code that's presented there.module Camping::Controllers
class Static < R '/static/(.+)'
TYPES = {'.css' => 'text/css', '.js' => 'text/javascript',
'.jpg' => 'image/jpeg'}
PATH = File.expand_path(File.dirname(__FILE__))
def get(path)
@headers['Content-Type'] = 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
end

You would then create a path by calling, for example, R(Static, 'style.css') from your view. The X-Sendfile header tells the server to send the specified file. If you want to support other kinds of resources, simply add an entry into the TYPES hash.

If you're using LIGHTTPD, apparently some old versions don't support the X-Sendfile header, but they do support a custom X-LIGHTTPD-send-file header, so modify the code above accordingly. I haven't tested it but the current version should work fine with X-Sendfile.

This is one of those lines of code that just shines with Ruby magic. @headers['Content-Type'] = TYPES[path[/\.\w+$/, 0]] || "text/plain"A couple of things are happening here. The path string passed in to the function, which is the name of the resource, is accessed using a regular expression, path[/\.\w+$/, 0], a dot followed by one or more word characters. The 0 says which atom of the match data to return, and is actually redundant, I believe, as no atoms are defined, and 0 is the entire match anyway. There are more details in the String API.

The regular expression grabs the extension of the file, which is then used as the key into the TYPES hash. If the extension is in the hash, the appropriate content type will be returned and the header set. If it isn't in the hash, nil will be returned, Ruby's tasty || operator will come into effect, and the content type will be set to a sensible default, text/plain.