# In one of your helper module, dump:
def cachable_time_ago_in_words(from)
js_call = javascript_tag "document.write(DateHelper.timeAgoInWords(#{(from.to_i * 1000).to_json}) + ' ago');"
"<noscript>on #{from.to_formatted_s(:long)}</noscript>#{js_call}"
end
# In your application.js
var DateHelper = {
timeAgoInWords: function(from) {
return this.distanceOfTimeInWords(new Date().getTime(), from);
},
distanceOfTimeInWords: function(to, from) {
seconds_ago = ((to - from) / 1000);
minutes_ago = Math.floor(seconds_ago / 60);
if(minutes_ago == 0) { return "less than a minute"; }
if(minutes_ago == 1) { return "a minute"; }
if(minutes_ago < 45) { return minutes_ago + " minutes"; }
if(minutes_ago < 90) { return " about 1 hour"; }
hours_ago = Math.round(minutes_ago / 60);
if(minutes_ago < 1440) { return "about " + hours_ago + " hours"; }
if(minutes_ago < 2880) { return "1 day"; }
days_ago = Math.round(minutes_ago / 1440);
if(minutes_ago < 43200) { return days_ago + " days"; }
if(minutes_ago < 86400) { return "about 1 month"; }
months_ago = Math.round(minutes_ago / 43200);
if(minutes_ago < 525960) { return months_ago + " months"; }
if(minutes_ago < 1051920) { return "about 1 year"; }
years_ago = Math.round(minutes_ago / 525960);
return "over " + years_ago + " years";
}
}
# Then in your views use:
<%= cachable_time_ago_in_words Time.now %>
Refactorings
No refactoring yet !
macournoyer
September 19, 2007, September 19, 2007 13:16, permalink
Seems it doesn't work with AJAX request, here my solution, pretty simple
Thx James for reporting this!
def cachable_time_ago_in_words(from)
if request.xhr?
time_ago_in_words from
else
js_call = javascript_tag "document.write(DateHelper.timeAgoInWords(#{(from.to_i * 1000).to_json}) + ' ago');"
"<noscript>on #{from.to_formatted_s(:long)}</noscript>#{js_call}"
end
end
jamesgolick
September 19, 2007, September 19, 2007 13:18, permalink
But document.write is still gross, so here's my take
def cachable_time_ago_in_words(from, id_prefix = '')
id = "#{id_prefix}_#{from.to_i}"
content_tag :span, "", :id => id
javascript_tag "$('#{id}').update(DateHelper.timeAgoInWords(#{(from.to_i * 1000).to_json}))"
end
macournoyer
September 19, 2007, September 19, 2007 13:29, permalink
You're right, but this does not degrade gracefully and the first content_atg won't be rendered, here's my fix
def cachable_time_ago_in_words(from, id_prefix = 'time_ago')
id = "#{id_prefix}_#{from.to_i}"
content_tag(:span, from.to_formatted_s(:long), :id => id) <<
javascript_tag("$('#{id}').update(DateHelper.timeAgoInWords(#{(from.to_i * 1000).to_json}))")
end
makenai
April 10, 2008, April 10, 2008 21:54, permalink
In the rails version the numbers are not cast to Floats before dividing, so the results are not consistent when you put this code and time_ago_in_words side by side. Using Math.floor will give the proper results.
distanceOfTimeInWords: function(to, from) {
seconds_ago = ((to - from) / 1000);
minutes_ago = Math.floor(seconds_ago / 60);
if(minutes_ago == 0) { return "less than a minute"; }
if(minutes_ago == 1) { return "1 minute ago"; }
if(minutes_ago < 45) { return minutes_ago + " minutes"; }
if(minutes_ago < 90) { return " about 1 hour"; }
hours_ago = Math.floor(minutes_ago / 60);
if(minutes_ago < 1440) { return "about " + hours_ago + " hours"; }
if(minutes_ago < 2880) { return "1 day"; }
days_ago = Math.floor(minutes_ago / 1440);
if(minutes_ago < 43200) { return days_ago + " days"; }
if(minutes_ago < 86400) { return "about 1 month"; }
months_ago = Math.floor(minutes_ago / 43200);
if(minutes_ago < 525960) { return months_ago + " months"; }
if(minutes_ago < 1051920) { return "about 1 year"; }
years_ago = Math.floor(minutes_ago / 525960);
return "over " + years_ago + " years";
}
When a page is cached, if you use the Rails ActionView helper "time_ago_in_words" the time will stay the same until the cache is refreshed, ie.: about 1 sec ago .... 1 hour later .... 1 sec ago.
Here's the solution in found at http://nullstyle.com/2007/6/3/caching-time_ago_in_words
Got any better solution ? Let me know !