-
Notifications
You must be signed in to change notification settings - Fork 112
Update book content #287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update book content #287
Changes from all commits
fe848b9
3ef9f98
973945d
705f85c
ec9e68b
653fdec
b34da93
b46431c
50307b7
8ab17a5
6e38d3e
cb92975
bf8ac56
d59d246
b00ff97
3671ba8
69e3747
aa1a9ff
a9a347d
b7d5270
a57e33f
60a6b45
be58ca8
cb65fc3
d7b16ca
1b88949
b5b9037
659ce96
e1850a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,10 +15,22 @@ your individual Haskell files. | |
|
|
||
| [source, haskell] | ||
| ---- | ||
| {-# LANGUAGE OverloadedStrings, TypeFamilies, QuasiQuotes, | ||
| TemplateHaskell, GADTs, FlexibleContexts, | ||
| MultiParamTypeClasses, DeriveDataTypeable, | ||
| GeneralizedNewtypeDeriving, ViewPatterns #-} | ||
| {-# LANGUAGE DataKinds #-} | ||
| {-# LANGUAGE DeriveDataTypeable #-} | ||
| {-# LANGUAGE DerivingStrategies #-} | ||
| {-# LANGUAGE FlexibleContexts #-} | ||
| {-# LANGUAGE FlexibleInstances #-} | ||
| {-# LANGUAGE GADTs #-} | ||
| {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
| {-# LANGUAGE MultiParamTypeClasses #-} | ||
| {-# LANGUAGE OverloadedStrings #-} | ||
| {-# LANGUAGE QuasiQuotes #-} | ||
| {-# LANGUAGE StandaloneDeriving #-} | ||
| {-# LANGUAGE TemplateHaskell #-} | ||
| {-# LANGUAGE TypeFamilies #-} | ||
| {-# LANGUAGE TypeOperators #-} | ||
| {-# LANGUAGE UndecidableInstances #-} | ||
| {-# LANGUAGE ViewPatterns #-} | ||
| ---- | ||
|
|
||
| Now our imports. | ||
|
|
@@ -37,7 +49,6 @@ import Database.Persist.Sqlite | |
| , createSqlitePool, runSqlPersistMPool | ||
| ) | ||
| import Data.Time (UTCTime, getCurrentTime) | ||
| import Control.Applicative ((<$>), (<*>), pure) | ||
| import Data.Typeable (Typeable) | ||
| import Control.Monad.Logger (runStdoutLoggingT) | ||
| ---- | ||
|
|
@@ -252,7 +263,7 @@ good idea to always check for pending messages in your defaultLayout function. | |
| mmsg <- getMessage | ||
| ---- | ||
|
|
||
| We use widgets to compose together HTML, CSS and Javascript. At the end of the | ||
| We use widgets to compose together HTML, CSS and JavaScript. At the end of the | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The transition from "Javascript" to "JavaScript" was started in #284. |
||
| day, we need to unwrap all of that into simple HTML. That's what the | ||
| +widgetToPageContent+ function is for. We're going to give it a widget consisting | ||
| of the content we received from the individual page (inside), plus a standard | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -372,8 +372,8 @@ several hundred kilobytes. If we take a non-streaming approach, this can lead | |
| to huge memory usage and slow response times. | ||
|
|
||
| So how exactly do we create a streaming response? Yesod provides a helper | ||
| function for this case: +responseSourceDB+. This function takes two arguments: | ||
| a content type, and a conduit +Source+ providing a stream of blaze-builder | ||
| function for this case: +respondSourceDB+. This function takes two arguments: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was probably a mixup between Yesod's |
||
| a content type, and a Conduit providing a stream of blaze-builder | ||
| ++Builder++s. Yesod then handles all of the issues of grabbing a database | ||
| connection from the connection pool, starting a transaction, and streaming the | ||
| response to the user. | ||
|
|
@@ -387,7 +387,7 @@ is: | |
|
|
||
| [source, haskell] | ||
| ---- | ||
| renderBuilder :: Monad m => RenderSettings -> Conduit Event m Builder | ||
| renderBuilder :: Monad m => RenderSettings -> ConduitT Event Builder m () | ||
| ---- | ||
|
|
||
| In plain English, that means +renderBuilder+ takes some settings (we'll just use | ||
|
|
@@ -472,7 +472,7 @@ We start the document element with an +id+ attribute, start the content, insert | |
| the content, and then close both elements. We use +toPathPiece+ to convert a | ||
| +DocId+ into a +Text+ value. Next, we need to be able to convert a stream of | ||
| these entities into a stream of events. For this, we can use the built-in | ||
| +concatMap+ function from +Data.Conduit.List+: +CL.concatMap entityToEvents+. | ||
| +concatMapC+ function from +Conduit+: +concatMapC entityToEvents+. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I switched to functions from the |
||
|
|
||
| But what we _really_ want is to stream those events directly from the database. | ||
| For most of this book, we've used the +selectList+ function, but Persistent | ||
|
|
@@ -482,18 +482,18 @@ the function: | |
|
|
||
| [source, haskell] | ||
| ---- | ||
| docSource :: Source (YesodDB Searcher) X.Event | ||
| docSource = selectSource [] [] $= CL.concatMap entityToEvents | ||
| docSource :: ConduitT () X.Event (YesodDB Searcher) () | ||
| docSource = selectSource [] [] .| concatMapC entityToEvents | ||
| ---- | ||
|
|
||
| The $= operator joins together a source and a conduit into a new source. Now | ||
| The .| operator combines two Conduits together into a new Conduit. Now | ||
| that we have our +Event+ source, all we need to do is surround it with the | ||
| document start and end events. With +Source+'s +Monad+ instance, this is a | ||
| document start and end events. With +ConduitT+'s +Monad+ instance, this is a | ||
| piece of cake: | ||
|
|
||
| [source, haskell] | ||
| ---- | ||
| fullDocSource :: Source (YesodDB Searcher) X.Event | ||
| fullDocSource :: ConduitT () X.Event (YesodDB Searcher) () | ||
| fullDocSource = do | ||
| mapM_ yield startEvents | ||
| docSource | ||
|
|
@@ -508,33 +508,35 @@ getXmlpipeR :: Handler TypedContent | |
| getXmlpipeR = | ||
| respondSourceDB "text/xml" | ||
| $ fullDocSource | ||
| $= renderBuilder def | ||
| $= CL.map Chunk | ||
| .| renderBuilder def | ||
| .| mapC Chunk | ||
| ---- | ||
|
|
||
| === Full code | ||
|
|
||
| [source, haskell] | ||
| ---- | ||
| {-# LANGUAGE DataKinds #-} | ||
| {-# LANGUAGE DerivingStrategies #-} | ||
| {-# LANGUAGE FlexibleContexts #-} | ||
| {-# LANGUAGE FlexibleInstances #-} | ||
| {-# LANGUAGE GADTs #-} | ||
| {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
| {-# LANGUAGE MultiParamTypeClasses #-} | ||
| {-# LANGUAGE OverloadedStrings #-} | ||
| {-# LANGUAGE QuasiQuotes #-} | ||
| {-# LANGUAGE StandaloneDeriving #-} | ||
| {-# LANGUAGE TemplateHaskell #-} | ||
| {-# LANGUAGE TypeFamilies #-} | ||
| {-# LANGUAGE TypeOperators #-} | ||
| {-# LANGUAGE UndecidableInstances #-} | ||
| {-# LANGUAGE ViewPatterns #-} | ||
| import Control.Applicative ((<$>), (<*>)) | ||
| import Conduit | ||
| import Control.Monad (forM) | ||
| import Control.Monad.Logger (runStdoutLoggingT) | ||
| import Data.Conduit | ||
| import qualified Data.Conduit.List as CL | ||
| import Data.Maybe (catMaybes) | ||
| import Data.Monoid (mconcat) | ||
| import Data.Text (Text) | ||
| import qualified Data.Text as T | ||
| import Data.Text.Lazy.Encoding (decodeUtf8) | ||
| import qualified Data.XML.Types as X | ||
| import Database.Persist.Sqlite | ||
| import Text.Blaze.Html (preEscapedToHtml) | ||
|
|
@@ -719,8 +721,8 @@ getXmlpipeR :: Handler TypedContent | |
| getXmlpipeR = | ||
| respondSourceDB "text/xml" | ||
| $ fullDocSource | ||
| $= renderBuilder def | ||
| $= CL.map Chunk | ||
| .| renderBuilder def | ||
| .| mapC Chunk | ||
|
|
||
| entityToEvents :: (Entity Doc) -> [X.Event] | ||
| entityToEvents (Entity docid doc) = | ||
|
|
@@ -731,14 +733,14 @@ entityToEvents (Entity docid doc) = | |
| , X.EventEndElement document | ||
| ] | ||
|
|
||
| fullDocSource :: Source (YesodDB Searcher) X.Event | ||
| fullDocSource :: ConduitT () X.Event (YesodDB Searcher) () | ||
| fullDocSource = do | ||
| mapM_ yield startEvents | ||
| docSource | ||
| mapM_ yield endEvents | ||
|
|
||
| docSource :: Source (YesodDB Searcher) X.Event | ||
| docSource = selectSource [] [] $= CL.concatMap entityToEvents | ||
| docSource :: ConduitT () X.Event (YesodDB Searcher) () | ||
| docSource = selectSource [] [] .| concatMapC entityToEvents | ||
|
|
||
| toName :: Text -> X.Name | ||
| toName x = X.Name x (Just "http://sphinxsearch.com/") (Just "sphinx") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -93,7 +93,7 @@ out a safe storage solution for the client session key. | |
| There are two commonly used features in the Yesod world: serving your site over | ||
| HTTPS, and placing your static files on a separate domain name. While both of | ||
| these are good practices, when combined they can lead to problems if you're not | ||
| careful. In particular, most web browsers will not load up Javascript files | ||
| careful. In particular, most web browsers will not load up JavaScript files | ||
| from a non-HTTPS domain name if your HTML is served from an HTTPS domain name. | ||
| In this situation, you'll need to do one of two things: | ||
|
|
||
|
|
@@ -233,23 +233,13 @@ responsible to run your own process. I strongly recommend a monitoring utility | |
| which will automatically restart your application in case it crashes. There are | ||
| many great options out there, such as angel or daemontools. | ||
|
|
||
| To give a concrete example, here is an Upstart config file. The file must be | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Upstart is discontinued so I guess it doesn't make much sense to keep this example. |
||
| placed in +/etc/init/mysite.conf+: | ||
|
|
||
| ---- | ||
| description "My awesome Yesod application" | ||
| start on runlevel [2345]; | ||
| stop on runlevel [!2345]; | ||
| respawn | ||
| chdir /home/michael/sites/mysite | ||
| exec /home/michael/sites/mysite/dist/build/mysite/mysite | ||
| ---- | ||
|
|
||
| Once this is in place, bringing up your application is as simple as | ||
| +sudo start mysite+. A similar systemd configuration file placed in | ||
| To give a concrete example, here is a systemd configuration file placed in | ||
| +/etc/systemd/system/yesod-sample.service+: | ||
|
|
||
| ---- | ||
| [Unit] | ||
| Description=My awesome Yesod application | ||
|
|
||
| [Service] | ||
| ExecStart=/home/sibi/.local/bin/my-yesod-executable | ||
| Restart=always | ||
|
|
@@ -344,7 +334,7 @@ A similar approach, without requiring the QtWebkit library, is | |
| wai-handler-launch, which launches a Warp server and then opens up the user's | ||
| default web browser. There's a little trickery involved here: in order to know | ||
| that the user is still using the site, +wai-handler-launch+ inserts a "ping" | ||
| Javascript snippet to every HTML page it serves. It +wai-handler-launch+ | ||
| JavaScript snippet to every HTML page it serves. It +wai-handler-launch+ | ||
| doesn't receive a ping for two minutes, it shuts down. | ||
|
|
||
| === CGI on Apache | ||
|
|
@@ -381,7 +371,7 @@ and the last line does the actual rewriting. | |
| === FastCGI on lighttpd | ||
|
|
||
| For this example, I've left off some of the basic FastCGI settings like | ||
| mime-types. I also have a more complex file in production that prepends "www." | ||
| MIME types. I also have a more complex file in production that prepends "www." | ||
| when absent and serves static files from a separate domain. However, this | ||
| should serve to show the basics. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mpsGenericis deprecated and the default value isFalseanyway.Cf. yesodweb/persistent#1348