jekyll-timeago 0.5.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +53 -39
- data/_config.yml.example +1 -0
- data/jekyll-timeago.gemspec +1 -1
- data/lib/jekyll-timeago/filter.rb +140 -0
- data/lib/jekyll-timeago/tag.rb +24 -0
- data/lib/{jekyll/timeago → jekyll-timeago}/version.rb +1 -1
- data/lib/jekyll-timeago.rb +3 -0
- data/script/console +13 -0
- metadata +7 -4
- data/lib/jekyll/timeago.rb +0 -142
data/README.md
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
Jekyll-Timeago
|
2
|
-
==============
|
1
|
+
# Jekyll-Timeago
|
3
2
|
|
4
3
|
[![Gem Version](https://badge.fury.io/rb/jekyll-timeago.svg)](http://badge.fury.io/rb/jekyll-timeago)
|
5
4
|
|
6
5
|
Custom and simple implementation of `timeago` date filter. Main features:
|
7
6
|
|
8
|
-
*
|
7
|
+
* Distance of dates in words
|
9
8
|
* Future time
|
9
|
+
* Usage via Filter or Tag
|
10
|
+
* Localization
|
10
11
|
* Level of detail
|
11
12
|
|
12
|
-
In fact, `jekyll-timeago` is an extension of [Liquid](https://github.com/Shopify/liquid)
|
13
|
+
In fact, `jekyll-timeago` is an extension of [Liquid](https://github.com/Shopify/liquid) Filters and Tags, so you can use it in other Liquid templates (like Octopress).
|
13
14
|
|
14
15
|
|
15
16
|
## Installation
|
16
|
-
|
17
|
-
You have 3 options for installing the plugin:
|
17
|
+
You have 3 options to install the plugin:
|
18
18
|
|
19
19
|
**Via Jekyll plugin system**
|
20
20
|
|
@@ -27,7 +27,7 @@ gem install jekyll-timeago
|
|
27
27
|
In your `_config.yml` file, add a new array with the key gems and the values of the gem names of the plugins you’d like to use. In this case:
|
28
28
|
|
29
29
|
```
|
30
|
-
gems: [jekyll
|
30
|
+
gems: [jekyll-timeago]
|
31
31
|
```
|
32
32
|
|
33
33
|
**Via Bundler**
|
@@ -38,18 +38,24 @@ Add this gem to your `Gemfile` and run `bundle`:
|
|
38
38
|
gem 'jekyll-timeago'
|
39
39
|
```
|
40
40
|
|
41
|
-
|
41
|
+
Then load the plugin adding the following into some file under `_plugins/` folder:
|
42
42
|
|
43
43
|
```ruby
|
44
|
-
|
44
|
+
# _plugins/ext.rb
|
45
|
+
require 'rubygems'
|
46
|
+
require 'bundler/setup'
|
47
|
+
Bundler.require(:default)
|
45
48
|
```
|
46
49
|
|
47
50
|
**Manually**
|
48
51
|
|
49
|
-
Alternatively, you can simply copy [this file](
|
52
|
+
Alternatively, you can simply copy [this file](lib/jekyll-timeago/filter.rb) and [this file](lib/jekyll-timeago/tag.rb) directly into your `_plugins/` directory!
|
50
53
|
|
51
54
|
|
52
55
|
## Usage
|
56
|
+
By default `timeago` computes distance of dates from passed date to current date (using `Date.today`). But you are able to modify this range passing a second argument in order to compute the distance of these dates in words.
|
57
|
+
|
58
|
+
**Filter example**:
|
53
59
|
|
54
60
|
```html
|
55
61
|
<span>{{ page.date | timeago }}</span>
|
@@ -60,20 +66,31 @@ Alternatively, you can simply copy [this file](https://github.com/markets/jekyll
|
|
60
66
|
</div>
|
61
67
|
```
|
62
68
|
|
63
|
-
|
69
|
+
Passing a parameter:
|
70
|
+
|
71
|
+
```html
|
72
|
+
<span>{{ page.date | timeago: '2020-1-1' }}</span>
|
73
|
+
```
|
74
|
+
|
75
|
+
**Tag example**:
|
76
|
+
|
77
|
+
```html
|
78
|
+
<p>{% timeago 2000-1-1 %}</p>
|
79
|
+
```
|
64
80
|
|
65
|
-
|
81
|
+
Passing a second parameter:
|
66
82
|
|
67
83
|
```html
|
68
|
-
<
|
84
|
+
<p>{% timeago 2000-1-1 20010-1-1 %}</p>
|
69
85
|
```
|
70
86
|
|
71
|
-
## Localization
|
72
87
|
|
73
|
-
|
88
|
+
## Localization
|
89
|
+
The plugin allows you to localize the strings needed to build the time ago sentences. For do this, you must add some extra keys to your `_config.yml`. You can simply copy them from [this example file](_config.yml.example) and translate it to your site's language. Sample:
|
74
90
|
|
75
91
|
```
|
76
92
|
jekyll_timeago:
|
93
|
+
depth: 2 # Level of detail
|
77
94
|
today: 'today'
|
78
95
|
yesterday: 'yesterday'
|
79
96
|
tomorrow: 'tomorrow'
|
@@ -92,42 +109,39 @@ jekyll_timeago:
|
|
92
109
|
day: 'day'
|
93
110
|
```
|
94
111
|
|
95
|
-
## Output Examples
|
96
112
|
|
97
|
-
|
113
|
+
## Level of detail (Depth)
|
114
|
+
You are able to change the level of detail (from 1 up to 4, 2 by default) to get higher or lower granularity. This option is setted via the `config` file (see sample in previous section). Examples:
|
115
|
+
|
116
|
+
* Depht => 1 `1 year ago`
|
117
|
+
* Depht => 2 `1 year and 4 months ago` (default)
|
118
|
+
* Depht => 3 `1 year, 4 months and 1 week ago`
|
119
|
+
* Depht => 4 `1 year, 4 months, 1 week and 4 days ago`
|
120
|
+
|
121
|
+
|
122
|
+
## Output Examples
|
123
|
+
Run `script/console` to start a custom IRB session and play with `timeago` method:
|
98
124
|
|
99
125
|
```ruby
|
100
|
-
|
126
|
+
>> timeago(Date.today)
|
101
127
|
=> "today"
|
102
|
-
|
128
|
+
>> timeago(Date.today - 1.day)
|
103
129
|
=> "yesterday"
|
104
|
-
|
130
|
+
>> timeago(Date.today - 10.days)
|
105
131
|
=> "1 week and 3 days ago"
|
106
|
-
|
132
|
+
>> timeago(Date.today - 100.days)
|
107
133
|
=> "3 months and 1 week ago"
|
108
|
-
|
134
|
+
>> timeago(Date.today - 500.days)
|
109
135
|
=> "1 year and 4 months ago"
|
110
|
-
|
136
|
+
>> timeago('2010-1-1', '2012-1-1')
|
137
|
+
=> "2 years ago"
|
138
|
+
>> timeago(Date.today + 1.days)
|
111
139
|
=> "tomorrow"
|
112
|
-
|
140
|
+
>> timeago(Date.today + 7.days)
|
113
141
|
=> "in 1 week"
|
114
|
-
|
142
|
+
>> timeago(Date.today + 1000.days)
|
115
143
|
=> "in 2 years and 8 months"
|
116
144
|
```
|
117
145
|
|
118
|
-
Change level of detail to get higher or lower granularity:
|
119
|
-
|
120
|
-
```ruby
|
121
|
-
> timeago(Date.today - 500.days) # default
|
122
|
-
=> "1 year and 4 months ago"
|
123
|
-
> timeago(Date.today - 500.days, 3)
|
124
|
-
=> "1 year, 4 months and 1 week ago"
|
125
|
-
> timeago(Date.today - 500.days, 4)
|
126
|
-
=> "1 year, 4 months, 1 week and 4 days ago"
|
127
|
-
> timeago(Date.today - 500.days, 1)
|
128
|
-
=> "1 year ago"
|
129
|
-
```
|
130
|
-
|
131
146
|
## License
|
132
|
-
|
133
147
|
Copyright (c) 2013-2014 Marc Anguera. Jekyll-Timeago is released under the [MIT](LICENSE) License.
|
data/_config.yml.example
CHANGED
data/jekyll-timeago.gemspec
CHANGED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module Timeago
|
5
|
+
module Filter
|
6
|
+
|
7
|
+
DAYS_PER = {
|
8
|
+
:days => 1,
|
9
|
+
:weeks => 7,
|
10
|
+
:months => 30,
|
11
|
+
:years => 365
|
12
|
+
}
|
13
|
+
|
14
|
+
# Max level of detail
|
15
|
+
# years > months > weeks > days
|
16
|
+
# 1 year and 7 months and 2 weeks and 6 days
|
17
|
+
MAX_DEPTH_LEVEL = 4
|
18
|
+
|
19
|
+
# Default level of detail
|
20
|
+
# 1 month and 5 days, 3 weeks and 2 days, 2 years and 6 months
|
21
|
+
DEFAULT_DEPTH_LEVEL = 2
|
22
|
+
|
23
|
+
def timeago(from, to = Date.today)
|
24
|
+
from = validate_date!(from)
|
25
|
+
to = validate_date!(to)
|
26
|
+
depth = validate_depth!(options[:depth])
|
27
|
+
|
28
|
+
time_ago_to_now(from, to, depth)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate_date!(date)
|
34
|
+
Date.parse(date.to_s)
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_depth!(depth)
|
38
|
+
(1..MAX_DEPTH_LEVEL).include?(depth) or raise("Invalid depth level: #{depth.inspect}")
|
39
|
+
depth
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get plugin configuration from site. Returns an empty hash if not provided.
|
43
|
+
def config
|
44
|
+
@config ||= Jekyll.configuration({}).fetch('jekyll_timeago', {}) rescue {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def options
|
48
|
+
@options ||= {
|
49
|
+
:depth => config['depth'] || DEFAULT_DEPTH_LEVEL,
|
50
|
+
:today => config['day'] || 'today',
|
51
|
+
:yesterday => config['yesterday'] || 'yesterday',
|
52
|
+
:tomorrow => config['tomorrow'] || 'tomorrow',
|
53
|
+
:and => config['and'] ||'and',
|
54
|
+
:suffix => config['suffix'] || 'ago',
|
55
|
+
:prefix => config['prefix'] || '',
|
56
|
+
:suffix_future => config['suffix_future'] || '',
|
57
|
+
:prefix_future => config['prefix_future'] || 'in',
|
58
|
+
:years => config['years'] || 'years',
|
59
|
+
:year => config['year'] || 'year',
|
60
|
+
:months => config['months'] || 'months',
|
61
|
+
:month => config['month'] || 'month',
|
62
|
+
:weeks => config['weeks'] || 'weeks',
|
63
|
+
:week => config['week'] || 'week',
|
64
|
+
:days => config['days'] || 'days',
|
65
|
+
:day => config['day'] || 'day'
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def translate(key)
|
70
|
+
options[key.to_sym]
|
71
|
+
end
|
72
|
+
alias_method :t, :translate
|
73
|
+
|
74
|
+
# Days passed to time ago sentence
|
75
|
+
def time_ago_to_now(from, to, depth)
|
76
|
+
days_passed = (to - from).to_i
|
77
|
+
|
78
|
+
return t(:today) if days_passed == 0
|
79
|
+
return t(:yesterday) if days_passed == 1
|
80
|
+
return t(:tomorrow) if days_passed == -1
|
81
|
+
|
82
|
+
future = days_passed < 0
|
83
|
+
slots = build_time_ago_slots(days_passed.abs, depth)
|
84
|
+
sentence = to_sentence(slots)
|
85
|
+
|
86
|
+
if future
|
87
|
+
"#{t(:prefix_future)} #{sentence} #{t(:suffix_future)}".strip
|
88
|
+
else
|
89
|
+
"#{t(:prefix)} #{sentence} #{t(:suffix)}".strip
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Builds time ranges: ['1 month', '5 days']
|
94
|
+
# - days_passed: integer in absolute
|
95
|
+
# - depth: level of detail
|
96
|
+
# - current_slots: built time slots
|
97
|
+
def build_time_ago_slots(days_passed, depth, current_slots = [])
|
98
|
+
return current_slots if depth == 0 || days_passed == 0
|
99
|
+
|
100
|
+
time_range = days_to_time_range(days_passed)
|
101
|
+
days = DAYS_PER[time_range]
|
102
|
+
num_elems = days_passed / days
|
103
|
+
range_type = if num_elems == 1
|
104
|
+
t(time_range[0...-1]) # singularize key
|
105
|
+
else
|
106
|
+
t(time_range)
|
107
|
+
end
|
108
|
+
|
109
|
+
current_slots << "#{num_elems} #{range_type}"
|
110
|
+
pending_days = days_passed - (num_elems*days)
|
111
|
+
build_time_ago_slots(pending_days, depth - 1, current_slots)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Number of days to minimum period time which can be grouped
|
115
|
+
def days_to_time_range(days_passed)
|
116
|
+
case days_passed.abs
|
117
|
+
when 0...7
|
118
|
+
:days
|
119
|
+
when 7...31
|
120
|
+
:weeks
|
121
|
+
when 31...365
|
122
|
+
:months
|
123
|
+
else
|
124
|
+
:years
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Array to sentence: ['1 month', '1 week', '5 days'] => "1 month, 1 week and 5 days"
|
129
|
+
def to_sentence(slots)
|
130
|
+
if slots.length == 1
|
131
|
+
slots[0]
|
132
|
+
else
|
133
|
+
"#{slots[0...-1].join(', ')} #{t(:and)} #{slots[-1]}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
Liquid::Template.register_filter(Jekyll::Timeago::Filter) if defined?(Liquid)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Jekyll
|
2
|
+
module Timeago
|
3
|
+
class Tag < Liquid::Tag
|
4
|
+
include Jekyll::Timeago::Filter
|
5
|
+
|
6
|
+
def initialize(tag_name, dates, tokens)
|
7
|
+
super
|
8
|
+
@dates = dates.strip.split(' ')
|
9
|
+
end
|
10
|
+
|
11
|
+
def render(context)
|
12
|
+
from, to = @dates[0], @dates[1]
|
13
|
+
|
14
|
+
if to
|
15
|
+
timeago(from, to)
|
16
|
+
else
|
17
|
+
timeago(from)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Liquid::Template.register_tag('timeago', Jekyll::Timeago::Tag) if defined?(Liquid)
|
data/script/console
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path('../../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'jekyll-timeago/filter'
|
7
|
+
include Jekyll::Timeago::Filter
|
8
|
+
|
9
|
+
require 'active_support/core_ext'
|
10
|
+
|
11
|
+
require 'irb'
|
12
|
+
ARGV.clear
|
13
|
+
IRB.start
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-timeago
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-05-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -58,8 +58,11 @@ files:
|
|
58
58
|
- Rakefile
|
59
59
|
- _config.yml.example
|
60
60
|
- jekyll-timeago.gemspec
|
61
|
-
- lib/jekyll
|
62
|
-
- lib/jekyll
|
61
|
+
- lib/jekyll-timeago.rb
|
62
|
+
- lib/jekyll-timeago/filter.rb
|
63
|
+
- lib/jekyll-timeago/tag.rb
|
64
|
+
- lib/jekyll-timeago/version.rb
|
65
|
+
- script/console
|
63
66
|
homepage: https://github.com/markets/jekyll-timeago
|
64
67
|
licenses:
|
65
68
|
- MIT
|
data/lib/jekyll/timeago.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
begin; require "jekyll/timeago/version"; rescue LoadError; end
|
2
|
-
|
3
|
-
module Jekyll
|
4
|
-
module Timeago
|
5
|
-
|
6
|
-
DAYS_PER = {
|
7
|
-
:days => 1,
|
8
|
-
:weeks => 7,
|
9
|
-
:months => 30,
|
10
|
-
:years => 365
|
11
|
-
}
|
12
|
-
|
13
|
-
# Max level of detail
|
14
|
-
# years > months > weeks > days
|
15
|
-
# 1 year and 7 months and 2 weeks and 6 days
|
16
|
-
MAX_DEPTH_LEVEL = 4
|
17
|
-
|
18
|
-
# Default level of detail
|
19
|
-
# 1 month and 5 days, 3 weeks and 2 days, 2 years and 6 months
|
20
|
-
DEFAULT_DEPTH_LEVEL = 2
|
21
|
-
|
22
|
-
def timeago(input, depth = DEFAULT_DEPTH_LEVEL)
|
23
|
-
validate!(input, depth)
|
24
|
-
|
25
|
-
time_ago_to_now(input, depth)
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
# Validates inputs
|
31
|
-
def validate!(input, depth)
|
32
|
-
unless depth_allowed?(depth)
|
33
|
-
raise "Invalid depth level: #{depth.inspect}"
|
34
|
-
end
|
35
|
-
|
36
|
-
unless input.is_a?(Date) || input.is_a?(Time)
|
37
|
-
raise "Invalid input type: #{input.inspect}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Get plugin configuration from site. Returns an empty hash if not provided.
|
42
|
-
def config
|
43
|
-
@config ||= Jekyll.configuration({}).fetch('jekyll_timeago', {})
|
44
|
-
end
|
45
|
-
|
46
|
-
def strings
|
47
|
-
{
|
48
|
-
:today => config['day'] || 'today',
|
49
|
-
:yesterday => config['yesterday'] || 'yesterday',
|
50
|
-
:tomorrow => config['tomorrow'] || 'tomorrow',
|
51
|
-
:and => config['and'] ||'and',
|
52
|
-
:suffix => config['suffix'] || 'ago',
|
53
|
-
:prefix => config['prefix'] || '',
|
54
|
-
:suffix_future => config['suffix_future'] || '',
|
55
|
-
:prefix_future => config['prefix_future'] || 'in',
|
56
|
-
:years => config['years'] || 'years',
|
57
|
-
:year => config['year'] || 'year',
|
58
|
-
:months => config['months'] || 'months',
|
59
|
-
:month => config['month'] || 'month',
|
60
|
-
:weeks => config['weeks'] || 'weeks',
|
61
|
-
:week => config['week'] || 'week',
|
62
|
-
:days => config['days'] || 'days',
|
63
|
-
:day => config['day'] || 'day'
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
def translate(key)
|
68
|
-
strings[key.to_sym]
|
69
|
-
end
|
70
|
-
alias_method :t, :translate
|
71
|
-
|
72
|
-
# Days passed to time ago sentence
|
73
|
-
def time_ago_to_now(input_date, depth)
|
74
|
-
days_passed = (Date.today - Date.parse(input_date.to_s)).to_i
|
75
|
-
|
76
|
-
return t(:today) if days_passed == 0
|
77
|
-
return t(:yesterday) if days_passed == 1
|
78
|
-
return t(:tomorrow) if days_passed == -1
|
79
|
-
|
80
|
-
future = days_passed < 0
|
81
|
-
slots = build_time_ago_slots(days_passed.abs, depth)
|
82
|
-
sentence = to_sentence(slots)
|
83
|
-
|
84
|
-
if future
|
85
|
-
"#{t(:prefix_future)} #{sentence} #{t(:suffix_future)}".strip
|
86
|
-
else
|
87
|
-
"#{t(:prefix)} #{sentence} #{t(:suffix)}".strip
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Builds time ranges: ['1 month', '5 days']
|
92
|
-
# - days_passed: integer in absolute
|
93
|
-
# - depth: level of detail
|
94
|
-
# - current_slots: built time slots
|
95
|
-
def build_time_ago_slots(days_passed, depth, current_slots = [])
|
96
|
-
return current_slots if depth == 0 || days_passed == 0
|
97
|
-
|
98
|
-
time_range = days_to_time_range(days_passed)
|
99
|
-
days = DAYS_PER[time_range]
|
100
|
-
num_elems = days_passed / days
|
101
|
-
range_type = if num_elems == 1
|
102
|
-
t(time_range[0...-1]) # singularize key
|
103
|
-
else
|
104
|
-
t(time_range)
|
105
|
-
end
|
106
|
-
|
107
|
-
current_slots << "#{num_elems} #{range_type}"
|
108
|
-
pending_days = days_passed - (num_elems*days)
|
109
|
-
build_time_ago_slots(pending_days, depth - 1, current_slots)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Number of days to minimum period time which can be grouped
|
113
|
-
def days_to_time_range(days_passed)
|
114
|
-
case days_passed.abs
|
115
|
-
when 0...7
|
116
|
-
:days
|
117
|
-
when 7...31
|
118
|
-
:weeks
|
119
|
-
when 31...365
|
120
|
-
:months
|
121
|
-
else
|
122
|
-
:years
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
# Validate if allowed level of detail
|
127
|
-
def depth_allowed?(depth)
|
128
|
-
(1..MAX_DEPTH_LEVEL).include?(depth)
|
129
|
-
end
|
130
|
-
|
131
|
-
# Array to sentence: ['1 month', '1 week', '5 days'] => "1 month, 1 week and 5 days"
|
132
|
-
def to_sentence(slots)
|
133
|
-
if slots.length == 1
|
134
|
-
slots[0]
|
135
|
-
else
|
136
|
-
"#{slots[0...-1].join(', ')} #{t(:and)} #{slots[-1]}"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
Liquid::Template.register_filter(Jekyll::Timeago) if defined?(Liquid)
|