runexec.chp/

https://github.com/runexec/chp/.git

git clone 'https://github.com/runexec/chp/.git'

(ql:quickload :runexec.chp/)
151

CHP endorse

ClojureHomePage is a Clojure Web Framework that provides the following.

Documentation

CHTML, Routing, and Sessions

Ring

Code Generation, Modules, and JSON API

SQL Configuration, Migrations, and Manipulation

General Information

Example CHTML & Routes

Routes can be stored in two places

  1. File: src/chp/handler.clj
  2. 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 ```clojure You have viewed this page

(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))

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.

  1. lib-noir API
  2. lib-noir Github
  3. Ring CSRF protection
  4. Ring Middleware Extensions

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}
  1. Lein-ring documentation

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])))
  1. Lobos Project & Documentation
  2. More Lobos Documentation

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-# 
  1. Lobos Project & Documentation
  2. More Lobos Documentation

Clojure and SQL

ClojureHomePage uses the SQLKorma DSL by default. korma.db is required as kdb and korma.core is required as kc

  1. Korma Documentation

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 SelectChannelConnector@0.0.0.0: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

Actionidname
view edit1user1


More
Connection closed by foreign host.



$ telnet localhost 8000
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /view/user/1

Viewing 1

[name user1][password badcleartext] [admin true]

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”) bash [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 SelectChannelConnector@0.0.0.0: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 SelectChannelConnector@0.0.0.0: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.

  1. lein-cljsbuild Documentation
  2. ClojureScript Documentation

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

user

Data Link: user

Data Returned

Optional Where Params

id
name
id
name
admin

Getting started

1) Download & Run

git clone https://github.com/runexec/chp
cd chp
lein ring server

2) 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.

uml diagram

Namespace Dependencies

(Generated by ntable) https://github.com/runexec/ntable

chp/src/chp/routes/example.clj

:use chp.core compojure.core [chp.html :only [escape]]

chp/src/chp/routes/user.clj

:use chp.core

chp/src/chp/routes/chp.clj

:use chp.core [cheshire.core :only [generate-string]] [chp.api :only [api->where]] [chp.builder :only [binding-exist?]]

chp/src/chp/clean.clj

chp/src/chp/migration.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/password.clj

chp/src/chp/handler.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/html.clj

:require hiccup.core hiccup.util hiccup.form hiccup.element

chp/src/chp/server.clj

chp/src/chp/api.clj

:use [chp.db :only [*db*]] :require [korma.db :as kdb] [korma.core :as kc]

chp/src/chp/package.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/template.clj

:use chp.core

chp/src/chp/builder.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/login.clj

:require [korma.db :as kdb] [korma.core :as kc] :use [chp.db :only [*db*]] [chp.password :only [password]]

chp/src/chp/core.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/generator.clj

:use [chp.core :only [root-path]]

chp/src/chp/module.clj

:require [clojure.java.io :as io]

chp/src/chp/db.clj

chp/src/chp/schema.clj

:refer-clojure :exclude [bigint boolean char double float time] :use [chp.db :only [*db*]] [lobos.core :only [create]]

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.