dcsimg

Micro-Frameworks: Big Things in Small Packages

Are Rails, CakePHP, Django, and Catalys too big for your taste but you still want the benefits of DRY programming? Maybe it's time to get small with micro-frameworks.

I love working in Ruby on Rails. Given migrations, routes, and its foundational classes, virtually every Web application decomposes to a set of small, finite, tractable problems: What am I storing (models)? What must be shown (views)? And how can the information be affected (controllers)?

Of course, Rails isn’t the only framework to provide model-view-controller features. (CakePHP is a Rails analog for PHP; Django is a parallel for Python; and Perl has Catalyst.) Moreover, MVC is just one technique. There are many approaches to constructing a Web application—simply because there’s no “right” way to define a data structure, render a page, and code logic.

To borrow a phrase from my cluster clumping colleague, Douglas Eadline, the only correct answer to every computing question is “It depends.” Or, more colloquially, “Your mileage may vary.” Rails may suitable to some problems, warranted by others, and overkill for still others.

Indeed, the quality of Rails, CakePHP, Django, and Catalyst notwithstanding, some developers have rebuffed the large frameworks, citing bulk and complexity, to create smaller and simpler alternatives. Dubbed micro-frameworks—think microcomputer versus mainframe—the tools shape incoming requests into something manageable and leave the rest up to you. Choose your design pattern, object-relational mapper (ORM), and rendering technology, and off you go. As you’ll see, a working Web application can be composed in less than ten lines of code in a single source file.

Let’s look at two micro-frameworks, one for Ruby and the other for PHP.

Singing the Praises of Sinatra

Sinatra is a micro-framework for Web applications written in Ruby. Here’s a complete Sinatra Web application.

$ sudo gem install rack -v 0.9.1
$ sudo gem install sinatra
$ cat > hi.rb
require 'rubygems'
require 'sinatra'
get '/hi' do
  "Hello World!"
end
^D
$ ruby hi.rb
== Sinatra/0.9.1.1 has taken the stage on 4567
   for development with backup from Mongrel

Sinatra depends on Rack to bundle up an incoming request. As I write this, Sinatra depends specifically on Rack version 0.9.1. Sinatra itself is also provided as a Ruby gem.

Once those two prerequisites are installed and required, the application is four lines: A GET request to the URL /hi generates “Hello, World!”

In fact, even more advanced applications are much of the same. Simply tie route, a method (GET, POST, PUT, or DELETE) and URL pair, to some code. When a request arrives, it’s compared to all routes from top to bottom; when a match is made, the block associated with the route is called. Routes can include parameters, widlcards, and regular expressions.

In addition to routes, Sinatra provides operators such as redirect and status (the latter sets the HTTP status code), sessions, cookies, filters, and layouts. You can use erb, Haml, and Builder for rendering, and you can choose what ORM to use for your models.

For example, here is a more elaborate example to display a specific record from a database. Haml renders the HTML, while Sass expands its shorthand into CSS; ActiveRecord is the ORM; and SQLite is the database.

require 'rubygems'
require 'sinatra'
require 'activerecord'

class Student < ActiveRecord::Base
end

ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :dbfile =>  'example.sqlite3.db'
)

get '/favicon.ico' do
end

get '/stylesheet.css' do
  content_type 'text/css', :charset => 'utf-8'
  sass :stylesheet
end

get '/:name' do
  @student = Student.find_by_first( params[ :name ].capitalize )
  haml :show
end

use_in_file_templates!
__END__

@@ layout
%html
  %head
    %link{ :href => 'stylesheet.css', :rel => :stylesheet, |
      :type => 'text/css' } |
  %body
    = yield

@@ show
%div.title
  %h1 Student record
  %p
    = @student.first
  %p
    = @student.last
  %p
    = @student.student_id

The file views/stylesheet.sass looks like this:

div
  :border solid 1px black
  :margin 20px
  :padding 20px

h1
  :font
    :family Georgia, Tahoma, Verdana, serif

If you call this application as http://localhost:4567/sue, it produces this.

A screenshot of the working application
A screenshot of the working application

In the topmost listing, everything up to __END__ is source. This example places the layout and view inline for clarity, but both can be placed in the views subdirectory, if you prefer. Notice the routes and the order. Originally, I had the '/:name‘ route first, which inadvertently matched /stylesheet.css, yielding a query for a student named “stylesheet.css.” To fix my error, I put the most specific routes first. And admittedly, using a first name for lookup is a bad idea; nonetheless, it makes a worthwhile example.

There is a lack of error checking, too. To make a quick fix, I could change my controller code to return an error status.

@student = Student.find_by_first( params[ :name ].capitalize )
halt 401, 'No record found' if @student.nil?
haml :show

Sinatra includes everything you need to create a Web application in Ruby, yet it has a remarkably scant number of operations. The micro-framework is used in a variety of applications, including Pagestacker and the back-end of GitHub. You can also read how to clone Twitter with 200 lines of Ruby and Sinatra. Refer to the Sinatra “In the Wild” page for a list.

Refreshing PHP with Limonade

Limondae is a micro-framework for PHP. Much like Sinatra, Limonade rolls up an incoming request and maps it to a specific block of code.

Assuming you already have PHP installed, Limonade is quick to get running. Create an empty directory, clone the Git repository for Limonade, and include the Limonade library in your code.

$ cd /var/www
$ git clone git://github.com/sofadesign/limonade.git
$ cd limonade
$ cat > index.php

As you might deduce, this code maps any a GET request for the root of the website to the function hello(), which prints the familiar message.

Limonade provides four functions for dispatching: dispatch_get() (or just dispatch()), dispatch_delete(), dispatch_post(), and dispatch_update(). Each function requires two arguments, a pattern and a function to call. Akin to Sinatra, you can also place named parameters, wildcards, and regular expressions in each pattern. Views are also stored in a views subdirectory.

These listings reproduce the Sinatra example above. The first is the Limonade code; the second is the PHP template referenced in the call to html() to render the result.

<?php
require_once 'lib/limonade.php';

dispatch( '/:name', 'find_student' );

function find_student() {
  $student = params( 'name' );

  $dbHandle = new PDO('sqlite:'."example.sqlite3.db");
  $result = $dbHandle->query(
    "SELECT * FROM `students` WHERE first='$student'" );
  $answer = $result->fetch();

  set( 'answer', $answer );
  return html( 'student.html.php' );
}

run();
?>
<html>
<head>
  <base href='http://localhost/~strike/limonade/' />
  <link rel="stylesheet" href="stylesheet.css" type="text/css" />
</head>
<body>
  <div class="title">
    <h1>
      Student record
    </h1>
    <p>
      <? echo $answer[1]; ?>
    </p>
    <p>
      <? echo $answer[2]; ?>
    </p>
    <p>
      <? echo $answer[3]; ?>
    </p>
  </div>
</body>
</html>

To refine the code, I should add a try/catch block around the database access and check if the query succeeded. Limonade provides a halt() function as well.

Limonade is not as extensive as Sinatra because PHP already has a vast assortment of operations (such as header()) to manipulate responses. Otherwise, the two are very similar. Arguably, that makes sense since Camping, another Ruby micro-framework (or perhaps nano-framework, since it’s only 4K) that is the progenitor of many micro-frameworks, including these two.

Does Size Matter? It Depends.

Whether you apply Rails or CakePHP or Sinatra or Limonade depends on the problem you are trying to solve. The good news is that you can choose the solution that best suits and fits the problem at-hand. You’re the tailor.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62