jekyll-recker 1.6.0 → 1.11.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/_includes/figure.html +1 -1
- data/lib/jekyll-recker.rb +14 -18
- data/lib/jekyll_recker/commands.rb +18 -26
- data/lib/jekyll_recker/configuration.rb +26 -28
- data/lib/jekyll_recker/filters.rb +12 -9
- data/lib/jekyll_recker/generators.rb +125 -84
- data/lib/jekyll_recker/mixins.rb +30 -19
- data/lib/jekyll_recker/shell.rb +5 -8
- data/lib/jekyll_recker/social.rb +175 -0
- data/lib/jekyll_recker/tags.rb +7 -9
- data/lib/jekyll_recker/version.rb +2 -4
- metadata +18 -9
- data/lib/jekyll_recker/error.rb +0 -8
- data/lib/jekyll_recker/facebook.rb +0 -97
- data/lib/jekyll_recker/logger.rb +0 -20
- data/lib/jekyll_recker/slack.rb +0 -79
- data/lib/jekyll_recker/twitter.rb +0 -91
- data/lib/jekyll_recker/words.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15d0b79e8f6f9370433856ad0d89a1ae7996d0900644739fa915bd5f155e60c9
|
4
|
+
data.tar.gz: 630418f988d9e17ac451be9932aa6e3f881cb71a37570b7d4e70bd3f1b629477
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 141f627ae47682e8cd42320cd522693e33f01970f8f229c7394f26d66e1a13284ac1f73f8ac90fc4361093dccef209698a89ea168be299b9fae264efe6106523
|
7
|
+
data.tar.gz: 9563f420e21f8a343c409521b06065c5d36f8aad3f503303511e913ec37b1d15ee476819bcffa1ccf4a6c8781b6c1da8d52b474c25dbc007a68e3d27395412aa
|
data/_includes/figure.html
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
{%- if include.url %}
|
3
3
|
<a href="{{ include.url }}">
|
4
4
|
{%- endif %}
|
5
|
-
<img alt="{{ alt | default: include.filename }}" src="{{ site.baseurl }}assets/images/{{ include.filename }}"/>
|
5
|
+
<img alt="{{ include.alt | default: include.filename }}" src="{{ site.baseurl }}assets/images/{{ include.filename }}"/>
|
6
6
|
{%- if include.url %}
|
7
7
|
</a>
|
8
8
|
{%- endif %}
|
data/lib/jekyll-recker.rb
CHANGED
@@ -2,23 +2,19 @@
|
|
2
2
|
|
3
3
|
require 'jekyll'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# jekyll-recker
|
6
|
+
#
|
7
|
+
# The greatest jekyll plugin in the world
|
8
|
+
module JekyllRecker
|
9
|
+
autoload :Configuration, 'jekyll_recker/configuration.rb'
|
10
|
+
autoload :Mixins, 'jekyll_recker/mixins.rb'
|
11
|
+
autoload :Shell, 'jekyll_recker/shell.rb'
|
12
|
+
autoload :Social, 'jekyll_recker/social.rb'
|
13
|
+
autoload :VERSION, 'jekyll_recker/version.rb'
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
require 'jekyll_recker/generators.rb'
|
17
|
-
require 'jekyll_recker/shell.rb'
|
18
|
-
require 'jekyll_recker/slack.rb'
|
19
|
-
require 'jekyll_recker/tags.rb'
|
20
|
-
require 'jekyll_recker/twitter.rb'
|
21
|
-
require 'jekyll_recker/version.rb'
|
22
|
-
require 'jekyll_recker/words.rb'
|
23
|
-
end
|
15
|
+
# Eager loads!
|
16
|
+
require 'jekyll_recker/commands.rb'
|
17
|
+
require 'jekyll_recker/filters.rb'
|
18
|
+
require 'jekyll_recker/generators.rb'
|
19
|
+
require 'jekyll_recker/tags.rb'
|
24
20
|
end
|
@@ -1,34 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
include Mixins::Logging
|
3
|
+
module JekyllRecker
|
4
|
+
# Commands
|
5
|
+
module Commands
|
6
|
+
# Share
|
7
|
+
class Share < Jekyll::Command
|
8
|
+
include Mixins::Logging
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
10
|
+
def self.init_with_program(prog)
|
11
|
+
prog.command(:share) do |c|
|
12
|
+
c.syntax 'share'
|
13
|
+
c.description 'Share latest post with each configured backend'
|
14
|
+
c.option 'dry', '-d', '--dry', 'perform dry run'
|
15
|
+
c.action { |args, opts| action(args, opts) }
|
18
16
|
end
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# elsif args.empty? || args.include?('facebook')
|
26
|
-
# Recker::Facebook.share(dry: options['dry'])
|
27
|
-
end
|
28
|
-
rescue ReckerError => e
|
29
|
-
logger.error e.message
|
30
|
-
exit 1
|
31
|
-
end
|
19
|
+
def self.action(args, options)
|
20
|
+
JekyllRecker::Social.action(args, options)
|
21
|
+
rescue Error => e
|
22
|
+
logger.error e.message
|
23
|
+
exit 1
|
32
24
|
end
|
33
25
|
end
|
34
26
|
end
|
@@ -1,39 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
3
|
+
module JekyllRecker
|
4
|
+
# Configuration
|
5
|
+
module Configuration
|
6
|
+
def self.jekyll
|
7
|
+
@jekyll ||= Jekyll.configuration
|
8
|
+
end
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def self.recker
|
11
|
+
jekyll.fetch('recker', {})
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def self.facebook
|
15
|
+
recker.fetch('facebook', {})
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def self.twitter
|
19
|
+
recker.fetch('twitter', {})
|
20
|
+
end
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def self.slack
|
23
|
+
recker.fetch('slack', {})
|
24
|
+
end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
def self.site
|
27
|
+
@site = Jekyll::Site.new(jekyll)
|
28
|
+
@site.reset
|
29
|
+
@site.read
|
30
|
+
@site
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
end
|
33
|
+
def self.latest_post
|
34
|
+
@latest_post ||= site.posts.docs.last
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
@@ -1,15 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
module JekyllRecker
|
4
|
+
# Filters
|
5
|
+
module Filters
|
6
|
+
# Converts a date object to standard Uhh Yeah Dude format.
|
7
|
+
def uyd_date(date)
|
8
|
+
date.strftime('%A, %B %d %Y')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adds commas to a number
|
12
|
+
def pretty(num)
|
13
|
+
num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
11
14
|
end
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
15
|
-
Liquid::Template.register_filter(
|
18
|
+
Liquid::Template.register_filter(JekyllRecker::Filters)
|
@@ -1,111 +1,152 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
3
|
+
require 'mini_magick'
|
4
|
+
require 'fastimage'
|
5
|
+
|
6
|
+
module JekyllRecker
|
7
|
+
module Generators
|
8
|
+
# Image Resize Generator
|
9
|
+
class ImageResize < Jekyll::Generator
|
10
|
+
include Mixins::Logging
|
11
|
+
|
12
|
+
def generate(site)
|
13
|
+
@site = site
|
14
|
+
logger.info 'checking images'
|
15
|
+
resizeable_images.each do |f, d|
|
16
|
+
logger.info "resizing #{f} to fit #{d}"
|
17
|
+
image = MiniMagick::Image.new(f)
|
18
|
+
image.resize d
|
16
19
|
end
|
20
|
+
end
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@site.data['stats'] ||= {}
|
22
|
-
@site.data['stats'][key] = crunch
|
23
|
-
end
|
22
|
+
def image?(file)
|
23
|
+
['.jpg', 'jpeg', '.png', '.svg'].include? File.extname(file)
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def too_big?(width, height)
|
27
|
+
width > 800 || height > 800
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# @return [Numeric] rounded, calculated average of numlist.
|
33
|
-
def average(numlist)
|
34
|
-
calc = numlist.inject { |sum, el| sum + el }.to_f / numlist.size
|
35
|
-
calc.round
|
36
|
-
end
|
30
|
+
def images
|
31
|
+
@site.static_files.collect(&:path).select { |f| image?(f) }
|
32
|
+
end
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
34
|
+
def resizeable_images
|
35
|
+
with_sizes = images.map { |f| [f, FastImage.size(f)].flatten }
|
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
44
|
end
|
45
|
+
end
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
# Stats Module
|
49
|
+
#
|
50
|
+
# Functions for stats generators.
|
51
|
+
# @abstract
|
52
|
+
module Stats
|
53
|
+
include Mixins::Logging
|
54
|
+
include Jekyll::Filters
|
55
|
+
|
56
|
+
def key
|
57
|
+
self.class.const_get(:KEY)
|
49
58
|
end
|
50
59
|
|
51
|
-
|
52
|
-
|
53
|
-
|
60
|
+
def generate(site)
|
61
|
+
@site = site
|
62
|
+
logger.info "crunching stats.#{key}"
|
63
|
+
@site.data['stats'] ||= {}
|
64
|
+
@site.data['stats'][key] = crunch
|
65
|
+
end
|
54
66
|
|
55
|
-
|
67
|
+
def crunch
|
68
|
+
raise NotImplementedError, '#crunch not implemented!'
|
69
|
+
end
|
56
70
|
|
57
|
-
|
58
|
-
|
59
|
-
|
71
|
+
# Calculates the average of a list of numbers.
|
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
|
79
|
+
|
80
|
+
# Calculates the total of a list of numbers.
|
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 }
|
60
86
|
end
|
61
87
|
|
62
|
-
|
63
|
-
|
64
|
-
|
88
|
+
def entries
|
89
|
+
@site.posts.docs.select(&:published?)
|
90
|
+
end
|
91
|
+
end
|
65
92
|
|
66
|
-
|
93
|
+
# Post Count Generator
|
94
|
+
class PostCount < Jekyll::Generator
|
95
|
+
include Stats
|
67
96
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
'total' => total(total_counts).pretty
|
73
|
-
}
|
74
|
-
end
|
97
|
+
KEY = 'posts'
|
98
|
+
|
99
|
+
def crunch
|
100
|
+
entries.count
|
75
101
|
end
|
102
|
+
end
|
76
103
|
|
77
|
-
|
78
|
-
|
79
|
-
|
104
|
+
# Word Count Generator
|
105
|
+
class Words < Jekyll::Generator
|
106
|
+
include Stats
|
80
107
|
|
81
|
-
|
108
|
+
KEY = 'words'
|
82
109
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
110
|
+
def crunch
|
111
|
+
total_counts = entries.collect(&:content).map { |c| number_of_words(c) }
|
112
|
+
{
|
113
|
+
'average' => average(total_counts),
|
114
|
+
'total' => total(total_counts)
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
92
118
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
curr != prev - 1
|
99
|
-
end.each do |dates|
|
100
|
-
first, last = dates.minmax
|
101
|
-
_streaks << [(last - first).to_i, [first, last]]
|
102
|
-
end
|
103
|
-
_streaks
|
104
|
-
end
|
119
|
+
# Streak Count Generator
|
120
|
+
class Streaks < Jekyll::Generator
|
121
|
+
include Stats
|
122
|
+
|
123
|
+
KEY = 'days'
|
105
124
|
|
106
|
-
|
107
|
-
|
125
|
+
def crunch
|
126
|
+
streaks.take(1).map do |count, dates|
|
127
|
+
{
|
128
|
+
'days' => count,
|
129
|
+
'start' => dates[0],
|
130
|
+
'end' => dates[1]
|
131
|
+
}
|
132
|
+
end.first
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def streaks
|
138
|
+
_streaks = []
|
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]]
|
108
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
|
109
150
|
end
|
110
151
|
end
|
111
152
|
end
|
data/lib/jekyll_recker/mixins.rb
CHANGED
@@ -1,28 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
module Recker
|
5
|
-
module Mixins
|
6
|
-
# Descendants
|
7
|
-
module Descendants
|
8
|
-
def self.included(base)
|
9
|
-
base.extend(self)
|
10
|
-
end
|
3
|
+
require 'logger'
|
11
4
|
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
module JekyllRecker
|
6
|
+
module Mixins
|
7
|
+
# Introspection
|
8
|
+
#
|
9
|
+
# Adds functions which let the class see things about itself.
|
10
|
+
module Introspection
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(self)
|
15
13
|
end
|
16
14
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
# Get the class instance
|
16
|
+
def class_name
|
17
|
+
self.class.name.split('::').last
|
18
|
+
end
|
19
|
+
|
20
|
+
# Get a list of all classes which inherit from this class
|
21
|
+
def descendants
|
22
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Logging
|
27
|
+
module Logging
|
28
|
+
def self.included(base)
|
29
|
+
base.extend(self)
|
30
|
+
end
|
22
31
|
|
23
|
-
|
24
|
-
|
25
|
-
|
32
|
+
def logger
|
33
|
+
@logger ||= Logger.new(
|
34
|
+
STDOUT,
|
35
|
+
formatter: proc { |_severity, _datetime, _progname, msg| "jekyll-recker: #{msg}\n" }
|
36
|
+
)
|
26
37
|
end
|
27
38
|
end
|
28
39
|
end
|
data/lib/jekyll_recker/shell.rb
CHANGED
@@ -2,13 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'open3'
|
4
4
|
|
5
|
-
module
|
6
|
-
#
|
7
|
-
module
|
8
|
-
|
9
|
-
class ShellCommandFailed < ReckerError; end
|
10
|
-
|
11
|
-
def self.shell(cmd)
|
5
|
+
module JekyllRecker
|
6
|
+
# Shell
|
7
|
+
module Shell
|
8
|
+
def self.run(cmd)
|
12
9
|
out, err, status = Open3.capture3(cmd)
|
13
10
|
return out if status.success?
|
14
11
|
|
@@ -22,7 +19,7 @@ module Jekyll
|
|
22
19
|
#{err}
|
23
20
|
ERROR
|
24
21
|
|
25
|
-
raise
|
22
|
+
raise msg
|
26
23
|
end
|
27
24
|
end
|
28
25
|
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'slack-notifier'
|
4
|
+
require 'twitter'
|
5
|
+
|
6
|
+
module JekyllRecker
|
7
|
+
module Social
|
8
|
+
def self.action(args, options)
|
9
|
+
args += %w[slack twitter] if args.empty?
|
10
|
+
Slack.share(dry: options['dry']) if args.include?('slack')
|
11
|
+
Twitter.share(dry: options['dry']) if args.include?('twitter')
|
12
|
+
end
|
13
|
+
# Backend
|
14
|
+
#
|
15
|
+
# Backend base class for social sharing backends.
|
16
|
+
# @abstract
|
17
|
+
class Share
|
18
|
+
include Mixins::Introspection
|
19
|
+
include Mixins::Logging
|
20
|
+
|
21
|
+
def self.share(dry: false)
|
22
|
+
backend = new(dry: dry)
|
23
|
+
logger.info "#{backend.name} - building configuration"
|
24
|
+
backend.configure!
|
25
|
+
|
26
|
+
logger.info "#{backend.name} - sharing \"#{backend.latest_title}\""
|
27
|
+
backend.post!
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(dry: false)
|
31
|
+
@dry = dry
|
32
|
+
end
|
33
|
+
|
34
|
+
def dry?
|
35
|
+
@dry
|
36
|
+
end
|
37
|
+
|
38
|
+
def config
|
39
|
+
@config ||= JekyllRecker::Configuration.recker.fetch(config_key)
|
40
|
+
end
|
41
|
+
|
42
|
+
def config_key
|
43
|
+
class_name.downcase
|
44
|
+
end
|
45
|
+
alias name config_key
|
46
|
+
|
47
|
+
def post_body
|
48
|
+
url = File.join Configuration.jekyll['url'], latest.url
|
49
|
+
<<~BODY
|
50
|
+
#{latest.data['date'].strftime('%A, %B %-d %Y')}
|
51
|
+
#{latest.data['title']}
|
52
|
+
#{url}
|
53
|
+
BODY
|
54
|
+
end
|
55
|
+
|
56
|
+
def latest
|
57
|
+
@latest ||= Configuration.latest_post
|
58
|
+
end
|
59
|
+
|
60
|
+
def latest_title
|
61
|
+
latest.data['title']
|
62
|
+
end
|
63
|
+
|
64
|
+
def configure!
|
65
|
+
raise NotImplementedError
|
66
|
+
end
|
67
|
+
|
68
|
+
def post!
|
69
|
+
raise NotImplementedError
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Slack
|
74
|
+
#
|
75
|
+
# Slack social sharing backend
|
76
|
+
class Slack < Share
|
77
|
+
def configure!
|
78
|
+
@creds = {}
|
79
|
+
workspaces.each do |key, data|
|
80
|
+
webhook = ENV["SLACK_#{key.upcase}_WEBHOOK"] || extract_from_config(data)
|
81
|
+
if webhook.nil?
|
82
|
+
raise "cannot find slack webhook for #{key} workspace!"
|
83
|
+
end
|
84
|
+
|
85
|
+
@creds[key] = webhook
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def post!
|
90
|
+
message_body = ::Slack::Notifier::Util::LinkFormatter.format(post_body)
|
91
|
+
workspaces.each do |key, config|
|
92
|
+
logger.info "posting to #{key} workspace"
|
93
|
+
if @dry
|
94
|
+
logger.info("BEGIN MESSAGE\n#{message_body.strip}\nEND MESSAGE")
|
95
|
+
else
|
96
|
+
::Slack::Notifier.new(
|
97
|
+
@creds[key].strip,
|
98
|
+
channel: config.fetch('channel'),
|
99
|
+
username: config.fetch('username'),
|
100
|
+
icon_emoji: config.fetch('emoji')
|
101
|
+
).post(text: message_body)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def extract_from_config(data)
|
109
|
+
cmd = data['webhook_cmd']
|
110
|
+
return nil if cmd.nil?
|
111
|
+
|
112
|
+
Shell.run(cmd)
|
113
|
+
end
|
114
|
+
|
115
|
+
def workspaces
|
116
|
+
config.each
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Twitter
|
121
|
+
#
|
122
|
+
# Twitter social sharing backend
|
123
|
+
class Twitter < Share
|
124
|
+
def configure!
|
125
|
+
creds = extract_from_env || extract_from_config
|
126
|
+
raise 'cannot find twitter credentials!' if creds.nil?
|
127
|
+
|
128
|
+
@client = ::Twitter::REST::Client.new do |settings|
|
129
|
+
settings.consumer_key = creds['consumer_api_key']
|
130
|
+
settings.consumer_secret = creds['consumer_api_secret']
|
131
|
+
settings.access_token = creds['access_token']
|
132
|
+
settings.access_token_secret = creds['access_token_secret']
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def post!
|
137
|
+
if dry?
|
138
|
+
logger.info('tweeting in dry mode, printing message')
|
139
|
+
logger.info("BEGIN TWEET\n#{post_body}END TWEET")
|
140
|
+
else
|
141
|
+
@client.update(post_body)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def extract_from_env
|
148
|
+
values = cred_fieldnames.map { |k| ENV["TWITTER_#{k.upcase}"] }
|
149
|
+
|
150
|
+
return nil if values.any? { |v| v.nil? || v.empty? }
|
151
|
+
|
152
|
+
Hash[cred_fieldnames.zip(values)]
|
153
|
+
end
|
154
|
+
|
155
|
+
def extract_from_config
|
156
|
+
values = cred_fieldnames.map do |k|
|
157
|
+
Shell.run(Configuration.twitter["#{k}_cmd"]).strip
|
158
|
+
end
|
159
|
+
|
160
|
+
return nil if values.any? { |v| v.nil? || v.empty? }
|
161
|
+
|
162
|
+
Hash[cred_fieldnames.zip(values)]
|
163
|
+
end
|
164
|
+
|
165
|
+
def cred_fieldnames
|
166
|
+
%w[
|
167
|
+
access_token_secret
|
168
|
+
access_token
|
169
|
+
consumer_api_key
|
170
|
+
consumer_api_secret
|
171
|
+
]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
data/lib/jekyll_recker/tags.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
VERSION
|
10
|
-
end
|
3
|
+
module JekyllRecker
|
4
|
+
module Tags
|
5
|
+
# Returns the VERSION of the running jekyll-recker gem.
|
6
|
+
class Version < Liquid::Tag
|
7
|
+
def render(_context)
|
8
|
+
VERSION
|
11
9
|
end
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
15
13
|
|
16
|
-
Liquid::Template.register_tag('recker_version',
|
14
|
+
Liquid::Template.register_tag('recker_version', JekyllRecker::Tags::Version)
|
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: 1.
|
4
|
+
version: 1.11.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-
|
11
|
+
date: 2020-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fastimage
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: jekyll
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,7 +123,7 @@ dependencies:
|
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '3.8'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
126
|
+
name: mini_magick
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
129
|
- - ">="
|
@@ -171,18 +185,13 @@ files:
|
|
171
185
|
- lib/jekyll-recker.rb
|
172
186
|
- lib/jekyll_recker/commands.rb
|
173
187
|
- lib/jekyll_recker/configuration.rb
|
174
|
-
- lib/jekyll_recker/error.rb
|
175
|
-
- lib/jekyll_recker/facebook.rb
|
176
188
|
- lib/jekyll_recker/filters.rb
|
177
189
|
- lib/jekyll_recker/generators.rb
|
178
|
-
- lib/jekyll_recker/logger.rb
|
179
190
|
- lib/jekyll_recker/mixins.rb
|
180
191
|
- lib/jekyll_recker/shell.rb
|
181
|
-
- lib/jekyll_recker/
|
192
|
+
- lib/jekyll_recker/social.rb
|
182
193
|
- lib/jekyll_recker/tags.rb
|
183
|
-
- lib/jekyll_recker/twitter.rb
|
184
194
|
- lib/jekyll_recker/version.rb
|
185
|
-
- lib/jekyll_recker/words.rb
|
186
195
|
homepage: https://www.github.com/arecker/jekyll-recker/
|
187
196
|
licenses:
|
188
197
|
- GPLv3
|
data/lib/jekyll_recker/error.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'koala'
|
4
|
-
|
5
|
-
module Jekyll
|
6
|
-
module Recker
|
7
|
-
# Facebook Client
|
8
|
-
class Facebook
|
9
|
-
include Mixins::Logging
|
10
|
-
|
11
|
-
def self.share(dry: false)
|
12
|
-
client = new(dry: dry)
|
13
|
-
logger.info 'discovering credentials'
|
14
|
-
client.discover_credentials!
|
15
|
-
logger.info "sharing #{client.latest.data['title']}"
|
16
|
-
client.post_latest!
|
17
|
-
end
|
18
|
-
|
19
|
-
def initialize(dry: false)
|
20
|
-
@dry = dry
|
21
|
-
end
|
22
|
-
|
23
|
-
def discover_credentials!
|
24
|
-
@creds = extract_from_env || extract_from_config
|
25
|
-
raise ReckerError, 'cannot find facebook credentials!' if @creds.nil?
|
26
|
-
|
27
|
-
set_credentials!
|
28
|
-
end
|
29
|
-
|
30
|
-
def post_latest!
|
31
|
-
if @dry
|
32
|
-
logger.info('posting in dry mode, printing message')
|
33
|
-
logger.info("BEGIN POST\n#{post_body.strip}\nEND POST")
|
34
|
-
else
|
35
|
-
@graph.put_connections("me", "feed", message: "I am writing on my wall!")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def latest
|
40
|
-
Configuration.latest_post
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def post_body
|
46
|
-
url = File.join Configuration.jekyll['url'], latest.url
|
47
|
-
<<~POST
|
48
|
-
#{latest.data['date'].strftime('%A, %B %-d %Y')}
|
49
|
-
#{latest.data['title']}
|
50
|
-
#{url}
|
51
|
-
POST
|
52
|
-
end
|
53
|
-
|
54
|
-
def set_credentials!
|
55
|
-
Koala.configure do |config|
|
56
|
-
config.access_token = @creds['access_token']
|
57
|
-
# config.app_access_token = @creds['app_access_token']
|
58
|
-
config.app_id = @creds['app_id']
|
59
|
-
config.app_secret = @creds['app_secret']
|
60
|
-
end
|
61
|
-
@client = Koala::Facebook::API.new(@creds['access_token'])
|
62
|
-
end
|
63
|
-
|
64
|
-
def extract_from_env
|
65
|
-
values = cred_fieldnames.map { |k| ENV["FACEBOOK_#{k.upcase}"] }
|
66
|
-
|
67
|
-
return nil if values.any? { |v| v.nil? || v.empty? }
|
68
|
-
|
69
|
-
Hash[cred_fieldnames.zip(values)]
|
70
|
-
end
|
71
|
-
|
72
|
-
def extract_from_config
|
73
|
-
values = cred_fieldnames.map do |k|
|
74
|
-
cmd = Configuration.facebook.fetch("#{k}_cmd", '').strip
|
75
|
-
if cmd.empty?
|
76
|
-
nil
|
77
|
-
else
|
78
|
-
Recker.shell(Configuration.facebook["#{k}_cmd"]).strip
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
return nil if values.any? { |v| v.nil? || v.empty? }
|
83
|
-
|
84
|
-
Hash[cred_fieldnames.zip(values)]
|
85
|
-
end
|
86
|
-
|
87
|
-
def cred_fieldnames
|
88
|
-
# app_access_token
|
89
|
-
%w[
|
90
|
-
access_token
|
91
|
-
app_id
|
92
|
-
app_secret
|
93
|
-
]
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
data/lib/jekyll_recker/logger.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
module Jekyll
|
6
|
-
# Recker
|
7
|
-
module Recker
|
8
|
-
def self.logger
|
9
|
-
@logger ||= make_logger
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.make_logger
|
13
|
-
logger = Logger.new(STDOUT)
|
14
|
-
logger.formatter = proc do |_severity, _datetime, _progname, msg|
|
15
|
-
"jekyll-recker: #{msg}\n"
|
16
|
-
end
|
17
|
-
logger
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/jekyll_recker/slack.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'slack-notifier'
|
4
|
-
|
5
|
-
module Jekyll
|
6
|
-
# Recker
|
7
|
-
module Recker
|
8
|
-
# Slack
|
9
|
-
class Slack
|
10
|
-
include Jekyll::Recker::Mixins::Logging
|
11
|
-
|
12
|
-
def self.share(dry: false)
|
13
|
-
each_in_config(dry: dry) do |client|
|
14
|
-
logger.info "#{client.key}: discovering webhook"
|
15
|
-
client.discover_webhook!
|
16
|
-
logger.info "#{client.key}: posting #{client.latest.data['title']}"
|
17
|
-
client.post_latest!
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.each_in_config(dry: false)
|
22
|
-
Configuration.slack.map do |key, body|
|
23
|
-
yield new(key, body, dry: dry)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
attr_reader :key
|
28
|
-
|
29
|
-
def initialize(config_key, config_body, dry: false)
|
30
|
-
@key = config_key
|
31
|
-
@data = config_body
|
32
|
-
@webhook = nil
|
33
|
-
@dry = dry
|
34
|
-
end
|
35
|
-
|
36
|
-
def discover_webhook!
|
37
|
-
@webhook = ENV["SLACK_#{@key.upcase}_WEBHOOK"] || extract_from_config
|
38
|
-
raise ReckerError, 'cannot find slack credentials!' if @webhook.nil?
|
39
|
-
end
|
40
|
-
|
41
|
-
def latest
|
42
|
-
@latest ||= Configuration.latest_post
|
43
|
-
end
|
44
|
-
|
45
|
-
def post_latest!
|
46
|
-
if @dry
|
47
|
-
logger.info('postign in dry mode, printing message')
|
48
|
-
logger.info("BEGIN MESSAGE\n#{message_body.strip}\nEND MESSAGE")
|
49
|
-
else
|
50
|
-
::Slack::Notifier.new(
|
51
|
-
@webhook.strip,
|
52
|
-
channel: @data.fetch('channel'),
|
53
|
-
username: @data.fetch('username'),
|
54
|
-
icon_emoji: @data.fetch('emoji')
|
55
|
-
).post(text: message_body)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def message_body
|
62
|
-
url = File.join Configuration.jekyll['url'], latest.url
|
63
|
-
body = <<~MSG
|
64
|
-
#{latest.data['date'].strftime('%A, %B %-d %Y')}
|
65
|
-
#{latest.data['title']}
|
66
|
-
#{url}
|
67
|
-
MSG
|
68
|
-
::Slack::Notifier::Util::LinkFormatter.format(body)
|
69
|
-
end
|
70
|
-
|
71
|
-
def extract_from_config
|
72
|
-
cmd = @data['webhook_cmd']
|
73
|
-
return nil if cmd.nil?
|
74
|
-
|
75
|
-
Recker.shell(cmd)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'twitter'
|
4
|
-
|
5
|
-
module Jekyll
|
6
|
-
module Recker
|
7
|
-
# Twitter Client
|
8
|
-
class Twitter
|
9
|
-
include Mixins::Logging
|
10
|
-
|
11
|
-
def self.share(dry: false)
|
12
|
-
client = new(dry: dry)
|
13
|
-
logger.info 'discovering credentials'
|
14
|
-
client.discover_credentials!
|
15
|
-
logger.info "tweeting #{client.latest.data['title']}"
|
16
|
-
client.post_latest!
|
17
|
-
end
|
18
|
-
|
19
|
-
def initialize(dry: false)
|
20
|
-
@dry = dry
|
21
|
-
end
|
22
|
-
|
23
|
-
def discover_credentials!
|
24
|
-
@creds = extract_from_env || extract_from_config
|
25
|
-
raise ReckerError, 'cannot find twitter credentials!' if @creds.nil?
|
26
|
-
|
27
|
-
set_credentials!
|
28
|
-
end
|
29
|
-
|
30
|
-
def post_latest!
|
31
|
-
if @dry
|
32
|
-
logger.info('tweeting in dry mode, printing message')
|
33
|
-
logger.info("BEGIN TWEET\n#{tweet_body.strip}\nEND TWEET")
|
34
|
-
else
|
35
|
-
@client.update(tweet_body)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def latest
|
40
|
-
Configuration.latest_post
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def tweet_body
|
46
|
-
url = File.join Configuration.jekyll['url'], latest.url
|
47
|
-
<<~TWEET
|
48
|
-
#{latest.data['date'].strftime('%A, %B %-d %Y')}
|
49
|
-
#{latest.data['title']}
|
50
|
-
#{url}
|
51
|
-
TWEET
|
52
|
-
end
|
53
|
-
|
54
|
-
def set_credentials!
|
55
|
-
@client ||= ::Twitter::REST::Client.new do |settings|
|
56
|
-
settings.consumer_key = @creds['consumer_api_key']
|
57
|
-
settings.consumer_secret = @creds['consumer_api_secret']
|
58
|
-
settings.access_token = @creds['access_token']
|
59
|
-
settings.access_token_secret = @creds['access_token_secret']
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def extract_from_env
|
64
|
-
values = cred_fieldnames.map { |k| ENV["TWITTER_#{k.upcase}"] }
|
65
|
-
|
66
|
-
return nil if values.any? { |v| v.nil? || v.empty? }
|
67
|
-
|
68
|
-
Hash[cred_fieldnames.zip(values)]
|
69
|
-
end
|
70
|
-
|
71
|
-
def extract_from_config
|
72
|
-
values = cred_fieldnames.map do |k|
|
73
|
-
Recker.shell(Configuration.twitter["#{k}_cmd"]).strip
|
74
|
-
end
|
75
|
-
|
76
|
-
return nil if values.any? { |v| v.nil? || v.empty? }
|
77
|
-
|
78
|
-
Hash[cred_fieldnames.zip(values)]
|
79
|
-
end
|
80
|
-
|
81
|
-
def cred_fieldnames
|
82
|
-
%w[
|
83
|
-
access_token_secret
|
84
|
-
access_token
|
85
|
-
consumer_api_key
|
86
|
-
consumer_api_secret
|
87
|
-
]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
data/lib/jekyll_recker/words.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Jekyll
|
4
|
-
module Recker
|
5
|
-
# Words
|
6
|
-
module Words
|
7
|
-
def self.array_to_and_list(array)
|
8
|
-
case array.length
|
9
|
-
when 0
|
10
|
-
''
|
11
|
-
when 1
|
12
|
-
array.first
|
13
|
-
when 2
|
14
|
-
"#{array.first} and #{array.last}"
|
15
|
-
else
|
16
|
-
array[0...-1].join(', ') + ", and #{array.last}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.and_list_to_array(str)
|
21
|
-
str = str.gsub(' and ', ', ')
|
22
|
-
str.split(',').map(&:strip).reject(&:empty?)
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.prettify_number(number)
|
26
|
-
number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.prettify_path(path, home = nil)
|
30
|
-
home ||= File.expand_path('~/')
|
31
|
-
path.sub(home, '~')
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.to_word_list(str)
|
35
|
-
str.split(' ')
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.to_weighted_list(arr)
|
39
|
-
arr.uniq.map do |word|
|
40
|
-
[word, arr.count(word)]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Array extensions
|
48
|
-
class Array
|
49
|
-
def to_and_list
|
50
|
-
Jekyll::Recker::Words.array_to_and_list(self)
|
51
|
-
end
|
52
|
-
|
53
|
-
def to_weighted_list
|
54
|
-
Jekyll::Recker::Words.to_weighted_list(self)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Integer extensions
|
59
|
-
class Integer
|
60
|
-
def pretty
|
61
|
-
Jekyll::Recker::Words.prettify_number(self)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# String extensions
|
66
|
-
class String
|
67
|
-
def words
|
68
|
-
Jekyll::Recker::Words.to_word_list(self)
|
69
|
-
end
|
70
|
-
|
71
|
-
def word_count
|
72
|
-
Jekyll::Recker::Words.to_word_list(self).count
|
73
|
-
end
|
74
|
-
|
75
|
-
def pretty_path(home = nil)
|
76
|
-
Jekyll::Recker::Words.prettify_path(self, home)
|
77
|
-
end
|
78
|
-
|
79
|
-
def to_and_array
|
80
|
-
Jekyll::Recker::Words.and_list_to_array(self)
|
81
|
-
end
|
82
|
-
end
|