2011-12-29 16:05:05 +00:00
<?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
< html xmlns = "http://www.w3.org/1999/xhtml" lang = "fr" xml:lang = "fr" >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=UTF-8" / >
< meta name = "keywords" content = "yesod, haskell, programming, web" >
< link rel = "shortcut icon" type = "image/x-icon" href = "/Scratch/img/favicon.ico" / >
< link rel = "stylesheet" type = "text/css" href = "/Scratch/assets/css/main.css" / >
< link rel = "stylesheet" type = "text/css" href = "/Scratch/css/twilight.css" / >
< link rel = "stylesheet" type = "text/css" href = "/Scratch/css/idc.css" / >
< link rel = "alternate" type = "application/rss+xml" title = "RSS" href = "http://feeds.feedburner.com/yannespositocomen" / >
< link rel = "alternate" lang = "fr" xml:lang = "fr" title = "Tutoriel Yesod pour les nuls" type = "text/html" hreflang = "fr" href = "/Scratch/fr/blog/Yesod-tutorial-for-newbies/" / >
< link rel = "alternate" lang = "en" xml:lang = "en" title = "Yesod tutorial for newbies" type = "text/html" hreflang = "en" href = "/Scratch/en/blog/Yesod-tutorial-for-newbies/" / >
< script type = "text/javascript" src = "/Scratch/js/jquery-1.3.1.min.js" > < / script >
< script type = "text/javascript" src = "/Scratch/js/jquery.cookie.js" > < / script >
< script type = "text/javascript" src = "/Scratch/js/index.js" > < / script >
<!-- [if lt IE 9]>
< script src = "http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js" > < / script >
<![endif]-->
< title > Yesod tutorial for newbies< / title >
< / head >
< body lang = "en" class = "article" >
< script type = "text/javascript" > / / < ! [ C D A T A [
document.write('< div id = "blackpage" > < img src = "/Scratch/img/loading.gif" alt = "loading..." / > < / div > ');
// ]]>
< / script >
< div id = "content" >
< div id = "choix" >
< div class = "return" > < a href = "#entete" > ↓ Menu ↓ < / a > < / div >
< div id = "choixlang" >
< a href = "/Scratch/fr/blog/Yesod-tutorial-for-newbies/" onclick = "setLanguage('fr')" > en Français< / a >
< / div >
< div class = "flush" > < / div >
< / div >
< div id = "titre" >
< h1 >
Yesod tutorial for newbies
< / h1 >
< / div >
< div class = "flush" > < / div >
< div class = "flush" > < / div >
< div id = "afterheader" >
< div class = "corps" >
< p > < img alt = "Title image" src = "/Scratch/img/blog/Yesod-tutorial-for-newbies/warp-benchmark.png" / > < / p >
< div class = "intro" >
< p > < span class = "sc" > < abbr title = "Too long; didn't read" > tl;dr< / abbr > : < / span > A simple yesod tutorial. You shouldn’ t need to know Haskell very well. < / p >
< blockquote >
< ul id = "markdown-toc" >
< li > < a href = "#install" > Install< / a > < / li >
< li > < a href = "#initialization" > Initialization< / a > < / li >
< li > < a href = "#configure-git" > Configure git< / a > < / li >
< li > < a href = "#a-last-point" > A last point< / a > < / li >
< li > < a href = "#protected-echo" > Protected echo< / a > < / li >
2011-12-30 16:14:57 +00:00
< li > < a href = "#cleaning-up" > Cleaning up< / a > < ul >
< li > < a href = "#separate-handlers" > Separate handlers< / a > < / li >
< li > < a href = "#use-datatext-instead-of-string" > Use < code > Data.Text< / code > instead of < code > String< / code > < / a > < / li >
< li > < a href = "#use-a-new-template-file" > Use a new template file< / a > < / li >
< / ul >
< / li >
2011-12-29 16:05:05 +00:00
< li > < a href = "#protected-input" > Protected input< / a > < / li >
< / ul >
< / blockquote >
< / div >
< p > You want the best technology to handle your new web application?
Me too. After searching a lot, it appears if you focus only on technical aspect, Haskell is the way to go.< / p >
< p > It is < a href = "http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks" > extremely fast< / a > .
It is secure by nature. Many typical programming bug are hard to make in Haskell.
Haskell is also a “high level of abstraction” language. You can organize your code clearly.< / p >
< p > Actually there are three web frameworks in Haskell:< / p >
< ol >
< li > < a href = "http://happstack.com" > Happstack< / a > < / li >
< li > < a href = "http://snapframework.com" > Snap< / a > < / li >
< li > < a href = "http://yesodweb.com" > Yesod< / a > < / li >
< / ol >
< p > It is very hard to choose between these three.
But my feeling goes to Yesod.
It appears to be the one with most part done for you.
As a beginner, let’ s stay away of the detail as most as possible.< / p >
< p > The following tutorial contains some parts.< / p >
< ul >
< li > Install → Install haskell and yesod. This can be long, but it is all automatic and this should be straightforward.< / li >
< li > Initialization → Initialize the project and configure it.< / li >
< li > Configure git → This is not mandatory, but it is a good practice.< / li >
< li > Verify the security → A first step to verify the yesod framework protect us from most common errors.< / li >
< li > Create a minimal blog → This is the “hello world” of web framework.< / li >
< li > Some tuning → Use html5 boilerplate for example.< / li >
< / ul >
< h2 id = "install" > Install< / h2 >
< p > First you need to install < a href = "http://www.haskell.org" > Haskell< / a > . The recommended way to do this is to use the < a href = "http://www.haskell.org/platform" > Haskell Platform< / a > .< / p >
< p > Secondly you need to install yesod.< / p >
< pre class = "twilight" >
< span class = "Keyword" > > < / span > cabal update
< span class = "Keyword" > > < / span > cabal install yesod cabal-dev
< / pre >
< p > That is all. It should take some time to
do this as cabal will download all
package and then compile them.< / p >
< h2 id = "initialization" > Initialization< / h2 >
< p > Open a terminal and:< / p >
< pre class = "twilight" >
< span class = "Keyword" > > < / span > yesod init
< / pre >
2011-12-30 16:14:57 +00:00
< p > I entered my name, the name of the project was < code > yosog< / code > and the name of the Foundation was < code > Yosog< / code > , then I chosen < code > sqlite< / code > .< / p >
2011-12-29 16:05:05 +00:00
< p > Perfect. Now you can start the development cycle:< / p >
< pre class = "twilight" >
< span class = "Keyword" > > < / span > cd yosog
< span class = "Keyword" > > < / span > cabal-dev install < span class = "Keyword" > & & < / span > yesod --dev devel
< / pre >
< p > This will compile the entire project.
In the end you should now be able to see your local website by clicking this link:< / p >
< p > < a href = "http://localhost:3000" > < code > http://localhost:3000< / code > < / a > < / p >
< p > Congratulation! You were able to see your yesod powered website.
For the rest of the tutorial, use another terminal and let this one open in a corner to see what occurs.< / p >
< h2 id = "configure-git" > Configure git< / h2 >
< p > It is not mandatory for a tutorial, but it is a good practice to have a CVS.< / p >
< p > To use < code > git< / code > copy this < code > .gitignore< / code > file into the < code > yosog< / code > folder.< / p >
< div class = "code" > < div class = "file" > < a href = "/Scratch/en/blog/Yesod-tutorial-for-newbies/code/.gitignore" > ➥ .gitignore < / a > < / div > < div class = "withfile" >
< pre class = "twilight" >
cabal-dev
dist
.static-cache
static/tmp
*.sqlite3
< / pre >
< / div > < / div >
< p > Then initialize your git repository:< / p >
< pre class = "twilight" >
< span class = "Keyword" > > < / span > git init .
< span class = "Keyword" > > < / span > git add .
< span class = "Keyword" > > < / span > git commit -a -m < span class = "String" > < span class = "String" > " < / span > Initial yesod commit< span class = "String" > " < / span > < / span >
< / pre >
< p > Now we are ready to modify our web application.< / p >
< h2 id = "a-last-point" > A last point< / h2 >
< p > What did we done:< / p >
< ol >
< li > We have a directory containing a bunch of files< / li >
< li > We have a local web server on port 3000< / li >
< / ol >
< p > If we modify a file inside this directory, yesod should try
to recompile as fast as possible the site. This way, you should
see the modification you done.< / p >
< p > Instead of explaining the role of every file,
let’ s get straight to the point.< / p >
< p > Inside the < code > yosog< / code > the important files/directories for this tutorial are:< / p >
< ol >
< li > < code > config/routes< / code > < / li >
< li > < code > Handler/< / code > < / li >
< li > < code > templates/< / code > < / li >
< li > < code > static/< / code > < / li >
< li > < code > config/models< / code > < / li >
< / ol >
< p > Obviously:< / p >
< ul >
< li > < code > config/routes< / code > is where you’ ll configure the map URL → Code.< / li >
< li > < code > Handler/< / code > contains the files that will contain the code called when a URL is accessed.< / li >
< li > < code > templates/< / code > contains HTML, JS and CSS templates. < / li >
< li > < code > static/< / code > contains static files.< / li >
< li > < code > config/models< / code > is where you’ ll configure the persistent objects (database tables).< / li >
< / ul >
< p > With these informations we should be able to do a lot.
Also note until here we don’ t even typed any line of Haskell.< / p >
< h2 id = "protected-echo" > Protected echo< / h2 >
< p > To verify the quality of the security of the yesod framework, let’ s look at a minimal echo application.< / p >
< p > Our goal:< / p >
2011-12-30 16:14:57 +00:00
< p > Accessing < a href = "http://localhost:3000/echo/some%20text" > < code > http://localhost:3000/echo/some%20text< / code > < / a > , should display “some text” in an %html web page.< / p >
2011-12-29 16:05:05 +00:00
< p > Let’ s take a look at the file < code > config/routes< / code > :< / p >
< pre class = "twilight" >
/static StaticR Static getStatic
/auth AuthR Auth getAuth
/favicon.ico FaviconR GET
/robots.txt RobotsR GET
/ RootR GET
< / pre >
< p > We want to add a route of the form < code > /echo/[anything]< / code > somehow and do some action with this.
We add the following:< / p >
< pre >
/echo/#String EchoR GET
< / pre >
< p > This line contains three elements: the < span class = "sc" > url< / span > pattern, a handler name, an HTTP method.
I am not particularly fan of the big R in the end of handler names.
But this is the standard convention, then I use it.< / p >
< p > If you save < code > config/routes< / code > , you should see your terminal in which you launched < code > yesod devel< / code > do things.
And certainly break in error.< / p >
< pre >
Application.hs:31:1: Not in scope: `getEchoR'
< / pre >
< p > Why? Simply because we didn’ t written the code for the handler < code > EchoR< / code > .
Now, let’ s do this. Edit the file < code > Handler/Root.hs< / code > and append this:< / p >
< pre class = "twilight" >
< span class = "Entity" > getEchoR< / span > :: < span class = "Constant" > String< / span > → < span class = "Constant" > Handler< / span > < span class = "Constant" > RepHtml< / span >
getEchoR theText = < span class = "Keyword" > do< / span >
defaultLayout $ < span class = "Keyword" > do< / span >
[whamlet|< h1> #{theText}|]
< / pre >
< p > After saving the file, you should see yesod recompile the application.
When the compilation is finished you’ ll see the message: < code > Starting devel application< / code > .
You can now visit: < a href = "http://localhost:3000/echo/Yesod%20rocks!" > < code > http://localhost:3000/echo/Yesod%20rocks!< / code > < / a > < / p >
< p > TADA! It works.< / p >
< p > Now, let’ s try to attack our website by entering name with special characters:< / p >
2011-12-30 16:14:57 +00:00
< p > < a href = "http://localhost:3000/echo/<a>I'm <script>alert("Bad!");" > < code > http://localhost:3000/echo/< a> I'm < script> alert("Bad!");< / code > < / a > < / p >
2011-12-29 16:05:05 +00:00
< p > The special characters are protected for us.
If you have a malicious user, he could not hide some bad script inside his name for example.< / p >
< p > This is a direct consequence of < em > type safety< / em > .
The URL string is put inside a URL type.
Then the interesting part in the URL is put inside a String type. To pass from URL type to String type some transformation are made. For example, replace all “< code > %20< / code > ” by space characters.
Then to show the String inside an HTML document, the string is put inside an HTML type. Some transformations occurs like replace “< code > < < / code > ” by “< code > & lt;< / code > ”.
Thanks to yesod, most of tedious string transformation job is done for us.< / p >
< pre class = "twilight" >
< span class = "String" > < span class = "String" > " < / span > http://localhost:3000/echo/some%20text< a> < span class = "String" > " < / span > < / span > :: URL
↓
< span class = "String" > < span class = "String" > " < / span > some text< a> < span class = "String" > " < / span > < / span > :: String
↓
< span class = "String" > < span class = "String" > " < / span > some text & lt;a& gt;< span class = "String" > " < / span > < / span > :: HTML
< / pre >
< p > That was the first very minimal example, and we already
verified Yesod protect us from many common errors.< / p >
2011-12-30 16:14:57 +00:00
< h2 id = "cleaning-up" > Cleaning up< / h2 >
2011-12-29 16:05:05 +00:00
2011-12-30 16:14:57 +00:00
< p > This first example was nice, but for simplicity reason we didn’ t used best practices.< / p >
2011-12-29 16:05:05 +00:00
2011-12-30 16:14:57 +00:00
< p > First we will separate the handler code into different files.
After that we will use < code > Data.Text< / code > instead of < code > String< / code > . < / p >
2011-12-29 16:05:05 +00:00
2011-12-30 16:14:57 +00:00
< h3 id = "separate-handlers" > Separate handlers< / h3 >
2011-12-29 16:05:05 +00:00
2011-12-30 16:14:57 +00:00
< p > In a first time create a new file < code > Handler/Echo.hs< / code > containing:< / p >
< pre class = "twilight" >
< span class = "Keyword" > module< / span > < span class = "Constant" > Handler< / span > .< span class = "Constant" > Echo< / span > < span class = "Keyword" > where< / span >
< span class = "Keyword" > import< / span > < span class = "Constant" > Import< / span >
< span class = "Entity" > getEchoR< / span > :: < span class = "Constant" > String< / span > → < span class = "Constant" > Handler< / span > < span class = "Constant" > RepHtml< / span >
getEchoR theText = < span class = "Keyword" > do< / span >
defaultLayout $ < span class = "Keyword" > do< / span >
[whamlet|< h1> #{theText}|]
< / pre >
< p > Do not forget to remove the getEchoR function inside the < code > Handler/Root.hs< / code > file.< / p >
< p > We must declare the file inside the cabal configuration file < code > yosog.cabal< / code > . Just after < code > Handler.Root< / code > add:< / p >
< pre >
Handler.Echo
< / pre >
< p > We must also declare the new Handler module inside < code > Application.hs< / code > .
Just after the “< code > import Handler.Root< / code > ”, add:< / p >
< pre class = "twilight" >
< span class = "Keyword" > import< / span > < span class = "Constant" > Handler< / span > .< span class = "Constant" > Echo< / span >
< / pre >
< h3 id = "use-datatext-instead-of-string" > Use < code > Data.Text< / code > instead of < code > String< / code > < / h3 >
< p > Now our handler is separated in another file.< / p >
< p > But we used < code > String< / code > but it is a good practice to use < code > Data.Text< / code > instead.< / p >
< p > To declare we will use < code > Data.Text< / code > we modify the file < code > Foundation.hs< / code > .
Add an import directive just after the last one:< / p >
< pre class = "twilight" >
import Data.Text
< / pre >
< p > And also we must modify < code > config/routes< / code > and our handler accordingly. Replace < code > #String< / code > by < code > #Text< / code > in < code > config/routes< / code > :< / p >
< pre >
/echo/#Text EchoR GET
< / pre >
< p > And do the same in < code > Handler/Echo.hs< / code > :< / p >
< div class = "code" > < div class = "file" > < a href = "/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Echo.hs" > ➥ Echo.hs < / a > < / div > < div class = "withfile" >
< pre class = "twilight" >
< span class = "Keyword" > module< / span > < span class = "Constant" > Handler< / span > .< span class = "Constant" > Echo< / span > < span class = "Keyword" > where< / span >
< span class = "Keyword" > import< / span > < span class = "Constant" > Import< / span >
< span class = "Entity" > getEchoR< / span > :: < span class = "Constant" > Text< / span > → < span class = "Constant" > Handler< / span > < span class = "Constant" > RepHtml< / span >
getEchoR theText = < span class = "Keyword" > do< / span >
defaultLayout $ < span class = "Keyword" > do< / span >
[whamlet|< h1> #{theText}|]
< / pre >
< / div > < / div >
< h3 id = "use-a-new-template-file" > Use a new template file< / h3 >
< p > The last thing to change in order to do things like in
a real project is to use another template file.< / p >
< p > Just create a new file < code > template/echo.hamlet< / code > containing:< / p >
< div class = "code" > < div class = "file" > < a href = "/Scratch/en/blog/Yesod-tutorial-for-newbies/code/echo.hamlet" > ➥ echo.hamlet < / a > < / div > < div class = "withfile" >
< pre class = "twilight" >
< h1> #{theText}
< / pre >
< / div > < / div >
< p > and modify the handler < code > Handler/Echo.hs< / code > :< / p >
< pre class = "twilight" >
< span class = "Entity" > getEchoR< / span > :: < span class = "Constant" > Text< / span > → < span class = "Constant" > Handler< / span > < span class = "Constant" > RepHtml< / span >
getEchoR theText = < span class = "Keyword" > do< / span >
defaultLayout $ < span class = "Keyword" > do< / span >
$(widgetFile < span class = "String" > < span class = "String" > " < / span > echo< span class = "String" > " < / span > < / span > )
< / pre >
2011-12-29 16:05:05 +00:00
< h2 id = "protected-input" > Protected input< / h2 >
< hr / >
< div style = "background-color: #800; color: #FFF; font-weight: bold; border: 2px solid #300; box-shadow: 0 0 60px #300 inset;padding-left: 2em;" >
< h2 style = "margin: 0 auto; text-align: center" > TODO< / h2 >
< ul >
< li > Display something, show it is protected.
< / li > < li > Make the same as before, but with an input.
< / li > < li > Create a minimal blog system.
< / li > < li > Change template to html5 boilerplate.
< / li > < li > Use Authentification.
< / li >
< / ul >
< / div >
< / div >
< div id = "choixrss" >
< a id = "rss" href = "http://feeds.feedburner.com/yannespositocomen" >
Subscribe
< / a >
< / div >
< script type = "text/javascript" >
$(document).ready(function(){
$('#comment').hide();
$('#clickcomment').click(showComments);
});
function showComments() {
$('#comment').show();
$('#clickcomment').fadeOut();
}
document.write('< div id = "clickcomment" > Comments< / div > ');
< / script >
< div class = "flush" > < / div >
< div class = "corps" id = "comment" >
< h2 class = "first" > comments< / h2 >
< noscript >
You must enable javascript to comment.
< / noscript >
< script type = "text/javascript" >
var idcomments_acct = 'a307f0044511ff1b5cfca573fc0a52e7';
var idcomments_post_id = '/Scratch/en/blog/Yesod-tutorial-for-newbies/';
var idcomments_post_url = 'http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/';
< / script >
< span id = "IDCommentsPostTitle" style = "display:none" > < / span >
< script type = 'text/javascript' src = '/Scratch/js/genericCommentWrapperV2.js' > < / script >
< / div >
< div id = "entete" class = "corps_spaced" >
< div id = "liens" >
< ul > < li > < a href = "/Scratch/en/" > Home< / a > < / li >
< li > < a href = "/Scratch/en/blog/" > Blog< / a > < / li >
< li > < a href = "/Scratch/en/softwares/" > Softwares< / a > < / li >
< li > < a href = "/Scratch/en/about/" > About< / a > < / li > < / ul >
< / div >
< div class = "flush" > < / div >
< hr / >
< div id = "next_before_articles" >
< div id = "previous_articles" >
previous entries
< div class = "previous_article" >
< a href = "/Scratch/en/blog/SVG-and-m4-fractals/" > < span class = "nicer" > «< / span > Increase the power of deficient languages.< / a >
< / div >
< div class = "previous_article" >
< a href = "/Scratch/en/blog/Yesod-excellent-ideas/" > < span class = "nicer" > «< / span > Yesod excellent ideas< / a >
< / div >
< div class = "previous_article" >
< a href = "/Scratch/en/blog/programming-language-experience/" > < span class = "nicer" > «< / span > Programming Language Experience< / a >
< / div >
< / div >
< div id = "next_articles" >
next entries
< / div >
< div class = "flush" > < / div >
< / div >
< / div >
< div id = "bottom" >
< div >
< a rel = "license" href = "http://creativecommons.org/licenses/by-sa/3.0/" > Copyright ©, Yann Esposito< / a >
< / div >
< div id = "lastmod" >
Created: 12/28/2011
Modified: 12/29/2011
< / div >
< div >
Entirely done with
< a href = "http://www.vim.org" > Vim< / a >
and
< a href = "http://nanoc.stoneship.org" > nanoc< / a >
< / div >
< div >
< a href = "/Scratch/en/validation/" > Validation< / a >
< a href = "http://validator.w3.org/check?uri=referer" > [xhtml] < / a >
.
< a href = "http://jigsaw.w3.org/css-validator/check/referer?profile=css3" > [css] < / a >
.
< a href = "http://validator.w3.org/feed/check.cgi?url=http%3A//yannesposito.com/Scratch/en/blog/feed/feed.xml" > [rss]< / a >
< / div >
< / div >
< div class = "clear" > < / div >
< / div >
< / body >
< / html >