Bfec5f7d1a4aaafc5a2451be8c42d26a

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 !

# 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 !

Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

September 19, 2007, September 19, 2007 13:16, permalink

No rating. Login to rate!

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
F6eddf2f983d23c2d031e407852625e9

jamesgolick

September 19, 2007, September 19, 2007 13:18, permalink

1 rating. Login to rate!

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
Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

September 19, 2007, September 19, 2007 13:29, permalink

No rating. Login to rate!

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
501dac4c25141b9ecffecf6819fe086b

makenai

April 10, 2008, April 10, 2008 21:54, permalink

No rating. Login to rate!

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";
}

Your refactoring





Format Copy from initial code

or Cancel