packages feed

scotty-form 0.4.1.0 → 0.5.0.0

raw patch · 4 files changed

+138/−71 lines, 4 filesdep +unliftiodep ~basedep ~dittodep ~ditto-lucidPVP ok

version bump matches the API change (PVP)

Dependencies added: unliftio

Dependency ranges changed: base, ditto, ditto-lucid, lucid, scotty, text

API changes (from Hackage documentation)

- Web.Scotty.Trans.Form: instance (Web.Scotty.Internal.Types.ScottyError e, GHC.Base.Monad m) => Ditto.Core.Environment (Web.Scotty.Internal.Types.ActionT e m) [Web.Scotty.Internal.Types.Param]
+ Web.Scotty.Trans.Form: instance Control.Monad.IO.Unlift.MonadUnliftIO m => Ditto.Core.Environment (Web.Scotty.Internal.Types.ActionT m) [Web.Scotty.Internal.Types.Param]
- Web.Scotty.Form: ditto :: Monoid view => ([(Text, Text)] -> view -> view) -> Text -> Form ActionM [Param] err view a -> ActionM (Result err a, view)
+ Web.Scotty.Form: ditto :: ([(Text, Text)] -> view -> view) -> Text -> Form ActionM [Param] err view a -> ActionM (Result err a, view)
- Web.Scotty.Form: simpleDittoGET :: Applicative f => Text -> Form ActionM [Param] err (HtmlT f ()) b -> ActionM (Result err b, HtmlT f ())
+ Web.Scotty.Form: simpleDittoGET :: forall (f :: Type -> Type) err b. Applicative f => Text -> Form ActionM [Param] err (HtmlT f ()) b -> ActionM (Result err b, HtmlT f ())
- Web.Scotty.Form: simpleDittoPOST :: Applicative f => Text -> Form ActionM [Param] err (HtmlT f ()) b -> ActionM (Result err b, HtmlT f ())
+ Web.Scotty.Form: simpleDittoPOST :: forall (f :: Type -> Type) err b. Applicative f => Text -> Form ActionM [Param] err (HtmlT f ()) b -> ActionM (Result err b, HtmlT f ())
- Web.Scotty.Form: type ScottyForm a = Form ActionM [Param] ScottyFormError (Html ()) a
+ Web.Scotty.Form: type ScottyForm a = Form ActionM [Param] ScottyFormError Html () a
- Web.Scotty.Trans.Form: ditto :: (Monoid view, Monad m, ScottyError e) => ([(Text, Text)] -> view -> view) -> Text -> Form (ActionT e m) [Param] err view a -> ActionT e m (Result err a, view)
+ Web.Scotty.Trans.Form: ditto :: forall (m :: Type -> Type) view err a. Monad m => ([(Text, Text)] -> view -> view) -> Text -> Form (ActionT m) [Param] err view a -> ActionT m (Result err a, view)
- Web.Scotty.Trans.Form: dittoSingle :: (Monad m, ScottyError e) => ([(Text, Text)] -> view -> view) -> Text -> Form (ActionT e m) [Param] err view a -> ActionT e m (Result err a, view)
+ Web.Scotty.Trans.Form: dittoSingle :: forall (m :: Type -> Type) view err a. Monad m => ([(Text, Text)] -> view -> view) -> Text -> Form (ActionT m) [Param] err view a -> ActionT m (Result err a, view)
- Web.Scotty.Trans.Form: simpleDittoGET :: (Applicative f, Monad m, ScottyError e) => Text -> Form (ActionT e m) [Param] err (HtmlT f ()) b -> ActionT e m (Result err b, HtmlT f ())
+ Web.Scotty.Trans.Form: simpleDittoGET :: forall (f :: Type -> Type) (m :: Type -> Type) err b. (Applicative f, Monad m) => Text -> Form (ActionT m) [Param] err (HtmlT f ()) b -> ActionT m (Result err b, HtmlT f ())
- Web.Scotty.Trans.Form: simpleDittoPOST :: (Applicative f, Monad m, ScottyError e) => Text -> Form (ActionT e m) [Param] err (HtmlT f ()) b -> ActionT e m (Result err b, HtmlT f ())
+ Web.Scotty.Trans.Form: simpleDittoPOST :: forall (f :: Type -> Type) (m :: Type -> Type) err b. (Applicative f, Monad m) => Text -> Form (ActionT m) [Param] err (HtmlT f ()) b -> ActionT m (Result err b, HtmlT f ())
- Web.Scotty.Trans.Form: type ScottyForm e m a = Form (ActionT e m) [Param] ScottyFormError (HtmlT (ActionT e m) ()) a
+ Web.Scotty.Trans.Form: type ScottyForm (m :: Type -> Type) a = Form ActionT m [Param] ScottyFormError HtmlT ActionT m () a

