Rails Recipe for Setting the Page Title within a View

This is a relatively simple recipe but I dive in deeper to bang out a fairly solid solution. Let us say you would like to set the page title of the current page without mucking around in your controller; there are a ton of different ways to go about this. Whatever way you chose you should probably setup your default layout template (app/views/layout/applicaton.html.erb) to contain the following:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
	<head>
		<title><%= APP_CONFIG[:site_name] %><% if !@page_title.blank? %> - <%= @page_title %><% end %></title>
		<meta http-equiv="content-type" content="text/xhtml; charset=utf-8" />
		<%= stylesheet_link_tag 'base' %>
		<%= javascript_include_tag :defaults %>
	</head>
	<body>
          <h1>Hello World!</h1>
          <%= yield :layout %>
	</body>
</html>

You could setup the @page_title instance variable within each action of your controller (app/controllers/foo_controller.rb) and set the title this way (yuck!):

class FooController < ApplicationController
  def index
    @page_title = "Show all Foo's"
    respond_to do |format|
      format.html # => /foos/index.html
    end
  end

  def show
    @foo = Foo.find(params[:id])
    @page_title = "Showing Foo \"#{@foo.name}\""
    respond_to do |format|
      format.html { render :template => 'foos/index' } # => /foos/index.html
    end
  end
end

The problem with this approach is that it is not really separating the controller and view logic for something we could arguably handle entirely within the view (and its helpers). Not to mention the fact that it will litter every controller and action where you would like to set a page title. Instead, what if we could create a helper method that assisted us with this and then just call that method within each view (app/helpers/application_helper.rb):

module ApplicationHelper
  def title(phrase, container=nil)
    @page_title = phrase
    content_tag(container, phrase) if container
  end
end

That’s better! So what do we have to do? It is simple: in whatever view template (e.g. app/views/index.html.erb) we simply place something like <% title "Foo#index" %> to set the page title to “Foo#index”. If we want to set the page title and also print out an <h1> tag containing the title we could simply place something like <%=h title "Foo#index", :h1 %>. And that will work about 99.9% of the time, but there is at least one exception I can think of: rendering a view template associated with another action. In other words, suppose you wanted to do the following within your controller (app/controllers/foo_controller.rb):

class FooController < ApplicationController
  def index
    @page_title = "Show all Foo's"
    respond_to do |format|
      format.html # => /foos/index.html.erb
    end
  end

  def search
    @page_title = "Search all Foo's for #{params[:search]}"
    @foos = Foo.find(:all, :conditions => ['name LIKE ?', "%#{params[:search]}%"])
    respond_to do |format|
      format.html { render :template => 'foos/index' } # => /foos/index.html.erb
    end
  end
end

Then, if you have <% title "Foo#index" %> in app/views/index.html.erb, and were to visit the search page, the title would still be “Foo#index” instead of "Search all Foo’s for "terms"". Setting the @page_title instance variable in the controller does not trump the value when it is set in the app/views/index.html.erb template so I posit that this is in fact a bug. If we make a few small changes to our helper method we can rectify this very concern and still keep the expected functionality:

module ApplicationHelper
  def title(phrase, container = nil)
    @page_title ||= phrase
    content_tag(container, @page_title) if container
  end
end

This will allow us to set the @page_title instance variable in our individuals views or in the controller (which is necessary in the case just illustrated). Now setting the @page_title instance variable in our controller will trump whatever values happen to be later set in the view. That’s it! Well, not entirely, I am sure this could be even better, but cannot immediately think of anything. Have an idea? Send it in by commenting on this article.

Posted by John on Feb 04, 2009

Post a comment...

Creating Active Tab Toggling for Site Navigation in a Ruby on Rails Application

I have a tabbed navigation menu in an application I am developing that fairly closely matches my RESTful resources and routes. I wanted these tabs to have an active and passive state like you find in very common practice throughout the web. To do this I could take one of two approaches: let the client-side handle everything (read: JavaScript) or put some code in my controllers and helpers to setup the CSS as necessary. I chose the latter of the two because it was probably going to be easier and would degrade if JavaScript was broken or disabled. For more on the first approach you can visit this well documented article that details a very solid way to implement the technique. Here was what I had:

# apps/controllers/application.rb
class ApplicationController < ActionController::Base
  before_filter :set_active_tab
protected
  # Borrowed from http://rpheath.com/posts/304-tabbed-navigation-in-rails-refactored
  def set_active_tab
    # will default to controller_name if @active_tab
    # has not been set by another controller
    @active_tab ||= self.controller_name.to_sym
  end
end

Now the code for the helper which will spit out our navigable tabs:

# app/helpers/application_helper.rb
module ApplicationHelper
  def navigation(*links)
    items = []
    links.each do |link|
      if (controller.controller_name.to_sym == link)
        items << content_tag(:li, "#{link.to_s}", :class => "active")
      else
        items << content_tag(:li, link_to("#{link.to_s}", link))
      end
    end
    content_tag :ul, items
  end
end

With both pieces of code now in place we can now add a simple one-liner in our layout to render the unordered list of navigable tabs:

<!-- in apps/views/layouts/layout.html.erb -->
<div class="navbar">
  <%= navigation :students, :teachers, :classes, :schools %>
</div>

If a user were to navigate to the following path, /students/1, the StudentsController class would be called upon, which would run the before_filter :set_active_tab filter, which would set the @active_tab variable, and then subsequently render the layout with the students tab active:

<ul>
  <li class="active">students</li>
  <li><a href="/teachers">teachers</a></li>
  <li><a href="/classes">classes</a></li>
  <li><a href="/schools">schools</a></li>
</ul>

Posted by John on Jan 23, 2009

Post a comment...

Wrestling with polymorphic through associations and single table inheritance

I have hit a wall when developing some complex associations in an application I am building using polymorphic through associations for objects that utilize single table inheritance (STI). Lets say I have the following setup:

class Attachment < ActiveRecord::Base
  belongs_to :attachable, :polymorphic => :true
end

class Asset < ActiveRecord::Base
  has_one :attachment, :as => :attachable
end

class Photo < Asset
end

class Logo < Asset
end

class Video < Asset
end

And the following migrations:

create_table :attachments do |t|
  t.references :attachable, :polymorphic => true # => attachable_type, attachable_id
end

create_table :assets do |t|
  t.string :type                                 # => one of ['Asset', 'Photo', 'Logo', 'Video']
end

This all looks fairly straight-forward and you might actually expect this would work; well it does, sort of. However, there is one big problem. Let me illustrate this through the console:

Loading development environment (Rails 2.2.2)
>> a = Attachment.find_by_attachable_type 'Video'
=> #<Attachment id: 471080843, attachable_id: 969206778, attachable_type: "Video", created_at: "2009-01-03 01:21:05", updated_at: "2009-01-03 01:21:05">
>> a.attachable
=> #<Video id: 969206778, type: "Video", created_at: "2009-01-03 01:21:05", updated_at: "2009-01-03 01:21:05">
>> v = Video.find :first
=> #<Video id: 969206778, type: "Video", created_at: "2009-01-03 01:21:05", updated_at: "2009-01-03 01:21:05">
>> v.attachment
=> nil

As you can see in the last execution the Video object is not aware of any associations to the Attachment model even thought the Attachment object is aware of associations it has with the Video (or Asset) model. This means that the polymorphic association is not aware of subclasses within the STI schema. So next I wondered if at least the associations were working for the base class in the STI schema:

>> a = Attachment.find_by_attachable_type 'Asset'
=> #<Attachment id: 304596194, attachable_id: 128027076, attachable_type: "Asset", created_at: "2009-01-03 04:39:20", updated_at: "2009-01-03 04:39:20">
>> a.attachable
=> #<Asset id: 128027076, type: "Asset", created_at: "2009-01-03 04:39:20", updated_at: "2009-01-03 04:39:20">
>> a = Asset.find_by_type 'Asset'
=> #<Asset id: 128027076, type: "Asset", created_at: "2009-01-03 04:39:20", updated_at: "2009-01-03 04:39:20">
>> a.attachment
=> #<Attachment id: 304596194, attachable_id: 128027076, attachable_type: "Asset", created_at: "2009-01-03 04:39:20", updated_at: "2009-01-03 04:39:20">

Well that was at least mildly interesting; it does show that the association works at least for the base class. But how to get it to work for the subclasses would be no easy task, in that it would mean hacking up the Rails core (like these folks did). So I began to dig around a bit and lo and behold there has been a heated debate (and again) surrounding this particular subject. After reading through all of this I have determined that the official position of the maintainers is that this is the desired behavior. According to them and the Rails API I have to do some hacking to my Models:

Using polymorphic associations in combination with single table inheritance (STI) is a little tricky. In order for the associations to work as expected, ensure that you store the base model for the STI models in the type column of the polymorphic association. To continue with the asset example above, suppose there are guest posts and member posts that use the posts table for STI. In this case, there must be a type column in the posts table.

class Attachment < ActiveRecord::Base
  belongs_to :attachable, :polymorphic => true

  def attachable_type=(sType)
    super(sType.to_s.classify.constantize.base_class.to_s)
  end
end

class Asset < ActiveRecord::Base
  # because we store "Asset" in attachable_type now :dependent => :destroy will work
  has_many :attachments, :as => :attachable, :dependent => :destroy
end

class Photo < Asset
end

class Logo < Asset
end

class Video < Asset
end

Posted by John on Jan 10, 2009

Post a comment...

Cron to email you Twitter tweets

While Twitter has a few various ways to notify you with tweets of those you follow, I found it was not quite fitting my needs. I wanted to receive emails periodically, pool all the tweets in to a single email, and notify me of tweets of some accounts I did not follow. Make sure to read through all of the code and make necessary changes. Using this dated code as a foundation this is what I was able to quickly come up with [tweetdaemon.rb]:

#!/usr/bin/env ruby

require 'rubygems'
require 'twitter'
require 'fileutils'
require 'activesupport'
require 'net/smtp'

# Extend the twitter code to get what we need
class UserTimeline < Twitter::Base
  def user_timeline(id_or_screenname)
    statuses(request("statuses/user_timeline/#{id_or_screenname}.xml", :auth => true))
  end
end

# Daemon section
module Daemon
  WorkingDirectory = File.expand_path(File.dirname(__FILE__))  

  class Base
    def self.pid_fn
      File.join(WorkingDirectory, "#{name}.pid")
    end

    def self.daemonize
      Controller.daemonize(self)
    end
  end

  module PidFile
    def self.store(daemon, pid)
      File.open(daemon.pid_fn, 'w') {|f| f << pid}
    end

    def self.recall(daemon)
      IO.read(daemon.pid_fn).to_i rescue nil
    end
  end

  module Controller
    def self.daemonize(daemon)
      case !ARGV.empty? && ARGV[0]
      when 'start'
        start(daemon)
      when 'stop'
        stop(daemon)
      when 'restart'
        stop(daemon)
        start(daemon)
      when 'pid'
        pid = PidFile.recall(daemon)
        if !pid.nil?
          puts pid
        end
      else
        puts "Invalid command. Please specify start, stop or restart."
        exit
      end
    end

    def self.start(daemon)
      fork do
        Process.setsid
        exit if fork
        PidFile.store(daemon, Process.pid)
        Dir.chdir WorkingDirectory
        File.umask 0000
        STDIN.reopen "/dev/null"
        STDOUT.reopen "/dev/null", "a"
        STDERR.reopen STDOUT
        trap("TERM") {daemon.stop; exit}
        daemon.start
      end
    end

    def self.stop(daemon)
      if !File.file?(daemon.pid_fn)
        puts "Pid file not found. Is the daemon started?"
        exit
      end
      pid = PidFile.recall(daemon)
      FileUtils.rm(daemon.pid_fn)
      pid && Process.kill("TERM", pid)
    end
  end
