├── .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 | [](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 | [](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 |
--------------------------------------------------------------------------------