rod mclaughlin


Class variables in Ruby on Rails (05 may 09)

Rails has a cute method, cattr_accessor, which creates class-wide variables

These variables begin with '@@' and are for storing things you only need one of across your whole application

My http://pdxspurs.com has a 'News' section which uses the Guardian Ruby API to search the files of the Guardian newspaper in London for news about football - http://www.guardian.co.uk/open-platform

It takes a while to search, and the news doesn't change very often, so I wanted to set it up so the news is only gotten once per day

I store the date, and if the date has changed since the last time anyone looked at the news, I get new news

The funny thing is, it didn't work in development - it always reloaded the news, even if the date hadn't changed

I figured out why - all code is reloaded in development mode every time you hit 'Reload' in your browser

This means that the class-wide variables were reset, so the code thought the date was new every time

So I ran it in production mode, and t worked - it only loaded the news once

I guess I'll have to wait 'til tomorrow to find out if it really worked, but here's the code, guardian.rb:

# Part of the Guardian API
require 'custodian'
# sudo gem install libxml-ruby
require 'xml/libxml'

class Guardian
ONE_DAY = 60 * 60 * 24
NUM_DAYS_BACK = 4
HEAD = "//content/headline"
BODY = "//content/type-specific/body"
DATE = "//content/publication-date"
ENDING = "\n</content>"
MAX_RESULTS = '25'

cattr_accessor :results
# Create @@results and @@last_time_got
cattr_accessor :last_time_got

# Formats date num_days ago: 'Sun Oct 05 11:00 2008' -> '20081005'
def Guardian.display_date(num_days)
t = Time.now - (num_days * ONE_DAY)
d = t.to_s
l = d.length
y = d[l-4..l-1]
d = d[8..9]
m = t.month.to_s
m = '0' + m if m.length < 2
y + m + d
end

def Guardian.result(which_one)
Guardian.set_results
which_one = rand(@@results.length) if which_one.nil?
return @@results[which_one.to_i]
end

def Guardian.results_html
Guardian.set_results
return 'No news is good news' if @@results.length < 1
html = ''
i = 0
@@results.each do |r|
arr = r.split("\n")
tit = arr[0]
tit.gsub!('&nbsp;', '')
html += "<a href='" + "/news/" + i.to_s + "'>" + tit + "</a>" + "\n<br/>"
i = i + 1
end
return html
end

private
def Guardian.set_results

Custodian.api_key = 'THIS IS A SECRET'

@@last_time_got = 'nil' if @@last_time_got.nil?

@date = Guardian.display_date( NUM_DAYS_BACK )
@@results = [] if @@results.nil?
# Get new results just once every day
return if @@results.length > 1 and @date == @@last_time_got
@@last_time_got = @date

@articles = Custodian::Article.find(:all,
{ :q => "Tottenham", :filter => '/football',
:after => @date, :content_type => 'article',
:count => MAX_RESULTS })
if @articles.length < 1
@@results = ["Well, they say no news is good news"]
return
end

@@results = []
xml_str = ''
@articles.reverse.each do |a|
begin
# Correct the XML
xml_str = a.xml.to_s
xml_arr = xml_str.split("\n")
# Strip off the Grauniad's incorrect ending - '</publication-date>...</content>'
xml_arr.delete_at(xml_arr.length - 1)
xml_str = xml_arr.join("\n")
# Tack on the happy ending
xml_str = xml_str + ENDING
doc = XML::Parser.string( xml_str ).parse
date = ''
head = ''
body = ''
doc.find(HEAD).each do |node|
head = node.content
head = "<b>" + head + "</b>"
end
doc.find(DATE).each do |node|
date = node.content
date = "\n<br/><b>" + date[0..9] + "</b>"
end
doc.find(BODY).each do |node|
body = node.content
# Try to add some formatting
body.gsub!( ". ", ".\n<br/><br/>")
end
@@results << head + date + "\n<br/><br/>" + body if not (body.strip.empty?)

rescue Exception => err
@@results = [err.message]
end
end
end

end



Back
Portland London