Remove the long-outdated examples

This commit is contained in:
Michael Snoyman 2013-03-15 07:35:14 +02:00
parent 8b9f8ea024
commit ac6ab5b4d0
21 changed files with 0 additions and 976 deletions

View File

@ -1,3 +0,0 @@
client_session_key.aes
dist
cabal-dev/

View File

@ -1,20 +0,0 @@
Copyright (c) 2012 Michael Snoyman, http://www.yesodweb.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

View File

@ -1,3 +0,0 @@
#!/usr/bin/env runhaskell
import Distribution.Simple
main = defaultMain

View File

@ -1,15 +0,0 @@
{-# LANGUAGE TypeFamilies, QuasiQuotes, GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module MkToForm2 where
import Yesod
import Data.Time (Day)
mkPersist [$persist|
Entry
title String
day Day Desc toFormField=YesodJquery.jqueryDayField'
content Html toFormField=YesodNic.nicHtmlField
deriving
|]

View File

@ -1,116 +0,0 @@
<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 TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-}
> import Yesod
> import Yesod.Static
> import Data.Text (Text, unpack)
Like the blog example, we'll define some data first.
> data Page = Page
> { pageName :: Text
> , pageSlug :: Text
> , pageContent :: Text
> }
> 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"
> ]
> data Ajax = Ajax
> { ajaxPages :: [Page]
> , ajaxStatic :: Static
> }
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/#Text 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 :: Text -> Handler RepHtmlJson
> getPageR slug = do
> Ajax pages _ <- getYesod
> case filter (\e -> pageSlug e == slug) pages of
> [] -> notFound
> page:_ -> defaultLayoutJson (do
> setTitle $ toHtml $ pageName page
> addHamlet $ html page
> ) (json page)
> where
> html page = [hamlet|
> <h1>#{pageName page}
> <article>#{pageContent page}
> |]
> json page = jsonMap
> [ ("name", jsonScalar $ unpack $ pageName page)
> , ("content", jsonScalar $ unpack $ 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
> s <- static "static/yesod/ajax"
> warpDebug 3000 $ Ajax pages s
And just to avoid some warnings...
> _ignored :: Widget
> _ignored = undefined ajaxPages

View File

@ -1,120 +0,0 @@
<p>Well, just about every web framework I've seen starts with a blog tutorial- so here's mine! Actually, you'll see that this is actually a much less featureful blog than most, but gives a good introduction to Yesod basics. I recommend you start by <a href="/book/basics">reading the basics chapter</a>.</p>
<p>This file is literate Haskell, so we'll start off with our language pragmas and import statements. Basically every Yesod application will start off like this:</p>
> {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-}
> import Yesod
Next, we'll define the blog entry information. Usually, we would want to store the data in a database and allow users to modify them, but we'll simplify for the moment.
> data Entry = Entry
> { entryTitle :: String
> , entrySlug :: String -- ^ used in the URL
> , entryContent :: String
> }
Since normally you'll need to perform an IO action to load up your entries from a database, we'll define the loadEntries function to be in the IO monad.
> loadEntries :: IO [Entry]
> loadEntries = return
> [ Entry "Entry 1" "entry-1" "My first entry"
> , Entry "Entry 2" "entry-2" "My second entry"
> , Entry "Entry 3" "entry-3" "My third entry"
> ]
Each Yesod application needs to define the site argument. You can use this for storing anything that should be loaded before running your application. For example, you might store a database connection there. In our case, we'll store our list of entries.
> data Blog = Blog { blogEntries :: [Entry] }
Now we use the first "magical" Yesod set of functions: mkYesod and parseRoutes. If you want to see *exactly* what they do, look at their Haddock docs. For now, we'll try to keep this tutorial simple:
> mkYesod "Blog" [parseRoutes|
> / HomeR GET
> /entry/#String EntryR GET
> |]
Usually, the next thing you want to do after a call to mkYesod is to create an instance of Yesod. Every Yesod app needs this; it is a centralized place to define some settings. All settings but approot have sensible defaults. In general, you should put in a valid, fully-qualified URL for your approot, but you can sometimes get away with just doing this:
> instance Yesod Blog where approot _ = ""
This only works if you application is being served from the root of your webserver, and if you never use features like sitemaps and atom feeds that need absolute URLs.
We defined two resource patterns for our blog: the homepage, and the page for each entry. For each of these, we are allowing only the GET request method. For the homepage, we want to simply redirect to the most recent entry, so we'll use:
> getHomeR :: Handler ()
> getHomeR = do
> Blog entries <- getYesod
> let newest = last entries
> redirect RedirectTemporary $ EntryR $ entrySlug newest
We go ahead and send a 302 redirect request to the entry resource. Notice how we at no point need to construct a String to redirect to; this is the beauty of type-safe URLs.
Next we'll define a template for entry pages. Normally, I tend to just define them within the handler function, but it's easier to follow if they're separate. Also for clarity, I'll define a datatype for the template arguments. It would also be possible to simply use the Entry datatype with some filter functions, but I'll save that for a later tutorial.
> data TemplateArgs = TemplateArgs
> { templateTitle :: Html
> , templateContent :: Html
> , templateNavbar :: [Nav]
> }
The Nav datatype will contain navigation information (ie, the URL and title) of each entry.
> data Nav = Nav
> { navUrl :: Route Blog
> , navTitle :: Html
> }
And now the template itself:
> entryTemplate :: TemplateArgs -> HtmlUrl (Route Blog)
> entryTemplate args = [hamlet|
> !!!
> <html>
> <head>
> <title>#{templateTitle args}
> <body>
> <h1>Yesod Sample Blog
> <h2>#{templateTitle args}
> <ul id="nav">
> $forall nav <- templateNavbar args
> <li>
> <a href="@{navUrl nav}">#{navTitle nav}
> <div id="content">
> \#{templateContent args}
> |]
Hopefully, that is fairly easy to follow; if not, please review the Hamlet documentation. Just remember that dollar signs mean Html variables, and at signs mean URLs.
Finally, the entry route handler:
> getEntryR :: String -> Handler RepHtml
> getEntryR slug = do
> Blog entries <- getYesod
> case filter (\e -> entrySlug e == slug) entries of
> [] -> notFound
> (entry:_) -> do
> let nav = reverse $ map toNav entries
> let tempArgs = TemplateArgs
> { templateTitle = toHtml $ entryTitle entry
> , templateContent = toHtml $ entryContent entry
> , templateNavbar = nav
> }
> hamletToRepHtml $ entryTemplate tempArgs
> where
> toNav :: Entry -> Nav
> toNav e = Nav
> { navUrl = EntryR $ entrySlug e
> , navTitle = toHtml $ entryTitle e
> }
All that's left now is the main function. Yesod is built on top of WAI, so you can use any WAI handler you wish. For the tutorials, we'll use the basicHandler that comes built-in with Yesod: it serves content via CGI if the appropriate environment variables are available, otherwise with simpleserver.
> main :: IO ()
> main = do
> entries <- loadEntries
> warpDebug 3000 $ Blog entries
And this is just to avoid some warnings...
> _ignored :: Widget
> _ignored = undefined blogEntries

View File

@ -1,122 +0,0 @@
{-# LANGUAGE TemplateHaskell, QuasiQuotes, TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Yesod
import Yesod.Static
import Control.Concurrent.STM
import Control.Arrow ((***))
import Data.Text (Text, unpack)
-- speaker and content
data Message = Message Text Text
-- all those TChans are dupes, so writing to any one writes to them all, but reading is separate
data Chat = Chat
{ chatClients :: TVar [(Int, TChan Message)]
, nextClient :: TVar Int
, chatStatic :: Static
}
staticFiles "static"
mkYesod "Chat" [parseRoutes|
/ HomeR GET
/check CheckR GET
/post PostR GET
/static StaticR Static chatStatic
|]
instance Yesod Chat where
approot _ = ""
defaultLayout widget = do
content <- widgetToPageContent widget
hamletToRepHtml [hamlet|
!!!
<html>
<head>
<title>#{pageTitle content}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
<script src="@{StaticR chat_js}">
^{pageHead content}
<body>
^{pageBody content}
|]
getHomeR :: Handler RepHtml
getHomeR = do
Chat clients next _ <- getYesod
client <- liftIO . atomically $ do
c <- readTVar next
writeTVar next (c+1)
cs <- readTVar clients
chan <- case cs of
[] -> newTChan
(_,x):_ -> dupTChan x
writeTVar clients ((c,chan) : cs)
return c
defaultLayout $ do
setTitle "Chat Page"
toWidget [hamlet|
!!!
<h1>Chat Example
<form>
<textarea cols="80" rows="20" name="chat">
<p>
<input type="text" size="15" name="name" id="name">
<input type="text" size="60" name="send" id="send">
<input type="submit" value="Send">
<script>var clientNumber = #{show client}
|]
getCheckR :: Handler RepJson
getCheckR = do
liftIO $ putStrLn "Check"
Chat clients _ _ <- getYesod
client <- do
c <- lookupGetParam "client"
case c of
Nothing -> invalidArgs ["No client value in Check request"]
Just c' -> return $ read $ unpack c'
cs <- liftIO . atomically $ readTVar clients
chan <- case lookup client cs of
Nothing -> invalidArgs ["Bad client value"]
Just ch -> return ch
-- block until there's something there
first <- liftIO . atomically $ readTChan chan
let Message s c = first
jsonToRepJson $ zipJson ["sender", "content"] [s,c]
zipJson :: [Text] -> [Text] -> Json
zipJson x y = jsonMap $ map (unpack *** jsonScalar . unpack) $ zip x y
getPostR :: Handler RepJson
getPostR = do
liftIO $ putStrLn "Post"
Chat clients _ _ <- getYesod
(sender,content) <- do
s <- lookupGetParam "name"
c <- lookupGetParam "send"
case (s,c) of
(Just s', Just c') -> return (s', c')
_ -> invalidArgs ["Either name or send not provided."]
liftIO . atomically $ do
cs <- readTVar clients
let chan = snd . head $ cs -- doesn't matter which one we use, they're all duplicates
writeTChan chan (Message sender content)
jsonToRepJson $ jsonScalar "success"
main :: IO ()
main = do
clients <- newTVarIO []
next <- newTVarIO 0
s <- static "static"
warpDebug 3000 $ Chat clients next s

View File

@ -1,37 +0,0 @@
> {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-}
> import Yesod
> import qualified Data.ByteString.Char8 as S8
> import qualified Data.Text as T
> data Echo = Echo
> mkYesod "Echo" [parseRoutes|
> / Homepage GET POST
> |]
> instance Yesod Echo where approot _ = ""
> getHomepage :: Handler RepHtml
> getHomepage = defaultLayout $ do
> setTitle "Upload a file"
> addHamlet [hamlet|
> <form method=post action=. enctype=multipart/form-data>
> File name:
> <input type=file name=file
> <input type=submit
> |]
> postHomepage :: Handler [(ContentType, Content)]
> postHomepage = do
> (_, files) <- runRequestBody
> fi <- maybe notFound return $ lookup "file" files
> return [(S8.pack $ T.unpack $ fileContentType fi, toContent $ fileContent fi)]
> main :: IO ()
> main = warpDebug 3000 Echo
To avoid warnings
> _ignored :: Widget
> _ignored = undefined

View File

@ -1,55 +0,0 @@
<p>Forms can be a tedious part of web development since they require synchronization of code in many different areas: the HTML form declaration, parsing of the form and reconstructing a datatype from the raw values. The Yesod form library simplifies things greatly. We'll start off with a basic application.</p>
> {-# LANGUAGE TypeFamilies, QuasiQuotes, OverloadedStrings, MultiParamTypeClasses, TemplateHaskell #-}
> import Yesod hiding (Form)
> import Control.Applicative
> import Data.Text (Text)
> data FormExample = FormExample
> mkYesod "FormExample" [parseRoutes|
> / RootR GET
> |]
> type Form a = Html -> MForm FormExample FormExample (FormResult a, Widget)
> type Formlet a = Maybe a -> Form a
> instance Yesod FormExample where approot _ = ""
> instance RenderMessage FormExample FormMessage where
> renderMessage _ _ = defaultFormMessage
Next, we'll declare a Person datatype with a name and age. After that, we'll create a formlet. A formlet is a declarative approach to forms. It takes a Maybe value and constructs either a blank form, a form based on the original value, or a form based on the values submitted by the user. It also attempts to construct a datatype, failing on validation errors.
> data Person = Person { name :: Text, age :: Int }
> deriving Show
> personFormlet :: Formlet Person
> personFormlet p = renderTable $ Person
> <$> areq textField "Name" (fmap name p)
> <*> areq intField "Age" (fmap age p)
We use an applicative approach and stay mostly declarative. The "fmap name p" bit is just a way to get the name from within a value of type "Maybe Person".
> getRootR :: Handler RepHtml
> getRootR = do
> ((res, wform), enctype) <- runFormGet $ personFormlet Nothing
<p>We use runFormGet to bind to GET (query-string) parameters; we could also use runFormPost. The "Nothing" is the initial value of the form. You could also supply a "Just Person" value if you like. There is a three-tuple returned, containing the parsed value, the HTML form as a widget and the encoding type for the form.</p>
<p>We use a widget for the form since it allows embedding CSS and Javascript code in forms directly. This allows unobtrusive adding of rich Javascript controls like date pickers.</p>
> defaultLayout $ do
> setTitle "Form Example"
> form <- extractBody wform
<p>extractBody returns the HTML of a widget and "passes" all of the other declarations (the CSS, Javascript, etc) up to the parent widget. The rest of this is just standard Hamlet code and our main function.</p>
> addHamlet [hamlet|
> <p>Last result: #{show res}
> <form enctype="#{enctype}">
> <table>
> ^{form}
> <tr>
> <td colspan="2">
> <input type="submit">
> |]
>
> main :: IO ()
> main = warpDebug 3000 FormExample

View File

@ -1,56 +0,0 @@
> {-# LANGUAGE QuasiQuotes #-}
> {-# LANGUAGE TemplateHaskell #-}
> {-# LANGUAGE TypeFamilies #-}
> {-# LANGUAGE MultiParamTypeClasses #-}
> {-# LANGUAGE OverloadedStrings #-}
> {-# LANGUAGE CPP #-}
> import Yesod
> import Data.Text (Text)
> data I18N = I18N
> mkYesod "I18N" [parseRoutes|
> / HomepageR GET
> /set/#Text SetLangR GET
> |]
> instance Yesod I18N where
> approot _ = "http://localhost:3000"
> getHomepageR :: Handler RepHtml
> getHomepageR = do
> ls <- languages
> let hello = chooseHello ls
> let choices =
> [ ("en", "English") :: (Text, Text)
> , ("es", "Español")
> , ("he", "עִבְרִית")
> ]
> defaultLayout $ do
> setTitle "I18N Homepage"
> addHamlet [hamlet|
> <h1>#{hello}
> <p>In other languages:
> <ul>
> $forall choice <- choices
> <li>
> <a href="@{SetLangR (fst choice)}">#{snd choice}
> |]
> chooseHello :: [Text] -> Text
> chooseHello [] = "Hello"
> chooseHello ("he":_) = "שלום"
> chooseHello ("es":_) = "¡Hola!"
> chooseHello (_:rest) = chooseHello rest
> getSetLangR :: Text -> Handler ()
> getSetLangR lang = do
> setLanguage lang
> redirect RedirectTemporary HomepageR
> main :: IO ()
> main = warpDebug 3000 I18N
> _ignored :: Widget
> _ignored = undefined

View File

@ -1,66 +0,0 @@
<p>This example uses the <a href="http://hackage.haskell.org/package/data-object-yaml">data-object-yaml package</a> to display YAML files as cleaned-up HTML. If you've read through the other tutorials, this one should be easy to follow.</p>
> {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-}
> import Yesod
> import Data.Object
> import Data.Object.Yaml
> import qualified Data.ByteString as B
> import qualified Data.ByteString.Lazy as L
> data PY = PY
> mkYesod "PY" [parseRoutes|
> / Homepage GET POST
> |]
> instance Yesod PY where approot _ = ""
> template :: Maybe (HtmlUrl url) -> HtmlUrl url
> template myaml = [hamlet|
> !!!
>
> <html>
> <head>
> <meta charset="utf-8">
> <title>Pretty YAML
> <body>
> <form method="post" action="" enctype="multipart/form-data" .>
> \File name:
> <input type="file" name="yaml">
> <input type="submit">
> $maybe yaml <- myaml
> <div>^{yaml}
> |]
> getHomepage :: Handler RepHtml
> getHomepage = hamletToRepHtml $ template Nothing
> postHomepage :: Handler RepHtml
> postHomepage = do
> (_, files) <- runRequestBody
> fi <- case lookup "yaml" files of
> Nothing -> invalidArgs ["yaml: Missing input"]
> Just x -> return x
> so <- liftIO $ decode $ B.concat $ L.toChunks $ fileContent fi
> hamletToRepHtml $ template $ Just $ objToHamlet so
> objToHamlet :: StringObject -> HtmlUrl url
> objToHamlet (Scalar s) = [hamlet|#{s}|]
> objToHamlet (Sequence list) = [hamlet|
> <ul
> $forall o <- list
> <li>^{objToHamlet o}
> |]
> objToHamlet (Mapping pairs) = [hamlet|
> <dl
> $forall pair <- pairs
> <dt>#{fst pair}
> <dd>^{objToHamlet $ snd pair}
> |]
> main :: IO ()
> main = warpDebug 3000 PY
> _ignored :: Widget
> _ignored = undefined

View File

@ -1,39 +0,0 @@
> {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-}
> import Yesod
> import Control.Applicative ((<$>), (<*>))
>
> data Session = Session
> mkYesod "Session" [parseRoutes|
> / Root GET POST
> |]
>
> instance Yesod Session where
> approot _ = ""
> clientSessionDuration _ = 1
>
> instance RenderMessage Session FormMessage where
> renderMessage _ _ = defaultFormMessage
>
> getRoot :: Handler RepHtml
> getRoot = do
> sess <- getSession
> hamletToRepHtml [hamlet|
> <form method=post
> <input type=text name=key
> <input type=text name=val
> <input type=submit
> <h1>#{show sess}
> |]
>
> postRoot :: Handler ()
> postRoot = do
> (key, val) <- runInputPost $ (,) <$> ireq textField "key" <*> ireq textField "val"
> setSession key val
> liftIO $ print (key, val)
> redirect RedirectTemporary Root
>
> main :: IO ()
> main = warpDebug 3000 Session
> _ignored :: Widget
> _ignored = undefined

View File

@ -1,92 +0,0 @@
> {-# LANGUAGE TypeFamilies, QuasiQuotes, OverloadedStrings, MultiParamTypeClasses, TemplateHaskell #-}
> import Yesod
> import Yesod.Static
> import Yesod.Form.Jquery
> import Yesod.Form.Nic
> import Control.Applicative
> import Data.Text (unpack)
> import Text.Blaze (string)
>
> data HW = HW { hwStatic :: Static }
> mkYesod "HW" [$parseRoutes|
> / RootR GET
> /form FormR
> /static StaticR Static hwStatic
> /autocomplete AutoCompleteR GET
> |]
> instance Yesod HW where approot _ = ""
> instance YesodJquery HW
> instance YesodNic HW
> wrapper h = [hamlet|
> <#wrapper>^{h}
> <footer>Brought to you by Yesod Widgets&trade;
> |]
> instance RenderMessage HW FormMessage where
> renderMessage _ _ = defaultFormMessage
>
> getRootR = defaultLayout $ wrapper $ do
> i <- lift newIdent
> setTitle $ string "Hello Widgets"
> addCassius [$cassius|
> #$i$
> color: red|]
> addStylesheet $ StaticR $ StaticRoute ["style.css"] []
> addStylesheetRemote "http://localhost:3000/static/style2.css"
> addScriptRemote "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"
> addScript $ StaticR $ StaticRoute ["script.js"] []
> addHamlet [$hamlet|
> <h1 ##{i}>Welcome to my first widget!!!
> <p
> <a href=@RootR@>Recursive link.
> <p
> <a href=@FormR@>Check out the form.
> <p .noscript>Your script did not load. :(
> |]
> addHtmlHead [$hamlet|<meta keywords=haskell|]
>
> handleFormR = do
> (res, form, enctype, nonce) <- runFormPost $ renderTable $ (,,,,,,,,)
> <$> aopt textField "My Field" Nothing
> <*> aopt textField "Another field" (Just "some default text")
> <*> aopt intField "A number field" (Just 5)
> <*> aopt jqueryDayField "A day field" Nothing
> <*> aopt timeField "A time field" Nothing
> <*> aopt boolField "A checkbox" (Just False)
> <*> aopt jqueryAutocompleteField AutoCompleteR "Autocomplete" Nothing
> <*> aopt nicHtmlField "HTML"
> (Just $ string "You can put <rich text> here")
> <*> aopt emailField "An e-mail addres" Nothing
> let mhtml = case res of
> FormSuccess (_, _, _, _, _, _, _, x, _) -> Just x
> _ -> Nothing
> defaultLayout $ do
> addCassius [$cassius|
> .tooltip
> color: #666
> font-style: italic
> textarea.html
> width: 300px
> height: 150px|]
> addWidget [$hamlet|
> <form method="post" enctype="#{enctype}">
> <table>
> \^{form}
> <tr>
> <td colspan="2">
> \#{nonce}
> <input type="submit">
> $maybe html <- mhtml
> \#{html}
> |]
> setTitle $ string "Form"
>
> main = static "static" >>= (warpDebug 3000 . HW)
>
> getAutoCompleteR :: Handler RepJson
> getAutoCompleteR = do
> term <- runInputGet $ ireq textField "term"
> jsonToRepJson $ jsonList
> [ jsonScalar $ unpack term ++ "foo"
> , jsonScalar $ unpack term ++ "bar"
> , jsonScalar $ unpack term ++ "baz"
> ]

View File

@ -1,21 +0,0 @@
$(document).ready(function () {
$("form").submit(function (e) {
e.preventDefault();
$.getJSON("/post", { name: $("#name").attr("value"), send: $("#send").attr("value") }, function(o) { });
$("#send").attr("value", "");
});
checkIn();
});
function checkIn () {
$.getJSON("/check", { client: clientNumber }, function(o) {
//alert("response: " + o);
var ta = $("textarea");
ta.html(ta.html() + o.sender + ": " + o.content + "\n");
ta.scrollTop(10000);
checkIn();
});
}

View File

@ -1,9 +0,0 @@
$(function(){
$("#navbar a").click(function(){
$.getJSON($(this).attr("href"), {}, function(o){
$("h1").html(o.name);
$("article").html(o.content);
});
return false;
});
});

View File

@ -1,11 +0,0 @@
#navbar {
width: 100px;
float: left;
background: #eee;
padding: 1em;
list-style: none;
}
#content {
margin-left: 230px;
}

View File

@ -1,74 +0,0 @@
\begin{code}
{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
import Text.Hamlet
import Data.Text (Text, cons)
import qualified Data.Text.Lazy.IO as L
import Text.Blaze.Renderer.Text (renderHtml)
data Person = Person
{ name :: String
, age :: String
, page :: PersonUrls
, isMarried :: Bool
, children :: [String]
}
data PersonUrls = Homepage | PersonPage Text
renderUrls :: PersonUrls -> [(Text, Text)] -> Text
renderUrls Homepage _ = "/"
renderUrls (PersonPage name') _ = '/' `cons` name'
footer :: HtmlUrl url
footer = [hamlet|
<div id="footer">Thank you, come again
|]
template :: Person -> HtmlUrl PersonUrls
template person = [hamlet|
!!!
<html>
<head>
<title>Hamlet Demo
<body>
<h1>Information on #{name person}
<p>#{name person} is #{age person} years old.
<h2>
$if isMarried person
\Married
$else
\Not married
<ul>
$forall child <- children person
<li>#{child}
<p>
<a href="@{page person}">See the page.
\^{footer}
|]
main :: IO ()
main = do
let person = Person
{ name = "Michael"
, age = "twenty five & a half"
, page = PersonPage "michael"
, isMarried = True
, children = ["Adam", "Ben", "Chris"]
}
L.putStrLn $ renderHtml $ (template person) renderUrls
\end{code}
Outputs (new lines added for readability):
<code><pre>
&lt;!DOCTYPE html&gt;
&lt;html&gt;&lt;head&gt;&lt;title&gt;Hamlet Demo&lt;/title&gt;&lt;/head&gt;&lt;body&gt;
&lt;h1&gt;Information on Michael&lt;/h1&gt;
&lt;p&gt;Michael is twenty five &amp; a half years old.&lt;/p&gt;
&lt;h2&gt;Married&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Adam&lt;/li&gt;&lt;li&gt;Ben&lt;/li&gt;&lt;li&gt;Chris&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;a href="/michael"&gt;See the page.&lt;/a&gt;&lt;/p&gt;
&lt;div id="footer"&gt;Thank you, come again&lt;/div&gt;
&lt;/body&gt;&lt;/html&gt;
</pre></code>

View File

@ -1,42 +0,0 @@
This example uses the sqlite backend for Persistent, since it can run in-memory and has no external dependencies.
> {-# LANGUAGE GADTs, TypeFamilies, GeneralizedNewtypeDeriving, QuasiQuotes, TemplateHaskell, OverloadedStrings #-}
>
> import Database.Persist.Sqlite
> import Database.Persist.TH
> import Control.Monad.IO.Class (liftIO)
>
> mkPersist sqlSettings [persist|Person
> name String Eq
> age Int Update
> |]
>
> main :: IO ()
> main = withSqliteConn ":memory:" $ runSqlConn go
>
> go :: SqlPersist IO ()
> go = do
> runMigration $ migrate (undefined :: Person)
> key <- insert $ Person "Michael" 25
> liftIO $ print key
> p1 <- get key
> liftIO $ print p1
> update key [PersonAge =. 26]
> p2 <- get key
> liftIO $ print p2
> p3 <- selectList [PersonName ==. "Michael"] []
> liftIO $ print p3
> delete key
> p4 <- selectList [PersonName ==. "Michael"] []
> liftIO $ print p4
The output of the above is:
<code><pre>PersonId 1
Just (Person {personName = "Michael", personAge = 25})
Just (Person {personName = "Michael", personAge = 26})
[(PersonId 1,Person {personName = "Michael", personAge = 26})]
[]</pre></code>
> _ignored :: PersonId
> _ignored = undefined personName personAge

View File

@ -1,75 +0,0 @@
Name: yesod-examples
Version: 0.9.0
Synopsis: Example programs using the Yesod Web Framework.
Description: These are the same examples and tutorials found on the documentation site.
Homepage: http://www.yesodweb.com/
License: BSD3
License-file: LICENSE
Author: Michael Snoyman
Maintainer: michael@snoyman.com
Stability: Experimental
Category: Web, Yesod
Build-type: Simple
Cabal-version: >=1.6
extra-source-files: static/yesod/ajax/script.js,
static/yesod/ajax/style.css,
static/chat.js
flag ghc7
Executable yesod-blog
Main-is: src/blog.lhs
Build-depends: base >= 4 && < 5,
yesod >= 0.9
Executable yesod-ajax
Main-is: src/ajax.lhs
Build-depends: yesod-static,
blaze-html >= 0.4.1.3 && < 0.5,
yesod >= 0.9
Executable yesod-file-echo
Main-is: src/file-echo.lhs
Build-depends: text >= 0.9 && < 0.12,
yesod >= 0.9
Executable yesod-pretty-yaml
Main-is: src/pretty-yaml.lhs
Build-depends: data-object-yaml >= 0.3.0 && < 0.4,
data-object >= 0.3.1 && < 0.4,
bytestring >= 0.9.1.4 && < 0.10,
yesod >= 0.9
Executable yesod-i18n
Main-is: src/i18n.lhs
if flag(ghc7)
cpp-options: -DGHC7
Executable yesod-session
Main-is: src/session.lhs
-- Executable yesod-widgets
-- Main-is: src/widgets.lhs
-- Build-depends: yesod-form
Executable yesod-form
Main-is: src/form.lhs
Executable yesod-persistent-synopsis
Main-is: synopsis/persistent.lhs
Build-depends: transformers >= 0.2.2 && < 0.3,
persistent-sqlite >= 0.6 && < 0.7,
persistent-template >= 0.6 && < 0.7
extra-libraries: sqlite3
Executable yesod-hamlet-synopsis
Main-is: synopsis/hamlet.lhs
Build-depends: hamlet, yesod-core
Executable yesod-chat
Main-is: src/chat.hs
Build-depends: stm
source-repository head
type: git
location: https://github.com/yesodweb/yesod