jekyll-recker 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/blog/slack.rb ADDED
@@ -0,0 +1,20 @@
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 ADDED
@@ -0,0 +1,80 @@
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 ADDED
@@ -0,0 +1,14 @@
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
@@ -0,0 +1,26 @@
1
+ module Jekyll
2
+ module Recker
3
+ module Commands
4
+ # Tweet
5
+ class Tweet < Jekyll::Command
6
+ class << self
7
+ def init_with_program(prog)
8
+ prog.command(:tweet) do |c|
9
+ c.syntax "tweet"
10
+ c.description 'tweet latest post'
11
+ c.action do |args, options|
12
+ client = Jekyll::Recker::Twitter.new
13
+ Jekyll.logger.info 'discovering credentials'
14
+ client.discover_credentials!
15
+ Jekyll.logger.info "tweeting #{client.latest.data['title']}"
16
+ client.post_latest!
17
+ rescue => e
18
+ Jekyll.logger.abort_with e.message
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module Jekyll
2
+ module Recker
3
+ # Configuration
4
+ module Configuration
5
+ def self.jekyll
6
+ @jekyll ||= Jekyll.configuration
7
+ end
8
+
9
+ def self.recker
10
+ jekyll.fetch('recker', {})
11
+ end
12
+
13
+ def self.twitter
14
+ recker.fetch('twitter', {})
15
+ end
16
+
17
+ def self.site
18
+ @site = Jekyll::Site.new(jekyll)
19
+ @site.reset
20
+ @site.read
21
+ @site
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Recker
5
+ module Generators
6
+ # StatsGenerator
7
+ class StatsGenerator < Jekyll::Generator
8
+ def generate(site)
9
+ site.data['stats'] = Stats.crunch(site)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+ require 'date'
3
+
4
+ module Jekyll
5
+ module Recker
6
+ # Stats
7
+ module Stats
8
+ def self.crunch(site)
9
+ stats = {}
10
+ BaseCruncher.descendants.each do |cruncher_class|
11
+ cruncher = cruncher_class.new(site)
12
+ stats[cruncher.stats_key] = cruncher.crunch
13
+ end
14
+ stats
15
+ end
16
+
17
+ # Base Cruncher
18
+ class BaseCruncher
19
+ include Jekyll::Filters
20
+
21
+ def self.descendants
22
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
23
+ end
24
+
25
+ def initialize(site)
26
+ @site = site
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :journal
32
+
33
+ def average(numlist)
34
+ calc = numlist.inject { |sum, el| sum + el }.to_f / numlist.size
35
+ calc.round
36
+ end
37
+
38
+ def total(numlist)
39
+ numlist.inject(0) { |sum, x| sum + x }
40
+ end
41
+
42
+ def entries
43
+ @site.posts.docs.select(&:published?)
44
+ end
45
+ end
46
+
47
+ # PostCountCruncher
48
+ class PostCountCruncher < BaseCruncher
49
+ def stats_key
50
+ 'posts'
51
+ end
52
+
53
+ def crunch
54
+ entries.count.pretty
55
+ end
56
+ end
57
+
58
+ # WordCountCruncher
59
+ class WordCountCruncher < BaseCruncher
60
+ def stats_key
61
+ 'words'
62
+ end
63
+
64
+ def crunch
65
+ total_counts = entries.collect(&:content).map { |c| number_of_words(c) }
66
+ {
67
+ 'average' => average(total_counts).pretty,
68
+ 'total' => total(total_counts).pretty
69
+ }
70
+ end
71
+ end
72
+
73
+ # Streak Cruncher
74
+ class StreakCruncher < BaseCruncher
75
+ def stats_key
76
+ 'days'
77
+ end
78
+
79
+ def crunch
80
+ streaks.take(1).map do |count, dates|
81
+ {
82
+ 'days' => count.pretty,
83
+ 'start' => dates[0],
84
+ 'end' => dates[1]
85
+ }
86
+ end.first
87
+ end
88
+
89
+ private
90
+
91
+ def streaks
92
+ _streaks = []
93
+ entry_dates.slice_when do |prev, curr|
94
+ curr != prev - 1
95
+ end.each do |dates|
96
+ first, last = dates.min, dates.max
97
+ _streaks << [(last - first).to_i, [first, last]]
98
+ end
99
+ _streaks
100
+ end
101
+
102
+ def entry_dates
103
+ entries.collect(&:date).map { |t| Date.new(t.year, t.month, t.day) }.sort.reverse
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Recker
5
+ module Tags
6
+ # Version
7
+ class Version < Liquid::Tag
8
+ def render(_context)
9
+ VERSION
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ Liquid::Template.register_tag('recker_version', Jekyll::Recker::Tags::Version)
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+ require 'twitter'
3
+
4
+ module Jekyll
5
+ module Recker
6
+ # Twitter Client
7
+ class Twitter
8
+ class CannotFindCreds < StandardError
9
+ def initialize(msg = 'could not find twitter credentials')
10
+ super
11
+ end
12
+ end
13
+
14
+ def discover_credentials!
15
+ @creds = extract_from_env || extract_from_config
16
+ raise CannotFindCreds if @creds.nil?
17
+ set_credentials!
18
+ end
19
+
20
+ def post_latest!
21
+ @client.update(tweet_body)
22
+ end
23
+
24
+ def latest
25
+ @latest ||= Configuration.site.posts.docs.last
26
+ end
27
+
28
+ private
29
+
30
+ def tweet_body
31
+ url = File.join Configuration.jekyll['url'], latest.url
32
+ <<~TWEET
33
+ #{latest.data['excerpt']}
34
+ #{latest.data['title']}
35
+ #{url}
36
+ TWEET
37
+ end
38
+
39
+ def set_credentials!
40
+ @client ||= ::Twitter::REST::Client.new do |settings|
41
+ settings.consumer_key = @creds['consumer_api_key']
42
+ settings.consumer_secret = @creds['consumer_api_secret']
43
+ settings.access_token = @creds['access_token']
44
+ settings.access_token_secret = @creds['access_token_secret']
45
+ end
46
+ end
47
+
48
+ def extract_from_env
49
+ values = cred_fieldnames.map { |k| ENV[k.upcase] }
50
+ Hash[cred_fieldnames.zip(values)] unless values.any? { |v| v.nil? || v.empty? }
51
+ end
52
+
53
+ def extract_from_config
54
+ values = cred_fieldnames.map { |k| shell(Configuration.twitter["#{k}_cmd"]) }
55
+ Hash[cred_fieldnames.zip(values)] unless values.any? { |v| v.nil? || v.empty? }
56
+ end
57
+
58
+ def shell(cmd)
59
+ Jekyll.logger.info "running command: #{cmd}"
60
+ `#{cmd}`.strip
61
+ end
62
+
63
+ def cred_fieldnames
64
+ [
65
+ 'access_token_secret',
66
+ 'access_token',
67
+ 'consumer_api_key',
68
+ 'consumer_api_secret'
69
+ ]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Recker
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,82 @@
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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ # Recker
5
+ module Recker
6
+ require 'jekyll-recker/commands.rb'
7
+ require 'jekyll-recker/configuration.rb'
8
+ require 'jekyll-recker/generators.rb'
9
+ require 'jekyll-recker/stats.rb'
10
+ require 'jekyll-recker/tags.rb'
11
+ require 'jekyll-recker/twitter.rb'
12
+ require 'jekyll-recker/version.rb'
13
+ require 'jekyll-recker/words.rb'
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-recker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Recker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jekyll
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: twitter
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'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - alex@reckerfamily.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE
77
+ - README.org
78
+ - _includes/archive.html
79
+ - _includes/nav.html
80
+ - _includes/pager.html
81
+ - _includes/stats.html
82
+ - _layouts/default.html
83
+ - _layouts/home.html
84
+ - _layouts/page.html
85
+ - _layouts/post.html
86
+ - _sass/jekyll-recker.sass
87
+ - assets/images/example-stats.png
88
+ - assets/images/example-tweet.png
89
+ - assets/images/me.jpg
90
+ - assets/images/words.png
91
+ - 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
+ - lib/jekyll-recker.rb
103
+ - lib/jekyll-recker/commands.rb
104
+ - lib/jekyll-recker/configuration.rb
105
+ - lib/jekyll-recker/generators.rb
106
+ - lib/jekyll-recker/stats.rb
107
+ - lib/jekyll-recker/tags.rb
108
+ - lib/jekyll-recker/twitter.rb
109
+ - lib/jekyll-recker/version.rb
110
+ - lib/jekyll-recker/words.rb
111
+ homepage: https://www.alexrecker.com/jekyll-recker.html
112
+ licenses:
113
+ - GPLv3
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubygems_version: 3.0.3
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: This is the jekyll theme for my personal website.
134
+ test_files: []