Coder Social home page Coder Social logo

digestive-functors's Introduction

What

This is the source code of my personal home page. It might be of interest to anyone who wants to set up a blog using Hakyll.

License

This website falls under regular copyright laws. This means the code is available as a reference. You shouldn't use it as a starting point for your own site, the examples on the Hakyll site are much cleaner.

digestive-functors's People

Contributors

3noch avatar adinapoli avatar ajnsit avatar amigalemming avatar aslatter avatar axman6 avatar bergmark avatar bitonic avatar bjornbm avatar chrisdone avatar cimmanon avatar fhartwig avatar franklinchen avatar igrep avatar jaspervdj avatar juanpaucar avatar lpsmith avatar lukehoersten avatar mavenraven avatar mightybyte avatar mwu avatar norm2782 avatar ozataman avatar randrew avatar relrod avatar stepcut avatar superduper avatar tathougies avatar wunki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

digestive-functors's Issues

More flexible Heist radio buttons?

I'd like to have more fine-grained control over how my radio buttons are displayed. For instance, I'd like the possibility of wrapping each button/label pair in a span, for styling purposes. My suggestion: instead of having dfInputRadio return simply a set list of nodes, have it bind splices for "dfInputRadio:button" and "dfInputRadio:label" and then run its children with those splices.

Here's a sample implementation:

getParamAttrs :: Monad m => HeistT m [(Text, Text)]
getParamAttrs = do
  node <- getParamNode
  return $ case node of
             X.Element _ as _ -> as
             _                -> []

