(ns web-app (:use compojure clojure.contrib.monads web-session)) ; some functions to build input fields in HTML (defn input-field [attribs field-name & [initial-value]] [:input (merge attribs {:type "text" :name field-name :value initial-value :id (str field-name "-field")})]) (defn labeled-field [attribs field-name field-label initial-value] [:fieldset (merge {:style "border-style: none; width: 0; float: left; padding: 0"} attribs) [:input {:type "text" :name field-name :value initial-value :id (str field-name "-field")}] [:br] [:label {:for(str field-name "-field")} field-label]]) ; converts the context to HTML (defn show-context [context] (into [:div "context: " [:br]] (interpose [:br] (map (fn [[k v]] (str (name k) ": " v)) context)))) ; generate a form that allows the user to enter a first and last name (web-fn name-page [context request] [(html [:html [:title "Name"] [:body [:form {:method "post"} ; add the session id to the form (hidden-field {} "session-id" (:session-id context)) (print-str "Session Id:" (:session-id context)) [:br] ; generate an id for the screen and add it to the form (let [screen-id (new-id)] (html (hidden-field {} "screen-id" screen-id) (print-str "Screen Id:" screen-id))) [:br] [:br] [:fieldset {:style "border-style: none; padding: 0"} (labeled-field {} "first-name" "First Name" (:first-name context)) (labeled-field {} "last-name" "Last Name" (:last-name context))] [:br] [:br] (show-context context) [:br] [:br] [:input {:type "submit"}]] ]]) context]) ; extract the first and last name from the request and add them to the context (web-fn save-name [context request] [nil (assoc context :first-name (.trim (:first-name (:params request))) :last-name (.trim (:last-name (:params request))))]) ; verify that the context contains a valid first and last name (defn name-valid? [context request] (and (not= "" (:first-name context)) (not= "" (:last-name context)))) ; generate a form that allows the user to enter their age (web-fn age-page [context request] [(html [:html [:title "Age"] [:body [:form {:method "post"} (hidden-field {} "session-id" (:session-id context)) (print-str "Session Id:" (:session-id context)) [:br] (let [screen-id (new-id)] (html (hidden-field {} "screen-id" screen-id) (print-str "Screen Id:" screen-id))) [:br] [:br] [:fieldset {:style "border-style: none; padding: 0"} (labeled-field {} "age" "Age" (:age context))] [:br] [:br] (show-context context) [:br] [:br] [:input {:type "submit"}]] ]]) context]) ; extract the age from the request and add it to the context (web-fn save-age [context request] (if (nil? (:age (:params request))) [nil context] [nil (assoc context :age (try (new Integer (:age (:params request))) (catch Exception e nil)))])) ; verify that the context contains a valid age (defn age-valid? [context request] (and (number? (:age context)) (> (:age context) 15) (< (:age context) 100))) ; generate a form that allows the user to select their gender (web-fn gender-page [context request] [(html [:html [:title "Gender"] [:body [:form {:method "post"} (hidden-field {} "session-id" (:session-id context)) (print-str "Session Id:" (:session-id context)) [:br] (let [screen-id (new-id)] (html (hidden-field {} "screen-id" screen-id) (print-str "Screen Id:" screen-id))) [:br] [:br] "Gender:" [:fieldset {:style "border-style: none; padding: 0"} [:input {:type "radio" :name "gender" :value "Male" :checked "checked"} "Male"] [:br] [:input {:type "radio" :name "gender" :value "Female"} "Female"]] [:br] [:br] (show-context context) [:br] [:br] [:input {:type "submit"}]]]]) context]) ; generate a page that displays the context (web-fn show-summary [context request] [(html [:html [:title "Summary"] [:body (print-str "Session Id:" (:session-id context)) [:br] [:br] (show-context context) [:br] [:br] [:a {:href "/app-url"} "Do it again!"]]]) context]) ; extract the gender from the request and add it to the context (web-fn save-gender [context request] [nil (assoc context :gender (:gender (:params request)))]) ; predicate to test for male users (defn male? [context request] (= "Male" (:gender context))) ; web fn that does nothing (web-fn male-options [context request] [nil context]) ; web fn that does nothing (web-fn female-options [context request] [nil context]) ; create a web function that displays the name form and adds the name ; to the context until the user has entered a valid name (def get-name (web-until name-valid? (web-seq name-page save-name))) ; create a web function that displays the age form and adds the age ; to the context until the user has entered a valid age (def get-age (web-until age-valid? (web-seq age-page save-age))) ; create a web function that displays the gender form and adds the gender ; to the context (def get-gender (web-seq gender-page save-gender)) ; define the top level web app and the sequence the pages are presented to the user (def web-app (web-while (constantly true) (web-seq get-name get-age get-gender (web-cond male? male-options :else female-options) show-summary))) ; define the route handlers for compojure (defroutes new-file-routes (ANY "/app-url" (handle-request request web-app))) ; define the server (defserver serv-app {:port 8080} "/app-url/*" (servlet new-file-routes)) ; clear all the sessions and start the server (dosync (ref-set sessions {})) (start serv-app)