aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml1
-rw-r--r--LICENSE21
-rw-r--r--README.md31
-rw-r--r--shard.lock6
-rw-r--r--shard.yml11
-rw-r--r--spec/guff_spec.cr9
-rw-r--r--spec/spec_helper.cr2
-rw-r--r--src/guff.cr160
-rw-r--r--src/guff/config.cr98
-rw-r--r--src/guff/version.cr3
11 files changed, 348 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..37a3029
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/doc/
+/libs/
+/.crystal/
+/.shards/
+
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..ffc7b6a
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1 @@
+language: crystal
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3f522c4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Paul Duncan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5129c51
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# guff
+
+TODO: Write a description here
+
+## Installation
+
+
+TODO: Write installation instructions here
+
+
+## Usage
+
+
+
+TODO: Write usage instructions here
+
+## Development
+
+TODO: Write development instructions here
+
+## Contributing
+
+1. Fork it ( https://github.com/[your-github-name]/guff/fork )
+2. Create your feature branch (git checkout -b my-new-feature)
+3. Commit your changes (git commit -am 'Add some feature')
+4. Push to the branch (git push origin my-new-feature)
+5. Create a new Pull Request
+
+## Contributors
+
+- [[your-github-name]](https://github.com/[your-github-name]) Paul Duncan - creator, maintainer
diff --git a/shard.lock b/shard.lock
new file mode 100644
index 0000000..63e9126
--- /dev/null
+++ b/shard.lock
@@ -0,0 +1,6 @@
+version: 1.0
+shards:
+ sqlite3:
+ github: manastech/crystal-sqlite3
+ version: 0.1.0
+
diff --git a/shard.yml b/shard.yml
new file mode 100644
index 0000000..a0d1d15
--- /dev/null
+++ b/shard.yml
@@ -0,0 +1,11 @@
+name: guff
+version: 0.1.0
+
+dependencies:
+ sqlite3:
+ github: manastech/crystal-sqlite3
+
+authors:
+ - Paul Duncan <pabs@pablotron.org>
+
+license: MIT
diff --git a/spec/guff_spec.cr b/spec/guff_spec.cr
new file mode 100644
index 0000000..b7c87db
--- /dev/null
+++ b/spec/guff_spec.cr
@@ -0,0 +1,9 @@
+require "./spec_helper"
+
+describe Guff do
+ # TODO: Write tests
+
+ it "works" do
+ false.should eq(true)
+ end
+end
diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr
new file mode 100644
index 0000000..b69b555
--- /dev/null
+++ b/spec/spec_helper.cr
@@ -0,0 +1,2 @@
+require "spec"
+require "../src/guff"
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