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
CHTML, Routing, and Sessions
Ring
Code Generation, Modules, and JSON API
- 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
SQL Configuration, Migrations, and Manipulation
General Information
- 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. test-page.chtml
More CHTML examples are located in chp-root
Routes Example
(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
Perform migration
$ lein migrate
add-topic-table
add-topic-subject-table
add-tag-table
Lobos migration files
$ 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-table
(up [] (create
(tbl :tag
(varchar :title 25)
(integer :id :auto-inc :primary-key))))
(down [] (drop (table :tag))))
Tables after migration
example=# \dt
List of relations
Schema | Name | Type | Owner
--------+------------------+-------+-------
public | example | table | on
public | lobos_migrations | table | on
public | tag | table | on
public | topic | table | on
public | topicSubject | table | on
public | user | table | on
(6 rows)
Rollbacks
$ lein rollback
add-tag-table
$ lein rollback
add-topic-subject-table
Tables after rollback
example-# \dt
List of relations
Schema | Name | Type | Owner
--------+------------------+-------+-------
public | example | table | on
public | lobos_migrations | table | on
public | topic | table | on
public | user | table | on
(4 rows)
example-#
Clojure and SQL
ClojureHomePage uses the SQLKorma DSL by default. korma.db is required as kdb and korma.core is required as kc
Get column syntax example
The $cljdb macro binds a row and allows the $db macro to retrieve columns of the row. Example, ($cljdb table-keyword where-clause & code-body)
Here's how to use these macros
chp.test.core> ($cljdb :user {:id 2}
(format "%s id is %d"
($db name)
($db id)))
"admin id is 2"
chp.test.core> (kc/select {:table "user"} (kc/fields :name :id))
[{:id 2, :name "admin"}]
Generating Table Views
$ lein gen user
resources/generation-templates/routes/name.clj -> src/chp/routes/user.clj
resources/generation-templates/chtml/new.chtml -> chp-root/user/new.chtml
resources/generation-templates/chtml/edit.chtml -> chp-root/user/edit.chtml
resources/generation-templates/chtml/view.chtml -> chp-root/user/view.chtml
resources/generation-templates/chtml/list.chtml -> chp-root/user/list.chtml
URL DATA BOUND TO => resources/bindings/user.clj
site.com/new/user
site.com/list/user
site.com/edit/user/:id
site.com/view/user/:id
$ cat resources/bindings/user.clj
;; Example bindings for resources/schema/user.clj
;; All values will be retrieved by the id column
;; table must match the filename withut the clj extension
;; user.clj -> user
{:table :user
;; List view value
;; (chp.builder/binding-list :user 0 10)
;; /chp/list/user
:list (list :name :id)
;; View view values
;; (chp.builder/binding->view :user 1)
;; site.com/chp/view/user/:id
:view (list :name :password :admin)
;; Edit view values
;; (chp.builder/binding->edit :user 1)
;; site.com/chp/edit/user/:id
;; site.com/chp/new/user
;; edit is a hash-set with table columns
;; as the key and the chp.html namespace
;; function used to display the value.
:edit {:name #(text-field :name (escape %))
:password #(password-field :password (escape %))
:admin #(check-box :admin (Boolean/valueOf %))}
;; enforce data type with fn to check and
;; or convert before going into database.
;; The function must take one arg.
;; :name is limited to a string of 20 chars
;; :password is limited to 100 chars
;; :admin mut be a boolean value
:edit-enforce {:name #(->> % str seq (take 20) (apply str))
:password #(->> % str seq (take 100) (apply str))
:admin #(Boolean/valueOf %)}}
Builder Bindings
The example user.clj bindings below will be used to make the new, list, view, and edit pages of the user table in schema/user.clj.
;; Example bindings for resources/schema/user.clj
;; All values will be retrieved by the id column
;; table must match the filename withut the clj extension
;; user.clj -> user
{:table :user
;; List view value
;; (chp.builder/binding-list :user 0 10)
;; /chp/list/user
:list (list :name :id)
;; View view values
;; (chp.builder/binding->view :user 1)
;; site.com/chp/view/user/:id
:view (list :name :password :admin)
;; Edit view values
;; (chp.builder/binding->edit :user 1)
;; site.com/chp/edit/user/:id
;; site.com/chp/new/user
;; edit is a hash-set with table columns
;; as the key and the chp.html namespace
;; function used to display the value.
:edit {:name #(text-field :name (escape %))
:password #(password-field :password (escape %))
:admin #(check-box :admin (Boolean/valueOf %))}
;; enforce data type with fn to check and
;; or convert before going into database.
;; The function must take one arg.
:edit-enforce {:name str
:password str
:admin #(Boolean/valueOf %)}}
Builder binding views example
$ cd chp
$ cat resources/schema/user.clj
(create *db*
(table :user
(integer :id :primary-key :auto-inc)
(varchar :name 20)
(varchar :password 100)
(boolean :admin)
(unique [:name])))
$ lein schema
Creating Table => resources/schema/example.clj
OKAY
Creating Table => resources/schema/user.clj
OKAY
$ lein gen user
resources/generation-templates/routes/name.clj -> src/chp/routes/user.clj
resources/generation-templates/chtml/new.chtml -> chp-root/user/new.chtml
resources/generation-templates/chtml/edit.chtml -> chp-root/user/edit.chtml
resources/generation-templates/chtml/view.chtml -> chp-root/user/view.chtml
resources/generation-templates/chtml/list.chtml -> chp-root/user/list.chtml
URL DATA BOUND TO => resources/bindings/user.clj
site.com/new/user
site.com/list/user
site.com/edit/user/:id
site.com/view/user/:id
$ psql example
psql (9.2.4)
Type "help" for help.
example=# INSERT INTO "user" (name,password,admin) VALUES ('user1','badcleartext',true);
example=# \q
$ lein ring server &
Jun 24, 2013 3:40:47 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
2013-06-24 15:40:47.999:INFO:oejs.Server:jetty-7.6.1.v20120215
2013-06-24 15:40:48.064:INFO:oejs.AbstractConnector:Started [email protected]:8000
Started server on port 8000
# In another terminal ###
$ telnet localhost 8000
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /list/user
Viewing table of user ```bash Connection closed by foreign host.
$ telnet localhost 8000 Trying ::1... Connected to localhost. Escape character is '^]'. GET /view/user/1
<h1>Viewing 1
</h1>
[name user1]
[password badcleartext]
[admin true]
```bash
Connection closed by foreign host.
$ telnet localhost 8000
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /edit/user/1
<h1> Editing user #1 </h1>
<form action="/edit/user/1
" method="POST">
<label for=":admin">admin</label><br /><input checked="checked" id="admin" name="admin" type="checkbox" value="true" /><br /><br /><label for=":password">password</label><br /><input id="password" name="password" type="password" value="badcleartext" /><br /><br /><label for=":name">name</label><br /><input id="name" name="name" type="text" value="user1" /><br /><br /> <input type="submit" value="save" />
</form>
Connection closed by foreign host.
Enable Admin Account
[user@machine ~]$ cd /tmp/; mkdir admin-example; cd admin-example;
[user@machine admin-example]$ git clone https://github.com/runexec/chp.git
Cloning into 'chp'...
remote: Counting objects: 594, done.
remote: Compressing objects: 100% (331/331), done.
remote: Total 594 (delta 287), reused 501 (delta 194)
Receiving objects: 100% (594/594), 160.96 KiB | 34.00 KiB/s, done.
Resolving deltas: 100% (287/287), done.
[user@machine admin-example]$ cd chp/
[user@machine chp]$ ls
chp-examples/ chp-root/ resources/ src/ test/ tutorial/ project.clj README.md
[user@machine chp]$ rm -rf chp-examples/ tutorial/
[user@machine chp]$ cat resources/schema/user.clj
(create *db*
(table :user
(integer :id :primary-key :auto-inc)
(varchar :name 20)
(varchar :password 128)
(varchar :salt 128)
(boolean :admin)
(unique [:name])))
[user@machine chp]$ lein schema
Creating Table => resources/schema/user.clj
OKAY
Creating Table => resources/schema/example.clj
OKAY
[user@machine chp]$ lein migrate
Jun 30, 2013 2:47:07 AM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
create-chp-admin
The admin password is located in the migration file resources/migrations/01-add-admin.clj
;; (chp.password/salt-set "your password here")
[user@machine chp]$ cat resources/migrations/01-add-admin.clj
(let [table (kc/create-entity "user")]
(comment Assuming resources/schema/user.clj
(table :user
(integer :id :primary-key :auto-inc)
(varchar :name 20)
(varchar :password 128)
(varchar :salt 128)
(boolean :admin)
(unique [:name])))
(defmigration create-chp-admin
(up []
(let [{:keys [salt
password]} (chp.password/salt-set "admin")]
(kc/insert table
(kc/values
{:name "admin"
:password password
:salt salt
:admin true}))))
(down []
(kc/delete table
(kc/where {:name "admin"})))))
[user@machine chp]$ lein ring server
Jun 30, 2013 2:54:20 AM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
2013-06-30 02:54:21.391:INFO:oejs.Server:jetty-7.6.1.v20120215
2013-06-30 02:54:21.489:INFO:oejs.AbstractConnector:Started [email protected]:8000
Started server on port 8000
### In a new shell ###
[user@machine chp]$ curl --data "username=admin&password=admin" http://localhost:8000/chp/login
<html>
<head><title>Login Handler</title></head>
<body>
Logged in{:user "admin", :logged-in? true}
</body>
</html>
[user@machine chp]$ curl --data "username=admin&password=adminadasdsad" http://localhost:8000/chp/login
<html>
<head><title>Login Handler</title></head>
<body>
Login Failed
</body>
</html>
Basic bindings example
##### get CHP
[user@machine ~]$ mkdir blog; cd blog/
[user@machine blog]$ git clone https://github.com/runexec/chp.git
Cloning into 'chp'...
remote: Counting objects: 456, done.
remote: Compressing objects: 100% (224/224), done.
remote: Total 456 (delta 208), reused 413 (delta 165)
Receiving objects: 100% (456/456), 144.89 KiB | 80.00 KiB/s, done.
Resolving deltas: 100% (208/208), done.
[user@machine blog]$ cd chp/; ls
chp-examples/ chp-root/ resources/ src/ test/ tutorial/ project.clj README.md
##### Remove extra directories
[user@machine chp]$ rm -rf chp-examples/ tutorial/
##### Create Database configuration and db
[user@machine chp]$ cd resources/config/
[user@machine config]$ emacs -nw -q db.clj
[user@machine config]$ psql example -c 'CREATE DATABASE "blog";'
CREATE DATABASE
##### Create tables
[user@machine config]$ cd ../schema/
[user@machine schema]$ cat user.clj
(create *db*
(table :user
(integer :id :primary-key :auto-inc)
(varchar :name 20)
(varchar :password 128)
(varchar :salt 128)
(boolean :admin)
(unique [:name])))
[user@machine schema]$ emacs -nw news.clj
[user@machine schema]$ cat news.clj
(create *db*
(table :news
(integer :id :primary-key :auto-inc)
(integer :userid)
(varchar :title 100)
(text :body)
(unique [:title])))
[user@machine schema]$ lein schema
Creating Table => resources/schema/user.clj
OKAY
Creating Table => resources/schema/news.clj
OKAY
##### Create binding
[user@machine bindings]$ emacs -nw news.clj
[user@machine bindings]$ cat news.clj
{:table :news
:list (list :title :id)
:view (list :title :body)
:edit {:title #(text-field :title (escape %))
:body #(text-area :body (escape %))}
:edit-enforce {:title #(->> % str seq (take 100) (apply str))
:body str}}
##### Create Views
[user@machine bindings]$ lein gen news
Jul 14, 2013 9:59:55 AM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
resources/generation-templates/routes/name.clj -> src/chp/routes/news.clj
resources/generation-templates/chtml/new.chtml -> chp-root/news/new.chtml
resources/generation-templates/chtml/edit.chtml -> chp-root/news/edit.chtml
resources/generation-templates/chtml/view.chtml -> chp-root/news/view.chtml
resources/generation-templates/chtml/list.chtml -> chp-root/news/list.chtml
URL DATA BOUND TO => resources/bindings/news.clj
site.com/new/news
site.com/list/news
site.com/edit/news/:id
site.com/view/news/:id
##### Add Routes to chp/src/chp/handler.clj
(:require [chp.routes.news :refer [news-table-routes]])
(def app
(chp-site news-table-routes
example-routes
user-table-routes
chp-builder-paths
app-routes))
[user@machine bindings]$ lein ring server
Jul 06, 2013 9:29:46 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
2013-07-06 21:29:47.735:INFO:oejs.Server:jetty-7.6.1.v20120215
2013-07-06 21:29:48.834:INFO:oejs.AbstractConnector:Started [email protected]:8000
Started server on port 8000
##### Create new blog post
[user@machine bindings]$ firefox http://localhost:8000/new/news
Clojure and HTML Generation
The following methods presented in the documentation below are accessible from within CHTML files by default. These abstractions are drop-in replacements for the Hiccup API located at http://weavejester.github.io/hiccup/. Please note that these forms DO NOT generate Hiccup code, but HTML.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Form Fields
(escape string)
(url-encode string)
(check-box attr-map? name)
(check-box attr-map? name checked?)
(check-box attr-map? name checked? value)
(drop-down attr-map? name options)
(drop-down attr-map? name options selected)
(email-field attr-map? name)
(email-field attr-map? name value)
(file-upload attr-map? name)
(form-to attr-map? [method action] & body)
(hidden-field attr-map? name)
(hidden-field attr-map? name value)
(label attr-map? name text)
(password-field attr-map? name)
(password-field attr-map? name value)
(radio-button attr-map? group)
(radio-button attr-map? group checked?)
(radio-button attr-map? group checked? value)
(reset-button attr-map? text)
(select-options attr-map? coll)
(select-options attr-map? coll selected)
(submit-button attr-map? text)
(text-area attr-map? name)
(text-area attr-map? name value)
(text-field attr-map? name)
(text-field attr-map? name value)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Common Elements
(javascript-tag script)
(image attr-map? src)
(image attr-map? src alt)
(link-to attr-map? url & content)
(mail-to attr-map? e-mail & [content])
(ordered-list attr-map? coll)
(unordered-list attr-map? coll)
Clojure and CSS Generation
ClojureHomePage uses the Garden CSS generation library by default. The Garden documentation page is located at https://github.com/noprompt/garden
Clojure and JavaScript Generation
ClojureHomePage uses ClojureScript and lein-cljsbuild to generate javascript. CHP uses the directory resources/cljs/ as the default cljs source code directory.
Modules
Modules are optional resources that are shipped with every release.
$ lein mod-enable middleware verbose-log example
Copying resources/modules/middleware/verbose-log.clj -> resources/middleware/verbose-log.clj
Copying resources/modules/middleware/example.clj -> resources/middleware/example.clj
$ lein mod-disable middleware verbose-log
Deleting resources/middleware/verbose-log.clj
$ lein mod-list
resources/modules
resources/modules/migrations
resources/modules/migrations/01-add-admin.clj
resources/modules/cljs
resources/modules/middleware
resources/modules/middleware/example.clj
resources/modules/middleware/operating-status.clj
resources/modules/middleware/text-mime-only.clj
resources/modules/middleware/verbose-log.clj
resources/modules/schema
resources/modules/schema/example.clj
resources/modules/schema/user.clj
resources/modules/schema/blog-post.clj
resources/modules/bindings
resources/modules/bindings/user.clj
Module Packages
Module packages are stored in resources/packages/
. Each package is a clj file that describes what modules to install when calling lein package-run name1 name2 etc...
$ cat resources/packages/admin.clj
;; CHP Admin module
;; {:module-type [:module :module :module etc..]}
{:schema [:user] ;; resources/modules/schema/user.clj
:migrations [:01-add-admin] ;; resources/modules/migrations/01-add-admin.clj
:bindings [:user] ;; resources/modules/bindings/user.clj
:middleware [] ;; resources/modules/middleware/ -- nothing
:cljs [] ;; resources/modules/cljs/ -- nothing
:api []} ;; resources/modules/api/ -- nothing
$ lein package-run admin
Loading for :schema
Copying resources/modules/schema/user.clj -> resources/schema/user.clj
Creating Table => resources/schema/user.clj
OKAY
Loading for :migrations
Copying resources/modules/migrations/01-add-admin.clj -> resources/migrations/01-add-admin.clj
Jul 09, 2013 7:52:37 PM com.mchange.v2.log.MLog <clinit>
INFO: MLog clients using java 1.4+ standard logging.
create-chp-admin
Loading for :bindings
Copying resources/modules/bindings/user.clj -> resources/bindings/user.clj
Loading for :middleware
Loading for :cljs
Loading for :api
JSON API
To enable the JSON read-only API, create an API config file in resources/api/. Once this configuration file is made, you can access JSON values @ site.com/chp/api/{table-name}
GET params can be used to locate specific values. If the params aren't listed in the configuration, the extra params are ignored.
$ cat resources/api/user.clj
;; API settings for the User table
;; the filename must be the same as the table name
;; {:table :user} => user.clj
{:table :user
:return [:id :name]
;; The where key holds a map that describes
;; columns that can be used to locate the
;; data.
;; Each column key needs to have a function
;; as the value that accepts one arg. This
;; function needs to convert the arg to the
;; proper data type.
;; The single arg is a string from the uri
:where {:id #(Integer. %)
:name str
:admin #(Boolean. %)}}
$ curl http://localhost:8000/chp/api/user; echo " << done"
{"data": [{
"name" : "admin",
"id" : 1
},{
"name" : "example",
"id" : 3
}]} << done
$ curl http://localhost:8000/chp/api/user?id=3; echo " << done"
{"data": [{
"name" : "example",
"id" : 3
}]} << done
$ curl http://localhost:8000/chp/api/user?name=admin; echo " << done"
{"data": [{
"name" : "admin",
"id" : 1
}]} << done
$ curl http://localhost:8000/chp/api/userasdasd; echo " << done"
An error occured << done
JSON API Documentation Page
After creating an API config file in resources/api/
, you can view the dynamically generated API documentation at site.com/chp/api
.
Before showing the documentation page, here's what the default user API config looks like.
$ cat resources/api/user.clj
;; API settings for the User table
;; the filename must be the same as the table name
;; {:table :user} => user.clj
{:table :user
:return [:id :name]
;; The where key holds a map that describes
;; columns that can be used to locate the
;; data.
;; Each column key needs to have a function
;; as the value that accepts one arg. This
;; function needs to convert the arg to the
;; proper data type.
;; The single arg is a string from the uri
:where {:id #(Integer. %)
:name str
:admin #(Boolean. %)}}
And here's what the documentation looks like for all config files in resources/api/
$ curl localhost:8000/chp/api
Available API
Data Link: user user
Data Returned | Optional Where Params |
id name | id name admin |
Getting started
- Download & Run
git clone https://github.com/runexec/chp
cd chp
lein ring server
- Edit the default app-routes template located at the bottom of src/chp/handler.clj
UML
Full image size at https://raw.github.com/runexec/chp/master/violet-uml-workings.png
(2013-07-08 JST) NOTICE: This graph is not consistent with the latest update(s) and contains minor errors.
Namespace Dependencies
(Generated by ntable) https://github.com/runexec/ntable
:use chp.core compojure.core [chp.html :only [escape]] chp/src/chp/routes/example.clj
:use chp.core chp/src/chp/routes/user.clj
:use chp.core [cheshire.core :only [generate-string]] [chp.api :only [api->where]] [chp.builder :only [binding-exist?]] chp/src/chp/routes/chp.clj
chp/src/chp/clean.clj
:refer-clojure :exclude [complement alter drop bigint boolean char double float time] :use [chp.db :only [*db*]] (lobos core connectivity migration) chp/src/chp/migration.clj
chp/src/chp/password.clj
:use compojure.core chp.html chp.template [chp.core :exclude [korma-db]] [chp.api :only [api->where api-dir]] [chp.db :only [*db*]] [garden.core :only [css]] :require chp.server [compojure.route :as route] [korma.db :as kdb] [korma.core :as kc] [noir.session :as session] [chp.routes.chp :refer [chp-builder-paths]] [chp.routes.example :refer [example-routes]] [chp.routes.user :refer [user-table-routes]] chp/src/chp/handler.clj
:require hiccup.core hiccup.util hiccup.form hiccup.element chp/src/chp/html.clj
chp/src/chp/server.clj
:use [chp.db :only [*db*]] :require [korma.db :as kdb] [korma.core :as kc] chp/src/chp/api.clj
:refer-clojure :exclude [bigint boolean char double float time] :use [chp.schema :only [load-schemas]] [chp.migration :only [chp-migrate]] [chp.module :only [mod-enable]] chp/src/chp/package.clj
:use chp.core chp/src/chp/template.clj
:use [chp.db :only [*db*]] [chp.core :exclude [korma-db]] chp.html chp.password [chp.login :exclude [korma-db]] :require [korma.core :as kc] [korma.db :as kdb] [noir.session :as session] chp/src/chp/builder.clj
:require [korma.db :as kdb] [korma.core :as kc] :use [chp.db :only [*db*]] [chp.password :only [password]] chp/src/chp/login.clj
:use compojure.core [noir.session :only [wrap-noir-flash wrap-noir-session]] [chp.db :only [*db*]] :require chp.server [compojure.handler :as handler] [clojure.string :as string] [korma.db :as kdb] [korma.core :as kc] chp/src/chp/core.clj
:use [chp.core :only [root-path]] chp/src/chp/generator.clj
:require [clojure.java.io :as io] chp/src/chp/module.clj
chp/src/chp/db.clj
:refer-clojure :exclude [bigint boolean char double float time] :use [chp.db :only [*db*]] [lobos.core :only [create]] chp/src/chp/schema.clj
How?
By default, the CHTML files are located in chp-root folder of the project folder.
When a CHTML file is parsed, all public variables of the chp.handler namespace
are accessible during the evaluation of the <clj></clj>
tags. Use print
or println within the tags to have the results displayed.
Removing Example Files
$ ls
chp-examples/ chp-root/ resources/ src/ target/ test/ tutorial/ project.clj README.md
$ lein chp-clean
Removing README.md
Removing resources/schema
Removing resources/schema/example.clj
Removing resources/schema/user.clj
Removing resources/bindings
Removing resources/bindings/user.clj
Removing resources/migrations
Removing resources/migrations/02-add-tag-table.clj
Removing resources/migrations/01-add-topic-tables.clj
$ ls
chp-root/ resources/ src/ target/ test/ project.clj
$ ls resources/
bindings/ cljs/ config/ generation-templates/ migrations/ public/ schema/
License
Copyright © 2013 Runexec
Distributed under the Eclipse Public License, the same as Clojure.