Skip to content

Commit

Permalink
Site updated at 2012-01-02 08:16:32 UTC
Browse files Browse the repository at this point in the history
  • Loading branch information
norm2782 committed Jan 2, 2012
1 parent 5601418 commit fd47896
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 112 deletions.
80 changes: 63 additions & 17 deletions atom.xml
Expand Up @@ -4,7 +4,7 @@
<title><![CDATA[Norm 2782]]></title>
<link href="http://norm2782.github.com/atom.xml" rel="self"/>
<link href="http://norm2782.github.com/"/>
<updated>2012-01-01T23:42:22+01:00</updated>
<updated>2012-01-02T09:16:28+01:00</updated>
<id>http://norm2782.github.com/</id>
<author>
<name><![CDATA[Jurriën Stutterheim]]></name>
Expand Down Expand Up @@ -91,7 +91,8 @@ first some imports:</p>
<p>Since we are using Snap 0.7 at the moment of writing, we start by defining
out snaplet state type, generating some lenses using Template Haskell and
defining a handy type synonym for our handlers. We also want to use Heist,
so we need to define a <code>HasHeist</code> instance for our <code>App</code> type as well.</p>
so we need to define a <code>HasHeist</code> instance for our <code>App</code> type as well.
And, since this is a snaplet, we need to define our snaplet initialiser.</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
Expand All @@ -104,6 +105,14 @@ so we need to define a <code>HasHeist</code> instance for our <code>App</code> t
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">data</span> <span class="kt">App</span>
</span><span class='line'> <span class="ow">=</span> <span class="kt">App</span>
</span><span class='line'> <span class="p">{</span> <span class="n">_heist</span> <span class="ow">::</span> <span class="kt">Snaplet</span> <span class="p">(</span><span class="kt">Heist</span> <span class="kt">App</span><span class="p">)</span>
Expand All @@ -115,6 +124,14 @@ so we need to define a <code>HasHeist</code> instance for our <code>App</code> t
</span><span class='line'>
</span><span class='line'><span class="kr">instance</span> <span class="kt">HasHeist</span> <span class="kt">App</span> <span class="kr">where</span>
</span><span class='line'> <span class="n">heistLens</span> <span class="ow">=</span> <span class="n">subSnaplet</span> <span class="n">heist</span>
</span><span class='line'>
</span><span class='line'><span class="nf">app</span> <span class="ow">::</span> <span class="kt">SnapletInit</span> <span class="kt">App</span> <span class="kt">App</span>
</span><span class='line'><span class="nf">app</span> <span class="ow">=</span> <span class="n">makeSnaplet</span> <span class="s">&quot;hdf&quot;</span>
</span><span class='line'> <span class="s">&quot;An example of digestive-functors and Heist playing nicely together.&quot;</span>
</span><span class='line'> <span class="kt">Nothing</span> <span class="o">$</span> <span class="kr">do</span>
</span><span class='line'> <span class="n">h</span> <span class="ow">&lt;-</span> <span class="n">nestSnaplet</span> <span class="s">&quot;heist&quot;</span> <span class="n">heist</span> <span class="o">$</span> <span class="n">heistInit</span> <span class="s">&quot;resources/templates&quot;</span>
</span><span class='line'> <span class="n">addRoutes</span> <span class="p">[</span> <span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">,</span> <span class="n">formHandler</span><span class="p">)</span> <span class="p">]</span>
</span><span class='line'> <span class="n">return</span> <span class="o">$</span> <span class="kt">App</span> <span class="n">h</span>
</span></code></pre></td></tr></table></div></figure>
Expand Down Expand Up @@ -190,22 +207,31 @@ comes from the email-validate library.</p>
</span></code></pre></td></tr></table></div></figure>
<p>So far we have not seen anything new yet.</p>
<p>Up to this point we have not seen anything new yet, so lets start with something a bit
more interesting. For most of my Snap apps I use the following function to render a form:</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="nf">showForm</span> <span class="ow">::</span> <span class="kt">AttributeValue</span> <span class="ow">-&gt;</span> <span class="kt">FormHtml</span> <span class="p">(</span><span class="kt">HtmlM</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">Html</span>
</span><span class='line'><span class="nf">showForm</span> <span class="n">act</span> <span class="n">frm</span> <span class="ow">=</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="nf">showForm</span> <span class="ow">::</span> <span class="kt">AttributeValue</span> <span class="ow">-&gt;</span> <span class="kt">AttributeValue</span> <span class="ow">-&gt;</span> <span class="kt">FormHtml</span> <span class="p">(</span><span class="kt">HtmlM</span> <span class="n">a</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="kt">Html</span>
</span><span class='line'><span class="nf">showForm</span> <span class="n">act</span> <span class="n">mth</span> <span class="n">frm</span> <span class="ow">=</span>
</span><span class='line'> <span class="kr">let</span> <span class="p">(</span><span class="n">formHtml&#39;</span><span class="p">,</span> <span class="n">enctype</span><span class="p">)</span> <span class="ow">=</span> <span class="n">renderFormHtml</span> <span class="n">frm</span>
</span><span class='line'> <span class="kr">in</span> <span class="kt">H</span><span class="o">.</span><span class="n">form</span> <span class="o">!</span> <span class="kt">A</span><span class="o">.</span><span class="n">enctype</span> <span class="p">(</span><span class="kt">H</span><span class="o">.</span><span class="n">toValue</span> <span class="o">$</span> <span class="n">show</span> <span class="n">enctype</span><span class="p">)</span> <span class="o">!</span> <span class="kt">A</span><span class="o">.</span><span class="n">method</span> <span class="s">&quot;post&quot;</span>
</span><span class='line'> <span class="kr">in</span> <span class="kt">H</span><span class="o">.</span><span class="n">form</span> <span class="o">!</span> <span class="kt">A</span><span class="o">.</span><span class="n">enctype</span> <span class="p">(</span><span class="kt">H</span><span class="o">.</span><span class="n">toValue</span> <span class="o">$</span> <span class="n">show</span> <span class="n">enctype</span><span class="p">)</span> <span class="o">!</span> <span class="kt">A</span><span class="o">.</span><span class="n">method</span> <span class="n">mth</span>
</span><span class='line'> <span class="o">!</span> <span class="kt">A</span><span class="o">.</span><span class="n">action</span> <span class="n">act</span> <span class="o">$</span> <span class="n">formHtml&#39;</span> <span class="o">&gt;&gt;</span> <span class="n">return</span> <span class="nb">()</span>
</span></code></pre></td></tr></table></div></figure>
<p>It takes an <code>AttributeValue</code> containing the target of the form, an <code>AttributeValue</code> containing
the HTTP request method and a form as produced by the <code>eitherSnapForm</code> function we will see
below, resulting in a rendered form of type <code>Html</code>.</p>
<p>Now for the request handler, which is where most of the action will take place. We want to
make our lives easy, so we call in the help of the digestive-functors-snap library, which
provides the <code>eitherSnapForm</code> function. This function can be applied to a digestive-functors
form and a form name, after which it will use the Snap API to parse the request. Before
continueing, lets have a look at some code:</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
Expand All @@ -220,13 +246,29 @@ comes from the email-validate library.</p>
</span><span class='line'> <span class="n">res</span> <span class="ow">&lt;-</span> <span class="n">eitherSnapForm</span> <span class="n">loginForm</span> <span class="s">&quot;login-form&quot;</span>
</span><span class='line'> <span class="kr">case</span> <span class="n">res</span> <span class="kr">of</span>
</span><span class='line'> <span class="kt">Left</span> <span class="n">form</span> <span class="ow">-&gt;</span> <span class="kr">do</span>
</span><span class='line'> <span class="kr">let</span> <span class="n">nodes</span> <span class="ow">=</span> <span class="n">renderHtmlNodes</span> <span class="o">$</span> <span class="n">showForm</span> <span class="s">&quot;/&quot;</span> <span class="n">form</span>
</span><span class='line'> <span class="n">heistLocal</span> <span class="p">(</span><span class="n">bindSplice</span> <span class="s">&quot;form&quot;</span> <span class="p">(</span><span class="n">return</span> <span class="n">nodes</span><span class="p">))</span> <span class="o">$</span> <span class="n">render</span> <span class="s">&quot;form&quot;</span>
</span><span class='line'> <span class="kr">let</span> <span class="n">nodes</span> <span class="ow">=</span> <span class="n">renderHtmlNodes</span> <span class="o">$</span> <span class="n">showForm</span> <span class="s">&quot;/&quot;</span> <span class="s">&quot;post&quot;</span> <span class="n">form</span>
</span><span class='line'> <span class="n">heistLocal</span> <span class="p">(</span><span class="n">bindSplice</span> <span class="s">&quot;formElm&quot;</span> <span class="p">(</span><span class="n">return</span> <span class="n">nodes</span><span class="p">))</span> <span class="o">$</span> <span class="n">render</span> <span class="s">&quot;formTpl&quot;</span>
</span><span class='line'> <span class="kt">Right</span> <span class="p">(</span><span class="kt">LoginData</span> <span class="n">e</span> <span class="n">p</span> <span class="n">r</span><span class="p">)</span> <span class="ow">-&gt;</span> <span class="n">writeBS</span> <span class="s">&quot;Success!&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>Finally we define our initialiser, which sets up Heist and our routes.</p>
<p>The result of <code>eitherSnapForm</code> is an <code>Either</code> value. When the form has not been submitted yet,
or if a submitted form failed validation, the result will be a <code>Left</code> constructor containing
a form of type <code>FormHtml (HtmlM a)</code>. When a form has been submitted and has succesfully passed
validation, we will get a <code>Right</code> value containing the constructor applied in our form (in this
case the <code>LoginData</code> constructor).</p>
<p>Rendering the form is done when we get a <code>Left</code> result. As it turns out, it is almost trivially
easy to render the form in Heist. To bind the form as a Heist splice, we first need to render it
to an <code>Html</code> value using our <code>showForm</code> function. Since Heist cannot work with values of type
<code>Html</code>, we have to convert the <code>Html</code> to something Heist does understand. Luckily, the xmlhtml
library provides us with a function that does just that: <code>renderHtmlNodes :: Html -&gt; [Node]</code>.
Heist loves a list of <code>Node</code>s, so all we need to do is <code>return</code> it to the <code>Splice</code> context so
we can bind it as a splice to our template.</p>
<p>The final piece of the puzzle is the template in which the form needs to be rendered. As you can
see, rendering the form&#8211;including potential validation error messages&#8211;is done by adding nothing
but a single element to the template.</p>
<figure class='code'> <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
Expand All @@ -235,17 +277,21 @@ comes from the email-validate library.</p>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="nf">app</span> <span class="ow">::</span> <span class="kt">SnapletInit</span> <span class="kt">App</span> <span class="kt">App</span>
</span><span class='line'><span class="nf">app</span> <span class="ow">=</span> <span class="n">makeSnaplet</span> <span class="s">&quot;hdf&quot;</span>
</span><span class='line'> <span class="s">&quot;An example of digestive-functors and Heist playing nicely together.&quot;</span>
</span><span class='line'> <span class="kt">Nothing</span> <span class="o">$</span> <span class="kr">do</span>
</span><span class='line'> <span class="n">h</span> <span class="ow">&lt;-</span> <span class="n">nestSnaplet</span> <span class="s">&quot;heist&quot;</span> <span class="n">heist</span> <span class="o">$</span> <span class="n">heistInit</span> <span class="s">&quot;resources/templates&quot;</span>
</span><span class='line'> <span class="n">addRoutes</span> <span class="p">[</span> <span class="p">(</span><span class="s">&quot;/&quot;</span><span class="p">,</span> <span class="n">formHandler</span><span class="p">)</span> <span class="p">]</span>
</span><span class='line'> <span class="n">return</span> <span class="o">$</span> <span class="kt">App</span> <span class="n">h</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="nt">&lt;html&gt;</span>
</span><span class='line'> <span class="nt">&lt;head&gt;</span>
</span><span class='line'> <span class="nt">&lt;title&gt;</span>Heist and digestive-functors playing nice<span class="nt">&lt;/title&gt;</span>
</span><span class='line'> <span class="nt">&lt;/head&gt;</span>
</span><span class='line'> <span class="nt">&lt;body&gt;</span>
</span><span class='line'> <span class="nt">&lt;form</span> <span class="nt">/&gt;</span>
</span><span class='line'> <span class="nt">&lt;/body&gt;</span>
</span><span class='line'><span class="nt">&lt;/html&gt;</span>
</span></code></pre></td></tr></table></div></figure>
<p>With this, we have seen how to use digestive-functors and Heist together in a win-win scenario.
On the one hand you mostly maintain your separation of concerns by using Heist for most of your
HTML output, while on the other hand you can enjoy the great digestive-functors library as-is.</p>
]]></content>
</entry>

Expand Down

0 comments on commit fd47896

Please sign in to comment.