BoilerPlate — easy data-centric applications on Rails

BoilerPlate is a framework on top of Ruby on Rails that is intended to make development of data-centric applications as easy as possible.

Some technicalities can be found in the README. For the scenic route, see below.

Status

By now, BoilerPlate itself has died, but it lives on in a few plugins that I've published. Please see my page at RubyForge.

Requirements

Rails
Currently, BoilerPlate needs a patched version of Rails SVN HEAD. It's included in the download.
libgettext-ruby
get it from its home page. If you are using Debian/Linux, a package is available.
ruby-json
available as a gem from RubyForge.
libalgorithm-diff
available here or as a Debian/Linux package.
MySQL
only for the demo app; otherwise PostgreSQL works, too.

Download

Guided Tour

Being in its early childhood, BoilerPlate is still rather browser shy. It looks and works as intended in Mozilla Firefox and Konqueror. In Internet Explorer it will probably present itself as an angry fruit salad with bad manners.

Currently, I can't host a live demo. To overcome this a bit, I've set up a couple of static demo pages. These are essentially saved versions of pages generated by the demo application. I have tweaked them somewhat to avoid the most egregious errors, but they will contain dead links and produce errors on some actions. For the list views, client-side column reordering is switched on; normally this is done on the server.

A simple list

Here's all the code from app/views/tasks/list.rhtml needed to create this view. No need to write it yourself as it gets generated.

<%
  @page_title = 'Tasks'
%>

<div id="entitylist">
  <%= render :partial => 'shared/entity_list' %>
</div>
  

Well, okay, that's not all. The controller needs to support it somewhat:

class TasksController < ApplicationController
  layout :standard_layout

  define_list_action :list, Task,
    { :path => 'description', :inline => '<%= truncate(object, 30, BoilerPlate::Unicode::Constants::HORIZONTAL_ELLIPSIS) %>'},
    { :path => 'due_date', :partial => 'shared/short_date'},
    { :path => 'category.name', :title => _('Category'), :type => :string, :include => :category },
    { :path => 'status.name', :title => _('Status'), :type => :enum, :include => :status }

  ...
end
  

Beyond that, however, you don't need any more code.

Reordering columns

Table columns can be reordered by drag & drop.

Filter popups

Filter the displayed rows by certain criteria.

A form

A form for creating a new object. Input elements with (client-side) validation errors are marked red(ish).

The code for app/views/employees/_form.rhtml:

<!--[form:employee]-->

<%= labeled_text_field 'employee', 'lastname', :label => _('Last name'),
    :validation_message => _('Please enter a last name in this field.') %>
<%= labeled_text_field 'employee', 'firstname', :label => _('First name'),
    :validation_message => _('Please enter a first name in this field.') %>
<%= labeled_gender_choice 'employee', 'gender',
    :validation_message => _('Please choose one option.') %>

<% collapsible_fieldset(:legend => _('Address'), :except => 'new', :valid => @address) do %>
  <%= render :partial => 'shared/address', :object => @address %>
<% end %>

<%= labeled_association_target_chooser 'employee', 'manager', { :label => _('Managed by') }, :action => :manager_candidates %>

<% collapsible_fieldset(:legend => _('Assigned Tasks'), :only => 'new') do %>
  <%= association_targets_chooser_with_query 'employee', 'tasks', :text_method => :description, :action => :task_candidates %>
<% end -%>

<%= version_lock 'employee', 'address' %>

<!--[eoform:employee]-->
    

Code for new.rhtml and edit.rhtml is generated.

Choosing an association target

Choose an associated object. There's AJAX in there...

Having and belonging to many

No extra code is required in the controller to handle this.

Handling update conflicts

With optimistic locking there's always the possibility of conflicts. Some other user may have changed or deleted an object just while you're editing it yourself. BoilerPlate tells you when this has happened and allows you to clean up the conflict.

There's nothing spectacular in the form itself.

<!--[form:task]-->

<%= labeled_calendar_field 'task', 'due_date', {}, :class => 'mandatory' %>
<%= labeled_text_area 'task', 'description', {}, :rows => 5 %>
<%= labeled_select 'task', 'status', :class => 'mandatory' %>
<%= labeled_tree_navigator 'task', 'category', {}, :initially_collapsed => (@action_name == 'edit') %>

<%= version_lock 'task' %>

<!--[eoform:task]-->
    

And the code in the controller looks pretty easy, too.

  def update
    @task = Task.find(params[:id])
    result = update_optimistically(@task, params[:task])
    handle_update_result(result, [:task])
  end
    

Tree Navigator

The code above already contains the tree navigator. In the screenshot above it is collapsed; below is what it looks like when expanded. Levels are loaded dynamically via AJAX requests.

The controller needs this bit of code to service the request.

  define_ajax_action :child_categories, Category, {
    :conditions => [ 'parent_id = ?' ],
    :condition_values => [ :id ],
    :partial => 'categories',
    :default_response => ' '
  }
  

About the author...

I'm Michael Schürig, a software developer located in Bonn/Germany. If you'd like to get an idea of what I do and what I'm interested in, please visit my home page. Available for hire.

License

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See http://www.gnu.org/copyleft/lesser.html