Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 276 lines (220 sloc) 6.01 KB
#!/usr/bin/env ruby
# frozen_string_literal: true
# ssqdb.rb
# Author: William Woodruff
# ------------------------
# A tiny static quote DB, inspired by QDB.
# ssqdb expects the following quote file format:
# %
# <person1> witty comment
# <person2> witty response
# %
# <person3> less witty comment
# %
# (etc)
# ------------------------
# This code is licensed by William Woodruff under the MIT License.
# http://opensource.org/licenses/MIT
require "erb"
require "cgi"
require "json"
VERSION = 9
GENERATED_AT = Time.now.to_i
CSS = <<~CSS
body {
background-color: #F6F6F6;
font-family: monospace;
color: #222;
max-width: 50em;
margin: auto;
padding: 1em;
}
a {
color: #555;
}
pre {
overflow: auto;
white-space: pre-wrap;
}
.quote-link {
text-decoration: none;
}
.quote-header {
padding-left: 0.5em;
padding-top: 0.5em;
color: #555;
}
.quote-text {
padding: 0 .5em .5em .5em;
}
.quote {
padding: 0;
margin: 0;
}
.quote:nth-of-type(odd) {
background-color: #e0e0e0;
padding-bottom: 0.1em;
margin-bottom: -0.5em;
}
CSS
JS = <<~JS
function urlify() {
var urlRegex = /(https?:\\/\\/[^\\s]+)/g;
var newContent = document.body.innerHTML.replace(urlRegex, '<a href="$1" target="_blank">$1</a>');
document.body.innerHTML = newContent;
}
function makeUrl(count) {
var num = Math.floor(Math.random() * count);
window.location = "quote" + num + ".html";
}
function randomQuote() {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState == 4 && req.status == 200) {
makeUrl(req.responseText);
}
}
req.open("GET", "count", true);
req.send(null);
}
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("random-quote").addEventListener("click", randomQuote);
var urlifiers = document.querySelectorAll("#urlify-btn");
urlifiers.forEach(function(urlifier) {
urlifier.addEventListener("click", urlify);
});
});
JS
HEADER = <<~HEADER
<html>
<head>
<meta charset="UTF-8">
<meta name="generated-at" content="#{GENERATED_AT}">
<title><%= title %> <%= " - quote \\#\#{index}" if defined? index %></title>
<link rel="stylesheet" href="ssqdb.css">
<script src="ssqdb.js" defer></script>
</head>
<body>
<a href="index.html">all</a> /
<a href="#" id="random-quote">random</a> /
<a href="quotes.db">db</a> /
<a href="about.html">about</a>
HEADER
FOOTER = <<~FOOTER
</body>
</html>
FOOTER
QUOTE = <<~QUOTE
<div class="quote">
<h5 class="quote-header">
quote #<%= index %>
<a class="quote-link" href="quote<%= index %>.html">(&rarr;)</a>
<a class="quote-link" href="#" id="urlify-btn">(urls)</a>
<% if opts[:json] %>
<a class="quote-link" href="quote<%= index %>.json">(json)</a>
<% end %>
</h5>
<pre class="quote-text">
<%= quote %>
</pre>
</div>
QUOTE
INDEX_PAGE = <<~INDEX
#{HEADER}
<% html_quotes.each_with_index do |quote, index| %>
#{QUOTE}
<% end %>
#{FOOTER}
INDEX
QUOTE_PAGE = <<~QUOTE
#{HEADER}
#{QUOTE}
#{FOOTER}
QUOTE
ABOUT_PAGE = <<~ABOUT
#{HEADER}
<p>
ssqdb is a tiny static quote DB, inspired by QDB and its siblings.
</p>
<p>
usage: <code>$ ssqdb.rb /path/to/your/quotes.txt</code>
</p>
<p>
"quotes.txt" should be formatted as follows:
<pre>
%
&lt;person1&gt; witty comment
&lt;person2&gt; witty response
%
&lt;person3&gt; less witty comment
%
(etc)
</pre>
</p>
<p>
this site was generated by ssqdb version #{VERSION}.
</p>
<p>
the latest version can be found <a href="https://yossarian.net/snippets#ssqdb">here</a>.
</p>
#{FOOTER}
ABOUT
def help
puts <<~HELP
Usage: #{$PROGRAM_NAME} <quote file> [output directory] [title] [options]
Options:
--json Generate JSON files in addition to HTML
--help Print this help message
--version Print version information
--verbose Write verbose information to stderr
HELP
exit 0
end
def version
puts "ssqdb version #{VERSION}."
exit 0
end
def verbose(msg, level: 0)
STDERR.puts "#{"\t" * level}[+] #{msg}"
end
opts = {
json: !!ARGV.delete("--json"),
help: !!ARGV.delete("--help") || !!ARGV.delete("-h"),
version: !!ARGV.delete("--version"),
verbose: !!ARGV.delete("--verbose"),
}
help if opts[:help]
version if opts[:version]
quotes_file = ARGV.shift || abort("I need a file to load quotes from.")
output_dir = ARGV.shift || __dir__
title = CGI.escapeHTML(ARGV.shift || "ssqdb")
abort("That isn't a file.") unless File.file?(quotes_file)
abort("Output directory doesn't exist.") unless Dir.exist?(output_dir)
quotes_string = File.read(quotes_file)
quotes = quotes_string.split(/^%$/).map(&:strip).reject(&:empty?).uniq
abort("This file doesn't look like a quote DB.") if quotes.empty?
html_quotes = quotes.map do |quote|
CGI.escapeHTML(quote)
end
verbose "generating count, quotes.db, index/css/js, and about"
File.write(File.join(output_dir, "count"), quotes.size)
File.write(File.join(output_dir, "quotes.db"), quotes_string)
File.write(File.join(output_dir, "index.html"), ERB.new(INDEX_PAGE).result(binding))
File.write(File.join(output_dir, "ssqdb.css"), CSS)
File.write(File.join(output_dir, "ssqdb.js"), JS)
File.write(File.join(output_dir, "about.html"), ERB.new(ABOUT_PAGE).result(binding))
verbose "generating individual quote pages"
html_quotes.each_with_index do |quote, index|
html_file = File.join(output_dir, "quote#{index}.html")
File.write(html_file, ERB.new(QUOTE_PAGE).result(binding))
verbose "generated #{html_file}", level: 1
end
if opts[:json]
verbose "generating individual json files (requested)"
quotes.each_with_index do |quote, index|
json_file = File.join(output_dir, "quote#{index}.json")
blob = { count: index, quote: quote }
File.write(json_file, blob.to_json)
verbose "generated #{json_file}", level: 1
end
end