commit e455387f9645e089f2f62d37ea2554dc8d9abf14 Author: Simon Brooke Date: Tue Jul 1 11:35:55 2014 +0100 OK, at this stage it's just an empty LuminusWeb project... diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..a4bcedb --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: lein with-profile production trampoline ring server diff --git a/README.md b/README.md new file mode 100644 index 0000000..329710d --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# mw-ui + +FIXME + +## Prerequisites + +You will need [Leiningen][1] 2.0 or above installed. + +[1]: https://github.com/technomancy/leiningen + +## Running + +To start a web server for the application, run: + + lein ring server + +## License + +Copyright © 2014 FIXME diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..b57ba24 --- /dev/null +++ b/project.clj @@ -0,0 +1,32 @@ +(defproject mw-ui "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :dependencies [[org.clojure/clojure "1.6.0"] + [mw-engine "0.1.0-SNAPSHOT"] + [lib-noir "0.8.4"] + [ring-server "0.3.1"] + [selmer "0.6.8"] + [com.taoensso/timbre "3.2.1"] + [com.taoensso/tower "2.0.2"] + [markdown-clj "0.9.44"] + [environ "0.5.0"] + [noir-exception "0.2.2"]] + + :repl-options {:init-ns mw-ui.repl} + :plugins [[lein-ring "0.8.10"] + [lein-environ "0.5.0"]] + :ring {:handler mw-ui.handler/app + :init mw-ui.handler/init + :destroy mw-ui.handler/destroy} + :profiles + {:uberjar {:aot :all} + :production {:ring {:open-browser? false + :stacktraces? false + :auto-reload? false}} + :dev {:dependencies [[ring-mock "0.1.5"] + [ring/ring-devel "1.3.0"] + [pjstadig/humane-test-output "0.6.0"]] + :injections [(require 'pjstadig.humane-test-output) + (pjstadig.humane-test-output/activate!)] + :env {:dev true}}} + :min-lein-version "2.0.0") \ No newline at end of file diff --git a/resources/public/css/screen.css b/resources/public/css/screen.css new file mode 100644 index 0000000..d759510 --- /dev/null +++ b/resources/public/css/screen.css @@ -0,0 +1,6 @@ +html, +body { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + height: 100%; + padding-top: 40px; +} diff --git a/resources/public/fonts/glyphicons-halflings-regular.eot b/resources/public/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..78b119f Binary files /dev/null and b/resources/public/fonts/glyphicons-halflings-regular.eot differ diff --git a/resources/public/fonts/glyphicons-halflings-regular.svg b/resources/public/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..83f74b2 --- /dev/null +++ b/resources/public/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/public/fonts/glyphicons-halflings-regular.ttf b/resources/public/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..ba41d0f Binary files /dev/null and b/resources/public/fonts/glyphicons-halflings-regular.ttf differ diff --git a/resources/public/fonts/glyphicons-halflings-regular.woff b/resources/public/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..60dd28d Binary files /dev/null and b/resources/public/fonts/glyphicons-halflings-regular.woff differ diff --git a/resources/public/md/docs.md b/resources/public/md/docs.md new file mode 100644 index 0000000..a86e3fe --- /dev/null +++ b/resources/public/md/docs.md @@ -0,0 +1,21 @@ + +### Managing Your Middleware + +Two middleware functions are provided by default in the `mw-ui.middleware` namespace. + +* `log-request` - logs all requests using the debug level +* `template-error-page` - provides friendly formatting for Selmer errors in dev mode + +See the `:middleware` key of the `app` definition located in the `mw-ui.handler` namespace to manage the enabled middleware. + +### Here are some links to get started + +1. [HTML templating](http://www.luminusweb.net/docs/html_templating.md) +2. [Accessing the database](http://www.luminusweb.net/docs/database.md) +3. [Serving static resources](http://www.luminusweb.net/docs/static_resources.md) +4. [Setting response types](http://www.luminusweb.net/docs/responses.md) +5. [Defining routes](http://www.luminusweb.net/docs/routes.md) +6. [Adding middleware](http://www.luminusweb.net/docs/middleware.md) +7. [Sessions and cookies](http://www.luminusweb.net/docs/sessions_cookies.md) +8. [Security](http://www.luminusweb.net/docs/security.md) +9. [Deploying the application](http://www.luminusweb.net/docs/deployment.md) diff --git a/resources/templates/about.html b/resources/templates/about.html new file mode 100644 index 0000000..730fc45 --- /dev/null +++ b/resources/templates/about.html @@ -0,0 +1,4 @@ +{% extends "templates/base.html" %} +{% block content %} +

