jekyll-recker 1.2.2 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Jekyll
6
+ module Recker
7
+ module Mixins
8
+ # Descendants
9
+ module Descendants
10
+ def self.included(base)
11
+ base.extend(self)
12
+ end
13
+
14
+ def descendants
15
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
16
+ end
17
+ end
18
+
19
+ # Introspection
20
+ module Introspection
21
+ def self.included(base)
22
+ base.extend(self)
23
+ end
24
+
25
+ def class_name
26
+ self.class.name.split('::').last
27
+ end
28
+ end
29
+
30
+ # Logging
31
+ module Logging
32
+ def self.included(base)
33
+ base.extend(self)
34
+ end
35
+
36
+ def logger
37
+ @logger ||= Logger.new(
38
+ STDOUT,
39
+ formatter: proc { |_severity, _datetime, _progname, msg| "jekyll-recker: #{msg}\n" }
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'slack-notifier'
4
+ require 'twitter'
5
+
6
+ module Jekyll
7
+ # Recker
8
+ module Recker
9
+ module Social
10
+ # Backend
11
+ #
12
+ # Backend base class for social sharing backends.
13
+ # @abstract
14
+ class Share
15
+ include Mixins::Introspection
16
+ include Mixins::Logging
17
+
18
+ def self.share(dry: false)
19
+ backend = new(dry: dry)
20
+ logger.info "#{backend.name} - building configuration"
21
+ backend.configure!
22
+
23
+ logger.info "#{backend.name} - sharing \"#{backend.latest_title}\""
24
+ backend.post!
25
+ end
26
+
27
+ def initialize(dry: false)
28
+ @dry = dry
29
+ end
30
+
31
+ def dry?
32
+ @dry
33
+ end
34
+
35
+ def config
36
+ @config ||= Jekyll::Recker::Configuration.recker.fetch(config_key)
37
+ end
38
+
39
+ def config_key
40
+ class_name.downcase
41
+ end
42
+ alias name config_key
43
+
44
+ def post_body
45
+ url = File.join Configuration.jekyll['url'], latest.url
46
+ <<~BODY
47
+ #{latest.data['date'].strftime('%A, %B %-d %Y')}
48
+ #{latest.data['title']}
49
+ #{url}
50
+ BODY
51
+ end
52
+
53
+ def latest
54
+ @latest ||= Configuration.latest_post
55
+ end
56
+
57
+ def latest_title
58
+ latest.data['title']
59
+ end
60
+
61
+ def configure!
62
+ raise NotImplementedError
63
+ end
64
+
65
+ def post!
66
+ raise NotImplementedError
67
+ end
68
+ end
69
+
70
+ # Slack
71
+ #
72
+ # Slack social sharing backend
73
+ class Slack < Share
74
+ def configure!
75
+ @creds = {}
76
+ workspaces.each do |key, data|
77
+ webhook = ENV["SLACK_#{key.upcase}_WEBHOOK"] || extract_from_config(data)
78
+ if webhook.nil?
79
+ raise ReckerError, "cannot find slack webhook for #{key} workspace!"
80
+ end
81
+
82
+ @creds[key] = webhook
83
+ end
84
+ end
85
+
86
+ def post!
87
+ message_body = ::Slack::Notifier::Util::LinkFormatter.format(post_body)
88
+ workspaces.each do |key, config|
89
+ logger.info "posting to #{key} workspace"
90
+ if @dry
91
+ logger.info("BEGIN MESSAGE\n#{message_body.strip}\nEND MESSAGE")
92
+ else
93
+ ::Slack::Notifier.new(
94
+ @creds[key].strip,
95
+ channel: config.fetch('channel'),
96
+ username: config.fetch('username'),
97
+ icon_emoji: config.fetch('emoji')
98
+ ).post(text: message_body)
99
+ end
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def extract_from_config(data)
106
+ cmd = data['webhook_cmd']
107
+ return nil if cmd.nil?
108
+
109
+ Jekyll::Recker.shell(cmd)
110
+ end
111
+
112
+ def workspaces
113
+ config.each
114
+ end
115
+ end
116
+
117
+ # Twitter
118
+ #
119
+ # Twitter social sharing backend
120
+ class Twitter < Share
121
+ def configure!
122
+ creds = extract_from_env || extract_from_config
123
+ raise ReckerError, 'cannot find twitter credentials!' if creds.nil?
124
+
125
+ @client = ::Twitter::REST::Client.new do |settings|
126
+ settings.consumer_key = creds['consumer_api_key']
127
+ settings.consumer_secret = creds['consumer_api_secret']
128
+ settings.access_token = creds['access_token']
129
+ settings.access_token_secret = creds['access_token_secret']
130
+ end
131
+ end
132
+
133
+ def post!
134
+ if dry?
135
+ logger.info('tweeting in dry mode, printing message')
136
+ logger.info("BEGIN TWEET\n#{post_body}END TWEET")
137
+ else
138
+ @client.update(post_body)
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def extract_from_env
145
+ values = cred_fieldnames.map { |k| ENV["TWITTER_#{k.upcase}"] }
146
+
147
+ return nil if values.any? { |v| v.nil? || v.empty? }
148
+
149
+ Hash[cred_fieldnames.zip(values)]
150
+ end
151
+
152
+ def extract_from_config
153
+ values = cred_fieldnames.map do |k|
154
+ Recker.shell(Configuration.twitter["#{k}_cmd"]).strip
155
+ end
156
+
157
+ return nil if values.any? { |v| v.nil? || v.empty? }
158
+
159
+ Hash[cred_fieldnames.zip(values)]
160
+ end
161
+
162
+ def cred_fieldnames
163
+ [
164
+ 'access_token_secret',
165
+ 'access_token',
166
+ 'consumer_api_key',
167
+ 'consumer_api_secret'
168
+ ]
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -3,7 +3,7 @@
3
3
  module Jekyll
4
4
  module Recker
5
5
  module Tags
6
- # Version
6
+ # Returns the VERSION of the running jekyll-recker gem.
7
7
  class Version < Liquid::Tag
8
8
  def render(_context)
9
9
  VERSION
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module Recker
5
- VERSION = '1.2.2'
5
+ VERSION = '1.7.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,37 +1,37 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-recker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.7.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-04-04 00:00:00.000000000 Z
11
+ date: 2020-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: jekyll
14
+ name: bump
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '3.8'
20
- type: :runtime
19
+ version: '0'
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '3.8'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: slack-notifier
28
+ name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :runtime
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -39,13 +39,13 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: twitter
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
- type: :runtime
48
+ type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: bundler
56
+ name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rake
70
+ name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -80,6 +80,62 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: jekyll
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.8'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.8'
111
+ - !ruby/object:Gem::Dependency
112
+ name: slack-notifier
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: twitter
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
@@ -88,33 +144,29 @@ extensions: []
88
144
  extra_rdoc_files: []
89
145
  files:
90
146
  - LICENSE
91
- - README.org
147
+ - README.md
148
+ - _includes/figure.html
92
149
  - _includes/footer.html
150
+ - _includes/head.html
93
151
  - _includes/header.html
94
152
  - _includes/nav.html
95
- - _layouts/default.html
153
+ - _includes/pager.html
96
154
  - _layouts/home.html
97
155
  - _layouts/page.html
98
156
  - _layouts/post.html
99
- - assets/jekyll-recker.scss
100
157
  - lib/jekyll-recker.rb
101
- - lib/jekyll-recker/commands.rb
102
- - lib/jekyll-recker/commands/share.rb
103
- - lib/jekyll-recker/commands/slack.rb
104
- - lib/jekyll-recker/commands/tweet.rb
105
- - lib/jekyll-recker/configuration.rb
106
- - lib/jekyll-recker/error.rb
107
- - lib/jekyll-recker/generators.rb
108
- - lib/jekyll-recker/logger.rb
109
- - lib/jekyll-recker/mixins.rb
110
- - lib/jekyll-recker/shell.rb
111
- - lib/jekyll-recker/slack.rb
112
- - lib/jekyll-recker/stats.rb
113
- - lib/jekyll-recker/tags.rb
114
- - lib/jekyll-recker/twitter.rb
115
- - lib/jekyll-recker/version.rb
116
- - lib/jekyll-recker/words.rb
117
- homepage: https://www.alexrecker.com/jekyll-recker.html
158
+ - lib/jekyll_recker/commands.rb
159
+ - lib/jekyll_recker/configuration.rb
160
+ - lib/jekyll_recker/error.rb
161
+ - lib/jekyll_recker/filters.rb
162
+ - lib/jekyll_recker/generators.rb
163
+ - lib/jekyll_recker/mixins.rb
164
+ - lib/jekyll_recker/shell.rb
165
+ - lib/jekyll_recker/social.rb
166
+ - lib/jekyll_recker/tags.rb
167
+ - lib/jekyll_recker/version.rb
168
+ - lib/jekyll_recker/words.rb
169
+ homepage: https://www.github.com/arecker/jekyll-recker/
118
170
  licenses:
119
171
  - GPLv3
120
172
  metadata: {}
@@ -136,5 +188,5 @@ requirements: []
136
188
  rubygems_version: 3.0.3
137
189
  signing_key:
138
190
  specification_version: 4
139
- summary: This is the jekyll theme for my personal website.
191
+ summary: The Greatest Jekyll Plugin in the World
140
192
  test_files: []
data/README.org DELETED
@@ -1,205 +0,0 @@
1
- #+TITLE: jekyll-recker
2
- #+SLUG: jekyll-recker.html
3
- #+PERMALINK: jekyll-recker.html
4
- #+STARTUP: showall
5
- #+DESCRIPTION: my website's custom jekyll plugin
6
-
7
- This is the jekyll plugin for my personal website. Source code
8
- available on [[https://github.com/arecker/blog][github]].
9
-
10
- ** Installation
11
-
12
- Add =jekyll-recker= to the =jekyll_plugins= group of your =Gemfile=.
13
-
14
- #+BEGIN_SRC ruby
15
- group :jekyll_plugins do
16
- gem 'jekyll-recker'
17
- end
18
- #+END_SRC
19
-
20
- Add =jekyll-recker= to the list of plugins in jekyll's =_config.yml=.
21
-
22
- #+BEGIN_SRC yaml
23
- # _config.yaml
24
- plugins:
25
- - jekyll-recker
26
- #+END_SRC
27
-
28
- Set the theme.
29
-
30
- #+BEGIN_SRC yaml
31
- theme: jekyll-recker
32
- #+END_SRC
33
-
34
- Install and enjoy.
35
-
36
- #+BEGIN_SRC sh
37
- bundle install
38
- bundle exec jekyll serve
39
- #+END_SRC
40
-
41
- ** Usage
42
-
43
- *** Commands
44
-
45
- **** =slack=
46
-
47
- The =slack= command posts a slack message advertising the latest
48
- published jekyll blog post using a private incoming webhook.
49
-
50
- Configure _config.yml
51
-
52
- #+BEGIN_SRC yaml
53
- # _config.yml
54
- recker:
55
- slack:
56
- MyTeam:
57
- channel: '#blogs' # required!
58
- username: 'blogbot' # required!
59
- emoji: ':robot:' # required!
60
- #+END_SRC
61
-
62
- Multiple teams are supported too!
63
-
64
- #+BEGIN_SRC yaml
65
- # _config.yml
66
- recker:
67
- slack:
68
- MyTeamA:
69
- channel: '#blogs' # required!
70
- username: 'blogbot' # required!
71
- emoji: ':robot:' # required!
72
- MyTeamB:
73
- channel: '#blogs' # required!
74
- username: 'blogbot' # required!
75
- emoji: ':robot:' # required!
76
- MyTeamC:
77
- channel: '#blogs' # required!
78
- username: 'blogbot' # required!
79
- emoji: ':robot:' # required!
80
- #+END_SRC
81
-
82
-
83
- Supply the private webhook through an environment variable.
84
-
85
- #+BEGIN_SRC sh
86
- export SLACK_MYTEAM_WEBHOOK="https://..." # SLACK_ + <MyTeam.upcase> + _WEBHOOK
87
- #+END_SRC
88
-
89
- Alternatively, add the command with which to retrieve the webhook in
90
- _config.yml
91
-
92
- #+BEGIN_SRC yaml
93
- # _config.yml
94
- recker:
95
- slack:
96
- MyTeam:
97
- webhook_cmd: cat secrets/my-teams-secret-webhook.txt
98
- #+END_SRC
99
-
100
- Run =bundle exec jekyll slack= to let it rip!
101
-
102
- [[assets/images/example-slack.png]]
103
-
104
- Using the =--dry= flag, you can preview the message post without
105
- actually posting anything.
106
-
107
- #+BEGIN_EXAMPLE
108
- arecker@25732-arecker:~/src/blog$ be jekyll slack --dry
109
- Configuration file: /Users/arecker/src/blog/_config.yml
110
- jekyll-recker: reckerfamily: discovering webhook
111
- Configuration file: /Users/arecker/src/blog/_config.yml
112
- jekyll-recker: reckerfamily: posting drag racing, windshield wipers, and alex's painting tips
113
- jekyll-recker: postign in dry mode, printing message
114
- jekyll-recker: BEGIN MESSAGE
115
- Sunday, March 15 2020
116
- drag racing, windshield wipers, and alex's painting tips
117
- https://www.alexrecker.com/2020-03-15.html
118
- END MESSAGE
119
- #+END_EXAMPLE
120
-
121
- **** =tweet=
122
-
123
- The =tweet= command tweets a link to the latest published jekyll blog
124
- post.
125
-
126
- Ensure the following environment variables are set,.
127
-
128
- #+BEGIN_SRC sh
129
- export ACCESS_TOKEN_SECRET="..."
130
- export ACCESS_TOKEN="..."
131
- export CONSUMER_API_KEY="..."
132
- export CONSUMER_API_SECRET="..."
133
- #+END_SRC
134
-
135
- Alternatively, configure which commands to run to fetch the secrets.
136
-
137
- #+BEGIN_SRC yaml
138
- # _config.yml
139
- recker:
140
- twitter:
141
- access_token_secret_cmd: cat secrets/access-token-secret
142
- access_token_cmd: cat secrets/access-token
143
- consumer_api_key_cmd: cat secrets/consumer-api-key
144
- consumer_api_secret_cmd: cat secrets/consumer-api-secret-key
145
- #+END_SRC
146
-
147
- Run =bundle exec jekyll tweet= to let it rip!
148
-
149
- [[assets/images/example-tweet.png]]
150
-
151
- Using the =--dry= flag, you can test your configuration without
152
- actually tweeting anything.
153
-
154
- #+BEGIN_EXAMPLE
155
- arecker@25732-arecker:~/src/blog$ be jekyll tweet --dry
156
- jekyll-recker: discovering credentials
157
- Configuration file: /Users/arecker/src/blog/_config.yml
158
- Configuration file: /Users/arecker/src/blog/_config.yml
159
- jekyll-recker: tweeting drag racing, windshield wipers, and alex's painting tips
160
- jekyll-recker: tweeting in dry mode, printing message
161
- jekyll-recker: BEGIN TWEET
162
- Sunday, March 15 2020
163
- drag racing, windshield wipers, and alex's painting tips
164
- https://www.alexrecker.com/2020-03-15.html
165
- END TWEET
166
- #+END_EXAMPLE
167
-
168
- *** Generators
169
-
170
- **** =stats=
171
-
172
- On build time, =jekyll-recker= calculates and stores the following
173
- stats in the =site.data.stats= object
174
-
175
- | Field Name | Field Description |
176
- |-----------------+------------------------------------------------------|
177
- | =posts= | The total number of published posts. |
178
- | =words.total= | The total number of words from all published post. |
179
- | =words.average= | The average number of words for each published post. |
180
- | =days.days= | Current streak of daily, consecutive posts. |
181
- | =days.start= | First day of current streak. |
182
- | =days.end= | Last day of current streak. |
183
-
184
- Use these variables to render your own page of neat writing statistics!
185
-
186
- #+BEGIN_SRC html
187
- <table>
188
- <tr>
189
- <th>Total Posts</th>
190
- <th>Total Words</th>
191
- <th>Average Words per Post</th>
192
- <th>Current Streak</th>
193
- <th>First day of current streak</th>
194
- <th>Last day of current streak</th>
195
- </tr>
196
- <tr>
197
- <td>{{ site.data.stats.posts }}</td>
198
- <td>{{ site.data.stats.words.total }}</td>
199
- <td>{{ site.data.stats.words.average }}</td>
200
- <td>{{ site.data.stats.days.days }}</td>
201
- <td>{{ site.data.stats.days.start }}</td>
202
- <td>{{ site.data.stats.days.end }}</td>
203
- </tr>
204
- </table>
205
- #+END_SRC