Adds guide for how to add Orville your application stack

This commit is contained in:
David Vollbracht 2023-12-19 14:47:35 -06:00
parent 58eee51209
commit 16c6ecad11
37 changed files with 941 additions and 21 deletions

1
.gitignore vendored
View File

@ -34,3 +34,4 @@ orville-docsite/site-builder/_site
# Ignore directories created by samples
orville-docsite/samples/getting-started/orville-getting-started
orville-docsite/samples/adding-orville-new-readert/src

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="./how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="./how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="./how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Orville - How To Add Orville to Your Application Monad (Upcoming)</title>
<title>Orville - How To Add Orville to Your Application Monad</title>
<link rel="stylesheet" href="../css/syntax.css" />
<link rel="stylesheet" href="../css/default.css" />
</head>
@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>
@ -72,10 +72,290 @@
</section>
<main role="main">
<h1>How To Add Orville to Your Application Monad (Upcoming)</h1>
<h1>How To Add Orville to Your Application Monad</h1>
<article>
<section>
<p>Coming Soon</p>
<p>This guide will show you how to add Orville to your already existing
application monad in the simplest way possible. It relies on the <code>mtl</code> package
in addition to <code>orville-postgresql</code>, so you should make sure these packages are
in your package dependencies in either your <code>.cabal</code> or <code>package.yaml</code> file.</p>
<p>The file listing below shows a simple, minimal application with its own
<code>Application</code> monad. Your application is certainly more complicated than this,
but you can think of the <code>IO</code> type below being whatever base monad or monad
transformer stack you already have in place.</p>
<div class="codeblock-label">
Main.hs (Before) : haskell
</div>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">Main</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> ( main</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> ) <span class="kw">where</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Control.Monad.IO.Class</span> <span class="kw">as</span> <span class="dt">MIO</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Application</span> a <span class="ot">=</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">Application</span> (<span class="dt">IO</span> a)</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> ( <span class="dt">Functor</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Applicative</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Monad</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> , <span class="dt">MIO.MonadIO</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="ot">runApplication ::</span> <span class="dt">Application</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>runApplication (<span class="dt">Application</span> io) <span class="ot">=</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> io</span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="ot">myApplication ::</span> <span class="dt">Application</span> ()</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>myApplication <span class="ot">=</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> MIO.liftIO <span class="op">.</span> <span class="fu">putStrLn</span> <span class="op">$</span> <span class="st">&quot;Hello Application&quot;</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> runApplication myApplication</span></code></pre></div>
<p>Were going to add a new <code>ReaderT</code> transformer to the innards of the
<code>Application</code> newtype to hold Orvilles <code>OrvilleState</code> parameter. Well have to
import the <code>Control.Monad.Reader</code> module (from the <code>mtl</code> package) and the
<code>Orville.PostgreSQL</code> module (from the <code>orville-postgresql</code>) package to
reference these types.</p>
<div class="codeblock-label">
Main.hs : diff
</div>
<div class="sourceCode" id="cb2"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">*** Main.hs (Old)</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">--- Main.hs (New)</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 5,10 ****</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> import qualified Control.Monad.IO.Class as MIO</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> newtype Application a =</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="st">! Application (IO a)</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> deriving</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 5,12 ----</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="va">+ import qualified Control.Monad.Reader as Reader</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> import qualified Control.Monad.IO.Class as MIO</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="va">+ import qualified Orville.PostgreSQL as O</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> newtype Application a =</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="va">! Application (Reader.ReaderT O.OrvilleState IO a)</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> deriving</span></code></pre></div>
<p>This new <code>ReaderT</code> context adds the internal state that Orville needs to do its
job. In order to use functions from the Orville package directly in your monad
it will need to provide instances for the three typeclasses that make up a
complete Orville monad - <code>MonadOrville</code>, <code>MonadOrvilleControl</code> and
<code>HasOrvilleState</code>. Luckily, its a simple matter of adding these three
typeclasses the the deriving list for <code>Application</code>. If youre not using
<code>GHC2021</code> youll need the <code>GeneralizedNewtypeDeriving</code> language extension, as
in the example in this guide.</p>
<div class="codeblock-label">
Main.hs : diff
</div>
<div class="sourceCode" id="cb3"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">*** Main.hs (Old)</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">--- Main.hs (New)</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 16,17 ****</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 16,20 ----</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> , MIO.MonadIO</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="va">+ , O.MonadOrville</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="va">+ , O.MonadOrvilleControl</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="va">+ , O.HasOrvilleState</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div>
<p>Somewhere in your code youll have a function similar to this examples
<code>runApplication</code> function. It needs to be updated to expect a <code>ConnectionPool</code>
argument. Well use the connection pool to build a fresh new <code>OrvilleState</code> for
our <code>Reader</code> context. Then we can use <code>runReaderT</code> resolve the <code>ReaderT</code> layer
we added to our stack and get back whatever monad type was there before we
added Orville. In this case thats just the <code>IO</code> type. This means
<code>runApplication</code> will return the same type that it returned befored we added
Orville, it just requires the <code>ConnectionPool</code> parameter to do its job now.</p>
<div class="codeblock-label">
Main.hs : diff
</div>
<div class="sourceCode" id="cb4"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">*** Main.hs (Old)</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">--- Main.hs (New)</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 21,25 ****</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="st">! runApplication :: Application a -&gt; IO a</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="st">! runApplication (Application io) =</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="st">! io</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 21,31 ----</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="va">! runApplication :: O.ConnectionPool -&gt; Application a -&gt; IO a</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="va">! runApplication pool (Application reader) =</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="va">! let</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="va">! orvilleState =</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a><span class="va">! O.newOrvilleState</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a><span class="va">! O.defaultErrorDetailLevel</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a><span class="va">! pool</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a><span class="va">! in</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a><span class="va">! Reader.runReaderT reader orvilleState</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> </span></code></pre></div>
<p>Since we added a parameter to <code>runApplication</code>, we need to go to each place
its called and pass a <code>ConnectionPool</code> parameter now. In this guide thats
just a single place in the <code>main</code> function. Well use <code>createConnectionPool</code> as
normal to make the pool we want to use.</p>
<div class="codeblock-label">
Main.hs : diff
</div>
<div class="sourceCode" id="cb5"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">*** Main.hs (Old)</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">--- Main.hs (New)</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 36,38 ****</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> main :: IO ()</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="st">! main =</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="st">! runApplication myApplication</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 36,48 ----</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> main :: IO ()</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="va">! main = do</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="va">! pool &lt;-</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="va">! O.createConnectionPool</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="va">! O.ConnectionOptions</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="va">! { O.connectionString = &quot;host=localhost user=postgres password=postgres&quot;</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="va">! , O.connectionNoticeReporting = O.DisableNoticeReporting</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a><span class="va">! , O.connectionPoolStripes = O.OneStripePerCapability</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a><span class="va">! , O.connectionPoolLingerTime = 10</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a><span class="va">! , O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="va">! }</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="va">! </span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a><span class="va">! runApplication pool myApplication</span></span></code></pre></div>
<p>Our <code>Application</code> monad is now fully equipped to run Orville operations! To
show it works, lets add a <code>messages</code> table and update the <code>myApplication</code>
logic to insert and retrieve a simple greeting message.</p>
<div class="codeblock-label">
Main.hs : diff
</div>
<div class="sourceCode" id="cb6"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">*** Main.hs (Old)</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="kw">--- Main.hs (New)</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 7,8 ****</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 7,9 ----</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> import qualified Control.Monad.IO.Class as MIO</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="va">+ import qualified Data.Text as T</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> import qualified Orville.PostgreSQL as O</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 31,35 ****</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> myApplication :: Application ()</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="st">! myApplication =</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="st">! MIO.liftIO . putStrLn $ &quot;Hello Application&quot;</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 32,44 ----</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="va">+ messagesTable :: O.TableDefinition O.NoKey T.Text T.Text</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a><span class="va">+ messagesTable =</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a><span class="va">+ O.mkTableDefinitionWithoutKey</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a><span class="va">+ &quot;messages&quot;</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a><span class="va">+ (O.marshallField id (O.unboundedTextField &quot;message&quot;))</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a><span class="va">+ </span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a> myApplication :: Application ()</span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="va">! myApplication = do</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a><span class="va">! O.insertEntity messagesTable (T.pack &quot;Hello Orville&quot;)</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a><span class="va">! messages &lt;- O.findEntitiesBy messagesTable mempty</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a><span class="va">! MIO.liftIO . print $ messages</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> </span></code></pre></div>
<p>Finally, well equip our application with automatic migrations using Orvilles
<code>Orville.PostgreSQL.Automigration</code> module so that the <code>messages</code> table will be
created before we try to access it.</p>
<div class="codeblock-label">
Main.hs : diff
</div>
<div class="sourceCode" id="cb7"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">*** Main.hs (Old)</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">--- Main.hs (New)</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 9,10 ****</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 9,11 ----</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> import qualified Orville.PostgreSQL as O</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="va">+ import qualified Orville.PostgreSQL.AutoMigration as AutoMigration</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 44,45 ****</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 45,51 ----</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a><span class="va">+ schema :: [AutoMigration.SchemaItem]</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a><span class="va">+ schema =</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a><span class="va">+ [ AutoMigration.SchemaTable messagesTable</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a><span class="va">+ ]</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a><span class="va">+ </span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> main :: IO ()</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 56,57 ****</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 62,64 ----</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a><span class="va">+ runApplication pool (AutoMigration.autoMigrateSchema AutoMigration.defaultOptions schema)</span></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a> runApplication pool myApplication</span></code></pre></div>
<p>Thats it! That completes this guide about how to add Orville to your existing
application monad. To wrap things up, heres the final listing of <code>Main.hs</code>
with all the changes we made.</p>
<div class="codeblock-label">
Main.hs (After) : haskell
</div>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE GeneralizedNewtypeDeriving #-}</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">module</span> <span class="dt">Main</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> ( main</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> ) <span class="kw">where</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Control.Monad.Reader</span> <span class="kw">as</span> <span class="dt">Reader</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Control.Monad.IO.Class</span> <span class="kw">as</span> <span class="dt">MIO</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Text</span> <span class="kw">as</span> <span class="dt">T</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Orville.PostgreSQL</span> <span class="kw">as</span> <span class="dt">O</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Orville.PostgreSQL.AutoMigration</span> <span class="kw">as</span> <span class="dt">AutoMigration</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Application</span> a <span class="ot">=</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">Application</span> (<span class="dt">Reader.ReaderT</span> <span class="dt">O.OrvilleState</span> <span class="dt">IO</span> a)</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> ( <span class="dt">Functor</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Applicative</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a> , <span class="dt">Monad</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a> , <span class="dt">MIO.MonadIO</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a> , <span class="dt">O.MonadOrville</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a> , <span class="dt">O.MonadOrvilleControl</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a> , <span class="dt">O.HasOrvilleState</span></span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a><span class="ot">runApplication ::</span> <span class="dt">O.ConnectionPool</span> <span class="ot">-&gt;</span> <span class="dt">Application</span> a <span class="ot">-&gt;</span> <span class="dt">IO</span> a</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a>runApplication pool (<span class="dt">Application</span> reader) <span class="ot">=</span></span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span></span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true" tabindex="-1"></a> orvilleState <span class="ot">=</span></span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true" tabindex="-1"></a> O.newOrvilleState</span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true" tabindex="-1"></a> O.defaultErrorDetailLevel</span>
<span id="cb8-30"><a href="#cb8-30" aria-hidden="true" tabindex="-1"></a> pool</span>
<span id="cb8-31"><a href="#cb8-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span></span>
<span id="cb8-32"><a href="#cb8-32" aria-hidden="true" tabindex="-1"></a> Reader.runReaderT reader orvilleState</span>
<span id="cb8-33"><a href="#cb8-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-34"><a href="#cb8-34" aria-hidden="true" tabindex="-1"></a><span class="ot">messagesTable ::</span> <span class="dt">O.TableDefinition</span> <span class="dt">O.NoKey</span> <span class="dt">T.Text</span> <span class="dt">T.Text</span></span>
<span id="cb8-35"><a href="#cb8-35" aria-hidden="true" tabindex="-1"></a>messagesTable <span class="ot">=</span></span>
<span id="cb8-36"><a href="#cb8-36" aria-hidden="true" tabindex="-1"></a> O.mkTableDefinitionWithoutKey</span>
<span id="cb8-37"><a href="#cb8-37" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;messages&quot;</span></span>
<span id="cb8-38"><a href="#cb8-38" aria-hidden="true" tabindex="-1"></a> (O.marshallField <span class="fu">id</span> (O.unboundedTextField <span class="st">&quot;message&quot;</span>))</span>
<span id="cb8-39"><a href="#cb8-39" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-40"><a href="#cb8-40" aria-hidden="true" tabindex="-1"></a><span class="ot">myApplication ::</span> <span class="dt">Application</span> ()</span>
<span id="cb8-41"><a href="#cb8-41" aria-hidden="true" tabindex="-1"></a>myApplication <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb8-42"><a href="#cb8-42" aria-hidden="true" tabindex="-1"></a> O.insertEntity messagesTable (T.pack <span class="st">&quot;Hello Orville&quot;</span>)</span>
<span id="cb8-43"><a href="#cb8-43" aria-hidden="true" tabindex="-1"></a> messages <span class="ot">&lt;-</span> O.findEntitiesBy messagesTable <span class="fu">mempty</span></span>
<span id="cb8-44"><a href="#cb8-44" aria-hidden="true" tabindex="-1"></a> MIO.liftIO <span class="op">.</span> <span class="fu">print</span> <span class="op">$</span> messages</span>
<span id="cb8-45"><a href="#cb8-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-46"><a href="#cb8-46" aria-hidden="true" tabindex="-1"></a><span class="ot">schema ::</span> [<span class="dt">AutoMigration.SchemaItem</span>]</span>
<span id="cb8-47"><a href="#cb8-47" aria-hidden="true" tabindex="-1"></a>schema <span class="ot">=</span></span>
<span id="cb8-48"><a href="#cb8-48" aria-hidden="true" tabindex="-1"></a> [ <span class="dt">AutoMigration.SchemaTable</span> messagesTable</span>
<span id="cb8-49"><a href="#cb8-49" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb8-50"><a href="#cb8-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-51"><a href="#cb8-51" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb8-52"><a href="#cb8-52" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb8-53"><a href="#cb8-53" aria-hidden="true" tabindex="-1"></a> pool <span class="ot">&lt;-</span></span>
<span id="cb8-54"><a href="#cb8-54" aria-hidden="true" tabindex="-1"></a> O.createConnectionPool</span>
<span id="cb8-55"><a href="#cb8-55" aria-hidden="true" tabindex="-1"></a> <span class="dt">O.ConnectionOptions</span></span>
<span id="cb8-56"><a href="#cb8-56" aria-hidden="true" tabindex="-1"></a> { O.connectionString <span class="ot">=</span> <span class="st">&quot;host=localhost user=postgres password=postgres&quot;</span></span>
<span id="cb8-57"><a href="#cb8-57" aria-hidden="true" tabindex="-1"></a> , O.connectionNoticeReporting <span class="ot">=</span> <span class="dt">O.DisableNoticeReporting</span></span>
<span id="cb8-58"><a href="#cb8-58" aria-hidden="true" tabindex="-1"></a> , O.connectionPoolStripes <span class="ot">=</span> <span class="dt">O.OneStripePerCapability</span></span>
<span id="cb8-59"><a href="#cb8-59" aria-hidden="true" tabindex="-1"></a> , O.connectionPoolLingerTime <span class="ot">=</span> <span class="dv">10</span></span>
<span id="cb8-60"><a href="#cb8-60" aria-hidden="true" tabindex="-1"></a> , O.connectionPoolMaxConnections <span class="ot">=</span> <span class="dt">O.MaxConnectionsPerStripe</span> <span class="dv">1</span></span>
<span id="cb8-61"><a href="#cb8-61" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb8-62"><a href="#cb8-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-63"><a href="#cb8-63" aria-hidden="true" tabindex="-1"></a> runApplication pool (AutoMigration.autoMigrateSchema AutoMigration.defaultOptions schema)</span>
<span id="cb8-64"><a href="#cb8-64" aria-hidden="true" tabindex="-1"></a> runApplication pool myApplication</span></code></pre></div>
</section>
</article>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="./how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="./how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="./how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -36,7 +36,7 @@
<h3>How-To Guides</h3>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad (Upcoming)</a>
<a href="../how-tos/how-to-add-orville-to-your-application-monad.html">How To Add Orville to Your Application Monad</a>
<a href="../how-tos/how-to-marshall-a-haskell-record.html">How To Marshall a Haskell Record (Upcoming)</a>

View File

@ -0,0 +1,19 @@
*** Main.hs (Old)
--- Main.hs (New)
***************
*** 5,10 ****
import qualified Control.Monad.IO.Class as MIO
newtype Application a =
! Application (IO a)
deriving
--- 5,12 ----
+ import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
+ import qualified Orville.PostgreSQL as O
newtype Application a =
! Application (Reader.ReaderT O.OrvilleState IO a)
deriving

View File

@ -0,0 +1,10 @@
*** Main.hs (Old)
--- Main.hs (New)
***************
*** 16,17 ****
--- 16,20 ----
, MIO.MonadIO
+ , O.MonadOrville
+ , O.MonadOrvilleControl
+ , O.HasOrvilleState
)

View File

@ -0,0 +1,21 @@
*** Main.hs (Old)
--- Main.hs (New)
***************
*** 21,25 ****
! runApplication :: Application a -> IO a
! runApplication (Application io) =
! io
--- 21,31 ----
! runApplication :: O.ConnectionPool -> Application a -> IO a
! runApplication pool (Application reader) =
! let
! orvilleState =
! O.newOrvilleState
! O.defaultErrorDetailLevel
! pool
! in
! Reader.runReaderT reader orvilleState

View File

@ -0,0 +1,21 @@
*** Main.hs (Old)
--- Main.hs (New)
***************
*** 36,38 ****
main :: IO ()
! main =
! runApplication myApplication
--- 36,48 ----
main :: IO ()
! main = do
! pool <-
! O.createConnectionPool
! O.ConnectionOptions
! { O.connectionString = "host=localhost user=postgres password=postgres"
! , O.connectionNoticeReporting = O.DisableNoticeReporting
! , O.connectionPoolStripes = O.OneStripePerCapability
! , O.connectionPoolLingerTime = 10
! , O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
! }
!
! runApplication pool myApplication

View File

@ -0,0 +1,29 @@
*** Main.hs (Old)
--- Main.hs (New)
***************
*** 7,8 ****
--- 7,9 ----
import qualified Control.Monad.IO.Class as MIO
+ import qualified Data.Text as T
import qualified Orville.PostgreSQL as O
***************
*** 31,35 ****
myApplication :: Application ()
! myApplication =
! MIO.liftIO . putStrLn $ "Hello Application"
--- 32,44 ----
+ messagesTable :: O.TableDefinition O.NoKey T.Text T.Text
+ messagesTable =
+ O.mkTableDefinitionWithoutKey
+ "messages"
+ (O.marshallField id (O.unboundedTextField "message"))
+
myApplication :: Application ()
! myApplication = do
! O.insertEntity messagesTable (T.pack "Hello Orville")
! messages <- O.findEntitiesBy messagesTable mempty
! MIO.liftIO . print $ messages

View File

@ -0,0 +1,24 @@
*** Main.hs (Old)
--- Main.hs (New)
***************
*** 9,10 ****
--- 9,11 ----
import qualified Orville.PostgreSQL as O
+ import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
***************
*** 44,45 ****
--- 45,51 ----
+ schema :: [AutoMigration.SchemaItem]
+ schema =
+ [ AutoMigration.SchemaTable messagesTable
+ ]
+
main :: IO ()
***************
*** 56,57 ****
--- 62,64 ----
+ runApplication pool (AutoMigration.autoMigrateSchema AutoMigration.defaultOptions schema)
runApplication pool myApplication

View File

@ -0,0 +1,33 @@
cabal-version: 2.2
name: adding-orville-new-readert
version: 0.1.0.0
-- synopsis:
-- description:
homepage: https://github.com/flipstone/adding-orville-new-readert#readme
license: BSD-3-Clause
author: Flipstone Technology Partners, Inc
maintainer: maintainers@flipstone.com
copyright:
category: sample
build-type: Simple
executable adding-orville-new-readert
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5,
mtl,
orville-postgresql,
text
ghc-options: -Wall
-Wcompat
-Widentities
-Wincomplete-record-updates
-Wincomplete-uni-patterns
-Wmissing-export-lists
-Wmissing-home-modules
-Wpartial-fields
-Wredundant-constraints

View File

@ -0,0 +1 @@
["Hello Orville"]

View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
DIFF_OPTS=(-C 1 --label 'Main.hs (Old)' --label 'Main.hs (New)')
diff "${DIFF_OPTS[@]}" snapshots/Main-1.hs snapshots/Main-2.hs > 1-add-readert.patch
diff "${DIFF_OPTS[@]}" snapshots/Main-2.hs snapshots/Main-3.hs > 2-add-orville-typeclasses.patch
diff "${DIFF_OPTS[@]}" snapshots/Main-3.hs snapshots/Main-4.hs > 3-update-runApplication.patch
diff "${DIFF_OPTS[@]}" snapshots/Main-4.hs snapshots/Main-5.hs > 4-update-main.patch
diff "${DIFF_OPTS[@]}" snapshots/Main-5.hs snapshots/Main-6.hs > 5-add-table.patch
diff "${DIFF_OPTS[@]}" snapshots/Main-6.hs snapshots/Main-7.hs > 6-add-migrations.patch

View File

@ -0,0 +1,25 @@
# SNIPPET: hidden
set -e
service postgresql start
# SNIPPET: hidden
patch --output=snapshots/Main-2.hs snapshots/Main-1.hs 1-add-readert.patch
patch --output=snapshots/Main-3.hs snapshots/Main-2.hs 2-add-orville-typeclasses.patch
patch --output=snapshots/Main-4.hs snapshots/Main-3.hs 3-update-runApplication.patch
patch --output=snapshots/Main-5.hs snapshots/Main-4.hs 4-update-main.patch
patch --output=snapshots/Main-6.hs snapshots/Main-5.hs 5-add-table.patch
patch --output=snapshots/Main-7.hs snapshots/Main-6.hs 6-add-migrations.patch
mkdir -p src
cp snapshots/Main-7.hs src/Main.hs
# SNIPPET: buildAndExecute
stack build
# SNIPPET: hidden
expected=$(cat expected-output.txt)
actual=$(stack exec adding-orville-new-readert)
if [ "$expected" = "$actual" ]; then
echo "Output matches expected"
else
echo "Expected output to be: $expected"
echo "But it was actually : $actual"
exit 1
fi;

View File

@ -0,0 +1,27 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.IO.Class as MIO
newtype Application a =
Application (IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
)
runApplication :: Application a -> IO a
runApplication (Application io) =
io
myApplication :: Application ()
myApplication =
MIO.liftIO . putStrLn $ "Hello Application"
main :: IO ()
main =
runApplication myApplication

View File

@ -0,0 +1,29 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
import qualified Orville.PostgreSQL as O
newtype Application a =
Application (Reader.ReaderT O.OrvilleState IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
)
runApplication :: Application a -> IO a
runApplication (Application io) =
io
myApplication :: Application ()
myApplication =
MIO.liftIO . putStrLn $ "Hello Application"
main :: IO ()
main =
runApplication myApplication

View File

@ -0,0 +1,32 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
import qualified Orville.PostgreSQL as O
newtype Application a =
Application (Reader.ReaderT O.OrvilleState IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
, O.MonadOrville
, O.MonadOrvilleControl
, O.HasOrvilleState
)
runApplication :: Application a -> IO a
runApplication (Application io) =
io
myApplication :: Application ()
myApplication =
MIO.liftIO . putStrLn $ "Hello Application"
main :: IO ()
main =
runApplication myApplication

View File

@ -0,0 +1,38 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
import qualified Orville.PostgreSQL as O
newtype Application a =
Application (Reader.ReaderT O.OrvilleState IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
, O.MonadOrville
, O.MonadOrvilleControl
, O.HasOrvilleState
)
runApplication :: O.ConnectionPool -> Application a -> IO a
runApplication pool (Application reader) =
let
orvilleState =
O.newOrvilleState
O.defaultErrorDetailLevel
pool
in
Reader.runReaderT reader orvilleState
myApplication :: Application ()
myApplication =
MIO.liftIO . putStrLn $ "Hello Application"
main :: IO ()
main =
runApplication myApplication

View File

@ -0,0 +1,48 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
import qualified Orville.PostgreSQL as O
newtype Application a =
Application (Reader.ReaderT O.OrvilleState IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
, O.MonadOrville
, O.MonadOrvilleControl
, O.HasOrvilleState
)
runApplication :: O.ConnectionPool -> Application a -> IO a
runApplication pool (Application reader) =
let
orvilleState =
O.newOrvilleState
O.defaultErrorDetailLevel
pool
in
Reader.runReaderT reader orvilleState
myApplication :: Application ()
myApplication =
MIO.liftIO . putStrLn $ "Hello Application"
main :: IO ()
main = do
pool <-
O.createConnectionPool
O.ConnectionOptions
{ O.connectionString = "host=localhost user=postgres password=postgres"
, O.connectionNoticeReporting = O.DisableNoticeReporting
, O.connectionPoolStripes = O.OneStripePerCapability
, O.connectionPoolLingerTime = 10
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
}
runApplication pool myApplication

View File

@ -0,0 +1,57 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
import qualified Data.Text as T
import qualified Orville.PostgreSQL as O
newtype Application a =
Application (Reader.ReaderT O.OrvilleState IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
, O.MonadOrville
, O.MonadOrvilleControl
, O.HasOrvilleState
)
runApplication :: O.ConnectionPool -> Application a -> IO a
runApplication pool (Application reader) =
let
orvilleState =
O.newOrvilleState
O.defaultErrorDetailLevel
pool
in
Reader.runReaderT reader orvilleState
messagesTable :: O.TableDefinition O.NoKey T.Text T.Text
messagesTable =
O.mkTableDefinitionWithoutKey
"messages"
(O.marshallField id (O.unboundedTextField "message"))
myApplication :: Application ()
myApplication = do
O.insertEntity messagesTable (T.pack "Hello Orville")
messages <- O.findEntitiesBy messagesTable mempty
MIO.liftIO . print $ messages
main :: IO ()
main = do
pool <-
O.createConnectionPool
O.ConnectionOptions
{ O.connectionString = "host=localhost user=postgres password=postgres"
, O.connectionNoticeReporting = O.DisableNoticeReporting
, O.connectionPoolStripes = O.OneStripePerCapability
, O.connectionPoolLingerTime = 10
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
}
runApplication pool myApplication

View File

@ -0,0 +1,64 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Main
( main
) where
import qualified Control.Monad.Reader as Reader
import qualified Control.Monad.IO.Class as MIO
import qualified Data.Text as T
import qualified Orville.PostgreSQL as O
import qualified Orville.PostgreSQL.AutoMigration as AutoMigration
newtype Application a =
Application (Reader.ReaderT O.OrvilleState IO a)
deriving
( Functor
, Applicative
, Monad
, MIO.MonadIO
, O.MonadOrville
, O.MonadOrvilleControl
, O.HasOrvilleState
)
runApplication :: O.ConnectionPool -> Application a -> IO a
runApplication pool (Application reader) =
let
orvilleState =
O.newOrvilleState
O.defaultErrorDetailLevel
pool
in
Reader.runReaderT reader orvilleState
messagesTable :: O.TableDefinition O.NoKey T.Text T.Text
messagesTable =
O.mkTableDefinitionWithoutKey
"messages"
(O.marshallField id (O.unboundedTextField "message"))
myApplication :: Application ()
myApplication = do
O.insertEntity messagesTable (T.pack "Hello Orville")
messages <- O.findEntitiesBy messagesTable mempty
MIO.liftIO . print $ messages
schema :: [AutoMigration.SchemaItem]
schema =
[ AutoMigration.SchemaTable messagesTable
]
main :: IO ()
main = do
pool <-
O.createConnectionPool
O.ConnectionOptions
{ O.connectionString = "host=localhost user=postgres password=postgres"
, O.connectionNoticeReporting = O.DisableNoticeReporting
, O.connectionPoolStripes = O.OneStripePerCapability
, O.connectionPoolLingerTime = 10
, O.connectionPoolMaxConnections = O.MaxConnectionsPerStripe 1
}
runApplication pool (AutoMigration.autoMigrateSchema AutoMigration.defaultOptions schema)
runApplication pool myApplication

View File

@ -0,0 +1,69 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-21.13
# resolver: nightly-2023-09-24
# resolver: ghc-9.6.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2023-01-01.yaml
resolver: lts-21.19
system-ghc: true
install-ghc: false
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# subdirs:
# - auto-update
# - wai
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver.
# These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example:
#
# extra-deps:
# - acme-missiles-0.3
# - git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
extra-deps:
- orville-postgresql-1.0.0.0
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of Stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=2.13"
#
# Override the architecture used by Stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by Stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

View File

@ -0,0 +1,19 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages:
- completed:
hackage: orville-postgresql-1.0.0.0@sha256:35e9b9f8bc0bc1ee1847bcb5340fa39bed320f1573099ec16ca394726a50593a,9018
pantry-tree:
sha256: b8d324f2ad94f12ac419996cc2947ee0c69c5178b2caf13dc92135118602bbd8
size: 12020
original:
hackage: orville-postgresql-1.0.0.0
snapshots:
- completed:
sha256: fb482b8e2d5d061cdda4ba1da2957c012740c893a5ee1c1b99001adae7b1fbe7
size: 640046
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/19.yaml
original: lts-21.19

View File

@ -1,6 +1,71 @@
---
title: How To Add Orville to Your Application Monad (Upcoming)
title: How To Add Orville to Your Application Monad
navOrder: 1
---
Coming Soon
This guide will show you how to add Orville to your already existing
application monad in the simplest way possible. It relies on the `mtl` package
in addition to `orville-postgresql`, so you should make sure these packages are
in your package dependencies in either your `.cabal` or `package.yaml` file.
The file listing below shows a simple, minimal application with its own
`Application` monad. Your application is certainly more complicated than this,
but you can think of the `IO` type below being whatever base monad or monad
transformer stack you already have in place.
$sample("adding-orville-new-readert/snapshots/Main-1.hs", "filename=Main.hs (Before)")$
We're going to add a new `ReaderT` transformer to the innards of the
`Application` newtype to hold Orville's `OrvilleState` parameter. We'll have to
import the `Control.Monad.Reader` module (from the `mtl` package) and the
`Orville.PostgreSQL` module (from the `orville-postgresql`) package to
reference these types.
$sample("adding-orville-new-readert/1-add-readert.patch", "filename=Main.hs")$
This new `ReaderT` context adds the internal state that Orville needs to do its
job. In order to use functions from the Orville package directly in your monad
it will need to provide instances for the three typeclasses that make up a
complete Orville monad - `MonadOrville`, `MonadOrvilleControl` and
`HasOrvilleState`. Luckily, it's a simple matter of adding these three
typeclasses the the deriving list for `Application`. If you're not using
`GHC2021` you'll need the `GeneralizedNewtypeDeriving` language extension, as
in the example in this guide.
$sample("adding-orville-new-readert/2-add-orville-typeclasses.patch", "filename=Main.hs")$
Somewhere in your code you'll have a function similar to this example's
`runApplication` function. It needs to be updated to expect a `ConnectionPool`
argument. We'll use the connection pool to build a fresh new `OrvilleState` for
our `Reader` context. Then we can use `runReaderT` resolve the `ReaderT` layer
we added to our stack and get back whatever monad type was there before we
added Orville. In this case that's just the `IO` type. This means
`runApplication` will return the same type that it returned befored we added
Orville, it just requires the `ConnectionPool` parameter to do its job now.
$sample("adding-orville-new-readert/3-update-runApplication.patch", "filename=Main.hs")$
Since we added a parameter to `runApplication`, we need to go to each place
it's called and pass a `ConnectionPool` parameter now. In this guide that's
just a single place in the `main` function. We'll use `createConnectionPool` as
normal to make the pool we want to use.
$sample("adding-orville-new-readert/4-update-main.patch", "filename=Main.hs")$
Our `Application` monad is now fully equipped to run Orville operations! To
show it works, let's add a `messages` table and update the `myApplication`
logic to insert and retrieve a simple greeting message.
$sample("adding-orville-new-readert/5-add-table.patch", "filename=Main.hs")$
Finally, we'll equip our application with automatic migrations using Orville's
`Orville.PostgreSQL.Automigration` module so that the `messages` table will be
created before we try to access it.
$sample("adding-orville-new-readert/6-add-migrations.patch", "filename=Main.hs")$
That's it! That completes this guide about how to add Orville to your existing
application monad. To wrap things up, here's the final listing of `Main.hs`
with all the changes we made.
$sample("adding-orville-new-readert/snapshots/Main-7.hs", "filename=Main.hs (After)")$