├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md └── humanize.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # sublime project files 2 | sublime/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # jekyll-humanize Changelog 2 | 3 | ## v1.1.0 (2014-07-14) 4 | 5 | + Added new filesize filter for converting bytes 6 | + Resolved #1 to add `` support to ordinal numbers 7 | - Small updates to README.md 8 | 9 | ## v1.0.1 (2014-01-29) 10 | 11 | + Fixed small type in gsub (proper escaping needed) 12 | 13 | ## v1.0.0 (2014-01-28) 14 | 15 | + Updated README.md with basic usage documentation 16 | + Fixed a small bug in code documentation 17 | + Updated _config.yml setting to include `humanize` namespace 18 | 19 | ## v0.1.0 (2014-01-26) 20 | 21 | + Initial commit -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ryan Morrissey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jekyll-humanize 2 | 3 | This began as a port of the Django app `humanize` which adds a "human touch" to data. Each method represents a Fluid type filter that can be used in your Jekyll site templates. Given that Jekyll produces static sites, some of the original methods do not make logical sense to port (e.g. naturaltime). 4 | 5 | ## Installation 6 | 7 | Simply download the `humanize.rb` file and place it in the `_plugins` directory of your Jekyll site. 8 | 9 | ## Usage 10 | 11 | Each of the methods in the file presents an available Fluid type filter to be used in Jekyll templates. 12 | 13 | There is one **optional** setting that can be overridden in your `_config.yml` file. The setting should follow the format below and should be a valid `strftime` date string. Documentation on `strftime` can be found at [www.ruby-doc.com][2]. 14 | 15 | ```yaml 16 | humanize: 17 | date_format: "%m/%d/%Y" 18 | ``` 19 | 20 | ### ordinal(_value_, _flag="super"_) 21 | 22 | Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd', 3 is '3rd', etc. Works for any integer. An optional flag can be added which puts the ordinal suffix in `` tags. 23 | 24 | ```ruby 25 | {{ somenum }} >>> 3 26 | {{ somenum | ordinal }} >>> '3rd' 27 | {{ somenum | ordinal: "super" }} >>> '3rd' 28 | ``` 29 | 30 | ### intcomma(_value_, _delimiter=","_) 31 | 32 | 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 (if you wanted to use periods for European numerical separators). 33 | 34 | ```ruby 35 | {{ post.content | number_of_words }} >>> 12345 36 | {{ post.content | number_of_words | intcomma }} >>> '12,345' 37 | {{ post.content | number_of_words | intcomma: '.' }} >>> '12.345' 38 | ``` 39 | 40 | ### intword(_value_) 41 | 42 | 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'. 43 | 44 | ```ruby 45 | {{ largenum }} >>> 1200000 46 | {{ largenum | intword }} >>> '1.2 million' 47 | ``` 48 | 49 | ### apnumber(_value_) 50 | 51 | For numbers 0-9, returns the number spelled out. Otherwise, returns the number. This follows the Associated Press style. 52 | 53 | ```ruby 54 | {{ num }} >>> 6 55 | {{ num | apnumber }} >>> 'six' 56 | ``` 57 | 58 | ### naturalday(_date_) 59 | 60 | 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`. 61 | 62 | ```ruby 63 | # TODAY == '01/26/2014' 64 | {{ post.updated }} >>> 01/25/2014 65 | {{ post.updated | naturalday }} >>> 'yesterday' 66 | {{ post.date }} >>> 01/19/2014 67 | {{ post.date | naturalday }} >>> 'seven days ago' 68 | ``` 69 | 70 | ### filesize(_bytes_) 71 | 72 | For filesize values in bytes, returns the number rounded to 3 decimal places with the correct suffix. 73 | 74 | ```ruby 75 | {{ bytes }} >>> 123456789 76 | {{ bytes | filesize }} >>> 117.738 MB 77 | ``` 78 | 79 | ## License 80 | 81 | [LICENSE](LICENSE) 82 | 83 | #### Django Humanize 84 | 85 | Copyright (c) Django Software Foundation and individual contributors. 86 | All rights reserved. 87 | 88 | Source code for the original Django app can be viewed at [https://github.com/django/django][1]. 89 | 90 | #### JS-humanize 91 | 92 | Filesize format forked from javascript to ruby from [https://github.com/milanvrekic/JS-humanize][3] 93 | 94 | ## Changelog 95 | 96 | [CHANGELOG](CHANGELOG.md) 97 | 98 | Issues 99 | ------ 100 | 101 | [![Issue Stats](http://issuestats.com/github/23maverick23/jekyll-humanize/badge/issue?style=flat)](http://issuestats.com/github/23maverick23/jekyll-humanize) 102 | 103 | You can log issues from the menu at right, or by [clicking here](https://github.com/23maverick23/jekyll-humanize/issues). Curious about responsiveness? Check out our [Issue Stats](http://issuestats.com/github/23maverick23/jekyll-humanize)! 104 | 105 | Contribute 106 | ---------- 107 | 108 | [![Issue Stats](http://issuestats.com/github/23maverick23/jekyll-humanize/badge/pr?style=flat)](http://issuestats.com/github/23maverick23/jekyll-humanize) 109 | 110 | 1. [Fork](https://github.com/23maverick23/jekyll-humanize/fork) this repo. 111 | 2. Create a branch `git checkout -b my_feature` 112 | 3. Commit your changes `git commit -am "Added Feature"` 113 | 4. Push to the branch `git push origin my_feature` 114 | 5. Open a [Pull Request](https://github.com/23maverick23/jekyll-humanize/pulls) 115 | 116 | [1]: https://github.com/django/django 117 | [2]: http://www.ruby-doc.org/core-2.1.0/Time.html#method-i-strftime 118 | [3]: https://github.com/milanvrekic/JS-humanize 119 | -------------------------------------------------------------------------------- /humanize.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | 3 | module Humanize 4 | ## 5 | # This is a port of the Django app `humanize` which adds a "human touch" 6 | # to data. Given that Jekyll produces static sites, some of the original 7 | # methods do not make logical sense (e.g. naturaltime). 8 | # 9 | # Source code can be viewed here: 10 | # https://github.com/django/django 11 | # 12 | # Copyright (c) Django Software Foundation and individual contributors. 13 | # All rights reserved. 14 | 15 | #################### 16 | # PUBLIC METHODS # 17 | #################### 18 | 19 | def ordinal(value, flag=nil) 20 | ## 21 | # Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd', 22 | # 3 is '3rd', etc. Works for any integer. 23 | # 24 | # Usage: 25 | # {{ somenum }} >>> 3 26 | # {{ somenum | ordinal }} >>> '3rd' 27 | # {{ somenum | ordinal: "super" }} >>> '3rd' 28 | 29 | begin 30 | value = value.to_i 31 | flag.to_s.downcase! 32 | rescue Exception => e 33 | puts "#{e.class} #{e}" 34 | return value 35 | end 36 | 37 | suffix = "" 38 | suffixes = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"] 39 | unless [11, 12, 13].include? value % 100 then 40 | suffix = suffixes[value % 10] 41 | else 42 | suffix = suffixes[0] 43 | end 44 | 45 | unless flag and flag == "super" 46 | return "#{value}%s" % suffix 47 | else 48 | return "#{value}%s" % suffix 49 | end 50 | 51 | end 52 | 53 | def intcomma(value, delimiter=",") 54 | ## 55 | # Converts an integer to a string containing commas every three digits. 56 | # For example, 3000 becomes '3,000' and 45000 becomes '45,000'. 57 | # Optionally supports a delimiter override for commas. 58 | # 59 | # Usage: 60 | # {{ post.content | number_of_words }} >>> 12345 61 | # {{ post.content | number_of_words | intcomma }} >>> '12,345' 62 | # {{ post.content | number_of_words | intcomma: '.' }} >>> '12.345' 63 | 64 | begin 65 | orig = value.to_s 66 | delimiter = delimiter.to_s 67 | rescue Exception => e 68 | puts "#{e.class} #{e}" 69 | return value 70 | end 71 | 72 | copy = orig.strip 73 | copy = orig.gsub(/^(-?\d+)(\d{3})/, "\\1#{delimiter}\\2") 74 | orig == copy ? copy : intcomma(copy, delimiter) 75 | end 76 | 77 | INTWORD_HELPERS = [ 78 | [6, "million"], 79 | [9, "billion"], 80 | [12, "trillion"], 81 | [15, "quadrillion"], 82 | [18, "quintillion"], 83 | [21, "sextillion"], 84 | [24, "septillion"], 85 | [27, "octillion"], 86 | [30, "nonillion"], 87 | [33, "decillion"], 88 | [100, "googol"], 89 | ] 90 | 91 | def intword(value) 92 | ## 93 | # Converts a large integer to a friendly text representation. Works best 94 | # for numbers over 1 million. For example, 1000000 becomes '1.0 million', 95 | # 1200000 becomes '1.2 million' and 1200000000 becomes '1.2 billion'. 96 | # 97 | # Usage: 98 | # {{ largenum }} >>> 1200000 99 | # {{ largenum | intword }} >>> '1.2 million' 100 | 101 | begin 102 | value = value.to_i 103 | rescue Exception => e 104 | puts "#{e.class} #{e}" 105 | return value 106 | end 107 | 108 | if value < 1000000 109 | return value 110 | end 111 | 112 | for exponent, text in INTWORD_HELPERS 113 | large_number = 10 ** exponent 114 | 115 | if value < large_number * 1000 116 | return "%#{value}.1f #{text}" % (value / large_number.to_f) 117 | end 118 | 119 | end 120 | 121 | return value 122 | end 123 | 124 | def apnumber(value) 125 | ## 126 | # For numbers 0-9, returns the number spelled out. Otherwise, returns the 127 | # number. This follows Associated Press style. 128 | # 129 | # Usage: 130 | # {{ num }} >>> 6 131 | # {{ num | apnumber }} >>> six 132 | 133 | begin 134 | value = value.to_i 135 | rescue Exception => e 136 | puts "#{e.class} #{e}" 137 | return value 138 | end 139 | 140 | unless value >= 0 and value < 10 then 141 | return value 142 | else 143 | return ["zero", "one", "two", "three", "four", "five", "six", 144 | "seven", "eight", "nine"][value] 145 | end 146 | 147 | end 148 | 149 | def naturalday(date) 150 | ## 151 | # For date values that are within a 9 day stretch from present day, this 152 | # will attempt to return the string representation in the format of today, 153 | # tomorrow, yesterday, "in # days" or "# days ago". Otherwise, returns a 154 | # string formatted according to the "date_format" setting in your 155 | # _config.yml file using strftime format (if not defined, it will default 156 | # to "%m/%d/%Y"). 157 | # 158 | # Usage: 159 | # TODAY == 01/26/2014 160 | # {{ post.updated }} >>> 01/25/2014 161 | # {{ post.updated | naturalday }} >>> 'yesterday' 162 | # {{ post.date }} >>> 01/19/2014 163 | # {{ post.date | naturalday }} >>> 'seven days ago' 164 | 165 | begin 166 | site = @context.registers[:site] 167 | date_format = site.config['humanize']['date_format'] 168 | date = time(date).to_date 169 | rescue Exception => e 170 | puts "#{e.class} #{e}" 171 | return date 172 | end 173 | 174 | unless date_format then 175 | date_format = "%m/%d/%Y" 176 | end 177 | 178 | today = time(Time.now).to_date 179 | delta = (date - today).to_i 180 | 181 | case delta 182 | when 0 183 | return "today" 184 | when 1 185 | return "tomorrow" 186 | when 2..9 187 | delta = apnumber(delta) 188 | return "in #{delta} days" 189 | when -1 190 | return "yesterday" 191 | when -9..-2 192 | delta = apnumber(delta * -1) 193 | return "#{delta} days ago" 194 | else 195 | return date.strftime("#{date_format}") 196 | end 197 | 198 | end 199 | 200 | def filesize(value) 201 | ## 202 | # For filesize values in bytes, returns the number rounded to 3 203 | # decimal places with the correct suffix. 204 | # 205 | # Usage: 206 | # {{ bytes }} >>> 123456789 207 | # {{ bytes | filesize }} >>> 117.738 MB 208 | filesize_tb = 1099511627776.0 209 | filesize_gb = 1073741824.0 210 | filesize_mb = 1048576.0 211 | filesize_kb = 1024.0 212 | 213 | begin 214 | value = value.to_f 215 | rescue Exception => e 216 | puts "#{e.class} #{e}" 217 | return value 218 | end 219 | 220 | if value >= filesize_tb 221 | return "%s TB" % (value / filesize_tb).to_f.round(3) 222 | elsif value >= filesize_gb 223 | return "%s GB" % (value / filesize_gb).to_f.round(3) 224 | elsif value >= filesize_mb 225 | return "%s MB" % (value / filesize_mb).to_f.round(3) 226 | elsif value >= filesize_kb 227 | return "%s KB" % (value / filesize_kb).to_f.round(0) 228 | elsif value == 1 229 | return "1 byte" 230 | else 231 | return "%s bytes" % value.to_f.round(0) 232 | end 233 | 234 | end 235 | 236 | ##################### 237 | # PRIVATE METHODS # 238 | ##################### 239 | 240 | private 241 | def time(input) 242 | case input 243 | when Time 244 | input 245 | when String 246 | Time.parse(input) 247 | else 248 | Jekyll.logger.error "Invalid Date:", "'#{input}' not valid datetime." 249 | exit(1) 250 | end 251 | end 252 | 253 | end 254 | 255 | end 256 | 257 | Liquid::Template.register_filter(Jekyll::Humanize) 258 | --------------------------------------------------------------------------------