-
# frozen_string_literal: true
-
-
1
require 'spec_helper'
-
-
# rubocop:disable Metrics/BlockLength
-
1
describe Blog::Entry do
-
10
let(:doc) { double('doc') }
-
-
1
describe '#date' do
-
1
it 'should convert Time to a Date' do
-
1
expect(doc).to receive('date').and_return Time.parse('2020-07-03').localtime
-
-
1
actual = Blog::Entry.new(doc).date
-
1
expected = Date.new(2020, 0o7, 0o3)
-
1
expect(actual).to eq(expected)
-
end
-
end
-
-
1
describe '#word_count' do
-
1
it 'should count the words in content' do
-
1
expect(doc).to receive(:content).and_return 'This is a test, see?'
-
1
actual = Blog::Entry.new(doc).word_count
-
1
expect(actual).to eq(5)
-
end
-
end
-
-
1
describe '#title' do
-
1
it 'should convert the date to UYD format' do
-
1
entry = Blog::Entry.new(doc)
-
1
expect(entry).to receive(:date).and_return Date.new(2020, 1, 1)
-
1
expect(entry.title).to eq('Wednesday, January 1 2020')
-
end
-
end
-
-
1
describe '#subtitle' do
-
1
it 'should return the document title' do
-
1
expect(doc).to receive(:data).and_return({ 'title' => 'boop' })
-
1
expect(Blog::Entry.new(doc).subtitle).to eq('boop')
-
end
-
end
-
-
1
describe '#url' do
-
1
it 'should return the document url' do
-
1
expect(doc).to receive(:url).and_return 'boop'
-
1
expect(Blog::Entry.new(doc).url).to eq('boop')
-
end
-
end
-
-
1
describe '#words' do
-
1
it 'should remove punctuation' do
-
1
expect(doc).to receive(:content).and_return 'alex...!'
-
1
actual = Blog::Entry.new(doc).words
-
1
expected = ['alex']
-
1
expect(actual).to eq(expected)
-
end
-
-
1
it 'should leave apostrophes' do
-
1
expect(doc).to receive(:content).and_return "This is Alex\'s blog"
-
1
actual = Blog::Entry.new(doc).words
-
1
expected = ['this', 'is', "alex\'s", 'blog']
-
1
expect(actual).to eq(expected)
-
end
-
-
1
it 'should make all words lowercase' do
-
1
expect(doc).to receive(:content).and_return 'Alex Martin Recker'
-
1
actual = Blog::Entry.new(doc).words
-
1
expected = %w[alex martin recker]
-
1
expect(actual).to eq(expected)
-
end
-
-
1
it 'should remove newlines and whitespace' do
-
1
content = <<~TEXT
-
But soft!
-
What light through yonder window breaks?
-
It is the east, and Juliet, is the sun!
-
TEXT
-
1
expect(doc).to receive(:content).and_return content
-
1
expected = %w[
-
but soft what light through
-
yonder window breaks it is
-
the east and juliet is the sun
-
]
-
1
actual = Blog::Entry.new(doc).words
-
1
expect(actual).to eq(expected)
-
end
-
end
-
end
-
# rubocop:enable Metrics/BlockLength
-
# frozen_string_literal: true
-
-
1
describe Blog::Files do
-
7
let(:k) { Class.new { extend ::Blog::Files } }
-
-
1
describe '#root' do
-
1
it 'should return a real path' do
-
1
expect(File.directory?(k.root)).to be true
-
end
-
end
-
-
1
describe '#join' do
-
1
it 'should take one argument' do
-
1
actual = k.join('images')
-
1
expect(File.basename(actual)).to eq('images')
-
end
-
-
1
it 'should take multiple arguments' do
-
1
actual = k.join('a', 'b')
-
1
expect(actual.split('/').last(2)).to eq(%w[a b])
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
describe Blog::Filters do
-
5
let(:k) { Class.new { extend ::Blog::Filters } }
-
-
1
describe '#uyd_date' do
-
1
it 'should convert a date into standard UYD format' do
-
1
actual = k.uyd_date(Date.new(2020, 7, 21))
-
1
expected = 'Tuesday, July 21 2020'
-
1
expect(actual).to eq(expected)
-
end
-
end
-
-
1
describe '#pretty' do
-
1
it 'should add commas to a number' do
-
1
actual = k.pretty(1_000_001)
-
1
expected = '1,000,001'
-
1
expect(actual).to eq(expected)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
describe Blog::Math do
-
7
let(:k) { Class.new { extend ::Blog::Math } }
-
-
1
describe '#average' do
-
1
it 'should average a list of integers' do
-
1
expect(k.average([1, 1, 2, 2, 3, 3])).to eq(2)
-
end
-
end
-
-
1
describe '#total' do
-
1
it 'should total a list of integers' do
-
1
expect(k.total([1, 1, 2, 2, 3, 3])).to eq(12)
-
end
-
end
-
-
1
describe '#occurences' do
-
1
it 'should count occurances of keys in a list of targets' do
-
1
keys = %w[Kelly Alex Sarah Frank]
-
1
text = 'Kelly and Alex and Sarah are siblings. Kelly and Sarah are sisters'
-
-
expected = {
-
1
'Kelly' => 2,
-
'Alex' => 1,
-
'Sarah' => 2
-
}
-
-
1
expect(k.occurences(keys, text.split)).to eq(expected)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/BlockLength
-
1
describe Blog::Site do
-
4
let(:site) { double('site') }
-
-
1
describe '#entries' do
-
1
it 'should return an ordered list of descending posts' do
-
4
dates = (1..3).map { |n| Date.new(2020, 7, n) }
-
-
1
posts = dates.map do |date|
-
3
post = double('post')
-
3
allow(post).to receive(:published?).and_return true
-
3
allow(post).to receive(:date).and_return date
-
3
post
-
end
-
-
1
expect(site).to receive_message_chain(:posts, :docs).and_return(posts)
-
-
1
actual = Blog::Site.new(site).entries.collect(&:date)
-
1
expect(actual).to eq(dates.reverse)
-
end
-
-
1
it 'should return published posts' do
-
1
posts = []
-
-
1
published = double('published')
-
1
expect(published).to receive(:published?).and_return true
-
1
posts << published
-
-
1
unpublished = double('unpublished')
-
1
expect(unpublished).to receive(:published?).and_return false
-
1
posts << unpublished
-
-
1
posts.each_with_index do |post, i|
-
2
date = Date.new(2020, 7, i + 1)
-
2
allow(post).to receive(:date).and_return date
-
end
-
-
1
expect(site).to receive_message_chain(:posts, :docs).and_return(posts)
-
-
1
actual = Blog::Site.new(site).entries
-
1
expect(actual.count).to eq(1)
-
1
expect(actual.first.date).to eq(Date.new(2020, 7, 1))
-
end
-
-
1
describe '#data' do
-
1
it 'should pass through to @site.data' do
-
1
site = double('site')
-
1
data = {}
-
1
expect(site).to receive(:data).and_return(data)
-
1
Blog::Site.new(site).data['hello'] = 'world'
-
1
expect(data).to eq({ 'hello' => 'world' })
-
end
-
end
-
-
1
describe '#word_counts' do
-
1
it 'should aggregate entry word counts' do
-
1
entries = []
-
-
1
3.times do
-
3
entry = double('entry')
-
3
expect(entry).to receive(:word_count).and_return 100
-
3
entries << entry
-
end
-
-
1
site = Blog::Site.new({})
-
1
expect(site).to receive(:entries).and_return entries
-
-
1
expect(site.word_counts).to eq([100, 100, 100])
-
end
-
end
-
-
1
describe '#dates' do
-
1
it 'should aggregate entry dates' do
-
4
dates = (1..3).map { |n| ::Date.new(2020, 1, n) }
-
1
entries = dates.map do |date|
-
3
entry = double('entry')
-
3
expect(entry).to receive(:date).and_return(date)
-
3
entry
-
end
-
1
site = Blog::Site.new(site)
-
1
expect(site).to receive(:entries).and_return entries.reverse
-
1
expect(site.dates).to eq(dates.reverse)
-
end
-
end
-
-
1
describe '#recker_config' do
-
1
it 'should default to empty' do
-
1
expect(site).to receive(:config).and_return({})
-
1
expect(Blog::Site.new(site).recker_config).to eq({})
-
end
-
end
-
-
1
describe '#production?' do
-
3
let(:k) { Blog::Site.new({}) }
-
-
1
context 'JEKYLL_ENV=production' do
-
2
before(:each) { expect(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('production') }
-
-
1
it 'should return true' do
-
1
expect(k.production?).to be true
-
end
-
end
-
-
1
context 'JEKYLL_ENV=development' do
-
2
before(:each) { expect(ENV).to receive(:[]).with('JEKYLL_ENV').and_return('development') }
-
-
1
it 'should return false' do
-
1
expect(k.production?).to be false
-
end
-
end
-
end
-
end
-
end
-
# rubocop:enable Metrics/BlockLength
-
# frozen_string_literal: true
-
-
# rubocop:disable Metrics/BlockLength
-
1
describe Blog::Time do
-
15
let(:k) { Class.new { extend ::Blog::Time } }
-
-
1
describe '#to_uyd_date' do
-
1
it 'should convert a date into UYD show opening format' do
-
1
expect(k.to_uyd_date(::Date.new(2020, 1, 1))).to eq 'Wednesday, January 1 2020'
-
end
-
end
-
-
1
describe '#slice_by_consecutive' do
-
1
it 'should slice a list of ascending dates' do
-
dates = [
-
1
::Date.new(2001, 2, 3),
-
::Date.new(2001, 2, 4),
-
::Date.new(2001, 2, 6),
-
::Date.new(2001, 2, 7),
-
::Date.new(2001, 2, 8)
-
]
-
-
expected = [
-
[
-
1
::Date.new(2001, 2, 3),
-
::Date.new(2001, 2, 4)
-
],
-
[
-
::Date.new(2001, 2, 6),
-
::Date.new(2001, 2, 7),
-
::Date.new(2001, 2, 8)
-
]
-
]
-
-
1
expect(k.slice_by_consecutive(dates)).to eq(expected)
-
end
-
-
1
it 'should slice a list of descending dates' do
-
dates = [
-
1
::Date.new(2001, 2, 8),
-
::Date.new(2001, 2, 7),
-
::Date.new(2001, 2, 6),
-
::Date.new(2001, 2, 4),
-
::Date.new(2001, 2, 3)
-
]
-
-
expected = [
-
[
-
1
::Date.new(2001, 2, 8),
-
::Date.new(2001, 2, 7),
-
::Date.new(2001, 2, 6)
-
],
-
[
-
::Date.new(2001, 2, 4),
-
::Date.new(2001, 2, 3)
-
]
-
]
-
-
1
expect(k.slice_by_consecutive(dates)).to eq(expected)
-
end
-
end
-
-
1
describe '#calculate_streaks' do
-
1
it 'should calculate streaks from ascending dates' do
-
dates = [
-
1
::Date.new(2001, 2, 3),
-
::Date.new(2001, 2, 4),
-
::Date.new(2001, 2, 6),
-
::Date.new(2001, 2, 7),
-
::Date.new(2001, 2, 8)
-
]
-
-
expected = [
-
{
-
1
'days' => 1,
-
'start' => ::Date.new(2001, 2, 3),
-
'end' => ::Date.new(2001, 2, 4)
-
},
-
{
-
'days' => 2,
-
'start' => ::Date.new(2001, 2, 6),
-
'end' => ::Date.new(2001, 2, 8)
-
}
-
]
-
-
1
expect(k.calculate_streaks(dates)).to eq(expected)
-
end
-
-
1
it 'should calculate streaks from descending dates' do
-
dates = [
-
1
::Date.new(2001, 2, 8),
-
::Date.new(2001, 2, 7),
-
::Date.new(2001, 2, 6),
-
::Date.new(2001, 2, 4),
-
::Date.new(2001, 2, 3)
-
]
-
-
expected = [
-
{
-
1
'days' => 2,
-
'start' => ::Date.new(2001, 2, 6),
-
'end' => ::Date.new(2001, 2, 8)
-
},
-
{
-
'days' => 1,
-
'start' => ::Date.new(2001, 2, 3),
-
'end' => ::Date.new(2001, 2, 4)
-
}
-
]
-
-
1
expect(k.calculate_streaks(dates)).to eq(expected)
-
end
-
end
-
-
1
describe '#time_to_date' do
-
1
it 'should convert a time to a date' do
-
1
actual = k.time_to_date(Time.parse('2020-07-03').localtime)
-
1
expected = Date.new(2020, 0o7, 0o3)
-
1
expect(actual).to eq(expected)
-
end
-
end
-
-
1
describe '#date_to_time' do
-
1
it 'should convert a date to a time' do
-
1
actual = k.date_to_time(::Date.new(2020, 1, 1))
-
1
expect(actual).to be_a ::DateTime
-
1
expect([actual.hour, actual.minute, actual.second]).to eq([0, 0, 0])
-
end
-
end
-
end
-
# rubocop:enable Metrics/BlockLength
-
# frozen_string_literal: true
-
-
# Blog
-
1
module Blog
-
1
autoload :Entry, 'blog/entry'
-
1
autoload :Files, 'blog/files'
-
1
autoload :Git, 'blog/git'
-
1
autoload :Logging, 'blog/logging'
-
1
autoload :Math, 'blog/math'
-
1
autoload :Shell, 'blog/shell'
-
1
autoload :Site, 'blog/site'
-
1
autoload :Social, 'blog/social'
-
1
autoload :Time, 'blog/time'
-
-
# Eager Loads
-
1
require 'blog/commands'
-
1
require 'blog/filters'
-
1
require 'blog/generators'
-
end
-
# frozen_string_literal: true
-
-
1
require 'jekyll'
-
-
1
module Blog
-
# Commands
-
1
module Commands
-
# Share Command
-
1
class Share < Jekyll::Command
-
1
def self.init_with_program(prog)
-
prog.command(:share) do |c|
-
c.syntax 'share'
-
c.description 'Share latest post with each configured backend'
-
c.option 'dry', '-d', '--dry', 'perform dry run'
-
c.action { |args, opts| action(args, opts) }
-
end
-
end
-
-
1
def self.action(args, options)
-
site = ::Jekyll::Site.new(configuration_from_options(options))
-
site.reset
-
site.read
-
Social.action(site, args, options)
-
rescue StandardError => e
-
Jekyll.logger.error e.message
-
exit 1
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Blog
-
# Entry
-
1
class Entry
-
1
include Blog::Time
-
-
1
def initialize(doc)
-
13
@doc = doc
-
end
-
-
1
def content
-
5
@doc.content
-
end
-
-
1
def date
-
5
@date ||= time_to_date(@doc.date)
-
end
-
-
1
def title
-
1
to_uyd_date(date)
-
end
-
-
1
def subtitle
-
1
@doc.data['title']
-
end
-
-
1
def url
-
1
@doc.url
-
end
-
-
1
def words
-
5
content.split.map do |token|
-
30
token.gsub!(/[^0-9a-z ']/i, '')
-
30
token.downcase
-
end
-
end
-
-
1
def word_count
-
1
@word_count ||= words.size
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Blog
-
# Files
-
1
module Files
-
1
def root
-
1
File.expand_path(File.join(__dir__, '../../'))
-
end
-
-
1
def join(*subpaths)
-
2
File.join(*subpaths)
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'liquid'
-
-
1
module Blog
-
# Filters
-
1
module Filters
-
1
def uyd_date(date)
-
1
date.strftime('%A, %B %-d %Y')
-
end
-
-
1
def pretty(num)
-
1
num.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
-
end
-
end
-
end
-
-
1
::Liquid::Template.register_filter(Blog::Filters)
-
# frozen_string_literal: true
-
-
1
require 'jekyll'
-
1
require 'rake'
-
-
1
module Blog
-
# Generators
-
1
module Generators
-
# Base
-
1
module Base
-
1
include Blog::Logging
-
1
include Blog::Time
-
end
-
-
# Stats Generator
-
1
class Stats < Jekyll::Generator
-
1
include Base
-
1
include Blog::Math
-
-
1
attr_reader :site
-
-
1
def generate(site)
-
@site = Site.new(site)
-
site.data['stats'] = if @site.production?
-
info 'calculating statistics'
-
real_stats
-
else
-
info 'stubbing out statistics'
-
stub_stats
-
end
-
end
-
-
1
def real_stats
-
{
-
'total_words' => total(site.word_counts),
-
'average_words' => average(site.word_counts).round(0),
-
'total_posts' => site.entries.size,
-
'consecutive_posts' => consecutive_posts,
-
'swears' => calculate_swears
-
}
-
end
-
-
1
def stub_stats
-
{
-
'total_words' => 0,
-
'average_words' => 0,
-
'total_posts' => 0,
-
'consecutive_posts' => 0,
-
'swears' => 0
-
}
-
end
-
-
1
private
-
-
1
def consecutive_posts
-
calculate_streaks(site.dates).first['days']
-
end
-
-
1
def calculate_swears
-
results = Hash[count_swears]
-
results['total'] = total(results.values)
-
results
-
end
-
-
1
def count_swears
-
occurences(swears, site.words).reject { |_k, v| v.zero? }.sort_by { |_k, v| -v }
-
end
-
-
1
def swears
-
%w[
-
ass asshole booger
-
crap damn fart fuck hell jackass
-
piss poop shit
-
]
-
end
-
end
-
-
# GitGenerator
-
1
class GitGenerator < Jekyll::Generator
-
1
include Base
-
1
include Blog::Git
-
-
1
attr_reader :site
-
-
1
def generate(site)
-
@site = Site.new(site)
-
site.data.merge!(data)
-
end
-
-
1
def data
-
if site.production?
-
info 'reading git history'
-
real_data
-
else
-
info 'stubbing out git history'
-
stub_data
-
end
-
end
-
-
1
def real_data
-
{
-
'git_head' => git_head,
-
'git_head_summary' => git_head_summary,
-
'git_shorthead' => git_short_head,
-
'git_commit_count' => git_commit_count
-
}
-
end
-
-
1
def stub_data
-
{
-
'git_head' => '',
-
'git_head_summary' => '',
-
'git_shorthead' => '',
-
'git_commit_count' => 0
-
}
-
end
-
end
-
-
# ResizeGenerator
-
1
class ResizeGenerator < Jekyll::Generator
-
1
include Base
-
-
1
def generate(site); end
-
end
-
-
# FrontmatterGenerator
-
1
class FrontmatterGenerator < Jekyll::Generator
-
1
include Base
-
-
1
def generate(site)
-
info 'setting default frontmatter'
-
scan_pages!(site)
-
scan_posts!(site)
-
end
-
-
1
def scan_pages!(site)
-
site.pages.each do |page|
-
filename = "#{File.basename(page.name, '.*')}.html"
-
page.data['filename'] = filename
-
page.data['permalink'] = filename if page.data['permalink'].nil?
-
end
-
end
-
-
1
def scan_posts!(site)
-
site.posts.docs.each do |post|
-
post.data.merge!(
-
{
-
'title' => to_uyd_date(post.date),
-
'description' => post.data['title'],
-
'permalink' => "/#{to_filename_date(post.date)}.html",
-
'filename' => "#{to_filename_date(post.date)}.html"
-
}
-
)
-
end
-
end
-
end
-
-
# Nav Generator
-
1
class NavGenerator < Jekyll::Generator
-
1
include Base
-
-
1
attr_reader :site
-
-
1
def generate(site)
-
@site = Site.new(site)
-
info 'building site navigation'
-
site.data['nav'] = nav
-
end
-
-
1
def nav
-
flagged_pages.sort_by { |p| p.data['nav'].to_i }
-
end
-
-
1
def flagged_pages
-
site.pages.select { |p| p.data.key? 'nav' }
-
end
-
end
-
-
# CoverageGenerator
-
1
class CoverageGenerator < Jekyll::Generator
-
1
include Base
-
1
include Blog::Files
-
1
include Blog::Shell
-
-
1
def generate(site)
-
info 'reading code coverage'
-
data_file = join('_site/coverage/data.json')
-
site.data['coverage'] = JSON.parse(File.read(data_file))
-
end
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Blog
-
# Git
-
1
module Git
-
1
include Blog::Shell
-
-
1
def git_short_head
-
shell('git rev-parse --short HEAD').chomp
-
end
-
-
1
def git_head
-
shell('git rev-parse HEAD').chomp
-
end
-
-
1
def git_head_summary
-
shell('git log -1 --pretty=format:"%s" HEAD').chomp
-
end
-
-
1
def git_commit_count
-
shell('git rev-list --count master').chomp.to_i
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'jekyll'
-
-
1
module Blog
-
# Logging
-
1
module Logging
-
1
def info(msg)
-
Jekyll.logger.info 'blog:', msg
-
end
-
-
1
def debug(msg)
-
Jekyll.logger.debug 'blog:', msg
-
end
-
-
1
def error(msg)
-
Jekyll.logger.error 'blog:', msg
-
end
-
-
1
def warn(msg)
-
Jekyll.logger.warn 'blog:', msg
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Blog
-
# Math
-
1
module Math
-
1
def average(numlist)
-
6
numlist.inject { |sum, el| sum + el }.to_f / numlist.size
-
end
-
-
1
def total(numlist)
-
7
numlist.inject(0) { |sum, x| sum + x }
-
end
-
-
1
def occurences(keys, targets)
-
1
results = Hash.new(0)
-
1
targets.each do |target|
-
12
results[target] += 1 if keys.include? target
-
end
-
1
results
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'open3'
-
-
1
module Blog
-
# Shell
-
1
module Shell
-
# ShellCommand
-
1
class ShellCommand
-
1
include Blog::Logging
-
-
1
attr_reader :cmd, :stdout, :stderr, :status
-
-
1
def initialize(cmd)
-
@cmd = cmd
-
end
-
-
1
def run!
-
@stdout, @err, @status = Open3.capture3(cmd)
-
debug(to_s)
-
end
-
-
1
def failed?
-
!@status.success?
-
end
-
-
1
def to_s
-
msg = "`#{cmd}` returned #{status}"
-
msg += "\n--- STDOUT\n#{stdout}" if stdout
-
msg += "\n--- STDERR\n#{stderr}" if stderr
-
msg
-
end
-
end
-
-
1
def shell(cmd, raises: true)
-
result = ShellCommand.new(cmd)
-
result.run!
-
raise result.to_s if raises && result.failed?
-
-
result.stdout
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
module Blog
-
# Site
-
1
class Site
-
1
def initialize(site)
-
8
@site = site
-
end
-
-
1
def entries
-
2
@entries ||= build_entries
-
end
-
-
1
def latest
-
entries.first
-
end
-
-
1
def pages
-
@site.pages
-
end
-
-
1
def production?
-
2
ENV['JEKYLL_ENV'] == 'production'
-
end
-
-
1
def data
-
1
@site.data
-
end
-
-
1
def url
-
@site.config['url']
-
end
-
-
1
def word_counts
-
1
entries.collect(&:word_count)
-
end
-
-
1
def words
-
entries.collect(&:words).flatten
-
end
-
-
1
def dates
-
1
entries.collect(&:date)
-
end
-
-
1
def images
-
exts = ['.jpg', 'jpeg', '.png', '.svg']
-
@site.static_files.collect(&:path).select { |f| exts.include? File.extname(f) }
-
end
-
-
1
def root
-
File.absolute_path(File.join(__dir__, '../../'))
-
end
-
-
1
def root_join(path)
-
File.join(root, path)
-
end
-
-
1
def recker_config
-
1
@site.config.fetch('recker', {})
-
end
-
-
1
def config
-
@site.config
-
end
-
-
1
def graphs_dir
-
recker_config.fetch('graphs', 'assets/images/graphs/')
-
end
-
-
1
def data_dir
-
File.join root, '_data'
-
end
-
-
1
def tmp_join(path)
-
File.join root, 'tmp', path
-
end
-
-
1
def graphs_join(path)
-
File.join root, 'assets/images/graphs/', path
-
end
-
-
1
def site_join(path)
-
File.join(root, '_site', path)
-
end
-
-
1
private
-
-
1
def build_entries
-
2
@site.posts.docs
-
.select(&:published?)
-
.sort_by(&:date)
-
.reverse
-
4
.map { |p| Entry.new(p) }
-
end
-
end
-
end
-
# frozen_string_literal: true
-
-
1
require 'date'
-
1
require 'time'
-
-
1
module Blog
-
# Time
-
1
module Time
-
1
def time_to_date(time)
-
6
::Date.parse(time.strftime('%Y-%m-%d'))
-
end
-
-
1
def date_to_time(date)
-
1
date.to_time.to_datetime
-
end
-
-
1
def to_uyd_date(date)
-
2
date.strftime('%A, %B %-d %Y')
-
end
-
-
1
def to_filename_date(date)
-
date.strftime('%Y-%m-%d')
-
end
-
-
1
def parse_date(datestr)
-
::Date.parse(datestr).to_datetime.new_offset(offset).to_date
-
end
-
-
1
def slice_by_consecutive(dates)
-
20
dates.slice_when { |p, c| c != p - 1 && c != p + 1 }.to_a
-
end
-
-
1
def calculate_streaks(dates)
-
2
slice_by_consecutive(dates).map do |pair|
-
4
first, last = pair.minmax
-
{
-
8
'days' => (last - first).to_i,
-
'start' => first,
-
'end' => last
-
}
-
end
-
end
-
end
-
end