jekyll-recker 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.org +93 -0
- data/assets/images/example-slack.png +0 -0
- data/lib/jekyll-recker/commands.rb +17 -6
- data/lib/jekyll-recker/configuration.rb +4 -0
- data/lib/jekyll-recker/error.rb +8 -0
- data/lib/jekyll-recker/log.rb +5 -0
- data/lib/jekyll-recker/shell.rb +29 -0
- data/lib/jekyll-recker/slack.rb +68 -0
- data/lib/jekyll-recker/twitter.rb +28 -15
- data/lib/jekyll-recker/version.rb +1 -1
- data/lib/jekyll-recker.rb +5 -0
- metadata +19 -11
- data/lib/blog/cli.rb +0 -102
- data/lib/blog/config.rb +0 -67
- data/lib/blog/entry.rb +0 -68
- data/lib/blog/git.rb +0 -28
- data/lib/blog/jekyll.rb +0 -17
- data/lib/blog/journal.rb +0 -46
- data/lib/blog/log.rb +0 -30
- data/lib/blog/slack.rb +0 -20
- data/lib/blog/words.rb +0 -80
- data/lib/blog.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27007112ff63186d2d55ea50ea4318817aab091824c2657401c8f6b51d0cd7a2
|
4
|
+
data.tar.gz: 7cd0e1169baa77572ff3cead226a6a7b03f0e74849bba8e3b362c1eeba39a579
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47d59369933cd797ab422071c43f5a62c8619791511931a2c5f4a5aa16539cda1c9fe80f9bee37f57fe877d43a83a027d869b0434a99a5282f48af8f97f99dea
|
7
|
+
data.tar.gz: 70ec0f5d69e913b6810b4c5ca57568710d4854285126e51f6a4431b5e53f3f2e893a6e90e58501cba3f4fdc0b509e411901bc8ecdc6789d43dcc01e23534dcfc
|
data/README.org
CHANGED
@@ -41,6 +41,82 @@ bundle exec jekyll serve
|
|
41
41
|
|
42
42
|
** Commands
|
43
43
|
|
44
|
+
*** =slack=
|
45
|
+
|
46
|
+
The =slack= command posts a slack message advertising the latest
|
47
|
+
published jekyll blog post using a private incoming webhook.
|
48
|
+
|
49
|
+
Configure _config.yml
|
50
|
+
|
51
|
+
#+BEGIN_SRC yaml
|
52
|
+
# _config.yml
|
53
|
+
recker:
|
54
|
+
slack:
|
55
|
+
MyTeam:
|
56
|
+
channel: '#blogs' # required!
|
57
|
+
username: 'blogbot' # required!
|
58
|
+
emoji: ':robot:' # required!
|
59
|
+
#+END_SRC
|
60
|
+
|
61
|
+
Multiple teams are supported too!
|
62
|
+
|
63
|
+
#+BEGIN_SRC yaml
|
64
|
+
# _config.yml
|
65
|
+
recker:
|
66
|
+
slack:
|
67
|
+
MyTeamA:
|
68
|
+
channel: '#blogs' # required!
|
69
|
+
username: 'blogbot' # required!
|
70
|
+
emoji: ':robot:' # required!
|
71
|
+
MyTeamB:
|
72
|
+
channel: '#blogs' # required!
|
73
|
+
username: 'blogbot' # required!
|
74
|
+
emoji: ':robot:' # required!
|
75
|
+
MyTeamC:
|
76
|
+
channel: '#blogs' # required!
|
77
|
+
username: 'blogbot' # required!
|
78
|
+
emoji: ':robot:' # required!
|
79
|
+
#+END_SRC
|
80
|
+
|
81
|
+
|
82
|
+
Supply the private webhook through an environment variable.
|
83
|
+
|
84
|
+
#+BEGIN_SRC sh
|
85
|
+
export SLACK_MYTEAM_WEBHOOK="https://..." # SLACK_ + <MyTeam.upcase> + _WEBHOOK
|
86
|
+
#+END_SRC
|
87
|
+
|
88
|
+
Alternatively, add the command with which to retrieve the webhook in
|
89
|
+
_config.yml
|
90
|
+
|
91
|
+
#+BEGIN_SRC yaml
|
92
|
+
# _config.yml
|
93
|
+
recker:
|
94
|
+
slack:
|
95
|
+
MyTeam:
|
96
|
+
webhook_cmd: cat secrets/my-teams-secret-webhook.txt
|
97
|
+
#+END_SRC
|
98
|
+
|
99
|
+
Run =bundle exec jekyll slack= to let it rip!
|
100
|
+
|
101
|
+
[[assets/images/example-slack.png]]
|
102
|
+
|
103
|
+
Using the =--dry= flag, you can preview the message post without
|
104
|
+
actually posting anything.
|
105
|
+
|
106
|
+
#+BEGIN_EXAMPLE
|
107
|
+
arecker@25732-arecker:~/src/blog$ be jekyll slack --dry
|
108
|
+
Configuration file: /Users/arecker/src/blog/_config.yml
|
109
|
+
jekyll-recker: reckerfamily: discovering webhook
|
110
|
+
Configuration file: /Users/arecker/src/blog/_config.yml
|
111
|
+
jekyll-recker: reckerfamily: posting drag racing, windshield wipers, and alex's painting tips
|
112
|
+
jekyll-recker: postign in dry mode, printing message
|
113
|
+
jekyll-recker: BEGIN MESSAGE
|
114
|
+
Sunday, March 15 2020
|
115
|
+
drag racing, windshield wipers, and alex's painting tips
|
116
|
+
https://www.alexrecker.com/2020-03-15.html
|
117
|
+
END MESSAGE
|
118
|
+
#+END_EXAMPLE
|
119
|
+
|
44
120
|
*** =tweet=
|
45
121
|
|
46
122
|
The =tweet= command tweets a link to the latest published jekyll blog
|
@@ -71,6 +147,23 @@ Run =bundle exec jekyll tweet= to let it rip!
|
|
71
147
|
|
72
148
|
[[assets/images/example-tweet.png]]
|
73
149
|
|
150
|
+
Using the =--dry= flag, you can test your configuration without
|
151
|
+
actually tweeting anything.
|
152
|
+
|
153
|
+
#+BEGIN_EXAMPLE
|
154
|
+
arecker@25732-arecker:~/src/blog$ be jekyll tweet --dry
|
155
|
+
jekyll-recker: discovering credentials
|
156
|
+
Configuration file: /Users/arecker/src/blog/_config.yml
|
157
|
+
Configuration file: /Users/arecker/src/blog/_config.yml
|
158
|
+
jekyll-recker: tweeting drag racing, windshield wipers, and alex's painting tips
|
159
|
+
jekyll-recker: tweeting in dry mode, printing message
|
160
|
+
jekyll-recker: BEGIN TWEET
|
161
|
+
Sunday, March 15 2020
|
162
|
+
drag racing, windshield wipers, and alex's painting tips
|
163
|
+
https://www.alexrecker.com/2020-03-15.html
|
164
|
+
END TWEET
|
165
|
+
#+END_EXAMPLE
|
166
|
+
|
74
167
|
** Generators
|
75
168
|
|
76
169
|
*** =stats=
|
Binary file
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Jekyll
|
2
4
|
module Recker
|
3
5
|
module Commands
|
@@ -8,14 +10,15 @@ module Jekyll
|
|
8
10
|
prog.command(:tweet) do |c|
|
9
11
|
c.syntax 'tweet'
|
10
12
|
c.description 'tweet latest post'
|
11
|
-
c.
|
12
|
-
|
13
|
+
c.option 'dry', '-d', '--dry', 'print message instead of tweeting'
|
14
|
+
c.action do |_args, options|
|
15
|
+
client = Jekyll::Recker::Twitter.new(dry: options['dry'])
|
13
16
|
Recker.info 'discovering credentials'
|
14
17
|
client.discover_credentials!
|
15
18
|
Recker.info "tweeting #{client.latest.data['title']}"
|
16
19
|
client.post_latest!
|
17
|
-
rescue => e
|
18
|
-
abort_with e.message
|
20
|
+
rescue ReckerError => e
|
21
|
+
Recker.abort_with e.message
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -29,8 +32,16 @@ module Jekyll
|
|
29
32
|
prog.command(:slack) do |c|
|
30
33
|
c.syntax 'slack'
|
31
34
|
c.description 'slack latest post'
|
32
|
-
c.
|
33
|
-
|
35
|
+
c.option 'dry', '-d', '--dry', 'print message instead of posting'
|
36
|
+
c.action do |_args, options|
|
37
|
+
Recker::Slack.each_in_config(dry: options['dry']) do |client|
|
38
|
+
Recker.info "#{client.key}: discovering webhook"
|
39
|
+
client.discover_webhook!
|
40
|
+
Recker.info "#{client.key}: posting #{client.latest.data['title']}"
|
41
|
+
client.post_latest!
|
42
|
+
end
|
43
|
+
rescue ReckerError => e
|
44
|
+
Recker.abort_with e.message
|
34
45
|
end
|
35
46
|
end
|
36
47
|
end
|
data/lib/jekyll-recker/log.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
# Recker
|
7
|
+
module Recker
|
8
|
+
# ShellCommandFailed
|
9
|
+
class ShellCommandFailed < ReckerError; end
|
10
|
+
|
11
|
+
def self.shell(cmd)
|
12
|
+
Recker.debug("running shell command \`#{cmd}\`")
|
13
|
+
out, err, status = Open3.capture3(cmd)
|
14
|
+
return out if status.success?
|
15
|
+
|
16
|
+
msg = <<~ERROR
|
17
|
+
the command \`#{cmd}\` failed!
|
18
|
+
--- exit
|
19
|
+
#{status}
|
20
|
+
--- stdout
|
21
|
+
#{out}
|
22
|
+
--- stderr
|
23
|
+
#{err}
|
24
|
+
ERROR
|
25
|
+
|
26
|
+
raise ShellCommandFailed, msg
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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
|
+
def self.each_in_config(dry: false)
|
11
|
+
Configuration.slack.map do |key, body|
|
12
|
+
yield new(key, body, dry: dry)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :key
|
17
|
+
|
18
|
+
def initialize(config_key, config_body, dry: false)
|
19
|
+
@key = config_key
|
20
|
+
@data = config_body
|
21
|
+
@webhook = nil
|
22
|
+
@dry = dry
|
23
|
+
end
|
24
|
+
|
25
|
+
def discover_webhook!
|
26
|
+
@webhook = ENV["SLACK_#{@key.upcase}_WEBHOOK"] || extract_from_config
|
27
|
+
raise ReckerError, 'cannot find slack credentials!' if @webhook.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def latest
|
31
|
+
@latest ||= Configuration.latest_post
|
32
|
+
end
|
33
|
+
|
34
|
+
def post_latest!
|
35
|
+
if @dry
|
36
|
+
Recker.info('postign in dry mode, printing message')
|
37
|
+
Recker.info("BEGIN MESSAGE\n#{message_body.strip}\nEND MESSAGE")
|
38
|
+
else
|
39
|
+
::Slack::Notifier.new(
|
40
|
+
@webhook.strip,
|
41
|
+
channel: @data.fetch('channel'),
|
42
|
+
username: @data.fetch('username'),
|
43
|
+
icon_emoji: @data.fetch('emoji')
|
44
|
+
).post(text: message_body)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def message_body
|
51
|
+
url = File.join Configuration.jekyll['url'], latest.url
|
52
|
+
body = <<~MSG
|
53
|
+
#{latest.data['date'].strftime('%A, %B %-d %Y')}
|
54
|
+
#{latest.data['title']}
|
55
|
+
#{url}
|
56
|
+
MSG
|
57
|
+
::Slack::Notifier::Util::LinkFormatter.format(body)
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_from_config
|
61
|
+
cmd = @data['webhook_cmd']
|
62
|
+
return nil if cmd.nil?
|
63
|
+
|
64
|
+
Recker.shell(cmd)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,28 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'twitter'
|
3
4
|
|
4
5
|
module Jekyll
|
5
6
|
module Recker
|
6
7
|
# Twitter Client
|
7
8
|
class Twitter
|
8
|
-
|
9
|
-
|
10
|
-
super
|
11
|
-
end
|
9
|
+
def initialize(dry: false)
|
10
|
+
@dry = dry
|
12
11
|
end
|
13
12
|
|
14
13
|
def discover_credentials!
|
15
14
|
@creds = extract_from_env || extract_from_config
|
16
|
-
raise
|
15
|
+
raise ReckerError, 'cannot find twitter credentials!' if @creds.nil?
|
16
|
+
|
17
17
|
set_credentials!
|
18
18
|
end
|
19
19
|
|
20
20
|
def post_latest!
|
21
|
-
@
|
21
|
+
if @dry
|
22
|
+
Recker.info('tweeting in dry mode, printing message')
|
23
|
+
Recker.info("BEGIN TWEET\n#{tweet_body.strip}\nEND TWEET")
|
24
|
+
else
|
25
|
+
@client.update(tweet_body)
|
26
|
+
end
|
22
27
|
end
|
23
28
|
|
24
29
|
def latest
|
25
|
-
|
30
|
+
Configuration.latest_post
|
26
31
|
end
|
27
32
|
|
28
33
|
private
|
@@ -47,12 +52,20 @@ module Jekyll
|
|
47
52
|
|
48
53
|
def extract_from_env
|
49
54
|
values = cred_fieldnames.map { |k| ENV[k.upcase] }
|
50
|
-
|
55
|
+
|
56
|
+
return nil if values.any? { |v| v.nil? || v.empty? }
|
57
|
+
|
58
|
+
Hash[cred_fieldnames.zip(values)]
|
51
59
|
end
|
52
60
|
|
53
61
|
def extract_from_config
|
54
|
-
values = cred_fieldnames.map
|
55
|
-
|
62
|
+
values = cred_fieldnames.map do |k|
|
63
|
+
Recker.shell(Configuration.twitter["#{k}_cmd"])
|
64
|
+
end
|
65
|
+
|
66
|
+
return nil if values.any? { |v| v.nil? || v.empty? }
|
67
|
+
|
68
|
+
Hash[cred_fieldnames.zip(values)]
|
56
69
|
end
|
57
70
|
|
58
71
|
def shell(cmd)
|
@@ -60,11 +73,11 @@ module Jekyll
|
|
60
73
|
end
|
61
74
|
|
62
75
|
def cred_fieldnames
|
63
|
-
[
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
76
|
+
%w[
|
77
|
+
access_token_secret
|
78
|
+
access_token
|
79
|
+
consumer_api_key
|
80
|
+
consumer_api_secret
|
68
81
|
]
|
69
82
|
end
|
70
83
|
end
|
data/lib/jekyll-recker.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'jekyll'
|
4
|
+
|
3
5
|
module Jekyll
|
4
6
|
# Recker
|
5
7
|
module Recker
|
6
8
|
require 'jekyll-recker/commands.rb'
|
7
9
|
require 'jekyll-recker/configuration.rb'
|
10
|
+
require 'jekyll-recker/error.rb'
|
8
11
|
require 'jekyll-recker/generators.rb'
|
9
12
|
require 'jekyll-recker/log.rb'
|
13
|
+
require 'jekyll-recker/shell.rb'
|
14
|
+
require 'jekyll-recker/slack.rb'
|
10
15
|
require 'jekyll-recker/stats.rb'
|
11
16
|
require 'jekyll-recker/tags.rb'
|
12
17
|
require 'jekyll-recker/twitter.rb'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll-recker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Recker
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: slack-notifier
|
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: twitter
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,26 +98,20 @@ files:
|
|
84
98
|
- _layouts/page.html
|
85
99
|
- _layouts/post.html
|
86
100
|
- _sass/jekyll-recker.sass
|
101
|
+
- assets/images/example-slack.png
|
87
102
|
- assets/images/example-stats.png
|
88
103
|
- assets/images/example-tweet.png
|
89
104
|
- assets/images/me.jpg
|
90
105
|
- assets/images/words.png
|
91
106
|
- assets/jekyll-recker.scss
|
92
|
-
- lib/blog.rb
|
93
|
-
- lib/blog/cli.rb
|
94
|
-
- lib/blog/config.rb
|
95
|
-
- lib/blog/entry.rb
|
96
|
-
- lib/blog/git.rb
|
97
|
-
- lib/blog/jekyll.rb
|
98
|
-
- lib/blog/journal.rb
|
99
|
-
- lib/blog/log.rb
|
100
|
-
- lib/blog/slack.rb
|
101
|
-
- lib/blog/words.rb
|
102
107
|
- lib/jekyll-recker.rb
|
103
108
|
- lib/jekyll-recker/commands.rb
|
104
109
|
- lib/jekyll-recker/configuration.rb
|
110
|
+
- lib/jekyll-recker/error.rb
|
105
111
|
- lib/jekyll-recker/generators.rb
|
106
112
|
- lib/jekyll-recker/log.rb
|
113
|
+
- lib/jekyll-recker/shell.rb
|
114
|
+
- lib/jekyll-recker/slack.rb
|
107
115
|
- lib/jekyll-recker/stats.rb
|
108
116
|
- lib/jekyll-recker/tags.rb
|
109
117
|
- lib/jekyll-recker/twitter.rb
|
data/lib/blog/cli.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'commander'
|
4
|
-
require 'fileutils'
|
5
|
-
|
6
|
-
module Blog
|
7
|
-
# CLI
|
8
|
-
class CLI
|
9
|
-
include Commander::Methods
|
10
|
-
|
11
|
-
def logger
|
12
|
-
Blog::Log.logger
|
13
|
-
end
|
14
|
-
|
15
|
-
def config_path
|
16
|
-
@config_path ||= File.expand_path '~/.blog.yml'
|
17
|
-
end
|
18
|
-
|
19
|
-
def config
|
20
|
-
Blog::Config.load_from_file(config_path)
|
21
|
-
end
|
22
|
-
|
23
|
-
def journal
|
24
|
-
@journal ||= Blog::Journal.from_file(config.journal_path)
|
25
|
-
end
|
26
|
-
|
27
|
-
def latest
|
28
|
-
@latest ||= journal.public_entries.first
|
29
|
-
end
|
30
|
-
|
31
|
-
def build
|
32
|
-
logger.info "deleting #{config.site_dir.pretty_path}"
|
33
|
-
FileUtils.rm_rf(config.site_dir)
|
34
|
-
logger.info "parsing #{config.journal_path.pretty_path}"
|
35
|
-
journal = Blog::Journal.from_file(config.journal_path)
|
36
|
-
logger.info "writing #{journal.public_entries.count.pretty} public entries"
|
37
|
-
journal.write_public_entries! config.posts_dir
|
38
|
-
logger.info "building jekyll"
|
39
|
-
Blog::Jekyll.build(config)
|
40
|
-
end
|
41
|
-
|
42
|
-
def commit
|
43
|
-
git = Blog::Git.new(config.blog_repo)
|
44
|
-
git.run!
|
45
|
-
end
|
46
|
-
|
47
|
-
def slack
|
48
|
-
logger.info "fetched latest entry: #{latest.excerpt}"
|
49
|
-
config.slacks.each do |info|
|
50
|
-
Blog::Slacky.post(latest, `#{info['webhook_cmd']}`, info)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def run
|
55
|
-
program :name, 'blog'
|
56
|
-
program :version, 'v0.0.0'
|
57
|
-
program :description, 'script to generate and publish my blog'
|
58
|
-
|
59
|
-
default_command :all
|
60
|
-
|
61
|
-
global_option '--config FILE', String, 'path to blog.yml' do |file|
|
62
|
-
@config_path = file
|
63
|
-
end
|
64
|
-
|
65
|
-
command :build do |c|
|
66
|
-
c.syntax = 'build'
|
67
|
-
c.description = 'build jekyll site'
|
68
|
-
c.action do |_args, _options|
|
69
|
-
build
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
command :commit do |c|
|
74
|
-
c.syntax = 'commit'
|
75
|
-
c.description = 'commit and push new post'
|
76
|
-
c.action do |_args, _options|
|
77
|
-
commit
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
command :slack do |c|
|
82
|
-
c.syntax = 'slack'
|
83
|
-
c.description = 'send slack notifications'
|
84
|
-
c.action do |_args, _options|
|
85
|
-
slack
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
command :all do |c|
|
90
|
-
c.syntax = 'all'
|
91
|
-
c.description = 'build, commit, and slack'
|
92
|
-
c.action do |_args, _options|
|
93
|
-
build
|
94
|
-
commit
|
95
|
-
slack
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
run!
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
data/lib/blog/config.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler'
|
4
|
-
require 'yaml'
|
5
|
-
|
6
|
-
module Blog
|
7
|
-
# Config
|
8
|
-
class Config
|
9
|
-
attr_reader :data
|
10
|
-
|
11
|
-
def self.load_from_file(config_path = File.expand_path('~/.blog.yml'))
|
12
|
-
new(YAML.load_file(config_path) || {})
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(data)
|
16
|
-
@data = data
|
17
|
-
end
|
18
|
-
|
19
|
-
def journal_path
|
20
|
-
File.join blog_repo, 'journal.org'
|
21
|
-
end
|
22
|
-
|
23
|
-
def posts_dir
|
24
|
-
File.join blog_repo, '_posts'
|
25
|
-
end
|
26
|
-
|
27
|
-
def site_dir
|
28
|
-
File.join(File.expand_path(blog_repo), '_site')
|
29
|
-
end
|
30
|
-
|
31
|
-
def blog_repo
|
32
|
-
Bundler.root.to_s
|
33
|
-
end
|
34
|
-
|
35
|
-
def stats_path
|
36
|
-
File.join blog_repo, '_data/stats.json'
|
37
|
-
end
|
38
|
-
|
39
|
-
def log_level
|
40
|
-
@data.fetch('log_level', 'INFO').upcase
|
41
|
-
end
|
42
|
-
|
43
|
-
def twitter_creds
|
44
|
-
twitter = @data.fetch('twitter')
|
45
|
-
creds = {}
|
46
|
-
[
|
47
|
-
'access_token_secret',
|
48
|
-
'access_token',
|
49
|
-
'consumer_api_key',
|
50
|
-
'consumer_api_secret'
|
51
|
-
].each do |key|
|
52
|
-
creds[key] = `#{twitter.fetch(key + '_cmd')}`.strip
|
53
|
-
end
|
54
|
-
creds
|
55
|
-
end
|
56
|
-
|
57
|
-
def slacks
|
58
|
-
@data.fetch('slacks', [])
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def missing_fields
|
64
|
-
required_keys.reject { |k| @data.key? k }
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
data/lib/blog/entry.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'date'
|
4
|
-
require 'org-ruby'
|
5
|
-
|
6
|
-
module Blog
|
7
|
-
# Entry
|
8
|
-
class Entry
|
9
|
-
def initialize(headline)
|
10
|
-
@headline = headline
|
11
|
-
end
|
12
|
-
|
13
|
-
def title
|
14
|
-
date.strftime('%A, %B %-e %Y')
|
15
|
-
end
|
16
|
-
|
17
|
-
def subtitle
|
18
|
-
@subtitle ||= @headline.headline_text.split(' ').drop(2).join(' ')
|
19
|
-
end
|
20
|
-
|
21
|
-
alias excerpt subtitle
|
22
|
-
|
23
|
-
def tags
|
24
|
-
@tags ||= @headline.tags
|
25
|
-
end
|
26
|
-
|
27
|
-
def date
|
28
|
-
@date ||= Date.strptime(
|
29
|
-
@headline.headline_text.split(' ').take(2).join(' '),
|
30
|
-
'%Y-%m-%d %A'
|
31
|
-
)
|
32
|
-
end
|
33
|
-
|
34
|
-
def date_slug
|
35
|
-
date.strftime('%Y-%m-%d')
|
36
|
-
end
|
37
|
-
|
38
|
-
def public?
|
39
|
-
!tags.include? 'private'
|
40
|
-
end
|
41
|
-
|
42
|
-
def filename
|
43
|
-
"#{date_slug}-#{date_slug}.html.html"
|
44
|
-
end
|
45
|
-
|
46
|
-
def permalink
|
47
|
-
"https://www.alexrecker.com/#{date_slug}.html"
|
48
|
-
end
|
49
|
-
|
50
|
-
def body_text
|
51
|
-
@headline.body_lines.drop(1).collect(&:output_text).join(' ')
|
52
|
-
end
|
53
|
-
|
54
|
-
def body_html
|
55
|
-
Orgmode::Parser.new(body_text).to_html
|
56
|
-
end
|
57
|
-
|
58
|
-
def to_html
|
59
|
-
<<~HTML
|
60
|
-
---
|
61
|
-
title: #{title}
|
62
|
-
excerpt: #{excerpt}
|
63
|
-
---
|
64
|
-
#{body_html}
|
65
|
-
HTML
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
data/lib/blog/git.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'git'
|
4
|
-
|
5
|
-
module Blog
|
6
|
-
# Git
|
7
|
-
class Git
|
8
|
-
attr_reader :client
|
9
|
-
|
10
|
-
def initialize(path)
|
11
|
-
@client = ::Git.open(path)
|
12
|
-
@logger = Blog::Log.logger
|
13
|
-
end
|
14
|
-
|
15
|
-
def run!
|
16
|
-
commit!
|
17
|
-
end
|
18
|
-
|
19
|
-
def commit!
|
20
|
-
commit = '[auto] Automatic Publish'
|
21
|
-
@logger.info "writing commit: #{commit}"
|
22
|
-
@client.add
|
23
|
-
@client.commit(commit)
|
24
|
-
@logger.info 'pushing commit'
|
25
|
-
@client.push
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/blog/jekyll.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'jekyll'
|
4
|
-
|
5
|
-
module Blog
|
6
|
-
module Jekyll
|
7
|
-
def self.build(config)
|
8
|
-
conf = ::Jekyll.configuration(
|
9
|
-
{
|
10
|
-
'source' => config.blog_repo,
|
11
|
-
'destination' => config.site_dir
|
12
|
-
}
|
13
|
-
)
|
14
|
-
::Jekyll::Site.new(conf).process
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/blog/journal.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'org-ruby'
|
4
|
-
|
5
|
-
module Blog
|
6
|
-
# Journal
|
7
|
-
class Journal
|
8
|
-
def self.from_file(path)
|
9
|
-
new(Orgmode::Parser.load(path))
|
10
|
-
end
|
11
|
-
|
12
|
-
def logger
|
13
|
-
Blog::Log.logger
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(parser)
|
17
|
-
@parser = parser
|
18
|
-
end
|
19
|
-
|
20
|
-
def public_entries
|
21
|
-
@public_entries ||= all_entries.select(&:public?).sort_by(&:date).reverse
|
22
|
-
end
|
23
|
-
|
24
|
-
def private_entries
|
25
|
-
@private_entries ||= all_entries.reject(&:public?).sort_by(&:date).reverse
|
26
|
-
end
|
27
|
-
|
28
|
-
def write_public_entries!(dir)
|
29
|
-
public_entries.each do |entry|
|
30
|
-
target = File.join(dir, entry.filename)
|
31
|
-
logger.debug "writing #{entry.title} to #{target.pretty_path}"
|
32
|
-
File.open(target, 'w+') { |f| f.write(entry.to_html) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def all_entries
|
37
|
-
entry_headlines.map { |h| Entry.new(h) }
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def entry_headlines
|
43
|
-
@parser.headlines.select { |h| h.level == 3 }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
data/lib/blog/log.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
module Blog
|
6
|
-
# Log
|
7
|
-
module Log
|
8
|
-
def self.logger
|
9
|
-
@logger ||= default_logger
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.default_logger
|
13
|
-
logger = Logger.new(STDOUT)
|
14
|
-
logger.level = Logger::INFO
|
15
|
-
logger.formatter = proc do |severity, _datetime, _progname, msg|
|
16
|
-
"blog: #{msg}\n"
|
17
|
-
end
|
18
|
-
logger
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.level=(setting)
|
22
|
-
@logger.level = case setting.upcase
|
23
|
-
when 'DEBUG'
|
24
|
-
Logger::DEBUG
|
25
|
-
else
|
26
|
-
Logger::INFO
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/blog/slack.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'slack-notifier'
|
4
|
-
|
5
|
-
module Blog
|
6
|
-
# Slack
|
7
|
-
module Slacky
|
8
|
-
def self.post(entry, url, info)
|
9
|
-
notifier = ::Slack::Notifier.new(
|
10
|
-
url.strip,
|
11
|
-
channel: info['channel'],
|
12
|
-
username: info['username'],
|
13
|
-
icon_emoji: ':reckerbot:'
|
14
|
-
)
|
15
|
-
message = "#{entry.title} - #{entry.excerpt}\n#{entry.permalink}"
|
16
|
-
Slack::Notifier::Util::LinkFormatter.format(message)
|
17
|
-
notifier.post text: message
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/blog/words.rb
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Blog
|
4
|
-
# Words
|
5
|
-
module Words
|
6
|
-
def self.array_to_and_list(array)
|
7
|
-
case array.length
|
8
|
-
when 0
|
9
|
-
''
|
10
|
-
when 1
|
11
|
-
array.first
|
12
|
-
when 2
|
13
|
-
"#{array.first} and #{array.last}"
|
14
|
-
else
|
15
|
-
array[0...-1].join(', ') + ", and #{array.last}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.and_list_to_array(str)
|
20
|
-
str = str.gsub(' and ', ', ')
|
21
|
-
str.split(',').map(&:strip).reject(&:empty?)
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.prettify_number(number)
|
25
|
-
number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.prettify_path(path, home = nil)
|
29
|
-
home ||= File.expand_path('~/')
|
30
|
-
path.sub(home, '~')
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.to_word_list(str)
|
34
|
-
str.split(' ')
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.to_weighted_list(arr)
|
38
|
-
arr.uniq.map do |word|
|
39
|
-
[word, arr.count(word)]
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Array extensions
|
46
|
-
class Array
|
47
|
-
def to_and_list
|
48
|
-
Blog::Words.array_to_and_list(self)
|
49
|
-
end
|
50
|
-
|
51
|
-
def to_weighted_list
|
52
|
-
Blog::Words.to_weighted_list(self)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Integer extensions
|
57
|
-
class Integer
|
58
|
-
def pretty
|
59
|
-
Blog::Words.prettify_number(self)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# String extensions
|
64
|
-
class String
|
65
|
-
def words
|
66
|
-
Blog::Words.to_word_list(self)
|
67
|
-
end
|
68
|
-
|
69
|
-
def word_count
|
70
|
-
Blog::Words.to_word_list(self).count
|
71
|
-
end
|
72
|
-
|
73
|
-
def pretty_path(home = nil)
|
74
|
-
Blog::Words.prettify_path(self, home)
|
75
|
-
end
|
76
|
-
|
77
|
-
def to_and_array
|
78
|
-
Blog::Words.and_list_to_array(self)
|
79
|
-
end
|
80
|
-
end
|
data/lib/blog.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Blog
|
4
|
-
module Blog
|
5
|
-
require_relative 'blog/cli'
|
6
|
-
require_relative 'blog/config'
|
7
|
-
require_relative 'blog/entry'
|
8
|
-
require_relative 'blog/git'
|
9
|
-
require_relative 'blog/jekyll'
|
10
|
-
require_relative 'blog/journal'
|
11
|
-
require_relative 'blog/log'
|
12
|
-
require_relative 'blog/slack'
|
13
|
-
require_relative 'blog/words'
|
14
|
-
end
|