this is the story of mw-ui... work in progress

+{% endblock %} diff --git a/resources/templates/base.html b/resources/templates/base.html new file mode 100644 index 0000000..6a28d46 --- /dev/null +++ b/resources/templates/base.html @@ -0,0 +1,40 @@ + + + + + Welcome to mw-ui + + + + + +
+ {% block content %} + {% endblock %} +
+ + + + {% style "/css/screen.css" %} + + + + + + + + diff --git a/resources/templates/home.html b/resources/templates/home.html new file mode 100644 index 0000000..013b45d --- /dev/null +++ b/resources/templates/home.html @@ -0,0 +1,14 @@ +{% extends "templates/base.html" %} +{% block content %} +
+

Welcome to mw-ui

+

Time to start building your site!

+

Learn more »

+
+ +
+
+ {{content|safe}} +
+
+{% endblock %} diff --git a/src/mw_ui/handler.clj b/src/mw_ui/handler.clj new file mode 100644 index 0000000..7e614d1 --- /dev/null +++ b/src/mw_ui/handler.clj @@ -0,0 +1,59 @@ +(ns mw-ui.handler + (:require [compojure.core :refer [defroutes]] + [mw-ui.routes.home :refer [home-routes]] + [mw-ui.middleware :refer [load-middleware]] + [noir.response :refer [redirect]] + [noir.util.middleware :refer [app-handler]] + [compojure.route :as route] + [taoensso.timbre :as timbre] + [taoensso.timbre.appenders.rotor :as rotor] + [selmer.parser :as parser] + [environ.core :refer [env]])) + +(defroutes app-routes + (route/resources "/") + (route/not-found "Not Found")) + +(defn init + "init will be called once when + app is deployed as a servlet on + an app server such as Tomcat + put any initialization code here" + [] + (timbre/set-config! + [:appenders :rotor] + {:min-level :info + :enabled? true + :async? false ; should be always false for rotor + :max-message-per-msecs nil + :fn rotor/appender-fn}) + + (timbre/set-config! + [:shared-appender-config :rotor] + {:path "mw_ui.log" :max-size (* 512 1024) :backlog 10}) + + (if (env :dev) (parser/cache-off!)) + (timbre/info "mw-ui started successfully")) + +(defn destroy + "destroy will be called when your application + shuts down, put any clean up code here" + [] + (timbre/info "mw-ui is shutting down...")) + + + +(def app (app-handler + ;; add your application routes here + [home-routes app-routes] + ;; add custom middleware here + :middleware (load-middleware) + ;; timeout sessions after 30 minutes + :session-options {:timeout (* 60 30) + :timeout-response (redirect "/")} + ;; add access rules here + :access-rules [] + ;; serialize/deserialize the following data formats + ;; available formats: + ;; :json :json-kw :yaml :yaml-kw :edn :yaml-in-html + :formats [:json-kw :edn])) diff --git a/src/mw_ui/layout.clj b/src/mw_ui/layout.clj new file mode 100644 index 0000000..6a6c224 --- /dev/null +++ b/src/mw_ui/layout.clj @@ -0,0 +1,24 @@ +(ns mw-ui.layout + (:require [selmer.parser :as parser] + [clojure.string :as s] + [ring.util.response :refer [content-type response]] + [compojure.response :refer [Renderable]])) + +(def template-path "templates/") + +(deftype RenderableTemplate [template params] + Renderable + (render [this request] + (content-type + (->> (assoc params + (keyword (s/replace template #".html" "-selected")) "active" + :servlet-context + (if-let [context (:servlet-context request)] + (.getContextPath context))) + (parser/render-file (str template-path template)) + response) + "text/html; charset=utf-8"))) + +(defn render [template & [params]] + (RenderableTemplate. template params)) + diff --git a/src/mw_ui/middleware.clj b/src/mw_ui/middleware.clj new file mode 100644 index 0000000..3440b32 --- /dev/null +++ b/src/mw_ui/middleware.clj @@ -0,0 +1,24 @@ +(ns mw-ui.middleware + (:require [taoensso.timbre :as timbre] + [selmer.parser :as parser] + [environ.core :refer [env]] + [selmer.middleware :refer [wrap-error-page]] + [noir-exception.core + :refer [wrap-internal-error wrap-exceptions]])) + +(defn log-request [handler] + (fn [req] + (timbre/debug req) + (handler req))) + +(def development-middleware + [log-request + wrap-error-page + wrap-exceptions]) + +(def production-middleware + [#(wrap-internal-error % :log (fn [e] (timbre/error e)))]) + +(defn load-middleware [] + (concat (when (env :dev) development-middleware) + production-middleware)) diff --git a/src/mw_ui/repl.clj b/src/mw_ui/repl.clj new file mode 100644 index 0000000..d05c737 --- /dev/null +++ b/src/mw_ui/repl.clj @@ -0,0 +1,34 @@ +(ns mw-ui.repl + (:use mw-ui.handler + ring.server.standalone + [ring.middleware file-info file])) + +(defonce server (atom nil)) + +(defn get-handler [] + ;; #'app expands to (var app) so that when we reload our code, + ;; the server is forced to re-resolve the symbol in the var + ;; rather than having its own copy. When the root binding + ;; changes, the server picks it up without having to restart. + (-> #'app + ; Makes static assets in $PROJECT_DIR/resources/public/ available. + (wrap-file "resources") + ; Content-Type, Content-Length, and Last Modified headers for files in body + (wrap-file-info))) + +(defn start-server + "used for starting the server in development mode from REPL" + [& [port]] + (let [port (if port (Integer/parseInt port) 3000)] + (reset! server + (serve (get-handler) + {:port port + :init init + :auto-reload? true + :destroy destroy + :join? false})) + (println (str "You can view the site at http://localhost:" port)))) + +(defn stop-server [] + (.stop @server) + (reset! server nil)) diff --git a/src/mw_ui/routes/home.clj b/src/mw_ui/routes/home.clj new file mode 100644 index 0000000..e910032 --- /dev/null +++ b/src/mw_ui/routes/home.clj @@ -0,0 +1,15 @@ +(ns mw-ui.routes.home + (:use compojure.core) + (:require [mw-ui.layout :as layout] + [mw-ui.util :as util])) + +(defn home-page [] + (layout/render + "home.html" {:content (util/md->html "/md/docs.md")})) + +(defn about-page [] + (layout/render "about.html")) + +(defroutes home-routes + (GET "/" [] (home-page)) + (GET "/about" [] (about-page))) diff --git a/src/mw_ui/util.clj b/src/mw_ui/util.clj new file mode 100644 index 0000000..0a41630 --- /dev/null +++ b/src/mw_ui/util.clj @@ -0,0 +1,10 @@ +(ns mw-ui.util + (:require [noir.io :as io] + [markdown.core :as md])) + +(defn md->html + "reads a markdown file from public/md and returns an HTML string" + [filename] + (->> + (io/slurp-resource filename) + (md/md-to-html-string))) diff --git a/test/mw_ui/test/handler.clj b/test/mw_ui/test/handler.clj new file mode 100644 index 0000000..47e4862 --- /dev/null +++ b/test/mw_ui/test/handler.clj @@ -0,0 +1,15 @@ +(ns mw-ui.test.handler + (:use clojure.test + ring.mock.request + mw-ui.handler)) + +(deftest test-app + (testing "main route" + (let [response (app (request :get "/"))] + (is (= (:status response) 200)) + (is (= (:body response) + "\n \n Welcome to mw-ui\n \n \n \n
\n \n
\n
\n

Welcome to mw-ui

\n \n

Some links to get started

  1. HTML templating
  2. Accessing the database
  3. Serving static resources
  4. Setting response types
  5. Defining routes
  6. Adding middleware
  7. Sessions and cookies
  8. Security
  9. Deploying the application
\n\n
\n \n \n\n\n\n")))) + + (testing "not-found route" + (let [response (app (request :get "/invalid"))] + (is (= (:status response) 404)))))