Building Tempo with Rails, Part IV
Posted by Brian Sam-Bodden

In the last three installments of this series we concentrated our efforts on building the most important entities of the Tempo domain. If you are anything like me you need visual feedback to truly get a sense of progress, if anything for the psychological effect of seeing something working (other than on the command line)
Before we jump into building the visual aspects of the application we should finish configuring the restful_authentication system we installed in installment #1.
Configuring Restful Authentication
The first step is to protect all controllers in our application by applying a before filter that will trigger the authentication logic. In Rails the ApplicationController is the ideal place to do so since is the default parent class for all controllers in an application:
class ApplicationController < ActionController::Base
include AuthenticatedSystem
session :session_key => '_tempo_session_id'
before_filter :login_required
endIn the file application.rb we include the AuthenticatedSystem and apply the before filter :login_required which will cascade down to any new controllers we create.
The restful_authentication plugin creates two controllers, the UsersController; a typical CRUD controller for User objects and the SessionsController which is the controller responsible the login/session management logic.
Taking a peek at the Session controller we see that it is set to skip the :login_required filter:
class SessionsController < ApplicationController
skip_before_filter :login_required
...
endIn the Views/sessions directory we customize the new session view (a.k.a the login screen):
<div class="login">
<div id="Dialog">
<h1>Welcome to Tempo, please log in</h1>
<dl>
<% form_tag session_path do -%>
<dt>Username:</dt>
<dd><%= text_field_tag 'login' %></dd>
<dt>Password:</dt>
<dd>
<%= password_field_tag 'password' %>
<span>(<a href="/forgot_password">I forgot my password/username</a>)</span>
</dd>
<dd><%= check_box_tag 'remember_me' %>Remember me on this computer</dd>
<dd><%= submit_tag 'Log in' %></dd>
<% end -%>
</dl>
</div>
</div>Permanent Scaffolding?
To give us a jump start in the right direction I'm going to make use of a Rails scaffolding plug-in that should allow us to get some fairly decent web pages up and running in no time. There seems to be two contenders in the area of "permanent" Rails scaffolding plugins, one is the Streamlined Framework produced by the brilliant folks at Relevance the other one is ActiveScaffold created by the same team that built the now deprecated AjaxScaffold.
Both frameworks attempt to provide you with a way to generate ActiveRecord-driven scaffolding screens that are configurable and flexible enough that you theoretically won't have to later tear them down to build the "real" pages of you application. I've briefly looked at both frameworks previously and they both seem fairly even in terms of features. So just for the sake of trying something new I'm gonna use ActiveScaffold for the Tempo time tracking application.
ActiveScaffold
The ActiveScaffold website has quite a bit of documentation that should help you get started. I in particular found their "How to Approach Active Scaffold" article useful in understanding where it would be a good idea to use the framework.
To get started with ActiveScaffold in the Tempo project you'll need to first install the plug-in using Piston:
/> piston import http://activescaffold.googlecode.com/svn/tags/active_scaffold vendor/plugins/active_scaffold Exported r633 from 'http://activescaffold.googlecode.com/svn/tags/active_scaffold' to 'vendor/plugins/active_scaffold'
Once you have installed the plug-in all that you need to do to enable the scaffolding for one of you model objects is to add a minimal amount of code to your controllers:
class UsersController < ApplicationController
active_scaffold :user
endIn your application layout head section you also need to include:
<%= javascript_include_tag :defaults %>
<%= active_scaffold_includes %>That will give you the basic ActiveScaffold CRUD functionality.
RESTful Scaffolding
To make your scaffolding "RESTful" with ActiveScaffold, you need set up the resource map entry in your routes.rb file for each of your ActiveScaffold controllers:
map.resources :users, :active_scaffold => trueUser Controller
ActiveScaffold will let you configure what fields are shown for each of the CRUD screens (and their formatting ...to an extent). ActiveScaffold allows you set configuration options globally for each controller in a global config block which you will place in your ApplicationController or with per-controller config blocks as we have done in the UsersController below:
class UsersController < ApplicationController
#skip_before_filter :login_required
active_scaffold :user do |conf|
conf.actions.exclude :show
# List
conf.list.columns = [:person, :login, :email]
# Create
conf.create.columns = [:person, :login, :email, :password, :password_confirmation]
# Update
conf.update.columns = [:person, :login, :email, :password, :password_confirmation]
end
endIn the code snippet above we turn off the "show" action and configure which objects fields we want to be shown in the different screens. For the "list" page we only want the to_s of Person, the login and email. In the create or update screens we also want to show the password and the password confirmation.
To test this screen you can point your browser to /users/new and create a new user. Make sure to uncomment the skip_before_filter line above since we don't have an official "registration" page yet. Once you create at least one user that you can log in with you can enable the filter again.
Other controllers will be similarly configured, but before we get there let's set up the look of the application with a set of CSS stylesheets and configuring some simple application navigation logic.
Main View Layout
To configure the main view layout for our application, under your Rails application Views/layouts directory customize the application.rhtml as shown below and pair it with a "good enough" stylesheet:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<head>
<title>Tempo</title>
<%= javascript_include_tag :defaults %>
<%= active_scaffold_includes %>
<%= stylesheet_link_tag 'tempo' %>
</head>
<body>
<!-- ====== -->
<!-- Header -->
<!-- ====== -->
<div id="Header">
<h3 class="global">
<% if logged_in? -%>
Logged in as <%= current_user %> (<a href="/logout" title="Log-out">Log-out</a>)
<% else -%>
<a href="/login" title="Login">Log-in</a>
<% end -%>
<a href="/help" class="image"><img alt="Help" src="/images/help.png" align="absmiddle" height="16" width="32"></a>
</h3>
<h1>
<img src="/images/tempo.png" title="Tracking time with Ease" alt="Tempo" border="0">
</h1>
<% if logged_in? -%>
<!-- =============== -->
<!-- Navigation Tabs -->
<!-- =============== -->
<div id="Tabs">
<%= links_for_navigation %>
</div>
<% end -%>
</div>
<!-- ============== -->
<!-- Beta Indicator -->
<!-- ============== -->
<img id="beta" src="/images/beta.png" alt="">
<div id="Wrapper">
<div class="Container">
<div id="DashContentFrame">
<!-- =========== -->
<!-- Left Column -->
<!-- =========== -->
<div class="Left">
<div class="col">
<% if flash[:notice] -%>
<div class="good" id="Flash">
<p><%= flash[:notice] %></p>
</div>
<% end -%>
<% if flash[:error] -%>
<div class="bad" id="Flash">
<p><%= flash[:error] %></p>
</div>
<% end -%>
<%= yield %>
</div>
<div class="bottom"> </div>
</div>
<!-- ============ -->
<!-- Right Column -->
<!-- ============ -->
<div class="Right">
<div class="col">
<h1>[[Help Title]]</h1>
<p>[[Help Body]]</p>
</div>
</div>
</div>
</div>
</div>
<!-- ====== -->
<!-- footer -->
<!-- ====== -->
<div id="footer">
<a id="powered" href="http://integrallis.com/" title="Integrallis Software, LLC.">
<img src="/images/powered-by-integrallis.png" title="Integrallis Software, LLC." alt="Integrallis logo" border="0">
</a>
</div>
</body>
</html>Let's examine what the template provides. At the top of the page I plan to have a simple header that shows the currently logged in user and a logout link. If there is no user logged in then a login link is shown.
Further down the page (after the application logo) we will have a set of application tabs that will be generated by the utility method links_for_navigation which we will create later in the ApplicationHelper.
The body of the page will be divided into two columns, the largest of the two on the left at about 75% of the page that will contain the main elements of the application and the right column for application controls and (if I ever get to it) some interactive help.
Navigation
The navigation code is fairly simple, I'm doing this old school (read: I'm sure there are cleaner ways to do this) but we'll get to refactor this code later on. In this helper I have a method that builds a link for a given page (tagging the "current" page with a specific CSS class) and a method that returns all the links for the application.
# Methods added to this helper will be available to all templates in the application.
module ApplicationHelper
# http://dev.rubyonrails.org/ticket/2382
# current_page? does not respect alternative routes
def nav_link_to(name, options, html_options)
if current_page?(options)
html_options.store(:class, 'current')
end
link_to name, options, html_options
end
def links_for_navigation
tabs = []
tabs << nav_link_to('Users', {:controller => 'users', :action => 'list'}, {:title => 'Manage Users', :style => 'float: right; margin-right: 30px;'})
tabs << nav_link_to('Projects', {:controller => 'projects', :action => 'list'}, {:title => 'Manage Projects', :style => 'float: right;'})
tabs << nav_link_to('Activities', {:controller => 'activities', :action => 'list'}, {:title => 'Manage Activities', :style => 'float: right;'})
tabs << nav_link_to('Time Entries', {:controller => 'time_entries', :action => 'list'}, {:title => 'Manage Time Entries', :style => 'float: right;'})
tabs << nav_link_to('Dashboard', {:controller => 'dashboard', :action => 'index'}, {:title => 'Dashboard', :style => 'float: left;'})
html = '<ul id="MainTabs">'
tabs.each do |tab|
html += '<li>'
html += tab
html += '</li>'
end
html += '</ul>'
end
endIn the links_for_navigation method I construct an HTML unordered list and add the links for the application, notice that I'm styling the links/tabs to show right justified except for the tab that will become the entry point of the application. The "dashboard" which will be left justified.
Stylesheet
Instead of boring you with the CSS, here is a screenshot of the original HTML template styled by my CSS and a link to download the CSS and graphics if you are interested.

