Tempo: Launch!

2008-03-17 20:00:00 -0400

Today marks the launch of our newest app, Tempo, a time tracking app for stats addicts, consultants, and anybody involved in professional services and billing time. The “beta” tag is gone, the gloves are off, the doors are open. Check out the tour to find out what it’s all about, sign up for a free account, let us know what you think (e-mail us: support AT zetetic DOT net). As our regulars know, we love hearing your suggestions.

Tempo: That's the Way We Get By - Part 1

2008-03-12 20:00:00 -0400

We built our newest product, Tempo, based on some of our own needs as a business here at Zetetic. Over the next couple of weeks (if we have the time!) we’re going to start talking about some of those reasons to give you a feel for how Tempo works and what it can do for your business.

Now, I’m basically a worker bee, in the sense that I’m not management here at Zetetic. I work on a number of projects that are disparate in terms of technology and clientele, and my day is often split up working on sub tasks within those projects and contracts. I generally use Tempo as the day goes on, whenever I complete a project or switch tracks.

The bookmarklet makes this particularly easy in that I don’t have to go to the site, bring up the new entry form, etc. I just click a button in my browser, I’m still logged in from who knows how long ago, I tap in the hours, and some tags describing what I did.

time entry

Usually around the end of the week I’ll log into the site and take a look at a couple of the charts. I generally like to review what projects I’ve spent my time on:

Project hours small

click to embiggen

The chart makes it very easy to see where I’ve been spending the most of my time, and where I might want to put a little more in.

Also instructive is taking a look at a break down of hours by tag:

tag hours small

click to embiggen

Here I can see that the lion’s share of my time so far this week has been spent on rails development, and that ‘recruitment’ could use a little love, because it’s on our minds.

Finally, I find myself looking at my total time trend for the week, or the month, to get a feel for, well, how productive (or not) I’ve been:

time trend small

click to embiggen

I always like to see that my time trend for the month so far has gone up and not down! But basically I should see a fairly steady trend, or I can expect some abnormal billing at the end of the month.

Setting Rails Date Helpers Via Javascript

2008-03-10 20:00:00 -0400

Tempo Date Selection

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:

%td{:colspan => '2'}= project_select(f, @current_user.projects, entry)
= 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>

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]}_")
options[:id] = name

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}"
field_id = "#{object}_#{object_id}_#{method}"
tag_options[:id] = field_id

(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))

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.

On the usefulness of unit testing

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.

PingMe - New Support for Pester-Repeat Pings

2008-02-21 19:00:00 -0500

Pester Repeat

Suppose you have something really important to do every week, like checking that your car is still legal for street cleaning (raise your hand if you’ve been towed). You’d probably be tempted to set up a ping that both repeats and pesters. This way you’d get pestered about it each day to make sure that you don’t forget, and PingMe would reschedule it for next week once you completed the task.

In the past, this feature has been missing from PingMe. When you received a pestering ping that also has a repeat schedule, and you replied with ‘off’ or ‘done’ or ‘stop’ (or ‘ok’ or ‘okay’) to stop the pester, say from your phone, the ping would be marked as done, and it would turn off. For good. But most people really set pester & repeat pings up so that after they turn off the pester for today, they’ll still get the ping tomorrow (or whenever the next scheduled repeat is, if you follow me).

Well now you have options! We’ve changed the behavior of two of the stop words so that they only stop the pester of a ping and not the repeat. As of this morning, replying to a ping with ‘ok’, ‘okay’ or ‘done’ will stop only the pester of a pestering & repeating ping. Replying with ‘stop’ or ‘off’ will turn the ping off as before.

To sum it up, once you go and move your car to the other side of the street, you can reply to the ping ‘ok’ and it will stop bothering you until tomorrow, when it’s time to move your car again.

==> /var/log/pingme/PingMeReceiver.log <==
[INFO] change: 17827, Preparing
[INFO] change: 17827, stripping message part 0...
[INFO] change: 17827, Processing.
[INFO] change: 17827, found a stop message on this line: Ok
[INFO] change: 17827, this is a stop message for ping 16427,
[INFO] change: 17827, user requests to stop pester
[INFO] change: 17827, nagging and recurring ping, clearing events for reschedule

It’s alive!