-- merge the paramNode's attributes with the given element
mergeWithNode :: Monad m => X.Node -> Splice m
mergeWithNode (X.Element t as c) = do
  as' <- getParamAttrs
  return [X.Element t (addAttrs as' as) c]
mergeWithNode n = return [n]

dfInputRadio :: Monad m => View Text -> Splice m
dfInputRadio view = do
  (ref, _) <- getRefAttributes Nothing
  let ref'     = absoluteRef ref view
      choices  = fieldInputChoice ref view
      value i  = ref' `mappend` "." `mappend` i
      button (i, c, sel) =
          X.Element "input"
               (attr sel ("checked", "checked")
        [ ("type", "radio"), ("value", value i)
        , ("id", value i), ("name", ref')
                ]) []
      label (i, c, sel) = X.Element "label" [("for", value i)] [X.TextNode c]
      choiceSplice c = runChildrenWith [ ("dfInputRadio:button", mergeWithNode $ button c)
                                       , ("dfInputRadio:label", mergeWithNode $ label c)
                                       ]
  mapSplices choiceSplice choices

Then in my template I can do

For instance, in my template, I'd like to do

<dfInputRadio ref="myRadio">
  <span class="my-stylish-class">
    <dfInputRadio:button class="some-button-class"/><br />
   And the label is <dfInputRadio:label />
  </span>
</dfInputRadio>

Provide an "optional" combinator

optional :: Form v m a -> Form v m (Maybe a)

The way I envision this working is if there is no input, then the form succeeds with a value of Nothing. If there is input, then the validator runs. In the ultimate result, you'll have the fairly ugly Maybe (Maybe a) where the outer Maybe represents whether there were errors and the inner Maybe represents whether there was data. It's pretty easy to write this combinator for primitive fields, but it's more difficult for compound forms. I imagine the semantics would be that the final result would be Nothing only if all of the subforms were completely empty. Otherwise it would go to the validator.

I'm not sure whether could implement this by recursively changing all subforms to return Maybe values or if it would need an augmented Result type such as:

data Result v a =
  Success a |
  Error v |
  NoData

No ref specified

I have removed a field from a form, in the Haskell and the HTML. Ever since then, in another form that is not connected to the changed one in any way, I only get this message:

A web handler threw an exception. Details:
No ref specified for field

I am using the Snap and Heist versions of digestive-functors. The full source can be found here.

It would be very nice if the error message could contain any hint about which field exactly, and if this error is a problem in the Heist template or the Haskell code. I'm completely lost here, as all the fields in the form are present in the template.

Different names for d-f-heist splices

The current names for digestive-functors-heist are understood by heist as generic tags, circumventing some of the special HTML parsing rules that might be convenient here. For example:

<p>
<dfInputText ref="foo">
</p>

is an error, because dfInputText isn't understood to be a void tag. If it were renamed to df:text:input instead, then it would be parsed as an input tag, meaning in particular that it would be implicitly closed. Heist (actually xmlhtml) applies parsing rules based on the "base name" of the tag, meaning the name after stripping off a colon-delimited prefix.

So I suggest you consider binding versions of all the d-f-heist splices by base names that reflect how they act in the document.

Note this even applies to dfForm as well, because a form tag will implicitly end a p tag, while dfForm will not.

Rendering of <select> fields should use proper values, not array indexes

Currently the output of inputSelect (from digestive-functors-blaze) will look something like the following:

<select id="edition.country" name="edition.country">
  <option value="edition.country.0" selected="selected"></option>
  <option value="edition.country.1">United Kingdom</option>
</select>

The values here are only correct for as long as the country table is the same in the database. As soon as the ordering changes, those values are incorrect. In this case, the type of choices is Maybe (Ref Country), which is effectively Maybe String, so it's important to consider how Nothing would be represented.

Running a form as a GET (rather than just displaying) causes the form to run twice [Snap/Heist]

If I use the standard runForm function to run my form, it only executes once, as expected. However, if I want the form to run on a GET request using runFormWith, the form executes twice. Normally this isn't a big deal, but my forms are requesting information from the database to populate select fields.

fooH :: AppHandler ()
fooH = do
    liftIO $ print "Before the form"
    (view, result) <- runGetForm "form" fooForm
    liftIO $ print "After the form"
    pass

fooForm :: (HasPostgres m, Monad m) => Form Text m (Bool, Bool)
fooForm = monadic $ do
    liftIO $ print "Inside form"
    return $ ( , )
        <$> "a" .: bool (Just False)
        <*> "b" .: bool (Just False)

getFormConfig :: SnapFormConfig
getFormConfig = SnapFormConfig (Just DF.Post) Nothing defaultUploadPolicy (\ _ -> disallow)

runGetForm :: MonadSnap m => Text -> Form v m a -> m (View v, Maybe a)
runGetForm = runFormWith getFormConfig

Running the fooH handler generates this log:

"Before the form"
"Inside form"
"Inside form"
"After the form"

multiple select

The form element has an attribute MULTIPLE which allows you to select multiple values at once. http://htmlhelp.com/reference/html40/forms/select.html Each selected option is returned as a separate name=value pair. The important thing to notice is that they will all have the same name. For example, if you had this form: <P>Select one or more sections to search: <SELECT NAME=sections MULTIPLE> <OPTION>Web Authoring Reference</OPTION> <OPTION>FAQ</OPTION> <OPTION>Design Elements</OPTION> <OPTION>Tools</OPTION> <OPTION>Feature Article</OPTION> </SELECT> </P> and the user selected FAQ and Tools then you would get back: sections=FAQ;sections=Tools Unfortuantely, this does not work with digestive-functors because we have: data Environment m i = Environment (FormId -> m (Maybe i)) | NoEnvironment where the Environment function can only return 0 or 1 matches. The reason we do not have this problem with checkboxes is because checkboxes allow you to assign a unique name to each checkbox option. But multiple select boxes do not. So, I think Environment needs to return a list of matches not just a Maybe? (Issue as reported by Jeremy Shaw, thanks!)

Add support for inputList

I'd be nice to be able to dynamically add form elements or subforms client-side in a way that would yield a list of form results in d-f server-side. Looks like it's existed in the past thanks to the work of Doug, Jasper, and some others but lost in overhauls:

I know this is a big request but I figured just documenting a demand for the feature would help in justifying the work.

postRootForm

It would be nice to have some sort of postRootForm that worked the same as postForm, but did not require a name for the form.

Currently I achieve this with:

postForm T.empty someForm (\(_:x) -> myEnv x)

But it would be nice if it were built in.

Better error detection and notification

In cases where your markup has inconsistencies the error detection/notification is really bad. I've been bitten numerous times by these problems and they are pretty hard to track down. I don't have any concrete suggestions right now. Just wanted to start a dialog.

Calling validate on an already-named field

If .: has already been used to add a name to a field, then it seems that one can no longer use validate to add validations to the field (the errors end up under the next-highest Ref, if any). It would be very useful to have a way to add validations to a field that already has a name.

Offer an 'optional' combinator

It would be much more elegant if we could have an optional combinator. This would both clean up the API (no need to offer stringRead and optionalStringRead), while also providing nicer code for authors. For example, at the moment I have:

  where labelCode = check "Label codes must be positive and at most 5 digits"
                      (maybe True (\i -> i > 0 && i < 100000)) $
                        optionalStringRead "Invalid integer" Nothing

Which is a tad on the messy side (I have to handle Maybe inside that check).

Multiple-input

digestive-functors' internals can model multiple values being submitted under the same name for a form (such as when submitting a list), but none of the Form helpers that exist expose this. It would be very nice to have a textList :: Form v m [Text] or similar to work with.

Form is not an instance of Monad

It would be very helpful to have Form become an instance of Monad, to allow for more complex forms (like where you have to use checkboxes to decide whether to add different things to a list in a datatype for example). I'm not sure how feasible , but if it is possible, I would find it very useful for something we're working on.

view to splices?

This is somewhat related to: #97

I'd like to dump the ref/value pairs from a view into splices so I can easily render them outside of a form context (e.g. in a profile page that just displays data). What I'd like to use is something like getRefAttributes and getContent to have a dfSpan or a dfDiv. Is there a good way to do this with the existing api? The example dumps everything into one splice with show, which isn't what I want.

Conditional Error Splice for Heist

The idea is to have a splice like this:

<dfIfErrors>There were errors</dfIfErrors>

<dfItErrors ref="name">There were errors for "name"</dfIfErrors>

Should be easily understood what they do.

listOf generates invalid HTML (w/ Heist)

When creating a list of elements via listOf/dfInputList, the generated HTML contains duplicate IDs. This is not valid in HTML, and can cause confusion with JavaScript.

My Heist template:

<fieldset>
    <legend>Product Codes</legend>

    <dfInputList ref="codes"><ul>
    <dfListItem><li itemAttrs><dfLabel ref="name">Name <dfInputText ref="name" itemAttrs /></dfLabel>
        <dfLabel ref="code">UPC <dfInputText ref="code" itemAttrs /></dfLabel>
        <input type="button" name="remove" value="Remove" /></li></dfListItem>
    </ul>

    <input type="button" name="add" value="Add another product" /></dfInputList>
</fieldset>

Generated HTML

<fieldset>
    <legend>Product Codes</legend>

    <div id='form.codes' class='inputList'><input type='hidden' name='form.codes.indices' value='0' /><ul>
    <li id='form.codes.-1' class='form.codes inputListTemplate'><label for='form.codes.-1.name'>Name <input id='form.codes.-1' class='form.codes inputListTemplate' type='text' name='form.codes.-1.name' value /></label>
        <label for='form.codes.-1.code'>UPC <input id='form.codes.-1' class='form.codes inputListTemplate' type='text' name='form.codes.-1.code' value /></label>
        <input type='button' name='remove' value='Remove' /></li><li id='form.codes.0' class='form.codes inputListItem'><label for='form.codes.0.name'>Name <input id='form.codes.0' class='form.codes inputListItem' type='text' name='form.codes.0.name' value='a' /></label>
        <label for='form.codes.0.code'>UPC <input id='form.codes.0' class='form.codes inputListItem' type='text' name='form.codes.0.code' value='1234' /></label>
        <input type='button' name='remove' value='Remove' /></li>
    </ul>

    <input type='button' name='add' value='Add another product' /></div>
</fieldset>

Multiple forms on one page

Using multiple forms on one page will result in invalid HTML, since IDs such as f0 and f1 will have multiple occurrences. A fix would be to add a certain prefix for every form.

Making the form's name accessible within the template (Heist)

I'd like to propose adding some hooks for JavaScript purposes when using Digestive Functors (not sure how much of this applies to non-Heist users). From a front-end perspective, the names of DF form fields have caused me a bit of trouble because I want to be able to write both my templates and my JavaScript to "just work" without having to hardcode things like form or subform names. Recently, I've been working on working with Digestive Aeson and would like to write an elegant solution that will go through the JSON response received from a submission made via XMLHttpRequest (AJAX) and match up the errors to the field they belong to. There's just one problem with that: Digestive Aeson doesn't tell you the form name.

Why this is a problem

Consider the following form:

data Foo = Foo
    { name :: Text
    , age :: Int
    , account :: Bar
    }

data Bar = Bar
    { username :: Text
    , password :: Text
    }

fooForm :: Monad m => Form Text m Foo
fooForm = Foo
    <$> "name" .: check "cannot be blank" (/= "") (text Nothing)
    <*> "age" .: stringRead "not a number" Nothing
    <*> "account" .: check "cannot have identical fields" (\ x -> username x /= password x) barForm

barForm :: Monad m => Form Text m Bar
barForm = Bar
    <$> "name" .: check "cannot be blank" (/= "") (text Nothing)
    <*> "password" .: check "cannot be blank" (/= "") (text Nothing)

Code to process the results (using Scotty):

(view, _) <- digestJSON fooForm =<< jsonData
json $ jsonErrors view

Submitting with an empty JSON object returns the following information:

{"name":"cannot be blank","account":{"password":"cannot be blank","name":"cannot be blank"},"age":"not a number"}

The problem becomes: how do I match the results to the fields via JavaScript? If I select all of the fields and split the names on the period character, I can traverse the JSON results and find the error:

var fields = el.querySelectorAll('input, textarea, select');
var prefix;
for (var i = 0, len = fields.length; i < len; i++) {
    var parent = fields[i].parentNode;
    var segments = fields[i].name.split(/\./);
    if (segments.length > 1) { prefix = segments.shift(); }
    var error = findNestedElement(segments, r);
    if (error) {
        var newErrorNode = errorNode.cloneNode(true);
        newErrorNode.innerHTML = error;
        parent.insertBefore(newErrorNode, fields[i].nextSibling);
    }
}

function findNestedElement(segments, collection) {
    var current = segments.shift();
    if (collection.hasOwnProperty(current)) {
        if (segments.length && typeof collection[current] === object) {
            return findNestedElement(segments, collection[current]);
        } else {
            return collection[current];
        }
    }
    return false;
}

However, it becomes inefficient to deal with results like this where the subform, which has no corresponding field, returns an error:

{"name":"cannot be blank","account":"cannot have identical fields","age":"not a number"}

It would be more efficient to traverse the JSON result and check the DOM for fields that match (especially since it would be more common for the JSON object to be smaller than the collection of form fields). If they don't exist, then they get pushed into my global error pile.

function unravelJSONObject(obj) {
    var collector = [];
    unraveler(obj, []);

    function unraveler(obj, keys) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                var keys_ = keys.slice(); // clone it
                keys_.push(prop);
                if (typeof obj[prop] === 'object') {
                    unraveler(obj[prop], keys_);
                } else {
                    collector.push({ error: obj[prop], path: keys_ });
                }
            }
        }
    }
    return collector;
}

