<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<title> - Crystal</title>
	<link href="https://www.fullstackstanley.com/tags/crystal/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="https://www.fullstackstanley.com"/>
	<generator uri="https://www.getzola.org/">Zola</generator>
	<updated>2020-05-20T00:00:00+00:00</updated>
	<id>https://www.fullstackstanley.com/tags/crystal/atom.xml</id>
	<entry xml:lang="en">
		<title>How to deploy a Crystal Lucky application with Dokku on Ubuntu 18.04</title>
		<published>2020-05-20T00:00:00+00:00</published>
		<updated>2020-05-20T00:00:00+00:00</updated>
		<link href="https://www.fullstackstanley.com/articles/how-to-deploy-a-crystal-lucky-application-with-dokku-on-ubuntu-18-04/" type="text/html"/>
		<id>https://www.fullstackstanley.com/articles/how-to-deploy-a-crystal-lucky-application-with-dokku-on-ubuntu-18-04/</id>
		<content type="html">&lt;p&gt;This tutorial will show how to set up an app written in the Crystal web framework &lt;a href=&quot;https:&#x2F;&#x2F;luckyframework.org&quot;&gt;Lucky&lt;&#x2F;a&gt; with Dokku on Ubuntu 18.04. &lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;You can run this setup on most low-end boxes, e.g. a $5 Digital Ocean droplet, or in my case, a $2.50 &lt;a href=&quot;https:&#x2F;&#x2F;www.hetzner.de&#x2F;cloud&quot;&gt;Hetzner Cloud Server&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;By the end of the tutorial you&#x27;ll know how to deploy your own Lucky apps and be able to manage them easily all on one server.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-is-lucky&quot;&gt;What is Lucky?&lt;&#x2F;h3&gt;
&lt;p&gt;Lucky is a web framework written in &lt;a href=&quot;https:&#x2F;&#x2F;crystal-lang.org&#x2F;&quot;&gt;Crystal&lt;&#x2F;a&gt;. Crystal is a programming language with syntax inspired by Ruby but with speeds similar to that of C.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-is-dokku&quot;&gt;What is Dokku?&lt;&#x2F;h3&gt;
&lt;p&gt;Dokku is a small PaaS service powered by Docker that you can run on your own servers. Think of it as a self-hosted command-line &lt;a href=&quot;https:&#x2F;&#x2F;www.heroku.com&#x2F;&quot;&gt;Heroku&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prequisites&quot;&gt;Prequisites&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Crystal 0.34.0 installed locally&lt;&#x2F;li&gt;
&lt;li&gt;A fresh Lucky application installed and running locally.&lt;&#x2F;li&gt;
&lt;li&gt;Basic knowledge of Git.&lt;&#x2F;li&gt;
&lt;li&gt;A server with &lt;a href=&quot;http:&#x2F;&#x2F;dokku.viewdocs.io&#x2F;dokku&#x2F;getting-started&#x2F;installation&#x2F;#installing-the-latest-stable-version&quot;&gt;Dokku Installed&lt;&#x2F;a&gt; using Ubuntu 18.04.&lt;&#x2F;li&gt;
&lt;li&gt;A domain or subdomain to point to the server.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You&#x27;ll need a Lucky app to deploy. If you don&#x27;t have one then no problem! A fresh Lucky app comes with several goodies out of the box, a home page and an authentication system linked to a database. This is more than enough to work with. See &lt;a href=&quot;https:&#x2F;&#x2F;luckyframework.org&#x2F;guides&#x2F;getting-started&#x2F;installing&quot;&gt;Getting Started&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;luckyframework.org&#x2F;guides&#x2F;getting-started&#x2F;starting-project&quot;&gt;Starting a Lucky Project&lt;&#x2F;a&gt;. Make sure you have the project commited to git and ready for the first deployment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-1-setting-up-the-dokku-configuration&quot;&gt;Step 1 - Setting up the Dokku configuration&lt;&#x2F;h2&gt;
&lt;p&gt;SSH into your server to get it ready for deployment.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;ssh root@ip
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the following commands replace &lt;code&gt;app.example.com&lt;&#x2F;code&gt; with your domain or subdomain. Replace &lt;code&gt;exampledb&lt;&#x2F;code&gt; with your preferred database name.&lt;&#x2F;p&gt;
&lt;p&gt;This will create the initial app container in Dokku, create the database, and link them both together. The last three lines set environment variables for the Lucky app. The first will tell Lucky to run the app in &lt;code&gt;production&lt;&#x2F;code&gt; mode, the second will tell Lucky what the app URL is, and the third will tell the app to run on port 5000 which Dokku will connect with Nginx to expose to the world.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;dokku apps:create app.example.com
&lt;&#x2F;span&gt;&lt;span&gt;dokku postgres:create exampledb
&lt;&#x2F;span&gt;&lt;span&gt;dokku postgres:link exampledb app.example.com
&lt;&#x2F;span&gt;&lt;span&gt;dokku config:set app.example.com LUCKY_ENV=production
&lt;&#x2F;span&gt;&lt;span&gt;dokku config:set app.example.com APP_DOMAIN=app.example.com
&lt;&#x2F;span&gt;&lt;span&gt;dokku config:set app.example.com PORT=5000
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a few more configuration details that need to be set. First, you&#x27;ll need to tell Lucky the database details that are set in Dokku. Run the below command to get the database URL.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;dokku postgres:info exampledb
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Set the &lt;code&gt;DATABASE_URL&lt;&#x2F;code&gt; environment variable from the above output.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;dokku config:set app.example.com DATABASE_URL=postgres:&#x2F;&#x2F;...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, Lucky needs a secret key environment variable. In your app directory on your local machine run the following command which will generate a secure key.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;lucky gen.secret_key
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, back on your remote Dokku server add it to the &lt;code&gt;SECRET_KEY_BASE&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;dokku config:set app.example.com SECRET_KEY_BASE=...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;re planning to send emails through the app you&#x27;ll also need to set &lt;code&gt;SEND_GRID_KEY&lt;&#x2F;code&gt; key, otherwise, change &lt;code&gt;config&#x2F;email.cr&lt;&#x2F;code&gt; to use &lt;code&gt;Carbon::DevAdapter.new&lt;&#x2F;code&gt; in production (and make sure to commit!)&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;dokku config:set app.example.com SEND_GRID_KEY=...
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Your server is almost ready for deployment. If you&#x27;re using a server with 2GB of RAM or less, you&#x27;ll need to set up a Swap. Without it the deployment process will most likely run into memory issues. &lt;a href=&quot;https:&#x2F;&#x2F;www.digitalocean.com&#x2F;community&#x2F;tutorials&#x2F;how-to-add-swap-space-on-ubuntu-18-04&quot;&gt;Checkout this tutorial for setting up a Swap&lt;&#x2F;a&gt;. A 1G Swap should be more than enough.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-ready-the-app-for-deployment&quot;&gt;Step 2 - Ready the app for deployment&lt;&#x2F;h2&gt;
&lt;p&gt;Back in your local app add a new file called &lt;code&gt;.buildpacks&lt;&#x2F;code&gt; with the following contents&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;https:&#x2F;&#x2F;github.com&#x2F;heroku&#x2F;heroku-buildpack-nodejs
&lt;&#x2F;span&gt;&lt;span&gt;https:&#x2F;&#x2F;github.com&#x2F;luckyframework&#x2F;heroku-buildpack-crystal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;step-3-deploy&quot;&gt;Step - 3 Deploy&lt;&#x2F;h2&gt;
&lt;p&gt;Make a new commit. Finally you&#x27;re ready to deploy. Run the following commands locally. The first will add your server as a remote origin for your git repo. The second will push the code to the server.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;git remote add dokku dokku@ip:app.example.com
&lt;&#x2F;span&gt;&lt;span&gt;git push dokku master 
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;step-4-set-up-ssl&quot;&gt;Step 4 - Set up SSL&lt;&#x2F;h2&gt;
&lt;p&gt;Dokku lets you easily set up a Lets Encrypt SSL certificate. Run the following commands to do so&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dokku&lt;&#x2F;span&gt;&lt;span&gt; config:set&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --no-restart&lt;&#x2F;span&gt;&lt;span&gt; app.example.com DOKKU_LETSENCRYPT_EMAIL=your.email@example.com
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dokku&lt;&#x2F;span&gt;&lt;span&gt; letsencrypt app.example.com
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Adding RSS Feeds to a Lucky app</title>
		<published>2019-06-13T00:00:00+00:00</published>
		<updated>2019-06-13T00:00:00+00:00</updated>
		<link href="https://www.fullstackstanley.com/articles/adding-rss-feeds-to-a-lucky-app/" type="text/html"/>
		<id>https://www.fullstackstanley.com/articles/adding-rss-feeds-to-a-lucky-app/</id>
		<content type="html">&lt;p&gt;Just a quick post for anyone looking to implement RSS Feeds in Crystal Lucky Framework. This post works with Lucky 0.14.1 but it should work with 0.15 as well.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;paulcsmith&quot;&gt;@paulcsmith&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jeremywoertink&quot;&gt;@jeremywoertink&lt;&#x2F;a&gt; for helping me work this out in Gitter.&lt;&#x2F;p&gt;
