jkk / formative

Web forms for Clojure and ClojureScript - rendering, parsing, and validating
208 stars 18 forks source link

option to parse to joda dates #23

Open jkk opened 11 years ago

jkk commented 11 years ago

Must account for this in validations, too - does Verily need to be updated?

lkrubner commented 10 years ago

Perhaps related to this, I can not get a datetime to render correctly. I posted some screenshots of the problem on my blog:

http://www.smashcompany.com/technology/trying-to-use-the-formative-library

Is that a bug, or do I need to reformat the datetime to get it to work?

jkk commented 10 years ago

Might be a bug. Could you verify the class of the date before passing it to Formative? E.g. with (class some-date-object)

lkrubner commented 10 years ago

Okay, this:

(println (str (class (:created-at item))))

gives me:

class org.joda.time.DateTime

Do I need to coerce this to a different type?

This value is probably coming back to me straight from the driver that connects to MongoDb, though elsewhere in my code I rely on https://github.com/clj-time/clj-time for handling of dates.

lkrubner commented 10 years ago

Apparently, I was able to fix this problem by adding the special keys "year-start" and "year-end". Perhaps the documentation could make clear that these keys are mandatory?

I ended up with this field definition:

{ :name :created-at :type :datetime-select :note "When was this item first created?" :year-start 2000 :year-end 2020 }

My thanks to you for the work you've done on Formative. It is a good library.

lkrubner commented 10 years ago

Actually I can not get this to work. I am using (wrap-keyword-params) so most of the form inputs are keywords, but not the datetime-select inputs, which for some reason remain strings. If I use pprint on the form input I see data like:

"created-at[month]" "11", :top-winner-bio "", :mailing-address "", :country "", :cash-total "0.2659130096435547", :subscribe-to-questions-via-email "true", :previous-item-name "", "created-at[day]" "21", :email "zach@gmail.com", "updated-at[m]" "", "updated-at[year]" "", "updated-at[h]" "",

All of the field names are correctly transformed to keywords, except for the datetime-select forms. More so, when I validate the form:

(fp/parse-params the-form (:params request))

The datetime-select fields simply vanish from the item.

I looked here:

https://github.com/jkk/formative/blob/master/src/formative/parse.cljx

and it seems like the code can parse datetime-select. What do I need to do to enable this? Is there a :datatype that I need to set? Or is there specific Ring middleware I should use? Or does (wrap-keyword-params) actually block the workings of Formative?

lkrubner commented 10 years ago

Huh. This is strange. Even if I manually create a datetime and put it into the (:params request) Formative still removes it when it validates the data.

Right now I have this clunky bit of code, with a pprint at top and pprint at bottom:

(println " these values will be validated by formative: ") (pp/pprint (:params request)) (let item-type-as-keyword (keyword (get-in request [:params :item-type])) the-form (secretary/get-from-interactions [:schema item-type-as-keyword]) item (:params request) item (if (:item-name item) item (assoc item :item-name (in/make-item-name item))) created-at (tyme/make-created-at item) updated-at (tyme/make-updated-at item) start-at (tyme/make-start-at item) end-at (tyme/make-end-at item) item (if created-at (assoc item :created-at created-at) item) item (if updated-at (assoc item :updated-at updated-at) item) item (if start-at (assoc item :start-at start-at) item) item (if end-at (assoc item :end-at end-at) item) item (fp/parse-params the-form item) (pp/pprint item)

And this:

(tyme/make-created-at item)

is a clunky way to force a datetime:

(defn make-created-at [item](if %28get-in item) (tyme/date-time (Integer/parseInt (get-in item "created-at[year]")) (Integer/parseInt (get-in item "created-at[month]")) (Integer/parseInt (get-in item "created-at[day]")) (Integer/parseInt (get-in item "created-at[h]")) (Integer/parseInt (get-in item "created-at[m]")))))

"tyme" is just a refer to clj-time.

The pprint shows the datetime-select input with string keys like this:

"updated-at[year]" "", "updated-at[h]" "",

The bottom pprint surprises me: the datetimes have been removed. Formative gives no error, no exception, Formative simply removes the datetime.

I am trying to figure out why this is.

lkrubner commented 10 years ago

Looking here:

https://github.com/jkk/formative/blob/master/src/formative/parse.cljx

I see:

(fu/to-date
            (fu/from-timezone
              (fu/with-time date
                (fu/hour time)
                (fu/minute time)
                (fu/sec time))

So I added a "timezone" as string:

                  {
                   :name :start-at
                   :note " :start-at "
                   :year-start 1950
                   :year-end 2030
                   :important-to-show-in-lists true
                   :datatype :date
                   :ampm false
                   :seconds false
                   :timezone "US/Eastern"
                   }

This did not help.

jkk commented 10 years ago

Could you try (fp/parse-params the-form (:form-params req)) or (fp/parse-request the-form req) and see if that does what you expect?

lkrubner commented 10 years ago

This:

(pprint request)

shows me (among other things):

:form-params {},

all data is in:

:multipart-params

and:

:params

These last 2, :multipart-params and :params, have similar content, but :multipart-params have keys that are strings. (wrap-keyword-params) gives me keyword keys in :params, but not in :multipart-params.

I do not know why :form-params is empty. Maybe I have wrap-params in the wrong place? I have my Ring middleware set up as:

(def app (-> app-routes (friend/authenticate {:workflows [(workflows/interactive-form)] :credential-fn (partial creds/bcrypt-credential-fn users)}) (wrap-session {:cookie-name "tma-admin-session" :cookie-attrs {:max-age 90000000}}) (wrap-cookies) (wrap-keyword-params) (wrap-multipart-params) (wrap-nested-params) (wrap-params)))

I just tried:

(def app (-> app-routes (wrap-params) (friend/authenticate {:workflows [(workflows/interactive-form)] :credential-fn (partial creds/bcrypt-credential-fn users)}) (wrap-session {:cookie-name "tma-admin-session" :cookie-attrs {:max-age 90000000}}) (wrap-cookies) (wrap-keyword-params) (wrap-multipart-params) (wrap-nested-params)))

but then I am unable to log in, so I moved wrap-params back to the end.

Am I missing any middleware that is necessary to work with Formative? I didn't see anything special in the demo app you've set up.

If I access :multipart-params and send that to parse-params, like this:

    item (:multipart-params request)
    item (do
           (println " now we will parse this item: ")
           (pp/pprint item)
            (fp/parse-params the-form item))

I get:

java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to java.lang.CharSequence at clojure.string$blankQMARK.invoke(string.clj:279) at formative.parse$parse_date.invoke(parse.clj:98) at formative.parse$fn9329.invoke(parse.clj:105) at clojure.lang.MultiFn.invoke(MultiFn.java:231) at formative.parse$parse_nested_params$fn9410.invoke(parse.clj:251)

I am guessing that the clojure.lang.PersistentArrayMap causing the problems is the file maps being uploaded, such as:

"file-1" {:size 0, :tempfile

<File /var/folders/85/50nmp8fx3q72pxv4zlh_74pm0000gn/T/ring-multipart-7431437485937371196.tmp>,

:content-type "application/octet-stream", :filename ""},

In this case I did not upload a file, but the map is still there in :multipart-params.