Created
February 2, 2012 00:33
-
-
Save jeffreyiacono/1720397 to your computer and use it in GitHub Desktop.
module that enables a model to produce "time ago" string representations for any given date field (requires active_support)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module MakeTimeAgoable | |
extend ActiveSupport::Concern | |
included do | |
# code goes here that should be run on the actual include | |
# or just leave it blank. no one cares. where's my cake? | |
end | |
module ClassMethods | |
attr_reader :_time_agoable | |
# Usage: | |
# | |
# For a model that has date fields (ex. created_at, updated_at, etc.) and | |
# that includes this module, you can call make_time_agoable, passing in | |
# +*date_fields+ as a list that you'd like to be made "time ago"-able. | |
# | |
# "time-ago"-able means we can get a value of how long ago the given date | |
# was in a nice, friendly format. | |
# | |
# For example, if created_at was set to a timestamp that was 3 days | |
# ago, by using make_time_agoable we could get the output of "3 days ago" | |
# | |
# Let's look at an example: | |
# | |
# class Lolrus | |
# make_time_agoable :created_at, :updated_at | |
# end | |
# | |
# Now we'll have the following methods metaprogrammed for us: | |
# | |
# Lolrus#created_at_time_ago (tied to the Lolrus instance's created_at field) | |
# Lolrus#updated_at_time_ago (tied to the Lolrus instance's updated_at field) | |
# | |
# Each method will return the equivalent of using the Rails | |
# distance_of_time_in_words_to_now helper if passed the field it is tied | |
# to (we append "ago" for convenience, but you get the point): | |
# | |
# a_lolrus = Lolrus.new(:created_at => Time.now) | |
# | |
# [sudo code] | |
# (distance_of_time_in_words_to_now(a_lolrus.created_at) + " ago") == a_lolrus.created_at_time_ago | |
# | |
# With this module, we can get these timeframe representations back from the model, | |
# rather than depending on the view helper. | |
# | |
# A usecase is when dealing with an app that leverages client-side UI rendering / updating | |
# by hitting an api backend using js, for example. Although we can't use server-side view helpers, | |
# we can now get nicely formated timeframes from the model. Hooray. | |
# | |
# Alternatively we could write the equivalent helper in js and modify the | |
# data that way. | |
def make_time_agoable(*date_fields) | |
# create hash of form {method name => underlying field} for passed date fields | |
@_time_agoable = date_fields.reduce({}) do |agg, fld| | |
agg["#{fld}_time_ago".to_sym] = fld.to_sym | |
agg | |
end | |
# dynamically define methods | |
@_time_agoable.each do |method_name, underlying_field| | |
define_method method_name do | |
"#{distance_of_time_in_words(read_attribute(underlying_field), Time.now)} ago" | |
end | |
end | |
# add default as_json that includes these newly generated methods so | |
# they'll be included in any #to_json requests. This can be overriden in | |
# the model if needed. Just remember to include the dynamically generated | |
# methods if you do! | |
define_method :as_json do |options| | |
super methods: self.class._time_agoable.keys | |
end | |
end | |
end | |
private | |
# pulled from Rails 2.1 (pre-internationalization) | |
# TODO: use current, I18n-leveraging version, es mas bueno | |
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false) | |
from_time = from_time.to_time if from_time.respond_to?(:to_time) | |
to_time = to_time.to_time if to_time.respond_to?(:to_time) | |
distance_in_minutes = (((to_time - from_time).abs)/60).round | |
distance_in_seconds = ((to_time - from_time).abs).round | |
case distance_in_minutes | |
when 0..1 | |
return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds | |
case distance_in_seconds | |
when 0..4 then 'less than 5 seconds' | |
when 5..9 then 'less than 10 seconds' | |
when 10..19 then 'less than 20 seconds' | |
when 20..39 then 'half a minute' | |
when 40..59 then 'less than a minute' | |
else '1 minute' | |
end | |
when 2..44 then "#{distance_in_minutes} minutes" | |
when 45..89 then 'about 1 hour' | |
when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours" | |
when 1440..2879 then '1 day' | |
when 2880..43199 then "#{(distance_in_minutes / 1440).round} days" | |
when 43200..86399 then 'about 1 month' | |
when 86400..525599 then "#{(distance_in_minutes / 43200).round} months" | |
when 525600..1051199 then 'about 1 year' | |
else "over #{(distance_in_minutes / 525600).round} years" | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment