Webgear off-label experimentation
Some quick 'off-label' experimentation with WebGear (a high-performance framework to build composable, type-safe HTTP APIs. It is designed to make common API development tasks easy. It is also easily extensible to add components needed by your project. – haskell-webgear/webgear). Also see earlier Webgear post.
Introduction
Experimenting with Lucid templating on the server and automatic recompilation for changes to the Haskell source code.
The Hello World example from Webgear is used as a starting point and renamed to webgear-example-hello-lucid
.
flake.nix
The program name has been changed and the Haskell package hsPkgs.ghcid
is added to buildInputs
.
1 {
2 description = "WebGear example project + Lucid - Hello World";
3
4 inputs = {
5 nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
6 flake-utils.url = "github:numtide/flake-utils";
7 gitignore = {
8 url = "github:hercules-ci/gitignore.nix";
9 # Use the same nixpkgs
10 inputs.nixpkgs.follows = "nixpkgs";
11 };
12 };
13
14 outputs = { self, nixpkgs, flake-utils, gitignore }:
15 flake-utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" ] (system:
16 let
17 pkgs = import nixpkgs {
18 inherit system;
19 overlays = [ haskellOverlay ];
20 };
21 ghcVersion = "ghc924";
22 hsPkgs = pkgs.haskell.packages.${ghcVersion};
23
24 pkgName = "webgear-example-hello-lucid";
25
26 haskellOverlay = final: prev: {
27 haskell = prev.haskell // {
28 packages = prev.haskell.packages // {
29 ${ghcVersion} = prev.haskell.packages.${ghcVersion}.override {
30 overrides = hfinal: hprev: {
31 webgear-core = hfinal.callPackage
32 ./nix/haskell-packages/webgear-core-1.0.4.nix { };
33 webgear-server = hfinal.callPackage
34 ./nix/haskell-packages/webgear-server-1.0.4.nix { };
35 ${pkgName} = hfinal.callCabal2nix pkgName
36 (gitignore.lib.gitignoreSource ./.) { };
37 };
38 };
39 };
40 };
41 };
42 in {
43 packages.default = hsPkgs.${pkgName};
44 devShells.default = hsPkgs.shellFor {
45 name = pkgName;
46 packages = pkgs: [ pkgs.${pkgName} ];
47 buildInputs = [
48 pkgs.cabal-install
49 pkgs.cabal2nix
50 hsPkgs.fourmolu
51 hsPkgs.ghc
52 hsPkgs.ghcid
53 pkgs.hlint
54 pkgs.haskell-language-server
55 ];
56 src = null;
57 };
58 });
59}
webgear-example-hello-lucid.cabal
The program name is changed and the ghc-option
to error out on unused packages -Wunused-packages
is commented out because the automatic recompilation doesn't like it. Furthermore lucid
is added to build-depends
.
1cabal-version: 2.4
2name: webgear-example-hello-lucid
3version: 1.0.4
4description:
5 Please see the README at <https://github.com/haskell-webgear/webgear-example-hello#readme>
6
7homepage:
8 https://github.com/haskell-webgear/webgear-example-hello#readme
9
10bug-reports:
11 https://github.com/haskell-webgear/webgear-example-hello/issues
12
13author: Raghu Kaippully [changes and additions by Mari Donkers]
14maintainer: rkaippully@gmail.com
15copyright: 2021-2022 Raghu Kaippully
16license: MPL-2.0
17license-file: LICENSE
18build-type: Simple
19extra-source-files: README.md
20
21source-repository head
22 type: git
23 location: https://github.com/haskell-webgear/webgear-example-hello
24
25executable hello
26 default-language: Haskell2010
27 build-depends:
28 , base >=4.12.0.0 && <5
29 , http-types ^>=0.12
30 , lucid
31 , warp ^>=3.3
32 , webgear-server ==1.0.4
33
34 ghc-options:
35 -threaded -rtsopts -with-rtsopts=-N -Wall
36 -Wno-unticked-promoted-constructors -Wcompat -Widentities
37 -Wincomplete-record-updates -Wincomplete-uni-patterns
38 -Wmissing-fields -Wmissing-home-modules
39 -Wmissing-deriving-strategies -Wpartial-fields
40 -Wredundant-constraints -fshow-warning-groups -Werror
41
42 -- -Wunused-packages
43 main-is: Main.hs
44 hs-source-dirs: src
src/Main.hs
The file is changed because of the 'off-label' experimentation (some arbitrary code changes and additions to demonstrate Lucid).
1{-# LANGUAGE Arrows #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE ExtendedDefaultRules #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE QuasiQuotes #-}
7{-# LANGUAGE TypeApplications #-}
8
9import Control.Category ((.))
10import Lucid
11import Network.HTTP.Types (StdMethod (GET))
12import qualified Network.HTTP.Types as HTTP
13import Network.Wai.Handler.Warp (run)
14import Prelude hiding ((.))
15import WebGear.Server
16
17title :: Html ()
18title = "Webgear hello example with Lucid templating server generated HTML"
19
20stylesheet :: Text
21stylesheet = "https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.2/gh-fork-ribbon.min.css"
22
23description :: Text
24description = "WebGear is a high-performance framework to build composable, type-safe HTTP APIs. It is designed to make common API development tasks easy. It is also easily extensible to add components needed by your project."
25
26viewport :: Text
27viewport = "width=device-width, initial-scale=1"
28
29listItemCount :: Int
30listItemCount = 7
31
32pageHead :: Html ()
33pageHead = do
34 title_ title
35 link_ [ rel_ "stylesheet"
36 , href_ stylesheet
37 ]
38 meta_ [ charset_ "utf-8" ]
39 meta_ [ name_ "theme-color", content_ "#00d1b2" ]
40 meta_ [ httpEquiv_ "X-UA-Compatible"
41 , content_ "IE=edge"
42 ]
43 meta_ [ name_ "viewport"
44 , content_ viewport
45 ]
46 meta_ [ name_ "description"
47 , content_ description
48 ]
49 style_ ".github-fork-ribbon:before { background-color: \"#e59751\" !important; }"
50
51pageBody :: Html ()
52pageBody = do
53 p_ "This is the first (1st) section in an HTML body."
54 p_ [style_ "color:red"] "The second (2nd) section."
55 p_ [class_ "third"] "And the third (3rd) section."
56 div_ [class_ "table"] "A table:" <> table
57 p_ "THIS IS GENERATED BY HASKELL CODE:"
58 hr_ []
59 div_ [id_ "genlist"] $ do
60 p_ "This is an example of Lucid syntax (a list generated by code)."
61 ul_ $ gl
62 where
63 table = table_ [rows_ "2", style_ "border: 1px solid black; padding:10px;"] $ do
64 tr_ $ do
65 td_ [class_ "top",
66 style_ "color:blue; border: 1px dashed black;"]
67 $ p_ "1a"
68 td_ [class_ "top",
69 style_ "color:blue; border: 1px dashed black;"] "1a"
70 tr_ $ do
71 td_ [class_ "top",
72 style_ "color:blue; border: 1px dashed black;"]
73 $ p_ "1b"
74 td_ [class_ "top",
75 style_ "color:blue; border: 1px dashed black;"] "2b"
76 gl = mapM_ (\i -> li_ $
77 toHtml $ "Item index [" ++ show i ++ "]") [1..listItemCount]
78
79page :: Html ()
80page = doctypehtml_ $ do
81 head_ pageHead
82 body_ pageBody
83
84lucid :: ServerHandler IO t Response
85lucid = proc _ -> do
86 let h = page
87 unlinkA <<< respondA HTTP.ok200 "text/html"
88 -< renderBS h
89
90hello :: ServerHandler
91 IO
92 (Linked '[PathEnd, PathVar "name" String, Path, Method] Request)
93 Response
94hello = proc request -> do
95 let name = pick @(PathVar "name" String) $ from request
96 unlinkA <<< respondA HTTP.ok200 "text/plain" -< "Hello, " <> name
97
98routes :: RequestHandler (ServerHandler IO) '[]
99routes = [route| GET /lucid |] lucid
100 <+> [route| GET /hello/name:String/ |] hello
101
102main :: IO ()
103main = run 3000 (toApplication routes)
Development environment
Start the development environment as follows.
1nix develop
Automatic recompilation
To start automatic recompilation use the following command:
1ghcid -c 'cabal repl' -T Main.main --restart=./webgear-example-hello-lucid.cabal
Loading the Lucid generated page
Navigate to http://localhost:3000/lucid and reload the page in the browser when you have changed something and the automatic recompilation has finished. Note that you will need to terminate the automatic reload command and exit
out of the development environment and restart both, after changing the flake.nix
or webgear-example-hello-lucid.cabal
file.