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...
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...
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...
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...
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...