Files

CHANGELOG.md view
@@ -0,0 +1,21 @@+# Changelog++## 0.5.0.0 — 2026-08-27++### Breaking+- Require `scotty >= 0.20` (drops `ScottyError` and `ActionT e m`; use `ActionT m`)+- `ScottyForm e m a` renamed to `ScottyForm m a` in `Web.Scotty.Trans.Form`+- `Param` values are strict `Text` (scotty >= 0.20); `liftParser` now takes strict `Text` parsers (same as `liftParser'`)+- `Environment` instance for `ActionT` requires `MonadUnliftIO` (needed for `formParams`)++### Changed+- Compatible with `ditto` 0.4 and 0.5 (ditto 0.5 is backwards compatible)+- Replace deprecated `params` with `pathParams`, `queryParams`, and `formParams`+- Prefer GHC 9.10+ (`GHC2024`) with fallback to `GHC2021` / `Haskell2010`+- Widen dependency bounds (`base >= 4.14`, `text >= 1.2`)+- Move language extensions into cabal `default-extensions`+- Modern cabal format (`cabal-version: 3.8`)++### Added+- GitHub Actions CI+- `cabal.project.local.example` for local `ditto` / `ditto-lucid` development
scotty-form.cabal view
@@ -1,35 +1,94 @@-cabal-version: >=1.10+cabal-version: 3.8 name: scotty-form-version: 0.4.1.0+version: 0.5.0.0 synopsis: Html form validation using `ditto` description: Formlet library for `scotty` using `lucid` and `ditto`--- bug-reports: license: MIT license-file: LICENSE author: goolord maintainer: zacharyachurchill@gmail.com--- copyright:+homepage: https://github.com/goolord/scotty-form+bug-reports: https://github.com/goolord/scotty-form/issues category: Web build-type: Simple-extra-source-files: CHANGELOG.md+extra-doc-files:+  CHANGELOG.md+tested-with:+  GHC == 9.6.7+  GHC == 9.8.4+  GHC == 9.10.3+  GHC == 9.12.4+  GHC == 9.14.1  source-repository head   type: git   location: https://github.com/goolord/scotty-form.git +common warnings-base+  ghc-options: -Wall++common warnings-modern+  if impl(ghc >= 8.10)+  ghc-options:+    -Wcompat+    -Widentities+    -Wincomplete-record-updates+    -Wincomplete-uni-patterns+    -Wmissing-deriving-strategies+    -Wunused-packages++common warnings-strict+  if impl(ghc >= 9.4)+  ghc-options: -Wredundant-constraints++common extensions-shared+  default-extensions:+    BangPatterns+    FlexibleInstances+    MultiParamTypeClasses+    OverloadedStrings+    RecordWildCards+    TypeApplications+    TypeFamilies++common defaults-ghc2024+  import:+    warnings-base, warnings-modern, warnings-strict, extensions-shared+  default-language: GHC2024++common defaults-ghc2021+  import:+    warnings-base, warnings-modern, warnings-strict, extensions-shared+  default-language: GHC2021+  default-extensions:+    DataKinds+    DerivingStrategies+    LambdaCase++common defaults-legacy+  import: warnings-base, extensions-shared+  default-language: Haskell2010++common defaults+  if impl(ghc >= 9.10)+    import: defaults-ghc2024+  elif impl(ghc >= 9.2)+    import: defaults-ghc2021+  else+    import: defaults-legacy+ library+  import: defaults   exposed-modules:     Web.Scotty.Form     Web.Scotty.Trans.Form-  ghc-options: -Wall-  -- other-modules:-  -- other-extensions:   build-depends:-      base >=4.12 && <5-    , text >=1.2.4 && <3.0-    , lucid >=2.9.12 && <3.0-    , scotty >=0.12 && <1-    , ditto >=0.4 && <=0.5-    , ditto-lucid >= 0.4 && <= 0.5+      base        >= 4.14 && < 5+    , text        >= 1.2  && < 3+    , lucid       >= 2.9  && < 3+    , scotty      >= 0.20 && < 1+    , ditto       >= 0.4  && < 0.6+    , ditto-lucid >= 0.4  && < 0.6+    , unliftio    >= 0.2  && < 0.3   hs-source-dirs: src-  default-language: Haskell2010+  ghc-options: -fno-warn-orphans -Wno-error=orphans
src/Web/Scotty/Form.hs view
@@ -15,17 +15,16 @@ import Ditto.Types import Web.Scotty import qualified Web.Scotty.Trans.Form as Trans-import qualified Data.Text.Lazy as TL import Lucid -encQP :: [(a, TL.Text)] -> Text+encQP :: [(a, Text)] -> Text encQP = Trans.encQP  -- | a @ditto@ formlet for @scotty@ type ScottyForm a = Form ActionM [Param] Trans.ScottyFormError (Html ()) a -ditto :: (Monoid view)-  => ([(Text, Text)] -> view -> view) -- ^ wrap raw form html inside a <form> tag+ditto+  :: ([(Text, Text)] -> view -> view) -- ^ wrap raw form html inside a <form> tag   -> Text -- ^ form name prefix   -> Form ActionM [Param] err view a -- ^ the formlet   -> ActionM (Result err a, view)@@ -40,14 +39,14 @@ dittoSingle = Trans.dittoSingle  -- | create @\<form action=action method=\"GET\" enctype=\"application/xxx-form-urlencoded\"\>@-simpleDittoGET :: (Applicative f)+simpleDittoGET :: Applicative f   => Text -- ^ action   -> Form ActionM [Param] err (HtmlT f ()) b -- ^ formlet   -> ActionM (Result err b, HtmlT f ()) simpleDittoGET = Trans.simpleDittoGET  -- | create @\<form action=action method=\"POST\" enctype=\"application/xxx-form-urlencoded\"\>@-simpleDittoPOST :: (Applicative f)+simpleDittoPOST :: Applicative f   => Text -- ^ action   -> Form ActionM [Param] err (HtmlT f ()) b -- ^ formlet   -> ActionM (Result err b, HtmlT f ())@@ -57,12 +56,6 @@ liftParser' :: (Text -> Either Text a) -> ([Param] -> Either Trans.ScottyFormError a) liftParser' = Trans.liftParser' --- | lift a function which parses lazy @Text@ into a function which parses a @[Param]@--- e.g.------ @--- parserRead :: Read a => [Param] -> Either ScottyFormError a--- parserRead = liftParser readEither--- @-liftParser :: (TL.Text -> Either TL.Text a) -> ([Param] -> Either Trans.ScottyFormError a)+-- | alias for 'liftParser''. Parses strict `Text` from each `Param`.+liftParser :: (Text -> Either Text a) -> ([Param] -> Either Trans.ScottyFormError a) liftParser = Trans.liftParser
src/Web/Scotty/Trans/Form.hs view
@@ -1,38 +1,32 @@-{-# LANGUAGE-    OverloadedStrings-  , MultiParamTypeClasses-  , FlexibleInstances-  , TypeFamilies-  , BangPatterns-#-}--{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# OPTIONS_GHC -fno-warn-orphans -Wno-error=orphans #-}  module Web.Scotty.Trans.Form where +import Data.Bifunctor (first)+import UnliftIO (MonadUnliftIO) import Data.Text (Text)+import Ditto.Backend import Ditto.Core hiding (view) import Ditto.Lucid import Ditto.Types import Lucid (HtmlT, ToHtml (toHtml))+import Lucid.Base (ToHtml (toHtmlRaw)) import Web.Scotty.Trans-import Ditto.Backend import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import Data.Bifunctor (first)-import Lucid.Base (ToHtml (toHtmlRaw)) -instance (ScottyError e, Monad m) => Environment (ActionT e m) [Param] where+instance MonadUnliftIO m => Environment (ActionT m) [Param] where   environment formId = do-    qp <- params-    let !formId' = TL.fromStrict $ encodeFormId formId-    case filter (\(x,_) -> x == formId') qp of+    pp <- pathParams+    qp <- queryParams+    fp <- formParams+    let !formId' = encodeFormId formId+    case filter (\(x, _) -> x == formId') (pp ++ qp ++ fp) of       [] -> pure Missing       xs -> pure (Found xs)  instance FormInput [Param] where   type FileType [Param] = ()-  getInputStrings xs = fmap (TL.unpack . snd) xs+  getInputStrings xs = fmap (T.unpack . snd) xs   getInputFile _ = Left $ commonFormError $ (NoFileFound [("","No support for file uploads")] :: CommonFormError [Param])  instance FormError [Param] ScottyFormError where@@ -55,18 +49,18 @@   toHtmlRaw SFEUnexpectedEmpty = "Unexpected empty query param list"   toHtmlRaw SFEUnexpectedMultiple = "Unexpected multiple query param list" -encQP :: [(a, TL.Text)] -> Text+encQP :: [(a, Text)] -> Text encQP [] = ""-encQP xs = T.intercalate ", " (fmap (TL.toStrict . snd) xs)+encQP xs = T.intercalate ", " (fmap snd xs)  -- | a @ditto@ formlet for @scotty@-type ScottyForm e m a = Form (ActionT e m) [Param] ScottyFormError (HtmlT (ActionT e m) ()) a+type ScottyForm m a = Form (ActionT m) [Param] ScottyFormError (HtmlT (ActionT m) ()) a -ditto :: (Monoid view, Monad m, ScottyError e)+ditto :: Monad m   => ([(Text, Text)] -> view -> view) -- ^ wrap raw form html inside a <form> tag   -> Text -- ^ form name prefix-  -> Form (ActionT e m) [Param] err view a -- ^ the formlet-  -> ActionT e m (Result err a, view)+  -> Form (ActionT m) [Param] err view a -- ^ the formlet+  -> ActionT m (Result err a, view) ditto toForm prefix formlet = do   dittoSingle toForm' prefix formlet   where@@ -74,11 +68,11 @@  -- | a helpful wrapper around 'runForm' dittoSingle-  :: (Monad m, ScottyError e)+  :: Monad m   => ([(Text, Text)] -> view -> view) -- ^ wrap raw form html inside a <form> tag   -> Text -- ^ form name prefix-  -> Form (ActionT e m) [Param] err view a -- ^ the formlet-  -> ActionT e m (Result err a, view)+  -> Form (ActionT m) [Param] err view a -- ^ the formlet+  -> ActionT m (Result err a, view) dittoSingle toForm prefix formlet = do   (View viewf, res) <- runForm prefix formlet   case res of@@ -86,33 +80,33 @@     Ok (Proved _ unProved') -> pure (Ok unProved', toForm [] $ viewf [])  -- | create @\<form action=action method=\"GET\" enctype=\"application/xxx-form-urlencoded\"\>@-simpleDittoGET :: (Applicative f, Monad m, ScottyError e)+simpleDittoGET+  :: (Applicative f, Monad m)   => Text -- ^ action-  -> Form (ActionT e m) [Param] err (HtmlT f ()) b -- ^ formlet-  -> ActionT e m (Result err b, HtmlT f ())+  -> Form (ActionT m) [Param] err (HtmlT f ()) b -- ^ formlet+  -> ActionT m (Result err b, HtmlT f ()) simpleDittoGET action form = ditto (formGenGET action) "ditto" form  -- | create @\<form action=action method=\"POST\" enctype=\"application/xxx-form-urlencoded\"\>@-simpleDittoPOST :: (Applicative f, Monad m, ScottyError e)+simpleDittoPOST+  :: (Applicative f, Monad m)   => Text -- ^ action-  -> Form (ActionT e m) [Param] err (HtmlT f ()) b -- ^ formlet-  -> ActionT e m (Result err b, HtmlT f ())+  -> Form (ActionT m) [Param] err (HtmlT f ()) b -- ^ formlet+  -> ActionT m (Result err b, HtmlT f ()) simpleDittoPOST action form = ditto (formGenPOST action) "ditto" form  -- | lift a function which parses strict @Text@ into a function which parses a @[Param]@ liftParser' :: (Text -> Either Text a) -> ([Param] -> Either ScottyFormError a)-liftParser' f [(_,x)] = first SFEParseError $ f (TL.toStrict x)+liftParser' f [(_, x)] = first SFEParseError $ f x liftParser' _ [] = Left SFEUnexpectedEmpty liftParser' _ _ = Left SFEUnexpectedMultiple --- | lift a function which parses lazy @Text@ into a function which parses a @[Param]@--- e.g.+-- | lift a function which parses strict @Text@ into a function which parses a @[Param]@ -- -- @--- parserRead :: Read a => [Param] -> Either ScottyFormError a--- parserRead = liftParser readEither+-- parserRead :: [Param] -> Either ScottyFormError Int+-- parserRead = liftParser' $ \t ->+--   maybe (Left "not an integer") Right (readMaybe @Int (T.unpack t)) -- @-liftParser :: (TL.Text -> Either TL.Text a) -> ([Param] -> Either ScottyFormError a)-liftParser f [(_,x)] = first (SFEParseError . TL.toStrict) $ f x-liftParser _ [] = Left SFEUnexpectedEmpty-liftParser _ _ = Left SFEUnexpectedMultiple+liftParser :: (Text -> Either Text a) -> ([Param] -> Either ScottyFormError a)+liftParser = liftParser'