From 3b51abdac7b77209cbf86e96f331ad38560adae2 Mon Sep 17 00:00:00 2001 From: Magnar Sveen Date: Thu, 9 Jan 2014 18:05:26 +0100 Subject: [PATCH] Initial commit --- .gitignore | 9 ++++++ README.md | 37 +++++++++++++++++++++ dev/user.clj | 12 +++++++ project.clj | 10 ++++++ src/stasis/core.clj | 52 ++++++++++++++++++++++++++++++ test/stasis/core_test.clj | 67 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 187 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 dev/user.clj create mode 100644 project.clj create mode 100644 src/stasis/core.clj create mode 100644 test/stasis/core_test.clj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e04714b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/target +/classes +/checkouts +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port diff --git a/README.md b/README.md new file mode 100644 index 0000000..edf26ba --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# stasis + +A Clojure library of tools for developing static web sites. + +## Install + +Add `[stasis "0.1.0"]` to `:dependencies` in your `project.clj`. + +## Another static site framework? Why? + +I didn't want a static site *framework*, I wanted a *library*. If +you want a framework that makes it really quick and easy to create a +blog, you should take a look at these: + +- https://github.com/liquidz/misaki +- http://algernon.github.io/madness/ +- http://nakkaya.com/static.html +- http://samrat.me/ecstatic/ +- https://github.com/thegeez/clj-static-blog + +They generally come with a folder where you put your blog posts in +some template language, and a set of configuration options about how +to set up your blog. + +This is not that. This is just a set of functions that are pretty +useful when creating static web sites. + +## Usage + + + +## License + +Copyright © 2014 Magnar Sveen + +Distributed under the Eclipse Public License either version 1.0 or (at +your option) any later version. diff --git a/dev/user.clj b/dev/user.clj new file mode 100644 index 0000000..4b01db9 --- /dev/null +++ b/dev/user.clj @@ -0,0 +1,12 @@ +(ns user + (:require [clojure.java.io :as io] + [clojure.string :as str] + [clojure.set :as set] + [clojure.pprint :refer (pprint pp)] + [clojure.repl :refer :all] + [print.foo :refer :all])) + +(defmacro dump-locals [] + `(clojure.pprint/pprint + ~(into {} (map (fn [l] [`'~l l]) (reverse (keys &env)))))) + diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..d949508 --- /dev/null +++ b/project.clj @@ -0,0 +1,10 @@ +(defproject stasis "0.1.0" + :description "A library of tools for creating static websites." + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + :dependencies [[org.clojure/clojure "1.5.1"]] + :profiles {:dev {:dependencies [[midje "1.6.0"] + [print-foo "0.4.2"] + [test-with-files "0.1.0"]] + :plugins [[lein-midje "3.1.3"]] + :source-paths ["dev"]}}) diff --git a/src/stasis/core.clj b/src/stasis/core.clj new file mode 100644 index 0000000..ac5b997 --- /dev/null +++ b/src/stasis/core.clj @@ -0,0 +1,52 @@ +(ns stasis.core + (:require [clojure.java.io :as io])) + +(defn- normalize-uri [#^String uri] + (cond + (.endsWith uri ".html") uri + (.endsWith uri "/") (str uri "index.html") + :else (str uri "/index.html"))) + +(defn- normalize-page-uris [pages] + (zipmap (map normalize-uri (keys pages)) + (vals pages))) + +(defn- serve-page [page request] + {:status 200 + :body (page request) + :headers {"Content-Type" "text/html"}}) + +(def not-found + {:status 404 + :body "

Page not found

" + :headers {"Content-Type" "text/html"}}) + +(defn serve-pages [pages] + (let [pages (normalize-page-uris pages)] + (fn [request] + (let [uri (normalize-uri (:uri request))] + (if-let [page (pages uri)] + (serve-page page (assoc request :uri uri)) + not-found))))) + +(defn- create-folders [path] + (.mkdirs (.getParentFile (io/file path)))) + +(defn export-pages [pages target-dir options] + (doseq [[uri get-page] pages] + (let [uri (normalize-uri uri) + path (str target-dir uri)] + (create-folders path) + (spit path (get-page (assoc options :uri uri)))))) + +(defn- delete-file-recursively [f] + (let [f (io/file f)] + (if (.isDirectory f) + (doseq [child (.listFiles f)] + (delete-file-recursively child))) + (io/delete-file f))) + +(defn delete-directory! [f] + (if (.isDirectory (io/file f)) + (delete-file-recursively f) + (throw (Exception. (str f " is not a directory."))))) diff --git a/test/stasis/core_test.clj b/test/stasis/core_test.clj new file mode 100644 index 0000000..c95c2d4 --- /dev/null +++ b/test/stasis/core_test.clj @@ -0,0 +1,67 @@ +(ns stasis.core-test + (:require [stasis.core :refer :all] + [midje.sweet :refer :all] + [clojure.java.io :as io] + [test-with-files.core :refer [with-tmp-dir tmp-dir]])) + +(fact + "Stasis creates a Ring handler to serve your pages." + + (let [app (serve-pages {"/page.html" (fn [req] "The page contents")})] + + (fact (app {:uri "/page.html"}) + => {:status 200 + :body "The page contents" + :headers {"Content-Type" "text/html"}}) + + (fact (app {:uri "/missing.html"}) + => {:status 404 + :body "

Page not found

" + :headers {"Content-Type" "text/html"}}))) +(fact + "If you use paths without .html, it serves them as directories with + an index.html." + + (let [app (serve-pages {"/page/" (fn [req] (str "I'm serving " (:uri req)))})] + + (fact (:body (app {:uri "/page/index.html"})) => "I'm serving /page/index.html") + (fact (:body (app {:uri "/page/"})) => "I'm serving /page/index.html") + (fact (:body (app {:uri "/page"})) => "I'm serving /page/index.html"))) + +(fact + "Stasis exports pages to your directory of choice." + + (with-tmp-dir + (export-pages {"/page/index.html" (fn [req] "The contents")} + tmp-dir {}) + (slurp (str tmp-dir "/page/index.html")) => "The contents")) + +(fact + "Stasis adds the :uri the 'request' for exported pages too." + + (with-tmp-dir + (export-pages {"/page" (fn [req] (str "I'm serving " (:uri req)))} + tmp-dir {}) + (slurp (str tmp-dir "/page/index.html")) => "I'm serving /page/index.html")) + +(fact + "You can add more information to the 'request' if you want. Like + configuration options, or optimus assets." + + (with-tmp-dir + (export-pages {"/page" (fn [req] (str "I got " (:conf req)))} + tmp-dir {:conf "served"}) + (slurp (str tmp-dir "/page/index.html")) => "I got served")) + +(fact + "It's really easy emptying an entire folder of files. Be careful." + + (with-tmp-dir + (export-pages {"/folder/page.html" (fn [_] "Contents")} tmp-dir {}) + + (io/as-file (str tmp-dir "/folder/page.html")) => #(.exists %) + + (delete-directory! (str tmp-dir "/folder")) + + (io/as-file (str tmp-dir "/folder/page.html")) => #(not (.exists %)) + (io/as-file (str tmp-dir "/folder")) => #(not (.exists %))))