module Jekyll module Humanize ## # This is a port of the Django app `humanize` which adds a "human touch" # to data. Given that Jekyll produces static sites, some of the original # methods do not make logical sense (e.g. naturaltime). # # Source code can be viewed here: # https://github.com/django/django # # Copyright (c) Django Software Foundation and individual contributors. # All rights reserved. #################### # PUBLIC METHODS # #################### def ordinal(value, flag=nil) ## # Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd', # 3 is '3rd', etc. Works for any integer. # # Usage: # {{ somenum }} >>> 3 # {{ somenum | ordinal }} >>> '3rd' # {{ somenum | ordinal: "super" }} >>> '3rd' begin value = value.to_i flag.to_s.downcase! rescue Exception => e puts "#{e.class} #{e}" return value end suffix = "" suffixes = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"] unless [11, 12, 13].include? value % 100 then suffix = suffixes[value % 10] else suffix = suffixes[0] end unless flag and flag == "super" return "#{value}%s" % suffix else return "#{value}%s" % suffix end end def intcomma(value, delimiter=",") ## # Converts an integer to a string containing commas every three digits. # For example, 3000 becomes '3,000' and 45000 becomes '45,000'. # Optionally supports a delimiter override for commas. # # Usage: # {{ post.content | number_of_words }} >>> 12345 # {{ post.content | number_of_words | intcomma }} >>> '12,345' # {{ post.content | number_of_words | intcomma: '.' }} >>> '12.345' begin orig = value.to_s delimiter = delimiter.to_s rescue Exception => e puts "#{e.class} #{e}" return value end copy = orig.strip copy = orig.gsub(/^(-?\d+)(\d{3})/, "\\1#{delimiter}\\2") orig == copy ? copy : intcomma(copy, delimiter) end INTWORD_HELPERS = [ [6, "million"], [9, "billion"], [12, "trillion"], [15, "quadrillion"], [18, "quintillion"], [21, "sextillion"], [24, "septillion"], [27, "octillion"], [30, "nonillion"], [33, "decillion"], [100, "googol"], ] def intword(value) ## # Converts a large integer to a friendly text representation. Works best # for numbers over 1 million. For example, 1000000 becomes '1.0 million', # 1200000 becomes '1.2 million' and 1200000000 becomes '1.2 billion'. # # Usage: # {{ largenum }} >>> 1200000 # {{ largenum | intword }} >>> '1.2 million' begin value = value.to_i rescue Exception => e puts "#{e.class} #{e}" return value end if value < 1000000 return value end for exponent, text in INTWORD_HELPERS large_number = 10 ** exponent if value < large_number * 1000 return "%#{value}.1f #{text}" % (value / large_number.to_f) end end return value end def apnumber(value) ## # For numbers 0-9, returns the number spelled out. Otherwise, returns the # number. This follows Associated Press style. # # Usage: # {{ num }} >>> 6 # {{ num | apnumber }} >>> six begin value = value.to_i rescue Exception => e puts "#{e.class} #{e}" return value end unless value >= 0 and value < 10 then return value else return ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"][value] end end def naturalday(date) ## # For date values that are within a 9 day stretch from present day, this # will attempt to return the string representation in the format of today, # tomorrow, yesterday, "in # days" or "# days ago". Otherwise, returns a # string formatted according to the "date_format" setting in your # _config.yml file using strftime format (if not defined, it will default # to "%m/%d/%Y"). # # Usage: # TODAY == 01/26/2014 # {{ post.updated }} >>> 01/25/2014 # {{ post.updated | naturalday }} >>> 'yesterday' # {{ post.date }} >>> 01/19/2014 # {{ post.date | naturalday }} >>> 'seven days ago' begin site = @context.registers[:site] date_format = site.config['humanize']['date_format'] date = time(date).to_date rescue Exception => e puts "#{e.class} #{e}" return date end unless date_format then date_format = "%m/%d/%Y" end today = time(Time.now).to_date delta = (date - today).to_i case delta when 0 return "today" when 1 return "tomorrow" when 2..9 delta = apnumber(delta) return "in #{delta} days" when -1 return "yesterday" when -9..-2 delta = apnumber(delta * -1) return "#{delta} days ago" else return date.strftime("#{date_format}") end end def filesize(value) ## # For filesize values in bytes, returns the number rounded to 3 # decimal places with the correct suffix. # # Usage: # {{ bytes }} >>> 123456789 # {{ bytes | filesize }} >>> 117.738 MB filesize_tb = 1099511627776.0 filesize_gb = 1073741824.0 filesize_mb = 1048576.0 filesize_kb = 1024.0 begin value = value.to_f rescue Exception => e puts "#{e.class} #{e}" return value end if value >= filesize_tb return "%s TB" % (value / filesize_tb).to_f.round(3) elsif value >= filesize_gb return "%s GB" % (value / filesize_gb).to_f.round(3) elsif value >= filesize_mb return "%s MB" % (value / filesize_mb).to_f.round(3) elsif value >= filesize_kb return "%s KB" % (value / filesize_kb).to_f.round(0) elsif value == 1 return "1 byte" else return "%s bytes" % value.to_f.round(0) end end ##################### # PRIVATE METHODS # ##################### private def time(input) case input when Time input when String Time.parse(input) else Jekyll.logger.error "Invalid Date:", "'#{input}' not valid datetime." exit(1) end end end end Liquid::Template.register_filter(Jekyll::Humanize)