Latest from the Blog:

Ajax, tabs and Rails: Part 1 September 26th, 2008

Developing a horizontal tabbed menu with Ajax functionality with Ruby on Rails, provides us with a fine example of why unobtrusive JavaScript is important, and worth bothering with.

I'll quickly set up a skeleton of a rails app with an 'about us' section showing several related pages with information about something or another.


$ rails tabs
$ script/generate controller about page_1 page_2 page_3 page_4


class AboutController < ApplicationController
  #specify layout  
  layout 'application'
 
  #I don't need to define any methods, Rails will simply go looking for the called page in the 'about' folder under 'views'   
end

My layout 'application' is minimal so as to keep things simple.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>

<head>
 <meta http-equiv="Content-type" content="text/html; charset=utf-8">
 # be sure to include javascript defaults, we'll be using Prototype    
 <%= javascript_include_tag :all, :cache => true %>
 # ...and a style sheet	
 <%= stylesheet_link_tag 'screen' %>		
 <title>Tabs</title>				
</head>
<body>
 
 <div id="content">

  # Tabbed navigation is appropriately represented in HTML as an unordered list.
  <ul id="tabnav">
  
 	 # To help keep my template clean, I'll call a helper method (yet to be written), for generating a link. 
 	 <%= link('page_1', 'Page One') %>
 	 <%= link('page_2', 'Page Two') %>				
	 <%= link('page_3', 'Page Three') %>				
	 <%= link('page_4', 'Page Four') %>				
  </ul>

  <div id="tabContent">
   <%= yield %>
  </div>
</body>
</html>


Here is my helper method in about_helper.rb:


# helpers/about_helper.rb
# Method takes a page id and label and generates a link inside a list element, attaching a class name of 'active' on list element for active link. 
def link(id, label)
  "<li#{ ' class="active"' if controller.action_name == id}>#{link_to(label, send('about_' + id + "_path"))}</li>"
end

I'll need to write some routes before we have something that works:


# config/routes.rb
ActionController::Routing::Routes.draw do |map|
  def map.controller_actions(controller, actions)
    actions.each do |action|
      self.send("#{controller}_#{action}", "#{controller}/#{action}", :controller => controller, :action => action)
    end
  end

  map.controller_actions 'about', %w[page_1 page_2 page_3 page_4]
end

And now for a lick of paint:


/* screen.css */
body {
	font: 100% verdana, arial, sans-serif;
	background-color: #fff;
	margin: 50px;
}

ul#tabnav { 
	text-align: left;
	margin: 1em 0 1em 0;
	font: bold 11px verdana, arial, sans-serif; 
	border-bottom: 1px solid #6c6;
	list-style-type: none;
	padding: 3px 10px 3px 10px; 
}

ul#tabnav li { 
	display: inline;
}

ul#tabnav li.active {
	border-bottom: 1px solid #fff; 
	background-color: #fff;	
}

ul#tabnav li.active a {
	background-color: #fff; 
	color: #000; 
	position: relative;
	top: 1px;
	padding-top: 4px;	
}

ul#tabnav li a { 
	padding: 3px 4px; 
	border: 1px solid #6c6; 
	background-color: #cfc; 
	color: #666; 
	margin-right: 0px; 
	text-decoration: none;
	border-bottom: none;
}

ul#tabnav a:hover {
	background: #fff; 
}


Within the 'about' folder under views, you'll find several pages created earlier that need some dummy content. After adding some lorem text I kick-start mongrel and visit http://0.0.0.0:3000/about/page_1:

Clicking through each tab works like a charm. I now have a simple, but functionally sound tab menu device. I'll consider adding a behavioral layer on top of what I've created, in order to provide Ajax functionality, and will write about it very soon in part two.

Widget with Rails August 3rd, 2008

Widgets offer a simple and effective form of content distribution. Creating a JavaScript widget for your Rails app couldn't be easier. Here's how:

I'll create a demo app called 'leaf recipes', a resource for salad lovers, where people can share favourite salad recipes:


$ rails salad
$ cd salad

Now for the recipe resource. My first iteration will be simple, all I want is a title and a description:

	
$ script/generate scaffold recipe title:string description:text
$ rake db:migrate

Now some content. I'll do this through the console:


$ script/console	
>> Recipe.create(:title => 'Crunchy Romaine Toss')
>> Recipe.create(:title => 'Roquefort Pear Salad')
>> Recipe.create(:title => 'Cranberry Spinach Salad')
>> exit

Fire up webrick and visit http://localhost:3000/recipe, which displays an index view, listing the three salads I just created.

I want my JavaScript widget to access my recipe resource with the same URL, but instead of returning HTML, I want it to spit out some JavaScript which the client browser can interpret. I need to visit the recipe controller:

app/controllers/recipes_controller.rb

I'm interested in the index method:


def index
  @recipes = Recipe.find(:all)
  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @recipes }
  end
end	

The respond_to block is ready to respond to requests for html and xml. My widget's going to want JavaScript. I'll add it to the block:


def index
  @recipes = Recipe.find(:all)
  respond_to do |format|
    format.js # index.js.erb - this is where I'll craft my JavaScript
    format.html # index.html.erb
    format.xml  { render :xml => @recipes }
  end
end	

I'll create a corresponding view template:

app/views/recipes/index.js.erb


var txt = ''

txt += "<div id='salads_widget'>";
txt += "<h2>Fine green salads from salads.com</h2>";
txt += "<ul>";
<% for recipe in @recipes do %>
  txt += "<li>";
  txt += "<%= escape_javascript(link_to recipe.title, recipe_url(recipe)) %>";
  txt += "</li>"
<% end %>
txt += "</ul>"
txt += "</div>"

document.write(txt);

Actual JavaScript code is minimal. Past txt += all we have is HTML markup with some erb template code for looping through the @recipes instance variable assigned in the recipes controller and displaying the title attribute within an HTML list. All of this is assigned to a JavaScript variable I've named txt which is eventually written to the client's web page with document.write(txt).

Our (very basic) JavaScript widget is ready for public consumption. Here's my 'copy and embed' snippet:

<script type="text/javascript" language="JavaScript" src="http://localhost:3000/recipes.js"></script>

It is clear to see from the URL that the recipe resource is being called. The .js at the end is for the repsond_to block, letting it know to reply with JavaScript.