var foo = {"name":"cannot be blank","account":{"password":"cannot be blank","name":"cannot be blank"},"age":"not a number"};

var errors = unravelJSONObject(foo);

for (var i = 0, len = errors.length; i < len; i++) {
    console.log(errors[i].path.join('.'));
}

The results look like this:

name
account.password
account.name
age

The most efficient way to lookup a DOM element when you only know the last part of the element's name is via document.querySelector([id$=a]), but it isn't very accurate when you have multiple fields with the same name (eg. name and account.name) because you could match multiple elements (note that querySelector returns a single item, so it could potentially get the right one if the elements are in the correct source order, but who wants to depend on that?). Ideally, I'd use getElementById instead of querySelector since it is a more efficient function. In order to do that, I need some way to discerning the form's name without selecting all of the fields and examining each one for a prefix (which can be error prone if the designer is adding form fields with periods in the name just to mess with us).

Proposal

I would like to see an attribute added to the form by the dfForm splice that contains the name of the form. If our template looks like this:

<dfForm>

</dfForm>

And our form's name is "name", then the generated markup would look like this:

<form method="POST" data-df-name="form">

</form>

For those not in the know, custom attributes are allowed in HTML5 by prefixing them with data-. I'm hesitant to suggest attaching it with the id, since I suspect many people are using it for other purposes and the name attribute was deprecated in HTML4.

From there, it would be a simple as making these modifications:

function unravelJSONObject(obj, prefix) {
    var collector = [];
    unraveler(obj, prefix ? [prefix] : []);

    function unraveler(obj, keys) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                var keys_ = keys.slice(); // clone it
                keys_.push(prop);
                if (typeof obj[prop] === 'object') {
                    unraveler(obj[prop], keys_);
                } else {
                    collector.push({ error: obj[prop], path: keys_ });
                }
            }
        }
    }
    return collector;
}

var foo = {"name":"cannot be blank","account":{"password":"cannot be blank","name":"cannot be blank"},"age":"not a number"};

// el is known in advance to be the form element
var errors = unravelJSONObject(foo, el.getAttribute('data-df-name'));

for (var i = 0, len = errors.length; i < len; i++) {
    console.log(errors[i].path.join('.'));
}

And now we have useful information to do an accurate check against the DOM:

form.name
form.account.password
form.account.name
form.age

If you've got a better way of attaching the form's name to the the form element, I'm all for it. It just needs to be on the form element for the best results. I realize this is something I could write a splice for myself, but it just seems like unnecessary boilerplate to write for every project. Plus, if there's an interest in this when I'm finished, I'd like to contribute it here -- so it has to be something I can depend on.

Keep error Path when wrapping error list

dfChildErrorList only list all error message without each error Path.
I think it is better to keep the Path, for instance, put the Path as a attribute of li element.

By doing this is just to be more flexible regrading customize error highlights.
For instance, utilize JavaScript to highlight error over each element.

-- A quick fix at my local
dfChildErrorListRef view = return $ errorList (viewErrors view)
errorList :: [(Path, Text)] -> [X.Node]
errorList []    = []
errorList errs  = [X.Element "ul" [] $ map makeError errs]
                  where makeError (p:_, e) = X.Element "li" [("data-error", p)] [X.TextNode e]

Since HTML 5 allow add customize attribute started with data-, I name data-error here.

Thanks.

Test suite builld failure with GHC 7.4.2

