
I just noticed those. I think that came from hpaste. The first mail
was a cut and paste from a post I made there. When I went to look at
your reply, I had the very same question as you.
On Mon, Mar 28, 2011 at 7:51 PM, Luke Palmer
On Mon, Mar 28, 2011 at 6:28 PM, Michael Litchard
wrote: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
Ready or not, here I come. What is the purposes of these 368 numbers? Luke
I'm working off of a example file from Yesod, ajax.lhs I've made an important change in types, and this has resulted in having to make the old code conform to the change. I will point out the specifics, then present my question. In the event I failed to include important information, I will paste in my code as well as the prototype.
[Original]
getHomeR :: Handler () getHomeR = do Ajax pages _ <- getYesod let first = head pages redirect RedirectTemporary $ PageR $ pageSlug first
[Changed]
getHomeR :: Handler () getHomeR = do Tframe pages _ <- getYesod let first = head pages redirect RedirectTemporary $ PageR $ pageSlug first
Error Message
test.lhs:62:4: Constructor `Tframe' should have 2 arguments, but has been given 1 In the pattern: Tframe pages In a stmt of a 'do' expression: Tframe pages <- getYesod **** This is not what I wrote ***** In the expression: do { Tframe pages <- getYesod; content <- widgetToPageContent widget; hamletToRepHtml (hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue (do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "<!DOCTYPE html><html><head><title>"; (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (Main.pageTitle content); .... })) }
As far as I can tell, I only made a cosmetic change. I don't know what's going on here.
[Original]
data Page = Page { pageName :: String , pageSlug :: String , pageContent :: String ******** I'm going to change this ********** }
[Changed]
data Page = Page { pageTitle :: String , pageSlug :: String -- ^ used in the URL , pageContent :: IO String ******** This is the change ******* }
Here's where I run into trouble
[Original]
json page = jsonMap [ ("name", jsonScalar $ pageName page) , ("content", jsonScalar $ pageContent page) ******** I'm going to change this ******** ]
[My changes]
json page = jsonMap [ ("name", jsonScalar $ Main.pageTitle page) , ("content", jsonScalar $ liftIO $ pageContent page) ******* This is the change *********** ]
Here's the compiler error
test.lhs:107:35: Couldn't match expected type `Char' against inferred type `[Char]' Expected type: String Inferred type: [String] In the second argument of `($)', namely `liftIO $ pageContent page' In the expression: jsonScalar $ liftIO $ pageContent page Failed, modules loaded: none.
I'd appreciate a discussion about why this is wrong, and perhaps clues as to what is right.
Last problem, stemming from the change in type to IO String. I don't have a clue as to what change I should make.
test.lhs:100:25: No instance for (Text.Blaze.ToHtml (IO String)) arising from a use of `Text.Blaze.toHtml' at test.lhs:(100,25)-(103,3) Possible fix: add an instance declaration for (Text.Blaze.ToHtml (IO String)) In the second argument of `(.)', namely `Text.Blaze.toHtml' In a stmt of a 'do' expression: (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (pageContent page) In the first argument of `hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue', nam
ely `do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "<h1>"; (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (Main.pageTitle page); (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . preEscapedString) "</h1><article>"; (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad . Text.Blaze.toHtml) (pageContent page); .... }'
And finally, both files can be found below, if it is necessary to look at them.
[Original]
<p>We're going to write a very simple AJAX application. It will be a simple site with a few pages and a navbar; when you have Javascript, clicking on the links will load the pages via AJAX. Otherwise, it will use static HTML.</p>
<p>We're going to use jQuery for the Javascript, though anything would work just fine. Also, the AJAX responses will be served as JSON. Let's get started.</p>
{-# LANGUAGE ScopedTypeVariables, TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses #-} import Yesod import Yesod.Helpers.Static import Data.Monoid (mempty)
Like the blog example, we'll define some data first.
data Page = Page { pageName :: String , pageSlug :: String , pageContent :: String }
loadPages :: IO [Page] loadPages = return [ Page "Page 1" "page-1" "My first page" , Page "Page 2" "page-2" "My second page" , Page "Page 3" "page-3" "My third page" ]
loadPages :: IO [Page] loadPages = do
data Ajax = Ajax { ajaxPages :: [Page] , ajaxStatic :: Static } type Handler = GHandler Ajax Ajax
Next we'll generate a function for each file in our static folder. This way, we get a compiler warning when trying to using a file which does not exist.
staticFiles "static/yesod/ajax"
Now the routes; we'll have a homepage, a pattern for the pages, and use a static subsite for the Javascript and CSS files.
mkYesod "Ajax" [$parseRoutes| / HomeR GET /page/#String PageR GET /static StaticR Static ajaxStatic |]
<p>That third line there is the syntax for a subsite: Static is the datatype for the subsite argument; siteStatic returns the site itself (parse, render and dispatch functions); and ajaxStatic gets the subsite argument from the master argument.</p>
<p>Now, we'll define the Yesod instance. We'll still use a dummy approot value, but we're also going to define a default layout.</p>
instance Yesod Ajax where approot _ = "" defaultLayout widget = do Ajax pages _ <- getYesod content <- widgetToPageContent widget hamletToRepHtml [$hamlet| \<!DOCTYPE html>
<html> <head> <title>#{pageTitle content} <link rel="stylesheet" href="@{StaticR style_css}"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"> <script src="@{StaticR script_js}"> \^{pageHead content} <body> <ul id="navbar"> $forall page <- pages <li> <a href="@{PageR (pageSlug page)}">#{pageName page} <div id="content"> \^{pageBody content} |]
<p>The Hamlet template refers to style_css and style_js; these were generated by the call to staticFiles above. There's nothing Yesod-specific about the <a href="/static/yesod/ajax/style.css">style.css</a> and <a href="/static/yesod/ajax/script.js">script.js</a> files, so I won't describe them here.</p>
<p>Now we need our handler functions. We'll have the homepage simply redirect to the first page, so:</p>
getHomeR :: Handler () getHomeR = do Ajax pages _ <- getYesod let first = head pages redirect RedirectTemporary $ PageR $ pageSlug first
And now the cool part: a handler that returns either HTML or JSON data, depending on the request headers.
getPageR :: String -> Handler RepHtmlJson getPageR slug = do Ajax pages _ <- getYesod case filter (\e -> pageSlug e == slug) pages of [] -> notFound page:_ -> defaultLayoutJson (do setTitle $ string $ pageName page addHamlet $ html page ) (json page) where html page = [$hamlet| <h1>#{pageName page} <article>#{pageContent page} |] json page = jsonMap [ ("name", jsonScalar $ pageName page) , ("content", jsonScalar $ pageContent page) ]
<p>We first try and find the appropriate Page, returning a 404 if it's not there. We then use the applyLayoutJson function, which is really the heart of this example. It allows you an easy way to create responses that will be either HTML or JSON, and which use the default layout in the HTML responses. It takes four arguments: 1) the title of the HTML page, 2) some value, 3) a function from that value to a Hamlet value, and 4) a function from that value to a Json value.</p>
<p>Under the scenes, the Json monad is really just using the Hamlet monad, so it gets all of the benefits thereof, namely interleaved IO and enumerator output. It is pretty straight-forward to generate JSON output by using the three functions jsonMap, jsonList and jsonMap. One thing to note: the input to jsonScalar must be HtmlContent; this helps avoid cross-site scripting attacks, by ensuring that any HTML entities will be escaped.</p>
<p>And now our typical main function. We need two parameters to build our Ajax value: the pages, and the static loader. We'll load up from a local directory.</p>
main :: IO () main = do
pages <- loadPages let s = static "static/yesod/ajax" warpDebug 3000 $ Ajax pages s
[My changes]
{-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses #-} import Yesod import Yesod.Helpers.Static import System.Environment import System.IO import System.Directory import System.FilePath.Posix import Control.Applicative import Data.List.Split
data Page = Page { pageTitle :: String , pageSlug :: String -- ^ used in the URL , pageContent :: IO String }
loadPage :: IO [Page] loadPage = do let directoryPath = "/home/mlitchard/playground/webTests/files" let processedPath = map (directoryPath >) . filter (`notElem` [".",".."]) pageFileNames <- processedPath <$> getDirectoryContents directoryPath let pageFiles = map readFile pageFileNames return $ zipWith popEntries pageFileNames pageFiles
-- > return $ zipWith popEntries
popEntries :: FilePath -> IO String -> Page popEntries pageFileName pageFile = let pageT = last $ splitOn "/" pageFileName pageS = "Job" ++ pageT in Page { Main.pageTitle=pageT, pageSlug=pageS, pageContent=pageFile }
data Tframe = Tframe { tframePages :: [Page] , tframeStatic :: Static }
type Handler = GHandler Tframe Tframe
staticFiles "static/yesod/ajax"
Routes
mkYesod "Tframe" [$parseRoutes| / HomeR GET /page/#String PageR GET /static StaticR Static tframeStatic |]
defining the Yesod instance
instance Yesod Tframe where approot _ = "" defaultLayout widget = do Tframe pages <- getYesod content <- widgetToPageContent widget hamletToRepHtml [$hamlet| \<!DOCTYPE html>
<html> <head> <title>#{Main.pageTitle content} <link rel="stylesheet" href="@{StaticR style_css}"> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"> <script src="@{StaticR script_js}"> \^{pageHead content} <body> <ul id="navbar"> $forall page <- pages <li> <a href="@{PageR (pageSlug page)}">#{Main.pageTitle page} <div id="content"> \^{pageBody content} |]
getHomeR :: Handler () getHomeR = do Tframe pages _ <- getYesod let first = head pages redirect RedirectTemporary $ PageR $ pageSlug first
getPageR :: String -> Handler RepHtmlJson getPageR slug = do Tframe pages _ <- getYesod case filter (\e -> pageSlug e == slug) pages of [] -> notFound page:_ -> defaultLayoutJson (do setTitle $ string $ Main.pageTitle page addHamlet $ html page ) (json page) where html page = [$hamlet| <h1>#{Main.pageTitle page} <article>#{pageContent page} |]
json page = jsonMap [ ("name", jsonScalar $ Main.pageTitle page) , ("content", jsonScalar $ liftIO $ pageContent page) ]
main :: IO () main = do
pages <- loadPage let s = static "static/yesod/ajax" warpDebug 3000 $ Tframe pages s
If you've read to the bottom, thanks for your patience. I appreciate any illumination you can send my way.
Michael
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe