Categories: archive |code

Site wide announcements in Rails using jQuery (jGrowl)

I like it when a site tells me that they’re going to do some maintenance, so I wanted to implement similar features for a web application that I’m working on. I based my announcements on Ryan Bates’ Railscasts episode 103 and updated code based on avarhirion’s post on the Rails Forum. The major changes are the added changes for cookie support, graceful degradation, and jQuery (jGrowl).

If you’d rather, check out all the files on Github.


class Announcement < ActiveRecord::Base
  named_scope :active, lambda { { :conditions => ['starts_at <= ? AND ends_at >= ?',,] } }
  named_scope :since, lambda { |hide_time| { :conditions => (hide_time ? ['updated_at > ? OR starts_at > ?', hide_time.utc, hide_time.utc] : nil) } }
  def self.current_announcements(hide_time)


- unless current_announcements.empty?
    -for announcement in current_announcements
      .announcement{:id => 'announcement_' +}
        =h announcement.message
        = link_to "Hide Announcements", hide_announcements_path, :id => 'hideAnn'


map.hide_announcements '/hide_announcements', :controller => 'javascripts', :action => 'hide_announcements'

Here’s the first of the important ones: javascripts_controller.rb. Note that I’m setting the session and a cookie. Also see my TODO. I’d like to set the expiration date of the cookie to the oldest expiring active announcement.

class JavascriptsController < ApplicationController
  def hide_announcements
    time =
    set_session time
    set_cookies time
    respond_to do |format|
      format.html { redirect_to(root_path) }
      format.js { redirect_to(root_path) }

    def set_session(time)
      session[:announcement_hide_time] = time
    #TODO change expiration time to be the expiration
    #date from the list in current_announcements
    def set_cookies(time)
      cookies[:announcement_hide_time] = {
        :value => time.to_datetime.to_s,
        :expires => time.next_week

In the application_helper.rb, we check the session first and set the announcements, otherwise, we check the cookies.

def current_announcements
  unless session[:announcement_hide_time].nil?
    time = session[:announcement_hide_time]
    time = cookies[:announcement_hide_time].to_datetime unless cookies[:announcement_hide_time].nil?
  @current_announcements ||= Announcement.current_announcements(time)

Finally, to make it with the cool-kids, we need to add some jQuery. So I decided to use the jGrowl plugin because it’s awesome. This code goes in your application.js file (somewhere in $(function){ //here })

//jgrowl announcements. ajax GET to hide ann/store cookie
$.jGrowl.defaults.closer = false;
$("#announcements_box .announcement").each(function(){
  $.jGrowl(this.textContent,{ sticky:true, close:function(e,m,o){hide_announcements();} });

function hide_announcements(){

  return false;

Obviously, you’ll need to include jGrowl js and css files.

I like this solution because it provides a great looking way to do site-wide announcements. The solution degrades gracefully if the user does not have javascript enabled. Finally, by saving to a cookie, users can leave, close their browsers, and come back as the please and not see the same announcements over and over again.

I’d like to leave you with a couple of specs that I wrote. I attempted to do TDD/BDD and do the specs first, but it was tough. I didn’t get too far, as I was having some cookie/session problems. They’re in some serious need of help, so if you know cool stuff, help me out in the comments, thanks.


#TODO these are weak/inaccurate specs.  These are more-or-less stubs
describe "should find the current announcements" do
  it "when the session['announcement_hide_time'] is not nil" do
    session['announcement_hide_time'] =
    current_announcements.should == Announcement.current_announcements(session['announcement_hide_time'])

  it "when the session['announcement_hide_time'] is nil and cookies['announcement_hide_time'] is not nil" do
    session['announcement_hide_time'] = nil
    time =
    request.cookies['announcement_hide_time'] = {:value   => time.to_datetime.to_s,
                                                  :expires => time.next_week}

  it "when the session['announcement_hide_time'] and cookies['announcement_hide_time'] are not nil" do
    session['announcement_hide_time'] = nil
    cookies['announcement_hide_time'] = nil
    current_announcements.should == Announcement.current_announcements(nil)


#TODO these are weak/inaccurate specs.  These are more-or-less stubs
describe "the current_announcements" do
  it "should return announcements that are active" do
    time = == Announcement.find(:all, :conditions => ['starts_at <= ? AND ends_at >= ?', time, time])

  it "should return announcements since a time provided" do
    time =
    Announcement.since.should == Announcement.find(
          :conditions => (time ? ['updated_at > ? OR starts_at > ?', time.utc, time.utc] : nil))

  it "should return announcements since a time provided (nil)" do
    time = nil
    Announcement.since.should == Announcement.find(
          :conditions => (time ? ['updated_at > ? OR starts_at > ?', time.utc, time.utc] : nil))

Any feedback is appreciated.