&lt;p&gt;First, create an &lt;code&gt;Action&lt;&#x2F;code&gt; that inherits from &lt;code&gt;Lucky::Action&lt;&#x2F;code&gt;.  We’ll add a method called &lt;code&gt;xml&lt;&#x2F;code&gt; which can be called in each of your actions, passing in the data for the feed. The &lt;code&gt;xml&lt;&#x2F;code&gt; method will then create the xml string with Crystal’s built in XML Builder and iterate over the data that you pass in.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# src&#x2F;actions&#x2F;xml_action.cr
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;abstract class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;XMLAction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; Lucky::Action
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;title
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Website RSS Feed&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;description
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Updates for Website&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;link
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;websiteurl.dev&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span&gt;(articles : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticleQuery&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        string = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;XML&lt;&#x2F;span&gt;&lt;span&gt;.build(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;indent: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;  &amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;encoding: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;UTF-8&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;            xml.element(
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rss&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;version: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:dc&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;purl.org&#x2F;dc&#x2F;elements&#x2F;1.1&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;purl.org&#x2F;rss&#x2F;1.0&#x2F;modules&#x2F;content&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:atom&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;2005&#x2F;Atom&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:media&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;search.yahoo.com&#x2F;mrss&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                ) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;channel&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata title }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata description }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text link }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;generator&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Lucky Framework&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;lastBuildDate&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;.utc_now.to_s }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;atom:link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { 
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;request.path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rel&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;application&#x2F;rss+xml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ttl&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                    articles.each &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;                            # title, description, link, category, dc:creator, pubDate, content:encoded
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata article.title }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; article.meta_description
&lt;&#x2F;span&gt;&lt;span&gt;                                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata article.meta_description.not_nil! }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;articles&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article.slug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }             xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;dc:creator&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Author Name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;pubDate&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text article.created_at.to_s }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; article.og_image
&lt;&#x2F;span&gt;&lt;span&gt;                                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;media:content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                                    xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article.og_image&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                                    xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;medium&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; article.content
&lt;&#x2F;span&gt;&lt;span&gt;                                content = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Markdown&lt;&#x2F;span&gt;&lt;span&gt;.to_html(article.content.not_nil!)
&lt;&#x2F;span&gt;&lt;span&gt;                                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content:encoded&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata content }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Lucky&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;TextResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content_type: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&#x2F;xml; charset=utf-8&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;body:&lt;&#x2F;span&gt;&lt;span&gt; string &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Although this abstract class looks quite large it’s not really doing much except generating the XML to output to the browser. By doing this it will greatly simplify our action classes.&lt;&#x2F;p&gt;
&lt;p&gt;In the example we’re passing in an &lt;code&gt;ArticleQuery&lt;&#x2F;code&gt; which you will need to implement in your own app. For reference, mine has a scope for &lt;code&gt;published&lt;&#x2F;code&gt; so I can keep unpublished articles from being viewed.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticleQuery &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; Article::BaseQuery
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;published
&lt;&#x2F;span&gt;&lt;span&gt;    published_at.lte(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;.now)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;Article&lt;&#x2F;code&gt; model has the following attributes: &lt;code&gt;title&lt;&#x2F;code&gt;, &lt;code&gt;meta_description&lt;&#x2F;code&gt; (optional), &lt;code&gt;slug&lt;&#x2F;code&gt;, &lt;code&gt;content&lt;&#x2F;code&gt; (optional), &lt;code&gt;og_image&lt;&#x2F;code&gt; (optional). You will need to update this for your own purposes as well.&lt;&#x2F;p&gt;
&lt;p&gt;At the end of the &lt;code&gt;XMLAction#xml&lt;&#x2F;code&gt; method we’re using &lt;code&gt;Lucky::TextResponse&lt;&#x2F;code&gt; to send the XML string to the browser. Note that &lt;code&gt;application&#x2F;rss+xml&lt;&#x2F;code&gt; is the proper content type to respond with, however, if you want it to be viewable in a web browser you need to use &lt;code&gt;text&#x2F;xml; charset=utf-8&lt;&#x2F;code&gt; (This is how Ghost does it at least).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-action&quot;&gt;Example Action&lt;&#x2F;h2&gt;
&lt;p&gt;Since all the XML logic is in the &lt;code&gt;XMLAction&lt;&#x2F;code&gt; we can make our actions really clean. In the example below &lt;code&gt;Rss::Index&lt;&#x2F;code&gt; overrides the feed title and then passes in a list of published articles through the &lt;code&gt;xml&lt;&#x2F;code&gt; method.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# src&#x2F;actions&#x2F;rss&#x2F;index.cr
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Rss::Index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; ::XMLAction
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;title
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Latest Dailies&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    get &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;feeds&#x2F;all.rss&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;        articles = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticleQuery&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;.published.published_at.desc_order
&lt;&#x2F;span&gt;&lt;span&gt;        xml articles
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;creating-a-serializer&quot;&gt;Creating a Serializer&lt;&#x2F;h2&gt;
&lt;p&gt;Although we have a working RSS feed, we can go one step further and create an &lt;code&gt;ArticleXmlSerializer&lt;&#x2F;code&gt; to handle the XML, rather than using the &lt;code&gt;XMLAction&lt;&#x2F;code&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll pass this Serializer output to the modified &lt;code&gt;XMLAction&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# src&#x2F;actions&#x2F;xml_action.cr
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;abstract class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;XMLAction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; Lucky::Action
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span&gt;(body : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Lucky&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;TextResponse&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;context&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content_type: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&#x2F;xml; charset=utf-8&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;body:&lt;&#x2F;span&gt;&lt;span&gt; body, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;200&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# src&#x2F;serializers&#x2F;article_xml_serializer.cr
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticlesXmlSerializer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; Lucky::Serializer
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt; title : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt; description : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt; base_url : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;example.dev&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;property&lt;&#x2F;span&gt;&lt;span&gt; path : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;    # You can set the instance vars directly. This elininates some code and also is now compile time safe!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;    # That means if you forget one of these arguments, it is the wrong type, or you have a typo, Crystal will let
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;    # you know at compile-time
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;initialize&lt;&#x2F;span&gt;&lt;span&gt;(@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;articles &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticleQuery&lt;&#x2F;span&gt;&lt;span&gt;, @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;, @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;, @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span&gt;, @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;render
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;XML&lt;&#x2F;span&gt;&lt;span&gt;.build(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;indent: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;  &amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;encoding: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;UTF-8&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;xml&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;            xml.element(
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rss&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;version: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;2.0&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, 
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:dc&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;purl.org&#x2F;dc&#x2F;elements&#x2F;1.1&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;purl.org&#x2F;rss&#x2F;1.0&#x2F;modules&#x2F;content&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:atom&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;www.w3.org&#x2F;2005&#x2F;Atom&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;xmlns:media&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;search.yahoo.com&#x2F;mrss&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                ) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;channel&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata title }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata description }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text base_url }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;generator&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Solo&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;lastBuildDate&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Time&lt;&#x2F;span&gt;&lt;span&gt;.utc_now.to_s }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;atom:link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { 
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;href&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;path&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;rel&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;application&#x2F;rss+xml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                    xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ttl&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;articles&lt;&#x2F;span&gt;&lt;span&gt;.each &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do &lt;&#x2F;span&gt;&lt;span&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;article&lt;&#x2F;span&gt;&lt;span&gt;|
&lt;&#x2F;span&gt;&lt;span&gt;                        xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;item&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata article.title }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; article.meta_description
&lt;&#x2F;span&gt;&lt;span&gt;                                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata article.meta_description.not_nil! }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;articles&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article.slug&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;dc:creator&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Author Name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span&gt;                            xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;pubDate&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.text article.created_at.to_s }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; article.og_image
&lt;&#x2F;span&gt;&lt;span&gt;                                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;media:content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;                                    xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;article.og_image&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                                    xml.attribute &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;medium&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; article.content
&lt;&#x2F;span&gt;&lt;span&gt;                                content = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Markdown&lt;&#x2F;span&gt;&lt;span&gt;.to_html(article.content.not_nil!)
&lt;&#x2F;span&gt;&lt;span&gt;                                xml.element(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content:encoded&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) { xml.cdata content }
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, here&#x27;s the new action&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# src&#x2F;actions&#x2F;rss&#x2F;index.cr
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Rss::Index &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; ::XMLAction
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    get &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;feeds&#x2F;all.rss&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;        articles = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticleQuery&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;.published.published_at.desc_order
&lt;&#x2F;span&gt;&lt;span&gt;        xml &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ArticlesXmlSerializer&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(articles, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;title: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Feed Name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;description: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Here&amp;#39;s the descr&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;path:&lt;&#x2F;span&gt;&lt;span&gt; request.path).render
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>How to build a URL shortener in Amber</title>
		<published>2018-11-10T00:00:00+00:00</published>
		<updated>2018-11-10T00:00:00+00:00</updated>
		<link href="https://www.fullstackstanley.com/articles/how-to-build-a-url-shortener-in-amber/" type="text/html"/>
		<id>https://www.fullstackstanley.com/articles/how-to-build-a-url-shortener-in-amber/</id>
		<content type="html">&lt;p&gt;In this tutorial, we will create a URL shortening service in Amber - one of the popular Crystal web application frameworks.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;amber-shortly-home.png&quot; alt=&quot;Shortener home page preview&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Along the way, we’ll do the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Set up a new Amber project&lt;&#x2F;li&gt;
&lt;li&gt;Use the built-in scaffold generator&lt;&#x2F;li&gt;
&lt;li&gt;Run through the basics of routing, controllers and templates&lt;&#x2F;li&gt;
&lt;li&gt;Validate user-entered data&lt;&#x2F;li&gt;
&lt;li&gt;Use UUID primary keys for generating random URLs&lt;&#x2F;li&gt;
&lt;li&gt;Redirect and respond to requests &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;setting-up-a-new-project&quot;&gt;Setting up a new project&lt;&#x2F;h2&gt;
&lt;p&gt;If you haven’t already, follow the instructions to install Crystal, Amber and Node &lt;a href=&quot;https:&#x2F;&#x2F;docs.amberframework.org&#x2F;amber&#x2F;guides&#x2F;installation&quot;&gt;as seen here&lt;&#x2F;a&gt;. This tutorial uses Crystal 0.27.0 and Amber 0.11.1 but works with Crystal 0.26.1 and Amber 0.10.0 as well. You’ll also need PostgreSQL installed as well for the database.&lt;&#x2F;p&gt;
&lt;p&gt;First let&#x27;s create a new Amber application, run the following command in your terminal to get started.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; new shortly
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will generate a new app named shortly using the Amber defaults, PostgreSQL for the database, and Slang for the templates. If you prefer, you can use &lt;code&gt;amber new shortly -t ecr&lt;&#x2F;code&gt; to use HTML templates rather than Slang.  ECR is less efficient than Slang but more convenient if you have a HTML template to work from.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the code is in the &lt;code&gt;src&lt;&#x2F;code&gt; directory, this includes Models, Views and Controllers. Similarly to Ruby on Rails, the routes are located in the &lt;code&gt;config&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;p&gt;To get the server up and running use the following commands. The first one downloads Crystal dependencies, the second creates the database, and the third runs the HTTP Server and Webpack with live reloading.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Note: If you want to edit the database configuration, now is the time to do so. Open &lt;code&gt;config&#x2F;environments&#x2F;development.yml&lt;&#x2F;code&gt; and look for the &lt;code&gt;database_url&lt;&#x2F;code&gt; key&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shards&lt;&#x2F;span&gt;&lt;span&gt; install
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; db create
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; watch
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Open &lt;code&gt;http:&#x2F;&#x2F;0.0.0.0:3000&lt;&#x2F;code&gt; to see the default home page.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-brief-introduction-to-routing&quot;&gt;A brief introduction to routing&lt;&#x2F;h2&gt;
&lt;p&gt;Amber uses a similar approach to Elixir Phoenix when it comes to routing by using &lt;code&gt;plugs&lt;&#x2F;code&gt; and &lt;code&gt;pipelines&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A plug is a piece of middleware that runs during a request. Lots of these will run one after the other, transforming the request depending on what was sent from the client and which route the request matches against.&lt;&#x2F;p&gt;
&lt;p&gt;Some example plugs include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Amber::Pipe::Logger.new&lt;&#x2F;code&gt; - Handles the logging functionality.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Amber::Pipe::CSRF.new&lt;&#x2F;code&gt; - Prevent form requests from Cross-Site Request Forgery&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;plug Amber::Pipe::Flash.new&lt;&#x2F;code&gt; - Handles flash messages&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Applying plugs to each individual route wouldn’t be an efficient way of doing things. Instead, You can group common sets of plugs together in a &lt;em&gt;pipeline&lt;&#x2F;em&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;Amber comes with three pipelines that provide common functionality - but you can create your own, too.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;web&lt;&#x2F;code&gt; is for standard web requests and includes most things you’ll need.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;api&lt;&#x2F;code&gt; is similar to web but takes out the unnecessary plugs for APIs such as CSRF and Flash messages. It also adds in the CORS plug.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;static&lt;&#x2F;code&gt; is for requests to static assets.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Other useful pipelines you might want to build include &lt;code&gt;authentication&lt;&#x2F;code&gt; for requests that sit behind a log-in system. Or &lt;code&gt;admin&lt;&#x2F;code&gt; for requests that need user-level permissions to view admin functionality.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;routes&lt;&#x2F;code&gt; function takes a pipeline’s name and a block of routes. When a route resides within the &lt;code&gt;web&lt;&#x2F;code&gt; routes block, it will run all of the &lt;code&gt;web&lt;&#x2F;code&gt; pipeline plugs on a matching web request.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;  pipeline &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:web &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Amber&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Pipe&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;PoweredByAmber&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Citrine&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;I18n&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Handler&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Amber&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Pipe&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Amber&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Pipe&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Logger&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Amber&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Pipe&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Session&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Amber&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Pipe&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Flash&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;	plug &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Amber&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Pipe&lt;&#x2F;span&gt;&lt;span&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;CSRF&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  routes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:web &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	get &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;HomeController&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:static
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If your already familiar with other web frameworks then you’ll probably know the types of requests to expect. These include &lt;code&gt;get&lt;&#x2F;code&gt;, &lt;code&gt;post&lt;&#x2F;code&gt;, &lt;code&gt;update&lt;&#x2F;code&gt;, &lt;code&gt;put&lt;&#x2F;code&gt;, &lt;code&gt;delete&lt;&#x2F;code&gt; and &lt;code&gt;resources&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For all except resources, the first parameter is URL to match against, the second is the controller class to load, and the third is the method to run.&lt;&#x2F;p&gt;
&lt;p&gt;Resources, on the other hand,  takes 2 parameters: the base URL which will be expanded into various URLs for CRUD actions, and the controller class to use each request with. These will need class methods matching each URL. &lt;&#x2F;p&gt;
&lt;p&gt;There is also a third, optional, parameter which lets you include or exclude specific CRUD actions.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;resources &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;user&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;UserController&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;only: &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:index&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:show&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;creating-a-new-controller&quot;&gt;Creating a new controller&lt;&#x2F;h2&gt;
&lt;p&gt;Although a full &lt;code&gt;scaffold&lt;&#x2F;code&gt; command exists in Amber, for this application doing the process manually allows each step to be explained and understood more thoroughly.&lt;&#x2F;p&gt;
&lt;p&gt;Run the following command to create a new controller. &lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; g controller Shortener index:get show:get create:post
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This controller will generate 3 end points:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;index&lt;&#x2F;code&gt; which will replace the root URL and have a form for the user to enter their desired shortened web address.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;create&lt;&#x2F;code&gt; which will handle the post request to generate a shortened URL.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;show&lt;&#x2F;code&gt; will be where the user is redirected to after submitting a URL and will show their new address to copy and share.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Delete the &lt;code&gt;create.slang&lt;&#x2F;code&gt; template file as it won’t be used.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;rm&lt;&#x2F;span&gt;&lt;span&gt; src&#x2F;views&#x2F;shortener&#x2F;create.slang
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Opening up &lt;code&gt;src&#x2F;controllers&#x2F;shortener_controller.cr&lt;&#x2F;code&gt; we can see that the class has three methods and also inherits from &lt;code&gt;ApplicationController&lt;&#x2F;code&gt;. &lt;&#x2F;p&gt;
&lt;p&gt;Looking at the &lt;code&gt;src&#x2F;controllers&#x2F;application_controller.cr&lt;&#x2F;code&gt; file,  we can see &lt;code&gt;JasperHelpers&lt;&#x2F;code&gt; which makes a few useful helper methods available including form, input and link helpers. There&#x27;s also a layout variable which sets the layout template to use. This class then inherits further functionality from &lt;code&gt;Amber::Controller::Base&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Amber::Controller::Base&lt;&#x2F;code&gt; adds in several helper modules including functionality related to CSRF, Redirects, and Rendering. It also adds the validation &lt;code&gt;params&lt;&#x2F;code&gt; which is used for checking request data from the user (More on that later)&lt;&#x2F;p&gt;
&lt;p&gt;Back in the &lt;code&gt;shortener_controller.cr&lt;&#x2F;code&gt; remove the &lt;code&gt;render(&amp;quot;create.slang&amp;quot;)&lt;&#x2F;code&gt; as we won&#x27;t be needing it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-routes&quot;&gt;Adding routes&lt;&#x2F;h2&gt;
&lt;p&gt;Re-open the &lt;code&gt;config&#x2F;routes.cr&lt;&#x2F;code&gt; file and replace the &lt;code&gt;:web&lt;&#x2F;code&gt; block with this.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;Crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-Crystal &quot;&gt;&lt;code class=&quot;language-Crystal&quot; data-lang=&quot;Crystal&quot;&gt;&lt;span&gt;routes &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:web &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;  resources &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenerController&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;only: &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:index&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:create&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:show&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run &lt;code&gt;amber routes&lt;&#x2F;code&gt; in the terminal and the three routes we have added should show.&lt;&#x2F;p&gt;
&lt;p&gt;You can also see the fallback wildcard route, &lt;code&gt;&#x2F;*&lt;&#x2F;code&gt;. When Amber can&#x27;t find a matching route it will look for static assets in the &lt;code&gt;public&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;amber-shortly-routes-list.jpg&quot; alt=&quot;Route list&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-the-model&quot;&gt;Creating the model&lt;&#x2F;h2&gt;
&lt;p&gt;Use the &lt;code&gt;amber g model&lt;&#x2F;code&gt; command to create a new model and migration file for &lt;code&gt;ShortenedUrl&lt;&#x2F;code&gt;. Links can get quite long so its safer to use &lt;code&gt;text&lt;&#x2F;code&gt; rather than &lt;code&gt;string&lt;&#x2F;code&gt;, as &lt;code&gt;VARCHAR&lt;&#x2F;code&gt; has a limit of 256 characters.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; g model ShortenedLink original_url:text
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Open up the &lt;code&gt;db&#x2F;migrations&#x2F;x_create_shortened_link.sql&lt;&#x2F;code&gt; file. Amber uses &lt;code&gt;Micrate&lt;&#x2F;code&gt;, a shard which handles migrating the database. &lt;&#x2F;p&gt;
&lt;p&gt;Micrate reads the commented SQL to handle what should be run. &lt;&#x2F;p&gt;
&lt;p&gt;The SQL below &lt;code&gt;-- +micrate Up&lt;&#x2F;code&gt; will run when &lt;code&gt;amber db migrate&lt;&#x2F;code&gt; is entered and &lt;code&gt;-- +micrate Down&lt;&#x2F;code&gt; gets run when you want to rollback with &lt;code&gt;amber db rollback&lt;&#x2F;code&gt;. There&#x27;s also &lt;code&gt;amber db status&lt;&#x2F;code&gt; to see what state your database schema is in.&lt;&#x2F;p&gt;
&lt;p&gt;In this tutorial we&#x27;ll use &lt;code&gt;id&lt;&#x2F;code&gt; for the shortened url code. Since integers starting from 1 aren’t an ideal format for this we&#x27;ll store a shortened &lt;code&gt;UUID&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Modify the &lt;code&gt;id&lt;&#x2F;code&gt; to be &lt;code&gt;VARCHAR&lt;&#x2F;code&gt;, optionally set a max length e.g. &lt;code&gt;VARCHAR(8)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sql&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sql &quot;&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- +micrate Up
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;CREATE TABLE &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;shortened_links&lt;&#x2F;span&gt;&lt;span&gt; (
&lt;&#x2F;span&gt;&lt;span&gt;  id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;VARCHAR&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;8&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  original_url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;TEXT&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  created_at &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;TIMESTAMP&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  updated_at &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;TIMESTAMP
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;-- +micrate Down
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;DROP TABLE&lt;&#x2F;span&gt;&lt;span&gt; IF EXISTS shortened_links;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that &lt;code&gt;UUID&lt;&#x2F;code&gt;s are supported with Postgres, however, since we&#x27;re trimming the length down we&#x27;ll be using &lt;code&gt;varchar&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Run &lt;code&gt;amber db migrate&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll need to modify the model class to support the new id format. Open &lt;code&gt;src&#x2F;models&#x2F;shortened_link.cr&lt;&#x2F;code&gt; and update it to the following&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uuid&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; Granite::Base
&lt;&#x2F;span&gt;&lt;span&gt;  adapter pg
&lt;&#x2F;span&gt;&lt;span&gt;  table_name shortened_links
&lt;&#x2F;span&gt;&lt;span&gt;  primary id : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;auto: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  field original_url : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;String
&lt;&#x2F;span&gt;&lt;span&gt;  timestamps
&lt;&#x2F;span&gt;&lt;span&gt;  before_create &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:assign_id
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;assign_id
&lt;&#x2F;span&gt;&lt;span&gt;	potential_id = generate_id()
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink&lt;&#x2F;span&gt;&lt;span&gt;.find(potential_id)
&lt;&#x2F;span&gt;&lt;span&gt;	  potential_id = generate_id()
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;	@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id &lt;&#x2F;span&gt;&lt;span&gt;= potential_id
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;generate_id
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;UUID&lt;&#x2F;span&gt;&lt;span&gt;.random.hexstring.to_s[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This file has a reasonable amount of code, here’s the breakdown.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Line 1 includes the &lt;code&gt;UUID&lt;&#x2F;code&gt; library which is used to generate nicer IDs and shortened URL codes.&lt;&#x2F;li&gt;
&lt;li&gt;Line 2 opens the class and inherits from Granite, the default ORM for Amber.&lt;&#x2F;li&gt;
&lt;li&gt;Line 3 specifies that PostgreSQL is the database that’s used.&lt;&#x2F;li&gt;
&lt;li&gt;Line 4 declares the table name for the model.&lt;&#x2F;li&gt;
&lt;li&gt;Line 5 is the primary key, notice it’s set to a string and auto increment is turned off&lt;&#x2F;li&gt;
&lt;li&gt;Line 6 is the &lt;code&gt;original_url&lt;&#x2F;code&gt; field included in the schema. Any other fields should go here too.&lt;&#x2F;li&gt;
&lt;li&gt;Line 7 Adds timestamp functionality for &lt;code&gt;created_at&lt;&#x2F;code&gt; and &lt;code&gt;updated_at&lt;&#x2F;code&gt; columns.&lt;&#x2F;li&gt;
&lt;li&gt;Line 8 adds a before hook. This runs a method of the same name before new models are saved.&lt;&#x2F;li&gt;
&lt;li&gt;Line 10 is the above mentioned method. Here a UUID is generated, turned into a hex string, checked for duplicates and then updates the &lt;code&gt;id&lt;&#x2F;code&gt; instance property ready to be saved.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;finishing-the-shortener-controller&quot;&gt;Finishing the Shortener Controller&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;code&gt;index&lt;&#x2F;code&gt; will return the home page where users will enter their URL, the &lt;code&gt;create&lt;&#x2F;code&gt; is where the form is posted to be validated and created, and the &lt;code&gt;show&lt;&#x2F;code&gt; method will give the link to the user, ready for sharing.&lt;&#x2F;p&gt;
&lt;p&gt;Update the &lt;code&gt;create&lt;&#x2F;code&gt; method with the following&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;create
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; shortened_link_params.valid?
&lt;&#x2F;span&gt;&lt;span&gt;      shortened_link = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt; shortened_link_params.validate!
&lt;&#x2F;span&gt;&lt;span&gt;      shortened_link.save
&lt;&#x2F;span&gt;&lt;span&gt;      redirect_to(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;location: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;links&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;shortened_link.id&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;302&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;flash: &lt;&#x2F;span&gt;&lt;span&gt;{&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;success&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; =&amp;gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Created short link successfully.&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;}
&lt;&#x2F;span&gt;&lt;span&gt;      )
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else
&lt;&#x2F;span&gt;&lt;span&gt;      redirect_to(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;controller: :shortener&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;action: :index&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;flash: &lt;&#x2F;span&gt;&lt;span&gt;{&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;danger&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; =&amp;gt; shortened_link_params.errors.first.message})
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And add this private method to the controller&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;shortened_link_params
&lt;&#x2F;span&gt;&lt;span&gt;    params.validation &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;      required &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:original_url &lt;&#x2F;span&gt;&lt;span&gt;{|&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span&gt;| !f.nil? &amp;amp;&amp;amp; f.url? }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;create&lt;&#x2F;code&gt; method calls the &lt;code&gt;shortened_link_params&lt;&#x2F;code&gt; method to check if the posted data is valid, if it is then it saves it to the database and redirects the user to the &lt;code&gt;show&lt;&#x2F;code&gt; page. If it isn’t, the user is redirected back and is sent an error message. Notice how the validation checks if &lt;code&gt;:original_url&lt;&#x2F;code&gt; is sent, if it is nil and if it is a valid url string. This parameter validation functionality comes from the &lt;code&gt;Amber::Controller::Base&lt;&#x2F;code&gt; class mentioned earlier.&lt;&#x2F;p&gt;
&lt;p&gt;To finish off the controller add the following to the top of it&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;getter&lt;&#x2F;span&gt;&lt;span&gt; shortened_link : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  before_action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	only [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:show&lt;&#x2F;span&gt;&lt;span&gt;] { set_shortened_link }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And another private method to the bottom&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;set_shortened_link
&lt;&#x2F;span&gt;&lt;span&gt;	@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shortened_link &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink&lt;&#x2F;span&gt;&lt;span&gt;.find! params[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:id&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here we’re using a before hook to set relevant model for the &lt;code&gt;show&lt;&#x2F;code&gt; method, ready to be used in the template. Since there is only one method using this we could put the functionality straight into the controller &lt;code&gt;show&lt;&#x2F;code&gt; method. However, it&#x27;s worth demonstrating it for usage with controllers that have &lt;code&gt;edit&lt;&#x2F;code&gt;, &lt;code&gt;update&lt;&#x2F;code&gt;, and &lt;code&gt;delete&lt;&#x2F;code&gt; functionality as well.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;templates&quot;&gt;Templates&lt;&#x2F;h2&gt;
&lt;p&gt;Amber uses &lt;code&gt;Slang&lt;&#x2F;code&gt; templates by default. If you’re familiar with &lt;code&gt;Slim&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;Haml&lt;&#x2F;code&gt; in Ruby then you should be in familiar terrority. HTML (&lt;code&gt;ecr&lt;&#x2F;code&gt; templates) can be used as well, however they are not as efficient as &lt;code&gt;Slang&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Update &lt;code&gt;views&#x2F;layouts&#x2F;application.slang&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-haml &quot;&gt;&lt;code class=&quot;language-haml&quot; data-lang=&quot;haml&quot;&gt;&lt;span&gt;doctype html
&lt;&#x2F;span&gt;&lt;span&gt;html.min-h-screen
&lt;&#x2F;span&gt;&lt;span&gt;  head
&lt;&#x2F;span&gt;&lt;span&gt;	title Shortly using Amber
&lt;&#x2F;span&gt;&lt;span&gt;	meta charset=&amp;quot;utf-8&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=edge&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;npm&#x2F;tailwindcss&#x2F;dist&#x2F;tailwind.min.css&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;&#x2F;dist&#x2F;main.bundle.css&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	link rel=&amp;quot;apple-touch-icon&amp;quot; href=&amp;quot;&#x2F;favicon.png&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	link rel=&amp;quot;icon&amp;quot; href=&amp;quot;&#x2F;favicon.png&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	link rel=&amp;quot;icon&amp;quot; type=&amp;quot;image&#x2F;x-icon&amp;quot; href=&amp;quot;&#x2F;favicon.ico&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  body.flex.items-center.min-h-screen.bg-orange-lightest.text-black.pb-8
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.container.mx-auto
&lt;&#x2F;span&gt;&lt;span&gt;	  - flash.each &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do&lt;&#x2F;span&gt;&lt;span&gt; |key, value|
&lt;&#x2F;span&gt;&lt;span&gt;		div class=&amp;quot;alert alert-#{key}&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;		  p = flash[key]
&lt;&#x2F;span&gt;&lt;span&gt;	  == content
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;	script src=&amp;quot;&#x2F;dist&#x2F;main.bundle.js&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;views&#x2F;shortener&#x2F;index.slang&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-haml &quot;&gt;&lt;code class=&quot;language-haml&quot; data-lang=&quot;haml&quot;&gt;&lt;span&gt;div.container.mx-auto.text-center.mb-16
&lt;&#x2F;span&gt;&lt;span&gt;  h1.text-3xl.mb-8 Link Shortener
&lt;&#x2F;span&gt;&lt;span&gt;  == form(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;action: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;links&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;method: :post&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;class: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;w-full max-w-md mx-auto&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;	== csrf_tag
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.flex.items-center.border-b.border-b-2.border-black.py-2
&lt;&#x2F;span&gt;&lt;span&gt;	  == text_field &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;name: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;original_url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;value: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;placeholder: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;class: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;appearance-none bg-transparent border-none w-full text-grey-darker mr-3 py-1 px-2 leading-tight&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;	  == submit(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Create Link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;class: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;flex-no-shrink bg-teal bg-black border-black border-black text-sm border-4 text-white py-1 px-2 rounded&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;views&#x2F;shortener&#x2F;show.slang&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;haml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-haml &quot;&gt;&lt;code class=&quot;language-haml&quot; data-lang=&quot;haml&quot;&gt;&lt;span&gt;div.container.mx-auto.text-center.my-8
&lt;&#x2F;span&gt;&lt;span&gt;  h1.text-3xl.mb-8 Link Shortener
&lt;&#x2F;span&gt;&lt;span&gt;  h2.text-2xl.mb-4 Your link
&lt;&#x2F;span&gt;&lt;span&gt;  p
&lt;&#x2F;span&gt;&lt;span&gt;	  == link_to &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;#{request.host_with_port}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;#{@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shortened_link&lt;&#x2F;span&gt;&lt;span&gt;.id}&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;#{request.host_with_port}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;#{@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shortened_link&lt;&#x2F;span&gt;&lt;span&gt;.id}&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  p.button-link
&lt;&#x2F;span&gt;&lt;span&gt;	  == link_to &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Create a new link&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;```
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;src&#x2F;assets&#x2F;stylesheets&#x2F;main.scss&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scss&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-scss &quot;&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;alert-danger &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  background-color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#e3342f&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#fff&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;alert-success &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  background-color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#38c172&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#22292f&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;button-link &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  text-decoration: none;
&lt;&#x2F;span&gt;&lt;span&gt;  display: inline-block;
&lt;&#x2F;span&gt;&lt;span&gt;  margin-top: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.5rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  background-color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#22292f&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  background-color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#22292f&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  border-color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#22292f&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  border-color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#22292f&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  font-size: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.875rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  border-width: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;4px&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  color: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;#fff&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  padding-top: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.25rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  padding-bottom: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.25rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  padding-left: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.5rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  padding-right: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.5rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  border-radius: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;.25rem&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;div class=&quot;text-center&quot;&gt;&lt;em&gt;A preview of the create page&lt;&#x2F;em&gt;&lt;&#x2F;div&gt;
![Preview of creation page](&#x2F;images&#x2F;amber-shortly-create.png)
&lt;h2 id=&quot;handling-the-redirect&quot;&gt;Handling the Redirect&lt;&#x2F;h2&gt;
&lt;p&gt;Run the following command to create a new controller&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; g controller redirect show:get
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And add the new route to match the controller in &lt;code&gt;confi&#x2F;routes.cr&lt;&#x2F;code&gt; in the &lt;code&gt;:web&lt;&#x2F;code&gt; block&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;get &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;r&#x2F;:id&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;RedirectController&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:show
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then update the &lt;code&gt;RedirectController&lt;&#x2F;code&gt; to the following&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;RedirectController &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt; ApplicationController
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;getter&lt;&#x2F;span&gt;&lt;span&gt; shortened_link : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;new
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  before_action &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;do
&lt;&#x2F;span&gt;&lt;span&gt;    only [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:show&lt;&#x2F;span&gt;&lt;span&gt;] { set_shortened_link }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;show
&lt;&#x2F;span&gt;&lt;span&gt;    redirect_to(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;location: &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span&gt;@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shortened_link&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.original_url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ab7967;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;status: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;302&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;private def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;set_shortened_link
&lt;&#x2F;span&gt;&lt;span&gt;    @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;shortened_link &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;ShortenedLink&lt;&#x2F;span&gt;&lt;span&gt;.find! params[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:id&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This code is a bit overkill for how small the controller is, however, it demonstrates using &lt;code&gt;before_action&lt;&#x2F;code&gt; hooks again for an specific actions, defining a &lt;code&gt;getter&lt;&#x2F;code&gt; with a default empty model on line 3, and redirecting to a new URL on line 10.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;show&lt;&#x2F;code&gt; method triggers the &lt;code&gt;before_action&lt;&#x2F;code&gt; which in turn...&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Attempts to find a Shortened link by the ID URL parameter&lt;&#x2F;li&gt;
&lt;li&gt;Sets that model to the instance variable &lt;code&gt;shortened_link&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This means the show method then has access to this variable and can use it to redirect the user.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;running-the-application&quot;&gt;Running the application&lt;&#x2F;h2&gt;
&lt;p&gt;If you haven&#x27;t already tried, then run the &lt;code&gt;watch&lt;&#x2F;code&gt; command and view it in your browser.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;amber watch
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Amber watch will both start up the Crystal web server on port 3000 and run Webpack in the background to compile Javascript and CSS. Whenever you make a change the page in the browser will be refreshed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;This tutorial has covered many aspects of Amber, including basic usage of controllers, routes, and templates. It’s also given a gentle introduction to controller hooks and tables with a UUID primary key.&lt;&#x2F;p&gt;
&lt;p&gt;If this article interested you, please sign up for my newsletter below where I&#x27;ll announce my latest programming articles (including Crystal!).&lt;&#x2F;p&gt;
&lt;p&gt;You can also checkout my previous &lt;a href=&quot;&#x2F;articles&#x2F;categories&#x2F;crystal&quot;&gt;Crystal&#x2F;Amber articles&lt;&#x2F;a&gt;, including using &lt;a href=&quot;&#x2F;articles&#x2F;crystal-amber-with-tailwind-css&quot;&gt;Tailwind CSS with Amber&lt;&#x2F;a&gt;, or look into a deeper look into &lt;a href=&quot;https:&#x2F;&#x2F;fullstackstanley.com&#x2F;articles&#x2F;using-uuids-with-crystal-amber&quot;&gt;UUID’s with Amber&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>How to set up Crystal Amber framework with Tailwind CSS</title>
		<published>2018-09-09T00:00:00+00:00</published>
		<updated>2018-09-09T00:00:00+00:00</updated>
		<link href="https://www.fullstackstanley.com/articles/crystal-amber-with-tailwind-css/" type="text/html"/>
		<id>https://www.fullstackstanley.com/articles/crystal-amber-with-tailwind-css/</id>
		<content type="html">&lt;p&gt;This tutorial runs through adding Tailwind CSS to a Crystal Amber project using Webpack and SASS.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;It assumes you already have a working Amber project. If not, check out the Installation and &lt;a href=&quot;https:&#x2F;&#x2F;docs.amberframework.org&#x2F;amber&#x2F;guides&#x2F;installation&quot;&gt;Installation&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.amberframework.org&#x2F;amber&#x2F;guides&#x2F;create-new-app&quot;&gt;Create New App&lt;&#x2F;a&gt; guides first. You’ll also need Node installed.&lt;&#x2F;p&gt;
&lt;p&gt;This tutorial works sing Node 8.11.4, Crystal: 0.26.1, Amber 0.9.0, Webpack 3.12&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-tailwind-css&quot;&gt;Why Tailwind CSS?&lt;&#x2F;h2&gt;
&lt;p&gt;There are a lot of reasons to give Tailwind CSS a try, but coming from Bootstrap it can take some adjustment. Some of the things I really like include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Lots of helper classes built in to help you create a good looking UI.&lt;&#x2F;li&gt;
&lt;li&gt;Really easy to configure and extend it by editing &lt;code&gt;tailwind.js&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Adding new CSS classes by extending the defaults with &lt;code&gt;@apply&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Once you get used to the naming conventions it can be very productive.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;tailwindcss.com&#x2F;docs&#x2F;what-is-tailwind&#x2F;&quot;&gt;Check out the docs for more information&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-amber&quot;&gt;Why Amber?&lt;&#x2F;h2&gt;
&lt;p&gt;Coming from someone that enjoys using Ruby on Rails and is intrigued by Crystal, I really like Amber and how familiar it already is. Not to mention it’s blazing fast!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.amberframework.org&#x2F;amber&#x2F;&quot;&gt;Check out the docs for more information&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-tailwind-with-amber&quot;&gt;Using Tailwind with Amber&lt;&#x2F;h2&gt;
&lt;p&gt;Add Tailwind CSS and Post CSS to your &lt;code&gt;package.json&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tailwindcss&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;^0.6.5&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;postcss&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;^6.0.19&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;postcss-loader&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;^2.1.1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run &lt;code&gt;yarn install&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;code&gt;src&#x2F;assets&#x2F;stylesheets&lt;&#x2F;code&gt; add &lt;code&gt;postcss.config.js&lt;&#x2F;code&gt; with the following content:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tailwindcss &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tailwindcss&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;);
&lt;&#x2F;span&gt;&lt;span&gt;module.exports = {
&lt;&#x2F;span&gt;&lt;span&gt;  plugins: [
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;tailwindcss&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;config&#x2F;webpack&#x2F;tailwind.js&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;),
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;autoprefixer&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;  ],
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In &lt;code&gt;config&#x2F;webpack&#x2F;common.js&lt;&#x2F;code&gt; update the SASS rules to include PostCSS&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;	  {
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;test&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&#x2F;\.scss$&#x2F;,&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;exclude&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&#x2F;node_modules&#x2F;,&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;ExtractTextPlugin.extract({&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		  &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;fallback&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&amp;#39;style-loader&amp;#39;,&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;		  &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span&gt;: [ &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&amp;#39;css-loader&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&amp;#39;postcss-loader&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#bf616a;color:#2b303b;&quot;&gt;&amp;#39;sass-loader&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;		})
&lt;&#x2F;span&gt;&lt;span&gt;	  },
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Create the &lt;code&gt;tailwind.js&lt;&#x2F;code&gt; file inside of &lt;code&gt;config&#x2F;webpack&#x2F;&lt;&#x2F;code&gt; by running the following command.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.&#x2F;node_modules&#x2F;.bin&#x2F;tailwind&lt;&#x2F;span&gt;&lt;span&gt; init config&#x2F;webpack&#x2F;tailwind.js
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Update your &lt;code&gt;src&#x2F;assets&#x2F;stylesheets&#x2F;main.css&lt;&#x2F;code&gt; to inject Tailwind&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scss&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-scss &quot;&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * This injects Tailwind&amp;#39;s base styles, which is a combination of
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * Normalize.css and some additional base styles.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * You can see the styles here:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * https:&#x2F;&#x2F;github.com&#x2F;tailwindcss&#x2F;tailwindcss&#x2F;blob&#x2F;master&#x2F;css&#x2F;preflight.css
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * If using `postcss-import`, use this import instead:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; * @import &amp;quot;tailwindcss&#x2F;preflight&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt; *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;@tailwind preflight;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * This injects any component classes registered by plugins.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * If using `postcss-import`, use this import instead:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * @import &amp;quot;tailwindcss&#x2F;components&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt; @tailwind components;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Here you would add any of your custom component classes; stuff that you&amp;#39;d
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * want loaded *before* the utilities so that the utilities could still
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * override them.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Example:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * .btn { ... }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * .form-input { ... }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Or if using a preprocessor or `postcss-import`:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * @import &amp;quot;components&#x2F;buttons&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * @import &amp;quot;components&#x2F;forms&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * This injects all of Tailwind&amp;#39;s utility classes, generated based on your
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * config file.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * If using `postcss-import`, use this import instead:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * @import &amp;quot;tailwindcss&#x2F;utilities&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt; @tailwind utilities;
&lt;&#x2F;span&gt;&lt;span&gt; 
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Here you would add any custom utilities you need that don&amp;#39;t come out of the
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * box with Tailwind.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Example :
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * .bg-pattern-graph-paper { ... }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * .skew-45 { ... }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Or if using a preprocessor or `postcss-import`:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * @import &amp;quot;utilities&#x2F;background-patterns&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * @import &amp;quot;utilities&#x2F;skew-transforms&amp;quot;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;**
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  * Example usage
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;  *&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;body &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;	@apply &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;bg-red&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run &lt;code&gt;amber watch&lt;&#x2F;code&gt; and your &lt;code&gt;&amp;lt;body&amp;gt;&lt;&#x2F;code&gt; should have a red background!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Using UUIDs with Crystal Amber</title>
		<published>2018-09-09T00:00:00+00:00</published>
		<updated>2018-09-09T00:00:00+00:00</updated>
		<link href="https://www.fullstackstanley.com/articles/using-uuids-with-crystal-amber/" type="text/html"/>
		<id>https://www.fullstackstanley.com/articles/using-uuids-with-crystal-amber/</id>
		<content type="html">&lt;p&gt;In some situations it&#x27;s ideal to be able to hide the ID number in a database table. Here&#x27;s how to do it using Crystal Amber and the Granite ORM.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Create your model&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;amber&lt;&#x2F;span&gt;&lt;span&gt; g model WebLink url:string
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Edit the migration file to use either a &lt;code&gt;VARCHAR&lt;&#x2F;code&gt; or &lt;code&gt;UUID&lt;&#x2F;code&gt; if it supports it (Hint: Postgres does!). To use a hex string instead, stick with &lt;code&gt;VARCHAR&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;crystal&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-crystal &quot;&gt;&lt;code class=&quot;language-crystal&quot; data-lang=&quot;crystal&quot;&gt;&lt;span&gt;-- +micrate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Up
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;CREATE TABLE&lt;&#x2F;span&gt;&lt;span&gt; web_links (
&lt;&#x2F;span&gt;&lt;span&gt;  id &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;UUID PRIMARY KEY&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  url &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;VARCHAR&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  created_at &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;TIMESTAMP&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  updated_at &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;TIMESTAMP
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;-- +micrate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;Down
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;DROP TABLE IF EXISTS&lt;&#x2F;span&gt;&lt;span&gt; web_links;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Edit the model file to include the &lt;code&gt;UUID&lt;&#x2F;code&gt; library, set the Primary Key as a string and turn off auto increment. Then add the &lt;code&gt;before_create&lt;&#x2F;code&gt; macro to assign a new &lt;code&gt;UUID&lt;&#x2F;code&gt; on creation.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;require &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;uuid&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;class &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;WebLink &lt;&#x2F;span&gt;&lt;span style=&quot;color:#eff1f5;&quot;&gt;&amp;lt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Granite::Base
&lt;&#x2F;span&gt;&lt;span&gt;  adapter pg
&lt;&#x2F;span&gt;&lt;span&gt;  table_name web_links
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# id : Int64 primary key is created for you
&lt;&#x2F;span&gt;&lt;span&gt;  primary id : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;auto: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  field url : &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;String
&lt;&#x2F;span&gt;&lt;span&gt;  timestamps
&lt;&#x2F;span&gt;&lt;span&gt;  before_create &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;:assign_id
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;assign_id
&lt;&#x2F;span&gt;&lt;span&gt;	@&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;UUID&lt;&#x2F;span&gt;&lt;span&gt;.random.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_s
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alternatively, use &lt;code&gt;UUID.random.hexstring.to_s&lt;&#x2F;code&gt; for an alternate style (e.g. &lt;code&gt;2d10e1a2fa89425197d62f94c6cf07dc&lt;&#x2F;code&gt;). The &lt;code&gt;UUID&lt;&#x2F;code&gt; database data type will convert this automatically so make sure to set the data type to &lt;code&gt;VARCHAR&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To use shorter IDs chop the &lt;code&gt;hexstring&lt;&#x2F;code&gt; down by using the range syntax &lt;code&gt;[0..6]&lt;&#x2F;code&gt;, replacing 6 with the desired length, and then check for duplicates before creating the table row.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;ruby&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-ruby &quot;&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;assign_id
&lt;&#x2F;span&gt;&lt;span&gt;  potential_id = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;UUID&lt;&#x2F;span&gt;&lt;span&gt;.random.hexstring.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_s&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;WebLink&lt;&#x2F;span&gt;&lt;span&gt;.find(potential_id)
&lt;&#x2F;span&gt;&lt;span&gt;	  potential_id = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ebcb8b;&quot;&gt;UUID&lt;&#x2F;span&gt;&lt;span&gt;.random.hexstring.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_s&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;..&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;	&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;span&gt;  @&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;id &lt;&#x2F;span&gt;&lt;span&gt;= potential_id
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;end
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s it! Make sure to run &lt;code&gt;crystal db migrate&lt;&#x2F;code&gt; before running the application.&lt;&#x2F;p&gt;
</content>
	</entry>
</feed>
