aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/guff.cr160
-rw-r--r--src/guff/config.cr98
-rw-r--r--src/guff/version.cr3
3 files changed, 261 insertions, 0 deletions
diff --git a/src/guff.cr b/src/guff.cr
new file mode 100644
index 0000000..7e32bfc
--- /dev/null
+++ b/src/guff.cr
@@ -0,0 +1,160 @@
+require "sqlite3"
+require "http/server"
+require "./guff/*"
+
+module Guff
+ class Database < ::SQLite3::Database
+ # TODO (add table_exists?)
+ end
+
+ class Model
+ getter :db
+
+ def initialize(@config : Config)
+ # create site database
+ db_path = "%s/site.db" % [config["data"]]
+ @db = Database.new(db_path)
+ end
+ end
+
+ class Handler < ::HTTP::Handler
+ getter :model
+ getter :config
+
+ def initialize(@model : Model, @config : Config)
+ end
+
+ def call(context : HTTP::Server::Context)
+ # do nothing by default
+ call_next(context)
+ end
+ end
+
+
+ class BlogHandler < Handler
+ MATCHES = [%r{
+ ^/
+
+ # match YYYY/MM/DD/SLUG.html
+ (?<year>\d{4})
+ /
+ (?<month>\d{2})
+ /
+ (?<day>\d{2})
+ /
+ (?<slug>[a-z0-9._-]+)
+ \.html
+
+ $
+ }x, %r{
+ ^/
+
+ # match YYYY/MM/DD
+ (?<year>\d{4})
+ /
+ (?<month>\d{2})
+ /
+ (?<day>\d{2})
+ /?
+
+ $
+ }x, %r{
+ ^/
+
+ # match YYYY/MM
+ (?<year>\d{4})
+ /
+ (?<month>\d{2})
+ /?
+
+ $
+ }x, %r{
+ ^/
+
+ # match YYYY
+ (?<year>\d{4})
+ /?
+
+ $
+ }x, %r{
+ ^/
+
+ # match index
+ blog/?
+ |
+
+ $
+ }x]
+
+ def call(context : HTTP::Server::Context)
+ path = context.request.path || ""
+ if md = MATCHES.reduce(nil) { |r, m| r || m.match(path) }
+ context.response.puts "blog match: %s" % [md.to_s]
+ else
+ call_next(context)
+ end
+ end
+ end
+
+ class SlugHandler < Handler
+ MOCK_SLUGS = {
+ "foo": "test slug foo",
+ "bar": "test slug bar",
+ "baz": "test slug baz",
+ }
+
+ def call(context : HTTP::Server::Context)
+ puts "SlugHandler: path = %s" % [context.request.path]
+ call_next(context)
+ end
+ end
+
+ class Server
+ def self.run(model : Model, config : Config)
+ new(model, config).run
+ end
+
+ def initialize(@model : Model, @config : Config)
+ # create server
+ @server = HTTP::Server.new(
+ config["host"],
+ config["port"].to_i,
+ get_handlers(model, config)
+ ) do |context|
+ context.response.puts "asdf"
+ end
+ end
+
+ def run
+ puts "listening on %s:%s" % %w{host port}.map { |k| @config[k] }
+ @server.listen
+ end
+
+ private def get_handlers(
+ model : Model,
+ config : Config
+ ) : Array(HTTP::Handler)
+ @handlers ||= [
+ HTTP::ErrorHandler.new,
+ HTTP::LogHandler.new,
+ HTTP::DeflateHandler.new,
+ BlogHandler.new(model, config),
+ SlugHandler.new(model, config),
+ HTTP::StaticFileHandler.new(config["public"]),
+ ]
+ end
+ end
+
+ def self.run(app, args)
+ # parse env and cli options
+ config = Config.new(app, args)
+
+ # create model
+ model = Model.new(config)
+
+ # create server
+ Server.run(model, config)
+ end
+end
+
+Guff.run($0, ARGV)
diff --git a/src/guff/config.cr b/src/guff/config.cr
new file mode 100644
index 0000000..6021c63
--- /dev/null
+++ b/src/guff/config.cr
@@ -0,0 +1,98 @@
+require "option_parser"
+
+module Guff
+ class Config
+ DEFAULTS = {
+ "host": "localhost",
+ "port": "8989",
+ "data": "./data",
+ "public": "./public",
+ "environment": "development",
+ }
+
+ DIRS = %w{
+ data
+ public
+ }
+
+ def initialize(app, args)
+ @data = defaults
+ parse_args(app, args)
+ end
+
+ def [](key : String) : String
+ @data[key]
+ end
+
+ def []=(key : String, val : String | Nil) : String
+ @data[key] = val
+ end
+
+ private def defaults : Hash(String, String)
+ DEFAULTS.merge(DEFAULTS.keys.reduce({} of String => String) do |r, key|
+ env_key = "GUFF_" + key.upcase
+ r[key] = ENV[env_key] if ENV.has_key?(env_key)
+ r
+ end)
+ end
+
+ private def parse_args(app, args)
+ OptionParser.parse(args) do |p|
+ p.on(
+ "-h HOST",
+ "--host HOST",
+ "Host (defaults to \"localhost\")."
+ ) do |host|
+ self["host"] = host
+ end
+
+ p.on(
+ "-p PORT",
+ "--port PORT",
+ "Port (defaults to 8989)."
+ ) do |port|
+ self["port"] = port
+ end
+
+ p.on(
+ "-d PATH",
+ "--data-dir PATH",
+ "Path to data directory (defaults to \"./data\")."
+ ) do |path|
+ self["data"] = path
+ end
+
+ p.on(
+ "-u PATH",
+ "--public-dir PATH",
+ "Path to public directory (defaults to \"./public\")."
+ ) do |path|
+ self["public"] = path
+ end
+
+ p.on(
+ "-e ENV",
+ "--environment ENV",
+ "Environment (defaults to \"development\")."
+ ) do |path|
+ self["public"] = path
+ end
+
+
+ p.on(
+ "--help",
+ "Print usage"
+ ) do
+ puts p.to_s
+ end
+ end
+
+ # expand output directory paths and create them
+ %w{data public}.each do |key|
+ self[key] = File.expand_path(File.join(File.dirname(app), self[key]))
+ # puts "DEBUG: %s: %s" % [key, self[key]]
+ Dir.mkdir(self[key], 0o770) unless File.directory?(self[key])
+ end
+ end
+ end
+end
diff --git a/src/guff/version.cr b/src/guff/version.cr
new file mode 100644
index 0000000..898a0d6
--- /dev/null
+++ b/src/guff/version.cr
@@ -0,0 +1,3 @@
+module Guff
+ VERSION = "0.1.0"
+end