configuring
[1 of 1] Compiling Main             ( Setup.hs, Setup.o )
Linking Setup ...
configure flags: --enable-split-objs --disable-library-profiling --disable-shared --enable-library-vanilla --disable-executable-dynamic --enable-tests 
Configuring digestive-functors-0.7.1.0...
Dependency HUnit ==1.2.*: using HUnit-1.2.5.2
Dependency QuickCheck >=2.5 && <2.7: using QuickCheck-2.6
Dependency base ==4.*: using base-4.5.1.0
Dependency bytestring >=0.9 && <0.11: using bytestring-0.9.2.1
Dependency containers >=0.3 && <0.6: using containers-0.4.2.1
Dependency mtl >=1.1.0.0 && <3: using mtl-2.1.3.1
Dependency old-locale ==1.0.*: using old-locale-1.0.0.4
Dependency test-framework >=0.4 && <0.9: using test-framework-0.8.0.3
Dependency test-framework-hunit ==0.3.*: using test-framework-hunit-0.3.0.1
Dependency test-framework-quickcheck2 ==0.3.*: using
test-framework-quickcheck2-0.3.0.3
Dependency text >=0.10 && <1.2: using text-1.1.1.1
Dependency time ==1.4.*: using time-1.4
Using Cabal-1.14.0 compiled by ghc-7.4
Using compiler: ghc-7.4.2
Using install prefix:
/nix/store/5r1a1i4m6rpaarjwhn45gmahbkpgb50b-haskell-digestive-functors-ghc7.4.2-0.7.1.0
Binaries installed in:
/nix/store/5r1a1i4m6rpaarjwhn45gmahbkpgb50b-haskell-digestive-functors-ghc7.4.2-0.7.1.0/bin
Libraries installed in:
/nix/store/5r1a1i4m6rpaarjwhn45gmahbkpgb50b-haskell-digestive-functors-ghc7.4.2-0.7.1.0/lib/ghc-7.4.2/digestive-functors-0.7.1.0
Private binaries installed in:
/nix/store/5r1a1i4m6rpaarjwhn45gmahbkpgb50b-haskell-digestive-functors-ghc7.4.2-0.7.1.0/libexec
Data files installed in:
/nix/store/5r1a1i4m6rpaarjwhn45gmahbkpgb50b-haskell-digestive-functors-ghc7.4.2-0.7.1.0/share/digestive-functors-0.7.1.0
Documentation installed in:
/nix/store/5r1a1i4m6rpaarjwhn45gmahbkpgb50b-haskell-digestive-functors-ghc7.4.2-0.7.1.0/share/doc/digestive-functors-0.7.1.0
No alex found
Using ar found on system at:
/nix/store/xjvdpqgn2kd4rg0k30z020kxylvlbpx0-binutils-2.23.1/bin/ar
No c2hs found
No cpphs found
No ffihugs found
Using gcc version 4.8.2 found on system at:
/nix/store/agf8fk4xj2aqr3jccfcypqsmvkz3smn8-gcc-wrapper-4.8.2/bin/gcc
Using ghc version 7.4.2 found on system at:
/nix/store/mj77g1hjlvn9bj9f73qxp1salxp8mlg4-ghc-7.4.2-wrapper/bin/ghc
Using ghc-pkg version 7.4.2 found on system at:
/nix/store/mj77g1hjlvn9bj9f73qxp1salxp8mlg4-ghc-7.4.2-wrapper/bin/ghc-pkg
No greencard found
Using haddock version 2.11.0 found on system at:
/nix/store/7fz14sa0brpahnazc546xzi6al8nmrwm-ghc-7.4.2/bin/haddock
No happy found
No hmake found
Using hpc version 0.6 found on system at:
/nix/store/mj77g1hjlvn9bj9f73qxp1salxp8mlg4-ghc-7.4.2-wrapper/bin/hpc
Using hsc2hs version 0.67 found on system at:
/nix/store/mj77g1hjlvn9bj9f73qxp1salxp8mlg4-ghc-7.4.2-wrapper/bin/hsc2hs
No hscolour found
No hugs found
No jhc found
Using ld found on system at:
/nix/store/agf8fk4xj2aqr3jccfcypqsmvkz3smn8-gcc-wrapper-4.8.2/bin/ld
No lhc found
No lhc-pkg found
No nhc98 found
No pkg-config found
Using ranlib found on system at:
/nix/store/xjvdpqgn2kd4rg0k30z020kxylvlbpx0-binutils-2.23.1/bin/ranlib
Using strip found on system at:
/nix/store/xjvdpqgn2kd4rg0k30z020kxylvlbpx0-binutils-2.23.1/bin/strip
Using tar found on system at:
/nix/store/0x21z9gqd3rdhxffhxdaf1ccilaay4s4-gnutar-1.27.1/bin/tar
No uhc found
building
Building digestive-functors-0.7.1.0...
Preprocessing library digestive-functors-0.7.1.0...
[ 1 of 10] Compiling Text.Digestive.Util ( src/Text/Digestive/Util.hs, dist/build/Text/Digestive/Util.o )
[ 2 of 10] Compiling Text.Digestive.Form.List ( src/Text/Digestive/Form/List.hs, dist/build/Text/Digestive/Form/List.o )
[ 3 of 10] Compiling Text.Digestive.Types ( src/Text/Digestive/Types.hs, dist/build/Text/Digestive/Types.o )
[ 4 of 10] Compiling Text.Digestive.Form.Internal.Field ( src/Text/Digestive/Form/Internal/Field.hs, dist/build/Text/Digestive/Form/Internal/Field.o )
[ 5 of 10] Compiling Text.Digestive.Form.Internal ( src/Text/Digestive/Form/Internal.hs, dist/build/Text/Digestive/Form/Internal.o )
[ 6 of 10] Compiling Text.Digestive.Ref ( src/Text/Digestive/Ref.hs, dist/build/Text/Digestive/Ref.o )
[ 7 of 10] Compiling Text.Digestive.Form.Encoding ( src/Text/Digestive/Form/Encoding.hs, dist/build/Text/Digestive/Form/Encoding.o )
[ 8 of 10] Compiling Text.Digestive.View ( src/Text/Digestive/View.hs, dist/build/Text/Digestive/View.o )
[ 9 of 10] Compiling Text.Digestive.Form ( src/Text/Digestive/Form.hs, dist/build/Text/Digestive/Form.o )
[10 of 10] Compiling Text.Digestive   ( src/Text/Digestive.hs, dist/build/Text/Digestive.o )
Registering digestive-functors-0.7.1.0...
Preprocessing test suite 'digestive-functors-tests' for
digestive-functors-0.7.1.0...
[ 1 of 18] Compiling Text.Digestive.Util ( src/Text/Digestive/Util.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Util.o )
[ 2 of 18] Compiling Text.Digestive.Types ( src/Text/Digestive/Types.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Types.o )
[ 3 of 18] Compiling Text.Digestive.Form.Internal.Field ( src/Text/Digestive/Form/Internal/Field.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/Internal/Field.o )
[ 4 of 18] Compiling Text.Digestive.Ref ( src/Text/Digestive/Ref.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Ref.o )
[ 5 of 18] Compiling Text.Digestive.Form.List ( src/Text/Digestive/Form/List.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/List.o )
[ 6 of 18] Compiling Text.Digestive.Form.List.QTests ( tests/Text/Digestive/Form/List/QTests.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/List/QTests.o )
[ 7 of 18] Compiling Text.Digestive.Form.Internal ( src/Text/Digestive/Form/Internal.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/Internal.o )
[ 8 of 18] Compiling Text.Digestive.Form.QTests ( tests/Text/Digestive/Form/QTests.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/QTests.o )

