Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
magnars committed Jan 9, 2014
0 parents commit 3b51abd
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
@@ -0,0 +1,9 @@
/target
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
37 changes: 37 additions & 0 deletions 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.
12 changes: 12 additions & 0 deletions 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))))))

10 changes: 10 additions & 0 deletions 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"]}})
52 changes: 52 additions & 0 deletions 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 "<h1>Page not found</h1>"
: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.")))))
67 changes: 67 additions & 0 deletions 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 "<h1>Page not found</h1>"
: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 %))))

0 comments on commit 3b51abd

Please sign in to comment.