forma: Parse and validate forms in JSON format

[ bsd3, library, web ] [ Propose Tags ]

Parse and validate forms in JSON format.


[Skip to Readme]

Modules

[Index] [Quick Jump]

Flags

Manual Flags

NameDescriptionDefault
dev

Turn on development settings.

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0, 0.2.0, 1.0.0, 1.1.0, 1.1.1, 1.1.2, 1.1.3, 1.2.0
Change log CHANGELOG.md
Dependencies aeson (>=0.9 && <1.6), base (>=4.11 && <5.0), containers (>=0.5 && <0.7), mtl (>=2.0 && <3.0), text (>=0.2 && <1.3), unordered-containers (>=0.2.5 && <0.3) [details]
License BSD-3-Clause
Author Mark Karpov <markkarpov92@gmail.com>
Maintainer Mark Karpov <markkarpov92@gmail.com>
Revised Revision 1 made by mrkkrp at 2020-05-25T18:38:11Z
Category Web
Home page https://github.com/mrkkrp/forma
Bug tracker https://github.com/mrkkrp/forma/issues
Source repo head: git clone https://github.com/mrkkrp/forma.git
Uploaded by mrkkrp at 2019-11-25T19:52:05Z
Distributions LTSHaskell:1.2.0, Stackage:1.2.0
Reverse Dependencies 1 direct, 0 indirect [details]
Downloads 4623 total (32 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2019-11-25 [all 1 reports]

Readme for forma-1.1.3

[back to package description]

Forma

License BSD3 Hackage Stackage Nightly Stackage LTS Build Status

This module provides a tool for validation of forms that are represented in the JSON format. Sending forms in JSON format via an AJAX request instead of traditional submitting of forms has a number of advantages:

  • Smoother user experience: no need to reload the whole page.

  • Form rendering is separated and lives only in GET handler, POST (or whatever method you deem appropriate for your use case) handler only handles validation and actual effects that form submission should initiate.

  • You get a chance to organize form input just like you want.

The task of validation of a form in the JSON format may seem simple, but it's not trivial to get it right. The library allows you to:

  • Define form parser using type-safe applicative notation with field labels being stored on the type label which guards against typos and will force all your field labels be always up to date.

  • Parse JSON Value according to the definition of form you created.

  • Stop parsing immediately if given form is malformed and cannot be processed.

  • Validate forms using any number of composable checkers that you write for your specific problem domain. Once you have a vocabulary of checkers, creation of new forms is just a matter of combining them, and yes they do combine nicely.

  • Collect validation errors from multiple branches of parsing (one branch per form field) in parallel, so validation errors in one branch do not prevent us from collecting validation errors from other branches. This allows for a better user experience as the user can see all validation errors at the same time.

  • Use optional and (<|>) from Control.Applicative in your form definitions instead of ugly ad-hoc stuff.

  • Perform validation using several form fields at once. You choose which “sub-region” of your form a given check will have access to, see withCheck.

Example of use

Here is a complete working example:

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE OverloadedLabels  #-}
{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Control.Monad.Except
import Data.Aeson
import Data.Text (Text)
import Web.Forma
import qualified Data.Text as T

type LoginFields = '["username", "password", "remember_me"]

data LoginForm = LoginForm
  { loginUsername   :: Text
  , loginPassword   :: Text
  , loginRememberMe :: Bool
  } deriving (Show)

loginForm :: Monad m => FormParser LoginFields Text m LoginForm
loginForm = LoginForm
  <$> field #username notEmpty
  <*> field #password notEmpty
  <*> field' #remember_me

notEmpty :: Monad m => Text -> ExceptT Text m Text
notEmpty txt =
  if T.null txt
    then throwError "This field cannot be empty."
    else return txt

myInput :: Value
myInput = object
  [ "username"    .= ("Bob" :: Text)
  , "password"    .= ("123" :: Text)
  , "remember_me" .= True
  ]

main :: IO ()
main = runForm loginForm myInput >>= print

You may want to play with it a bit before writing serious code.

Contribution

Issues, bugs, and questions may be reported in the GitHub issue tracker for this project.

Pull requests are also welcome.

License

Copyright © 2017–present Mark Karpov

Distributed under BSD 3 clause license.