Chp
ClojureHomePage is a Compojure based web framework that allows you to write the backend and frontend with Clojure.
Install / Use
/learn @runexec/ChpREADME
CHP 
ClojureHomePage is a Clojure Web Framework that provides the following.
- Run Clojure inside a HTML file with the
<clj></clj>tags - Request params ex. ($p userid)
- Common web headers ex. ($ user-agent)
- Web Headers ex. ($$ cache-control)
- Environmental variables ex. (env java.vm.name)
- Have multiple method handlers under a single route (get, post, put, delete, and head)
- Routes can be defined in seperate files and namespaces
- Style templates can be written in CHTML ex. chp.template/using-template
- Create SQL database schemas ex. lein schema
- Perform SQL database migrations ex. lein migrate
- Perform migration rollbacks ex. lein rollback
- Manipulate SQL databases with KormaSQL
- Generate Page views (new,view,edit,list)
- Generate JavaScript / ECMAScript
- Generate HTML
- Generate CSS
Documentation
<b> CHTML, Routing, and Sessions </b>
<b> Ring </b>
<b> Code Generation, Modules, and JSON API </b>
- Generating views from a table
- View bindings
- View bindings Example
- Enable admin account
- Database and Bindings tutorial
- HTML Generation
- CSS Generation
- JavaScript Generation
- CHP Modules
- Module Packages
- JSON API
- Auto Documenting API
<b> SQL Configuration, Migrations, and Manipulation </b>
<b> General Information </b>
- Install
- UML Relationships
- Namespace Dependencies
- Unit Tests
- Removing example files
- License
- How?
- Tutorial
Example CHTML & Routes
Routes can be stored in two places
- File: src/chp/handler.clj
- Folder: src/chp/routes/
The following link is the chtml page that is used in the example below. <a href="https://github.com/runexec/chp/blob/master/chp-root/test-page.chtml"> test-page.chtml </a>
More CHTML examples are located in <a href="https://github.com/runexec/chp/blob/master/chp-root/">chp-root</a>
<b> Routes Example </b>
(defchp app-routes
;; Load CHP File
(chp-route "/chtml"
(binding [*title* "Test Page Example"]
(or (root-parse "test-page.chtml")
"error")))
(chp-route "/chp"
;; root-parse = root-path "/" file
(or (root-parse "chp-info.chtml")
"error"))
(chp-route "/session"
(or (root-parse "session-example.chtml")
"error"))
;; Named params
(chp-route "/index/:id"
(format "ID is %s"
(escape ($p id))))
(chp-route "/index/:id/:action"
(format "Action is %s"
(escape ($p action))))
;; Multiple handlers under a single route
(chp-route "/testing"
(or
(chp-when :post "POST METHOD RETURN")
(chp-when :get
(str (format "chp-body wasn't used to access %s from %s with %s"
($ uri) ($ ip) ($ user-agent))
(format "<p>Tracking you? DNT HTTP Header = %s</p>" ($$ dnt))
(format "<p>HTTP Header cache-control = %s</p>" ($$ cache-control))))
"Not Found"))
;; Multiple handlers under a single route
(chp-route "/"
(let [display (str (format "Method %s <br />" (escape ($ method)))
(format "URI %s <br />" (escape ($ uri)))
(format "Params %s <br />" (escape ($ params)))
(format "Header Values <p>%s</p>"
(with-out-str
(doseq [[k v] (escape-map ($ headers))]
(println k "=" v "<br />"))))
(format "Server Name %s <br /> Server IP %s"
($ server-name)
($ server-ip)))]
(chp-body {:-get (str "Get => " display)
:-post (str "Post => " display)
:-not-found "Sorry, but this page doesn't exist"})))
;; Bind to templates
(chp-route "/template"
(using-template "example.chtml"
{:body "chp-info.chtml"
:test-tag "test-page.chtml"}))
(route/resources "/")
(route/not-found "Not Found"))
(def app
(chp-site example-routes
app-routes))
Session handling, Cookies, and Compojure
Sessions are handled with the lib-noir.session namespace under the session alias.
This session example can be accessed at site.com/session
You have viewed this page
<clj>
(let [k :view-count
inc-view (if (nil? (session/get k))
(k (session/put! k 1))
(k (session/update-in! [k] inc)))]
(print inc-view))
</clj>
time(s).
Because CHP is based on Compojure, you can use Compojure and Ring extensions. These middleware extensions should be added to the chp-routing function of the chp.core namespace. Below is what the function currently looks like.
(defn chp-routing [& -chp-routes]
;;; (-> (apply routes ...) middleware-wrap xyz-wrap)
(let [auto-middleware (fn [x]
(let [wrapped (atom x)]
(doseq [m (load-middleware)]
(swap! wrapped m))
@wrapped))]
(-> (apply routes
(reduce into [] -chp-routes))
wrap-noir-flash
wrap-noir-session
auto-middleware)))
Already included, but not loaded by default (except noir.session), the lib-noir library is a great helper library for Clojure web development.
Ring configuration
The default configuration for CHP is located in project.clj
:ring {:port 8000
:auto-reload? true
:auto-refresh? true
:reload-paths ["src/chp/"
"chp-root/"
"resources/middleware/"
"resources/public/"]
:handler chp.handler/app}
Auto-loading Middleware
Middleware is automatically loaded from '''resources/middleware/*.clj''' when the server starts. The middleware is evaluated in the chp.core namespace with the load-middleware fn. All middleware is reloaded when triggering the ring-autoload.
$ cat resources/middleware/example.clj
;; This file is automatically loaded as middleware
;; and should only contain one function.
(defn example-middleware [handler]
(fn [request]
(let [resp (handler request)
headers (:headers resp)]
(println "resources/middleware/example.clj says "
"- Incoming request >> " headers)
resp)))
DB Configuration and Creation
A Korma SQL and Lobos compatible SQL connection configuration file is located at resources/config/db.clj
The SQL database tables are located in resources/schema/. These files can contain an unlimited amount of create calls and get evaluated by the lein alias lein schema
$ lein schema
Creating Table => resources/schema/example.clj
OKAY
Creating Table => resources/schema/user.clj
OKAY
The Lobos library handles the table syntax. Below is the user table from user.clj.
(create *db*
(table :user
(integer :id :primary-key :auto-inc)
(varchar :name 20)
(varchar :password 100)
(unique [:name])))
(create *db*
(table :some_other_table
(integer :id :primary-key :auto-inc)
(varchar :name 20)
(varchar :password 100)
(unique [:name])))
DB Migrations
<b> Perform migration </b>
$ lein migrate
add-topic-table
add-topic-subject-table
add-tag-table
<b> Lobos migration files </b>
$ cat resources/migrations/01-add-topic-tables.clj
(defmigration add-topic-table
(up [] (create
(tbl :topic
(varchar :title 50 :unique)
(text :content))))
(down [] (drop (table :topic))))
(defmigration add-topic-subject-table
(up [] (create
(tbl :topicSubject
(varchar :title 50 :unique)
(integer :id :auto-inc :primary-key))))
(down [] (drop (table :topicSubject))))
$ cat resources/migrations/02-add-tag-table.clj
(defmigration add-tag-t