tests/Text/Digestive/Form/QTests.hs:68:10:
    Warning: orphan instance:
      instance (Monad t, Monad m, Arbitrary a) =>
               Arbitrary (FormTree t v m a)

tests/Text/Digestive/Form/QTests.hs:82:10:
    Warning: orphan instance:
      instance Arbitrary v => Arbitrary (SomeField v)

tests/Text/Digestive/Form/QTests.hs:94:10:
    Warning: orphan instance:
      instance Arbitrary a => Arbitrary (Field v a)

tests/Text/Digestive/Form/QTests.hs:100:10:
    Warning: orphan instance: instance Arbitrary Text

tests/Text/Digestive/Form/QTests.hs:105:10:
    Warning: orphan instance: instance Show v => Show (SomeField v)
[ 9 of 18] Compiling Text.Digestive.Form ( src/Text/Digestive/Form.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form.o )
[10 of 18] Compiling Text.Digestive.Form.Encoding ( src/Text/Digestive/Form/Encoding.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/Encoding.o )
[11 of 18] Compiling Text.Digestive.Form.Encoding.Tests ( tests/Text/Digestive/Form/Encoding/Tests.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Form/Encoding/Tests.o )
[12 of 18] Compiling Text.Digestive.View ( src/Text/Digestive/View.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/View.o )
[13 of 18] Compiling Text.Digestive.Tests.Fixtures ( tests/Text/Digestive/Tests/Fixtures.hs, dist/build/digestive-functors-tests/digestive-functors-tests-tmp/Text/Digestive/Tests/Fixtures.o )

tests/Text/Digestive/Tests/Fixtures.hs:133:23:
    Not in scope: `TR.readEither'

tests/Text/Digestive/Tests/Fixtures.hs:158:23:
    Not in scope: `TR.readEither'

digestive-functors-wai

I am working on a generic adapter for digestive-functors to wai query strings and request bodies. Is this something that would be interesting to have managed as part of this project, or should I just upload separately?

Generating a list of checkboxes

Digestive Functors lacks a nice way of generating a list of checkboxes. The functionality of radio inputs is almost what I want.

Basically, I have a list of categories in the database. An item is allowed to have any number of categories associated with it or no categories at all. The listOf functionality only gets me the checkboxes themselves, no label text (what is the name of the category associated with this checkbox?). There's no need for JavaScript here at all: all of the options are presented to the user and they are highly unlikely to change between requests.

I ended up hacking together a new splice for retrieving the name of the input element as a text node. It isn't very nice, but it does the job: https://gist.github.com/cimmanon/7215147

Inconsistent behavior with subforms (Snap/Heist)

I have two forms that look similar to this (second form needs to be usable on its own as well as be a part of the first form):

fooForm :: Monad m => Form Text m (Text, Text)
fooForm = ( , )
    <$> "a" .: text Nothing
    <*> "b" .: barForm

barForm :: Monad m => Form Text m Text
barForm = "b" .: choice [("a", "Apple"), ("b", "Banana"), ("c", "Cantaloupe")] (Just "b")

The markup for the first form looks like this:

<dfForm>
    <dfInputText ref="a" />
    <dfInputSelect ref="b" />
    <input type="submit" />
</dfForm>

This generates markup that looks like this:

<form method='POST' enctype='application/x-www-form-urlencoded'>
    <input type='text' id='form.a' name='form.a' value />
    <select id='form.b' name='form.b'><option selected='selected' value='form.b.0'>Apple</option><option value='form.b.1'>Banana</option><option value='form.b.2'>Cantaloupe</option></select>
    <input type='submit' />
</form>

No matter what option you select for the "b" field, you get the 2nd option (the default). For whatever reason, Digestive Functors is able to correctly generate the input element, but it is unable to retrieve the element from the POST. The error, of course, is mine. The form should have looked like this:

fooForm :: Monad m => Form Text m (Text, Text)
fooForm = ( , )
    <$> "a" .: text Nothing
    <*> barForm

Digestive Functors, however, should have generated a run time error. There is no "b" field in the outer form: it is a subform that contains a "b" field.

subViews ignores viewContext

In its current implementation, subViews ignores viewContext, so that repeated calls to subViews only ever retrieve the children of the top level of a view. Consequently, it doesn't play nicely with subView. For example, consider

data Bar = Bar Foo Foo
data Foo = Foo Int Int

barForm :: Monad m => Form Text m Bar
barForm = Bar <$> "foo1" .:  fooForm <*> "foo2" .: fooForm

fooForm :: Monad m => Form Text m Foo
fooForm = Foo <$> "int1" .: intForm <*> "int2" .: intForm

intForm :: Monad m => Form Text m Int
intForm = stringRead "Not a number" (Just 0)

I would like to be able to call subViews (subView "foo1" view) and obtain views with contexts ["foo1", "int1"] and ["foo1", "int2"]. However, instead I get:

