2008-03-19 20:00:00 -0400
Throughout the course of building Tempo, we’ve relied heavily on software written by other people and made freely available. It’s worth doing a quick run-down to give credit where credit is due.
Ruby On Rails web application framework
No surprise there, right?
PostgreSQL relational database
Our favorite relational database system, Postgres is the most mature of the free systems out there, has the best feature set, and has quite a bit in common with Oracle. Highly recommended.
FamFamFam icons
Everybody needs icons, we’re big fans of the Silk set.
Acts As State Machine Rails plugin
This plugin by Scott Barron allows an ActiveRecord model to act as a finite state machine rather elegantly.
HAML & SASS HTML & CSS templating
Gone are the days when we painfully labor over HTML templates thanks to this great Rails plugin by Hampton Catlin. We can’t live without it now.
gchartrb Google Charts for Ruby
Those charts in Tempo look really good, but they’re largely the work of Google’s Chart API and this wrapper library for Ruby written by deepak.jois and aseemtandon. All we had to do was write some clever SQL and voila!
Active Merchant Rails plugin
Definitely the easiest way to integrate with a payment gateway in Rails. Also provides an awesome layer of abstraction in the event that we decide to switch gateways – we won’t have to do a major rewrite of the code in our site that handles payment processing.
Ruport Ruby Reports
Ruport made it incredibly easy for us to provide Excel/CSV and PDF exports from within Tempo’s WYSIWYG reporting interface.
RESTful Authentication Rails plugin
Very handy plugin by Rick Olson for quickly setting up an authentication system for your users that includes an activation step.
Thanks everyone for making these valuable open source contributions to make software like Tempo possible.
2008-03-10 20:00:00 -0400

This took enough of my time that I think it’s worth a blog post. In Tempo you’ll see a familiar paradigm in the time reporting interface (the main screen): a list of editable items in a row, each with the same set of controls. They are editable via AJAX calls, so you can open a number of them for editing at once.
Now, when you’re looking to add javascript observers to these elements (to do automated things like type ahead, etc), you have to assign them unique id attributes, usually based on the object id. While it’s easy to add an :id attribute to any of the usual tag helpers in Rails, it doesn’t work like this with date_select:
%table
%tr.s1
%td{:colspan => '2'}= project_select(f, @current_user.projects, entry)
%td{}
= f.date_select :occurred_on, :order => [:month, :day, :year], :start_year => 2007, :use_short_month => true, :use_short_year => true, :id => "#{entry.id}"
= popup_calendar("entry_#{entry.id}_occurred_on", entry.occurred_on)
Our javascript calendar is expecting a unique ID on the date select drop downs so that it can set their values. But, that’s not the case, the id of each drop down is generated automatically from the name attribute, thus:
<select id="entry_occurred_on_2i" name="entry[occurred_on(2i)]">
<option value="1">Jan</option>
<option value="2">Feb</option>
<option value="3" selected="selected">Mar</option>
...
</select>
Makes sense, really, since the separate drop downs are being generated to be re-assembled when posted, and what else to id them?
The trick to getting unique id’s into these elements was a monkey patch I put in config/initializers/date_helper.rb:
module ActionView
module Helpers
module DateHelper
def name_and_id_from_options(options, type)
options[:name] = (options[:prefix] || DEFAULT_PREFIX) + (options[:discard_type] ? '' : "[#{type}]")
name = options[:name].gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
unless options[:id].nil?
options[:id] = name.sub(/_/, "_#{options[:id]}_")
else
options[:id] = name
end
end
end
end
end
Pretty close to the original, it preserves the original behavior, but respects your inclusion of the :id attribute in the options you pass to date_select. Now our id’s look like:
<select id="entry_2013_occurred_on_3i" name="entry[occurred_on(3i)]">
We found a similar problem with the auto_complete plugin – doesn’t work when there are more than one active on the screen at once, due to non-unique id’s. That required a bit more work. First a monkey-patch in config/initializers/auto_complete_macros_helper.rb:
module AutoCompleteMacrosHelper
def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {}, object_id = nil)
if object_id.nil?
field_id = "#{object}_#{method}"
else
field_id = "#{object}_#{object_id}_#{method}"
tag_options[:id] = field_id
end
(completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
text_field(object, method, tag_options) +
content_tag("div", "", :id => "#{field_id}_auto_complete", :class => "auto_complete") +
auto_complete_field(field_id, { :url => { :action => "auto_complete_for_#{object}_#{method}" } }.update(completion_options))
end
end
Adding an optional parameter on there seemed like the easiest thing to do for the moment, creates for only a slight change in our form:
= text_field_with_auto_complete :entry, :tag_s, {:size => 40}, { :indicator => "entry_#{entry.id}_tag_s_form_loader", :frequency => 0.4, :tokens => ' ' }, entry.id
I’m tempted to make it work off of whatever shows up in tag_options[:id] but this will do for now.
2008-03-04 19:00:00 -0500
I’m not sure how I came across this article, I think maybe it was linked off the Google Reader blog, but Cedric has some interesting things to say about Test-Driven Development. I would just like to take issue with one small assertion of his:
Keep in mind that functional tests are the only tests that really matter to your users. Unit tests are just a convenience for you, the developer. A luxury. If you have time to write unit tests, great: they will save you time down the road when you need to track bugs. But if you don’t, make sure that your functional tests cover what your users expect from your product."
I can tell you from experience that having poor unit testing can make your functional testing a total nightmare because, in a sense, you may have good functional code working with bad “data”, in this case the unit code. Garbage in, garbage out, as was hammered into my head back in school. You can end up wasting more time, in this regard, and unit testing is no longer a thing of luxury, but something that could have saved you a few hours of head scratching.
2008-02-07 19:00:00 -0500
Over at Ryan’s Scraps, in a post about the new TimeWithZone functionality in edge Rails, there are a pair of comments that I want to highlight. A fella named Ben asks “Couldn’t this be pushed deeper so that current_user.registered_at is a TimeWithZone?”
Then there’s a response from the main guy who developed the TimeWithZone functionality, Geoff Buesig, in regards to how they intend it to be used (and with a bunch of other neat and helpful notes that you should check out):
1.TimeWithZone is similar to the Duration class, in that, you should never need to create an instance directly—in the TWZ case, you’ve got the #in_time_zone, #in_current_time_zone, #change_time_zone and #change_time_zone_to_current methods on Time and DateTime instances that will handle that for you.
So, for example, you can do this:
current_user.registered_at.in_current_time_zone
… and the result will automatically be wrapped in a TimeWithZone
What Ben is asking for, and what Geoff seems to be distancing himself from, is exactly what we here at Zetetic would find incredibly useful: the ability to harness our database backend’s time zone support, PostgreSQL’s ‘timestamp with time zone’.
Here’s the deal. PingMe was designed for users around the globe so it supports time zones. We set it up so that all timestamps (:datetime) were stored in UTC in the database, and converted to the user’s local time on display. We also convert from the user’s local time on datetime input. Nothing fancy or unexpected there, really. And hey, the tzinfo gem supports DST, so we’re good, right?
Well, PingMe is a scheduling system. It has a scheduler daemon that’s constantly checking to see which pings need to be sent out, then it creates outbound events for the dispatcher daemons to deliver. Never mind the terminology, the important thing here is that it’s working in UTC. And that Rails is storing the timestamps in Postgres’ default TIMESTAMP WITHOUT TIME ZONE data type. Here’s an illustrative query:
def lock_a_block(type_name)
before = (Time.now.utc).to_s(:db)
ActiveRecord::Base.connection.execute(
<<-END_OF_SQL
UPDATE events SET dispatcher = '#{@name}'
WHERE id IN (
SELECT e.id FROM
(( events e INNER JOIN targets t ON e.target_id = t.id )
INNER JOIN pings p ON e.ping_id = p.id)
INNER JOIN target_types tt ON t.target_type_id = tt.id
WHERE
tt.const = '#{type_name}'
AND
(
(e.dt_when < '#{before}' AND e.status = '#{Event::STATUS_PENDING}')
OR
(e.retry_at < '#{before}' AND e.status = '#{Event::STATUS_RETRY}')
)
AND e.dispatcher IS NULL
AND t.activated_at IS NOT NULL
AND (p.is_done = 'f' OR p.is_done IS NULL)
AND (p.deleted_at IS NULL)
ORDER BY
e.dt_when ASC
LIMIT #{@block_size}
);
END_OF_SQL
)
end
So the app is providing a UTC timestamp for the before variable, and the timestamps are in UTC in the database. What happens when DST begins or ends? Nothing changes. Everything is sent at the set time, for UTC. So a ping set for 5pm EST was stored at 12:00 UTC, and when 5pm shifts an hour for EDT, that ping is still stored at 12:00 UTC and will be sent either an hour early or an hour late, depending on the circumstance.
The only way we could break this up to work off the time zone setting on the user model is to execute separate queries for all of our users all the time joining against their timezone. Ridiculous! And following Geoff’s notion of things above, it’s just not a clean solution — storing the ping’s time without the time zone is decidedly inaccurate. I hate to say it.
I think the best solution is not to store in UTC here, but to store as a timestamp with time zone. I realize that sounds like an impure solution, but it’s not: PostgreSQL actually stores the data in UTC and can do all sorts of magical conversions for us. We could still use the code above and work in proper UTC, but any DST on the timezone would be respected:
WHERE ... e.dt_when AT TIME ZONE 'UTC' < '#{before}'
And that is why I hope Geoff changes his mind, because we do need TimeWithZone as a data type in Rails, or perhaps a col definition that will provide a TimeWithZone instead of Time objects:
col.datetime :col_name, :with_time_zone => true
As an aside, we don’t leave PingMe users to hang when DST rolls around, we update the relevant time stamps via SQL. But I would like to get us to a better solution. Being able to store TimeWithZone would do just the thing.
2007-11-07 19:00:00 -0500
Here at Zetetic we do a lot of logging, and a lot of looking at logs. In particular, we have a couple of daemon processes implemented in Rails for PingMe that handle our message queueing and parsing of incoming messages (when you reply to your pings or create new ones by remote). If you have any experience with message queueing systems, you’ll recall that these are not easy things to maintain, and require access to really good diagnostics. And if you are familiar with Rails you’ll recall that there are no time-stamps prepended to the log messages, making it very difficult sometimes to track down what happened when.
I did a quick bit of poking around and came across this fantastic article with a number of tips in terms of logging. Their solution for the issue of formatting the messages (so that you can have timestamps) is to subclass Logger, and instantiate that.
However, we have our own Loggers all over the place, in our daemons, they use the Logger class which has been patched by Rails to have that timestamp-less format. What we do from there is replace the Rails logger instance with our own (there are a few reasons for this, having to do with forking processes, resources, and the nature of daemons that I don’t want to get into), which works beautifully:
logger = Logger.new("#{config[:log_dir]}/#{config[:name]}.log", 'daily')
unless config[:log_level].blank?
begin
logger.level = Logger.const_get(config[:log_level])
rescue StandardError => e
logger.level = Logger::INFO
logger.error "An exception occurred while setting log level to #{config[:log_level]}, setting to INFO. Exception: #{e.message}"
end
else
logger.level = Logger::INFO
end
logger.info "Initialized log @ #{Time.now.utc} with log_level #{logger.level.to_s}"
logger.info "Starting up Dispatcher #{config[:name]}..."
# over-ride the active record logger (which would be closed now)
ActiveRecord::Base.logger = logger
ActionMailer::Base.logger = logger
I really don’t feel like subclassing Logger, I just want to adjust the default behavior, since we’re using the same loggers everywhere. So I opened up config/environment.rb, and at the bottom of it, added this:
# re-patch logger to restore format patched out by Rails
class Logger
def format_message(severity, timestamp, program, message)
"#{timestamp.to_formatted_s(:db)} #{program}: [#{severity}] #{message}\n"
end
end
Works fantastic! Thanks to Maintainable Software for their post.