I've zipped up the tempo specific files in the public directory which you can download here
Finishing the controllers with ActiveScaffold
Now that we have all the elements in place let's tackle the configuration of each of the controllers. Along the way we'll make enhancements to the domain objects.
If you haven't done so already, navigate to /users/new on you application and create a new user (remember to disable the login filter)

Activities Controller
The Activity model object is extremely simple, having only two string fields; name and description. To correctly display the object instances in other ActiveScaffold screens (in drop-downs) we can provide an implementation of the to_s method as shown below:
class Activity < ActiveRecord::Base
def to_s
self.name
end
endThe controller code is minimal:
class ActivitiesController < ApplicationController
active_scaffold :activity do |conf|
conf.actions.exclude :show
# List
conf.list.columns = [:name, :description]
# Create
conf.create.columns = [:name, :description]
# Update
conf.update.columns = [:name, :description]
end
end
Project Controller
As we did with Activities add the following to_s method to the Project model:
def to_s
self.name
endThe controller again is very simple to configure:
class ProjectsController < ApplicationController
active_scaffold :project do |conf|
conf.actions.exclude :show
# List
conf.list.columns = [:name, :description]
# Create
conf.create.columns = [:name, :description, :projects_activity, :projects_users]
# Update
conf.update.columns = [:name, :description, :projects_activity, :projects_users]
end
end
TimeEntries
The TimeEntries controller requires the most configuration. For the TimeEntry create and update screens we want to have the user, project, and project_activity appear as drop-down select elements. To accomplish this ActiveScaffold provides the form_ui element of the column API. The :select option renders a select box or a collection of checkboxes. There are also options for :calendar, :checkbox, :country, :password, :textarea and :usa_state
class TimeEntriesController < ApplicationController
active_scaffold :time_entry do |conf|
conf.actions.exclude :show
# UI Type
conf.columns[:project].form_ui = :select
conf.columns[:user].form_ui = :select
conf.columns[:projects_activity].form_ui = :select
conf.columns[:comment].form_ui = :textarea
# List
conf.list.columns = [:project, :user, :date, :total_hours, :projects_activity]
# Create
conf.create.columns = [:project, :user, :projects_activity, :start, :end, :hours, :comment]
# Update
conf.update.columns = [:project, :user, :projects_activity, :start, :end, :hours, :comment]
end
end
In the next installment we will build the DashboardController that will provide a more stylish and user friendly interface for users to enter time into Tempo.

hi.
thanks for this great tutorial, i finnaly managed to understand and see how to use RSPEC !
seems that the link do public/ files is bronken, could you provide this ?
thanks in advance.
Hm... how could I configure the :password and :passwordconfirmation fields to be shown as password field, cause config.columns[:password].formui = :password doesn't work.
In the above, how to relate the models.i mean in the project, its have projectactivity and projectusers.