[View "barForm" ["foo1","foo1"] App (Nothing)
  Map _
    App (Just "foo1")
      Map _
        Pure (Just "int1") (Text "0")
      Map _
        Pure (Just "int2") (Text "0")
  App (Just "foo2")
    Map _
      Pure (Just "int1") (Text "0")
    Map _
      Pure (Just "int2") (Text "0")
 [] [] Get,View "barForm" ["foo1","foo2"] App (Nothing)
  Map _
    App (Just "foo1")
      Map _
        Pure (Just "int1") (Text "0")
      Map _
        Pure (Just "int2") (Text "0")
  App (Just "foo2")
    Map _
      Pure (Just "int1") (Text "0")
    Map _
      Pure (Just "int2") (Text "0")
 [] [] Get]

These contexts are now incorrect, and give errors when I try to do things with them. Is this the desired behavior for subViews?

Does not restrict input to choices

choice and related inputs do not restrict the input to the available choices, but instead simply pick the default choice in the case of invalid input.

QuickCheck 2.7 support

The current version of digestive-functors compiles just fine with QuickCheck 2.7 after the Cabal file has been edited (and the test suite succeeds, too).

Optional select input

It would be nice to have a version of groupedChoiceWith where no input results in Nothing and otherwise the input must match up with one of the possible choices. An optionalGroupedChoiceWith.

Tutorial produces bad HTML

The form element is not embedded within the body element. The rendered output is:

<form method="POST" enctype="application/x-www-form-urlencoded" action="/"><!DOCTYPE HTML>
<html><head><title>digestive-functors tutorial</title><style type="text/css">label {width: 130px; float: left; clear: both}ul.digestive-functors-error-list { color: red; list-style-type: none; padding-left: 0px;}</style></head><body><style type="text/css">label {width: 130px; float: left; clear: both}ul.digestive-functors-error-list { color: red; list-style-type: none; padding-left: 0px;}</style><h2>Author</h2><label for="test.author.name">Name: </label><input type="text" id="test.author.name" name="test.author.name" value=""><br><label for="test.author.mail">Email address: </label><input type="text" id="test.author.mail" name="test.author.mail" value=""><br><h2>Package</h2><label for="test.package.name">Name: </label><input type="text" id="test.package.name" name="test.package.name" value=""><br><label for="test.package.version">Version: </label><input type="text" id="test.package.version" name="test.package.version" value="0.0.0.1"><br><label for="test.package.category">Category: </label><select id="test.package.category" name="test.package.category"><option value="test.package.category.0" selected="selected">Web</option><option value="test.package.category.1">Text</option><option value="test.package.category.2">Math</option></select><br><br><input type="submit" value="Submit"></body></html></form>

Improve digestive-functors-heist documentation

Chat extract:

18:52 < Luke> http://hpaste.org/69102
18:53 < Luke> http://hpaste.org/69103 html
18:53 < jaspervdj> Luke: Ah, I got it :-)
18:53 < jaspervdj> Luke: You only need to set the `ref` attribute in Heist
18:54 < jaspervdj> The `name` and `id` are filled in automatically to ensure they're unique
18:54 < jaspervdj> The same goes for the label; you don't need the `for` there

This should be made clear in the documentation.

Allow views to be generated in the m monad

The main motivation for this that I've encountered is to generate the first argument to the choice function from data read from a database. I imagine this will require something like

getForm :: Monad m => Text -> Form v m a -> m (View v)

The runFormWith function in digestive-functors-snap already has the right interface, but we might need more combinators in the core library to facilitate the needed operations.

I guess my immediate problem could be addressed by just adding a new function

choice :: Eq a => m [(a, v)] -> Maybe a -> Form v m a

The other existing primitives don't seem like they would need anything like this. However, it's easy to imagine more complex forms using them. Even something as simple as an HTML5 number spinner might use an IO computation to decide what the parameter values min, max, and step should be.

Input list using provided defaults

Here is a use case, taken from the example linked from the Cabal file.

Here is the output, I would expect the values in the inputs to be filled in with "foo" "bar" 0.

Am I doing something wrong? I looked at this all evening and the implementation of inputList and I came up with a fix, but I can't tell if it's the correct fix, or whether it breaks some assumptions:

forms = zipWith ($) fs $ maybe (maybe [Nothing] (map Just) defaults)
                               (\i -> take i (maybe [] (map Just) defaults ++ repeat Nothing))
                               countFromForm

From my understanding, this will enable the defaults to be included in all cases, and the repeat Nothing is for non-default inputs added dynamically by the client.

If you can find what I'm doing wrong, or grok this change I propose, I'd appreciate it. I had a hard time grokking this library and this function.

formId is not exported

The README uses the formId function. I found the function in Text.Digestive.Results, but it is not exported by the module.

Happstack 7.3.0

Tutorial.lhs shows this to be incompatible with the newest Happstack version. (7.3.0)

Heist outputs attributes with single quotes

The HTML produces by df heist uses single quotes for element attributes instead of double quotes. Output from snap-heist.hs

<form action='/' method='POST' enctype='application/x-www-form-urlencoded'>


    <label for='form.name'>Name: </label>
    <input type='text' id='form.name' name='form.name' value='Jasper' />
    <br />

    <label for='form.password'>Password: </label>
    <input type='password' id='form.password' name='form.password' value />
    <br />

    <label for='form.sex'>Sex: </label>
    <select id='form.sex' name='form.sex'><option selected='selected' value='form.sex.0'>Female</option><option value='form.sex.1'>Male</option></select>
    <br />

    Birthday:

    <input size='2' type='text' id='form.birthdate.day' name='form.birthdate.day' value='16' />
