jekyll-humanize.rb 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. module Jekyll
  2. module Humanize
  3. ##
  4. # This is a port of the Django app `humanize` which adds a "human touch"
  5. # to data. Given that Jekyll produces static sites, some of the original
  6. # methods do not make logical sense (e.g. naturaltime).
  7. #
  8. # Source code can be viewed here:
  9. # https://github.com/django/django
  10. #
  11. # Copyright (c) Django Software Foundation and individual contributors.
  12. # All rights reserved.
  13. ####################
  14. # PUBLIC METHODS #
  15. ####################
  16. def ordinal(value, flag=nil)
  17. ##
  18. # Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd',
  19. # 3 is '3rd', etc. Works for any integer.
  20. #
  21. # Usage:
  22. # {{ somenum }} >>> 3
  23. # {{ somenum | ordinal }} >>> '3rd'
  24. # {{ somenum | ordinal: "super" }} >>> '3<sup>rd</sup>'
  25. begin
  26. value = value.to_i
  27. flag.to_s.downcase!
  28. rescue Exception => e
  29. puts "#{e.class} #{e}"
  30. return value
  31. end
  32. suffix = ""
  33. suffixes = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"]
  34. unless [11, 12, 13].include? value % 100 then
  35. suffix = suffixes[value % 10]
  36. else
  37. suffix = suffixes[0]
  38. end
  39. unless flag and flag == "super"
  40. return "#{value}%s" % suffix
  41. else
  42. return "#{value}<sup>%s</sup>" % suffix
  43. end
  44. end
  45. def intcomma(value, delimiter=",")
  46. ##
  47. # Converts an integer to a string containing commas every three digits.
  48. # For example, 3000 becomes '3,000' and 45000 becomes '45,000'.
  49. # Optionally supports a delimiter override for commas.
  50. #
  51. # Usage:
  52. # {{ post.content | number_of_words }} >>> 12345
  53. # {{ post.content | number_of_words | intcomma }} >>> '12,345'
  54. # {{ post.content | number_of_words | intcomma: '.' }} >>> '12.345'
  55. begin
  56. orig = value.to_s
  57. delimiter = delimiter.to_s
  58. rescue Exception => e
  59. puts "#{e.class} #{e}"
  60. return value
  61. end
  62. copy = orig.strip
  63. copy = orig.gsub(/^(-?\d+)(\d{3})/, "\\1#{delimiter}\\2")
  64. orig == copy ? copy : intcomma(copy, delimiter)
  65. end
  66. INTWORD_HELPERS = [
  67. [6, "million"],
  68. [9, "billion"],
  69. [12, "trillion"],
  70. [15, "quadrillion"],
  71. [18, "quintillion"],
  72. [21, "sextillion"],
  73. [24, "septillion"],
  74. [27, "octillion"],
  75. [30, "nonillion"],
  76. [33, "decillion"],
  77. [100, "googol"],
  78. ]
  79. def intword(value)
  80. ##
  81. # Converts a large integer to a friendly text representation. Works best
  82. # for numbers over 1 million. For example, 1000000 becomes '1.0 million',
  83. # 1200000 becomes '1.2 million' and 1200000000 becomes '1.2 billion'.
  84. #
  85. # Usage:
  86. # {{ largenum }} >>> 1200000
  87. # {{ largenum | intword }} >>> '1.2 million'
  88. begin
  89. value = value.to_i
  90. rescue Exception => e
  91. puts "#{e.class} #{e}"
  92. return value
  93. end
  94. if value < 1000000
  95. return value
  96. end
  97. for exponent, text in INTWORD_HELPERS
  98. large_number = 10 ** exponent
  99. if value < large_number * 1000
  100. return "%#{value}.1f #{text}" % (value / large_number.to_f)
  101. end
  102. end
  103. return value
  104. end
  105. def apnumber(value)
  106. ##
  107. # For numbers 0-9, returns the number spelled out. Otherwise, returns the
  108. # number. This follows Associated Press style.
  109. #
  110. # Usage:
  111. # {{ num }} >>> 6
  112. # {{ num | apnumber }} >>> six
  113. begin
  114. value = value.to_i
  115. rescue Exception => e
  116. puts "#{e.class} #{e}"
  117. return value
  118. end
  119. unless value >= 0 and value < 10 then
  120. return value
  121. else
  122. return ["zero", "one", "two", "three", "four", "five", "six",
  123. "seven", "eight", "nine"][value]
  124. end
  125. end
  126. def naturalday(date)
  127. ##
  128. # For date values that are within a 9 day stretch from present day, this
  129. # will attempt to return the string representation in the format of today,
  130. # tomorrow, yesterday, "in # days" or "# days ago". Otherwise, returns a
  131. # string formatted according to the "date_format" setting in your
  132. # _config.yml file using strftime format (if not defined, it will default
  133. # to "%m/%d/%Y").
  134. #
  135. # Usage:
  136. # TODAY == 01/26/2014
  137. # {{ post.updated }} >>> 01/25/2014
  138. # {{ post.updated | naturalday }} >>> 'yesterday'
  139. # {{ post.date }} >>> 01/19/2014
  140. # {{ post.date | naturalday }} >>> 'seven days ago'
  141. begin
  142. site = @context.registers[:site]
  143. date_format = site.config['humanize']['date_format']
  144. date = time(date).to_date
  145. rescue Exception => e
  146. puts "#{e.class} #{e}"
  147. return date
  148. end
  149. unless date_format then
  150. date_format = "%m/%d/%Y"
  151. end
  152. today = time(Time.now).to_date
  153. delta = (date - today).to_i
  154. case delta
  155. when 0
  156. return "today"
  157. when 1
  158. return "tomorrow"
  159. when 2..9
  160. delta = apnumber(delta)
  161. return "in #{delta} days"
  162. when -1
  163. return "yesterday"
  164. when -9..-2
  165. delta = apnumber(delta * -1)
  166. return "#{delta} days ago"
  167. else
  168. return date.strftime("#{date_format}")
  169. end
  170. end
  171. def filesize(value)
  172. ##
  173. # For filesize values in bytes, returns the number rounded to 3
  174. # decimal places with the correct suffix.
  175. #
  176. # Usage:
  177. # {{ bytes }} >>> 123456789
  178. # {{ bytes | filesize }} >>> 117.738 MB
  179. filesize_tb = 1099511627776.0
  180. filesize_gb = 1073741824.0
  181. filesize_mb = 1048576.0
  182. filesize_kb = 1024.0
  183. begin
  184. value = value.to_f
  185. rescue Exception => e
  186. puts "#{e.class} #{e}"
  187. return value
  188. end
  189. if value >= filesize_tb
  190. return "%s TB" % (value / filesize_tb).to_f.round(3)
  191. elsif value >= filesize_gb
  192. return "%s GB" % (value / filesize_gb).to_f.round(3)
  193. elsif value >= filesize_mb
  194. return "%s MB" % (value / filesize_mb).to_f.round(3)
  195. elsif value >= filesize_kb
  196. return "%s KB" % (value / filesize_kb).to_f.round(0)
  197. elsif value == 1
  198. return "1 byte"
  199. else
  200. return "%s bytes" % value.to_f.round(0)
  201. end
  202. end
  203. #####################
  204. # PRIVATE METHODS #
  205. #####################
  206. private
  207. def time(input)
  208. case input
  209. when Time
  210. input
  211. when String
  212. Time.parse(input)
  213. else
  214. Jekyll.logger.error "Invalid Date:", "'#{input}' not valid datetime."
  215. exit(1)
  216. end
  217. end
  218. end
  219. end
  220. Liquid::Template.register_filter(Jekyll::Humanize)