Posted: Jun 27, 2008
by Billy Gray
Tagged
We use the Rails auto_complete plugin in both PingMe and Tempo, and while it’s incredibly handy, it takes a little bit of hacking to use it on a page where it will be used more than once. In the main time screen on Tempo any of the entries can be opened for editing, meaning more than one can be open at once, and each has its own auto complete field for tags:

Similarly in PingMe, we’ve got a list of pings on the screen, each a potential edit form (more than one can be edited at once), with auto complete on the tags field:

In this situation, when each form has the text_field_with_auto_complete in use, each has the same DOM ID: object_method, so ping_tags in PingMe and entry_tags in Tempo. Screwy things happen when you try to remotely update a DOM ID used more than once on a page, and those screwy things tend to change based on your browser, but basically the auto_complete plugin out-of-the-box will not work in this scenario.
We got around this by doing a bit of monkey patching to the auto complete plugin. This is what the initial text_field_with_auto_complete method looks like inside the plugin’s auto_complete_macros_helper.rb:
def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
(completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
text_field(object, method, tag_options) +
content_tag("div", "", :id => "#{object}_#{method}_auto_complete", :class => "auto_complete") +
auto_complete_field("#{object}_#{method}", { :url => { :action => "auto_complete_for_#{object}_#{method}" } }.update(completion_options))
end
What we do is add an extra, optional parameter to this method that accepts an id, that we then patch in as the dom id we’ll use:
module AutoCompleteMacrosHelper
def text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {}, object_id = nil)
field_id = (object_id.nil?) ? "#{object}_#{method}" : "#{object}_#{object_id}_#{method}"
(completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
text_field(object, method, tag_options.merge({:id => field_id})) +
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
Now, on the form/template itself, we use this to generate the text field and type ahead div (we’re using HAML, not RHTML):
= text_field_with_auto_complete :entry, :tag_s, {:size => 50}, { :indicator => "entry_#{entry.id}_tag_s_form_loader", :frequency => 0.4, :tokens => ' ' }, entry.id
= image_tag 'loading.gif', {:id => "entry_#{entry.id}_tag_s_form_loader", :style => 'display: none;'}
.auto_complete{:id => "entry_#{entry.id}_tag_s_auto_complete", :style => "display: none;"}
You’ll notice that each dom id is of the format entry_#id_tag_s_*.
In our controller, we have a few things that we need, nothing that complex:
auto_complete_for :entry, :tag_s
def auto_complete_for_entry_tag_s
unless params[:entry].nil? || params[:entry][:tag_s].blank?
tags = @current_user.tags.collect{|tag| tag.name.downcase.index(params[:ping][:tag_s].downcase) == 0 ? tag.name : nil}.compact
render :partial => 'autocomplete', :locals => { :items => tags }
end
end
Finally, we create a partial called ‘_autocomplete.haml’ that cranks through the items when the suggestions are displayed:
%ul.autocomplete_list
- items.each do |item|
%li.autocomplete_item= h item
Happily, this isn’t very different from the way the plugin recommends you do things, but allows you to support more than one field on the page at once when you have multiple instances of an object. I’m actually a bit surprised that there isn’t an inherent facility in the plugin for this, but it’s good learning for anyone looking to see how it’s done.
I should note that the above doesn’t denote how we really select out the set of tags for a user in Tempo, but it’s a simple example of how one might do it ;-)
Posted: Jun 26, 2008
by Billy Gray
Tagged pingme, tempo
With the new smart timers, I no longer need PingMe to remind me to sum up my time for the day. Thanks, old buddy!
(Although the street cleaning reminders are still pretty clutch…)
Posted: Jun 25, 2008
by Billy Gray
Tagged tempo
We’re very excited to officially announce the latest Tempo upgrade.
This time around we decided to try something big. Really big. A game changer. Something that would begin to redefine the way people track time. We decided to reinvent the traditional time tracking timer in a way that doesn’t suck. And thus, the Workstream release was born.
As the name implies worktreaming borrows heavily from the concept of lifestreaming. It’s not a new term but we have our own take on it: you “stream” updates directly into Tempo as you start working on tasks. We track exactly how long you spend on each item as you make updates. There’s no need to explicitly enter hours or manually track time – we do all the work for you. You get an exact log of everything you’ve worked on, tagged, assigned to projects, shareable with colleagues, accessible through RSS, and ready for reporting.
Imagine using Twitter and messaging Tempo as you change streams and switch between projects. Or, use our new Dashboard Widget, the Bookmarklet, the Tempo application proper, your mobile phone, email, whatever. How you stream is up to you but the fluid approach leaves you free to think about the task at hand.
!{margin: 10px 0; border: 4px solid #EEE;}/assets/2008/6/25/ws-timers.png!
Visit our documentation for more details on how workstream timers work.
Honorable Mentions
In addition to the core workstreaming functionality we’ve added a ton of other supporting functionality to Tempo.
iPhone
!>{margin: 0 0 0 10px; border: 4px solid #EEE;}/assets/2008/6/23/iPhoneyScreenSnapz007.png!
We’ve vastly improved the iPhone interface from our previously release. Now it looks and feels just like an iPhone app ought to, and provides you with the new timing features and a view into time you’ve already logged. Check it out on you iPhone at m.keeptempo.com.
Dashboard Widget
!<{margin: 0 10px 0 0; border: 4px solid #EEE; }/assets/2008/6/25/miniwidget.png!
Users of Mac OS X can work really seamlessly with Tempo using our new Dashboard widget. A command-line interface and a view of recent time entries gives you the most important features.
XML API
!>{margin: 0 0 0 10px; border: 4px solid #EEE;}/assets/2008/6/23/TextMateScreenSnapz002.png!
Tempo now sports it’s own API. Use it to integrate with internal systems or 3rd party services.
Delegated Management & Proxy Time Entry
Tempo now makes managing teams even easier. First, any member of your project can be made a manager (including users on free account plans), allowing them to view the dashboard statistics, and manage the team roster. Managers can also perform “proxy time entry”, allowing them to enter and edit time entries on behalf of other people on the team. This rocks for groups where only one person is responsible for handling all the time entry.
Stream-lined Interface
!>{margin: 0 0 0 10px; border: 4px solid #EEE; }/assets/2008/6/25/ws-commandline-typeahead.png!
We over-hauled Tempo’s interface to make it more intelligent and easier to work with. The ‘Add Entry’ tab provides a command-line interface for quickly entering time records or kicking off timers complete with type-ahead lookup when you start entering your tags!
!{margin: 10px 0; border: 4px solid #EEE;}/assets/2008/6/25/webform.png!
The full web interface is sparser by hiding optional fields.
Lock and Unlock
Project managers can now easily lock and unlock individual time entries.
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; }/assets/2008/6/25/ws-lock.png!
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; }/assets/2008/6/25/ws-unlock.png!
Smart Defaults
The web entry form remembers the last project you billed to, and whether you started a timer or not, saving you time when creating new records.
!{margin: 10px 0 0 0; border: 4px solid #EEE;;}/assets/2008/6/25/ws-form-memory.png!
Enjoy!
We hope you’ll find some of Tempo’s new features useful and we look forward to your feedback!
Posted: Jun 25, 2008
by Billy Gray
Tagged
Git log commits remind me of summer afternoons. Like last week.
(Guess who hasn’t updated his time in a bit?)
((The new timer helps keep me straight nowadays!))
Posted: Jun 23, 2008
by Stephen Lombardo
Tagged
We’ll be releasing a major upgrade to Tempo tonight, June 23rd between 10 PM EST and 11 PM EST. Tempo will be offline while we perform the latest batch of updates.
We’ve been calling this the “workstream release” internally because it quite literally lets you stream information into Tempo as you start working on it. As you make updates we intelligently keep track of how long you spend on each item. No need to explicitly enter hours, no need to manually track time, and it works from the site, bookmarklet, Twitter, SMS, Email, iPhone and even a brand new Tempo Dashboard Widget.
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; width: 200px;}/assets/2008/6/23/tour-entry-text.png!
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; width: 200px;}/assets/2008/6/23/tour-entry-timer.png!
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; width: 200px;}/assets/2008/6/23/TextMateScreenSnapz002.png!
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; width: 200px;}/assets/2008/6/23/iPhoneyScreenSnapz007.png!
!<{margin: 10px 10px 0 0; border: 4px solid #EEE; width: 200px;}/assets/2008/6/23/PreviewScreenSnapz006.png!
<div “style=”clear:both;">
In addition to the core workstream functionality there are a host of other improvements which we’ll get to in detail after the release.
Posted: Jun 13, 2008
by Billy Gray
Tagged
A very simple open-source support ticket system, transparently integrated with e-mail, that isn’t written in Python would be a fantastic thing to have and use.
Posted: Jun 12, 2008
by Billy Gray
Tagged rails
While doing a bit of work on Tempo recently, we found we had the need to provide a union’ed set of Active Record associations on a model. We already had the code in place in acts_as_network to do something very similar via the UnionCollection class, so it was a simple step to create ActsAsUnion within the same plugin.
Here’s an example of what we mean and how it works:
class Person < ActiveRecord::Base
acts_as_network :friends
acts_as_network :colleagues, :through => :invites, :foreign_key => 'person_id',
:conditions => ["is_accepted = 't'"]
acts_as_union :aquantainces, [:friends, :colleagues]
end
In this case a call to the aquantainces method will return a UnionCollection on both a person’s friends and their colleagues. Likewise, finder operations will work accross the two distinct sets as if they were one. Thus, for the following code:
stephen = Person.find_by_name('Stephen')
# search for user by login
billy = stephen.aquantainces.find_by_name('Billy')
both Stephen’s friends and colleagues collections would be searched for someone named Billy.
To obtain acts_as_union, simple install the newest version of the acts_as_network plugin and use as shown above. The acts_as_union method does not accept any options.
Posted: Jun 07, 2008
by Stephen Lombardo
Tagged business, buzz
I’m fresh out of the Seed Conference and it was, hands down, the best event I’ve been to in years. Imagine a full day of intelligent and down-to-earth entrepreneurs talking honestly about their businesses and communities, completely immersing the audience in creative opinions and ideas. I walked out Crown Hall feeling energized.
I don’t think that a play-by-play write up of each talk would even do it justice. Instead, here’s a selection of quotes from the various presenters that really speak to the overall vibe and flavor of the event.
Business
“Deliver more value than you charge for it” – Jason Fried on pricing software
“If Content is King, Marketing is Queen – and we all know who runs the house” – Gary Vaynerchuk
“We don’t have a 5 year plan, or even a 5 minute plan” – Jim Coudal on the ineffectiveness of long term planning
“Build relationships with clients that listen to you, and that you CHOOSE to work with” – Carlos Segura
“It’s about bleeding out of your eyes and working your face off” – Gary Vaynerchuk on how hard you need to work to be successful
“It’s better to fail in a group because you can all blame someone else together” – Jim Coudal when asked if it’s better to fail alone or with a team
Community
“I don’t consume. I put out. I PUT OUT.” – Gary Vaynerchuck on why he’s successful within his community
“If they come you will build it” – Jim Coudal on putting the community before the product
“You may be the client and paying the bills but you are irrelevant to this picture” – Carlos Segura on the need to connect with the intended audience as a designer, not the client
“Your app is having a conversation with your user” – Jason Fried
“I didn’t make the front page of Digg for 18 months” – Gary Vaynerchuk on being patient in the beginning of a venture
“I’d rather talk to the real people” – Jason Fried on why 37signals doesn’t track analytics in their applications
Hiring and the workplace culture
“Its about giving the right people the right opportunities” – Carlos Segura
“Make it fun. In a culture ruled by fear no amount of money can make it right.” – Jim Coudal
Why NOT to pay attention to the competition
“Every minute you spend on your competition is one minute you’re not spending on yourself” – Gary Vaynerchuk
“The more you look AT your competition the more you will look LIKE your competition” – Carlos Segura
“There really is room for everyone” – Jeffry Kalmikoff on the size and scope of online markets
“You can’t worry about things you can’t control and you can’t control your competition” – Jason Fried
Techniques
“Can it make money? Will we be proud of it? Will we learn something new along the way?” – Jim Coudal on evaluating business ideas
“You don’t know if its any good until you build it” – Jason Fried
“More people watch TV that read books in America” – Gary Vaynerchuk about using online video to your advantage
“It would be awesome if…” – Jake Nickell on how a great idea should start
“The person who buys enterprise software is not the person who uses it” – Jason Fried on why that model is broken
“Keep doing the stuff on the side and when something sticks run with it” – Jason Fried on doing side projects with full time work
“People will feel it even if they don’t know why” – Jason Friend on getting the extreme details right
Just Plain Funny
“I’ve answered a million emails while pooping” – Gary Vaynerchuk on the indispensable nature of laptop computers
“Fly in progressively closer circles until you fly up your own butt and disappear” – Jim Coudal on how to perfect your work
“I’m huge in Denmark – I’m like Hasselhoff” – Gary Vaynerchuk on his international community
Disclaimer: I wrote these out in field notes live during the talks (no recording involved), so if there are any inaccuracies just let me know and I’ll correct them.