SqueezeBox

Yet Another Web Framework

SqueezeBox extends the Mongrel web server to provide an effortless web framework. Designed after the Model-View-Controller pattern, SqueezeBox separates presentation and back-end cleanly but slightly differently than contemporary systems. The major difference between Rails, Merb, or the other Ruby frameworks is that SqueezeBox routes HTTP requests based on file system organization. (Like mod_php!) While some may scoff at this simplistic design, filesystem routing significantly lessens the complexity of usage.

Despite being little more than a directory of .erb files, SqueezeBox sites still have models and controllers. The model part is typical: ActiveRecord. The controller part is novel. A SqueezeBox website is divided into Resources (where Resource does not have the same meaning as it does in Rails). A Resource is an extension-less path in the document root — this path can match one or more files. For example the path /products might match several files

products.erb or something else entirely could be returned from a HTTP request to /products, depending on what the controller file, products.rb instructs.

The controller file is similar to an action in Rails.

# /home/web_store/public/products.rb 
@products = Product.find(:all)
render
The erubis templates are the same:
<!-- /home/web_store/public/products.erb -->
<h1>Here are our Products!</h1>
<ul>
<% for product in @products %>
  <li><%= product.name %></li>
<% end %>
</ul>

As with mod_php, Resources with directory names can be targeted by using index.rb and index.erb.

While SqueezeBox routes by the file-system like antiquated Apache CGI modules (like PHP), there are many old-fashioned annoyances that it does not copy. For example, one does not need to include model definitions or header/footer files into each erb file. Rather when SqueezeBox starts up, it looks in a special file called initialize.rb for instructions on what libraries to load globalally, what layouts should be availible, or other instructions.

Getting Started

There is nothing to using SqueezeBox. Here is everything you need to know. To install it:

~> sudo gem install squeezebox

To start new website:

~> mkdir -p new_website/public
~> echo "Now is <%= Time.now %>" > new_website/public/index.erb

Then start up the server by running the squeezebox command with the website directory as an argument.

~> squeezebox -p 4200 new_website/

http://localhost:4200 should show the date and time.

The Controller File

Note that in Ruby the last line of execution is the return value. Explicit use of return is allowed but not often used. When the last line of a controller file is render, the return value of the controller file is the output of the function render (a string containing a rendered erb template).

Controller files are functions whose return value is the body of the HTTP response. Typically one will call render() to render the erb template (contained in the same Resource path) but there are numerous other possibilities. Returning just a string use that string as the body of the response—useful for sending back JSON data to a client. Returning any IO object (or anything that acts like an IO object) will prompt SqueezeBox to stream, in chunks, data to the response body. If you want to stream dynamic content (perhaps JSON containing updates) you need only define a class with a read method and return an instance of that from a Controller file.

The controller file has access to all the relevant data:

Any instance variables assigned in the controller file (i.e., ones with @ signs) will be available to erb templates.

The function redirect_to() sets the status code to 301 for redirection and returns nil (which is what the body of the response should be for redirection).

Layouts

Layouts are an important part of web design—one would like their site to look consistent. In SqueezeBox one can define a default layout which is used automatically throughout the site unless otherwise specified. Layouts must be defined before SqueezeBox starts, in the initialize.rb file. The function to define layouts is set_layouts

set_layouts(
  :default => 'layouts/default.erb',
  :blue => 'layouts/blue.erb',
  :admin => 'layouts/admin.erb'
)

This commands initializes three layouts each identified but a symbol. When render() is called from a controller file an option :layout => :blue would tell SqueezeBox to render with layouts/blue.erb. Automatically render() will use the :default layout; to avoid this use render(:layout => false).

Layout templates should be placed outside of the public/ directory so SqueezeBox does not accidentally serve them.

A layout template has a special variable, content which will be replaced with the content of the inner template. Example:

<html>
  <head>
    <title>
      <%= @page_title ? @page_title : 'My Site' %>
    </title>
    <link type="text/css" rel="stylesheet" href="/site.css"/>
  </head>
  <body>
    <h1>
      <%= @page_title ? @page_title : 'My Site' %>
    </h1>
    <div id="mainContent"><%= content %></div>
    <div id="footer">:D</div>
  </body>
</html>

Note that @page_title can be defined inside the inner template yet effect the layout template.

Exceptions

SqueezeBox uses Ruby-level exceptions to display HTTP errors. It has a number of predefined exceptions which can be used. For example using

raise NotFound
from a controller file will stop current execution and display a page with status code 404 telling the user that the document could not be found. Similarly
raise Unauthorized
shows a page with a 501 error telling the user they do not have access rights to the requested information. A common exception would be denying requests unless they are using the POST method:
raise MethodNotAllowed unless @request.post?

These exceptions are actually genuine Resources with paths at /exceptions/not_found and /exceptions/unauthorized. That means that if you place not_found.erb in public/exceptions, SqueezeBox will use your template, and your controller to display the 404 page.

Exceptions that are raised during execution (syntax errors, out of memory, etc) which are not special SqueezeBox exceptions look at /exceptions/index for instructions and return with 500 status code (internal server error).

This is really powerful! Now you have a way to display dynamic error pages, perhaps logging Unauthorized requests to a database, or rendering a 404 page that knows what the request was.

Design Philosophy

More

Please look at the documentation and at the example web site included in the distribution for more information.

The mailing list is squeezebox-users@rubyforge.org. The FreeNode IRC channel is #squeezebox. The bug tracker is on RubyForge and the SVN repository is at

http://tinyclouds.org/svn/squeeze_box/trunk/