jekyll-recker 2.1.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/assets/site.css +114 -0
- data/lib/jekyll-recker.rb +7 -1
- data/lib/jekyll_recker/commands.rb +2 -2
- data/lib/jekyll_recker/date.rb +25 -0
- data/lib/jekyll_recker/entry.rb +44 -0
- data/lib/jekyll_recker/filters.rb +1 -3
- data/lib/jekyll_recker/generators.rb +109 -161
- data/lib/jekyll_recker/graphs.rb +88 -0
- data/lib/jekyll_recker/logging.rb +20 -0
- data/lib/jekyll_recker/math.rb +23 -0
- data/lib/jekyll_recker/site.rb +93 -0
- data/lib/jekyll_recker/social.rb +35 -28
- data/lib/jekyll_recker/tags.rb +15 -0
- data/lib/jekyll_recker/version.rb +1 -1
- data/tmp/.gitignore +2 -0
- metadata +67 -3
- data/lib/jekyll_recker/mixins.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94c8ec595131e2f6ae35840a4717fed7d8795e7756068121719b5b13546ad32c
|
4
|
+
data.tar.gz: 7b17a5e58aa2c2f62f284c69e46b0ec7bfbe6db25b825d018f2c7bbab1509eb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ee477dad55b742aa114c8210898c287a3926e10d3f4cdf01dc363ad568f4565769ea8e8a0fba71083086546e2bbf379bcfe2bb690865528996de4fd90affa8f
|
7
|
+
data.tar.gz: 3a173b2a612f8a92d0293cd4bc6f30751b8a7ef59acf99ffa40da367a2f6f8e47878a8acf5d817108c51599d845335886f4ba3f04fd52e4892dfd9dd0807de47
|
data/assets/site.css
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
body {
|
2
|
+
max-width: 750px;
|
3
|
+
margin: 0 auto;
|
4
|
+
padding: 10px;
|
5
|
+
font-size: 16px;
|
6
|
+
line-height: 1.5;
|
7
|
+
-webkit-font-smoothing: antialiased;
|
8
|
+
-moz-osx-font-smoothing: grayscale;
|
9
|
+
}
|
10
|
+
|
11
|
+
* {
|
12
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
13
|
+
}
|
14
|
+
|
15
|
+
.post p {
|
16
|
+
font-size: 18px;
|
17
|
+
}
|
18
|
+
|
19
|
+
code, pre, .code {
|
20
|
+
font-family: monospace;
|
21
|
+
}
|
22
|
+
|
23
|
+
.title {
|
24
|
+
margin: 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
.subtitle {
|
28
|
+
font-size: 18px;
|
29
|
+
margin: 0;
|
30
|
+
}
|
31
|
+
|
32
|
+
blockquote, small, figcaption, .subtitle {
|
33
|
+
color: #6f7370;
|
34
|
+
}
|
35
|
+
|
36
|
+
figure {
|
37
|
+
text-align: left;
|
38
|
+
}
|
39
|
+
|
40
|
+
figure img {
|
41
|
+
width: 100%;
|
42
|
+
height: auto;
|
43
|
+
max-width: 625px;
|
44
|
+
margin-right: auto;
|
45
|
+
display: block;
|
46
|
+
}
|
47
|
+
|
48
|
+
figure figcaption p {
|
49
|
+
font-style: italic;
|
50
|
+
line-height: 1.3;
|
51
|
+
margin-top: 10px;
|
52
|
+
}
|
53
|
+
|
54
|
+
figure {
|
55
|
+
text-align: left;
|
56
|
+
margin-top: 20px;
|
57
|
+
margin-bottom: 20px;
|
58
|
+
margin-left: 0;
|
59
|
+
max-width: 400px;
|
60
|
+
}
|
61
|
+
|
62
|
+
.float-right {
|
63
|
+
float: right;
|
64
|
+
}
|
65
|
+
|
66
|
+
.clearfix::after {
|
67
|
+
content: "";
|
68
|
+
clear: both;
|
69
|
+
display: block;
|
70
|
+
}
|
71
|
+
|
72
|
+
.column {
|
73
|
+
float: left;
|
74
|
+
width: 50%;
|
75
|
+
}
|
76
|
+
|
77
|
+
.show-on-mobile {
|
78
|
+
display: none;
|
79
|
+
}
|
80
|
+
|
81
|
+
@media screen and (max-width: 600px) {
|
82
|
+
.column {
|
83
|
+
width: 100%;
|
84
|
+
}
|
85
|
+
|
86
|
+
.hide-on-mobile {
|
87
|
+
display: none;
|
88
|
+
}
|
89
|
+
|
90
|
+
.show-on-mobile {
|
91
|
+
display: block;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
/* Clear floats after the columns */
|
96
|
+
.row:after {
|
97
|
+
content: "";
|
98
|
+
display: table;
|
99
|
+
clear: both;
|
100
|
+
}
|
101
|
+
|
102
|
+
.crowded h3, .crowded h2, .crowded p, .crowded ul {
|
103
|
+
margin-bottom: 0px;
|
104
|
+
margin-top: 5px;
|
105
|
+
}
|
106
|
+
|
107
|
+
td, nav span a {
|
108
|
+
padding-right: 5px;
|
109
|
+
}
|
110
|
+
|
111
|
+
footer small::after {
|
112
|
+
content:"\a";
|
113
|
+
white-space: pre;
|
114
|
+
}
|
data/lib/jekyll-recker.rb
CHANGED
@@ -6,8 +6,13 @@ require 'jekyll'
|
|
6
6
|
#
|
7
7
|
# The greatest jekyll plugin in the world
|
8
8
|
module JekyllRecker
|
9
|
-
autoload :
|
9
|
+
autoload :Date, 'jekyll_recker/date.rb'
|
10
|
+
autoload :Entry, 'jekyll_recker/entry.rb'
|
11
|
+
autoload :Graphs, 'jekyll_recker/graphs.rb'
|
12
|
+
autoload :Logging, 'jekyll_recker/logging.rb'
|
13
|
+
autoload :Math, 'jekyll_recker/math.rb'
|
10
14
|
autoload :Shell, 'jekyll_recker/shell.rb'
|
15
|
+
autoload :Site, 'jekyll_recker/site.rb'
|
11
16
|
autoload :Social, 'jekyll_recker/social.rb'
|
12
17
|
autoload :VERSION, 'jekyll_recker/version.rb'
|
13
18
|
|
@@ -15,4 +20,5 @@ module JekyllRecker
|
|
15
20
|
require 'jekyll_recker/commands.rb'
|
16
21
|
require 'jekyll_recker/filters.rb'
|
17
22
|
require 'jekyll_recker/generators.rb'
|
23
|
+
require 'jekyll_recker/tags.rb'
|
18
24
|
end
|
@@ -5,7 +5,7 @@ module JekyllRecker
|
|
5
5
|
module Commands
|
6
6
|
# Share Command
|
7
7
|
class Share < Jekyll::Command
|
8
|
-
include
|
8
|
+
include Logging
|
9
9
|
|
10
10
|
def self.init_with_program(prog)
|
11
11
|
prog.command(:share) do |c|
|
@@ -17,7 +17,7 @@ module JekyllRecker
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.action(args, options)
|
20
|
-
site = Jekyll::Site.new(configuration_from_options(options))
|
20
|
+
site = ::Jekyll::Site.new(configuration_from_options(options))
|
21
21
|
site.reset
|
22
22
|
site.read
|
23
23
|
Social.action(site, args, options)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllRecker
|
4
|
+
# Date Module
|
5
|
+
module Date
|
6
|
+
def slice_by_consecutive(dates)
|
7
|
+
dates.slice_when { |p, c| c != p - 1 && c != p + 1 }.to_a
|
8
|
+
end
|
9
|
+
|
10
|
+
def calculate_streaks(dates)
|
11
|
+
slice_by_consecutive(dates).map do |pair|
|
12
|
+
first, last = pair.minmax
|
13
|
+
{
|
14
|
+
'days' => (last - first).to_i,
|
15
|
+
'start' => first,
|
16
|
+
'end' => last
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def time_to_date(time)
|
22
|
+
::Date.parse(time.strftime('%Y-%m-%d'))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllRecker
|
4
|
+
# Entry
|
5
|
+
class Entry
|
6
|
+
include Date
|
7
|
+
include Filters
|
8
|
+
|
9
|
+
def initialize(doc)
|
10
|
+
@doc = doc
|
11
|
+
end
|
12
|
+
|
13
|
+
def content
|
14
|
+
@doc.content
|
15
|
+
end
|
16
|
+
|
17
|
+
def date
|
18
|
+
@date ||= time_to_date(@doc.date)
|
19
|
+
end
|
20
|
+
|
21
|
+
def title
|
22
|
+
uyd_date(date)
|
23
|
+
end
|
24
|
+
|
25
|
+
def subtitle
|
26
|
+
@doc.data['title']
|
27
|
+
end
|
28
|
+
|
29
|
+
def url
|
30
|
+
@doc.url
|
31
|
+
end
|
32
|
+
|
33
|
+
def words
|
34
|
+
content.split.map do |token|
|
35
|
+
token.gsub!(/[^0-9a-z ']/i, '')
|
36
|
+
token.downcase
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def word_count
|
41
|
+
@word_count ||= words.size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -3,12 +3,10 @@
|
|
3
3
|
module JekyllRecker
|
4
4
|
# Filters
|
5
5
|
module Filters
|
6
|
-
# Converts a date object to standard Uhh Yeah Dude format.
|
7
6
|
def uyd_date(date)
|
8
|
-
date.strftime('%A, %B
|
7
|
+
date.strftime('%A, %B %-d %Y')
|
9
8
|
end
|
10
9
|
|
11
|
-
# Adds commas to a number
|
12
10
|
def pretty(num)
|
13
11
|
num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
14
12
|
end
|
@@ -1,215 +1,163 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'fastimage'
|
4
|
-
require 'mini_magick'
|
5
|
-
|
6
3
|
module JekyllRecker
|
4
|
+
# Generators Module
|
7
5
|
module Generators
|
8
|
-
#
|
9
|
-
class
|
10
|
-
include
|
6
|
+
# Stats Generator
|
7
|
+
class Stats < Jekyll::Generator
|
8
|
+
include Date
|
9
|
+
include Logging
|
10
|
+
include Math
|
11
|
+
|
12
|
+
attr_reader :site
|
11
13
|
|
12
14
|
def generate(site)
|
13
|
-
@site = site
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
@site = Site.new(site)
|
16
|
+
info 'calculating statistics'
|
17
|
+
site.data['stats'] = stats
|
18
|
+
end
|
19
|
+
|
20
|
+
def stats
|
21
|
+
@stats ||= {
|
22
|
+
'total_words' => total(site.word_counts),
|
23
|
+
'average_words' => average(site.word_counts),
|
24
|
+
'total_posts' => site.entries.size,
|
25
|
+
'consecutive_posts' => calculate_streaks(site.dates).first['days'],
|
26
|
+
'swears' => calculate_swears
|
27
|
+
}
|
20
28
|
end
|
21
29
|
|
22
|
-
|
23
|
-
['.jpg', 'jpeg', '.png', '.svg'].include? File.extname(file)
|
24
|
-
end
|
30
|
+
private
|
25
31
|
|
26
|
-
def
|
27
|
-
|
32
|
+
def calculate_swears
|
33
|
+
results = Hash[count_swears]
|
34
|
+
results['total'] = total(results.values)
|
35
|
+
results
|
28
36
|
end
|
29
37
|
|
30
|
-
def
|
31
|
-
|
38
|
+
def count_swears
|
39
|
+
occurences(swears, site.words).reject { |_k, v| v.zero? }.sort_by { |_k, v| -v }
|
32
40
|
end
|
33
41
|
|
34
|
-
def
|
35
|
-
|
36
|
-
with_sizes.select! { |f| too_big?(f[1], f[2]) }
|
37
|
-
with_sizes.map do |f, w, h|
|
38
|
-
dimensions = if w > h
|
39
|
-
'800x600'
|
40
|
-
else
|
41
|
-
'600x800'
|
42
|
-
end
|
43
|
-
[f, dimensions]
|
44
|
-
end
|
42
|
+
def swears
|
43
|
+
site.recker_config.fetch('swears', [])
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
# @abstract
|
52
|
-
module Stats
|
53
|
-
include Mixins::Logging
|
54
|
-
include Jekyll::Filters
|
47
|
+
# Graphs Generator
|
48
|
+
class Graphs < Jekyll::Generator
|
49
|
+
include Logging
|
55
50
|
|
56
|
-
|
57
|
-
self.class.const_get(:KEY)
|
58
|
-
end
|
51
|
+
attr_reader :site
|
59
52
|
|
60
53
|
def generate(site)
|
61
|
-
@site = site
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
@site = Site.new(site)
|
55
|
+
if @site.production? && @site.recker_config.fetch('production_skip_graphs', true)
|
56
|
+
info 'skipping graphs (production)'
|
57
|
+
else
|
58
|
+
info 'generating graphs'
|
59
|
+
JekyllRecker::Graphs.generate_graphs(@site)
|
60
|
+
end
|
65
61
|
end
|
62
|
+
end
|
66
63
|
|
67
|
-
|
68
|
-
|
69
|
-
|
64
|
+
# Image Resize Generator
|
65
|
+
class ImageResize < Jekyll::Generator
|
66
|
+
require 'fastimage'
|
67
|
+
require 'mini_magick'
|
70
68
|
|
71
|
-
|
72
|
-
#
|
73
|
-
# @param [Array<Numeric>] numlist list of numbers to be averaged.
|
74
|
-
# @return [Numeric] rounded, calculated average of numlist.
|
75
|
-
def average(numlist)
|
76
|
-
calc = numlist.inject { |sum, el| sum + el }.to_f / numlist.size
|
77
|
-
calc.round
|
78
|
-
end
|
69
|
+
include Logging
|
79
70
|
|
80
|
-
|
81
|
-
#
|
82
|
-
# @param [Array<Numeric>] numlist list of numbers to be totaled.
|
83
|
-
# @return [Numeric] calculated total of numlist.
|
84
|
-
def total(numlist)
|
85
|
-
numlist.inject(0) { |sum, x| sum + x }
|
86
|
-
end
|
71
|
+
attr_reader :site
|
87
72
|
|
88
|
-
def
|
89
|
-
@site.
|
73
|
+
def generate(site)
|
74
|
+
@site = Site.new(site)
|
75
|
+
if @site.production? && @site.recker_config.fetch('production_skip_images', true)
|
76
|
+
info 'skipping image resizing (production)'
|
77
|
+
else
|
78
|
+
info 'checking images sizes'
|
79
|
+
resizeable_images.each do |f, d|
|
80
|
+
info "resizing #{f} to fit #{d}"
|
81
|
+
image = MiniMagick::Image.new(f)
|
82
|
+
image.resize d
|
83
|
+
end
|
84
|
+
end
|
90
85
|
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# Post Count Generator
|
94
|
-
class PostCount < Jekyll::Generator
|
95
|
-
include Stats
|
96
|
-
|
97
|
-
KEY = 'posts'
|
98
86
|
|
99
|
-
def
|
100
|
-
|
87
|
+
def too_big?(width, height)
|
88
|
+
width > 800 || height > 800
|
101
89
|
end
|
102
|
-
end
|
103
90
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
KEY = 'words'
|
91
|
+
def images_without_graphs
|
92
|
+
site.images.reject { |i| i.include?('/graphs/') }
|
93
|
+
end
|
109
94
|
|
110
|
-
def
|
111
|
-
|
112
|
-
{
|
113
|
-
|
114
|
-
|
115
|
-
|
95
|
+
def resizeable_images
|
96
|
+
with_sizes = images_without_graphs.map { |f| [f, FastImage.size(f)].flatten }
|
97
|
+
with_sizes.select! { |f| too_big?(f[1], f[2]) }
|
98
|
+
with_sizes.map do |f, w, h|
|
99
|
+
dimensions = if w > h
|
100
|
+
'800x600'
|
101
|
+
else
|
102
|
+
'600x800'
|
103
|
+
end
|
104
|
+
[f, dimensions]
|
105
|
+
end
|
116
106
|
end
|
117
107
|
end
|
118
108
|
|
119
|
-
#
|
120
|
-
class
|
121
|
-
include
|
109
|
+
# Code Coverage Generator
|
110
|
+
class CodeCoverage < Jekyll::Generator
|
111
|
+
include Logging
|
122
112
|
|
123
|
-
|
113
|
+
attr_reader :site
|
124
114
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
}
|
132
|
-
end.first
|
115
|
+
def generate(site)
|
116
|
+
@site = Site.new(site)
|
117
|
+
info 'running tests'
|
118
|
+
Shell.run 'rspec'
|
119
|
+
info 'reading code coverage'
|
120
|
+
@site.data['coverage'] = JSON.parse(File.read(tmp_file))
|
133
121
|
end
|
134
122
|
|
135
123
|
private
|
136
124
|
|
137
|
-
def
|
138
|
-
|
139
|
-
entry_dates.slice_when do |prev, curr|
|
140
|
-
curr != prev - 1
|
141
|
-
end.each do |dates|
|
142
|
-
first, last = dates.minmax
|
143
|
-
_streaks << [(last - first).to_i, [first, last]]
|
144
|
-
end
|
145
|
-
_streaks
|
146
|
-
end
|
147
|
-
|
148
|
-
def entry_dates
|
149
|
-
entries.collect(&:date).map { |t| Date.new(t.year, t.month, t.day) }.sort.reverse
|
125
|
+
def tmp_file
|
126
|
+
site.tmp_join('coverage.json')
|
150
127
|
end
|
151
128
|
end
|
152
129
|
|
153
|
-
#
|
154
|
-
class
|
155
|
-
include
|
130
|
+
# Yard Generator
|
131
|
+
class Yard < Jekyll::Generator
|
132
|
+
include Logging
|
156
133
|
|
157
|
-
|
134
|
+
attr_reader :site
|
158
135
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
swears.each do |swear|
|
164
|
-
count = words.count(swear)
|
165
|
-
results[swear] += count
|
166
|
-
end
|
167
|
-
end
|
168
|
-
results.reject { |_k, v| v.zero? }.sort_by { |_k, v| -v }
|
169
|
-
end
|
170
|
-
|
171
|
-
private
|
172
|
-
|
173
|
-
def swears
|
174
|
-
[
|
175
|
-
'ass',
|
176
|
-
'asshole',
|
177
|
-
'booger',
|
178
|
-
'crap',
|
179
|
-
'damn',
|
180
|
-
'fart',
|
181
|
-
'fuck',
|
182
|
-
'hell',
|
183
|
-
'jackass',
|
184
|
-
'piss',
|
185
|
-
'poop',
|
186
|
-
'shit',
|
187
|
-
]
|
136
|
+
def generate(site)
|
137
|
+
@site = Site.new(site)
|
138
|
+
info 'generating documentation'
|
139
|
+
Shell.run "yard -o #{@site.site_join('doc')} -q"
|
188
140
|
end
|
189
141
|
end
|
190
142
|
|
191
|
-
#
|
192
|
-
class
|
193
|
-
include
|
143
|
+
# Git History Generator
|
144
|
+
class GitHistory < Jekyll::Generator
|
145
|
+
include Logging
|
194
146
|
|
195
|
-
|
147
|
+
attr_reader :site
|
196
148
|
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
end
|
204
|
-
results['size'] = bytes_to_megabytes(results['size'])
|
205
|
-
results
|
149
|
+
def generate(site)
|
150
|
+
@site = Site.new(site)
|
151
|
+
info 'reading git history'
|
152
|
+
site.data['git'] = {
|
153
|
+
'commit_count' => commit_count
|
154
|
+
}
|
206
155
|
end
|
207
156
|
|
208
|
-
|
209
|
-
|
210
|
-
def bytes_to_megabytes(bytes)
|
211
|
-
(bytes / (1024.0 * 1024.0)).to_f.round(4)
|
157
|
+
def commit_count
|
158
|
+
@commit_count ||= Shell.run('git rev-list --count master').chomp
|
212
159
|
end
|
213
160
|
end
|
214
161
|
end
|
215
162
|
end
|
163
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllRecker
|
4
|
+
# Graphs module
|
5
|
+
module Graphs
|
6
|
+
def self.generate_graphs(site)
|
7
|
+
require 'gruff'
|
8
|
+
WordCount.new(site).write
|
9
|
+
Swears.new(site).write
|
10
|
+
end
|
11
|
+
|
12
|
+
# Base Graph
|
13
|
+
module Base
|
14
|
+
attr_reader :site
|
15
|
+
|
16
|
+
def graphs_join(path)
|
17
|
+
File.join site.root, @graphs_dir, path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Word Count Graph
|
22
|
+
class WordCount
|
23
|
+
include Base
|
24
|
+
|
25
|
+
def initialize(site)
|
26
|
+
@site = site
|
27
|
+
@graphs_dir = site.graphs_dir
|
28
|
+
end
|
29
|
+
|
30
|
+
def posts
|
31
|
+
site.entries[0..6].reverse
|
32
|
+
end
|
33
|
+
|
34
|
+
def word_counts
|
35
|
+
site.word_counts[0..6].reverse
|
36
|
+
end
|
37
|
+
|
38
|
+
def title
|
39
|
+
format = '%m/%d/%y'
|
40
|
+
first = posts.first.date.strftime(format)
|
41
|
+
last = posts.last.date.strftime(format)
|
42
|
+
"Word Count: #{first} - #{last}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def labels
|
46
|
+
Hash[posts.each_with_index.map { |p, i| [i, p.date.strftime('%a')] }]
|
47
|
+
end
|
48
|
+
|
49
|
+
def write
|
50
|
+
g = ::Gruff::Line.new('800x600')
|
51
|
+
g.theme = Gruff::Themes::PASTEL
|
52
|
+
g.hide_legend = true
|
53
|
+
g.labels = labels
|
54
|
+
g.data :words, word_counts
|
55
|
+
g.title = title
|
56
|
+
g.x_axis_label = 'Day'
|
57
|
+
g.y_axis_label = 'Word Count'
|
58
|
+
g.minimum_value = 0
|
59
|
+
g.write(graphs_join('words.png'))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Swears Chart
|
64
|
+
class Swears
|
65
|
+
include Base
|
66
|
+
|
67
|
+
def initialize(site)
|
68
|
+
@site = site
|
69
|
+
end
|
70
|
+
|
71
|
+
def results
|
72
|
+
data = site.data['stats']['swears'].clone
|
73
|
+
data.delete('total')
|
74
|
+
data
|
75
|
+
end
|
76
|
+
|
77
|
+
def write
|
78
|
+
g = ::Gruff::Pie.new('800x600')
|
79
|
+
g.theme = Gruff::Themes::PASTEL
|
80
|
+
g.hide_legend = false
|
81
|
+
g.legend_at_bottom = true
|
82
|
+
g.minimum_value = 0
|
83
|
+
results.each { |w, n| g.data w, n }
|
84
|
+
g.write(site.graphs_join('swears.png'))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module JekyllRecker
|
6
|
+
# Logging
|
7
|
+
module Logging
|
8
|
+
def self.included(base)
|
9
|
+
base.extend(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def info(msg)
|
13
|
+
Jekyll.logger.info 'jekyll-recker:', msg
|
14
|
+
end
|
15
|
+
|
16
|
+
def logger
|
17
|
+
::Jekyll.logger
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllRecker
|
4
|
+
# Math Module
|
5
|
+
module Math
|
6
|
+
def average(numlist)
|
7
|
+
calc = numlist.inject { |sum, el| sum + el }.to_f / numlist.size
|
8
|
+
calc.round
|
9
|
+
end
|
10
|
+
|
11
|
+
def total(numlist)
|
12
|
+
numlist.inject(0) { |sum, x| sum + x }
|
13
|
+
end
|
14
|
+
|
15
|
+
def occurences(keys, targets)
|
16
|
+
results = Hash.new(0)
|
17
|
+
targets.each do |target|
|
18
|
+
results[target] += 1 if keys.include? target
|
19
|
+
end
|
20
|
+
results
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllRecker
|
4
|
+
# Site
|
5
|
+
class Site
|
6
|
+
def initialize(site)
|
7
|
+
@site = site
|
8
|
+
end
|
9
|
+
|
10
|
+
def entries
|
11
|
+
@entries ||= build_entries
|
12
|
+
end
|
13
|
+
|
14
|
+
def latest
|
15
|
+
entries.first
|
16
|
+
end
|
17
|
+
|
18
|
+
def production?
|
19
|
+
ENV['JEKYLL_ENV'] == 'production'
|
20
|
+
end
|
21
|
+
|
22
|
+
def data
|
23
|
+
@site.data
|
24
|
+
end
|
25
|
+
|
26
|
+
def url
|
27
|
+
@site.config['url']
|
28
|
+
end
|
29
|
+
|
30
|
+
def word_counts
|
31
|
+
entries.collect(&:word_count)
|
32
|
+
end
|
33
|
+
|
34
|
+
def words
|
35
|
+
entries.collect(&:words).flatten
|
36
|
+
end
|
37
|
+
|
38
|
+
def dates
|
39
|
+
entries.collect(&:date)
|
40
|
+
end
|
41
|
+
|
42
|
+
def images
|
43
|
+
exts = ['.jpg', 'jpeg', '.png', '.svg']
|
44
|
+
@site.static_files.collect(&:path).select { |f| exts.include? File.extname(f) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def root
|
48
|
+
File.absolute_path(File.join(__dir__, '../../'))
|
49
|
+
end
|
50
|
+
|
51
|
+
def root_join(path)
|
52
|
+
File.join(root, path)
|
53
|
+
end
|
54
|
+
|
55
|
+
def recker_config
|
56
|
+
@site.config.fetch('recker', {})
|
57
|
+
end
|
58
|
+
|
59
|
+
def config
|
60
|
+
@site.config
|
61
|
+
end
|
62
|
+
|
63
|
+
def graphs_dir
|
64
|
+
recker_config.fetch('graphs', 'assets/images/graphs/')
|
65
|
+
end
|
66
|
+
|
67
|
+
def data_dir
|
68
|
+
File.join root, '_data'
|
69
|
+
end
|
70
|
+
|
71
|
+
def tmp_join(path)
|
72
|
+
File.join root, 'tmp', path
|
73
|
+
end
|
74
|
+
|
75
|
+
def graphs_join(path)
|
76
|
+
File.join root, 'assets/images/graphs/', path
|
77
|
+
end
|
78
|
+
|
79
|
+
def site_join(path)
|
80
|
+
File.join(root, '_site', path)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def build_entries
|
86
|
+
@site.posts.docs
|
87
|
+
.select(&:published?)
|
88
|
+
.sort_by(&:date)
|
89
|
+
.reverse
|
90
|
+
.map { |p| Entry.new(p) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/jekyll_recker/social.rb
CHANGED
@@ -4,6 +4,7 @@ require 'slack-notifier'
|
|
4
4
|
require 'twitter'
|
5
5
|
|
6
6
|
module JekyllRecker
|
7
|
+
# Social Module
|
7
8
|
module Social
|
8
9
|
def self.action(site, args, options)
|
9
10
|
args += %w[slack twitter] if args.empty?
|
@@ -16,19 +17,21 @@ module JekyllRecker
|
|
16
17
|
# Backend base class for social sharing backends.
|
17
18
|
# @abstract
|
18
19
|
class Share
|
19
|
-
include
|
20
|
+
include Logging
|
21
|
+
|
22
|
+
attr_reader :site
|
20
23
|
|
21
24
|
def self.share(site, dry: false)
|
22
25
|
backend = new(site, dry: dry)
|
23
|
-
|
26
|
+
info "#{backend.name} - building configuration"
|
24
27
|
backend.configure!
|
25
28
|
|
26
|
-
|
29
|
+
info "#{backend.name} - sharing \"#{backend.latest_title}\""
|
27
30
|
backend.post!
|
28
31
|
end
|
29
32
|
|
30
33
|
def initialize(site, dry: false)
|
31
|
-
@site = site
|
34
|
+
@site = Site.new(site)
|
32
35
|
@dry = dry
|
33
36
|
end
|
34
37
|
|
@@ -37,29 +40,28 @@ module JekyllRecker
|
|
37
40
|
end
|
38
41
|
|
39
42
|
def config
|
40
|
-
|
43
|
+
site.recker_config.fetch(config_key, {})
|
41
44
|
end
|
42
45
|
|
43
46
|
def config_key
|
44
47
|
self.class.const_get(:KEY)
|
45
48
|
end
|
46
|
-
alias name
|
49
|
+
alias name config_key
|
47
50
|
|
48
51
|
def post_body
|
49
|
-
url = File.join @site.config['url'], latest.url
|
50
52
|
<<~BODY
|
51
|
-
#{latest.
|
52
|
-
#{latest.
|
53
|
-
#{url}
|
53
|
+
#{latest.title}
|
54
|
+
#{latest.subtitle}
|
55
|
+
#{File.join site.url, latest.url}
|
54
56
|
BODY
|
55
57
|
end
|
56
58
|
|
57
|
-
def
|
58
|
-
|
59
|
+
def latest_title
|
60
|
+
latest.title
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
62
|
-
latest
|
63
|
+
def latest
|
64
|
+
site.latest
|
63
65
|
end
|
64
66
|
|
65
67
|
def configure!
|
@@ -81,31 +83,36 @@ module JekyllRecker
|
|
81
83
|
@creds = {}
|
82
84
|
workspaces.each do |key, data|
|
83
85
|
webhook = ENV["SLACK_#{key.upcase}_WEBHOOK"] || extract_from_config(data)
|
84
|
-
if webhook.nil?
|
85
|
-
raise "cannot find slack webhook for #{key} workspace!"
|
86
|
-
end
|
86
|
+
raise "cannot find slack webhook for #{key} workspace!" if webhook.nil?
|
87
87
|
|
88
88
|
@creds[key] = webhook
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
92
|
def post!
|
93
|
-
message_body = ::Slack::Notifier::Util::LinkFormatter.format(post_body)
|
94
93
|
workspaces.each do |key, config|
|
95
|
-
|
94
|
+
info "posting to #{key} workspace"
|
96
95
|
if @dry
|
97
|
-
|
96
|
+
puts "BEGIN MESSAGE\n#{post_body.strip}\nEND MESSAGE"
|
98
97
|
else
|
99
|
-
|
100
|
-
@creds[key].strip,
|
101
|
-
channel: config.fetch('channel'),
|
102
|
-
username: config.fetch('username'),
|
103
|
-
icon_emoji: config.fetch('emoji')
|
104
|
-
).post(text: message_body)
|
98
|
+
post(key, config)
|
105
99
|
end
|
106
100
|
end
|
107
101
|
end
|
108
102
|
|
103
|
+
def post(key, config)
|
104
|
+
::Slack::Notifier.new(
|
105
|
+
@creds[key].strip,
|
106
|
+
channel: config.fetch('channel'),
|
107
|
+
username: config.fetch('username'),
|
108
|
+
icon_emoji: config.fetch('emoji')
|
109
|
+
).post(text: message_body)
|
110
|
+
end
|
111
|
+
|
112
|
+
def post_body
|
113
|
+
::Slack::Notifier::Util::LinkFormatter.format(super)
|
114
|
+
end
|
115
|
+
|
109
116
|
private
|
110
117
|
|
111
118
|
def extract_from_config(data)
|
@@ -140,8 +147,8 @@ module JekyllRecker
|
|
140
147
|
|
141
148
|
def post!
|
142
149
|
if dry?
|
143
|
-
|
144
|
-
|
150
|
+
info('tweeting in dry mode, printing message')
|
151
|
+
puts "BEGIN TWEET\n#{post_body}END TWEET"
|
145
152
|
else
|
146
153
|
@client.update(post_body)
|
147
154
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllRecker
|
4
|
+
# Tags
|
5
|
+
module Tags
|
6
|
+
# Render the current plugin version
|
7
|
+
class Version < Liquid::Tag
|
8
|
+
def render(_ctx)
|
9
|
+
"v#{JekyllRecker::VERSION}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Liquid::Template.register_tag('version', JekyllRecker::Tags::Version)
|
data/tmp/.gitignore
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-recker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Recker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fastimage
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: gruff
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: jekyll
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +66,34 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: slack-notifier
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +122,20 @@ dependencies:
|
|
80
122
|
- - ">="
|
81
123
|
- !ruby/object:Gem::Version
|
82
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: yard
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
83
139
|
description:
|
84
140
|
email:
|
85
141
|
- alex@reckerfamily.com
|
@@ -87,14 +143,22 @@ executables: []
|
|
87
143
|
extensions: []
|
88
144
|
extra_rdoc_files: []
|
89
145
|
files:
|
146
|
+
- assets/site.css
|
90
147
|
- lib/jekyll-recker.rb
|
91
148
|
- lib/jekyll_recker/commands.rb
|
149
|
+
- lib/jekyll_recker/date.rb
|
150
|
+
- lib/jekyll_recker/entry.rb
|
92
151
|
- lib/jekyll_recker/filters.rb
|
93
152
|
- lib/jekyll_recker/generators.rb
|
94
|
-
- lib/jekyll_recker/
|
153
|
+
- lib/jekyll_recker/graphs.rb
|
154
|
+
- lib/jekyll_recker/logging.rb
|
155
|
+
- lib/jekyll_recker/math.rb
|
95
156
|
- lib/jekyll_recker/shell.rb
|
157
|
+
- lib/jekyll_recker/site.rb
|
96
158
|
- lib/jekyll_recker/social.rb
|
159
|
+
- lib/jekyll_recker/tags.rb
|
97
160
|
- lib/jekyll_recker/version.rb
|
161
|
+
- tmp/.gitignore
|
98
162
|
homepage: https://www.github.com/arecker/blog
|
99
163
|
licenses:
|
100
164
|
- GPLv3
|
data/lib/jekyll_recker/mixins.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
module JekyllRecker
|
6
|
-
module Mixins
|
7
|
-
# Logging
|
8
|
-
module Logging
|
9
|
-
def self.included(base)
|
10
|
-
base.extend(self)
|
11
|
-
end
|
12
|
-
|
13
|
-
def logger
|
14
|
-
@logger ||= Logger.new(
|
15
|
-
STDOUT,
|
16
|
-
formatter: proc { |_severity, _datetime, _progname, msg| "jekyll-recker: #{msg}\n" }
|
17
|
-
)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|