| Copyright | (c) 2024 Sean Hess |
|---|---|
| License | BSD3 |
| Maintainer | Sean Hess <seanhess@gmail.com> |
| Stability | experimental |
| Portability | portable |
| Safe Haskell | Safe-Inferred |
| Language | GHC2021 |
Web.Hyperbole
Description
Create fully interactive HTML applications with type-safe serverside Haskell. Inspired by HTMX, Elm, and Phoenix LiveView
- hyperbole.live - documentation and examples
- github - issues and source code
Synopsis
- liveApp :: (ByteString -> ByteString) -> Eff '[Hyperbole, Concurrent, IOE] Response -> Application
- run :: Port -> Application -> IO ()
- type Page es (views :: [Type]) = Eff (Reader (Root views) : es) (View (Root views) ())
- runPage :: (Hyperbole :> es, RunHandlers views es) => Page es views -> Eff es Response
- document :: View DocumentHead () -> ByteString -> ByteString
- quickStartDocument :: ByteString -> ByteString
- data DocumentHead
- quickStart :: View DocumentHead ()
- mobileFriendly :: View DocumentHead ()
- class Route a where
- routeRequest :: (Hyperbole :> es, Route route) => (route -> Eff es Response) -> Eff es Response
- routeUri :: Route a => a -> URI
- route :: Route a => a -> View c () -> View c ()
- data Hyperbole :: Effect
- request :: Hyperbole :> es => Eff es Request
- data Request = Request {}
- respondError :: Hyperbole :> es => ResponseError -> Eff es a
- respondErrorView :: Hyperbole :> es => Text -> View () () -> Eff es a
- notFound :: Hyperbole :> es => Eff es a
- redirect :: Hyperbole :> es => URI -> Eff es a
- class ToQuery a where
- class FromQuery a where
- parseQuery :: QueryData -> Either String a
- query :: (FromQuery a, Hyperbole :> es) => Eff es a
- setQuery :: (ToQuery a, Hyperbole :> es) => a -> Eff es ()
- modifyQuery :: (ToQuery a, FromQuery a, Default a, Hyperbole :> es) => (a -> a) -> Eff es a
- clearQuery :: Hyperbole :> es => Eff es ()
- param :: (FromParam a, Hyperbole :> es) => Param -> Eff es a
- lookupParam :: (FromParam a, Hyperbole :> es) => Param -> Eff es (Maybe a)
- setParam :: (ToParam a, Hyperbole :> es) => Param -> a -> Eff es ()
- deleteParam :: Hyperbole :> es => Param -> Eff es ()
- queryParams :: Hyperbole :> es => Eff es QueryData
- class Session a where
- sessionKey :: Key
- cookiePath :: Maybe Path
- toCookie :: a -> CookieValue
- parseCookie :: CookieValue -> Either String a
- session :: (Session a, Default a, Hyperbole :> es) => Eff es a
- saveSession :: forall a es. (Session a, Hyperbole :> es) => a -> Eff es ()
- lookupSession :: forall a es. (Session a, Hyperbole :> es) => Eff es (Maybe a)
- modifySession :: (Session a, Default a, Hyperbole :> es) => (a -> a) -> Eff es a
- modifySession_ :: (Session a, Default a, Hyperbole :> es) => (a -> a) -> Eff es ()
- deleteSession :: forall a es. (Session a, Hyperbole :> es) => Eff es ()
- pageTitle :: Hyperbole :> es => Text -> Eff es ()
- trigger :: (HyperView id es, HyperViewHandled id view, Hyperbole :> es) => id -> Action id -> Eff (Reader view : es) ()
- pushEvent :: (ToJSON a, Hyperbole :> es) => Text -> a -> Eff es ()
- pushUpdate :: (Hyperbole :> es, ViewId id, ToEncoded (ViewState id), ConcurrencyValue (Concurrency id)) => View id () -> Eff (Reader id : (State (ViewState id) : es)) ()
- class (ViewId id, ViewAction (Action id), ConcurrencyValue (Concurrency id)) => HyperView id es where
- hyper :: forall id ctx. (HyperViewHandled id ctx, ViewId id, ViewState id ~ (), ConcurrencyValue (Concurrency id)) => id -> View id () -> View ctx ()
- hyperState :: forall id ctx. (HyperViewHandled id ctx, ViewId id, ToEncoded (ViewState id), ConcurrencyValue (Concurrency id)) => id -> ViewState id -> View id () -> View ctx ()
- class HasViewId m view where
- viewId :: m view
- button :: ViewAction (Action id) => Action id -> View id () -> View id ()
- search :: ViewAction (Action id) => (Text -> Action id) -> DelayMs -> View id ()
- dropdown :: forall opt id. ViewAction (Action id) => (opt -> Action id) -> opt -> View (Option id opt) () -> View id ()
- option :: forall opt id. (ViewAction (Action id), Eq opt, ToParam opt) => opt -> Text -> View (Option id opt) ()
- data Option id opt
- onClick :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a
- onDblClick :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a
- onMouseEnter :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a
- onMouseLeave :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a
- onInput :: (ViewAction (Action id), Attributable a) => (Text -> Action id) -> DelayMs -> Attributes a -> Attributes a
- onLoad :: (ViewAction (Action id), Attributable a) => Action id -> DelayMs -> Attributes a -> Attributes a
- type DelayMs = Int
- onKeyDown :: (ViewAction (Action id), Attributable a) => Key -> Action id -> Attributes a -> Attributes a
- onKeyUp :: (ViewAction (Action id), Attributable a) => Key -> Action id -> Attributes a -> Attributes a
- data Key
- class FromForm (form :: Type) where
- class FromFormF (f :: (Type -> Type) -> Type) where
- formData :: forall form es. (FromForm form, Hyperbole :> es) => Eff es form
- class GenFields f (form :: (Type -> Type) -> Type) where
- genFields :: form f
- fieldNames :: forall form. GenFields FieldName form => form FieldName
- newtype FieldName a = FieldName {}
- data FormFields id
- type family Field (context :: Type -> Type) a
- data Identity a
- form :: ViewAction (Action id) => Action id -> View (FormFields id) () -> View id ()
- field :: forall (id :: Type) (a :: Type). FieldName a -> View (Input id a) () -> View (FormFields id) ()
- label :: View c () -> View c ()
- input :: forall id a. InputType -> View (Input id a) ()
- checkbox :: forall id a. Bool -> View (Input id a) ()
- radioGroup :: opt -> View (Radio id a opt) () -> View (Input id a) ()
- radio :: forall id a opt. (Eq opt, ToParam opt) => opt -> View (Radio id a opt) ()
- select :: forall opt id a. Eq opt => opt -> View (Option id opt) () -> View (Input id a) ()
- checked :: Attributable a => Bool -> Attributes a -> Attributes a
- textarea :: forall id a. Maybe Text -> View (Input id a) ()
- submit :: View (FormFields id) () -> View (FormFields id) ()
- placeholder :: Attributable h => Text -> Attributes h -> Attributes h
- data InputType
- data Validated a
- = Invalid Text
- | NotInvalid
- | Valid
- isInvalid :: Validated a -> Bool
- validate :: Bool -> Text -> Validated a
- invalidText :: forall a id. Validated a -> View (Input id a) ()
- data QueryData
- class ToParam a where
- toParam :: a -> ParamValue
- class FromParam a where
- parseParam :: ParamValue -> Either String a
- decodeFormValue :: Maybe Text -> Either String a
- class ToEncoded a
- class FromEncoded a
- target :: forall id ctx. (HyperViewHandled id ctx, ViewId id) => id -> ViewState id -> View id () -> View ctx ()
- data Response
- data Root (views :: [Type])
- data ConcurrencyMode
- newtype View c a = View {}
- type Name = Text
- newtype Table c a = Table (View c a)
- type AttValue = Text
- class HasViewId m view where
- viewId :: m view
- class ViewId a where
- class ViewAction a where
- newtype ChildView a = ChildView a
- newtype TableColumns c dt a = TableColumns (Eff '[State [TableColumn c dt]] a)
- newtype TableHead id a = TableHead (View id a)
- data TableColumn c dt = TableColumn {}
- newtype ListItem c a = ListItem (View c a)
- text :: Text -> View c ()
- pre :: Text -> View c ()
- link :: URI -> View c () -> View c ()
- value :: Attributable h => Text -> Attributes h -> Attributes h
- style :: ByteString -> View c ()
- name :: Attributable h => Text -> Attributes h -> Attributes h
- none :: View c ()
- space :: View c ()
- col :: View c () -> View c ()
- row :: View c () -> View c ()
- (@) :: Attributable h => h -> (Attributes h -> Attributes h) -> h
- modAttributes :: Attributable h => (Map Name AttValue -> Map Name AttValue) -> h -> h
- att :: Attributable h => Name -> AttValue -> Attributes h -> Attributes h
- el :: View c () -> View c ()
- tag :: Text -> View c () -> View c ()
- tag' :: Bool -> Text -> View c () -> View c ()
- raw :: Text -> View c ()
- renderLazyByteString :: View () () -> ByteString
- renderText :: View () () -> Text
- class_ :: Attributable h => AttValue -> Attributes h -> Attributes h
- runViewContext :: ctx -> ViewState ctx -> View ctx () -> View c ()
- script' :: ByteString -> View c ()
- type_ :: Attributable h => Text -> Attributes h -> Attributes h
- whenLoading :: Styleable h => (CSS h -> CSS h) -> CSS h -> CSS h
- cssEmbed :: ByteString
- scriptEmbed :: ByteString
- scriptEmbedSourceMap :: ByteString
- scriptLiveReload :: ByteString
- renderBody :: View () () -> Body
- execView :: forall c a. c -> ViewState c -> View c a -> Html a
- code :: Text -> View c ()
- src :: Attributable h => Text -> Attributes h -> Attributes h
- td :: View c () -> View c ()
- table :: [dt] -> TableColumns c dt () -> View c ()
- disabled :: Styleable h => CSS h -> CSS h
- loading :: Styleable h => CSS h -> CSS h
- content :: Attributable h => Text -> Attributes h -> Attributes h
- encodeAction :: ViewAction act => act -> Text
- decodeAction :: ViewAction act => Text -> Maybe act
- $sel:html:View :: View c a -> Eff '[Reader (c, ViewState c)] (Html a)
- context :: forall c. View c (c, ViewState c)
- viewState :: View c (ViewState c)
- runChildView :: ViewState ctx ~ ViewState c => (c -> ctx) -> View ctx () -> View c ()
- encodeViewId :: ViewId id => id -> Text
- decodeViewId :: ViewId id => Text -> Maybe id
- img :: Text -> View c ()
- autofocus :: Attributable h => Attributes h -> Attributes h
- meta :: View c ()
- title :: Text -> View c ()
- httpEquiv :: Attributable h => Text -> Attributes h -> Attributes h
- charset :: Attributable h => Text -> Attributes h -> Attributes h
- script :: Text -> View c ()
- stylesheet :: Text -> View c ()
- nav :: View c () -> View c ()
- usersTable :: View c ()
- tcol :: forall dt c. TableHead c () -> (dt -> View c ()) -> TableColumns c dt ()
- th :: View c () -> TableHead c ()
- ol :: ListItem c () -> View c ()
- ul :: ListItem c () -> View c ()
- li :: View c () -> ListItem c ()
- module Web.Hyperbole.View.Embed
- class (e :: Effect) :> (es :: [Effect])
- data Eff (es :: [Effect]) a
- data URI = URI {}
- uri :: QuasiQuoter
- type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
- class Generic a where
- type family Rep a :: Type -> Type
- class Default a where
- def :: a
- class ToJSON a
- class FromJSON a
Application
liveApp :: (ByteString -> ByteString) -> Eff '[Hyperbole, Concurrent, IOE] Response -> Application Source #
Turn one or more Pages into a Wai Application. Respond using both HTTP and WebSockets
main :: IO () main = do run 3000 $ liveApp quickStartDocument (runPage hello)
run :: Port -> Application -> IO () #
Run an Application on the given port.
This calls runSettings with defaultSettings.
Page
Document
document :: View DocumentHead () -> ByteString -> ByteString Source #
liveApp requires a function which turns an html fragment into an entire html document. Use this to import javascript, css, etc. Use quickStartDocument to get going quickly
app :: Application app = liveApp (document documentHead) (routeRequest router)
quickStartDocument :: ByteString -> ByteString Source #
A simple mobile-friendly document with all required embeds and live reload
liveAppquickStartDocument (routeRequestrouter)
data DocumentHead Source #
Create a custom <head> to use with document. Remember to include at least scriptEmbed!
import Web.Hyperbole (scriptEmbed, cssEmbed) documentHead :: View DocumentHead () documentHead = do title "My Website" script' scriptEmbed style cssEmbed script "custom.js" app :: Application app = liveApp (document documentHead) (routeRequest router)
Instances
| Generic DocumentHead Source # | |
Defined in Web.Hyperbole.Document Associated Types type Rep DocumentHead :: Type -> Type # | |
| ViewId DocumentHead Source # | |
Defined in Web.Hyperbole.Document Associated Types type ViewState DocumentHead Source # Methods toViewId :: DocumentHead -> Encoded Source # parseViewId :: Encoded -> Either String DocumentHead Source # | |
| type Rep DocumentHead Source # | |
| type ViewState DocumentHead Source # | |
Defined in Web.Hyperbole.Document | |
quickStart :: View DocumentHead () Source #
A simple mobile-friendly header with all required embeds and live reload
mobileFriendly :: View DocumentHead () Source #
Set the viewport to handle mobile zoom
Type-Safe Routes
Derive this class to use a sum type as a route. Constructors and Selectors map intuitively to url patterns
data AppRoute
= Main
| Messages
| User UserId
deriving (Eq, Generic)
instance Route AppRoute where
baseRoute = Just Main
>>>routeUri Main/
>>>routeUri (User 9)/user/9
Minimal complete definition
Nothing
Methods
The route to use if attempting to match an empty path
matchRoute :: Path -> Maybe a Source #
Try to match a path to a route
routePath :: a -> Path Source #
Map a route to a path
routeRequest :: (Hyperbole :> es, Route route) => (route -> Eff es Response) -> Eff es Response Source #
Route URL patterns to different pages
type UserId = Int data AppRoute = Main | Messages | User UserId deriving (Eq, Generic) instanceRouteAppRoute where baseRoute = Just Main router :: (Hyperbole:> es) => AppRoute ->EffesResponserouter Messages =runPageMessages.page router (User cid) =runPage$ Users.page cid router Main = do pure $ view $ doel"click a link below to visit a page"routeMessages "Messages"route(User 1) "User 1"route(User 2) "User 2" app :: Application app =liveApp(document documentHead) (routeRequestrouter)
route :: Route a => a -> View c () -> View c () Source #
A hyperlink to another route
>>>route (User 100) id "View User"<a href="/user/100">View User</a>
Hyperbole Effect
data Hyperbole :: Effect Source #
The Hyperbole Effect allows you to access information in the Request, manually respond, and manipulate the Client session and query.
Instances
| type DispatchOf Hyperbole Source # | |
Defined in Web.Hyperbole.Effect.Hyperbole | |
Request
Constructors
| Request | |
Response
respondError :: Hyperbole :> es => ResponseError -> Eff es a Source #
Abort execution and respond with an error
respondErrorView :: Hyperbole :> es => Text -> View () () -> Eff es a Source #
Abort execution and respond with an error view
notFound :: Hyperbole :> es => Eff es a Source #
Abort execution and respond with 404 Not Found
findUser :: (Hyperbole:> es, Users :> es) => Int ->Effes User findUser uid = do mu <- send (LoadUser uid) maybe notFound pure mu userPage :: (Hyperbole:> es, Users :> es) =>Pagees '[] userPage = do user <- findUser 100 -- skipped if user not found pure $ userView user
Query
class ToQuery a where Source #
A page can store state in the browser query string. ToQuery and FromQuery control how a datatype is encoded to a full query string
data Filters = Filters
{ active :: Bool
, term :: Text
}
deriving (Generic, Eq, FromQuery, ToQuery)
>>>QueryData.render $ toQuery $ Filter True "asdf""active=true&search=asdf"
If the value of a field is the same as Default, it will be omitted from the query string
>>>QueryData.render $ toQuery $ Filter True """active=true"
>>>QueryData.render $ toQuery $ Filter False """"
Minimal complete definition
Nothing
Methods
class FromQuery a where Source #
Decode a type from a QueryData. Missing fields are set to def
data Filters = Filters
{ active :: Bool
, term :: Text
}
deriving (Generic, Eq, FromQuery, ToQuery)
>>>parseQuery $ QueryData.parse "active=true&search=asdf"Right (Filters True "asdf")
>>>parseQuery $ QueryData.parse "search=asdf"Right (Filters False "asdf")
Minimal complete definition
Nothing
Methods
parseQuery :: QueryData -> Either String a Source #
default parseQuery :: (Generic a, GFromQuery (Rep a)) => QueryData -> Either String a Source #
setQuery :: (ToQuery a, Hyperbole :> es) => a -> Eff es () Source #
Update the client's querystring to an encoded datatype. See ToQuery
instanceHyperViewTodos es where dataActionTodos = SetSearch Text deriving (Generic,ViewAction)update(SetSearch term) = do let filters = Filters term setQuery filters todos <- loadTodos filters pure $ todosView todos
modifyQuery :: (ToQuery a, FromQuery a, Default a, Hyperbole :> es) => (a -> a) -> Eff es a Source #
lookupParam :: (FromParam a, Hyperbole :> es) => Param -> Eff es (Maybe a) Source #
Parse a single parameter from the query string if available
page :: (Hyperbole:> es, Concurrent :> es, Reader Text :> es) =>Pagees '[SlowReader] page = do pure $hyperSlowReader $ messageView "..."
deleteParam :: Hyperbole :> es => Param -> Eff es () Source #
Delete a single parameter from the query string
Sessions
class Session a where Source #
Configure a data type to persist in the session as a cookie. These are type-indexed, so only one of each can exist in the session
data Preferences = Preferences
{ color :: AppColor
}
deriving (Generic, ToEncoded, FromEncoded, Session)
instance Default Preferences where
def = Preferences White
Minimal complete definition
Nothing
Methods
sessionKey :: Key Source #
Unique key for this Session Type. Defaults to the datatypeName
default sessionKey :: (Generic a, GDatatypeName (Rep a)) => Key Source #
cookiePath :: Maybe Path Source #
By default Sessions are persisted only to the current page. Set to `Just "/"` to make an instance available application-wide
default cookiePath :: Maybe Path Source #
toCookie :: a -> CookieValue Source #
Encode type to a a cookie value
default toCookie :: ToEncoded a => a -> CookieValue Source #
parseCookie :: CookieValue -> Either String a Source #
Decode from a cookie value. Defaults to FromJSON
default parseCookie :: FromEncoded a => CookieValue -> Either String a Source #
Instances
| Session AuthFlow Source # | |
Defined in Web.Hyperbole.Effect.OAuth2 Methods sessionKey :: Key Source # cookiePath :: Maybe Path Source # toCookie :: AuthFlow -> CookieValue Source # parseCookie :: CookieValue -> Either String AuthFlow Source # | |
| Session Authenticated Source # | |
Defined in Web.Hyperbole.Effect.OAuth2 Methods sessionKey :: Key Source # cookiePath :: Maybe Path Source # toCookie :: Authenticated -> CookieValue Source # parseCookie :: CookieValue -> Either String Authenticated Source # | |
session :: (Session a, Default a, Hyperbole :> es) => Eff es a Source #
Load data from a browser cookie. If it doesn't exist, the Default instance is used
data Preferences = Preferences
{ color :: AppColor
}
deriving (Generic, ToEncoded, FromEncoded, Session)
instance Default Preferences where
def = Preferences White
page :: (Hyperbole :> es) => Page es '[Content]
page = do
prefs <- session @Preferences
pure $ el ~ bg prefs.color $ "Custom Background"
saveSession :: forall a es. (Session a, Hyperbole :> es) => a -> Eff es () Source #
Persist datatypes in browser cookies
data Preferences = Preferences
{ color :: AppColor
}
deriving (Generic, ToEncoded, FromEncoded, Session)
instance Default Preferences where
def = Preferences White
instance HyperView Content es where
data Action Content
= SetColor AppColor
deriving (Generic, ViewAction)
update (SetColor clr) = do
let prefs = Preferences clr
saveSession prefs
pure $ el ~ bg prefs.color $ "Custom Background"
lookupSession :: forall a es. (Session a, Hyperbole :> es) => Eff es (Maybe a) Source #
Return a session if it exists
deleteSession :: forall a es. (Session a, Hyperbole :> es) => Eff es () Source #
Remove a single Session from the browser cookies
Control Client
pageTitle :: Hyperbole :> es => Text -> Eff es () Source #
Set the document title
page :: (Hyperbole:> es) =>Pagees '[] page = do pageTitle "MyPageTitle" pure $el"Hello World"
trigger :: (HyperView id es, HyperViewHandled id view, Hyperbole :> es) => id -> Action id -> Eff (Reader view : es) () Source #
pushEvent :: (ToJSON a, Hyperbole :> es) => Text -> a -> Eff es () Source #
Dispatch a custom javascript event. This is emitted on the current hyper view and bubbles up to the document
instanceHyperViewMessage es where dataActionMessage = AlertMe deriving (Generic,ViewAction)updateAlertMe = do pushEvent "server-message" ("hello" :: Text) pure "Sent 'server-message' event"
function listenServerEvents() {
// you can listen on document instead, the event will bubble
Hyperbole.hyperView(Message).addEventListener("server-message", function(e) {
alert("Server Message: " + e.detail)
})
}
pushUpdate :: (Hyperbole :> es, ViewId id, ToEncoded (ViewState id), ConcurrencyValue (Concurrency id)) => View id () -> Eff (Reader id : (State (ViewState id) : es)) () Source #
HyperView
class (ViewId id, ViewAction (Action id), ConcurrencyValue (Concurrency id)) => HyperView id es where Source #
HyperViews are interactive subsections of a Page
Create an instance with a unique view id type and a sum type describing the actions the HyperView supports. The View Id can contain context (a database id, for example)
data Message = Message1 | Message2 deriving (Generic,ViewId) instanceHyperViewMessage es where dataActionMessage = Louder Text deriving (Generic,ViewAction)update(Louder msg) = do let new = msg <> "!" pure $ messageView new
Associated Types
Outline all actions that are permitted in this HyperView
data Action Message = SetMessage Text | ClearMessage deriving (Generic, ViewAction)
type Require id :: [Type] Source #
Include any child hyperviews here. The compiler will make sure that the page knows how to handle them
type Require Messages = '[ChildView]
type Require id = '[]
type Concurrency id :: ConcurrencyMode Source #
Control how overlapping actions are handled. Drop by default
type Concurrency Autocomplete = Replace
type Concurrency id = Drop
Methods
update :: Hyperbole :> es => Action id -> Eff (Reader id : (State (ViewState id) : es)) (View id ()) Source #
Specify how the view should be updated for each Action
update (SetMessage msg) = pure $ messageView msg update ClearMessage = pure $ messageView ""
hyper :: forall id ctx. (HyperViewHandled id ctx, ViewId id, ViewState id ~ (), ConcurrencyValue (Concurrency id)) => id -> View id () -> View ctx () Source #
hyperState :: forall id ctx. (HyperViewHandled id ctx, ViewId id, ToEncoded (ViewState id), ConcurrencyValue (Concurrency id)) => id -> ViewState id -> View id () -> View ctx () Source #
class HasViewId m view where Source #
Access the viewId in a View or update
data LazyData = LazyData TaskId deriving (Generic,ViewId) instance (Debug :> es, GenRandom :> es) =>HyperViewLazyData es where dataActionLazyData = Details deriving (Generic,ViewAction)updateDetails = do LazyData taskId <-viewIdtask <- pretendLoadTask taskId pure $ viewTaskDetails task
Interactive Elements
dropdown :: forall opt id. ViewAction (Action id) => (opt -> Action id) -> opt -> View (Option id opt) () -> View id () Source #
Type-safe dropdown. Sends (opt -> Action id) when selected. The default will be selected.
▶️ Filter
familyDropdown :: Filters -> View Languages ()
familyDropdown filters =
dropdown SetFamily filters.family ~ border 1 . pad 10 $ do
option Nothing "Any"
option (Just ObjectOriented) "Object Oriented"
option (Just Functional) "Functional"
option :: forall opt id. (ViewAction (Action id), Eq opt, ToParam opt) => opt -> Text -> View (Option id opt) () Source #
An option for a dropdown or select
The view context for an option
Instances
| Generic (Option id opt) Source # | |
| (ToParam id, ToParam opt, FromParam id, FromParam opt) => ViewId (Option id opt) Source # | |
| type Rep (Option id opt) Source # | |
Defined in Web.Hyperbole.HyperView.Input type Rep (Option id opt) = D1 ('MetaData "Option" "Web.Hyperbole.HyperView.Input" "hyperbole-0.6.0-inplace" 'False) (C1 ('MetaCons "Option" 'PrefixI 'True) (S1 ('MetaSel ('Just "id") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 id) :*: S1 ('MetaSel ('Just "defaultOption") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 opt))) | |
| type ViewState (Option id opt) Source # | |
Defined in Web.Hyperbole.HyperView.Input | |
Events
onClick :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a Source #
onDblClick :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a Source #
onMouseEnter :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a Source #
onMouseLeave :: (ViewAction (Action id), Attributable a) => Action id -> Attributes a -> Attributes a Source #
onInput :: (ViewAction (Action id), Attributable a) => (Text -> Action id) -> DelayMs -> Attributes a -> Attributes a Source #
Run an action when the user types into an input or textarea.
WARNING: a short delay can result in poor performance. It is not recommended to set the value of the input
input (onInput OnSearch) 250 id
onLoad :: (ViewAction (Action id), Attributable a) => Action id -> DelayMs -> Attributes a -> Attributes a Source #
onKeyDown :: (ViewAction (Action id), Attributable a) => Key -> Action id -> Attributes a -> Attributes a Source #
onKeyUp :: (ViewAction (Action id), Attributable a) => Key -> Action id -> Attributes a -> Attributes a Source #
Constructors
| ArrowDown | |
| ArrowUp | |
| ArrowLeft | |
| ArrowRight | |
| Enter | |
| Space | |
| Escape | |
| Alt | |
| CapsLock | |
| Control | |
| Fn | |
| Meta | |
| Shift | |
| OtherKey Text |
Type-Safe Forms
class FromForm (form :: Type) where Source #
Simple types that be decoded from form data
data ContactForm = ContactForm
{ name :: Text
, age :: Int
, isFavorite :: Bool
, planet :: Planet
, moon :: Moon
}
deriving (Generic, FromForm)
Minimal complete definition
Nothing
Methods
class FromFormF (f :: (Type -> Type) -> Type) where Source #
A Higher-Kinded type that can be parsed from a Form
data UserForm f = UserForm
{ user :: Field f User
, age :: Field f Int
, pass1 :: Field f Text
, pass2 :: Field f Text
}
deriving (Generic, FromFormF, GenFields Validated, GenFields FieldName)
Minimal complete definition
Nothing
formData :: forall form es. (FromForm form, Hyperbole :> es) => Eff es form Source #
Parse a full type from a submitted form body
class GenFields f (form :: (Type -> Type) -> Type) where Source #
Generate a Higher Kinded record with all selectors filled with default values. See GenField
data UserForm f = UserForm
{ user :: Field f User
, age :: Field f Int
, pass1 :: Field f Text
, pass2 :: Field f Text
}
deriving (Generic, FromFormF, GenFields Validated, GenFields FieldName)
newContactForm ::ViewNewContact () newContactForm = do row ~ pad 10 . gap 10 . border 1 $ do target Contacts () $ do contactForm AddUser (genFields :: ContactForm Maybe) col $ do spacebuttonCloseForm ~ btnLight $ "Cancel"
Minimal complete definition
Nothing
fieldNames :: forall form. GenFields FieldName form => form FieldName Source #
Generate FieldNames for a form
▶️ Forms
data TodoForm f = TodoForm
{ task :: Field f Text
}
deriving (Generic, FromFormF, GenFields FieldName)
todoForm :: FilterTodo -> View AllTodos ()
todoForm filt = do
let f :: TodoForm FieldName = fieldNames
row ~ border 1 $ do
el ~ pad 8 $ do
button (ToggleAll filt) Icon.chevronDown ~ width 32 . hover (color Primary)
form SubmitTodo ~ grow $ do
field f.task $ do
input TextInput ~ pad 12 @ placeholder "What needs to be done?" . value ""Form FieldName. This is embeded as the name attribute, and refers to the key need to parse the form when submitted. See fieldNames
Instances
| IsString (FieldName a) Source # | |
Defined in Web.Hyperbole.HyperView.Forms Methods fromString :: String -> FieldName a # | |
| Show (FieldName a) Source # | |
| FromParam (FieldName a) Source # | |
Defined in Web.Hyperbole.HyperView.Forms Methods parseParam :: ParamValue -> Either String (FieldName a) Source # decodeFormValue :: Maybe Text -> Either String (FieldName a) Source # | |
| ToParam (FieldName a) Source # | |
Defined in Web.Hyperbole.HyperView.Forms Methods toParam :: FieldName a -> ParamValue Source # | |
| GenField (FieldName a) Source # | |
| type Field (FieldName :: Type -> Type) a Source # | |
data FormFields id Source #
Context that allows form fields
Instances
| Generic (FormFields id) Source # | |
Defined in Web.Hyperbole.HyperView.Forms Associated Types type Rep (FormFields id) :: Type -> Type # Methods from :: FormFields id -> Rep (FormFields id) x # to :: Rep (FormFields id) x -> FormFields id # | |
| ViewId id => ViewId (FormFields id) Source # | |
Defined in Web.Hyperbole.HyperView.Forms Associated Types type ViewState (FormFields id) Source # Methods toViewId :: FormFields id -> Encoded Source # parseViewId :: Encoded -> Either String (FormFields id) Source # | |
| type Rep (FormFields id) Source # | |
Defined in Web.Hyperbole.HyperView.Forms type Rep (FormFields id) = D1 ('MetaData "FormFields" "Web.Hyperbole.HyperView.Forms" "hyperbole-0.6.0-inplace" 'True) (C1 ('MetaCons "FormFields" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 id))) | |
| type ViewState (FormFields id) Source # | |
Defined in Web.Hyperbole.HyperView.Forms | |
type family Field (context :: Type -> Type) a Source #
Field allows a Higher Kinded Form to reuse the same selectors for form parsing, generating html forms, and validation
Field Identity Text ~ Text Field Maybe Text ~ Maybe Text
Identity functor and monad. (a non-strict monad)
Since: base-4.8.0.0
Instances
Form View
form :: ViewAction (Action id) => Action id -> View (FormFields id) () -> View id () Source #
Type-safe <form>. Calls (Action id) on submit
formView ::ViewAddContact () formView = do form Submit ~ gap 15 . pad 10 . flexCol $ doel~ Style.h1 $ "Add Contact" -- Make sure these names match the field names used by FormParse / formData field "name" $ do label $ do text "Contact Name" input Usernameplaceholder "contact name" ~ Style.input field "age" $ do label $ do text "Age" input Numberplaceholder "age" . value "0" ~ Style.input field "isFavorite" $ do label $ do row ~ gap 10 $ do checkbox False ~ width 32 text "Favorite?" col ~ gap 5 $ doel$ text "Planet" field "planet" $ do radioGroup Earth $ do planet Mercury planet Venus planet Earth planet Mars field "moon" $ do label $ do text "Moon" select Callisto ~ Style.input $ do option Titan "Titan" option Europa "Europa" option Callisto "Callisto" option Mimas "Mimas" submit "Submit" ~ btn where planet val = label ~ flexRow . gap 10 $ do radio val ~ width 32 text (pack (show val))
field :: forall (id :: Type) (a :: Type). FieldName a -> View (Input id a) () -> View (FormFields id) () Source #
select :: forall opt id a. Eq opt => opt -> View (Option id opt) () -> View (Input id a) () Source #
checked :: Attributable a => Bool -> Attributes a -> Attributes a Source #
Set checkbox = checked via the client (VDOM doesn't work)
submit :: View (FormFields id) () -> View (FormFields id) () Source #
Button that submits the form
placeholder :: Attributable h => Text -> Attributes h -> Attributes h Source #
Choose one for inputs to give the browser autocomplete hints
Validation
Validation results for a Form. See validate
data UserForm f = UserForm
{ user :: Field f User
, age :: Field f Int
, pass1 :: Field f Text
, pass2 :: Field f Text
}
deriving (Generic, FromFormF, GenFields Validated, GenFields FieldName)
validateForm :: UserForm Identity -> UserForm Validated
validateForm u =
UserForm
{ user = validateUser u.user
, age = validateAge u.age
, pass1 = validatePass u.pass1 u.pass2
, pass2 = NotInvalid
}
validateAge :: Int -> Validated Int
validateAge a =
validate (a < 20) "User must be at least 20 years old"
Constructors
| Invalid Text | |
| NotInvalid | |
| Valid |
validate :: Bool -> Text -> Validated a Source #
specify a check for a Validation
validateAge :: Int -> Validated Int validateAge a = validate (a < 20) "User must be at least 20 years old"
Query Param Encoding
Key-value store for query params and sessions
class ToParam a where Source #
sessions, forms, and querys all encode data as query strings. ToParam and FromParam control how a datatype is encoded to a parameter.
-
This is equivalent to Web.HttpApiData, which is missing some instances and has some strange defaults
data AppColor = White | Red | Green deriving (Show, Generic,ToParam,FromParam)
Minimal complete definition
Nothing
Methods
toParam :: a -> ParamValue Source #
Instances
class FromParam a where Source #
Decode data from a query, session, or form parameter value
data AppColor = White | Red | Green deriving (Show, Generic,ToParam,FromParam)
Minimal complete definition
Nothing
Methods
parseParam :: ParamValue -> Either String a Source #
default parseParam :: (Generic a, GFromJSON Zero (Rep a)) => ParamValue -> Either String a Source #
Instances
Custom Encoding for embedding into web documents. Noteably used for ViewId and ViewAction
Instances
| ToEncoded Encoded Source # | |
| ToEncoded ParamValue Source # | |
Defined in Web.Hyperbole.Data.Encoded Methods toEncoded :: ParamValue -> Encoded Source # | |
| ToEncoded AuthFlow Source # | |
| ToEncoded Authenticated Source # | |
Defined in Web.Hyperbole.Effect.OAuth2 Methods toEncoded :: Authenticated -> Encoded Source # | |
| ToEncoded ConcurrencyMode Source # | |
Defined in Web.Hyperbole.HyperView.Types Methods toEncoded :: ConcurrencyMode -> Encoded Source # | |
| ToEncoded Text Source # | |
| ToEncoded () Source # | |
Defined in Web.Hyperbole.Data.Encoded | |
| ToEncoded Int Source # | |
| ToJSON a => ToEncoded (JSON a) Source # | |
class FromEncoded a Source #
Custom Encoding for embedding into web documents. Noteably used for ViewId and ViewAction
Instances
| FromEncoded Encoded Source # | |
Defined in Web.Hyperbole.Data.Encoded | |
| FromEncoded ParamValue Source # | |
Defined in Web.Hyperbole.Data.Encoded Methods parseEncoded :: Encoded -> Either String ParamValue Source # | |
| FromEncoded AuthFlow Source # | |
Defined in Web.Hyperbole.Effect.OAuth2 | |
| FromEncoded Authenticated Source # | |
Defined in Web.Hyperbole.Effect.OAuth2 Methods parseEncoded :: Encoded -> Either String Authenticated Source # | |
| FromEncoded ConcurrencyMode Source # | |
Defined in Web.Hyperbole.HyperView.Types Methods parseEncoded :: Encoded -> Either String ConcurrencyMode Source # | |
| FromEncoded Text Source # | |
Defined in Web.Hyperbole.Data.Encoded | |
| FromEncoded () Source # | |
Defined in Web.Hyperbole.Data.Encoded | |
| FromEncoded Int Source # | |
Defined in Web.Hyperbole.Data.Encoded | |
| FromJSON a => FromEncoded (JSON a) Source # | |
Defined in Web.Hyperbole.Data.JSON | |
Advanced
target :: forall id ctx. (HyperViewHandled id ctx, ViewId id) => id -> ViewState id -> View id () -> View ctx () Source #
Allow inputs to trigger actions for a different view
targetView ::ViewControls () targetView = do target Targeted () $ dobutton(SetMessage "Targeted!") ~ btn $ "Target SetMessage"
A processed response for the client, which might be a ResponseError
data Root (views :: [Type]) Source #
The top-level view returned by a Page. It carries a type-level list of every HyperView used in our Page so the compiler can check our work and wire everything together.
Instances
| Generic (Action (Root views)) Source # | |
| Generic (Root views) Source # | |
| ViewAction (Action (Root views)) Source # | |
| ViewId (Root views) Source # | |
| HyperView (Root views) es Source # | |
Defined in Web.Hyperbole.HyperView.Types | |
| type Rep (Action (Root views)) Source # | |
| type Rep (Root views) Source # | |
| data Action (Root views) Source # | |
Defined in Web.Hyperbole.HyperView.Types | |
| type Concurrency (Root views) Source # | |
Defined in Web.Hyperbole.HyperView.Types | |
| type Require (Root views) Source # | |
Defined in Web.Hyperbole.HyperView.Types | |
| type ViewState (Root views) Source # | |
Defined in Web.Hyperbole.HyperView.Types | |
data ConcurrencyMode Source #
Constructors
| Drop | Do not send any actions that occur while one is active. Prevents double-submitting writes or expensive operations |
| Replace | Ignore the results of older actions in favor of new ones. Use for read-only views with fast-firing interactions, like autocomplete, sliders, etc |
Instances
| Generic ConcurrencyMode Source # | |
Defined in Web.Hyperbole.HyperView.Types Associated Types type Rep ConcurrencyMode :: Type -> Type # Methods from :: ConcurrencyMode -> Rep ConcurrencyMode x # to :: Rep ConcurrencyMode x -> ConcurrencyMode # | |
| FromEncoded ConcurrencyMode Source # | |
Defined in Web.Hyperbole.HyperView.Types Methods parseEncoded :: Encoded -> Either String ConcurrencyMode Source # | |
| ToEncoded ConcurrencyMode Source # | |
Defined in Web.Hyperbole.HyperView.Types Methods toEncoded :: ConcurrencyMode -> Encoded Source # | |
| ConcurrencyValue 'Drop Source # | |
Defined in Web.Hyperbole.HyperView.Types Methods | |
| ConcurrencyValue 'Replace Source # | |
Defined in Web.Hyperbole.HyperView.Types Methods | |
| type Rep ConcurrencyMode Source # | |
Exports
View
Instances
| HasViewId (View ctx :: Type -> Type) (ctx :: Type) Source # | |
Defined in Web.Hyperbole.View.Types | |
| Applicative (View ctx) Source # | |
| Functor (View c) Source # | |
| Monad (View ctx) Source # | |
| IsString (View c ()) Source # | |
Defined in Web.Hyperbole.View.Types Methods fromString :: String -> View c () # | |
| Attributable (View c a) Source # | |
Defined in Web.Hyperbole.View.Types | |
| Styleable (View c a) Source # | |
| Styleable (TableColumns c dt () -> View c ()) Source # | |
Defined in Web.Hyperbole.View.Tag Methods (~) :: (TableColumns c dt () -> View c ()) -> (CSS (TableColumns c dt () -> View c ()) -> CSS (TableColumns c dt () -> View c ())) -> TableColumns c dt () -> View c () # modCSS :: ([Rule] -> [Rule]) -> (TableColumns c dt () -> View c ()) -> TableColumns c dt () -> View c () # | |
class HasViewId m view where Source #
Access the viewId in a View or update
data LazyData = LazyData TaskId deriving (Generic,ViewId) instance (Debug :> es, GenRandom :> es) =>HyperViewLazyData es where dataActionLazyData = Details deriving (Generic,ViewAction)updateDetails = do LazyData taskId <-viewIdtask <- pretendLoadTask taskId pure $ viewTaskDetails task
A unique identifier for a HyperView
data Message = Message1 | Message2
deriving (Generic, ViewId)
Minimal complete definition
Nothing
Methods
toViewId :: a -> Encoded Source #
parseViewId :: Encoded -> Either String a Source #
default parseViewId :: (Generic a, GFromEncoded (Rep a)) => Encoded -> Either String a Source #
Instances
| ViewId DocumentHead Source # | |
Defined in Web.Hyperbole.Document Associated Types type ViewState DocumentHead Source # Methods toViewId :: DocumentHead -> Encoded Source # parseViewId :: Encoded -> Either String DocumentHead Source # | |
| ViewId Metadata Source # | |
| ViewId () Source # | |
| ViewId id => ViewId (FormFields id) Source # | |
Defined in Web.Hyperbole.HyperView.Forms Associated Types type ViewState (FormFields id) Source # Methods toViewId :: FormFields id -> Encoded Source # parseViewId :: Encoded -> Either String (FormFields id) Source # | |
| ViewId (Root views) Source # | |
| (ViewId a, FromParam a, ToParam a) => ViewId (ChildView a) Source # | |
| (ViewId id, FromParam id, ToParam id) => ViewId (Input id a) Source # | |
| (ToParam id, ToParam opt, FromParam id, FromParam opt) => ViewId (Option id opt) Source # | |
class ViewAction a where Source #
Define every action possible for a given HyperView
instanceHyperViewMessage es where dataActionMessage = Louder Text deriving (Generic,ViewAction)update(Louder msg) = do let new = msg <> "!" pure $ messageView new
Minimal complete definition
Nothing
Methods
toAction :: a -> Encoded Source #
parseAction :: Encoded -> Either String a Source #
default parseAction :: (Generic a, GFromEncoded (Rep a)) => Encoded -> Either String a Source #
Instances
| ViewAction () Source # | |
| ViewAction (Action (Root views)) Source # | |
| ViewAction (Action ()) Source # | |
Constructors
| ChildView a |
newtype TableColumns c dt a Source #
Constructors
| TableColumns (Eff '[State [TableColumn c dt]] a) |
Instances
newtype TableHead id a Source #
Instances
| Applicative (TableHead id) Source # | |
Defined in Web.Hyperbole.View.Tag | |
| Functor (TableHead id) Source # | |
| Monad (TableHead id) Source # | |
| Styleable (TableHead id a) Source # | |
data TableColumn c dt Source #
Constructors
| TableColumn | |
value :: Attributable h => Text -> Attributes h -> Attributes h Source #
style :: ByteString -> View c () Source #
name :: Attributable h => Text -> Attributes h -> Attributes h Source #
(@) :: Attributable h => h -> (Attributes h -> Attributes h) -> h infixl 5 #
Apply an attribute to some html
el @ att "id" "main-content" $ do tag "img" @ att "src" "logo.png" tag "input" @ placeholder "message" ~ border 1
modAttributes :: Attributable h => (Map Name AttValue -> Map Name AttValue) -> h -> h #
att :: Attributable h => Name -> AttValue -> Attributes h -> Attributes h #
renderLazyByteString :: View () () -> ByteString Source #
renderText :: View () () -> Text Source #
class_ :: Attributable h => AttValue -> Attributes h -> Attributes h #
type_ :: Attributable h => Text -> Attributes h -> Attributes h Source #
whenLoading :: Styleable h => (CSS h -> CSS h) -> CSS h -> CSS h Source #
Apply CSS only when a request is in flight. See Example.Page.Contact
contactEditView :: User ->ViewContact () contactEditView u = doelcontactLoading ~ display None . whenLoading flexColel(contactEdit ViewContact Save u) ~ whenLoading (display None)
renderBody :: View () () -> Body Source #
src :: Attributable h => Text -> Attributes h -> Attributes h Source #
table :: [dt] -> TableColumns c dt () -> View c () Source #
Create a type safe data table by specifying columns
data User = User {name :: Text, email :: Text}
usersTable :: [User] -> View c ()
usersTable us = do
table us $ do
tcol (th "Name" ~ hd) $ \u -> td ~ cell $ text u.name
tcol (th "Email" ~ hd) $ \u -> td ~ cell $ text u.email
where
hd = cell . bold
cell :: (Styleable h) => CSS h -> CSS h
cell = pad 4 . border 1content :: Attributable h => Text -> Attributes h -> Attributes h Source #
encodeAction :: ViewAction act => act -> Text Source #
decodeAction :: ViewAction act => Text -> Maybe act Source #
encodeViewId :: ViewId id => id -> Text Source #
autofocus :: Attributable h => Attributes h -> Attributes h Source #
httpEquiv :: Attributable h => Text -> Attributes h -> Attributes h Source #
charset :: Attributable h => Text -> Attributes h -> Attributes h Source #
stylesheet :: Text -> View c () Source #
usersTable :: View c () Source #
ol :: ListItem c () -> View c () Source #
List elements do not include any inherent styling but are useful for accessibility. See list.
ol id $ do let nums = list Decimal li nums "one" li nums "two" li nums "three"
Embeds
Embedded CSS and Javascript to include in your document function. See quickStartDocument
module Web.Hyperbole.View.Embed
Effectful
class (e :: Effect) :> (es :: [Effect]) #
A constraint that requires that a particular effect e is a member of the
type-level list es. This is used to parameterize an Eff
computation over an arbitrary list of effects, so long as e is somewhere
in the list.
For example, a computation that only needs access to a mutable value of type
Integer would have the following type:
StateInteger:>es =>Effes ()
Instances
| (TypeError (('Text "There is no handler for '" ':<>: 'ShowType e) ':<>: 'Text "' in the context") :: Constraint) => e :> ('[] :: [Effect]) | |
Defined in Effectful.Internal.Effect Methods reifyIndex :: Int # | |
| e :> (e ': es) | |
Defined in Effectful.Internal.Effect Methods reifyIndex :: Int # | |
| e :> es => e :> (x ': es) | |
Defined in Effectful.Internal.Effect Methods reifyIndex :: Int # | |
The Eff monad provides the implementation of a computation that performs
an arbitrary set of effects. In , Eff es aes is a type-level list that
contains all the effects that the computation may perform. For example, a
computation that produces an Integer by consuming a String from the
global environment and acting upon a single mutable value of type Bool
would have the following type:
(ReaderString:>es,StateBool:>es) =>EffesInteger
Abstracting over the list of effects with (:>):
- Allows the computation to be used in functions that may perform other effects.
- Allows the effects to be handled in any order.
Instances
| IOE :> es => MonadBaseControl IO (Eff es) | Instance included for compatibility with existing code. Usage of Note: the unlifting strategy for |
| IOE :> es => MonadBase IO (Eff es) | Instance included for compatibility with existing code. Usage of |
Defined in Effectful.Internal.Monad | |
| ViewState view ~ st => HasViewId (Eff (Reader view ': (State st ': es)) :: Type -> Type) (view :: Type) Source # | |
| Fail :> es => MonadFail (Eff es) | |
Defined in Effectful.Internal.Monad | |
| MonadFix (Eff es) | |
Defined in Effectful.Internal.Monad | |
| IOE :> es => MonadIO (Eff es) | |
Defined in Effectful.Internal.Monad | |
| NonDet :> es => Alternative (Eff es) | Since: effectful-core-2.2.0.0 |
| Applicative (Eff es) | |
| Functor (Eff es) | |
| Monad (Eff es) | |
| NonDet :> es => MonadPlus (Eff es) | Since: effectful-core-2.2.0.0 |
| MonadCatch (Eff es) | |
Defined in Effectful.Internal.Monad | |
| MonadMask (Eff es) | |
Defined in Effectful.Internal.Monad Methods mask :: HasCallStack => ((forall a. Eff es a -> Eff es a) -> Eff es b) -> Eff es b # uninterruptibleMask :: HasCallStack => ((forall a. Eff es a -> Eff es a) -> Eff es b) -> Eff es b # generalBracket :: HasCallStack => Eff es a -> (a -> ExitCase b -> Eff es c) -> (a -> Eff es b) -> Eff es (b, c) # | |
| MonadThrow (Eff es) | |
Defined in Effectful.Internal.Monad Methods throwM :: (HasCallStack, Exception e) => e -> Eff es a # | |
| IOE :> es => MonadUnliftIO (Eff es) | Instance included for compatibility with existing code. Usage of Note: the unlifting strategy for |
Defined in Effectful.Internal.Monad | |
| Prim :> es => PrimMonad (Eff es) | |
| Monoid a => Monoid (Eff es a) | |
| Semigroup a => Semigroup (Eff es a) | |
| type PrimState (Eff es) | |
Defined in Effectful.Internal.Monad | |
| type StM (Eff es) a | |
Defined in Effectful.Internal.Monad | |
Other
Represents a general universal resource identifier using its component parts.
For example, for the URI
foo://anonymous@www.haskell.org:42/ghc?query#frag
the components are:
Constructors
| URI | |
Instances
| Data URI | |
Defined in Network.URI Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> URI -> c URI # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c URI # dataTypeOf :: URI -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c URI) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c URI) # gmapT :: (forall b. Data b => b -> b) -> URI -> URI # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> URI -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> URI -> r # gmapQ :: (forall d. Data d => d -> u) -> URI -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> URI -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> URI -> m URI # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> URI -> m URI # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> URI -> m URI # | |
| Generic URI | |
| Show URI | |
| NFData URI | |
Defined in Network.URI | |
| Eq URI | |
| Ord URI | |
| FromParam URI Source # | |
Defined in Web.Hyperbole.Data.Param Methods parseParam :: ParamValue -> Either String URI Source # | |
| ToParam URI Source # | |
Defined in Web.Hyperbole.Data.Param Methods toParam :: URI -> ParamValue Source # | |
| FromJSON URI | Since: aeson-2.2.0.0 |
Defined in Data.Aeson.Types.FromJSON | |
| FromJSONKey URI | Since: aeson-2.2.0.0 |
Defined in Data.Aeson.Types.FromJSON | |
| ToJSON URI | Since: aeson-2.2.0.0 |
| ToJSONKey URI | Since: aeson-2.2.0.0 |
Defined in Data.Aeson.Types.ToJSON | |
| Lift URI | |
| type Rep URI | |
Defined in Network.URI type Rep URI = D1 ('MetaData "URI" "Network.URI" "ntwrk-r-2.6.4.2-30ccded6" 'False) (C1 ('MetaCons "URI" 'PrefixI 'True) ((S1 ('MetaSel ('Just "uriScheme") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String) :*: S1 ('MetaSel ('Just "uriAuthority") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe URIAuth))) :*: (S1 ('MetaSel ('Just "uriPath") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String) :*: (S1 ('MetaSel ('Just "uriQuery") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String) :*: S1 ('MetaSel ('Just "uriFragment") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 String))))) | |
uri :: QuasiQuoter #
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived #
The WAI application.
Note that, since WAI 3.0, this type is structured in continuation passing
style to allow for proper safe resource handling. This was handled in the
past via other means (e.g., ResourceT). As a demonstration:
app :: Application
app req respond = bracket_
(putStrLn "Allocating scarce resource")
(putStrLn "Cleaning up")
(respond $ responseLBS status200 [] "Hello World")
Representable types of kind *.
This class is derivable in GHC with the DeriveGeneric flag on.
A Generic instance must satisfy the following laws:
from.to≡idto.from≡id
Instances
type family Rep a :: Type -> Type #
Generic representation type
Instances
A class for types with a default value.
Minimal complete definition
Nothing
Instances
A type that can be converted to JSON.
Instances in general must specify toJSON and should (but don't need
to) specify toEncoding.
An example type and instance:
-- Allow ourselves to writeTextliterals. {-# LANGUAGE OverloadedStrings #-} data Coord = Coord { x :: Double, y :: Double } instanceToJSONCoord wheretoJSON(Coord x y) =object["x".=x, "y".=y]toEncoding(Coord x y) =pairs("x".=x<>"y".=y)
Instead of manually writing your ToJSON instance, there are two options
to do it automatically:
- Data.Aeson.TH provides Template Haskell functions which will derive an instance at compile time. The generated instance is optimized for your type so it will probably be more efficient than the following option.
- The compiler can provide a default generic implementation for
toJSON.
To use the second, simply add a deriving clause to your
datatype and declare a GenericToJSON instance. If you require nothing other than
defaultOptions, it is sufficient to write (and this is the only
alternative where the default toJSON implementation is sufficient):
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data Coord = Coord { x :: Double, y :: Double } deriving Generic
instance ToJSON Coord where
toEncoding = genericToEncoding defaultOptions
or more conveniently using the DerivingVia extension
deriving viaGenericallyCoord instanceToJSONCoord
If on the other hand you wish to customize the generic decoding, you have to implement both methods:
customOptions =defaultOptions{fieldLabelModifier=maptoUpper} instanceToJSONCoord wheretoJSON=genericToJSONcustomOptionstoEncoding=genericToEncodingcustomOptions
Previous versions of this library only had the toJSON method. Adding
toEncoding had two reasons:
toEncodingis more efficient for the common case that the output oftoJSONis directly serialized to aByteString. Further, expressing either method in terms of the other would be non-optimal.- The choice of defaults allows a smooth transition for existing users:
Existing instances that do not define
toEncodingstill compile and have the correct semantics. This is ensured by making the default implementation oftoEncodingusetoJSON. This produces correct results, but since it performs an intermediate conversion to aValue, it will be less efficient than directly emitting anEncoding. (this also means that specifying nothing more thaninstance ToJSON Coordwould be sufficient as a generically decoding instance, but there probably exists no good reason to not specifytoEncodingin new instances.)
Instances
A type that can be converted from JSON, with the possibility of failure.
In many cases, you can get the compiler to generate parsing code for you (see below). To begin, let's cover writing an instance by hand.
There are various reasons a conversion could fail. For example, an
Object could be missing a required key, an Array could be of
the wrong size, or a value could be of an incompatible type.
The basic ways to signal a failed conversion are as follows:
failyields a custom error message: it is the recommended way of reporting a failure;empty(ormzero) is uninformative: use it when the error is meant to be caught by some(;<|>)typeMismatchcan be used to report a failure when the encountered value is not of the expected JSON type;unexpectedis an appropriate alternative when more than one type may be expected, or to keep the expected type implicit.
prependFailure (or modifyFailure) add more information to a parser's
error messages.
An example type and instance using typeMismatch and prependFailure:
-- Allow ourselves to writeTextliterals. {-# LANGUAGE OverloadedStrings #-} data Coord = Coord { x :: Double, y :: Double } instanceFromJSONCoord whereparseJSON(Objectv) = Coord<$>v.:"x"<*>v.:"y" -- We do not expect a non-Objectvalue here. -- We could useemptyto fail, buttypeMismatch-- gives a much more informative error message.parseJSONinvalid =prependFailure"parsing Coord failed, " (typeMismatch"Object" invalid)
For this common case of only being concerned with a single
type of JSON value, the functions withObject, withScientific, etc.
are provided. Their use is to be preferred when possible, since
they are more terse. Using withObject, we can rewrite the above instance
(assuming the same language extension and data type) as:
instanceFromJSONCoord whereparseJSON=withObject"Coord" $ \v -> Coord<$>v.:"x"<*>v.:"y"
Instead of manually writing your FromJSON instance, there are two options
to do it automatically:
- Data.Aeson.TH provides Template Haskell functions which will derive an instance at compile time. The generated instance is optimized for your type so it will probably be more efficient than the following option.
- The compiler can provide a default generic implementation for
parseJSON.
To use the second, simply add a deriving clause to your
datatype and declare a GenericFromJSON instance for your datatype without giving
a definition for parseJSON.
For example, the previous example can be simplified to just:
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
data Coord = Coord { x :: Double, y :: Double } deriving Generic
instance FromJSON Coord
or using the DerivingVia extension
deriving viaGenericallyCoord instanceFromJSONCoord
The default implementation will be equivalent to
parseJSON = ; if you need different
options, you can customize the generic decoding by defining:genericParseJSON defaultOptions
customOptions =defaultOptions{fieldLabelModifier=maptoUpper} instanceFromJSONCoord whereparseJSON=genericParseJSONcustomOptions