diff options
-rw-r--r-- | Makefile | 9 | ||||
-rwxr-xr-x | bin/index | 2 | ||||
-rwxr-xr-x | bin/page | 2 | ||||
-rw-r--r-- | bin/util.rb | 302 | ||||
-rw-r--r-- | config.yaml | 26 | ||||
-rw-r--r-- | lib/.gitignore | 1 | ||||
-rw-r--r-- | lib/config.rb | 48 | ||||
-rw-r--r-- | lib/license.rb | 17 | ||||
-rw-r--r-- | lib/page.rb | 83 | ||||
-rw-r--r-- | lib/page_index.rb | 63 | ||||
-rw-r--r-- | lib/page_local.rb | 114 | ||||
-rw-r--r-- | lib/page_remote.rb | 56 | ||||
-rw-r--r-- | lib/pandoc.rb (renamed from bin/pandoc.rb) | 1 | ||||
-rw-r--r-- | lib/person.rb | 32 | ||||
-rw-r--r-- | lib/sitegen.rb | 0 | ||||
-rw-r--r-- | lib/tag.rb | 17 | ||||
-rw-r--r-- | lib/util.rb | 23 | ||||
-rw-r--r-- | tmpl/index.atom.erb (renamed from bin/index.atom.erb) | 1 | ||||
-rw-r--r-- | tmpl/index.md.erb (renamed from bin/index.md.erb) | 0 | ||||
-rw-r--r-- | tmpl/page.html.erb (renamed from bin/page.html.erb) | 0 |
20 files changed, 491 insertions, 306 deletions
@@ -1,6 +1,6 @@ SHELL = bash -o pipefail -RUBYLIB=$(realpath .)/bin +RUBYLIB := $(realpath .)/lib$(if $(RUBYLIB),:$(RUBYLIB)) export RUBYLIB # Usage: $(call patsubst,PATTERN1 PATTERN2...,REPLACEMENT,TEXT) @@ -21,7 +21,7 @@ html.dirs = $(sort $(patsubst src%,out%, \ html.out += out/index.atom $(addsuffix /index.html,$(html.dirs)) all: $(html.out) out/main.css -.PHONY: all +.PHONY: all out/tags bin/page = bin/page bin/util.rb bin/pandoc.rb bin/index = bin/index bin/util.rb bin/pandoc.rb @@ -70,6 +70,11 @@ serve-%: all @printf '%s' $(call quote.shell,$($*)) | sed 's/^/#/' | bin/write-ifchanged $@ -include $(wildcard .var.*) + +irb: + irb +.PHONY: irb + .PHONY: FORCE .DELETE_ON_ERROR: .SECONDARY: @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # -*- coding: utf-8 -*- -load 'util.rb' +require 'util' require 'yaml' # ARGV[0] @@ -1,6 +1,6 @@ #!/usr/bin/env ruby # -*- coding: utf-8 -*- -load 'util.rb' +require 'util' require 'uri' template = "bin/page.#{ARGV[0]}.erb" diff --git a/bin/util.rb b/bin/util.rb deleted file mode 100644 index cd7974b..0000000 --- a/bin/util.rb +++ /dev/null @@ -1,302 +0,0 @@ -# coding: utf-8 -load 'pandoc.rb' -require 'erb' -require 'date' -require 'set' -require 'uri' - -$license_urls = { - "CC BY-SA-3.0" => 'https://creativecommons.org/licenses/by-sa/3.0/', - 'WTFPL-2' => "http://www.wtfpl.net/txt/copying/", -} -$person_uris = { - "Luke Shumaker" => "https://lukeshu.com/", - "Andrew Murrell" => "https://andrewdm.me/", -} -$person_emails = { - "Luke Shumaker" => "lukeshu@parabola.nu", - "Andrew Murrell" => "ImFromNASA@gmail.com", -} - -$tag_names = { - "DM" => "DMing Resource", - "ES" => "Essay", - "FF" => "Flash Fiction", - "HB" => "Homebrew", - "SS" => "Short Story", - "WP" => "WIP", -} - -$url = URI::parse('https://www.andrewdm.me') - -class Tag - def initialize(abbr) - @abbr = abbr - end - def abbr - @abbr - end - def name - $tag_names[@abbr] - end - def html - return "<a class=\"tag #{abbr}\" href=\"/tags/#{abbr}.html\">#{name}</a>" - end -end - -class Person - def initialize(name) - @name = name - end - def name - @name - end - def uri - $person_uris[@name] - end - def email - $person_emails[@name] - end - def html - if not email.nil? - return "<a href=\"mailto:#{email}\">#{name}</a>" - elsif not uri.nil? - return "<a href=\"#{uri}\">#{name}</a>" - else - return @name - end - end - def atom - ret = "" - ret += "<name>#{name}</name>" unless name.nil? - ret += "<uri>#{uri}</uri>" unless uri.nil? - ret += "<email>#{email}</email>" unless email.nil? - end -end - -class License - def initialize(name) - @name = name - end - def name - @name - end - def url - $license_urls[@name] - end - def html - "<a href=\"#{url}\">#{name}</a>" - end -end - -class Page - def initialize(infile) - @infile = infile - end - - def infile ; @infile ; end - def input ; @input ||= File.read(infile) ; end - def pandoc - if @pandoc.nil? - types = { - 'md' => 'markdown' - } - - ext = File.extname(infile).gsub(/^[.]/, '') - type = types[ext] || ext - @pandoc = Pandoc::load(type, input) - - if @pandoc['pandoc_format'] - @pandoc = Pandoc::load(@pandoc['pandoc_format'], input) - end - end - @pandoc - end - - def title ; @title ||= pandoc['title'] || input.split("\n",2).first ; end - def showtitle ; @showtitle ||= ! pandoc['title'].nil? ; end - - def author ; @author ||= Person.new( pandoc['author'] || "Andrew Murrell") ; end - def license ; @license ||= License.new(pandoc['license'] || "CC BY-SA-3.0") ; end - def content ; @content ||= pandoc.to('html5 '+(pandoc['pandoc_flags']||'')) ; end - def head ; @head ||= pandoc['html_head_extra'] ; end - def class ; @class ||= pandoc['class'] ; end - - def tags - if @tags.nil? - raw = pandoc['tags'] || [] - if raw.is_a?(String) - raw = raw.split - end - @tags = raw.map{|tag|Tag.new(tag)} - end - @tags - end - - def _published - if @_published.nil? - raw = pandoc['published'] - @_published = Date.parse(raw) unless raw.nil? - end - if @_published.nil? - raw = `git log -n1 --reverse --format='%cI' -- #{infile}` - @_published = DateTime.iso8601(raw) unless raw.empty? - end - @_published - end - - def _updated - if @_updated.nil? - raw = pandoc['updated'] - @_updated = Date.parse(raw) unless raw.nil? - end - if @_updated.nil? - raw = `git log -n1 --format='%cI' -- #{infile}` - @_updated = DateTime.iso8601(raw) unless raw.empty? - end - @_updated - end - - def published - if @published.nil? - unless _published.nil? - @published = _published - else - unless _updated.nil? - @published = _updated - end - end - # sanity check - unless _published.nil? or _updated.nil? - if _updated < _published - @published = _updated - end - end - end - @published - end - - def updated - if @updated.nil? - unless _updated.nil? - @updated = _updated - else - unless _published.nil? - @updated = _published - end - end - end - @updated - end - - def rights - if published.nil? || updated.nil? - years = '' - else - first = published.year - last = updated.year - - years = `git log --date=format:'%Y' --format='%cd' -- .config/login.sh`.split('\n').map{|s|s.to_i} - years.unshift(first) - years.unshift(last) - - # Remove dups and git years outside of [first,last] - # TODO: simplify year spans - years = Set[*years.select{|i|i > first && i < last}].sort.join(', ') - end - @rights ||= "<p>The content of this page is Copyright © #{years} #{author.html}.</p>\n" + - "<p>This page is licensed under the #{license.html} license.</p>" - end - - def abssrcpath - @srcpath ||= infile.sub(/^(src|out)\//, '/') - end - def absoutpath - @outpath ||= abssrcpath.sub(/\.[^\/.]*$/, '.html').sub(/\/index[.]html$/, '') - end - - def url - @url ||= $url + absoutpath - end - def srcurl - @srcurl ||= $url + abssrcpath - end - - def breadcrumbs - if @breadcrumbs.nil? - bc = [] - u = url.path - u = "/" if u == "" - while u != "/" - bc.unshift("<a href=\"#{u}\">#{File.basename(u, File.extname(u))}</a>") - u = File.dirname(u) - end - bc.unshift("<a href=\"/\">Andrew D. Murrell</a>") - @breadcrumbs = bc.join(' » ') - end - @breadcrumbs - end - - def section - return nil - end -end - -def html_escape(html) - html - .gsub('&', '&') - .gsub('>', '>') - .gsub('<', '<') -end - -class ExternPage - def initialize(metadata) - @metadata = metadata - end - - def title - @metadata['title'] - end - - def content - nil - end - - def tags - if @tags.nil? - raw = @metadata['tags'] || [] - if raw.is_a?(String) - raw = raw.split - end - @tags = raw.map{|tag|Tag.new(tag)} - end - return @tags - end - - def url - return $url + @metadata['url'] - end - - def author - Person.new(@metadata['author'] || "Andrew Murrell") - end - - def published - str = @metadata['published'] - if str.nil? and ! @metadata['updated'].nil? - str = @metadata['updated'] - end - return Date.parse(str) - end - def updated - str = @metadata['updated'] - if str.nil? and ! @metadata['published'].nil? - str = @metadata['published'] - end - return Date.parse(str) - end - - def section - return @metadata['section'] - end -end diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..b2eb53e --- /dev/null +++ b/config.yaml @@ -0,0 +1,26 @@ +url: "https://www.andrewdm.me/" +html_suffixes: ["md", "org"] + +# Licenses +default_license: "CC BS-SA-3.0" +license_uris: + "CC BY-SA-3.0": "https://creativecommons.org/licenses/by-sa/3.0/" + "WTFPL-2": "http://www.wtfpl.net/txt/copying/" + +# People +defualt_author: "Andrew Murrell" +person_uris: + "Luke Shumaker": "https://lukeshu.com/" + "Andrew Murrell": "https://andrewdm.me/" +person_emails: + "Luke Shumaker": "lukeshu@parabola.nu" + "Andrew Murrell": "ImFromNASA@gmail.com" + +# Tags +tag_names: + "DM": "DMing Resource" + "ES": "Essay" + "FF": "Flash Fiction" + "HB": "Homebrew" + "SS": "Short Story" + "WP": "WIP" diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..e8db6e5 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1 @@ +/page.rb.txt
\ No newline at end of file diff --git a/lib/config.rb b/lib/config.rb new file mode 100644 index 0000000..b22eedf --- /dev/null +++ b/lib/config.rb @@ -0,0 +1,48 @@ +# coding: utf-8 +require 'yaml' + +require 'uri' + +class Config + def self.get + return @config ||= Config::new('config.yaml') + end + def initialize(filename) + @data = YAML::load(File::read(filename)) + end + def url + return @url ||= URI::parse(@data['url']) + end + def html_suffixes + return @data['html_suffixes'] + end + # Licenses + def default_license + return @default_license ||= @data['default_license'] + end + def license_uri(name) + str = @data['license_uris'][name] + if str.nil? + return nil + end + return URI::parse(str) + end + # People + def default_author + return @default_person ||= @data['default_author'] + end + def person_uri(name) + str = @data['person_uris'][name] + if str.nil? + return nil + end + return URI::parse(str) + end + def person_email(name) + return @data['person_emails'][name] + end + # Tags + def tag_name(abbr) + return @data['tag_names'][abbr] + end +end diff --git a/lib/license.rb b/lib/license.rb new file mode 100644 index 0000000..da68f30 --- /dev/null +++ b/lib/license.rb @@ -0,0 +1,17 @@ +# coding: utf-8 +require 'config' + +class License + def initialize(name) + @name = name + end + def name + @name + end + def url + Config::get.license_uri(@name) + end + def html + "<a href=\"#{url}\">#{name}</a>" + end +end diff --git a/lib/page.rb b/lib/page.rb new file mode 100644 index 0000000..d0b18ef --- /dev/null +++ b/lib/page.rb @@ -0,0 +1,83 @@ +# coding: utf-8 +require 'set' + +require 'tag' + +class Page + # Page is an abstract class. + # + # Implementors must implement several methods: + # + # def url => URI + # def title => String + # def author => Person + # def content => html | nil + # def rights => html | nil + # + # def _tags => String | Enumerable<String> + # + # def _published => DateTime | nil + # def _updated => DateTime | nil + # def _years => Enumerable<Fixnum> + + def tags # => Enumerable<Tag> + if @tags.nil? + raw = _tags + if raw.is_a?(String) + raw = raw.split + end + @tags = raw.map{|tag|Tag.new(tag)} + end + @tags + end + + def published # => DateTime | nil + if @published.nil? + unless _published.nil? + @published = _published + else + unless _updated.nil? + @published = _updated + end + end + # sanity check + unless _published.nil? or _updated.nil? + if _updated < _published + @published = _updated + end + end + end + @published + end + + def updated # => DateTime | nil + if @updated.nil? + unless _updated.nil? + @updated = _updated + else + unless _published.nil? + @updated = _published + end + end + end + @updated + end + + def years # => Enumerable<Fixnum> + if @years.nil? + if published.nil? || updated.nil? + @years = Set[] + else + first = published.year + last = updated.year + + years = _years + years.add(first) + years.add(last) + + @years = Set[*years.select{|i|i >= first && i <= last}] + end + end + @years + end +end diff --git a/lib/page_index.rb b/lib/page_index.rb new file mode 100644 index 0000000..073537e --- /dev/null +++ b/lib/page_index.rb @@ -0,0 +1,63 @@ +# coding: utf-8 +require 'erb' +require 'set' +require 'yaml' + +require 'page_local' +require 'page_remote' +require 'config' + +class IndexPage < LocalPage + def initialize(dirname) + super(dirname) + end + + def _metadata + if @metadata.nil? + yamlfile = _infile+"/index.yaml" + if File::exist?(yamlfile) + @metadata = YAML::load(File::read(yamlfile)) + else + @metadata = {} + end + end + @metadata + end + def _ls + @ls ||= Dir::entries(_infile) + .select{|fname|not fname.start_with?(".")} + .map{|fname|"#{_infile}/#{fname}"} + .select{|path|Dir::exist?(path) or Config::get.html_suffixes.include?(File::extname(path).gsub(/^[.]/, ''))} + end + def pages + if @pages.nil? + @pages = [] + for path in _ls + if Dir::exist?(path) + page = IndexPage::new(path) + @pages.unshift(page) + @pages += page.pages + else + @pages.unshift(LocalPage::new(path)) + end + end + for data in _metadata['external'] + @pages.unshift(RemotePage::new(data)) + end + end + @pages + end + + def _published + return nil + end + def _updated + return nil + end + def _years + return Set[] + end +end + +ERB::new(File::read("tmpl/index.atom.erb")).def_method(IndexPage, 'atom()', "tmpl/index.atom.erb") +ERB::new(File::read("tmpl/index.md.erb")).def_method(IndexPage, '_input()', "tmpl/index.md.erb") diff --git a/lib/page_local.rb b/lib/page_local.rb new file mode 100644 index 0000000..1ca14f0 --- /dev/null +++ b/lib/page_local.rb @@ -0,0 +1,114 @@ +# coding: utf-8 +require 'date' +require 'erb' +require 'set' + +require 'config' +require 'license' +require 'page' +require 'pandoc' +require 'person' + +class LocalPage < Page + def initialize(infile) + @infile = infile + end + + # Some of this code looks a little weird because it is + # super-aggressively lazy-evaluated and cached. + + def _infile ; @infile ; end + def _input ; @input ||= File::read(_infile) ; end + def _pandoc + if @pandoc.nil? + types = { + 'md' => 'markdown' + } + + ext = File::extname(_infile).gsub(/^[.]/, '') + type = types[ext] || ext + @pandoc = Pandoc::load(type, _input) + + if @pandoc['pandoc_format'] + @pandoc = Pandoc::load(@pandoc['pandoc_format'], _input) + end + end + @pandoc + end + + # Query simple document metadata + def title ; @title ||= _pandoc['title'] || _input.split("\n",2).first ; end + def author ; @author ||= Person::new( _pandoc['author'] || Config::get.default_author) ; end + def license ; @license ||= License::new(_pandoc['license'] || Config::get.default_license); end + def head ; @head ||= _pandoc['html_head_extra'] ; end + def class ; @class ||= _pandoc['class'] ; end + def _tags ; @_tags ||= _pandoc['tags'] || [] ; end + + def content + if @content.nil? + @content = '' + # Only insert the title if it came from Pandoc metadata; + # if the title was inferred from the the body content, + # then it is already in the page. + unless _pandoc['title'].nil? + @content += "<h1 class=title>#{title}</h1>\n" + end + + # Insert the body + @content = _pandoc.to('html5 '+(_pandoc['pandoc_flags']||'')) + end + @content + end + + def rights + # TODO: simplify year spans + @rights ||= "<p>The content of this page is Copyright © #{years.sort.join(', ')} #{author.html}.</p>\n" + + "<p>This page is licensed under the #{license.html} license.</p>" + end + + def _gitdates + @gitdates ||= `git log --format='%cI' -- #{_infile}`.split('\n').select{|s|!s.empty?}.map{|s|DateTime::iso8601(s)} + end + + def _published + if @_published.nil? + raw = _pandoc['published'] + @_published = Datetime::parse(raw) unless raw.nil? + end + if @_published.nil? + @_published = _gitdates.sort.first + end + @_published + end + + def _updated + if @_updated.nil? + raw = _pandoc['updated'] + @_updated = DateTime::parse(raw) unless raw.nil? + end + if @_updated.nil? + @updated = _gitdates.sort.last + end + @_updated + end + + def _years + @years ||= Set[*_gitdates.map{|dt|dt.year}] + end + + def abssrcpath + @srcpath ||= _infile.sub(/^(src|out)\//, '/') + end + def absoutpath + @outpath ||= abssrcpath.sub(/\.[^\/.]*$/, '.html').sub(/\/index[.]html$/, '') + end + + def url + @url ||= Config::get.url + absoutpath + end + def srcurl + @srcurl ||= Config::get.url + abssrcpath + end +end + +ERB::new(File::read("tmpl/page.html.erb")).def_method(LocalPage, 'html()', "tmpl/page.html.erb") diff --git a/lib/page_remote.rb b/lib/page_remote.rb new file mode 100644 index 0000000..a754af6 --- /dev/null +++ b/lib/page_remote.rb @@ -0,0 +1,56 @@ +# coding: utf-8 +require 'date' + +require 'config' +require 'page' +require 'tag' + +class RemotePage < Page + def initialize(metadata) + @metadata = metadata + end + + def url + return Config::get.url + @metadata['url'] + end + + def title + @metadata['title'] + end + + def author + Person::new(@metadata['author'] || Config::get.default_author) + end + + def content + return nil + end + + def rights + return nil + end + + def _tags + @metadata['tags'] || [] + end + + def _published + str = @metadata['published'] + if str.nil? + return nil + end + return Date::parse(str) + end + + def _updated + str = @metadata['updated'] + if str.nil? + return nil + end + return Date::parse(str) + end + + def _years + return [] + end +end diff --git a/bin/pandoc.rb b/lib/pandoc.rb index 155ddde..f0bf3f6 100644 --- a/bin/pandoc.rb +++ b/lib/pandoc.rb @@ -1,3 +1,4 @@ +# coding: utf-8 require 'open3' require 'json' diff --git a/lib/person.rb b/lib/person.rb new file mode 100644 index 0000000..6882dd2 --- /dev/null +++ b/lib/person.rb @@ -0,0 +1,32 @@ +# coding: utf-8 +require 'config' + +class Person + def initialize(name) + @name = name + end + def name + @name + end + def uri + Config::get.person_uri(@name) + end + def email + Config::get.person_email(@name) + end + def html + if not email.nil? + return "<a href=\"mailto:#{email}\">#{name}</a>" + elsif not uri.nil? + return "<a href=\"#{uri}\">#{name}</a>" + else + return @name + end + end + def atom + ret = "" + ret += "<name>#{name}</name>" unless name.nil? + ret += "<uri>#{uri}</uri>" unless uri.nil? + ret += "<email>#{email}</email>" unless email.nil? + end +end diff --git a/lib/sitegen.rb b/lib/sitegen.rb new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/sitegen.rb diff --git a/lib/tag.rb b/lib/tag.rb new file mode 100644 index 0000000..4009f67 --- /dev/null +++ b/lib/tag.rb @@ -0,0 +1,17 @@ +# coding: utf-8 +require 'config' + +class Tag + def initialize(abbr) + @abbr = abbr + end + def abbr + @abbr + end + def name + Config::get.tag_name(@abbr) + end + def html + return "<a class=\"tag #{abbr}\" href=\"/tags/#{abbr}.html\">#{name}</a>" + end +end diff --git a/lib/util.rb b/lib/util.rb new file mode 100644 index 0000000..075ebb8 --- /dev/null +++ b/lib/util.rb @@ -0,0 +1,23 @@ +# coding: utf-8 + +module Util + def self.html_escape(html) + html + .gsub('&', '&') + .gsub('>', '>') + .gsub('<', '<') + end + + def self.breadcrumbs(url) + # TODO + bc = [] + u = url.path + u = "/" if u == "" + while u != "/" + bc.unshift("<a href=\"#{u}\">#{File::basename(u, File::extname(u))}</a>") + u = File::dirname(u) + end + bc.unshift("<a href=\"/\">Andrew D. Murrell</a>") + return bc.join(' » ') + end +end diff --git a/bin/index.atom.erb b/tmpl/index.atom.erb index 5b0ef36..cd70f7e 100644 --- a/bin/index.atom.erb +++ b/tmpl/index.atom.erb @@ -19,6 +19,7 @@ <author><%= page.author.atom %></author> <% if page.content %> <content type="html"><%= html_escape(page.content) %></content> +<% end %><% if page.rights %> <rights type="html"><%= html_escape(page.rights) %></rights> <% end %> </entry> diff --git a/bin/index.md.erb b/tmpl/index.md.erb index a3ec547..a3ec547 100644 --- a/bin/index.md.erb +++ b/tmpl/index.md.erb diff --git a/bin/page.html.erb b/tmpl/page.html.erb index e2dc9fe..e2dc9fe 100644 --- a/bin/page.html.erb +++ b/tmpl/page.html.erb |