Um, wow. It’s been awhile since I last posted anything!
During my quiet time, I’ve been busy; I’ve had a few web contracts that I worked on at night. I went to work at a cool new company called Primal Fusion as a software developer. You’ll want to check that out!
Things have finally settled down enough that I wanted to do some serious housecleaning on my site. I was getting irritated with the blogging engine I was using (Typo), irritated about the HTML I used for the beachboy theme (it never looked fine on all browsers) and I was yearning to return to the world of static website generators.
So, this site has been redesigned and re-staged. I’ve tried as much as possible to keep the links the same, but if you’ve got a 404 on a page that used to be there, please let me know about it.
The new publishing engine is a ruby-based static site generator called nanoc, which so far I’ve been very pleased with. The system provides me with a solid set of features for building a static site, and was easy to extend with features I’ve wanted to build in.
Take the homepage for example. You’ll see a mashup of my Delicious feed, Twitter feed, and my article feed sorted in order. It was only a few lines of code to build, including caching the feeds.
As per my usual, I’ve returned to a low-key presentation – the intial launch of this site doesn’t even have any javascript!
So if you’ve been subscribed to me, thank you for waiting so long!
Granted, I haven’t been spending all my time looking at everyone’s favorite framework, but from the ones I do know about, it seems really odd that we have an impedance mismatch between HTTP and the classes/methods we use in our framework. I want to spend a few posts exploring how I think a framework should be put together, so that it conforms as much as possible to REST. My code samples will be in Ruby, but I don’t see any reason why you can’t build this in any other language.
Naturally, I start where the HTTP request gets handed off to what we think of as the controller in most frameworks. It seems as though we ought to have a superclass that defines the uniform interface. It would look like this:
class HTTPResource
def get; end
def post; end
def put; end
def delete; end
def method_missing m
@status = 405
"Method Not Allowed"
end
end
With it, you would instantiate the resources you want to expose. In this case, I want to build, um, how about a Media Release administration tool, where you can add, edit, and post PRs. Let’s expose a resource called Release
.
class Release < HTTPResource
def get; end
def put; end
def delete; end
private
def post;end
end
Well, I’m sure you’re tempted to say that you can post
a Release
, and I’d agree with you, but I wanted to set it up this way to show you something cool: by making post
private, any calls to that method would instead be handled by the superclass’ method_missing
method, and return a 405 Method Not Allowed. Wow, that’s convenient.
There’s a lot of stuff missing. HTTPResource
really ought to get some context together, like a collection of HTTP headers and ready access to query variables. I haven’t yet thought about how that’s going to work yet – it could be that we need instance variables, or it could be that we need classes.
But one thing I thought was pretty cool about this is that this structure seems so simple and natural, you could easily write a commandline script that included these class definitions, and manipulate your app as easily from a script as from a browser. I’m sure you could do it with other frameworks too, but it looks obvious how you’d write such a tool. That seems like a win when it comes times to write tests – just call Release.new.get
, for example, and test what gets returned.
If you have used the Camping microframework for any length of time, you might have quickly found out that there’s no feature available like render_component
, which is found in Ruby on Rails. render_component
allows you to call a controller method (with its associated view) for the purposes of rendering a portion of your page. For example, you might want a page that primarily displays data for one employee, and show a list of employees in a side column - ideally, you’d like the controller responsible for generating a list of employees to continue to be responsible for that, even if you’re in the controller for displaying employee data. So I ended up working out three different methods for solving this problem.
Let’s go back to my running example here: I’ve got this employee management app, where people might want to add or delete employees, and on almost every page, I need to see a list of employees’ names, each linked to their profiles, on a side column. The controller class for retrieving this list could look something like this:
class Employees < R
def get
@employees = Employees.find :all, :order => "name asc"
render :employee_nav_list
end
end
My first cut at trying to build render_component
was the hardest thing I had to solve. The Employees
class, with its get
method for generating the list was already written, so what I really wanted to do was somehow instantiate the class and call get
. In my previous article, we learned that the return values of these methods are always a string; if I could somehow instantiate that class, and call its get
method, then I’d have a string containing a chunk of HTML that I can store in an instance variable to be used in my current view.
Calling Employees.new.get
, unfortunately, wasn’t that simple. Hints to what the Employee’s constructor looked like was kind of buried in the Camping microframework itself, and after poring over the code and testing things out, I was able to come up with the following module:
module Component_Support
def render_component( classname, method, payload, args )
classname.new( payload, @env, method.to_s ).send(method.to_sym, *args)
end
end
To use this, you’d have a bit of code in your app that looks like this:
module YourApp
include Component_Support
end
and then you’re free to call render_component
wherever you like. Let’s break it down. render_component
, here, takes 4 arguments:
The body of the method instantiates the object correctly, calls the method that we’re interested in, and returns the result. If I was displaying an employee profile page, and I wanted to show that list of employees down the side, all I need to do is add the following line to my profile controller:
@employee_list = render_component(Employees, 'GET', '', '')
And I’ll have a nice fragment of HTML to include in my output.
I don’t know about you, but up until recently, I built the classes in Camping using only get
and post
methods because I thought that was the way you were supposed to do it. It was “the rules”. It never occurred to me that I could easily add other methods if I needed to.
The thing is, it’s just a class, like any other, so if you really needed to, you could move code that’s common to both get
and post
methods into their own method calls and call them.
using the above code snippet, we could basically build something like this:
class Profile < R '/profile'
def get
# code specific to get
@employee_list = build_emp_list
render :profile
end
def post
# code specific to post
@employee_list = build_emp_list
render :profile
end
private
def build_emp_list
@employees = Employees.find :all, :order => "name asc"
render :employee_nav_list
end
end
As a variation on a theme, suppose you have a number of controller classes, and most of them needs to display the same type of data on a page. You’d like to be as DRY as possible, and you don’t want to use the render_component
code up above – maybe because the component you want to render doesn’t properly belong to one of the controller classes. All you’d need to do is make two changes to code that looks like method 2:
#build_emp_list
into a module, along with other methods you want to re-use.include Your_new_modulename
to the class.Here’s an example:
module MyApp::Controllers
module Employee_Components
# some components defined here...
def build_emp_list
@employees = Employees.find :all, :order => "name asc"
render :employee_nav_list
end
end
class Profile < R '/profile'
include Employee_Components
def get
# code specific to get
@employee_list = build_emp_list
render :profile
end
def post
# code specific to post
@employee_list = build_emp_list
render :profile
end
end
end
So, with this article, you’re well equipped to add render_component
functionality to your Camping apps, which will help in making your code more DRY. Which method you use should depend on how you can best make your code readable. If the code you want to reuse resides in a related class, then I’ve provided you with a means to instantiate that class and call the method. If the only time you require this method is in one class, define it as its own function. Otherwise, some clever module
management might do the trick.
It’s possible to construct a web application using the Camping microframework without using the *::Views
module. Most of the time, you would be crazy to do so, since the *::Views
module makes it very easy to print out HTML responses using _why’s Markaby markup engine.
There is a strong use case for not using *::Views
– if you want to return data that is not supposed to be formatted as HTML (like XML or JSON), you’ll probably find that it’s more work than it really ought to be, to print out that response.
That’s right. I’m going to show you a camping app that does not use either the *::Views
or the *::Models
modules, that is perfectly legit:
#!/usr/bin/env ruby
require 'rubygems'
require 'camping'
Camping.goes :HelloWorld
module HelloWorld::Controllers
class Index < R '/'
def get
%Q(
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head><title>My small page</title></head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
)
end
end
end
If you save this program as hello_world.rb
, then run camping hello_world.rb
, it will run fine, and if you point your browser to http://localhost:3301/
, you will see the friendly greeting.
What’s going on? Let’s rewrite the application to something more familiar:
#!/usr/bin/env ruby
require 'rubygems'
require 'camping'
Camping.goes :HelloWorld
module HelloWorld::Controllers
class Index < R '/'
def get
render :hello_world
end
end
end
module HelloWorld::Views
def hello_world
html do
head do
title "My small page"
end
body do
h1 "Hello, world!"
end
end
end
end
If you run this hello_world.rb
app, you’ll get almost the same output. There is almost no magic involved here; in the get
function, we have a render
statement that performs the necessary work to call the hello_world
function in the HelloWorld::Views
module. What’s not obvious here (except to seasoned Rubyists, I suppose) is that there is an implicit return in the hello_world
function; it returns a string containing the HTML to be printed. There is also an implicit return happening in the controller, where the results of the render
statement (the Hello, world page) is returned to its caller.
So the dirty secret is out: as long as the methods in your controller classes return a string, whatever is in that string is what’s going to be returned to the browser.
Most of the time, you want to use the *::Views
module, because, most of the time, you want to return HTML to the browser.
Knowing what we know now, we have the ability to do a couple of useful and interesting things:
render_component
feature into your Camping appI’m going to get into more of this in future articles.
A client of mine needed an admin tool for their site: a basic file upload script. Anyone who has ever written such a script knows that there are at least a good half dozen potential failure points when writing such a thing. Experience suggests that you should check at a minimum the following types of things:
For some of these tests, it may actually be pretty important to fail loudly enough for the user to notice. And of course, if we want to build RESTful web applications, it would be good to return the correct HTTP response code in a failure scenario.
While writing the script, using Camping, it suddenly occurred to me that with a bit of tooling, I could easily use Ruby’s exception handling tools to make my life easier, and to make the code maintainable. This technique can easily be applied to Rails applications too.
The Pickaxe, in its description of the Exception
class, describes a #status
method that is only available to the SystemExit
exception. That looks like a useful idea, but I need the status baked into the base Exception class. Why? Well, if the script is going to die because a directory isn’t writable, or for some other reason I couldn’t anticipate, I need the script to return a 500 error code. Time to monkey patch Exception
:
class Exception
def status
500
end
end
Now let’s extend this for some other error scenarios, like a user attempting to upload a file larger than I’d like:
class ForbiddenException < RuntimeError
def status
403
end
end
You should compile a big list of these exceptions into its own file and require
it in your app.
Ok, so now you’re in Camping (or Rails), and you’ve got your file processing logic sitting in a class in the model, you have an ‘error’ view (among others) and you’re calling this code from the controller. In the model, you might have a #save
method, and one of the things you’re going to do before you save your uploaded file is check the file size:
raise( ForbiddenException.new, "File Too Large!") if @file.size > 11000000
Now for the juicy bit: the controller logic:
class Index
def Post
file = FileUpload.new(...)
page = :index
begin
@file = file.save
rescue Exception => @e # oh, noes! my bad!
@status = @e.status
page = :error
end
render page
end
end
In Camping, the @status
variable is reserved for setting the HTTP response code, and is smart enough to change it from 200 to 302 when you’re redirecting; that said, you can set it to other codes as we’re doing here.
And the view (the @e
is being used here):
def error
h1 "An Error has occurred"
p @e
end
After getting this working, there’s two things I couldn’t believe:
One of the niftiest (yes, I like to use the word nifty) things about this method is that you could catch different kinds of exceptions and customize the output accordingly:
def Post
file = FileUpload.new(...)
page = :index
begin
@file = file.save
rescue ForbiddenException => @e # silly user!
@status = @e.status
page = :forbidden_page
rescue Exception => @e # oh, noes! my bad!
@status = @e.status
page = :generic_error
end
render page
end
With only a handful of lines, I managed to create a system that easily handled error scenarios in a maintainable fashion, and passed them off to the correct view as required. Better still, I managed to easily ensure that the client was receiving the correct HTTP response code. Are there ways this could be improved still more? Let me know.
Copyright © 2009
Robert Hahn.
All Rights Reserved unless otherwise indicated.