scratch/output/Scratch/fr/blog/Yesod-tutorial-for-newbies/index.html

963 lines
51 KiB
HTML
Raw Normal View History

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/yannespositocomfr"/>
2012-01-11 20:40:22 +00:00
<link rel="alternate" lang="fr" xml:lang="fr" title="Site en Haskell" type="text/html" hreflang="fr" href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/" />
<link rel="alternate" lang="en" xml:lang="en" title="Haskell web programming" type="text/html" hreflang="en" href="/Scratch/en/blog/Yesod-tutorial-for-newbies/" />
2011-12-29 16:05:05 +00:00
<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]-->
2012-01-11 20:40:22 +00:00
<title>Site en Haskell</title>
2011-12-29 16:05:05 +00:00
</head>
<body lang="fr" class="article">
<script type="text/javascript">// <![CDATA[
document.write('<div id="blackpage"><img src="/Scratch/img/loading.gif" alt="Chargement en cours..."/></div>');
// ]]>
</script>
<div id="content">
<div id="choix">
<div class="return"><a href="#entete">&darr; Menu &darr;</a></div>
<div id="choixlang">
<a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/" onclick="setLanguage('en')">in English</a>
</div>
<div class="flush"></div>
</div>
<div id="titre">
<h1>
2012-01-11 20:40:22 +00:00
Site en Haskell
2011-12-29 16:05:05 +00:00
</h1>
2012-01-11 20:40:22 +00:00
<h2>
Un tutoriel Yesod
</h2>
2011-12-29 16:05:05 +00:00
</div>
<div class="flush"></div>
<div class="flush"></div>
<div id="afterheader">
<div class="corps">
2012-01-11 20:40:22 +00:00
<p><img alt="Neo Flying at warp speed" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/flying_neo.jpg" /></p>
2011-12-29 16:05:05 +00:00
<div class="intro">
2012-01-18 13:28:01 +00:00
<p><span class="sc"><abbr title="Trop long à lire">tlàl</abbr>&nbsp;: </span> Un tutoriel pour yesod, un framework web Haskell.
Vous ne devriez pas avoir besoin de savoir programmer en Haskell.
Par contre je suis désolé pour les francophones,
2012-01-20 11:20:07 +00:00
mais je n&rsquo;ai pas eu le courage de traduire cet article en Français.</p>
2011-12-29 16:05:05 +00:00
<blockquote>
2012-01-11 20:40:22 +00:00
<center><span class="sc"><b>Table of content</b></span></center>
2011-12-29 16:05:05 +00:00
<ul id="markdown-toc">
2012-01-11 20:40:22 +00:00
<li><a href="#before-the-real-start">Before the real start</a> <ul>
<li><a href="#install">Install</a></li>
<li><a href="#initialize">Initialize</a></li>
<li><a href="#configure-git">Configure git</a></li>
2012-01-18 13:28:01 +00:00
<li><a href="#some-last-minute-words">Some last minute words</a></li>
2012-01-02 14:39:00 +00:00
</ul>
</li>
2012-01-11 20:40:22 +00:00
<li><a href="#echo">Echo</a> <ul>
<li><a href="#bulletproof">Bulletproof?</a></li>
2012-01-18 13:28:01 +00:00
<li><a href="#cleaning-up">Cleaning up</a> <ul>
<li><a href="#span-classschtml5span-boilerplate-and-a-better-span-classsccssspan"><span class="sc">html5</span> boilerplate and a better <span class="sc">css</span></a></li>
<li><a href="#separate-handlers">Separate Handlers</a></li>
<li><a href="#datatext"><code>Data.Text</code></a></li>
<li><a href="#use-templates">Use templates</a></li>
</ul>
</li>
2011-12-30 16:14:57 +00:00
</ul>
</li>
2012-01-18 13:28:01 +00:00
<li><a href="#mirror">Mirror</a></li>
<li><a href="#a-blog">A Blog</a></li>
2012-01-18 14:51:20 +00:00
<li><a href="#conclusion">Conclusion</a></li>
2011-12-29 16:05:05 +00:00
</ul>
</blockquote>
</div>
2012-01-18 13:28:01 +00:00
<p>Why Haskell?</p>
2012-01-11 20:40:22 +00:00
<p><img alt="Impressive Haskell Benchmark" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/haskell-benchmark.png" /></p>
2012-01-18 13:28:01 +00:00
<p>Its efficiency (see <a href="http://snapframework.com/blog/2010/11/17/snap-0.3-benchmarks">Snap Benchmark</a> <em class="pala">&amp;</em>
<a href="http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks">Warp Benchmark</a><sup id="fnref:benchmarkdigression"><a href="#fn:benchmarkdigression" rel="footnote">1</a></sup>).
Haskell is an order of magnitude faster than interpreted languages
like <a href="http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&amp;lang=ghc&amp;lang2=yarv">Ruby</a> and <a href="http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&amp;lang=ghc&amp;lang2=python3">Python</a><sup id="fnref:speeddigression"><a href="#fn:speeddigression" rel="footnote">2</a></sup>.</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<p>Haskell is a high level language and make it harder to shoot you in the foot
2012-01-18 14:51:20 +00:00
than <code>C</code>, <code>C++</code> or <code>Java</code> for example.
2012-01-18 13:28:01 +00:00
One of the best property of Haskell being:</p>
2012-01-11 20:40:22 +00:00
<blockquote>
2012-01-20 11:20:07 +00:00
<p>&ldquo;If your program compile it will be
very close to what the programmer intended&rdquo;.</p>
2012-01-11 20:40:22 +00:00
</blockquote>
2012-01-18 13:28:01 +00:00
<p>Haskell web frameworks handle parallel tasks perfectly.
For example even better than node.js<sup id="fnref:nodejstroll"><a href="#fn:nodejstroll" rel="footnote">3</a></sup>.</p>
2012-01-11 20:40:22 +00:00
2012-01-18 14:51:20 +00:00
<p><img alt="Thousands of Agent Smith" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/thousands_smiths.jpg" class="left" /></p>
2012-01-18 13:28:01 +00:00
2012-01-20 11:20:07 +00:00
<p>From the pure technical point of view,
2012-01-18 14:51:20 +00:00
Haskell seems to be the perfect web development tool.
2012-01-20 11:20:07 +00:00
Weaknesses of Haskell certainly won&rsquo;t be technical:</p>
2011-12-29 16:05:05 +00:00
2012-01-02 14:39:00 +00:00
<ul>
2012-01-11 20:40:22 +00:00
<li>Hard to grasp Haskell</li>
<li>Hard to find a Haskell programmer</li>
2012-01-18 14:51:20 +00:00
<li>The Haskell community is smaller than the community for <code>/.*/</code> </li>
2012-01-11 20:40:22 +00:00
<li>There is no <a href="http://heroku.com">heroku</a> for Haskell (even if <a href="http://www.yesodweb.com/blog/2011/07/haskell-on-heroku">Greg Weber did it</a>, it was more a workaround).</li>
2012-01-02 14:39:00 +00:00
</ul>
2011-12-29 16:05:05 +00:00
2012-01-20 11:20:07 +00:00
<p>I won&rsquo;t say these are not important drawbacks.
2012-01-18 14:51:20 +00:00
But, with Haskell your web application will have both properties
to absorb an impressive number of parallel request securely
and to adapt to change.</p>
2012-01-02 14:39:00 +00:00
2012-01-18 14:51:20 +00:00
<p>Actually there are three main Haskell web frameworks:</p>
2011-12-29 16:05:05 +00:00
<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>
2012-01-20 11:20:07 +00:00
<p>I don&rsquo;t think there is a real winner between these three framework.
2012-01-02 14:39:00 +00:00
The choice I made for yesod is highly subjective.
2012-01-18 13:28:01 +00:00
I just lurked a bit and tried some tutorials.
2012-01-11 20:40:22 +00:00
I had the feeling yesod make a better job at helping newcomers.
2012-01-18 13:28:01 +00:00
Furthermore, apparently the yesod team seems the most active.
Of course I might be wrong since it is a matter of feeling.</p>
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<p><img alt="1. Draw some circles. 2. Draw the rest of the fucking owl" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/owl_draw.png" /></p>
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<p>Why did I write this article?
2012-01-18 13:28:01 +00:00
The yesod documentation and particularly the book are excellent.
But I missed an intermediate tutorial.
2012-01-20 11:20:07 +00:00
This tutorial won&rsquo;t explain all details.
2012-01-18 13:28:01 +00:00
I tried to give a step by step of how to start from a five minute tutorial
to an almost production ready architecture.
Furthermore explaining something to others is a great way to learn.
2012-01-20 11:20:07 +00:00
If you are used to Haskell and Yesod, this tutorial won&rsquo;t learn you much.
If you are completely new to Haskell and Yesod it might hopefully helps you.
Also if you find yourself too confused by the syntax, it might helps to read this
<a href="http://blog.ezyang.com/2011/11/how-to-read-haskell/">article</a></p>
2012-01-18 13:28:01 +00:00
2012-01-20 11:20:07 +00:00
<p>During this tutorial you&rsquo;ll install, initialize and configure your first yesod project.
2012-01-18 13:28:01 +00:00
Then there is a very minimal 5 minutes yesod tutorial to heat up and verify the awesomeness of yesod.
2012-01-20 11:20:07 +00:00
Then we will clean up the 5 minutes tutorial to use some &ldquo;best practices&rdquo;.
2012-01-18 13:28:01 +00:00
Finally there will be a more standard real world example; a minimal blog system.</p>
2012-01-11 20:40:22 +00:00
<h2 id="before-the-real-start">Before the real start</h2>
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<h3 id="install">Install</h3>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>The recommended way to install <a href="http://www.haskell.org">Haskell</a>
is to download the <a href="http://www.haskell.org/platform">Haskell Platform</a>.</p>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>Once done, you need to install yesod.
Open a terminal session and do:</p>
2011-12-29 16:05:05 +00:00
<pre class="twilight">
2012-01-11 20:40:22 +00:00
<span class="Keyword">~</span> cabal update
<span class="Keyword">~</span> cabal install yesod cabal-dev
2011-12-29 16:05:05 +00:00
</pre>
2012-01-18 13:28:01 +00:00
<p>There are few steps but it should take some time to finish.</p>
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<h3 id="initialize">Initialize</h3>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>You are now ready to initialize your first yesod project.
Open a terminal and type:</p>
2011-12-29 16:05:05 +00:00
<pre class="twilight">
2012-01-11 20:40:22 +00:00
<span class="Keyword">~</span> yesod init
2011-12-29 16:05:05 +00:00
</pre>
2012-01-18 13:28:01 +00:00
<p>Enter your name, choose <code>yosog</code> for the project name and enter <code>Yosog</code> for the name of the Foundation.
Finally choose <code>sqlite</code>.
Now, start the development cycle:</p>
2011-12-29 16:05:05 +00:00
<pre class="twilight">
2012-01-11 20:40:22 +00:00
<span class="Keyword">~</span> cd yosog
2012-01-18 13:28:01 +00:00
<span class="Keyword">~</span> cabal-dev install <span class="Keyword">&amp;&amp;</span> yesod --dev devel
2011-12-29 16:05:05 +00:00
</pre>
2012-01-18 13:28:01 +00:00
<p>This will compile the entire project. Be patient it could take a while the first time.
2012-01-02 14:39:00 +00:00
Once finished a server is launched and you could visit it by clicking this link:</p>
2011-12-29 16:05:05 +00:00
<p><a href="http://localhost:3000"><code>http://localhost:3000</code></a></p>
2012-01-11 20:40:22 +00:00
<p>Congratulation! Yesod works!</p>
2012-01-02 14:39:00 +00:00
<blockquote>
2012-01-11 20:40:22 +00:00
Note: if something is messed up use the following command line inside the project directory.
<pre class="twilight">
<span class="Constant">\r</span>m -rf dist/* <span class="Keyword">;</span> cabal-dev install <span class="Keyword">&amp;&amp;</span> yesod --dev devel
</pre>
2012-01-02 14:39:00 +00:00
</blockquote>
2012-01-18 13:28:01 +00:00
<p>Until the end of the tutorial, use another terminal and let this one open
in a corner to see what occurs.</p>
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<h3 id="configure-git">Configure git</h3>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<blockquote>
<p>Of course this step is not mandatory for the tutorial
but it is a good practice.</p>
</blockquote>
2011-12-29 16:05:05 +00:00
2012-01-02 14:39:00 +00:00
<p>Copy this <code>.gitignore</code> file into the <code>yosog</code> folder.</p>
2011-12-29 16:05:05 +00:00
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/.gitignore"> &#x27A5; .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">
2012-01-11 20:40:22 +00:00
<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">&quot;</span>Initial yesod commit<span class="String">&quot;</span></span>
2011-12-29 16:05:05 +00:00
</pre>
2012-01-18 13:28:01 +00:00
<p>We are almost ready to start.</p>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<h3 id="some-last-minute-words">Some last minute words</h3>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>Up until here, we have a directory containing a bunch of files
and a local web server listening the port 3000.
If we modify a file inside this directory, yesod should try
to recompile as fast as possible the site.
Instead of explaining the role of every file,
2012-01-20 11:20:07 +00:00
let&rsquo;s focus only on the important files/directories for this tutorial:</p>
2011-12-29 16:05:05 +00:00
<ol>
<li><code>config/routes</code></li>
<li><code>Handler/</code></li>
<li><code>templates/</code></li>
<li><code>config/models</code></li>
</ol>
<p>Obviously:</p>
2012-01-11 20:40:22 +00:00
<table>
<tbody>
<tr>
<td><code>config/routes</code></td>
2012-01-20 11:20:07 +00:00
<td>is where you&rsquo;ll configure the map <span class="sc">url</span> → Code.</td>
2012-01-11 20:40:22 +00:00
</tr>
<tr>
<td><code>Handler/</code></td>
2012-01-18 13:28:01 +00:00
<td>contains the files that will contain the code called when a <span class="sc">url</span> is accessed.</td>
2012-01-11 20:40:22 +00:00
</tr>
<tr>
<td><code>templates/</code></td>
2012-01-18 13:28:01 +00:00
<td>contains <span class="sc">html</span>, js and <span class="sc">css</span> templates.</td>
2012-01-11 20:40:22 +00:00
</tr>
<tr>
<td><code>config/models</code></td>
2012-01-20 11:20:07 +00:00
<td>is where you&rsquo;ll configure the persistent objects (database tables).</td>
2012-01-11 20:40:22 +00:00
</tr>
</tbody>
</table>
2012-01-20 11:20:07 +00:00
<p>During this tutorial we&rsquo;ll modify other files as well,
but we won&rsquo;t explore them in detail.</p>
2012-01-11 20:40:22 +00:00
2012-01-20 11:20:07 +00:00
<p>Also note, shell commands are executed in the root directory of your project instead specified otherwise.</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<p>We are now ready to start!</p>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<h2 id="echo">Echo</h2>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>To verify the quality of the security of the yesod framework,
2012-01-20 11:20:07 +00:00
let&rsquo;s make a minimal echo application.</p>
2012-01-02 14:39:00 +00:00
2012-01-18 13:28:01 +00:00
<blockquote>
<p>Goal:</p>
2011-12-29 16:05:05 +00:00
2012-01-20 11:20:07 +00:00
<p>Make a server that when accessed <code>/echo/[some text]</code> should return a web page containing &ldquo;some text&rdquo; inside an <code>h1</code> bloc.</p>
2012-01-18 13:28:01 +00:00
</blockquote>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>In a first time, we must declare the <span class="sc">url</span> of the form <code>/echo/...</code> are meaningful.
2012-01-20 11:20:07 +00:00
Let&rsquo;s take a look at the file <code>config/routes</code>:</p>
2012-01-18 13:28:01 +00:00
<pre>
2011-12-29 16:05:05 +00:00
/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.
2012-01-18 13:28:01 +00:00
Add the following:</p>
2011-12-29 16:05:05 +00:00
<pre>
/echo/#String EchoR GET
</pre>
2012-01-18 13:28:01 +00:00
<p>This line contains three elements: the <span class="sc">url</span> pattern, a handler name, an <span class="sc">http</span> method.
I am not particularly fan of the big R notation but this is the standard convention.</p>
2011-12-29 16:05:05 +00:00
2012-01-02 14:39:00 +00:00
<p>If you save <code>config/routes</code>, you should see your terminal in which you launched <code>yesod devel</code> activate and certainly displaying an error message.</p>
2011-12-29 16:05:05 +00:00
<pre>
Application.hs:31:1: Not in scope: `getEchoR'
</pre>
2012-01-20 11:20:07 +00:00
<p>Why? Simply because we didn&rsquo;t written the code for the handler <code>EchoR</code>.
2012-01-18 13:28:01 +00:00
Edit the file <code>Handler/Root.hs</code> and append this:</p>
2011-12-29 16:05:05 +00:00
<pre class="twilight">
2012-01-11 20:40:22 +00:00
<span class="Entity">getEchoR</span> :: <span class="Constant">String</span> -&gt; <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
2011-12-29 16:05:05 +00:00
getEchoR theText = <span class="Keyword">do</span>
defaultLayout $ <span class="Keyword">do</span>
[whamlet|&lt;h1&gt;#{theText}|]
</pre>
2012-01-20 11:20:07 +00:00
<p>Don&rsquo;t worry if you find all of this a bit cryptic.
2012-01-18 13:28:01 +00:00
In short it just declare a function named <code>getEchoR</code> with one argument (<code>theText</code>) of type String.
When this function is called, it return a <code>Handler RepHtml</code> whatever it is.
But mainly this will encapsulate our expected result inside an <span class="sc">html</span> text.</p>
2012-01-02 14:39:00 +00:00
2011-12-29 16:05:05 +00:00
<p>After saving the file, you should see yesod recompile the application.
2012-01-20 11:20:07 +00:00
When the compilation is finished you&rsquo;ll see the message: <code>Starting devel application</code>.</p>
2012-01-02 14:39:00 +00:00
<p>Now you can visit: <a href="http://localhost:3000/echo/Yesod%20rocks!"><code>http://localhost:3000/echo/Yesod%20rocks!</code></a></p>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>TADA! It works! </p>
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<h3 id="bulletproof">Bulletproof?</h3>
2012-01-02 14:39:00 +00:00
2012-01-11 20:40:22 +00:00
<p><img alt="Neo stops a myriad of bullets" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/neo_bullet_proof.jpg" /></p>
2012-01-18 13:28:01 +00:00
<p>Even this extremely minimal web application has some impressive properties.
For exemple, imagine an attacker entering this <span class="sc">url</span>:</p>
2011-12-29 16:05:05 +00:00
2011-12-30 16:14:57 +00:00
<p><a href="http://localhost:3000/echo/&lt;a&gt;I'm &lt;script&gt;alert(&quot;Bad!&quot;);"><code>http://localhost:3000/echo/&lt;a&gt;I'm &lt;script&gt;alert("Bad!");</code></a></p>
2011-12-29 16:05:05 +00:00
<p>The special characters are protected for us.
2012-01-18 13:28:01 +00:00
A malicious user could not hide some bad script inside.</p>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>This behavior is a direct consequence of <em>type safety</em>.
The <span class="sc">url</span> string is put inside a <span class="sc">url</span> type.
Then the interesting part in the <span class="sc">url</span> is put inside a String type.
To pass from <span class="sc">url</span> type to String type some transformation are made.
2012-01-20 11:20:07 +00:00
For example, replace all &ldquo;<code>%20</code>&rdquo; by space characters.
2012-01-18 13:28:01 +00:00
Then to show the String inside an <span class="sc">html</span> document, the string is put inside an <span class="sc">html</span> type.
2012-01-20 11:20:07 +00:00
Some transformations occurs like replace &ldquo;<code>&lt;</code>&rdquo; by &ldquo;<code>&amp;lt;</code>&rdquo;.
2012-01-18 13:28:01 +00:00
Thanks to yesod, this tedious job is done for us.</p>
2011-12-29 16:05:05 +00:00
<pre class="twilight">
2012-01-11 20:40:22 +00:00
<span class="String"><span class="String">&quot;</span>http://localhost:3000/echo/some%20text&lt;a&gt;<span class="String">&quot;</span></span> :: URL
2011-12-29 16:05:05 +00:00
2012-01-11 20:40:22 +00:00
<span class="String"><span class="String">&quot;</span>some text&lt;a&gt;<span class="String">&quot;</span></span> :: String
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<span class="String"><span class="String">&quot;</span>some text &amp;lt;a&amp;gt;<span class="String">&quot;</span></span> :: Html
2011-12-29 16:05:05 +00:00
</pre>
2012-01-18 13:28:01 +00:00
<p>Yesod is not only fast, it helps us to remain secure.
It protects us from many common errors in other paradigms.
Yes, I am looking at you PHP!</p>
2012-01-02 14:39:00 +00:00
2012-01-11 20:40:22 +00:00
<h3 id="cleaning-up">Cleaning up</h3>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>Even this very minimal example should be enhanced.
We will clean up many details:</p>
<ul>
<li>Use <span class="sc">html5</span> boilerplate</li>
<li>Use a general <span class="sc">css</span> (cleaner than the empty by default)</li>
<li>Dispatch handler code into different files</li>
<li>Use <code>Data.Text</code> instead of <code>String</code></li>
2012-01-20 11:20:07 +00:00
<li>Put our &ldquo;views&rdquo;<sup id="fnref:explainviewwidget"><a href="#fn:explainviewwidget" rel="footnote">4</a></sup> inside the <code>template</code> directory</li>
2012-01-18 13:28:01 +00:00
</ul>
<h4 id="span-classschtml5span-boilerplate-and-a-better-span-classsccssspan"><span class="sc">html5</span> boilerplate and a better <span class="sc">css</span></h4>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>Copy the boilerplate template to the default template.
If you take a look at them, the format is not <span class="sc">html</span> but hamlet.</p>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<pre class="twilight">
<span class="Keyword">~</span> cp templates/boilerplate-wrapper.hamlet templates/default-layout-wrapper.hamlet
</pre>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>It is time to change the default <span class="sc">css</span>.
Add a file named <code>default-layout.lucius</code> inside the <code>template/</code> directory containing:</p>
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/default-layout.lucius"> &#x27A5; default-layout.lucius </a></div><div class="withfile">
<pre class="twilight">
<span class="CssTagName">body</span> {
<span class="CssPropertyName">font-family</span>: <span class="SupportConstant">Helvetica</span>, <span class="SupportConstant">sans-serif</span>;
<span class="CssPropertyName">font-size</span>: <span class="CssAdditionalConstants">18</span><span class="Keyword">px</span>; }
<span class="CssId"><span class="CssId">#</span>content</span> {
<span class="CssPropertyName">padding</span>: <span class="CssAdditionalConstants">1</span><span class="Keyword">em</span>;
<span class="CssPropertyName">border</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>CCC</span> <span class="CssPropertyValue">solid</span><span class="CssAdditionalConstants"> 2</span><span class="Keyword">px</span>;
<span class="CssPropertyName">border</span>-radius: <span class="CssAdditionalConstants">5</span><span class="Keyword">px</span>;
<span class="CssPropertyName">margin</span>: <span class="CssAdditionalConstants">1</span><span class="Keyword">em</span>;
<span class="CssPropertyName">width</span>: <span class="CssAdditionalConstants">37</span><span class="Keyword">em</span>;
<span class="CssPropertyName">margin</span>: <span class="CssAdditionalConstants">1</span><span class="Keyword">em</span> <span class="CssPropertyValue">auto</span>;
<span class="CssPropertyName">background</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>F2F2F2</span>;
<span class="CssPropertyName">line-height</span>: <span class="CssAdditionalConstants">1.5</span><span class="Keyword">em</span>;
<span class="CssPropertyName">color</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>333</span>; }
<span class="CssClass"><span class="CssClass">.</span>required</span> { <span class="CssPropertyName">margin</span>: <span class="CssAdditionalConstants">1</span><span class="Keyword">em</span><span class="CssAdditionalConstants"> 0</span>; }
<span class="CssClass"><span class="CssClass">.</span>optional</span> { <span class="CssPropertyName">margin</span>: <span class="CssAdditionalConstants">1</span><span class="Keyword">em</span><span class="CssAdditionalConstants"> 0</span>; }
<span class="CssTagName">label</span> { <span class="CssPropertyName">width</span>: <span class="CssAdditionalConstants">8</span><span class="Keyword">em</span>; <span class="CssPropertyName">display</span>: <span class="CssPropertyValue">inline-block</span>; }
<span class="CssTagName">input</span>, <span class="CssTagName">textarea</span> { <span class="CssPropertyName">background</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>FAFAFA</span>}
<span class="CssTagName">textarea</span> { <span class="CssPropertyName">width</span>: <span class="CssAdditionalConstants">27</span><span class="Keyword">em</span>; <span class="CssPropertyName">height</span>: <span class="CssAdditionalConstants">9</span><span class="Keyword">em</span>;}
<span class="CssTagName">ul</span> { <span class="CssPropertyName">list-style</span>: <span class="CssPropertyValue">square</span>; }
<span class="CssTagName">a</span> { <span class="CssPropertyName">color</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>A56</span>; }
<span class="CssTagName">a</span><span class="MetaTagInline"><span class="MetaTagInline">:</span>hover</span> { <span class="CssPropertyName">color</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>C58</span>; }
<span class="CssTagName">a</span><span class="MetaTagInline"><span class="MetaTagInline">:</span>active</span> { <span class="CssPropertyName">color</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>C58</span>; }
<span class="CssTagName">a</span><span class="MetaTagInline"><span class="MetaTagInline">:</span>visited</span> { <span class="CssPropertyName">color</span>: <span class="CssAdditionalConstants"><span class="CssAdditionalConstants">#</span>943</span>; }
</pre>
</div></div>
<p>Personally I would prefer if such a minimal <span class="sc">css</span> was put with the scaffolding tool.
I am sure somebody already made such a minimal <span class="sc">css</span> which give the impression
the browser handle correctly <span class="sc">html</span> without any style applied to it.
But I digress.</p>
<h4 id="separate-handlers">Separate Handlers</h4>
2012-01-20 11:20:07 +00:00
<p>Generally you don&rsquo;t want to have all your code inside a unique file.
2012-01-18 13:28:01 +00:00
This is why we will separate our handlers.
In a first time create a new file <code>Handler/Echo.hs</code> containing:</p>
2011-12-30 16:14:57 +00:00
<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>
2012-01-11 20:40:22 +00:00
<span class="Entity">getEchoR</span> :: <span class="Constant">String</span> -&gt; <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
2011-12-30 16:14:57 +00:00
getEchoR theText = <span class="Keyword">do</span>
defaultLayout $ <span class="Keyword">do</span>
[whamlet|&lt;h1&gt;#{theText}|]
</pre>
2012-01-18 13:28:01 +00:00
<p>Do not forget to remove the getEchoR function inside <code>Handler/Root.hs</code>.</p>
2011-12-30 16:14:57 +00:00
2012-01-18 13:28:01 +00:00
<p>We must declare this new file into<code>yosog.cabal</code>.
Just after <code>Handler.Root</code>, add:</p>
2011-12-30 16:14:57 +00:00
<pre>
Handler.Echo
</pre>
2012-01-18 13:28:01 +00:00
<p>We must also declare this new Handler module inside <code>Application.hs</code>.
2012-01-20 11:20:07 +00:00
Just after the &ldquo;<code>import Handler.Root</code>&rdquo;, add:</p>
2011-12-30 16:14:57 +00:00
<pre class="twilight">
<span class="Keyword">import</span> <span class="Constant">Handler</span>.<span class="Constant">Echo</span>
</pre>
2012-01-18 13:28:01 +00:00
<p>This is it. </p>
<p><small><em>ps:</em> I am sure not so far in the future we could simply write
<code>yesod add-handler Echo</code> to declare it and create a new handler file.</small></p>
2011-12-30 16:14:57 +00:00
2012-01-18 13:28:01 +00:00
<h4 id="datatext"><code>Data.Text</code></h4>
2011-12-30 16:14:57 +00:00
2012-01-11 20:40:22 +00:00
<p>It is a good practice to use <code>Data.Text</code> instead of <code>String</code>.</p>
2011-12-30 16:14:57 +00:00
2012-01-18 13:28:01 +00:00
<p>To declare it, add this import directive to <code>Foundation.hs</code> (just after the last one):</p>
2011-12-30 16:14:57 +00:00
<pre class="twilight">
import Data.Text
</pre>
2012-01-18 13:28:01 +00:00
<p>We have to modify <code>config/routes</code> and our handler accordingly.
Replace <code>#String</code> by <code>#Text</code> in <code>config/routes</code>:</p>
2011-12-30 16:14:57 +00:00
<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/fr/blog/Yesod-tutorial-for-newbies/code/Echo.hs"> &#x27A5; 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>
2012-01-11 20:40:22 +00:00
<span class="Entity">getEchoR</span> :: <span class="Constant">Text</span> -&gt; <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
2011-12-30 16:14:57 +00:00
getEchoR theText = <span class="Keyword">do</span>
defaultLayout $ <span class="Keyword">do</span>
[whamlet|&lt;h1&gt;#{theText}|]
</pre>
</div></div>
2012-01-18 13:28:01 +00:00
<h4 id="use-templates">Use templates</h4>
2011-12-30 16:14:57 +00:00
2012-01-18 13:28:01 +00:00
<p>Some <span class="sc">html</span> (more precisely hamlet) is written directly inside our handler.
We should put this part inside another file.
Create the new file <code>template/echo.hamlet</code> containing:</p>
2011-12-30 16:14:57 +00:00
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/echo.hamlet"> &#x27A5; echo.hamlet </a></div><div class="withfile">
<pre class="twilight">
&lt;h1&gt; #{theText}
</pre>
</div></div>
<p>and modify the handler <code>Handler/Echo.hs</code>:</p>
<pre class="twilight">
2012-01-11 20:40:22 +00:00
<span class="Entity">getEchoR</span> :: <span class="Constant">Text</span> -&gt; <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
2011-12-30 16:14:57 +00:00
getEchoR theText = <span class="Keyword">do</span>
defaultLayout $ <span class="Keyword">do</span>
$(widgetFile <span class="String"><span class="String">&quot;</span>echo<span class="String">&quot;</span></span>)
</pre>
2011-12-29 16:05:05 +00:00
2012-01-18 13:28:01 +00:00
<p>At this point, our web application is structured between different files.
2012-01-02 14:39:00 +00:00
Handler are grouped, we use <code>Data.Text</code> and our views are in templates.
2012-01-18 13:28:01 +00:00
It is the time to make a slightly more complex example.</p>
<h2 id="mirror">Mirror</h2>
2012-01-02 14:39:00 +00:00
2012-01-18 13:28:01 +00:00
<p><img alt="Neo touching a mirror" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/mirror.jpg" class="left" /></p>
2011-12-29 16:05:05 +00:00
2012-01-20 11:20:07 +00:00
<p>Let&rsquo;s make another minimal application.
2012-01-11 20:40:22 +00:00
You should see a form containing a text field and a validation button.
2012-01-20 11:20:07 +00:00
When you enter some text (for example &ldquo;Jormungad&rdquo;) and validate,
2012-01-18 13:28:01 +00:00
the next page present you the content and its reverse appended to it.
2012-01-20 11:20:07 +00:00
In our example it should return &ldquo;JormungaddagnumroJ&rdquo;. </p>
2012-01-02 14:39:00 +00:00
<p>First, add a new route:</p>
2012-01-18 13:28:01 +00:00
<pre>
/mirror MirrorR GET POST
2012-01-02 14:39:00 +00:00
</pre>
2012-01-18 13:28:01 +00:00
<p>This time the path <code>/mirror</code> will accept GET and POST requests.
Add the corresponding new Handler file:</p>
2012-01-02 14:39:00 +00:00
2012-01-18 13:28:01 +00:00
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/Mirror.hs"> &#x27A5; Mirror.hs </a></div><div class="withfile">
2012-01-02 14:39:00 +00:00
<pre class="twilight">
2012-01-18 13:28:01 +00:00
<span class="Keyword">module</span> <span class="Constant">Handler</span>.<span class="Constant">Mirror</span> <span class="Keyword">where</span>
2012-01-02 14:39:00 +00:00
<span class="Keyword">import</span> <span class="Constant">Import</span>
2012-01-18 13:28:01 +00:00
<span class="Keyword">import</span> qualified <span class="Constant">Data</span>.<span class="Constant">Text</span> <span class="Keyword">as</span> <span class="Constant">T</span>
2012-01-02 14:39:00 +00:00
2012-01-18 13:28:01 +00:00
<span class="Entity">getMirrorR</span> :: <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
getMirrorR = <span class="Keyword">do</span>
2012-01-02 14:39:00 +00:00
defaultLayout $ <span class="Keyword">do</span>
2012-01-18 13:28:01 +00:00
$(widgetFile <span class="String"><span class="String">&quot;</span>mirror<span class="String">&quot;</span></span>)
2012-01-02 14:39:00 +00:00
2012-01-18 13:28:01 +00:00
<span class="Entity">postMirrorR</span> :: <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
postMirrorR = <span class="Keyword">do</span>
postedText &lt;- runInputPost $ ireq textField <span class="String"><span class="String">&quot;</span>content<span class="String">&quot;</span></span>
defaultLayout $ <span class="Keyword">do</span>
$(widgetFile <span class="String"><span class="String">&quot;</span>posted<span class="String">&quot;</span></span>)
2012-01-02 14:39:00 +00:00
</pre>
</div></div>
2012-01-20 11:20:07 +00:00
<p>Don&rsquo;t forget to declare it inside <code>yosog.cabal</code> and <code>Application.hs</code>.</p>
2012-01-18 13:28:01 +00:00
<p>We will need to use the <code>reverse</code> function provided by <code>Data.Text</code>
which explain the additional import.</p>
2012-01-02 14:39:00 +00:00
2012-01-20 11:20:07 +00:00
<p>The only new thing here is the line that get the POST parameter named &ldquo;content&rdquo;.
2012-01-18 13:28:01 +00:00
If you want to know more detail about it and form in general you can take
look at <a href="http://www.yesodweb.com/book/forms">the yesod book</a>.</p>
2012-01-02 14:39:00 +00:00
<p>Create the two corresponding templates:</p>
2012-01-18 13:28:01 +00:00
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/mirror.hamlet"> &#x27A5; mirror.hamlet </a></div><div class="withfile">
2012-01-02 14:39:00 +00:00
<pre class="twilight">
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">h1</span><span class="MetaTagAll">&gt;</span></span> Enter your text
2012-01-18 13:28:01 +00:00
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">form</span> <span class="MetaTagAll">method</span>=<span class="MetaTagAll">post</span> <span class="MetaTagAll">action</span>=@<span class="EmbeddedSource">{MirrorR}</span><span class="MetaTagAll">&gt;</span></span>
2012-01-02 14:39:00 +00:00
<span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">input</span> <span class="MetaTagInline">type</span>=<span class="MetaTagInline">text</span> <span class="MetaTagInline">name</span>=<span class="MetaTagInline">content</span><span class="MetaTagInline">&gt;</span></span>
<span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">input</span> <span class="MetaTagInline">type</span>=<span class="MetaTagInline">submit</span><span class="MetaTagInline">&gt;</span></span>
</pre>
</div></div>
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/posted.hamlet"> &#x27A5; posted.hamlet </a></div><div class="withfile">
<pre class="twilight">
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">h1</span><span class="MetaTagAll">&gt;</span></span>You've just posted
2012-01-18 13:28:01 +00:00
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>#<span class="EmbeddedSource">{postedText}</span>#<span class="EmbeddedSource">{T.reverse postedText}</span>
2012-01-02 14:39:00 +00:00
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">hr</span><span class="MetaTagAll">&gt;</span></span>
2012-01-18 13:28:01 +00:00
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span><span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">a</span> <span class="MetaTagInline">href</span>=@<span class="EmbeddedSource">{MirrorR}</span><span class="MetaTagInline">&gt;</span></span>Get back
2012-01-02 14:39:00 +00:00
</pre>
</div></div>
2012-01-11 20:40:22 +00:00
<p>And that is all.
2012-01-20 11:20:07 +00:00
This time, we won&rsquo;t need to clean up.
2012-01-11 20:40:22 +00:00
We may have used another way to generate the form
2012-01-20 11:20:07 +00:00
but we&rsquo;ll see this in the next section.</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<p>Just try it by <a href="http://localhost:3000/mirror">clicking here</a>.</p>
2012-01-02 14:39:00 +00:00
2012-01-18 13:28:01 +00:00
<p>Also you can try to enter strange values.
Like before, your application is quite secure.</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<h2 id="a-blog">A Blog</h2>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<p>We saw how to retrieve <span class="sc">http</span> parameters.
It is the time to save things into a database.</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<p>As before add some routes inside <code>config/routes</code>:</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<pre>
/blog BlogR GET POST
/blog/#ArticleId ArticleR GET
</pre>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<p>This example will be very minimal:</p>
2011-12-29 16:05:05 +00:00
<ul>
2012-01-18 13:28:01 +00:00
<li><code>GET</code> on <code>/blog</code> should display the list of articles.</li>
<li><code>POST</code> on <code>/blog</code> should create a new article</li>
<li><code>GET</code> on <code>/blog/&lt;article id&gt;</code> should display the content of the article.</li>
2011-12-29 16:05:05 +00:00
</ul>
2012-01-18 13:28:01 +00:00
<p>First we declare another model object.
Append the following content to <code>config/models</code>:</p>
2012-01-11 20:40:22 +00:00
2012-01-18 13:28:01 +00:00
<pre>
Article
title Text
content Html
deriving
</pre>
<p>As <code>Html</code> is not an instance of <code>Read</code>, <code>Show</code> and <code>Eq</code>,
we had to add the <code>deriving</code> line.
If you forget it, there will be an error.</p>
<p>After the route and the model, we write the handler.
First, declare a new Handler module.
Add <code>import Handler.Blog</code> inside <code>Application.hs</code> and add it into <code>yosog.cabal</code>.
2012-01-20 11:20:07 +00:00
Let&rsquo;s write the content of <code>Handler/Blog.hs</code>.
2012-01-18 13:28:01 +00:00
We start by declaring the module and by importing some block necessary to
handle Html in forms.</p>
<pre class="twilight">
<span class="Keyword">module</span> <span class="Constant">Handler</span>.<span class="Constant">Blog</span>
( getBlogR
, postBlogR
, getArticleR
)
<span class="Keyword">where</span>
<span class="Keyword">import</span> <span class="Constant">Import</span>
<span class="Comment"><span class="Comment">--</span> to use Html into forms</span>
<span class="Keyword">import</span> <span class="Constant">Yesod</span>.<span class="Constant">Form</span>.<span class="Constant">Nic</span> (<span class="Constant">YesodNic</span>, nicHtmlField)
<span class="Keyword">instance</span> <span class="Constant">YesodNic</span> <span class="Constant">Yosog</span>
</pre>
2012-01-19 15:02:53 +00:00
<p><small>Remark: it is a best practice to add the YesodNic instance inside <code>Foundation.hs</code>.
I put this definition here to make things easier but you should see a warning about this orphan instance.
To put the include inside Foundation.hs is left as an exercice to the reader.</small></p>
<p><small><em>Hint: Do not forget to put <code>YesodNic</code> and <code>nicHtmlField</code> inside the exported objects of the module.</em>
</small></p>
2012-01-18 13:28:01 +00:00
<pre class="twilight">
<span class="Entity">entryForm</span> :: <span class="Constant">Form</span> <span class="Constant">Article</span>
entryForm = renderDivs $ <span class="Constant">Article</span>
&lt;$&gt; areq textField <span class="String"><span class="String">&quot;</span>Title<span class="String">&quot;</span></span> <span class="Constant">Nothing</span>
&lt;*&gt; areq nicHtmlField <span class="String"><span class="String">&quot;</span>Content<span class="String">&quot;</span></span> <span class="Constant">Nothing</span>
</pre>
<p>This function defines a form for adding a new article.
2012-01-20 11:20:07 +00:00
Don&rsquo;t pay attention to all the syntax.
2012-01-18 13:28:01 +00:00
If you are curious you can take a look at Applicative Functor.
You just have to remember <code>areq</code> is for required form input.
Its arguments being: <code>areq type label default_value</code>.</p>
<pre class="twilight">
<span class="Comment"><span class="Comment">--</span> The view showing the list of articles</span>
<span class="Entity">getBlogR</span> :: <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
getBlogR = <span class="Keyword">do</span>
<span class="Comment"><span class="Comment">--</span> Get the list of articles inside the database.</span>
articles &lt;- runDB $ selectList [] [<span class="Constant">Desc</span> <span class="Constant">ArticleTitle</span>]
<span class="Comment"><span class="Comment">--</span> We'll need the two &quot;objects&quot;: articleWidget and enctype</span>
<span class="Comment"><span class="Comment">--</span> to construct the form (see template/articles.hamlet).</span>
((_,articleWidget), enctype) &lt;- generateFormPost entryForm
defaultLayout $ <span class="Keyword">do</span>
$(widgetFile <span class="String"><span class="String">&quot;</span>articles<span class="String">&quot;</span></span>)
</pre>
<p>This handler should display a list of articles.
We get the list from the DB and we construct the form.
Just take a look at the corresponding template:</p>
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/articles.hamlet"> &#x27A5; articles.hamlet </a></div><div class="withfile">
<pre class="twilight">
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">h1</span><span class="MetaTagAll">&gt;</span></span> Articles
$if null articles
-- Show a standard message if there is no article
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>_<span class="EmbeddedSource">{MsgNoEntries}</span>
$else
-- Show the list of articles
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">ul</span><span class="MetaTagAll">&gt;</span></span>
$forall article <span class="InvalidIllegal">&lt;</span>- articles
<span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">li</span><span class="MetaTagInline">&gt;</span></span>
<span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">a</span> <span class="MetaTagInline">href</span>=@<span class="EmbeddedSource">{ArticleR (fst article)}</span> <span class="MetaTagInline">&gt;</span></span> #<span class="EmbeddedSource">{articleTitle (snd article)}</span>
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">hr</span>/<span class="MetaTagAll">&gt;</span></span>
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">form</span> <span class="MetaTagAll">method</span>=<span class="MetaTagAll">post</span> <span class="MetaTagAll">enctype</span>=#<span class="EmbeddedSource">{enctype}</span><span class="MetaTagAll">&gt;</span></span>
^<span class="EmbeddedSource">{articleWidget}</span>
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">div</span><span class="MetaTagAll">&gt;</span></span>
<span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">input</span> <span class="MetaTagInline">type</span>=<span class="MetaTagInline">submit</span> <span class="MetaTagInline">value</span>=<span class="String"><span class="String">&quot;</span>Post New Article<span class="String">&quot;</span></span><span class="MetaTagInline">&gt;</span></span>
</pre>
</div></div>
<p>You should remark we added some logic inside the template.
2012-01-20 11:20:07 +00:00
There is a test and a &ldquo;loop&rdquo;.</p>
2012-01-18 13:28:01 +00:00
<p>Another very interesting part is the creation of the form.
The <code>articleWidget</code> was created by yesod.
We have given him the right parameters
(input required or optional, labels, default values).
And now we have a protected form made for us.
But we have to create the submit button.</p>
<p>Get back to <code>Handler/Blog.hs</code>.</p>
<pre class="twilight">
<span class="Comment"><span class="Comment">--</span> we continue Handler/Blog.hs</span>
<span class="Entity">postBlogR</span> :: <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
postBlogR = <span class="Keyword">do</span>
((res,articleWidget),enctype) &lt;- runFormPost entryForm
<span class="Keyword">case</span> res <span class="Keyword">of</span>
<span class="Constant">FormSuccess</span> article -&gt; <span class="Keyword">do</span>
articleId &lt;- runDB $ insert article
2012-01-19 15:02:53 +00:00
setMessage $ toHtml $ (articleTitle article) &lt;&gt; <span class="String"><span class="String">&quot;</span> created<span class="String">&quot;</span></span>
2012-01-18 13:28:01 +00:00
redirect <span class="Constant">RedirectPermanent</span> $ <span class="Constant">ArticleR</span> articleId
_ -&gt; defaultLayout $ <span class="Keyword">do</span>
setTitle <span class="String"><span class="String">&quot;</span>Please correct your entry form<span class="String">&quot;</span></span>
$(widgetFile <span class="String"><span class="String">&quot;</span>articleAddError<span class="String">&quot;</span></span>)
</pre>
<p>This function should be used to create a new article.
We handle the form response.
If there is an error we display an error page.
For example if we left some required value blank.
If things goes right: </p>
<ul>
<li>we add the new article inside the DB (<code>runDB $ insert article</code>)</li>
<li>we add a message to be displayed (<code>setMessage $ ...</code>)</li>
<li>we are redirected to the article web page.</li>
</ul>
<p>Here is the content of the error Page:</p>
<pre class="twilight">
&lt;form method=post enctype=#{enctype}&gt;
^{articleWidget}
&lt;<span class="Entity">div</span>&gt;
&lt;input <span class="Keyword">type</span>=submit value=<span class="String"><span class="String">&quot;</span>Post New Article<span class="String">&quot;</span></span>&gt;
</pre>
<p>Finally we need to display an article:</p>
<pre class="twilight">
<span class="Entity">getArticleR</span> :: <span class="Constant">ArticleId</span> -&gt; <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
getArticleR articleId = <span class="Keyword">do</span>
article &lt;- runDB $ get404 articleId
defaultLayout $ <span class="Keyword">do</span>
setTitle $ toHtml $ articleTitle article
$(widgetFile <span class="String"><span class="String">&quot;</span>article<span class="String">&quot;</span></span>)
</pre>
<p>The <code>get404</code> function try to do a get on the DB.
If it fails it return a 404 page.
The rest should be clear.
Here is the content of <code>template/article.hamlet</code>:</p>
<div class="code"><div class="file"><a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/code/article.hamlet"> &#x27A5; article.hamlet </a></div><div class="withfile">
<pre class="twilight">
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">h1</span><span class="MetaTagAll">&gt;</span></span> #<span class="EmbeddedSource">{articleTitle article}</span>
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">article</span><span class="MetaTagAll">&gt;</span></span> #<span class="EmbeddedSource">{articleContent article}</span>
</pre>
</div></div>
2012-01-18 14:51:20 +00:00
<p>The blog system is finished.
Just for fun, you can try to create an article with the following content:</p>
2012-01-18 13:28:01 +00:00
<pre class="twilight">
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>A last try to <span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">em</span><span class="MetaTagInline">&gt;</span></span>cross script<span class="MetaTagInline"><span class="MetaTagInline">&lt;/</span><span class="MetaTagInline">em</span><span class="MetaTagInline">&gt;</span></span>
and <span class="MetaTagInline"><span class="MetaTagInline">&lt;</span><span class="MetaTagInline">em</span><span class="MetaTagInline">&gt;</span></span>SQL injection<span class="MetaTagInline"><span class="MetaTagInline">&lt;/</span><span class="MetaTagInline">em</span><span class="MetaTagInline">&gt;</span></span><span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>Here is the first try:
<span class="EmbeddedSource"> <span class="EmbeddedSource">&lt;</span><span class="MetaTagInline">script</span><span class="EmbeddedSource">&gt;</span><span class="SupportFunction">alert</span><span class="EmbeddedSource">(</span><span class="String"><span class="String">&quot;</span>You loose<span class="String">&quot;</span></span><span class="EmbeddedSource">)</span><span class="EmbeddedSource">;</span><span class="EmbeddedSource">&lt;/</span><span class="MetaTagInline">script</span><span class="EmbeddedSource">&gt;</span></span><span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>
<span class="MetaTagAll"><span class="MetaTagAll">&lt;</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span> And Here is the last <span class="MetaTagAll"><span class="MetaTagAll">&lt;/</span><span class="MetaTagAll">p</span><span class="MetaTagAll">&gt;</span></span>
&quot;); DROP TABLE ARTICLE;;
</pre>
2012-01-18 14:51:20 +00:00
<h2 id="conclusion">Conclusion</h2>
<p>This is the end of this tutorial.
I made it very minimal.
If you want to go further, you should take a look at the
recent <a href="http://yesodweb.com/blog/2012/01/blog-example">i18n blog tutorial</a>.
It will be obvious I inspired my own tutorial on it.
2012-01-20 11:20:07 +00:00
You&rsquo;ll learn in a very straightforward way how easy it is to use authorizations,
2012-01-18 14:51:20 +00:00
Time and internationalization.
The example on also add a comment system.</p>
2012-01-19 15:11:43 +00:00
2012-01-20 11:20:07 +00:00
<p><em>ps:</em> You can download the source of the blog at
<a href="http://github.com/yogsototh/yosog">github.com/yogsototh/yosog</a>.</p>
2012-01-11 20:40:22 +00:00
<hr/><div class="footnotes">
<ol>
<li id="fn:benchmarkdigression">
2012-01-20 11:20:07 +00:00
<p>One can argue these benchmark contains many problems. But the benchmarks are just here to give an order of idea. Mainly Haskell is very fast.<a href="#fnref:benchmarkdigression" rel="reference">&#8617;</a></p>
2012-01-11 20:40:22 +00:00
</li>
<li id="fn:speeddigression">
2012-01-20 11:20:07 +00:00
<p>Generally <em>high level</em> Haskell is slower than C, but <em>low level</em> Haskell is equivalent to C speed. It means that even if you can easily link C code with Haskell, this is not needed to reach the same speed. Furthermore writing a web service in C/C++ seems to be a very bad idea. You can take a look at a <a href="http://news.ycombinator.com/item?id=3449388">discussion on HN about this</a>. <a href="#fnref:speeddigression" rel="reference">&#8617;</a></p>
2012-01-11 20:40:22 +00:00
</li>
<li id="fn:nodejstroll">
2012-01-20 11:20:07 +00:00
<p>If you are curious, you can search about <a href="http://www.unlimitednovelty.com/2011/10/nodejs-has-jumped-shark.html">the Fibonacci node.js troll</a>. Without any tweaking, <a href="http://mathias-biilmann.net/posts/2011/10/is-haskell-the-cure">Haskell handled this problem perfectly</a>. I tested it myself using yesod instead of Snap.<a href="#fnref:nodejstroll" rel="reference">&#8617;</a></p>
2012-01-18 13:28:01 +00:00
</li>
<li id="fn:explainviewwidget">
2012-01-20 11:20:07 +00:00
<p>By view I mean yesod widget&rsquo;s hamlet, lucius and julius files. <a href="#fnref:explainviewwidget" rel="reference">&#8617;</a></p>
2012-01-11 20:40:22 +00:00
</li>
</ol>
2011-12-29 16:05:05 +00:00
</div>
</div>
<div id="choixrss">
<a id="rss" href="http://feeds.feedburner.com/yannespositocomfr">
s'abonner
</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">Commentaires</div>');
</script>
<div class="flush"></div>
<div class="corps" id="comment">
<h2 class="first">commentaires</h2>
<noscript>
Vous devez activer javascript pour commenter.
</noscript>
<script type="text/javascript">
var idcomments_acct = 'a307f0044511ff1b5cfca573fc0a52e7';
var idcomments_post_id = '/Scratch/fr/blog/Yesod-tutorial-for-newbies/';
var idcomments_post_url = 'http://yannesposito.com/Scratch/fr/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/fr/">Bienvenue</a></li>
<li><a href="/Scratch/fr/blog/">Blog</a></li>
<li><a href="/Scratch/fr/softwares/">Softwares</a></li>
<li><a href="/Scratch/fr/about/">À propos</a></li></ul>
</div>
<div class="flush"></div>
<hr/>
<div id="next_before_articles">
<div id="previous_articles">
articles précédents
<div class="previous_article">
<a href="/Scratch/fr/blog/SVG-and-m4-fractals/"><span class="nicer">«</span>&nbsp;Accroître le pouvoir des languages déficients.</a>
</div>
<div class="previous_article">
<a href="/Scratch/fr/blog/Yesod-excellent-ideas/"><span class="nicer">«</span>&nbsp;Les idées de yesod</a>
</div>
<div class="previous_article">
2012-01-11 20:40:22 +00:00
<a href="/Scratch/fr/blog/Higher-order-function-in-zsh/"><span class="nicer">«</span>&nbsp;Fonctions d'ordre supérieur en zsh</a>
2011-12-29 16:05:05 +00:00
</div>
</div>
<div id="next_articles">
articles suivants
</div>
<div class="flush"></div>
</div>
</div>
<div id="bottom">
<div>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.fr">Droits de reproduction ©, Yann Esposito</a>
</div>
<div id="lastmod">
2012-01-18 14:51:20 +00:00
Écrit le : 15/01/2012
2012-01-20 11:21:04 +00:00
modifié le : 20/01/2012
2011-12-29 16:05:05 +00:00
</div>
<div>
Site entièrement réalisé avec
<a href="http://www.vim.org">Vim</a>
et
<a href="http://nanoc.stoneship.org">nanoc</a>
</div>
<div>
<a href="/Scratch/fr/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/fr/blog/feed/feed.xml">[rss]</a>
</div>
</div>
<div class="clear"></div>
</div>
</body>
</html>