684 lines
No EOL
31 KiB
HTML
684 lines
No EOL
31 KiB
HTML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
|
|
|
|
<meta name="keywords" content="yesod, haskell, programming, web">
|
|
|
|
<link rel="shortcut icon" type="image/x-icon" href="/Scratch/img/favicon.ico" />
|
|
<link rel="stylesheet" type="text/css" href="/Scratch/assets/css/main.css" />
|
|
<link rel="stylesheet" type="text/css" href="/Scratch/css/twilight.css" />
|
|
<link rel="stylesheet" type="text/css" href="/Scratch/css/idc.css" />
|
|
<link rel="alternate" type="application/rss+xml" title="RSS" href="http://feeds.feedburner.com/yannespositocomen"/>
|
|
|
|
<link rel="alternate" lang="fr" xml:lang="fr" title="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/" />
|
|
<script type="text/javascript" src="/Scratch/js/jquery-1.3.1.min.js"></script>
|
|
<script type="text/javascript" src="/Scratch/js/jquery.cookie.js"></script>
|
|
<script type="text/javascript" src="/Scratch/js/index.js"></script>
|
|
<!--[if lt IE 9]>
|
|
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
|
|
<![endif]-->
|
|
<title>Haskell web programming</title>
|
|
</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">↓ Menu ↓</a></div>
|
|
<div id="choixlang">
|
|
<a href="/Scratch/fr/blog/Yesod-tutorial-for-newbies/" onclick="setLanguage('fr')">en Français</a>
|
|
</div>
|
|
<div class="flush"></div>
|
|
</div>
|
|
<div id="titre">
|
|
<h1>
|
|
Haskell web programming
|
|
</h1>
|
|
|
|
<h2>
|
|
A Yesod tutorial
|
|
</h2>
|
|
|
|
</div>
|
|
<div class="flush"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flush"></div>
|
|
<div id="afterheader">
|
|
<div class="corps">
|
|
<p><img alt="Neo Flying at warp speed" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/flying_neo.jpg" /></p>
|
|
|
|
|
|
<div class="intro">
|
|
|
|
|
|
<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. You shouldn’t need to know Haskell. </p>
|
|
|
|
<blockquote>
|
|
<center><span class="sc"><b>Table of content</b></span></center>
|
|
|
|
<ul id="markdown-toc">
|
|
<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>
|
|
<li><a href="#a-last-point">A last point</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#echo">Echo</a> <ul>
|
|
<li><a href="#bulletproof">Bulletproof?</a></li>
|
|
<li><a href="#cleaning-up">Cleaning up</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-a-new-template-file">Use a new template file</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#repeat">Repeat</a></li>
|
|
<li><a href="#end-of-part-1">End of Part 1</a></li>
|
|
</ul>
|
|
</blockquote>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p>Haskell is incredible and you should consider to use it to make your next web application. Why?</p>
|
|
|
|
<p><img alt="Impressive Haskell Benchmark" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/haskell-benchmark.png" /></p>
|
|
|
|
<p>My main reason to look at Haskell is its efficiency (see <a href="http://snapframework.com/blog/2010/11/17/snap-0.3-benchmarks">Snap Benchmark</a> <em class="pala">&</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 compiled and is an order of magnitude faster than interpreted languages like <a href="http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&lang=ghc&lang2=yarv">Ruby</a> and <a href="http://shootout.alioth.debian.org/u64q/benchmark.php?test=all&lang=ghc&lang2=python3">Python</a><sup id="fnref:speeddigression"><a href="#fn:speeddigression" rel="footnote">2</a></sup>.
|
|
Haskell 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>
|
|
|
|
<p>Its type system gives the feeling of using an interpreted language.
|
|
Haskell has many more great properties, one of the best being:</p>
|
|
|
|
<blockquote>
|
|
<p>“If your program compile it will be very close to what the programmer intended”.</p>
|
|
</blockquote>
|
|
|
|
<p>From the pure technical “point of vue”, Haskell seems to be the perfect web development tool.</p>
|
|
|
|
<p>Weaknesses of Haskell certainly won’t be technical but human:</p>
|
|
|
|
<ul>
|
|
<li>Hard to grasp Haskell</li>
|
|
<li>Hard to find a Haskell programmer</li>
|
|
<li>The Haskell community is smaller than the community for X</li>
|
|
<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>
|
|
</ul>
|
|
|
|
<p><img alt="Thousands of Agent Smith" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/thousands_smiths.jpg" class="left" /></p>
|
|
|
|
<p>I don’t say these are not important drawbacks.
|
|
But Haskell is certainly the best choice to create your new web application which could handle thousands of connexions in parallel.
|
|
Considering efficiency, security and quality, I believe Haskell is the best choice.
|
|
Furthermore, not only the Haskell community is excellent, but Haskell is a great language and learning it will certainly make you a better programmer.</p>
|
|
|
|
<p>Haskell is not the only choice to make.
|
|
The easiest path to create a web application is certainly to choose a web framework which has made a lot of work for us.
|
|
Actually there are three main choices:</p>
|
|
|
|
<ol>
|
|
<li><a href="http://happstack.com">Happstack</a></li>
|
|
<li><a href="http://snapframework.com">Snap</a></li>
|
|
<li><a href="http://yesodweb.com">Yesod</a></li>
|
|
</ol>
|
|
|
|
<p>I don’t think there is a real winner between these three framework.
|
|
The choice I made for yesod is highly subjective.
|
|
I had the feeling yesod make a better job at helping newcomers.
|
|
Furthermore, apparently the yesod team is the most active.
|
|
But as I said before, I might be wrong has it is a matter of feeling.</p>
|
|
|
|
<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>
|
|
|
|
<p>Why did I write this article?
|
|
The yesod documentation does an excellent job to explain you both some very minimal example and digging into the details.
|
|
But I missed an intermediate tutorial.</p>
|
|
|
|
<p>I tried to pass on the Haskell language and focus on the Yesod framework.
|
|
If you are not used to Haskell, some syntax details may feel strange.
|
|
Please, don’t try to understand the details right now.
|
|
Haskell is a very complex language and could suck all your energy if you want to dig too early.</p>
|
|
|
|
<p>During this tutorial you’ll install, initialize and configure your first yesod project.
|
|
Then a 5 minutes yesod tutorial to heat up and verify the awesomeness of yesod.
|
|
Then we clean up the 5 minutes tutorial to use the best practices.
|
|
Just after there will be a more standard real world example. A minimal blog system.
|
|
Good read.</p>
|
|
|
|
<h2 id="before-the-real-start">Before the real start</h2>
|
|
|
|
<h3 id="install">Install</h3>
|
|
|
|
<p>First you need to install <a href="http://www.haskell.org">Haskell</a>. The recommended way to do this is to download the <a href="http://www.haskell.org/platform">Haskell Platform</a>.</p>
|
|
|
|
<p>Then you need to install yesod.</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Keyword">~</span> cabal update
|
|
<span class="Keyword">~</span> cabal install yesod cabal-dev
|
|
</pre>
|
|
|
|
<p>That is all. It should take some time to
|
|
do this as cabal will download all
|
|
package and then compile them.</p>
|
|
|
|
<h3 id="initialize">Initialize</h3>
|
|
|
|
<p>Open a terminal and type:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Keyword">~</span> yesod init
|
|
</pre>
|
|
|
|
<p>Enter your name, name the project <code>yosog</code> and the name of the Foundation as <code>Yosog</code>, then choose <code>sqlite</code>.
|
|
Perfect. Now you can start the development cycle:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Keyword">~</span> cd yosog
|
|
<span class="Keyword">></span> cabal-dev install <span class="Keyword">&&</span> yesod --dev devel
|
|
</pre>
|
|
|
|
<p>This will compile the entire project. Be patient it could take some time.
|
|
Once finished a server is launched and you could visit it by clicking this link:</p>
|
|
|
|
<p><a href="http://localhost:3000"><code>http://localhost:3000</code></a></p>
|
|
|
|
<p>Congratulation! Yesod works!</p>
|
|
|
|
<blockquote>
|
|
|
|
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">&&</span> yesod --dev devel
|
|
</pre>
|
|
|
|
</blockquote>
|
|
|
|
<p>Until the end of the tutorial, use another terminal and let this one open in a corner to see what occurs.</p>
|
|
|
|
<h3 id="configure-git">Configure git</h3>
|
|
|
|
<p>This step is not mandatory for a tutorial, but I wanted to jump directly to good practice. There are many different choice of CVS, but for this tutorial I’ll use <code>git</code>.</p>
|
|
|
|
<p>Copy this <code>.gitignore</code> file into the <code>yosog</code> folder.</p>
|
|
|
|
<div class="code"><div class="file"><a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/code/.gitignore"> ➥ .gitignore </a></div><div class="withfile">
|
|
<pre class="twilight">
|
|
cabal-dev
|
|
dist
|
|
.static-cache
|
|
static/tmp
|
|
*.sqlite3
|
|
</pre>
|
|
</div></div>
|
|
|
|
<p>Then initialize your git repository:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Keyword">~</span> git init .
|
|
<span class="Keyword">~</span> git add .
|
|
<span class="Keyword">~</span> git commit -a -m <span class="String"><span class="String">"</span>Initial yesod commit<span class="String">"</span></span>
|
|
</pre>
|
|
|
|
<p>Now we are almost ready to start.</p>
|
|
|
|
<h3 id="a-last-point">A last point</h3>
|
|
|
|
<p>Up until here, we have a directory containing a bunch of files and a local web server listening the port 3000.</p>
|
|
|
|
<p>If we modify a file inside this directory, yesod should try
|
|
to recompile as fast as possible the site. </p>
|
|
|
|
<p>Instead of explaining the role of every file,
|
|
let’s focus only on the important files/directories for this tutorial:</p>
|
|
|
|
<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>
|
|
|
|
<table>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>config/routes</code></td>
|
|
<td>is where you’ll configure the map URL → Code.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>Handler/</code></td>
|
|
<td>contains the files that will contain the code called when a URL is accessed.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>templates/</code></td>
|
|
<td>contains HTML, JS and CSS templates.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>config/models</code></td>
|
|
<td>is where you’ll configure the persistent objects (database tables).</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<p>During this tutorial we’ll modify other files as well,
|
|
but we won’t explore them in detail.
|
|
Now, it is the time to start the interesting part.</p>
|
|
|
|
<h2 id="echo">Echo</h2>
|
|
|
|
<p>To verify the quality of the security of the yesod framework, let’s make a minimal echo application.</p>
|
|
|
|
<p>Our goal:</p>
|
|
|
|
<p>Make a server that when accessed <code>/echo/[some text]</code> should return a web page containing “some text” inside an <code>h1</code> bloc.</p>
|
|
|
|
<p>First, we must declare URL of the form <code>/echo/...</code> are meaningful.</p>
|
|
|
|
<p>Let’s take a look at the file <code>config/routes</code>:</p>
|
|
|
|
<pre class="twilight">
|
|
/static StaticR Static getStatic
|
|
/auth AuthR Auth getAuth
|
|
|
|
/favicon.ico FaviconR GET
|
|
/robots.txt RobotsR GET
|
|
|
|
/ RootR GET
|
|
</pre>
|
|
|
|
<p>We want to add a route of the form <code>/echo/[anything]</code> somehow and do some action with this.
|
|
We add the following:</p>
|
|
|
|
<pre>
|
|
/echo/#String EchoR GET
|
|
</pre>
|
|
|
|
<p>This line contains three elements: the <span class="sc">url</span> pattern, a handler name, an HTTP method.
|
|
I am not particularly fan of the big R in the end of handler names.
|
|
But this is the standard convention, then let’s use it.</p>
|
|
|
|
<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>
|
|
|
|
<pre>
|
|
Application.hs:31:1: Not in scope: `getEchoR'
|
|
</pre>
|
|
|
|
<p>Why? Simply because we didn’t written the code for the handler <code>EchoR</code>.
|
|
Now, let’s do this. Edit the file <code>Handler/Root.hs</code> and append this:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Entity">getEchoR</span> :: <span class="Constant">String</span> -> <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
|
|
getEchoR theText = <span class="Keyword">do</span>
|
|
defaultLayout $ <span class="Keyword">do</span>
|
|
[whamlet|<h1>#{theText}|]
|
|
</pre>
|
|
|
|
<p>Don’t worry if you find all of this a bit cryptic.
|
|
This is normal when learning a new framework.
|
|
In short it just declare a function named getEchoR with one argument (<code>theText</code>) of type String.
|
|
When this function is called, it return a “Handler RepHtml” whatever it is.
|
|
But mainly this will encapsulate our expected result inside an HTML text.</p>
|
|
|
|
<p>After saving the file, you should see yesod recompile the application.
|
|
When the compilation is finished you’ll see the message: <code>Starting devel application</code>.</p>
|
|
|
|
<p>Now you can visit: <a href="http://localhost:3000/echo/Yesod%20rocks!"><code>http://localhost:3000/echo/Yesod%20rocks!</code></a></p>
|
|
|
|
<p>TADA! It works.</p>
|
|
|
|
<h3 id="bulletproof">Bulletproof?</h3>
|
|
|
|
<p><img alt="Neo stops a myriad of bullets" src="/Scratch/img/blog/Yesod-tutorial-for-newbies/neo_bullet_proof.jpg" /></p>
|
|
|
|
<p>Let’s try to attack our website by entering a text with special characters:</p>
|
|
|
|
<p><a href="http://localhost:3000/echo/<a>I'm <script>alert("Bad!");"><code>http://localhost:3000/echo/<a>I'm <script>alert("Bad!");</code></a></p>
|
|
|
|
<p>All should work better than expected.</p>
|
|
|
|
<p>The special characters are protected for us.
|
|
If you have a malicious user, he could not hide some bad script inside his login name for example.</p>
|
|
|
|
<p>This is a direct consequence of <em>type safety</em>.
|
|
The URL string is put inside a URL type.
|
|
Then the interesting part in the URL is put inside a String type. To pass from URL type to String type some transformation are made. For example, replace all “<code>%20</code>” by space characters.
|
|
Then to show the String inside an HTML document, the string is put inside an HTML type. Some transformations occurs like replace “<code><</code>” by “<code>&lt;</code>”.
|
|
Thanks to yesod, most of tedious string transformation job is done for us.</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="String"><span class="String">"</span>http://localhost:3000/echo/some%20text<a><span class="String">"</span></span> :: URL
|
|
↓
|
|
<span class="String"><span class="String">"</span>some text<a><span class="String">"</span></span> :: String
|
|
↓
|
|
<span class="String"><span class="String">"</span>some text &lt;a&gt;<span class="String">"</span></span> :: HTML
|
|
</pre>
|
|
|
|
<p>That was the first very minimal example, and we already
|
|
verified Yesod protect us from many common errors.</p>
|
|
|
|
<p>Then not only Yesod is fast, it is also relatively secure.</p>
|
|
|
|
<h3 id="cleaning-up">Cleaning up</h3>
|
|
|
|
<p>This first example was nice, but for simplicity reason we didn’t used best practices.</p>
|
|
|
|
<p>First we will separate the handler code into different files.
|
|
After that we will use <code>Data.Text</code> instead of <code>String</code>.
|
|
Finally we’ll use a template file to better separate our view.</p>
|
|
|
|
<h3 id="separate-handlers">Separate handlers</h3>
|
|
|
|
<p>In a first time create a new file <code>Handler/Echo.hs</code> containing:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Keyword">module</span> <span class="Constant">Handler</span>.<span class="Constant">Echo</span> <span class="Keyword">where</span>
|
|
|
|
<span class="Keyword">import</span> <span class="Constant">Import</span>
|
|
|
|
<span class="Entity">getEchoR</span> :: <span class="Constant">String</span> -> <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
|
|
getEchoR theText = <span class="Keyword">do</span>
|
|
defaultLayout $ <span class="Keyword">do</span>
|
|
[whamlet|<h1>#{theText}|]
|
|
</pre>
|
|
|
|
<p>Do not forget to remove the getEchoR function inside the <code>Handler/Root.hs</code> file.</p>
|
|
|
|
<p>We must declare the file inside the cabal configuration file <code>yosog.cabal</code>. Just after <code>Handler.Root</code> add:</p>
|
|
|
|
<pre>
|
|
Handler.Echo
|
|
</pre>
|
|
|
|
<p>We must also declare the new Handler module inside <code>Application.hs</code>.
|
|
Just after the “<code>import Handler.Root</code>”, add:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Keyword">import</span> <span class="Constant">Handler</span>.<span class="Constant">Echo</span>
|
|
</pre>
|
|
|
|
<h3 id="datatext"><code>Data.Text</code></h3>
|
|
|
|
<p>Now our handler is separated in another file.</p>
|
|
|
|
<p>It is a good practice to use <code>Data.Text</code> instead of <code>String</code>.</p>
|
|
|
|
<p>To declare we will use the type <code>Data.Text</code> we modify the file <code>Foundation.hs</code>.
|
|
Add an import directive just after the last one:</p>
|
|
|
|
<pre class="twilight">
|
|
import Data.Text
|
|
</pre>
|
|
|
|
<p>And also we must modify <code>config/routes</code> and our handler accordingly. Replace <code>#String</code> by <code>#Text</code> in <code>config/routes</code>:</p>
|
|
|
|
<pre>
|
|
/echo/#Text EchoR GET
|
|
</pre>
|
|
|
|
<p>And do the same in <code>Handler/Echo.hs</code>:</p>
|
|
|
|
<div class="code"><div class="file"><a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/code/Echo.hs"> ➥ Echo.hs </a></div><div class="withfile">
|
|
<pre class="twilight">
|
|
<span class="Keyword">module</span> <span class="Constant">Handler</span>.<span class="Constant">Echo</span> <span class="Keyword">where</span>
|
|
|
|
<span class="Keyword">import</span> <span class="Constant">Import</span>
|
|
|
|
<span class="Entity">getEchoR</span> :: <span class="Constant">Text</span> -> <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
|
|
getEchoR theText = <span class="Keyword">do</span>
|
|
defaultLayout $ <span class="Keyword">do</span>
|
|
[whamlet|<h1>#{theText}|]
|
|
</pre>
|
|
</div></div>
|
|
|
|
<h3 id="use-a-new-template-file">Use a new template file</h3>
|
|
|
|
<p>The last thing to change in order to do things like in
|
|
a real project is to use another template file.</p>
|
|
|
|
<p>Just create a new file <code>template/echo.hamlet</code> containing:</p>
|
|
|
|
<div class="code"><div class="file"><a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/code/echo.hamlet"> ➥ echo.hamlet </a></div><div class="withfile">
|
|
<pre class="twilight">
|
|
<h1> #{theText}
|
|
</pre>
|
|
</div></div>
|
|
|
|
<p>and modify the handler <code>Handler/Echo.hs</code>:</p>
|
|
|
|
<pre class="twilight">
|
|
<span class="Entity">getEchoR</span> :: <span class="Constant">Text</span> -> <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
|
|
getEchoR theText = <span class="Keyword">do</span>
|
|
defaultLayout $ <span class="Keyword">do</span>
|
|
$(widgetFile <span class="String"><span class="String">"</span>echo<span class="String">"</span></span>)
|
|
</pre>
|
|
|
|
<p>At this point our code is clean.
|
|
Handler are grouped, we use <code>Data.Text</code> and our views are in templates.
|
|
It is now time to make a slightly more complex example.</p>
|
|
|
|
<h2 id="repeat">Repeat</h2>
|
|
|
|
<p>Let’s make another minimal application.
|
|
You should see a form containing a text field and a validation button.
|
|
When you click, the next page present you the content you entered in the field.</p>
|
|
|
|
<p>First, add a new route:</p>
|
|
|
|
<pre class="twilight">
|
|
/new NewR GET POST
|
|
</pre>
|
|
|
|
<p>This time the path /new will accept GET and POST requests. Add the corresponding new Handler file:</p>
|
|
|
|
<div class="code"><div class="file"><a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/code/New.hs"> ➥ New.hs </a></div><div class="withfile">
|
|
<pre class="twilight">
|
|
<span class="Keyword">module</span> <span class="Constant">Handler</span>.<span class="Constant">New</span> <span class="Keyword">where</span>
|
|
|
|
<span class="Keyword">import</span> <span class="Constant">Import</span>
|
|
|
|
<span class="Entity">getNewR</span> :: <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
|
|
getNewR = <span class="Keyword">do</span>
|
|
defaultLayout $ <span class="Keyword">do</span>
|
|
$(widgetFile <span class="String"><span class="String">"</span>new<span class="String">"</span></span>)
|
|
|
|
<span class="Entity">postNewR</span> :: <span class="Constant">Handler</span> <span class="Constant">RepHtml</span>
|
|
postNewR = <span class="Keyword">do</span>
|
|
postedText <- runInputPost $ ireq textField <span class="String"><span class="String">"</span>content<span class="String">"</span></span>
|
|
defaultLayout $ <span class="Keyword">do</span>
|
|
$(widgetFile <span class="String"><span class="String">"</span>posted<span class="String">"</span></span>)
|
|
</pre>
|
|
</div></div>
|
|
|
|
<p>Don’t forget to declare it inside <code>yosog.cabal</code> and <code>Application.hs</code>.</p>
|
|
|
|
<p>The only new thing here is the line that get the POST parameter named “content”.
|
|
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>
|
|
|
|
<p>Create the two corresponding templates:</p>
|
|
|
|
<div class="code"><div class="file"><a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/code/new.hamlet"> ➥ new.hamlet </a></div><div class="withfile">
|
|
<pre class="twilight">
|
|
<span class="MetaTagAll"><span class="MetaTagAll"><</span><span class="MetaTagAll">h1</span><span class="MetaTagAll">></span></span> Enter your text
|
|
<span class="MetaTagAll"><span class="MetaTagAll"><</span><span class="MetaTagAll">form</span> <span class="MetaTagAll">method</span>=<span class="MetaTagAll">post</span> <span class="MetaTagAll">action</span>=@<span class="EmbeddedSource">{NewR}</span><span class="MetaTagAll">></span></span>
|
|
<span class="MetaTagInline"><span class="MetaTagInline"><</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">></span></span>
|
|
<span class="MetaTagInline"><span class="MetaTagInline"><</span><span class="MetaTagInline">input</span> <span class="MetaTagInline">type</span>=<span class="MetaTagInline">submit</span><span class="MetaTagInline">></span></span>
|
|
</pre>
|
|
</div></div>
|
|
|
|
<div class="code"><div class="file"><a href="/Scratch/en/blog/Yesod-tutorial-for-newbies/code/posted.hamlet"> ➥ posted.hamlet </a></div><div class="withfile">
|
|
<pre class="twilight">
|
|
<span class="MetaTagAll"><span class="MetaTagAll"><</span><span class="MetaTagAll">h1</span><span class="MetaTagAll">></span></span>You've just posted
|
|
<span class="MetaTagAll"><span class="MetaTagAll"><</span><span class="MetaTagAll">p</span><span class="MetaTagAll">></span></span>#<span class="EmbeddedSource">{postedText}</span>
|
|
<span class="MetaTagAll"><span class="MetaTagAll"><</span><span class="MetaTagAll">hr</span><span class="MetaTagAll">></span></span>
|
|
<span class="MetaTagAll"><span class="MetaTagAll"><</span><span class="MetaTagAll">p</span><span class="MetaTagAll">></span></span><span class="MetaTagInline"><span class="MetaTagInline"><</span><span class="MetaTagInline">a</span> <span class="MetaTagInline">href</span>=@<span class="EmbeddedSource">{NewR}</span><span class="MetaTagInline">></span></span>Get back
|
|
</pre>
|
|
</div></div>
|
|
|
|
<p>And that is all.
|
|
This time, we used most good practices.
|
|
We may have used another way to generate the form
|
|
but this is beyond the scope of this tutorial.</p>
|
|
|
|
<p>Just try it by <a href="http://localhost:3000/new">clicking here</a>.</p>
|
|
|
|
<p>Hey! That was easy!</p>
|
|
|
|
<h2 id="end-of-part-1">End of Part 1</h2>
|
|
|
|
<p>This was a very minimal introduction.</p>
|
|
|
|
<p>In my next article, I will show you a closer real life system.</p>
|
|
|
|
<div style="display:hidden">
|
|
|
|
---
|
|
<div style="background-color: #800; color: #FFF; font-weight: bold; border: 2px solid #300; box-shadow: 0 0 60px #300 inset;padding-left: 2em;">
|
|
<h2 style="margin: 0 auto; text-align: center">TODO</h2>
|
|
|
|
<ul>
|
|
<li> Create a minimal blog system.
|
|
</li><li> Change template to <span class="sc">html5</span> boilerplate.
|
|
</li><li> Use Authentification.
|
|
</li>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
<hr/><div class="footnotes">
|
|
<ol>
|
|
<li id="fn:benchmarkdigression">
|
|
<p>One can argue these benchmark contains many problems. But benchmark are just here to give an order of idea. Mainly Haskell is very fast.<a href="#fnref:benchmarkdigression" rel="reference">↩</a></p>
|
|
</li>
|
|
<li id="fn:speeddigression">
|
|
<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">↩</a></p>
|
|
</li>
|
|
<li id="fn:nodejstroll">
|
|
<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">↩</a></p>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div id="choixrss">
|
|
<a id="rss" href="http://feeds.feedburner.com/yannespositocomen">
|
|
Subscribe
|
|
</a>
|
|
</div>
|
|
<script type="text/javascript">
|
|
$(document).ready(function(){
|
|
$('#comment').hide();
|
|
$('#clickcomment').click(showComments);
|
|
});
|
|
function showComments() {
|
|
$('#comment').show();
|
|
$('#clickcomment').fadeOut();
|
|
}
|
|
document.write('<div id="clickcomment">Comments</div>');
|
|
</script>
|
|
<div class="flush"></div>
|
|
<div class="corps" id="comment">
|
|
<h2 class="first">comments</h2>
|
|
<noscript>
|
|
You must enable javascript to comment.
|
|
</noscript>
|
|
|
|
<script type="text/javascript">
|
|
var idcomments_acct = 'a307f0044511ff1b5cfca573fc0a52e7';
|
|
var idcomments_post_id = '/Scratch/en/blog/Yesod-tutorial-for-newbies/';
|
|
var idcomments_post_url = 'http://yannesposito.com/Scratch/en/blog/Yesod-tutorial-for-newbies/';
|
|
</script>
|
|
<span id="IDCommentsPostTitle" style="display:none"></span>
|
|
<script type='text/javascript' src='/Scratch/js/genericCommentWrapperV2.js'></script>
|
|
|
|
</div>
|
|
|
|
<div id="entete" class="corps_spaced">
|
|
<div id="liens">
|
|
<ul><li><a href="/Scratch/en/">Home</a></li>
|
|
<li><a href="/Scratch/en/blog/">Blog</a></li>
|
|
<li><a href="/Scratch/en/softwares/">Softwares</a></li>
|
|
<li><a href="/Scratch/en/about/">About</a></li></ul>
|
|
</div>
|
|
<div class="flush"></div>
|
|
<hr/>
|
|
<div id="next_before_articles">
|
|
<div id="previous_articles">
|
|
previous entries
|
|
|
|
<div class="previous_article">
|
|
<a href="/Scratch/en/blog/SVG-and-m4-fractals/"><span class="nicer">«</span> Increase the power of deficient languages.</a>
|
|
</div>
|
|
|
|
|
|
<div class="previous_article">
|
|
<a href="/Scratch/en/blog/Yesod-excellent-ideas/"><span class="nicer">«</span> Yesod excellent ideas</a>
|
|
</div>
|
|
|
|
|
|
<div class="previous_article">
|
|
<a href="/Scratch/en/blog/Higher-order-function-in-zsh/"><span class="nicer">«</span> Higher order function in zsh</a>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
<div id="next_articles">
|
|
next entries
|
|
|
|
|
|
|
|
|
|
</div>
|
|
<div class="flush"></div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div id="bottom">
|
|
<div>
|
|
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Copyright ©, Yann Esposito</a>
|
|
</div>
|
|
<div id="lastmod">
|
|
Created: 12/28/2011
|
|
Modified: 01/02/2012
|
|
</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> |