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

987 lines
52 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/yannespositocomen"/>
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>Haskell web programming</title>
2011-12-29 16:05:05 +00:00
</head>
<body lang="en" class="article">
<script type="text/javascript">// <![CDATA[
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">&darr; Menu &darr;</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>
2012-01-11 20:40:22 +00:00
Haskell web programming
2011-12-29 16:05:05 +00:00
</h1>
2012-01-11 20:40:22 +00:00
<h2>
A Yesod tutorial
</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="Too long; didn't read">tl;dr</abbr>: </span> A simple yesod tutorial.
Yesod is an Haskell web framework.
2012-01-20 21:36:36 +00:00
You shouldn&rsquo;t need to know Haskell. </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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +00:00
If you are used to Haskell and Yesod, this tutorial won&rsquo;t learn you much.
2012-01-20 11:20:07 +00:00
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 21:36:36 +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 21:36:36 +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/en/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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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>.
2012-01-20 15:35:15 +00:00
Add a file named <code>default-layout.lucius</code> inside the <code>templates/</code> directory containing:</p>
2012-01-18 13:28:01 +00:00
<div class="code"><div class="file"><a href="/Scratch/en/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 21:36:36 +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 21:36:36 +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/en/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.
2012-01-20 15:35:15 +00:00
Create the new file <code>templates/echo.hamlet</code> containing:</p>
2011-12-30 16:14:57 +00:00
<div class="code"><div class="file"><a href="/Scratch/en/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 21:36:36 +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 21:36:36 +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 21:36:36 +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/en/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 21:36:36 +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 21:36:36 +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/en/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/en/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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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>
2012-01-20 15:35:15 +00:00
<span class="Comment"><span class="Comment">--</span> to construct the form (see templates/articles.hamlet).</span>
2012-01-18 13:28:01 +00:00
((_,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/en/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 21:36:36 +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:01:15 +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.
2012-01-20 15:35:15 +00:00
Here is the content of <code>templates/article.hamlet</code>:</p>
2012-01-18 13:28:01 +00:00
<div class="code"><div class="file"><a href="/Scratch/en/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.
2012-01-20 21:36:36 +00:00
I made it very minimal.</p>
<p>If you already know Haskell and you want to go further,
you should take a look at the
2012-01-18 14:51:20 +00:00
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 21:36:36 +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 21:36:36 +00:00
<p>If, on the other hand you don&rsquo;t know Haskell.
Then you shouldn&rsquo;t jump directly to web programming.
Haskell is a very complex and unusual language.
My advice to go as fast as possible in using Haskell for web programming is:</p>
<ol>
<li>Start by <a href="http://tryhaskell.org">try Haskell in your browser</a></li>
<li>Then read the excellent <a href="http://learnyouahaskell.com">Learn you a Haskell for Great Good</a></li>
<li>If you have difficulties in understanding concepts like monads, you should really read <a href="http://homepages.inf.ed.ac.uk/wadler/topics/monads.html">these articles</a>. For me they were enlightening.</li>
<li>If you feel confident, you should be able to follows the <a href="http://yesodweb.com/book">yesod book</a> and if you find difficult to follows the yesod book, you should read <a href="book.realworldhaskell.org">real world Haskell</a> first (it is a must read).</li>
</ol>
<p>Also, note that:</p>
<ul>
<li><a href="http://haskell.org">haskell.org</a> is full of excellent resources.</li>
<li><a href="http://www.haskell.org/hoogle/">hoogle</a> will be very useful</li>
<li>Use <a href="http://community.haskell.org/~ndm/hlint/">hlint</a> as soon as possible to get good habits.</li>
</ul>
<p>As you should see, if you don&rsquo;t already know Haskell,
the path is long but I guaranty you it will be very rewarding!</p>
<p><em>ps:</em> You can download the source of this yesod blog tutorial at
2012-01-20 11:20:07 +00:00
<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 21:36:36 +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 21:36:36 +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 21:36:36 +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 21:36:36 +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/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>&nbsp;Increase the power of deficient languages.</a>
</div>
<div class="previous_article">
<a href="/Scratch/en/blog/Yesod-excellent-ideas/"><span class="nicer">«</span>&nbsp;Yesod excellent ideas</a>
</div>
<div class="previous_article">
2012-01-11 20:40:22 +00:00
<a href="/Scratch/en/blog/Higher-order-function-in-zsh/"><span class="nicer">«</span>&nbsp;Higher order function in zsh</a>
2011-12-29 16:05:05 +00:00
</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">
2012-01-18 14:51:20 +00:00
Created: 01/15/2012
2012-01-20 11:21:04 +00:00
Modified: 01/20/2012
2011-12-29 16:05:05 +00:00
</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>