packages feed

happs-tutorial 0.6.5 → 0.7

raw patch · 44 files changed

+277/−379 lines, 44 filesdep +happstack-datadep +happstack-helpersdep +happstack-serverdep −HAppS-Datadep −HAppS-Serverdep −HAppS-Statenew-uploader

Dependencies added: happstack-data, happstack-helpers, happstack-server, happstack-state

Dependencies removed: HAppS-Data, HAppS-Server, HAppS-State, HAppSHelpers

Files

happs-tutorial.cabal view
@@ -1,13 +1,13 @@ Name:                happs-tutorial-Version:             0.6.5+Version:             0.7 Synopsis:            A HAppS Tutorial that is is own web 2.0-type demo.  Description:         A nice way to learn how to build web sites with HAppS  License:             BSD3 License-file:        LICENSE-Author:              Thomas Hartman, Eelco Lempsink+Author:              Thomas Hartman, Eelco Lempsink, Creighton Hogg -Maintainer:          thomashartman1 at gmail dot com+Maintainer:          Creighton Hogg <wchogg at gmail.com> Copyright:           2008 Thomas Hartman  Stability:           Experimental@@ -42,10 +42,6 @@         ControllerStressTests         View     Build-Depends:   base>=3.0.3.0, HStringTemplate, HStringTemplateHelpers, mtl, bytestring,-                     HAppS-Server, HAppS-Data, HAppS-State,+                     happstack-server == 0.1, happstack-data == 0.1, happstack-state == 0.1,                      containers, pretty, pureMD5, directory, filepath, hscolour >= 1.10.1, HTTP, safe,-                     old-time, parsec, HAppSHelpers >= 0.9, DebugTraceHelpers----+                     old-time, parsec, happstack-helpers >= 0.11 && < 0.20, DebugTraceHelpers
src/ControllerBasic.hs view
@@ -137,18 +137,21 @@             liftIO $ do templates <- directoryGroup "templates"                         let fp2 :: String                             fp2 = renderTemplateGroup templates [("favoritePlantTwo","Venus Fly Trap")] "favoritePlant"+                            mineralList :: String+                            mineralList = renderTemplateGroup templates [("favoriteMinerals",["zinc ","talc"])] "myFavoriteAnimal"                             r = renderTemplateGroup templates [("favoriteAnimal", "Tyrannasaurus Rex")                                                            , ("leastFavoriteAnimal","Bambi")                                                     -- if you set the same template variable several times it                                                     -- gets repeated when it gets displayed                                                     -- I think this is reasonable, because it gives you                                                      -- feedback that there's probably a bug in your program-                                                           , ("favoritePlant","Ficus")                                                            , ("favoritePlant","Wheat")+                                                           , ("favoritePlant","Ficus")                                                            , ("favoritePlant","Sugarcane")                                                            -- note that template variable names must be alpha                                                            -- favoritePlant2 below would get rejected -                                                           , ("fpTwo", fp2)]+                                                           , ("fpTwo", fp2)+                                                           , ("favoriteMinerals",mineralList)]                                                     $ "myFavoriteAnimalBase"                          return . toResponse . HtmlString $ r                             -- if template key is repeated, only the first value appears
src/ControllerPostActions.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} module ControllerPostActions where -import Debug.Trace.Helpers import Text.ParserCombinators.Parsec  import Control.Monad@@ -83,18 +82,20 @@   return $ maybe False ( == scramblepass (B.unpack pass) ) mbP  -changePasswordSP rglobs = withData $ \(ChangeUserInfo oldpass newpass1 newpass2) -> [ ServerPartT $ \rq -> do-    etRes <- runErrorT $ getLoggedInUserInfos rglobs-    case etRes of-      Left e -> errW e-      Right (u,_) -> do-        if newpass1 /= newpass2-           then errW "new passwords did not match"-           else do update $ ChangePassword u oldpass newpass1-                   return $ tutlayoutU rglobs [] "accountsettings-changed"+changePasswordSP :: RenderGlobals -> ServerPartT IO Response+changePasswordSP rglobs = withData $ \(ChangePasswordInfo newpass1 newpass2) -> [ ServerPartT $ \rq -> do+    if newpass1 /= newpass2 +      then errW "new passwords don't match"+      else do +        etRes <- runErrorT $ getLoggedInUserInfos rglobs+        case etRes of+          Left e -> errW e+          Right (u,_) ->  do     +            -- newp <- newPassword (B.unpack newpass1)+            update $ ChangePassword u newpass1+            return $ tutlayoutU rglobs [] "accountsettings-changed"   ]-  where errW msg = ( return . tutlayoutU rglobs [("errormsgAccountSettings", msg)] ) "changepassword"-+  where errW msg = return $ tutlayoutU rglobs [("errormsgAccountSettings", msg)] "changepassword"   processformEditConsultantProfile rglobs =
src/FromDataInstances.hs view
@@ -46,12 +46,11 @@     fromData = liftM2 UserAuthInfo ( do un <- (look "username" `mplus` return "") ; return . UserName . B.pack $ un)                                    (return . B.pack =<< look "password" `mplus` return "") -data ChangeUserInfo = ChangeUserInfo B.ByteString B.ByteString B.ByteString-instance FromData ChangeUserInfo where-    fromData = liftM3 ChangeUserInfo ( return . B.pack =<< (look "oldpass") `mplus` (return "") )+data ChangePasswordInfo = ChangePasswordInfo B.ByteString B.ByteString+instance FromData ChangePasswordInfo where+    fromData = liftM2 ChangePasswordInfo -- ( return . B.pack =<< (look "oldpass") `mplus` (return "") )                                      ( return . B.pack =<< look "password" `mplus` return "")                                      ( return . B.pack =<< look "password2" `mplus` return "")-   -- wrapper over UserProfile data type, the difference is that the avatar is file contents
src/StateVersions/AppState1.hs view
@@ -5,6 +5,7 @@   Users (..), UserName -- , add_user_job    ) -} where+ import HAppS.State import Data.Generics import Control.Monad (liftM)@@ -82,11 +83,6 @@ instance Version UserInfos $(deriveSerialize ''UserInfos)  ---- as a security measure, require that oldpass agrees with real old pass-set_password oldpass newpass (UserInfos pass up jobs) | pass == oldpass = return $ UserInfos newpass up jobs-                                              | otherwise = fail $ "bad old password: " - -- mod_password f (UserInfos pass up jobs) = UserInfos (f pass) up jobs set_userprofile = mod_userprofile . const mod_userprofile f (UserInfos pass up jobs) = UserInfos pass (f up) jobs@@ -131,9 +127,6 @@ set_user_userprofile_consultant username isconsultant =   mod_userM username $ ( mod_userprofile . set_consultant $ isconsultant ) --- fails monadically if oldpass doesn't match password in user profile, via set_password-set_user_password :: (Monad m) => UserName -> B.ByteString -> B.ByteString -> Users -> m Users-set_user_password username oldpass newpass = mod_userMM username $ set_password oldpass newpass  -- set_user_userprofile username p = mod_userM username $ Right . set_userprofile p  @@ -278,14 +271,20 @@                       return $ Right ()  -changePassword :: UserName -> B.ByteString -> B.ByteString -> Update AppState ()-changePassword un oldpass newpass = do+--changePassword :: UserName -> B.ByteString -> Update AppState (Either String ())+changePassword un newpass = do   AppState s us <- get-  case ( set_user_password un (B.pack hashedoldpass) (B.pack hashednewpass) us :: Either String Users) of-    Left err -> fail $ "changePassword"-    Right newus -> put $ AppState s us-  where hashedoldpass = scramblepass (B.unpack oldpass)-        hashednewpass = scramblepass (B.unpack newpass)+  let hashednewpass = scramblepass $ B.unpack newpass+  newUs <- set_user_password un (B.pack hashednewpass) us+  put $ AppState s newUs++-- set_user_password :: UserName -> B.ByteString -> Users -> Either String Users+set_user_password username newpass = mod_userM username $ set_password newpass++set_password :: B.ByteString -> UserInfos -> UserInfos+set_password newpass (UserInfos pass up jobs) = UserInfos newpass up jobs++   -- was getUser
templates/base.st view
@@ -3,7 +3,7 @@ <html xmlns="http://www.w3.org/1999/xhtml" >     <head>       <meta http-equiv="content-type" content="text/html; charset=utf-8" />-      <title> Real World HAppS: The Cabalized, Self-Demoing HAppS Tutorial </title>+      <title> Real World Happstack: The Cabalized, Self-Demoing Happstack Tutorial </title>       <link rel="stylesheet" href="/static/tutorial.css" type="text/css" />     </head> 
templates/basicurlhandling.st view
@@ -1,13 +1,14 @@-<h3>Basic Request Handling in HAppS</h3>+<h3>Basic Request Handling in Happstack</h3>  <p>One of the most basic functions of a web framework is to give you a way of controlling what happens when a web browser makes an http request.</p> -<p>$! Before explaining the theory of request handling in HAppS, !$ Let's look at some simple examples.</p>+<p>$! Before explaining the theory of request handling in Happstack, !$ Let's look at some simple +examples.</p>  <!-- who cares about MVC?... <p>In the <a href="#">FIX LINK... MVC paradigm</a> employed by Ruby on Rails and other popular frameworks, this is the task of the Controller. </p> -->  <ol>-  <li> Read the source code of the simpleHandlers function in <a href="/src/ControllerBasic.hs">ControllerBasic.hs</a> to see how urls are handled in HAppS. <b>Pay attention to the comments!</b>+  <li> Read the source code of the simpleHandlers function in <a href="/src/ControllerBasic.hs">ControllerBasic.hs</a> to see how urls are handled in Happstack. <b>Pay attention to the comments!</b>   <li>Follow the urls below by clicking on them. Match what happens when you click on a link with the code in <a href="/src/ControllerBasic.hs">ControllerBasic.hs</a></li> </ol> @@ -44,11 +45,11 @@   <li><a href="/htmlAttemptForeignChars">Foreign characters in formatted html</a></li>    <li>Serving static files: <a href="/templates/base.st">Using dir and fileserve "templates", we can view templates</a> ... -      <a href="/templates/basic-url-handling.st">The template that was used to generate this page</a>+      <a href="/templates/basicurlhandling.st">The template that was used to generate this page</a>   </li>  </ul>     <p>The static file serving example above hints at the templating system used by this tutorial to put together web pages behind the scenes.</p> -<p>We learn about <a href="/tutorial/templates-dont-repeat-yourself">using templates with HAppS</a> next.</p>+<p>We learn about <a href="/tutorial/templates-dont-repeat-yourself">using templates with Happstack</a> next.</p>
templates/changepassword.st view
@@ -2,7 +2,7 @@      <form action="/tutorial/changepassword" method="post">     <table>-	<tr><td>Old Password:</td><td><input type="password" name="oldpass"/></td></tr>+	$! <tr><td>Old Password:</td><td><input type="password" name="oldpass"/></td></tr> !$  	<tr><td>Password:</td><td><input type="password" name="password"/></td></tr> 
templates/consultants.st view
@@ -1,4 +1,4 @@-<h3>HAppS Developers+<h3>Happstack Developers     &nbsp <font size=-2>@@ -7,5 +7,5 @@ </h3>  -<p>The following users wish to be listed as HAppS developers:</p>+<p>The following users wish to be listed as Happstack developers:</p> $ consultantList $
templates/consultantswanted.st view
@@ -1,9 +1,9 @@-<h3>HAppS Developers Wanted+<h3>Happtack Developers Wanted   &nbsp <font size=-2>           <a class="attention" href="/tutorial/register?wantregisterconsult=1"> $ registerAsConsultant $ </a></p>         </font> </h3> -<p> The following users have posted HAppS jobs:</p>+<p> The following users have posted Happstack jobs:</p>  $ ulist $
templates/cookies.st view
@@ -1,18 +1,18 @@ <h3>Cookies</h3> -<p>Cookies don't work quite right in HAppS out of the box. The most common problem-   is that google analytics and HAppS session cookies are mutually incompatible.+<p>Cookies don't work quite right in Happstack out of the box. The most common problem+   is that google analytics and Happstack session cookies are mutually incompatible.    (There is a thread about what went wrong in the initial implementation in the happs googlegroup.) -<p>Hopefully the underlying problems will be fixed in the next HAppS hackathon.+<p>Support for cookies will be improved in future Happstack releases. -<p>Meanwhile, there is a workaround in the HAppSHelpers package on hackage, which is used by happstutorial,+<p>Meanwhile, there is a workaround in the happstack-helpers package on hackage, which is used by happs-tutorial,    and is built into smartserver.-   So cookies do work in happstutorial: I use google analytics to track visitors, -   along with normal HAppS session cookies.-   And if you use happstutorial as a template for happs apps you should be fine.+   So cookies do work in happs-tutorial: I use google analytics to track visitors, +   along with normal Happstack session cookies.+   And if you use happs-tutorial as a template for your apps you should be fine. -<p>Besides the cookies used by google analytics, which are obfuscated by javascript, happstutorial+<p>Besides the cookies used by google analytics, which are obfuscated by javascript, happs-tutorial    uses cookies to track session state -- the data that corresponds to the current user's session in <br> <br>data AppState = AppState {@@ -22,7 +22,7 @@   <p>When you log in, a cookie is created that expires in an hour -   (3600 seconds). And every time a happs handler needs to check if a user is logged in (for example, +   (3600 seconds). And every time a happstack handler needs to check if a user is logged in (for example,     when it needs to decide whether to show the logged-in-user menubar) a check is made to see if a cookie    has been set.  @@ -37,3 +37,6 @@ <br> <br>getMbSessKey rq = runReaderT (readCookieValue "sid") (rqInputs rq,rqCookies rq) +<p>We've spent a good bit of time now on how happstack-server handles requests,+   so we're ready to move on to the major feature of happstack-state,+   <a href="/tutorial/introductiontomacid">MACID</a>.
templates/debugging.st view
@@ -44,12 +44,13 @@    and need to disginguish them.  <p>There are probably smarter ways of debugging a happs app, the above is just what works for me.-   (Actually, I debug using these trace helpers all the time, not just in HApps.)-   If I get useful feedback on how other happs users approach debugging I will update this page.+   (Actually, I debug using these trace helpers all the time, not just in Happstack.)+   If I get useful feedback on how other Happstack users approach debugging I will update this page. -<p>Incidentally, ghci has a debugger since 6.8.3. Overall it's great, but I didn't find it too useful for happs,+<p>Incidentally, ghci has a debugger since 6.8.3. Overall it's great, but I didn't find it too useful for Happstack,    because it would die out on me in ways I didn't understand, typically around hard to understand exceptions.-   Basically, HAppS would behave differently when I was stepping through it with the ghci debugger than +   Basically, Happstack would behave differently when I was stepping through it with the ghci debugger than     it would when running without the debugger.    I suspect it's because the ghci debugger lacks support for multithreaded programs, or something along those lines.    At any rate, I wound up not using the interactive debugger much, though I would have liked to.+<p>Now, on to the pragmatic matter of <a href="/tutorial/get-and-post">form data</a>.
templates/editconsultantprofile.st view
@@ -6,7 +6,7 @@  	<tr><td>Contact:</td><td colspan=2><textarea name="contact" rows=3 cols=64>$contact$</textarea></td></tr> -        <tr><td>List me on the HAppS developers page:</td>+        <tr><td>List me on the Happstack developers page:</td> 	    <td colspan=2><input type="checkbox" name="listasconsultant" $listAsConsultantChecked$ ></td></tr>                   <tr><td>Upload picture:</td> 
templates/fileuploads.st view
@@ -15,3 +15,4 @@  <p>In the above, the <i>fdimagecontents</i> variable contents the entire contents of an image file.     writeFileForce is just a wrapper over writeFile that creates necessary parent directories if they are missing.+<p>Now for a few words on <a href="/tutorial/cookies">cookies</a> in Happstack
templates/footer.st view
@@ -1,5 +1,5 @@     <div id="footer">-    copyright thomashartman1 at gmail +    copyright thomashartman1 at gmail, wchogg at gmail      <br><a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/happs-tutorial">use the source</a>         | <a href="/projectroot/templates">browse templates</a> 	| <a href="/projectroot/src">browse haskell files</a>
templates/foreignchars.st view
@@ -29,13 +29,10 @@ <br>*Main> putStrLn "ö" <br>ö -<p>This annoyance may be alleviated when ghc support for unicode is added sometime after ghc 6.8.10, as alluded-to in the <a href="tk">release notes</a>- <p>I also had a problem where data in form textbox fields was mysteriously showing up \ed. It turned out  this is because I sloppily used show to escape quotes, rather than sticking a quote at the beginning and  end of the string as was more proper. I then got bitten by show as above, causing much misery.  <p>But all that having been said, as the live demo shows, in general utf8 data does work as it should. -<p>Next chapter is about using <a href="/tutorial/start-happs-on-boot">cron jobs</a> to keep happs running if it quits on your mysterious reasons.+<p>Next chapter is about using <a href="/tutorial/start-happstack-on-boot">cron jobs</a> to keep happs running if it quits on your mysterious reasons.
templates/foreigncharsBase.st view
@@ -4,7 +4,7 @@     <head>        $! meta tag seems to screw things up in t3, but removing it in base.st doesn't fix tutorial pages !$       $! <meta http-equiv="content-type" content="text/html; charset=utf-8" /> !$-      <title> Real World HAppS: The Cabalized, Self-Demoing HAppS Tutorial </title>+      <title> Real World Happstack: The Cabalized, Self-Demoing Happstack Tutorial </title>       <link rel="stylesheet" href="/static/tutorial.css" type="text/css" />     </head>  
templates/getandpost.st view
@@ -37,10 +37,10 @@    defined, like User or Job, which already plays a major role in the app.    Or it could be an ad-hoc datatype which is only used this once, in the form processing.    Whatever the case you declare that data type an instance of FromData, and define how it should-   grab data attached to the request. In this case, UserNameUrlString takes one argument, so we use LiftM -   -- for two args we would use LiftM2, three args LiftM3 etc. +   grab data attached to the request. In this case, UserNameUrlString takes one argument, so we use liftM +   -- for two args we would use liftM2, three args liftM3 etc.     To get that one arg, we look for a request GET variable named "user" and if we don't find it we    use a reasonable default, in this case the empty string. The result is a value of type UserNameUrlString    which gets passed to the ServerPartT handler in the userProfile function. -<p>Another use is... (pagination. now... sleeep.....)+Next we cover <a href="/tutorial/file-uploads">uploading files</a>
templates/gettingstarted.st view
@@ -1,13 +1,13 @@-<h4>Getting started with HAppS</h4>+<h4>Getting started with Happstack</h4>  -<p>I'm going to repeat myself now. The best way to learn how to use use HAppS with this tutorial is to <a-href="/tutorial/run-tutorial-locally">install and run</a> it in a+<p>I'm going to repeat myself now. The best way to learn how to use use Happstack with this +tutorial is to <ahref="/tutorial/run-tutorial-locally">install and run</a> it in a local environment.  So if you can, do that first.  $! <p>Getting on with the tutorial proper, our first lesson examines the -<a href="/tutorial/main-function">main function</a> in a HAppS program.+<a href="/tutorial/main-function">main function</a> in a Happstack program. The second lesson explains how to do <a href="/tutorial/basic-url-handling">basic url handling</a>. The third lesson covers <a href="/tutorial/templates-dont-repeat-yourself">using templates</a>.</p>
templates/ghciflounderingaskdatastore.st view
@@ -1,6 +1,6 @@-<h3>Appendix: Using ghci with HAppS</h3>+<h3>Appendix: Using ghci with Happstack</h3> -<p><font color=orange>When working with HAppS, I seem to spend a lot of time in ghci looking at type signatures,+<p><font color=orange>When working with Happstack, I seem to spend a lot of time in ghci looking at type signatures,    and it's not always obvious what's going on. So I thought I'd share a little mini ghci session     in case this helps anyone.</font> @@ -38,7 +38,7 @@  <p>Prelude Control.Monad.Reader HAppS.State GHC.Conc> :i Query <br>type Query state = Ev (ReaderT state STM)-<br>  	-- Defined in HAppS-State-0.9.2.1:HAppS.State.Types+<br>  	-- Defined in Happstack-state-0.1:HAppS.State.Types <br> <br>data StateStuff.Method st where <br>  ...@@ -80,6 +80,6 @@  <p>What about Ev?... nah. -<p>That is as far down the HAppS type rabbit hole as I am going to go.+<p>That is as far down the Happstack type rabbit hole as I am going to go. </font> 
− templates/happsslowlinkingbug.st
@@ -1,37 +0,0 @@-<h3>HAppS Slow Link Time Workarounds</h3>-<p>-The fact that cabal-installing HAppS takes an hour is-officially a <a-href="http://code.google.com/p/happs/issues/detail?id=29">bug</a>. Hopefully-this situation will be remedied as HAppS matures and, eventually, has-an official release.  -<p>-I found this bug pretty problematic when I was experiencing it and I know I'm not the only one. -So, I will share some experiences and observations that will hopefully help others,-and maybe even help diagnose and eventually squash this bug.-<p>-First of all, you don't actually need to compile an executable to run a HAppS server, -and when you run from inside ghci the link time bug has no effect. So for a while I -was doing this, by loading ghci using ./hackInGhci.sh and then running runInGhci inside Main.hs.-<p>-Secondly, at some point this problem went away, and my link time dropped from 5-10 minutes to under 10 seconds.-<p>-This is definitely due to a change in my own code base, not HAppS repo code, since I am only -running against what I cabal installed and not the volatile HAppS library code in darcs.-At some point I intend to attempt a more precise diagnosis-by doing binary cuts on my repo and identifying the changes that seem-to have the biggest impact. I do have some suspicions. -<ul>-  <li>Problems are related to Template Haskell and/or Data.Deriving</li>-  <li>Splitting up big modules into smaller modules-      <br>When I saw link times over 5 minutes I tried to isolate the -        "slow" methods in a file called "Slow.hs" so that the linking is only slow when that file changes.-        Lately I removed the Slow module since it didn't seem necessary anymore.</li>-  <li>Supplying type signatures helps, and the more concrete the type signature the better.-  <br>So, (askUsers :: Query TutorialState (M.Map String User) ) rather than (askUsers :: MonadReader State m => m (M.Map String User) )-  <br>tentative idea: ghc -fwarn-missing-signatures and give maximally precise signatures everywhere.</li>-</ul>--<p>--The shell command ./runServer.sh, which creates an executable and starts the server, also times the compile & link, and rings a bell when it's done. I figure this will be helpful if slow link times creep back in.
templates/header.st view
@@ -1,7 +1,7 @@      <div id="header">       -      <img src="/static/HAppSTutorialLogo.png" alt="HAppS Tutorial">+      <img src="/static/HAppSTutorialLogo.png" alt="Happstack Tutorial"> 	<a href="http://h-master.net/web2.0/"><font size=1>logo created by the web 2.0 logo generator</font></a>              <h4> $ menubar() $ </h4>
templates/home.st view
@@ -1,12 +1,12 @@-<h3>Real World HAppS: building a Web 2.0 App with haskell</h3>+<h3>Real World Happstack: building a Web 2.0 App with Haskell</h3> -<p><a href="http://www.happs.org">HAppS</a> is a great way to build web applications.  +<p><a href="http://www.happstack.com">Happstack</a> is a great way to build web applications.      Besides having a great feature set in its own right, it is probably the leading solution-   for implementing web apps in <a href="http://www.google.com/search?hl=en&q=why+haskell">haskell</a>, -   my favorite language.+   for implementing web apps in <a href="http://www.google.com/search?hl=en&q=why+haskell">Haskell</a>, +   my favorite language.</p>  <p>If you use Ruby on Rails, Django, Perl Catalyst, PHP, or some other popular web framework, but-have programmed in haskell and would like to use the world's greatest language for your next web project,+have programmed in Haskell and would like to use the world's greatest language for your next web project, just keep reading. I promise by the time you're done you  will posess all the knowledge  and sample code you need.</p> @@ -17,8 +17,7 @@ <a href="/tutorial/run-tutorial-locally">install and run</a> it in a local environment where you have control. Then, when you are done learning, you can use the tutorial code as a starting point for your own-HAppS applications. +Happstack applications.  </p> -<p><a href="/tutorial/why-happs-is-cool">Why HAppS?</a></p>-+<p><a href="/tutorial/why-happstack-is-cool">Why Happstack?</a></p>
templates/introductiontomacid.st view
@@ -1,14 +1,14 @@ <h3>Introduction To Macid</h3> -<p>Macid is the HAppS storage mechanism that allows you to use whatever data structure you want+<p>Macid is the Happstack storage mechanism that allows you to use whatever data structure you want    to hold your permanent data, without worrying about getting it into and out of tabular form-   fit for storage in a traditional <a href="wikipedia tk">rdbms</a>.+   fit for storage in a traditional <a href="http://en.wikipedia.org/wiki/Relational_database_management_system">rdbms</a>.  <p>Before delving into how it works let's learn a bit about it from an operational perspective.     Assuming you have the tutorial installed and running locally:   <ul>-  <li><a href=/tutorial/register>register</a> a new user with username "testuser" and password "testpassword"+  <li>Register a new user with username "testuser" and password "testpassword"   <li>Optionally, fill out profile information for your new test user, and/or make some job posts. </ul> @@ -35,5 +35,3 @@   <p><a href="/tutorial/maciddatasafety">Keeping your macid data safe</a>--
templates/maciddatasafety.st view
@@ -10,7 +10,7 @@    maybe you $!are even lucky enough to!$ have a database administrator that takes backups for you on a regular basis.    That probably helps you sleep at night, assuming that you can really trust that your dba is doing their job. -<p>As we learned in the previous lesson, if you are using HAppS with macid, your data is +<p>As we learned in the previous lesson, if you are using Happstack with macid, your data is     right there on your filesystem, by default in the directory called <i>_local</i>.  <p>~/happs-tutorial>ls _local/happs-tutorial_state/@@ -23,7 +23,7 @@ <p>When <a href="/tutorial/macid-migration">migrating macid data</a> to a new schema, you are also going to want to be extra cautious.  <p>But for now, since you don't have any valuable data, the following procedure is probably enough-   to remind yourself to be careful while learning about HAppS in the tutorial sandbox.+   to remind yourself to be careful while learning about Happstack in the tutorial sandbox.  <ul>   <li>Stop happs by doing ctrl-c if you are running the ./happs-tutorial app from a shell@@ -39,25 +39,16 @@  The above procedure raises some questions. -<p>Q: Do you have to shut down the HAppS server every time you migrate data to a new schema?+<p>Q: Do you have to shut down the Happstack server every time you migrate data to a new schema? -<p>A: As far as I can tell, yes, you do, at least for the HAppS 0.9.2.1 release. -      <br>There is code under development in HAppS head that supposedly allows schema migration without -          shutting down HAppS, by using multiple HAppS instances and storing your data in a -          distributed way. But to keep things straightforward, in particular the zero-hassle-          HAppS install, I'm not going to cover anything on this tutorial that hasn't been-          released on hackage.-       <br>If your web startup requires zero downtime during data migrations,-           HAppS probably isn't for you, at least not yet.-           Then again, schema migrations using a traditional rdbms are-	   <a href="http://www.squidoo.com/database-refactoring">no picnic</a> either.+<p>A: No, but online migrations are a more advanced topic that will be covered in a future chapter.  <p>Q: Is macid safe? Could I wake up one day with corrupted data under _local and no way to recover from it? <p>A: Let's be realistic. -      Compared to, say, mysql, HAppS hasn't been stress-tested much in critical high-volume web sites. -      Or if it has, no one has shared their experiences.-      (This is something I hope to change by making it easier to get started with HAppS.)-      So whatever the HAppS developers say about reliability, personally I wouldn't be surprised if I encountered +      Compared to, say, mysql, Happstack hasn't been stress-tested much in critical high-volume web sites. +      On the other hand, stress testing is on the docket for the Happstack team and when more data is known+      I'll be including it in this tutorial.+      So whatever the Happstack developers say about reliability, personally I wouldn't be surprised if I encountered        some kind of data corruption problem as an early adopter.    <p> That said, the unix filesystem is pretty good at not losing your data --         a point <a href="http://www.paulgraham.com/vwfaq.html">famously</a> made by startup guru paul graham,@@ -81,17 +72,17 @@   <p> Macid serialization works by writing state change event data-    one file at a time. At server startup, HAppS "replays" all the information here+    one file at a time. At server startup, Happstack "replays" all the information here     in the order specified by the file names.     This is similar to the <a href="http://en.wikipedia.org/wiki/Transaction_log">database transaction log</a>     used by many rdbms systems.-<p> So, if I woke up one morning with my HAppS application in a corrupt, non-startable state and +<p> So, if I woke up one morning with my Happstack application in a corrupt, non-startable state and      my inbox full of angry customer email, probably what I would do is move files, one at a time,-    out of the serialization directory, last-file created first, and keep trying to restart HAppS.+    out of the serialization directory, last-file created first, and keep trying to restart Happstack.  <p> Q: What if my hard drive dies and I can't get my data back? <p>    A: Like with any other data storage system, if there's valuable data, you need to be making backups.-       In the case of HAppS data stored under _local, I would probably be <a href="http://www.rsync.net">rsyncing</a>+       In the case of Happstack data stored under _local, I would probably be <a href="http://www.rsync.net">rsyncing</a>        the _local directory to a remote server, or maybe multiple remote servers for extra safety.        For now I am not worried        about securing data, but when that day comes I'm pretty confident I'll be ok.
templates/maciddummydata.st view
@@ -7,7 +7,7 @@ When quickly hacking together a data-driven website, populating your app with dummy data is a chore you want to avoid wasting time on, so let's see how to do that.  -<p>Firstly, restart HAppS in a pristine state by either deleting or backing up your _local data directory+<p>Firstly, restart Happstack in a pristine state by either deleting or backing up your _local data directory    as described earler.  <p>Now, click <a href="/tutorial/initializedummydata">this link</a>.
templates/macidlimits.st view
@@ -7,13 +7,11 @@    max that out if your application has a lot of data, if you are limited to one computer.     <p>(See the <a href=/tutorial/macid-stress-test>stress test</a> chapter for more caveats.)-   The HAppS core developers have promised HAppS features+   The Happstack core developers have promised Happstack features    that will make it easy to share application state    across many computers, making scaling to ebay-sized proportions relatively straightforward: you -   just add more computers to your amazon EC2 cloud. This hasn't happened yet, and I have a feeling that-   when (if?) it does happen it won't be a panacea for every scaling problem. But for reference, -   the features are replication and sharding. You can search the happs googlegroup to learn more -   about this.+   just add more computers to your amazon EC2 cloud.  This feature is called Multimaster and will be+   documented more in a future chapter of this tutorial.   <p>My take is that, as currently implemented, Macid may be impractical for an app with tens of thousands of @@ -22,7 +20,7 @@    (E.G, facebook, reddit, message boards.)    This is true for web apps with a database backend as well, for more or less similar reasons. -<p>A realistic way to use HAppS with macid is to write an alpha version of an application +<p>A realistic way to use Happstack with macid is to write an alpha version of an application     using macid (no database),     and then add some other type of persistent hard drive storage (probably database)    outside of macid only if it becomes necessary. @@ -37,23 +35,25 @@    to rewrite a lot of your state code, but it will be worth it, because venture capitalists will be     knocking down your door. (You're the next facebook, ebay, etc.)  -<p>In the best case scenario, the HAppS core team will deliver on their promise of easy scaling-   via ec2 and similar cloud solutions, in which case you won't even have to deal with a state rewrite.+<p>In the best case scenario, you can use Multimaster to spread your application across multiple machines and+   not even have to worry about a state rewrite.    <p>Since macid is available and macid is more straightforward to use than a database layer,    coding in a database from a start is a form of insidious premature optimization, if you buy    the argument that using a database from the start introduces significant maintenance overhead. +<!-- <p>Having made this lengthy argument for macid... I can see myself rewriting a future tutorial chapter    that describes using HAppS with takusen and postresql as a backend rather than macid. But that hasn't     happened yet. If it needs to, it will. &nbsp; :) &nbsp;+-->  <hr> -<p>Well folks, that more or less wraps up the <a href="http://www.happstutorial.com">happs tutorial</a>.+<p>Well folks, that more or less wraps up the <a href="http://tutorial.happstack.com">happstack tutorial</a>. -<p>You can now go out and use happs, using happstutorial as a template to get you started if appropriate.+<p>You can now go out and use happstack, using happs-tutorial as a template to get you started if appropriate.  <p>The remaining chapters are of an appendix nature. Nice to have, but not fundamental. 
templates/macidmigration.st view
@@ -5,18 +5,18 @@ <p>People who have been following this tutorial for a while may have noticed that every time I come out with     a new version, the existing users and jobs disappear and we start out with a blank slate again. -<p>That's because changing the data schema of a happs-with-macid web app (aka migrating) is a chore.+<p>That's because changing the data schema of a happstack-with-macid web app (aka migrating) is a chore.    <p>It's a chore with traditional web apps too. But it's probably more difficult with macid, especially    given the sparse documentation. -<p>This isn't a problem for happstutorial.com, because typically there are only a +<p>This isn't a problem for tutorial.happstack.com, because typically there are only a     few dozen users and jobs, plus whatever dummy data I've entered myself. Who cares?    So far, rather than migrating, I've just wiped the slate clean.  <p>However, that isn't going to work  for your latest facebook-killer. -<p>The good news is, there is a way to migrate HAppS state through various iterations, it's sufficiently+<p>The good news is, there is a way to migrate Happstack state through various iterations, it's sufficiently    documented if you know where to look, and it's not too painful once you've gotten used to it.  <p>The main challenge is finding documentation. @@ -75,7 +75,7 @@   reason why I started splitting things into multiple files to begin   with, which I later regretted because it made migration that much harder.)   -<p> Don't call HAppS State "State", as this conflicts with the+<p> Don't call Happstack State "State", as this conflicts with the   State datatype in Control.Monad.State. I usually call my state   datatype AppState. @@ -125,7 +125,9 @@     work now. But of course you have lost all your data, and need to rollback the live data again for the next step.      <p> Now make necessary changes in code for migration.   -    See eelco's and my uploads to happs google group (tk happs tutorial). Summary is:+    See eelco's and my uploads to +    <a href="http://groups.google.com/group/HAppS/browse_thread/thread/98e129e6349e4b0b/0e174e9c36edbd72?lnk=gst&q=eelco+tphyahoo#0e174e9c36edbd72">happs google group</a>. +    Summary is:     modify the version instance for AppState and add a migrate instance, allong the following lines. First,   <p> import qualified StateVersions.AppState1 as Old
templates/macidstresstest.st view
@@ -1,9 +1,9 @@ <h3>Macid stress test</h3> -<p>HAppS is a new, relatively unproven technology.+<p>Happstack is a new, relatively unproven technology.  <p>So I asked myself this question.-   Will HAppS allow me to scale the toy job board I created for happs-tutorial +   Will Happstack allow me to scale the toy job board I created for happs-tutorial     into a high-volume, high concurrency,    large-user count type job board?    You know, the kind that <a href=http://www.monster.com>might make money</a>?@@ -21,10 +21,12 @@  <p>I failed to achieve this goal.  +$! Okay, so seriously, should this part actually stay in or not? !$+ <p>Or, more optimistically,    I am declaring defeat for the time being so I can put a version 5 release up on hackage    and share what I learned with a wider audience. In a future release, I still intend  to show how you can scale -   a HAppS app up to large numbers of users! :)+   a Happstack app up to large numbers of users! :)  <p>Sidenote: as (I think) 37 Signals said, "you don't have a scaling problem." I certainly don't have a scaling problem,    and if I did I would probably be jumping for joy even while I was tearing my hair out trying to figure out@@ -34,11 +36,11 @@    in the works that might actually run into the RAM limit.   <p>That said, the most important lesson I learned is that putting all application state into macid won't work if you -   have a lot of data -- not even if you have one of those <a href="tk 16 gb box at amazon">heavy iron</a>+   have a lot of data -- not even if you have one of those <a href="http://aws.amazon.com/ec2/instance-types/">heavy iron</a>    boxen from amazon web services, or for that matter a whole virtual data center full of them.    16GB of RAM gets used up surprisingly fast if you are using ram for everything.    The cloud is no substitute for-   <a href="tk ebay link">thinking your application's transactional requirements through</a>+   <a href="http://highscalability.com/">thinking your application's transactional requirements through</a>    and coding to them.    <p>So, if I make good on the promise I made above about scaling this toy application up to 20,000,000 data records, you @@ -67,10 +69,11 @@  <p><font color=orange>    Before running any of these stres tests yourself, I recommend limiting the amount of swap your happs server-   is allowed to use via the ulimit command. Otherwise stressing HAppS will result in your computer grinding to a crawl.+   is allowed to use via the ulimit command. Otherwise stressing Happstack will result in your +   computer grinding to a crawl.    But if you limit your swap, the process just gets killed, which is a lot less annoying.     Since every macid action is serialized, you will still see the data your stress test-   inserted up to the point it was killed when you reestart HAppS. I have +   inserted up to the point it was killed when you reestart Happstack. I have      <p>ulimit -v 262144 # (256M of swap)  @@ -96,7 +99,8 @@     20,000 users (4 million jobs) before performance degraded because of the amount of data being kept in ram.     (With 10,000 users it ran without breaking a sweat, 20,000 was doable but went into swap.) -<p> Obviously this chapter of the happs tutorial is in progress and, to some extent, in flux. There is a -    thread about the stress tests in the <a href="tk">haspps googlegroup</a> that you can have a look at+<p> Obviously this chapter of the happs-tutorial is in progress and, to some extent, in flux. There is a +    thread about the stress tests in the <a href="http://groups.google.com/group/HAppS/browse_thread/thread/e5e8458edd34cde7/a3f0655bb66ecce2?lnk=gst&q=stress+testing#a3f0655bb66ecce2">+    happstack googlegroup</a> that you can have a look at     for even more information. I also say a few more words about what macid isn't good for in      <a href="/tutorial/macid-limits">macid limits</a>.
templates/macidupdatesandqueries.st view
@@ -1,7 +1,7 @@ <h3>Macid updates and queries</h3>  <p>Clicking <a href="/tutorial/initializedummydata">initializedummydata</a> passed off control to -  spAddDummyData in <a href="/src/Controller.hs">Controller.hs</a>. Keep that .hs file open in another window,+  spAddDummyData in <a href="/src/ControllerStressTests.hs">ControllerStressTests.hs</a>. Keep that .hs file open in another window,   scrolled down to spAddDummyData, to follow along.  <p>In spAddDummyData, pay particular attention to these two statements@@ -121,7 +121,7 @@    whether it's a query or an update, a quick way to find out is to ask ghci for information about     the lowercase function it's based on. -<p>Macid is one of the most powerful features of HApps, but I found it hard to learn. +<p>Macid is one of the most powerful features of Happstack, but I found it hard to learn.     There are a lot of types, and those signatures can get cryptic.     It took me a while to get as comfortable with the macid way of doing things as I was with sql.    But when I finally got it I was glad I made the effort.@@ -138,7 +138,7 @@       source code. See how queries and updates are used in the job board:       when logging in, when creating or deleting jobs,       when editing a user profile, and so on.-  <li>start using HAppS with macid for your own web apps+  <li>start using Happstack with macid for your own web apps </ul>  
templates/mainfunction.st view
@@ -22,24 +22,24 @@  <p>The second string argument controls where serialized app state will    be stored: in this case, under "_local/happs-tutorial". This is mainly for convenience, so the state-   directory is the same whether we are running in ghci or ghc. (HAppS out of the box just looks+   directory is the same whether we are running in ghci or ghc. (Happstack out of the box just looks    at the executable name, which in the case of ghci is something weird.) -<p>The third argument tells HAppS what to use as a controller, in the MVC sense.-   Basically, how to handle http requests. If dynamicTemplateReload is true, HAppS reloads-   reloads the templates from the template directory on every request, otherwise it just does this+<p>The third argument tells Happstack what to use as a controller, in the MVC sense.+   Basically, how to handle http requests. If dynamicTemplateReload is true, Happstack reloads+   the templates from the template directory on every request, otherwise it just does this    once at app startup. The former is nice for development because you can change content on the fly,    the latter is better when you want to maximize server performance uner heavy load and the content    has stabilized.  -<p>The fourth argument tells happs what data structure to use for application state.-   We'll cover about the HAppS state system in depth, but later.</p>+<p>The fourth argument tells Happstack what data structure to use for application state.+   We'll cover about the Happstack state system in depth, but later.</p>    -<p>To learn more about what the controller function is doing,-   , open this file in ghci: cd src; ghci Main.hs and have a look at these functions using ghci :info .</p>+<p>To learn more about what the controller function is doing, open this file in ghci: cd src; +ghci Main.hs and have a look at these functions using ghci :info .</p>  <p> <br>*Main> :i controller@@ -81,10 +81,10 @@ $!  <p>Now you have a choice about what to read next.</p> -<p>If you are in a hurry to write a HAppS application without delving too much into what's going on behind the scenes,+<p>If you are in a hurry to write a Happstack application without delving too much into what's going on behind the scenes, read , which looks at what's happening in the controller code.</p> -<p>If you want to understand the HAppS type system in more detail, read <a href="understanding-happs-types">understanding HAppS types</a>.+<p>If you want to understand the Happstack type system in more detail, read <a href="understanding-happs-types">understanding Happstack types</a>. </p> !$ 
templates/menubarmenu.st view
@@ -1,6 +1,6 @@ [("/tutorial/home","home"),- ("/tutorial/consultants","happs developers"),- ("/tutorial/consultantswanted","happs developers wanted"),- ("/tutorial/jobs","happs jobs"),+ ("/tutorial/consultants","happstack developers"),+ ("/tutorial/consultantswanted","happstack developers wanted"),+ ("/tutorial/jobs","happstack jobs"),  ("/tutorial/feedback","feedback")]  
− templates/missinghappsdocumentation.st
@@ -1,42 +0,0 @@-<h3>The missing HAppS documentation, and what to do about it</h3>--<p>When I started building this demo/tutorial, the documentation for HAppS was-<a href="http://forums.somethingawful.com/showthread.php?threadid=2947408">cringeworthy</a>.-So  bad that honestly I wasn't sure where to start with trying to fix it.-</p>--<p>-Instead of tackling the documentation probablem directly by doing the obvious thing-and writing patches for haddock, I decided to create an easy-to-install demo project-that included clear step-by-step instructions for getting from zero to final product. -There is still a lot of work to be done in the documentation and usability department.-That haddock still needs to be written, and more than anything, HAppS needs to be used and discussed-by more people in forums, blogs, wikis, and real applications like <a href="hackage.haskell.org">hackage</a> -(which I think is already using HAppS or will be soon). -These things tend to snowball. My hope is that this tutorial will play a part in bringing -commercial-use HAppS and haskell into the real world. --<p>-I figured what the HAppS-ecosystem needs most, after better documentation, is the opportunity for developers to make real money-doing real work by leveraging this technology. --<p>In this spirit, the demo project is a job board.-It's full of dummy data at the moment, but feel free to post real jobs here if you have them on offer.-(But cc the happs googlegroup as well! &nbsp; :) &nbsp; )--<p>-My hope is that that this material will tempt people to try HAppS out.-Both curious developers, and investors that want a non-trivial website and are willing to-<a href="http://www.paulgraham.com/avg.html">gamble</a> on a new technology-if it can get the job better, and most importantly, faster.-With enough users, I believe the documentation and other "batteries not included" issues with-HAppS will matter less and less, as the gaps get plugged in a gradual way.--<p>-On this note, I am currently available for haskell, HAppS, and startup consulting: thomashartman1 at gmail. -Write me if there is anything you need.-</p>--<p><a href="/tutorial/getting-started">Now let's get started.</a></p>-
templates/prerequisites.st view
@@ -3,18 +3,15 @@ <p>For best results with this tutorial, you hopefully have the following.</p>  <ul>-  <li>Basic knowledge of haskell and html.</li>+  <li>Basic knowledge of Haskell and html.</li>   <li>A somewhat modern computer. I have a PIII laptop with 512M of ram. </li>-  <li>A modern operating system. You should be able to develop HAppS applications using windows, mac, or linux.+  <li>A modern operating system. You should be able to develop Happstack applications using Windows, OS X, or Linux.         <ul>-	  <li>Linux Notes: I work with the latest <a href="http://www.ubuntu.com">ubuntu</a> linux distribution,-	  which is currently hardy heron. Works for HAppS pretty much out of the box </li>-	  <li>Win32 Notes: <a href="http://groups.google.com/group/fa.haskell/browse_thread/thread/e1f1d3bc65074b9c">-	                      You may have to darcs get HAppS-State head, and/or tweak the cabal file.</a> -	  </li>- 	</ul>+	  <li>  Happstack-server 0.1 does not currently build on Windows, but this is considered a bug and will +	        be rectified soon.+	</ul>       -  <li>If you want to create a public web site based on the materials here, I recommend getting either a private server (more expensive) or a virtual private server (less expensive) which gives you admin privileges to install the packages and software you need. I use the cheapest virtual private server plan at <a href="http://www.linode.com">linode</a> to host this tutorial publicly. I wouldn't try hosting a HappS project publicly using a shared hoster like <a href="http://www.dreamhost.com">dreamhost</a> that doesn't give you root powers to administer your server. (Dreamhost is great for other things, like cheap storage for online images.)</li>+  <li>If you want to create a public web site based on the materials here, I recommend getting either a private server (more expensive) or a virtual private server (less expensive) which gives you admin privileges to install the packages and software you need. I use the cheapest virtual private server plan at <a href="http://www.linode.com">linode</a> to host this tutorial publicly. I wouldn't try hosting a Happstack project publicly using a shared hoster like <a href="http://www.dreamhost.com">dreamhost</a> that doesn't give you root powers to administer your server. (Dreamhost is great for other things, like cheap storage for online images.)</li>   <li>More requirements are described in the instructions for       <a href="/tutorial/run-tutorial-locally">installing and running this tutorial locally</a>.       However, if you have the basics ingredients above you should be okay.
templates/runtutoriallocally.st view
@@ -2,18 +2,15 @@  <p> Before going further, you may want to inform yourself about the <a href=/tutorial/prerequisites>basic prerequisites</a>, both knowledge and equipment, you need to make the best use of this tutorial </p> -<p>This tutorial is cabalized. You can install it, and chase down all the HAppS dependencies it needs, simply by doing +<p>This tutorial is cabalized. You can install it, and chase down all the Happstack dependencies it needs, simply by doing  <p>cabal install happs-tutorial -<p>The cabal installation is kind of slow. I've had it take up to 15 minutes. But it should succeed in one-shot. (This is a symptom of the <a href="/tutorial/happs-slow-linking-bug">HAppS slow linking bug</a>.)- <p><font color=orange>Unfortunately, the above statement is false on ghc 6.10.1 at the moment, because     <br>the crypto package won't cabal install out of the box. You can build crypto first by doing     <br>darcs get http://code.haskell.org/crypto    <br>cd crypto    <br>cabal install-   <br>Hopefully the crypto package will get a version bump on hackage soon so  I can delete this ugly orange paragraph.</font>+   <br>When ghc 6.10.2 is released the hackage version of Crypto should build right out of the box</font>  <p>If you've never used cabal install or need more detailed info....</p> @@ -28,7 +25,7 @@    download the tar file of the latest version of cabal from hackage, unzip that and run bootstrap.sh.  </li>-    <li>If you want to check out the latest version of this tutorial, install <a href="http://www.darcs.net">Darcs</a> and check out the repo with darcs get http://code.haskell.org/happs-tutorial</li>+    <li>If you want to check out the latest version of this tutorial, install <a href="http://www.darcs.net">Darcs</a> and check out the repo with darcs get http://patch-tag.com/publicrepos/happstack-tutorial</li> </ul>  <p>The reason I cabalized happs-tutorial was for the dependency chasing you get with cabal install,@@ -46,11 +43,9 @@ Once you have the tar file, untar this somewhere, cd into that, build and run here as described below. Or, you could darcs get happs-tutorial and run there.  -<p>To run the app, either do ./hackInGhci.sh and then execute runInGhci inside Main.hs, or recompile-   the executable using ./runServerWithCompileNLink.sh.-   Really you only need to be inside ghci if you are experiencing-   the <a href="/tutorial/happs-slow-linking-bug">slow link time issue</a>.-   This isn't a problem at time of writing but seems to crop up from time to time.+<p>To run the app, either do ./hackInGhci.sh and then execute runInGhci inside Main.hs or run the happs-tutorial+      executable created by cabal install, with the caveat that you do need to be in the base directory of happs-tutorial.+   You shouldn't need to run inside of ghci, but the option is available. <p> Shutdown with ctrl-c. <p>@@ -69,10 +64,7 @@   how you should be running for a production application.  <p>-You may also want to <a href="start-happs-on-boot">start HAppS on boot</a>.--<p>Next up is the <a href="/tutorial/main-function">HAppS server main function</a>.</p>--+You may also want to <a href="start-happstack-on-boot">start Happstack on boot</a>. +<p>Next up is the <a href="/tutorial/main-function">Happstack server main function</a>.</p> 
− templates/starthappsonboot.st
@@ -1,26 +0,0 @@-<h3>Start HAppS Automatically At Boot Time</h3>--<p>What happens if your HAppS deployment server experiences a power outage?</p>--<p>Or what if the HAppS process just dies for-<a href="http://code.google.com/p/happs/issues/detail?id=40">mysterious reasons?</a>--<p>The way I deal with this both these issues with a public-facing happs application is to have a cron job that runs every minute, that will start the happs application if it isn't running.</p>--<p>-  thartman@thartman-laptop:~/happs-tutorial>crontab -l-<br>* * * * *   /home/thartman/happs-tutorial/happs-tutorial.cron.sh-<br>-<br>thartman@thartman-laptop:~/happs-tutorial>cat happs-tutorial.cron.sh-<br># this is a workaround to a problem that my happs app dies for reasons described at-<br># http://code.google.com/p/happs/issues/detail?id=40-<br># generate the executable first by running runServer.sh-<br># then add this file to your crontab so you have something like-<br># thartman@thartman-laptop:~>crontab -l-<br># * * * * *   /home/thartman/happs-tutorial/happs-tutorial.cron.sh-<br>-<br>if [ -z "`pgrep happs-tutorial`" ];-<br>  then cd ~/happs-tutorial-<br>          ./happs-tutorial >happs-tutorial.cron.out 2>happs-tutorial.cron.err-<br>fi-</p>
+ templates/starthappstackonboot.st view
@@ -0,0 +1,23 @@+<h3>Start Happstack Automatically At Boot Time</h3>++<p>What happens if your Happstack deployment server experiences a power outage?</p>++<p>The way I deal with this issue with a public-facing Happstack application is to have a cron job that runs every minute, that will start the happs application if it isn't running.</p>++<p>+  thartman@thartman-laptop:~/happs-tutorial>crontab -l+<br>* * * * *   /home/thartman/happs-tutorial/happs-tutorial.cron.sh+<br>+<br>thartman@thartman-laptop:~/happs-tutorial>cat happs-tutorial.cron.sh+<br># this is a workaround to a problem that my happs app dies for reasons described at+<br># http://code.google.com/p/happs/issues/detail?id=40+<br># generate the executable first by running runServer.sh+<br># then add this file to your crontab so you have something like+<br># thartman@thartman-laptop:~>crontab -l+<br># * * * * *   /home/thartman/happs-tutorial/happs-tutorial.cron.sh+<br>+<br>if [ -z "`pgrep happs-tutorial`" ];+<br>  then cd ~/happs-tutorial+<br>          ./happs-tutorial >happs-tutorial.cron.out 2>happs-tutorial.cron.err+<br>fi+</p>
templates/stringtemplatebasics.st view
@@ -2,7 +2,7 @@  <p>In the previous section we rendered a template in ghci using the following command -<p>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" ( renderTemplateGroup templates [] "templates-dont-repeat-yourself" ) +<p>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" ( renderTemplateGroup templates ([]::[(String,String)]) "templatesdontrepeatyourself" )   <p>Let's look more carefully at these functions. @@ -41,9 +41,9 @@ <p> For a more in depth look at StringTemplate, see the following:  <ul><li><a href="http://www.stringtemplate.org/">stringtemplate.org</a>-    <li><a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HStringTemplate-0.2">+    <li><a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HStringTemplate">     HStringTemplate, a haskell port of StringTemplate on hackage</a>     <li><a href="http://fmapfixreturn.wordpress.com/tag/hstringtemplate/">fmapfixreturn, Sterling Clover's blog</a> </ul> -<p>And now, on to <a href="/tutorial/introduction-to-macid">macid</a>.+<p>And now, on to <a href="/tutorial/debugging">debugging</a>.
templates/templatesdontrepeatyourself.st view
@@ -5,7 +5,7 @@  <p>This is why you need a templating system.</p> -<p>HAppS doesn't care much what templating system you use.  I use the+<p>Happstack doesn't care much what templating system you use.  I use the <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HStringTemplate">HStringTemplate</a> package to get the job done, so that's the syntax you'll be seeing in what follows. </p> @@ -30,26 +30,26 @@  <p> *Main> :m +Misc View Text.StringTemplate -<br>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" \$ renderTemplateGroup templates [] "templates-dont-repeat-yourself" +<br>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" \$ renderTemplateGroup templates ([]::[(String,String)]) "templatesdontrepeatyourself"   <p>and then opening the file output.html in firefox. (On ubuntu, in ghci, I just do ":! firefox output.html &" and the file opens in a new tab in firefox.)  <p>To see how this page would look if you were logged in:  -<p>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" \$ renderTemplateGroup templates [("loggedInUser","DarthVader")] "templates-dont-repeat-yourself" +<p>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates" ; writeFile "output.html" \$ renderTemplateGroup templates [("loggedInUser","DarthVader")] "templatesdontrepeatyourself"   <p>and reopen output.html in your browser.-+<!-- <p>As you may have noticed, the html written by the above command is only the current content pane, not the header or table of content links. To render a full page from ghci, as it would appear for a logged in user, try -<p>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates"; writeFile "output.html" \$ tutlayout (RenderGlobals templates (Just \$ User "Darth" "" undefined undefined)) [] "templates-dont-repeat-yourself"+<p>*Main Misc View Text.StringTemplate> do templates <- directoryGroup "templates"; writeFile "output.html" \$ tutlayout (RenderGlobals templates (Just \$ User "Darth" "" undefined undefined)) [] "templatesdontrepeatyourself"  <p>Note that tutlayout is a pure function. The only IO is in fetching the templates with directoryGroup. Currently the tutorial does a directory read every time the main webserver handler loops, but it could just as easily only do the directory read once at app startup time. This would mean a lot less disk reads, but of course you would have to stop/start the server whenever a template changed to see your changes. Which setup you want depends on whether you are willing to sacrifice development convenience for a snappier server.-I opted for convenience, but it would be easy to change -- just move the directoryGroup command out of the+I opted for convenience, but it would be easy to change - just move the directoryGroup command out of the tutorial handler and into Main.hs~ before the server starts. Try it as an exercise if you like.  <p>As a side note, you can get away with passing undefined values to the tutlayout function because@@ -59,6 +59,7 @@ but other than that the layout is complete.  <p>You can get a sense for how this all works by looking at the tutlayout function in <a href="/src/View.hs">src/View.hs</a>.+-->  <p>It's not too much fun to develop a web page by outputting a string to a static file and then opening  it in a browser every time something changes, so the next thing you might want to try is actually@@ -68,8 +69,8 @@ $! <p>You might have also noticed that the table of contents-style navigation links in the left pane change colors depending on what page is selected. You could have a look at the the-<a href="/templates/tableofcontents.st">/templates/tableofcontents.st</a> to get another taste of how templating works.-Here, HAppS looks at each request to determine what the page was called. +<a href="/templates/toc.st">/templates/toc.st</a> to get another taste of how templating works.+Here, Happstack looks at each request to determine what the page was called.  If the page matches anything in a certain list, the link class gets set to an "active" value, otherwise it gets a default value.  !$
templates/thanks.st view
@@ -6,17 +6,14 @@ <a href="http://www.dbpatterson.com/">D.B. Patterson</a>.  <p>-Justin Bailey contributed a patch for displaying haskell files colorized.+Justin Bailey contributed a patch for displaying Haskell files colorized. -<p>Lemmih, who is -- I think -- the main happs core developer at the present time,-   helped me think through the macid stress test chapter, and numerous other issues.+<p>Lemmih, who was the main happs core developer during early development of this tutorial,+   helped with the macid stress test chapter, and numerous other issues.  <p>Eelco Lempsink, and Jeremy Shaw (nhepthanlabs) provided valuable assistance     via code contributed and questions answered in the happs googlegroup. -<p>Did I forget someone? Drop me a line and I'll add you to the list.--<p>Oh, thanks for everyone who encouraged me to keep working on this. -   It's been a long slog, but I think it's been worth it. +<p>Thomas Hartman, the original author, put an incredible amount of work in this tutorial. -<p>Hey, that makes it sound like I'm done. But a tutorial is never done :)+<p>Special thanks go to Matthew Elder and the rest of the Happstack team for resurrecting HAppS.
templates/toc.st view
@@ -1,7 +1,6 @@-[("/tutorial/home","build a web 2.0 app in happs")- , ("/tutorial/why-happs-is-cool","why happs is cool")- , ("/tutorial/missing-happs-documentation","the missing happs documentation")- , ("/tutorial/getting-started","getting started with happs")+[("/tutorial/home","build a web 2.0 app in happstack")+ , ("/tutorial/why-happstack-is-cool","why happstack is cool")+ , ("/tutorial/getting-started","getting started with happstack")  , ("/tutorial/prerequisites","prerequisites")  , ("/tutorial/run-tutorial-locally","cabal install me")  , ("/tutorial/main-function","main")@@ -20,8 +19,7 @@  , ("/tutorial/macid-stress-test","macid stress test")  , ("/tutorial/macid-limits","limitations of macid")  , ("/tutorial/foreignchars","foreign characters")- , ("/tutorial/start-happs-on-boot","cron jobs")+ , ("/tutorial/start-happstack-on-boot","cron jobs")  , ("/tutorial/thanks","thanks")  , ("/tutorial/ghci-floundering-askdatastore","appendix (floundering in ghci)") ]-
templates/understandinghappstypes.st view
@@ -21,9 +21,9 @@ <br>  -- Defined in HAppS.Server.SimpleHTTP </p> -<p>So basically what this tells you is that the meat of a HAppS application is a list of ServerParts, which themselves are a wrapper over a function that takes an HTTP request to a response.</p>+<p>So basically what this tells you is that the meat of a Happstack application is a list of ServerParts, which themselves are a wrapper over a function that takes an HTTP request to a response.</p> -<p>I use ghci info a lot, and you should too as you are learning HAppS. This kind of comfort with the HAppS type system is particularly important at the present time, when the HAppS documentation is relatively sparse</p>+<p>I use ghci info a lot, and you should too as you are learning Happstack.  It's very useful for understanding how various pieces of HAppS fit together.  <p><a href="http://www.haskell.org/haskellwiki/HAppS_tutorial">HAppsTutorial2</a> at the haskell wiki has a more in-depth-look at thinking behind HAppS and the HAppS type system. </p>+look at thinking behind Happstack and the Happstack type system. </p>
− templates/whyhappsiscool.st
@@ -1,61 +0,0 @@-<h3>Look Ma, No Database</h3>--<p>There are a lot of advantages to programming in a typed functional language like haskell.-   Certain bugs, like misuse of global variables, are virtually impossible unless you bend over backwards-   to do things wrong. Code tends to be incredibly short, and modular. The haskell community is very -   friendly, and with coders in every time zone the #haskell irc channel seems well populated -   seemingly 24 hours a day.--<p>But we're here to talk about HAppS, not haskell.-   -<p>What got me interested in HAppS was a strongly held feeling that as-modern software systems tend toward ever increasing complexity,-database usage is an unnecessary source of complication that-<a href=http://gilesbowkett.blogspot.com/2007/05/sql-unnecessary-in-haskells-happs.html>should be factored out</a> where possible.--<p>Ruby's <a href="http://rubyonrails.com/">rails</a> and python's <a href="http://www.djangoproject.com">django</a> have become popular largely because of their <a href="http://en.wikipedia.org/wiki/Object-relational_mapping">object relational mapping</a> systems,-which hide the complexity of database engines by converting application data manipulation logic into sql.-When I first used an ORM, it felt like a huge improvement over writing sql statements every time I wanted to manipulate-an application's state. But pretty soon ORMs started seeming hackish to me too. At some point,-the metaphors I wanted to use just <a href="http://en.wikipedia.org/wiki/Object-Relational_impedance_mismatch">broke down</a>.---$!Or to put it another way, that <a-href=http://gilesbowkett.blogspot.com/2007/05/sql-unnecessary-in-haskells-happs.html>sql-is an ugly hack</a>. !$-<p> HAppS is haskell's answer to rails and django (and perl's <a href="http://www.catalystframework.org/">catalyst</a>, -    and <a href="http://www.php.net">php</a>).--    With HAppS, there is no wrangling data-    structures into and out of the database, because there is no database.  You use whatever data-    structures are natural to your application, and serialize them-    transparently using-    <a href="http://www.google.com/search?q=scrap+your+boilerplate">powerful</a>-    machinery</a> that's running <a href="http://www.haskell.org/th/">behind the-    scenes</a>. And if there are existing databases that you need to connect to, you can do that too -    -- you're not locked in to using macid for everything.--<p><a href="http://hackage.haskell.org/packages/archive/HAppS-State/0.9.2.1/doc/html/HAppS-State.html#1">MACID</a>,-the HAppS storage mechanism, is no vanilla serialization layer that will-start acting in weird ways when an application has many concurrent users doing possibly conflicting things. -By <a href="http://research.microsoft.com/~simonpj/papers/stm/">leveraging haskell's type system</a> -(see composable memory transactions paper),-you get the same <a href="http://en.wikipedia.org/wiki/ACID">ACID</a> guarantees that-normally only come with a database.  </p>---<p>There are some <a href="/tutorial/macid-stress-test">limitations</a> to using macid -   as a datastore that you should familiarize yourself with-   if you are looking into using HAppS for heavy-usage transactional applications.-   But long term, HAppS with macid looks promising enough that I've started-   using it as a platform for building commercial web 2.0 type apps. (My first -   commercial happs app will be public soon, so stay tuned on -   <a href=http://www.techcrunch.com>techcrunch</a>. &nbsp; :) &nbsp; )--<p>In short, HAppS is awesome, and webmonkeys everywhere should use it. Except...--<p>Well, nothing is <a href="/tutorial/missing-happs-documentation">perfect</a>.----
+ templates/whyhappstackiscool.st view
@@ -0,0 +1,61 @@+<h3>Look Ma, No Database</h3>++<p>There are a lot of advantages to programming in a typed functional language like Haskell.+   Certain bugs, like misuse of global variables, are virtually impossible unless you bend over backwards+   to do things wrong. Code tends to be incredibly short, and modular. The haskell community is very +   friendly, and with coders in every time zone the #haskell irc channel seems well populated +   seemingly 24 hours a day.</ p>++<p>But we're here to talk about Happstack, not Haskell.</ p>++<p>Happstack has its origins in the HAppS project.  Happstack is a successor to HAppS under the +leadership of Matthew Elder and the work of the Happstack team</ p>+   +<p>What got me interested in HAppS was a strongly held feeling that as+modern software systems tend toward ever increasing complexity,+database usage is an unnecessary source of complication that+<a href=http://gilesbowkett.blogspot.com/2007/05/sql-unnecessary-in-haskells-happs.html>should be factored out</a> where possible.++<p>Ruby's <a href="http://rubyonrails.com/">Rails</a> and Python's <a href="http://www.djangoproject.com">Django</a> have become popular largely because of their +	  <a href="http://en.wikipedia.org/wiki/Object-relational_mapping">object relational mapping</a> systems,+which hide the complexity of database engines by converting application data manipulation logic into sql.+When I first used an ORM, it felt like a huge improvement over writing sql statements every time I wanted to manipulate+an application's state. But pretty soon ORMs started seeming hackish to me too. At some point,+the metaphors I wanted to use just <a href="http://en.wikipedia.org/wiki/Object-Relational_impedance_mismatch">broke down</a>.+++$!Or to put it another way, that <a+href=http://gilesbowkett.blogspot.com/2007/05/sql-unnecessary-in-haskells-happs.html>sql+is an ugly hack</a>. !$+<p> Happstack is Haskell's answer to Rails and Django (and Perl's +<a href="http://www.catalystframework.org/">Catalyst</a>, +    and <a href="http://www.php.net">PHP</a>).++    With Happstack, there is no wrangling data+    structures into and out of the database, because there is no database.  You use whatever data+    structures are natural to your application, and serialize them+    transparently using+    <a href="http://www.google.com/search?q=scrap+your+boilerplate">powerful</a>+    machinery</a> that's running <a href="http://www.haskell.org/th/">behind the+    scenes</a>. And if there are existing databases that you need to connect to, you can do that too +    -- you're not locked in to using macid for everything.++<p>MACID,+the Happstack storage mechanism, is no vanilla serialization layer that will+start acting in weird ways when an application has many concurrent users doing possibly conflicting things. +By <a href="http://research.microsoft.com/~simonpj/papers/stm/">leveraging Haskell's type system</a> +(see composable memory transactions paper),+you get the same <a href="http://en.wikipedia.org/wiki/ACID">ACID</a> guarantees that+normally only come with a database.  </p>+++<p>There are some <a href="/tutorial/macid-stress-test">limitations</a> to using macid +   as a datastore that you should familiarize yourself with+   if you are looking into using Happstack for heavy-usage transactional applications.+   But long term, Happstack with macid looks promising enough that the original author started+   using it as a platform for building commercial web 2.0 type apps such as+   <a href=http://www.patch-tag.com>patch-tag</a>++<p>In short, Happstack is awesome, and webmonkeys everywhere should use it.++<p>Let's get <a href="/tutorial/getting-started">started</a>.