/
<input size='2' type='text' id='form.birthdate.month' name='form.birthdate.month' value='6' />
/
<input size='4' type='text' id='form.birthdate.year' name='form.birthdate.year' value='1990' />

    <br />

    <input value='Enter' type='submit' />
</form>

"monadic" seems to break field naming

If I have Foo <$> "bar" .: choice [] Nothing then inputSelect "bar" shows an empty <select> as expected.

If I change the form to Foo <$> "bar" .: (monadic $ return $ choice [] Nothing) then the same view crashes with 'bar is not a field'.

I may be doing something stoopid, but as monadic has no documentation, I can only assume this is how it is intended to work.

best practice for pre-populating form with existing data?

So, I'm working on a form that edits data that has already been saved to the database. Right now, I'm just manually splicing in the data with heist, but that kind of falls apart with my sum type / drop down mapping. I could dump the data into the text form digestive functor likes (e.g. "form.name=hello&form.field=bye..."), but I was thinking that there might be a nicer way to do that that I can't figure out, since it seems like a common use case? If not, I was thinking of adding something like

prepopulate :: a -> Form v Identity a -> View v

to digestive functors.

choice breaks with an empty array

Consider the following:

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Monad.IO.Class (liftIO)
import Snap.Blaze
import Snap.Core
import Snap.Http.Server
import Text.Digestive
import Text.Blaze.Html
import Text.Digestive.Blaze.Html5
import Text.Digestive.Snap

testForm :: (MonadSnap m) => Form Html m Int
testForm = "choice" .: choice [] Nothing

main :: IO ()
main = quickHttpServe handler
  where handler = do (v, i) <- runForm "f" testForm
                     case i of
                       Just r -> liftIO $ print "I'm about to crash!" >> print r
                       Nothing -> blaze (view v)
        view v = form v "foo" $ do inputSelect "choice" v
                                   inputSubmit "Explode!"

Expected output: the form does not validate
Actual output:

A web handler threw an exception. Details:
Prelude.(!!): index too large

Optional radio buttons

It would be nice to be able to define a set of optional radio buttons, such that when the user is presented with a choice, none of the buttons are initially selected. Instead of a choice of type c, this would return a Maybe c, with Nothing representing the possibility that the user fails to select any of the radio buttons.

(Apologies if this is already possible -- after some wrangling, I've been unable to make it work. It appears that digestive-functors automatically chooses the first choice if the given default doesn't match any of the choices. For instance,

choice [(Just 1, "one"), (Just 2, "two")] Nothing

results in 1 being selected in the view.)

'required' combinator

I'm suggesting the inclusion of this helpful function in the library:

required :: (Monad m) => v -> Form v m (Maybe a) -> Form v m a
required e = validate (maybe (Error e) Success)

The allows the use of functions which produce a maybe, along with the Applicative instance.

Monad instance for FormTree?

Hey, so I've looking into adding a confirmation validator to digestive-functors-validations that would work similar to Active Record confirmations .

This looks like it needs a Monad instance for FormTree, so I've looked into adding that. The problem is that some of the helper functions are impossible to make the type system unify (I think). For example, if

...
Return   :: Field v a -> FormTree Identity v m a
Bind     :: FormTree Identity v m a
         -> (Field v a -> FormTree Identity v m b)
         -> FormTree Identity v m b
...

, then for

formMapView :: Monad m => (v -> w) 
                       -> FormTree Identity v m a 
                       -> FormTree Identity w m a
...
formMapView (Bind m f) = ???

we start with

FormTree Identity v m z
-> (Field v z -> FormTree Identity v m a)
-> FormTree Identity v m a

and we need to get our type to

FormTree Identity w m z
-> (Field w z -> FormTree Identity w m a)
-> FormTree Identity w m a

.
So,

 formMapView (Bind m f) = ???

must be

 formMapView (Bind m f) = Bind  (mapFromView f m) ???

or

formMapView (Bind m f) = (\x -> ???)(mapFromView f m)

where

x :: FormTree Identity w m z -> FormTree Identity w m a

. The only way we have to get from

z -> a

is to use

(Field v z -> FormTree Identity v m a)

that we're given by y. But, we have no way of getting from

w -> v

only

v -> w

, so it doesn't look possible, at least not without changing the type signature. But the only place formMapView is used is for a Functor instance, so even that doesn't help. I was thinking of adding a new type called MonadFormTree and a runMonadFormTree that would let you get back to a regular FormTree. Seems kind of ugly though. Any thoughts?

Where is heist 0.10.x?

digestive-functors-heist-0.5.1.0 requires heist version 0.10.x to build, but that version of heist doesn't seem to exist on Hackage? Is that intentional?

Nested form errors?

I have something like the following:

currencyForm = Currency <$>
    "currency" .: validate threeLetterCode (string Nothing) <*>
    "to" .: stringRead "invalid address" Nothing

sendForm = Send <$>
    "to" .: stringRead "invalid address" Nothing <*>
    currencyForm

This works, but of course I get duplicate errors for the "to" field.
What is the preferred way to solve this, in the absence of a Monad instance for Form ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.