diff --git a/src/wanijo/attribute/routes.clj b/src/wanijo/attribute/routes.clj index 0a970a2..53e2aac 100644 --- a/src/wanijo/attribute/routes.clj +++ b/src/wanijo/attribute/routes.clj @@ -1,7 +1,7 @@ (ns wanijo.attribute.routes (:require [compojure.core :refer [defroutes GET POST DELETE]] [ring.util.response :as resp] - [wanijo.framework.form :as form] + [formulare.core :as form] [wanijo.framework.routing :refer [register! path]] [wanijo.attribute.domain :as domain] [wanijo.schema.view :as view-schema] diff --git a/src/wanijo/framework/form.clj b/src/wanijo/framework/form.clj deleted file mode 100644 index 5be6e88..0000000 --- a/src/wanijo/framework/form.clj +++ /dev/null @@ -1,169 +0,0 @@ -(ns wanijo.framework.form - (:require [clojure.spec.alpha :as spec] - [hiccup.form :as hform] - [hiccup.core :as hcore] - [wanijo.framework.view :as view] - [ring.util.anti-forgery :refer [anti-forgery-field]] - [wanijo.framework.common :refer [in?]])) - -(spec/def ::label string?) -(spec/def ::required boolean?) -(spec/def ::spec keyword?) -(spec/def ::options - (spec/or :empty empty? - :options (spec/coll-of (spec/tuple string? string?)))) -(spec/def ::widget - #(in? [:input :select :checkbox :textarea :mselect] %)) -(spec/def ::from-req - (spec/and fn? #(= 1 (->> % meta :arglists (map count) first)))) -(spec/def ::to-form ::from-req) -(spec/def ::field - (spec/keys :req-un [::label] - :opt-un [::options - ::widget - ::from-req - ::to-form - ::required - ::spec])) -(spec/def ::fields - (spec/map-of keyword? ::field)) -(spec/def ::form - (spec/keys :req-un [::fields])) - -(defn spec-to-errmsg [label spec-key field-value] - (view/flash-error - (map - (fn [prob] - [:p - "Field " - [:span.flash__field label] - " must comply to " - [:span.flash__pred (:pred prob)]]) - (:clojure.spec.alpha/problems - (spec/explain-data spec-key field-value))))) - -(defn field-valid?[value spec-key req] - (or (empty? (:form-params req)) - (spec/valid? spec-key value))) - -(defn field - ([form-def field-id req] - (field form-def field-id req - (get-in req [:params field-id]))) - ([form-def field-id req field-value] - {:pre [(spec/valid? ::form form-def)]} - (let [field-def (get-in form-def [:fields field-id]) - {:keys [label required] spec-key :spec} field-def] - (list (when-not (field-valid? field-value spec-key req) - (spec-to-errmsg label spec-key field-value)) - (hform/label field-id label) - (hform/text-field {:required (when required "required")} - field-id - field-value))))) - -(defn readonly - [form-def field-id field-value] - (list (hform/label field-id (get-in form-def [:fields field-id :label])) - (hform/text-field {:readonly "readonly"} - field-id - field-value))) - -(defn valid? [form-def req] - {:pre [(spec/assert ::form form-def)]} - (reduce-kv - (fn [result field value] - (if-let [field-spec (get-in form-def [:fields field :spec])] - (let [from-req (get-in form-def [:fields field :from-req]) - check-value (if from-req (from-req value) value)] - (if (spec/valid? field-spec check-value) - true - (reduced false))) - result)) - true - (:params req))) - -(defn input-widget [id def value] - (hform/text-field {:required (or (:required def) false)} - id - value)) - -(defn checkbox-widget [id def value] - (hform/check-box id - (and (some? value) - (not= 0 value)) - value)) - -(defn textarea-widget [id def value] - (hform/text-area {:required (or (:required def) false)} - id - value)) - -(defn select-widget [id def value] - (hform/drop-down id - (:options def) - value)) - -(defn multiselect-widget [id def value] - (let [options (:options def)] - [:select {:multiple "multiple" - :size 5 - :name (name id) - :id (name id)} - (for [option options] - [:option {:value (second option) - :selected (in? value (second option))} - (first option)])])) - -(def widget-mapping - {:input input-widget - :checkbox checkbox-widget - :textarea textarea-widget - :select select-widget - :mselect multiselect-widget}) - -(defn widget-markup - [id def value req-value validate?] - (let [{:keys [label spec widget options]} def - widget (cond (some? widget) widget - (some? options) :select - :else :input) - renderer (widget widget-mapping)] - (list - (when (and validate? (not (spec/valid? spec req-value))) - (spec-to-errmsg label spec req-value)) - (hform/label id label) - (renderer id def (if validate? req-value value))))) - -(defn form-hash [def values] - (str (hash [def values]))) - -(defn render-widgets [form-def values req] - {:pre [(spec/assert ::form form-def)]} - (let [form-hash (form-hash form-def values) - submitted-hash (get-in req [:params :__form-hash]) - validate? (= form-hash submitted-hash)] - (conj (map - (fn [[field-id field-def]] - (let [{:keys [options to-form]} field-def - value (field-id values) - req-value (get-in req [:params field-id])] - (widget-markup field-id - field-def - (if to-form (to-form value) value) - req-value - validate?))) - (:fields form-def)) - (hform/hidden-field "__form-hash" form-hash) - (anti-forgery-field)))) - -(defn form-data [form-def req] - {:pre [(spec/assert ::form form-def)]} - (reduce (fn [coll [id field]] - (let [value (get-in req [:params id])] - (assoc coll - id - (if-let [from-req (:from-req field)] - (from-req value) - value)))) - {} - (:fields form-def))) diff --git a/src/wanijo/schema/view.clj b/src/wanijo/schema/view.clj index fec60e7..cddceb9 100644 --- a/src/wanijo/schema/view.clj +++ b/src/wanijo/schema/view.clj @@ -12,8 +12,7 @@ {:fields {:name {:label "Name" :required true :spec ::domain/name} - :uuid {:label "" - :widget :hidden}}}) + :uuid {:widget :hidden}}}) (def attr-form {:fields {:name {:label "Name" @@ -37,8 +36,7 @@ :required false :spec ::domain/assigned-to :widget :mselect} - :uuid {:label "" - :widget :hidden}}}) + :uuid {:widget :hidden}}}) (defn overview! [req] (let [session (:session req) diff --git a/src/wanijo/user/view.clj b/src/wanijo/user/view.clj index 0947dd1..18577a3 100644 --- a/src/wanijo/user/view.clj +++ b/src/wanijo/user/view.clj @@ -1,29 +1,21 @@ (ns wanijo.user.view (:require [hiccup.form :as hform] [ring.util.anti-forgery :refer [anti-forgery-field]] + [formulare.core :as form] [wanijo.framework.view :as view] - [wanijo.framework.form :as form] [wanijo.framework.routing :refer [path]] [wanijo.user.domain :as domain])) (def edit-form - {:fields {:name {:label "Nimi" - :required false - :spec ::domain/name} - :ident {:label "Ident" + {:fields {:ident {:label "Ident" :required false - :spec ::domain/ident} - :password {:label "Toki Pimeja" - :required true - :spec ::domain/password}}}) + :spec ::domain/ident}}}) (defn profile! [req user] (view/layout! :session (:session req) :content [[:h1 "Hi, " (get-in req [:session :ident])] - [:div (str user)] (hform/form-to [:post (path :user-edit)] (anti-forgery-field) - (form/readonly edit-form :ident (:ident user)) - (form/field edit-form :name req (:name user)))])) + (form/render-widgets edit-form user req))])) diff --git a/test/wanijo/framework/form_test.clj b/test/wanijo/framework/form_test.clj deleted file mode 100644 index 6d93c6e..0000000 --- a/test/wanijo/framework/form_test.clj +++ /dev/null @@ -1,208 +0,0 @@ -(ns wanijo.framework.form-test - (:require [clojure.test :refer :all] - [wanijo.framework.form :refer [form-data - valid? - render-widgets - form-hash]] - [clojure.string :refer [starts-with?]] - [clojure.spec.alpha :as spec])) - -(deftest test-form-data - (testing "no from-req is given" - (let [def {:fields {:name {:label "" - :required false - :spec :a}}}] - (is (= {:name "a"} - (form-data def {:params {:name "a"}}))) - (is (= {:name nil} - (form-data def {:params {}}))))) - (testing "from-req is called" - (let [def {:fields {:name {:label "" - :required false - :spec :a - :from-req #(str "foo" %)}}}] - (is (= {:name "foo"} - (form-data def {:params {}}))) - (is (= {:name "fooa"} - (form-data def {:params {:name "a"}}))))) - (testing "from-req is given in one field, not in another" - (let [def {:fields {:foo {:label "" - :spec number? - :from-req bigint} - :bar {:label "" - :spec string?}}}] - (is (= {:foo 5 - :bar "a"} - (form-data def {:params {:foo "5" - :bar "a"}}))))) - (testing "type conversion" - (let [def {:fields {:foo {:label "" - :required false - :spec :a - :from-req bigint}}}] - (is (= {:foo 5} - (form-data def {:params {:foo 5}})))))) - -(deftest test-valid? - (testing "required-key does not change validity of the form" - (let [def {:fields {:name {:label "" - :spec nil? - :required true}}}] - (is (= true (valid? def {:params {:name nil}})))) - (let [def {:fields {:name {:label "" - :spec nil? - :required false}}}] - (is (= true (valid? def {:params {:name nil}}))))) - (testing "converted values are passed to spec" - (let [def {:fields {:foo {:label "" - :spec number? - :from-req bigint} - :bar {:label "" - :spec string?}}}] - (is (= true (valid? def {:params {:foo "5" - :bar "a"}})))))) - -(deftest test-render-widgets - (testing "form-hash is rendered" - (let [def {:fields {}}] - (is (= (list [:input {:type "hidden" - :name "__form-hash" - :id "__form-hash" - :value (form-hash def {})}]) - (rest (render-widgets def {} {})))))) - (testing "anti-forgery-field is rendered" - (let [def {:fields {}}] - (is (starts-with? (first (render-widgets def {} {})) - (str "