end

class TwitterMonitor < Daemon::Base
  def self.start
    loop do
      # Load the config and get the twitter UserTimeline object
      twitter = UserTimeline.new("YOUR_USERNAME", "YOUR_PASSWORD")

      # Find all relevant tweets
      tweets = {}
      ["TWITTER_USER_ONE", "TWITTER_USER_TWO", "TWITTER_USER_THREE"].each do |user|
        twitter.user_timeline(user).each do |status|
          # Time is specified here and in seconds below
          if Time.parse(status.created_at) > 15.minutes.ago
            tweets[user] ||= []
            tweets[user] << status
          end
        end
      end

      if !tweets.empty?
        # Compose the body of the email
        message = "At #{Time.now} the following tweets were made:\n\n"
        tweets.each do |u, statuses|
          message += "[" + u + "]\n"
          statuses.each {|s| message += "- " + s.text + "\n"}
          message += "\n"
        end

        # Send the email
        self.send_email(
          "TWITTERALERTS@EXAMPLE.COM",
          "YOUR_EMAIL@EXAMPLE.COM",
          "TwitterAlerts for [#{Time.now}]",
          "#{message}"
        )
      end

      # Time to wait in seconds
      sleep 900
    end
  end

  def self.stop
  end

  def self.send_email(from, to, subject, message)
    # Setup the necessary fields
    message_body = <<END_OF_MESSAGE
From: <#{from}>
To: <#{to}>
Subject: #{subject}

#{message}
END_OF_MESSAGE

    # Send the message
    Net::SMTP.start('localhost') do |smtp|
      smtp.send_message message_body, from, to
    end
  end
end

TwitterMonitor.daemonize

Posted by John on Jan 10, 2009

Post a comment...

Destroying, deleting, and erasing old Twitter tweets

If you are like me you have twittered a lot and, before long, amass a ton of twitter updates (tweets). Most of these tweets, lets face it, are pointless dribble. Since most tweets are monitored or read within a few days of their posting the value of keeping them around is pretty useless. It’s time to clean house!

We could go through the manual process of deleting all of the tweets manually, but even still the archived tweets (older than 3 months) would remain available on Twitter’s servers (see for yourself and also see Twitter User Timeline REST API). Using the Twitter gem from John Nunemaker we can obliterate all of the old and archived tweets to entirely cleanse a twitter account easily. If you do not have an Apple Mac find a friend with one and come back (you could install Ruby on your Windows machine but I do not have time to go through this process here). Once you are in front of a Mac (or Linux), you should now have ruby, so you can cut straight to the chase and install the twitter gem:

Goto Macintosh HD / Applications / Utilities and open Terminal.app and execute the following:

sudo gem install twitter

After this installs and completes you should be setup for this next step. Open the text editor (Macintosh HD / Applications / TextEdit.app) and paste in the following, substituting YOUR_USERNAME and YOUR_PASSWORD for your Twitter username and password:

#!/usr/bin/env ruby

require 'rubygems'
require 'activesupport'
require 'twitter'

# Change your username and password here
twitter = Twitter::Base.new('YOUR_USERNAME', 'YOUR_PASSWORD')

# Use 10 years since we know twitter hasn't been around that long :o)
twitter.timeline(:user, :since => 10.years.ago).each do |s|
  twitter.destroy(s.id)
  puts "Deleted tweet: [#{s.id}] \"#{s.text}\" (#{s.created_at})"
end

After you have this pasted in save this to a file called “twitter-delete.rb” on the Desktop (or download this one: twitter-delete.rb). Now go back to the Terminal program and enter in the following: ruby ~/Desktop/twitter-delete.rb and hit enter. This will delete around 20 tweets and then return you back to the prompt. You will be able to execute it again and again and again until you get an error. This is because Twitter limits how many operations you can perform within a given timeframe. Wait around, go drink a beer, watch some TV and come back and execute it again a few more times. Rinse. Repeat. Eventually all of you tweets will be gone and you are done!

Posted by John on Jan 06, 2009

Post a comment...