<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jenn</title>
    <description>The latest articles on Forem by Jenn (@geekgalgroks).</description>
    <link>https://forem.com/geekgalgroks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F6197%2Fgf_gDTGz.jpg</url>
      <title>Forem: Jenn</title>
      <link>https://forem.com/geekgalgroks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/geekgalgroks"/>
    <language>en</language>
    <item>
      <title>Using Lambda to rewrite URLs for Hugo</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Tue, 18 Aug 2020 04:38:15 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/using-lambda-to-rewrite-urls-for-hugo-2joi</link>
      <guid>https://forem.com/geekgalgroks/using-lambda-to-rewrite-urls-for-hugo-2joi</guid>
      <description>&lt;p&gt;Hugo by default uses &lt;a href="https://gohugo.io/content-management/urls/#pretty-urls"&gt;pretty URLs&lt;/a&gt; which work out of the box in most hosting environments. Using AWS Cloudfront in front of S3 causes it to not automatically translate pretty urls.&lt;/p&gt;

&lt;p&gt;Instead of setting &lt;code&gt;uglyurls = true&lt;/code&gt; in your site config, use AWS Lambda to translate for you.&lt;/p&gt;

&lt;p&gt;There are example Lambda@Edge functions out there, I used &lt;a href="https://medium.com/robot-kittens/aws-s3-static-hosting-with-ssl-and-user-friendly-urls-3ecf6c797991"&gt;this Medium post&lt;/a&gt; and an old &lt;a href="https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/"&gt;AWS Compute Blog&lt;/a&gt; to build my function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract the request from the CloudFront event that is sent to Lambda@Edge &lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract the URI from the request&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;olduri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// If last element of old uri doesn't contain a .html extension, assume it's a directory and append a '/'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;olduri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;olduri&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[^\.]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;
        &lt;span class="nx"&gt;olduri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;olduri&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Match any '/' that occurs at the end of a URI. Replace it with a default index&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;newuri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;olduri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;/index.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Log the URI as received by CloudFront and the new URI to be used to fetch from origin&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Old URI: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;olduri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New URI: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;newuri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Replace the received URI with the URI that includes the index page&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newuri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Return to CloudFront&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The main gotcha is you may get this weird looking error when publishing the function if you are using the web interface.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your function's execution role must be assumable by the edgelambda.amazonaws.com service principal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lambda can create an IAM role for you, but you still have to go into the newly created role and edit the &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-permissions.html#lambda-edge-permissions-function-execution"&gt;Trust relationships&lt;/a&gt; policy document to allow Lambda@Edge access.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "lambda.amazonaws.com",
          "edgelambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After editing the policy document for trust relationships, REFRESH the tab/window you are publishing the function from to force it to see the new permissions.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>hugo</category>
      <category>s3</category>
      <category>cloudfront</category>
    </item>
    <item>
      <title>Building and deploying a Hugo site with CodeBuild</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Mon, 17 Aug 2020 01:46:43 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/building-and-deploying-a-hugo-site-with-codebuild-ec</link>
      <guid>https://forem.com/geekgalgroks/building-and-deploying-a-hugo-site-with-codebuild-ec</guid>
      <description>&lt;p&gt;If you follow along in the &lt;a href="https://gohugo.io/hosting-and-deployment/hugo-deploy/"&gt;Hugo documentation&lt;/a&gt; there are many way to deploy your site. I already had a &lt;a href="https://aws.amazon.com/codepipeline/"&gt;CodePipeline&lt;/a&gt; and &lt;a href="https://aws.amazon.com/codebuild/"&gt;CodeBuild&lt;/a&gt; set up for my previous Gatsby blog, so I decided to leverage that instead.&lt;/p&gt;

&lt;p&gt;The biggest hurdle in doing this, was figuring out what changes I needed to make to my CodeBuild project to make it work with Hugo instead of Gatsby. I searched online until I found a &lt;a href="https://kbild.ch/blog/2019-01-31-codepipeline/"&gt;blog post&lt;/a&gt; with an example &lt;code&gt;buildspec.yml&lt;/code&gt; for me to work from.&lt;/p&gt;

&lt;p&gt;I made a few updates to the &lt;code&gt;buildspec.yml&lt;/code&gt; and then deployed my site with the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
  &lt;span class="na"&gt;parameter-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;

&lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Entered the install phase...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get -qq update &amp;amp;&amp;amp; apt-get -qq install curl&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apt-get -qq install asciidoctor&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;curl -s -L https://github.com/gohugoio/hugo/releases/download/v0.74.3/hugo_0.74.3_Linux-64bit.deb -o hugo.deb&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dpkg -i hugo.deb&lt;/span&gt;
    &lt;span class="na"&gt;finally&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Installation done&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Entered the build phase ...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Build started on `date`&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd $CODEBUILD_SRC_DIR&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rm -f buildspec.yml &amp;amp;&amp;amp; rm -f .git &amp;amp;&amp;amp; rm -f README.md&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;hugo&lt;/span&gt;
    &lt;span class="na"&gt;finally&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Building the HTML files finished&lt;/span&gt;
  &lt;span class="na"&gt;post_build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Entered the post_build phase...&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo Build completed on `date`&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws s3 sync public/ s3://bucket-name --region region --acl public-read --delete&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws cloudfront create-invalidation --distribution-id XX --paths '/*'&lt;/span&gt;
&lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;span class="na"&gt;secondary-artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I updated my &lt;code&gt;buildspec.yml&lt;/code&gt; to use the current version of Hugo &lt;a href="https://github.com/gohugoio/hugo/releases/tag/v0.74.3"&gt;0.74.3&lt;/a&gt; and due to some troubleshooting issues, made the build of the site not be silent.&lt;/p&gt;

&lt;p&gt;The biggest change is that I am leveraging the &lt;code&gt;post_build&lt;/code&gt; phase instead of sending an artifact back out into the CodePipeline. I sync the built &lt;code&gt;public/&lt;/code&gt; folder with my S3 bucket, deleting everything in the bucket, copying over the files, and making sure everything is &lt;code&gt;public-read&lt;/code&gt;. The last line in the phase does a cache invalidation for my CloudFront distribution.&lt;/p&gt;

&lt;p&gt;Now whenever I push to the main branch on my Git repo, the site is built and deployed automatically for me. The only issue left is that S3 does not play nicely with "pretty" URLs and I ended up setting my blog to use &lt;a href="https://gohugo.io/content-management/urls/#ugly-urls"&gt;ugly URLs&lt;/a&gt; until I could make a Lambda function to fix it, which will be my next blog post. &lt;/p&gt;

</description>
      <category>hugo</category>
      <category>aws</category>
      <category>s3</category>
      <category>codebuild</category>
    </item>
    <item>
      <title>How to convert a simple blog from Gatsby to Hugo</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Sun, 16 Aug 2020 05:13:28 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/how-to-convert-a-simple-blog-from-gatsby-to-hugo-3jic</link>
      <guid>https://forem.com/geekgalgroks/how-to-convert-a-simple-blog-from-gatsby-to-hugo-3jic</guid>
      <description>&lt;p&gt;I updated my hobby blog to Hugo today. &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; is a static site generator written in &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt; and it is very fast. I created that blog with Gatsby several years ago and it needed a refresh.&lt;/p&gt;

&lt;p&gt;Switching from Gatsby to Hugo is not bad. Both use the same style of front matter and Markdown so I didn't have rewrite everything. As they are written in different programming languages, I could work on converting everything while leaving Gatsby alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Hugo
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Backup everything or create a new branch.&lt;/li&gt;
&lt;li&gt;Rename the &lt;code&gt;content&lt;/code&gt; folder to &lt;code&gt;content1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to generate a new Hugo site in the existing web site folder (it will not work if a &lt;code&gt;content&lt;/code&gt; folder already exists).&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo new site &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a theme, I used &lt;a href="https://themes.gohugo.io/hermit/"&gt;Hermit&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Track3/hermit.git themes/hermit
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the theme to the site config.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'theme = "hermit"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; config.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the Hugo server.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo server &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start moving content.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Moving content
&lt;/h2&gt;

&lt;p&gt;In Gatsby, all my blog post files were named like the following: &lt;code&gt;content\blog\postname\index.md&lt;/code&gt;. In Hugo, the &lt;code&gt;postname&lt;/code&gt; folder was not needed so I began the slog of moving and renaming my files.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the folder &lt;code&gt;posts&lt;/code&gt; under &lt;code&gt;content&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Copy a blog post from &lt;code&gt;content1&lt;/code&gt; to the new &lt;code&gt;posts&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Rename &lt;code&gt;index.html&lt;/code&gt; to the desired slug name for the post.&lt;/li&gt;
&lt;li&gt;Repeat.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing new content
&lt;/h2&gt;

&lt;p&gt;I was not able to move my About and Equipment pages, as they were written in JavaScript. I had to write new Markdown files for them but writing two pages in Markdown is much better than rewriting a whole site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;I had to also move my static content from &lt;code&gt;src&lt;/code&gt; folder to a new top level folder called &lt;code&gt;static&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As my blog content had a lot of embedded tweets, I had to change all of the references over to a &lt;a href="https://gohugo.io/content-management/shortcodes/"&gt;shortcode&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting Gatsby
&lt;/h2&gt;

&lt;p&gt;Once I was happy that all my content was moved over and appearing in Hugo, I began deleting all the Gatsby files.&lt;/p&gt;

&lt;p&gt;I removed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gatsby-browser.js&lt;/li&gt;
&lt;li&gt;gatsby-config.js&lt;/li&gt;
&lt;li&gt;gatsby-node.js&lt;/li&gt;
&lt;li&gt;gatsby-ssr.js&lt;/li&gt;
&lt;li&gt;package-lock.json&lt;/li&gt;
&lt;li&gt;package.json&lt;/li&gt;
&lt;li&gt;src/&lt;/li&gt;
&lt;li&gt;yarn.lock&lt;/li&gt;
&lt;li&gt;content1/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Converting my blog was a bit tedious, but I think it was worth it. I was able to correct spelling mistakes that had been up for years and add tags to the posts.&lt;/p&gt;

&lt;p&gt;It only took 8 hours, 20 commits, and 7 builds.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>gatsby</category>
    </item>
    <item>
      <title>Building an RSS reader in Javascript</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Thu, 16 Jul 2020 19:25:21 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/building-an-rss-reader-in-javascript-1ep0</link>
      <guid>https://forem.com/geekgalgroks/building-an-rss-reader-in-javascript-1ep0</guid>
      <description>&lt;p&gt;Blogging never went away and neither did RSS feeds. &lt;a href="https://en.wikipedia.org/wiki/RSS"&gt;RSS&lt;/a&gt; (Really Simple Syndication) is a web feed to check for updates on sites. It used to be quite popular with several different apps devoted to reading these feeds. In addition, many browsers used to have RSS readers built in. Sadly, RSS has fallen out of popularity probably due to social media and other feeds taking over its role.&lt;/p&gt;

&lt;p&gt;But last night on a whim, I decided to build a personal RSS reader with Vanilla JS. Okay, it wasn't a whim, Twitter was on fire and what better way to distract myself from my usual distraction than creating a new web application?&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://vanilla-js.com/"&gt;Vanilla JS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"&gt;Fetch API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/firestore/query-data/get-data?authuser=0"&gt;Firestore&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;Whenever I start a new project, I look for examples of the idea or similar ideas to build from. I did a search for "rss reader javascript" and I came across several older projects and tutorials. Many of them were written in older syntax or used frameworks. I found one tutorial that used the (then new) Fetch API and decided to build on that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding RSS feeds
&lt;/h3&gt;

&lt;p&gt;Finding feeds is harder than it used to be back in 2006. I searched online for example feeds to pull from and decided on &lt;a href="https://dev.to/feed/"&gt;dev.to's feed&lt;/a&gt;, &lt;a href="https://codepen.io/picks/feed/"&gt;Codepen's feed&lt;/a&gt;, and &lt;a href="https://hacks.mozilla.org/feed/"&gt;Mozilla Hacks Blog&lt;/a&gt;. I tested that I could reach all the feeds in the browser and was served the appropriate XML.&lt;/p&gt;

&lt;p&gt;An example RSS XML document from my personal &lt;a href="https://dev.to/feed/geekgalgroks"&gt;dev.to feed&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;rss&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"2.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;channel&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Jenn&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;author&amp;gt;&lt;/span&gt;Jenn&lt;span class="nt"&gt;&amp;lt;/author&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;Jenn is a self taught web developer who specializes in usability and accessibility.  She is easily spotted at conferences by her bright lipstick and various code dresses and t-shirts.&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&amp;gt;&lt;/span&gt;https://dev.to/geekgalgroks&lt;span class="nt"&gt;&amp;lt;/link&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;language&amp;gt;&lt;/span&gt;en&lt;span class="nt"&gt;&amp;lt;/language&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;item&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;/item&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/channel&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/rss&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;I wanted a way to easily update my list of feeds without having to update my code. I used Google's Firestore for several other little projects and spun up a new collection called &lt;code&gt;rssfeed&lt;/code&gt;. I decided the only thing I needed was the url and added four RSS feed urls to the collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diving straight in
&lt;/h2&gt;

&lt;p&gt;As I had written other little Javascript web apps that used Firestore, I started by copying what I did in that project.&lt;/p&gt;

&lt;p&gt;I created a global variable to hold my feeds and queried the database to push the URL values into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rssfeed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  First problem
&lt;/h3&gt;

&lt;p&gt;I was getting 404 errors in my console. I realized I forgot to set the Firestore database rules to allow reading of the collection.&lt;/p&gt;

&lt;p&gt;I copied the rules of a previous collection and after waiting a bit, confirmed they worked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="s"&gt;match /rssfeed/{feedId} {&lt;/span&gt;
        &lt;span class="s"&gt;allow read;&lt;/span&gt;
        &lt;span class="s"&gt;allow write&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;if request.auth.uid == 'REDACTED';&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I was now able to console log the value of the array and confirm everything was working.&lt;/p&gt;

&lt;h3&gt;
  
  
  Doing too much at once
&lt;/h3&gt;

&lt;p&gt;Spurred on by new success I continued on. I built a function that used Fetch to get the title of a feed. I used a foreach loop on my array and called it.&lt;/p&gt;

&lt;p&gt;I got a bunch of odd errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  CORS and Promises
&lt;/h2&gt;

&lt;p&gt;The first error message that made sense in the console was about CORS.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS"&gt;CORS&lt;/a&gt; stands for Cross Origin Resource Sharing. CORS protects sites from calling assets (Javascript, images, apis, etc) from other websites. Some sites protect all their assets, others explicitly let others use some or all of them.&lt;/p&gt;

&lt;p&gt;Some of the feeds were being protected by CORS.&lt;/p&gt;

&lt;p&gt;At the time I thought it was all the feeds. I looked up how to add CORS modes to my Fetch call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// No CORS, this is an "opaque" mode that limits what headers are sent.&lt;/span&gt;
&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This didn't help. I started looking at proxies and other solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching again
&lt;/h3&gt;

&lt;p&gt;I was frustrated. I searched again for projects. Looking for something newer that might give me insight on how to combat CORS.&lt;/p&gt;

&lt;p&gt;I stumbled across &lt;a href="https://css-tricks.com/how-to-fetch-and-parse-rss-feeds-in-javascript/"&gt;CSS-Tricks How to Fetch and Parse RSS Feeds in JavaScript&lt;/a&gt;. It had a working example and was written in 2020!&lt;/p&gt;

&lt;p&gt;I commented out all of my code and pasted their example in, everything worked. I changed the hardcoded URL from Codepen to my dev.to feed, everything still worked. I wrapped the fetch call in a function and tested again, it worked. I was feeling great. I added back in my database call and using a foreach on my array, called the function.&lt;/p&gt;

&lt;p&gt;It didn't work because my array wasn't populated yet, it just held promises.&lt;/p&gt;

&lt;h3&gt;
  
  
  Promises
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises"&gt;Promises&lt;/a&gt; are placeholders. Asynchronous functions return promises instead of blocking everything on the page while they work. The function promises to get you a result.&lt;/p&gt;

&lt;p&gt;My array was full of promises. Fetch couldn't pull down content from a promised URL, it needed the real thing.&lt;/p&gt;

&lt;p&gt;This is where &lt;code&gt;then&lt;/code&gt; comes in handy. It waits until the asynchronous function completes and then does the next thing. I removed my global variable (should not have made it a global anyway), moved the return statement up on my database call, and chained in my fetch call.&lt;/p&gt;

&lt;p&gt;It worked!&lt;/p&gt;

&lt;p&gt;Except I had three results, not four.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS strikes again
&lt;/h3&gt;

&lt;p&gt;The Mozilla blog was protected by CORS. Instead of fighting it more, I just removed the url from my database. Some battles are not worth fighting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The final code
&lt;/h2&gt;

&lt;p&gt;My completed reader can be found on my &lt;a href="https://portfolio.jenn.dev/reader"&gt;portfolio site&lt;/a&gt;. I have included an HTML snippet and the full javascript file below. CSS is omitted because not everyone loves pastels.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML Snippet
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Jenn's Glorious RSS Reader&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Implemented in Vanilla JS because blogging never died.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  JS
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getRssFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DOMParser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;parseFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/xml&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;``&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/h2&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;div class="feeds"&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`
        &amp;lt;article&amp;gt;
          &amp;lt;h3&amp;gt;
            &amp;lt;a href="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" target="_blank" rel="noopener"&amp;gt;
              &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
            &amp;lt;/a&amp;gt;
          &amp;lt;/h3&amp;gt;
        &amp;lt;/article&amp;gt;
      `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;/div&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beforeend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getFeeds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;feeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rssfeed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;querySnapshot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;displayFeeds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;displayFeeds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getRssFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;getFeeds&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All in all, it took around four hours to write. Much of that time was troubleshooting and research. It probably would have been faster if I wasn't tired and didn't try to do too many things at once in the beginning.&lt;/p&gt;

</description>
      <category>rss</category>
      <category>javascript</category>
      <category>firestore</category>
    </item>
    <item>
      <title>Adventure in restoring WordPress to a Docker container</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Sun, 28 Jun 2020 21:05:23 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/adventure-in-restoring-wordpress-to-a-docker-container-4m6f</link>
      <guid>https://forem.com/geekgalgroks/adventure-in-restoring-wordpress-to-a-docker-container-4m6f</guid>
      <description>&lt;p&gt;Today's adventure was harrowing, fraught with errors, and shouts of victory!&lt;/p&gt;

&lt;h2&gt;
  
  
  Backup
&lt;/h2&gt;

&lt;p&gt;First, I downloaded a backup of my site. The backup included the database and items in wp-content folder (e.g. plugins, themes, and uploads). My backup was compressed into a single file, I extracted the files locally on my machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker-compose
&lt;/h2&gt;

&lt;p&gt;I followed the &lt;a href="https://docs.docker.com/compose/wordpress/"&gt;quickstart&lt;/a&gt; to create my containers for WordPress. After typing &lt;code&gt;docker-compose up&lt;/code&gt; I completed the install of WordPress. I now had a clean install to restore my site into.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plugins, themes, and uploads
&lt;/h2&gt;

&lt;p&gt;Restoring everything in the wp-content folder was a snap. I copied everything over from my download making sure the folder structure was correct. WordPress picked up the themes and plugins right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database
&lt;/h2&gt;

&lt;p&gt;Each table in my database was a separate &lt;code&gt;.sql&lt;/code&gt; file. Instead of having to keep track of all the files and import them in the correct order I concatenated them together into a single file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.sql &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; backup_db.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I now had a problem. How could I transfer this file into my MySQL container?&lt;/p&gt;

&lt;h3&gt;
  
  
  Volumes
&lt;/h3&gt;

&lt;p&gt;To make my life easier, I decided on creating a new volume for my MySQL container. &lt;/p&gt;

&lt;p&gt;I created a local folder called "sql" and added the mapping to a new folder "sql-backup" in my &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:5.7&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db_data:/var/lib/mysql&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./sql:/sql-backup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I chose "sql-backup" for the folder name as I knew it wouldn't already be in my MySQL container and would not conflict with anything. Docker would create it on the next restart and map anything I had in "sql" to it.&lt;/p&gt;

&lt;p&gt;I added the &lt;code&gt;backup_db.sql&lt;/code&gt; file to my "sql" folder and restarted my containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  MySQL
&lt;/h3&gt;

&lt;p&gt;I logged into the MySQL container and confidently entered the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-p&lt;/span&gt; wordpress &amp;lt; /sql-backup/backup_db.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I entered the root password and then it threw an error!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ERROR 1067 (42000) at line 21&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I looked it up and realized quickly that I was dealing with new MySQL defaults. I quickly found a &lt;a href="https://ixnfo.com/en/the-solution-of-error-error-1067-42000-at-line-211-invalid-default-value-for-blablabla.html"&gt;blog&lt;/a&gt; with reasonable steps.&lt;/p&gt;

&lt;p&gt;I grabbed my sql_mode variables and came to a new problem, most Docker images do not have editors installed.&lt;/p&gt;

&lt;p&gt;Containers are suppose to be able to be quickly rebuilt and thus shouldn't need an editor. I didn't want to rebuild mine in order to do the import, so I cheated and installed one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apt
&lt;/h3&gt;

&lt;p&gt;Thankfully the MySQL image had apt installed, after a quick update I installed vim and was on my way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update
apt &lt;span class="nb"&gt;install &lt;/span&gt;vim
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I edited my config file, restarted the service, and tried again only to get a new error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ERROR 1215 (HY000): Cannot add foreign key constraint&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Foreign keys
&lt;/h3&gt;

&lt;p&gt;The good thing about error 1215 is that it is common and there are many blogs and articles on how to fix it. The bad part is there is around 12 different things that can be wrong. It is a very generic error for MySQL.&lt;/p&gt;

&lt;p&gt;I logged back into the MySQL container, looked up once again how to switch databases (it is &lt;code&gt;USE DATABASENAME&lt;/code&gt;), and ran &lt;code&gt;SHOW TABLES&lt;/code&gt;. I got error 1146 which confirmed that the tables foreign keys were referencing were being created in the wrong order.&lt;/p&gt;

&lt;p&gt;I opted to disable foreign key checks instead of hunting through all the tables and creating them in the right order. (See #1 in &lt;a href="https://dzone.com/articles/dealing-with-mysql-error-code-1215-cannot-add-foreign-key-constraint"&gt;this list&lt;/a&gt; for the commands)&lt;/p&gt;

&lt;p&gt;And it worked!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/xTiTnEHBh7qapyuvwQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xTiTnEHBh7qapyuvwQ/giphy.gif" alt="Man in a baby blue leisure suit throwing gold glitter."&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>docker</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Nevertheless, Jenn Coded</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Sun, 08 Mar 2020 18:17:38 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/nevertheless-jenn-coded-538c</link>
      <guid>https://forem.com/geekgalgroks/nevertheless-jenn-coded-538c</guid>
      <description>&lt;p&gt;In college, I applied a position as a teaching assistant for C/C++. My boyfriend at the time said I would never get it as I wasn't in the computer science program.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;An imagining lab need instruction guides and example projects for 3D deconvolution. I am not a microbiologist nor know the supercomputers well.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;It was the final day to submit a game to a small contest. It is 80s themed, so I make a quick text based game.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;My career as a chemist was stalling, I interview for a sysadmin position on a whim.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;I move states suddenly and am looking for work, I apply at midnight for a job as a engineer.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;I move back and am looking for work. I apply randomly to a job and the interview is my former team lead.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;I go to a wedding and sit at my assigned seat, a month later I'm a developer at the local University.&lt;/p&gt;

&lt;p&gt;Nevertheless, Jenn Coded&lt;/p&gt;

&lt;p&gt;Life have thrown me a lot of curveballs but...&lt;/p&gt;

&lt;p&gt;Nevertheless, I coded.&lt;/p&gt;

</description>
      <category>wecoded</category>
    </item>
    <item>
      <title>Shattered or How to bomb interviews as a senior developer</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Fri, 06 Mar 2020 19:30:10 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/shattered-or-how-to-bomb-interviews-as-a-senior-developer-2edg</link>
      <guid>https://forem.com/geekgalgroks/shattered-or-how-to-bomb-interviews-as-a-senior-developer-2edg</guid>
      <description>&lt;p&gt;Yesterday I interviewed for a senior developer position at a large corporation. I was confident, in charge, and ready to rock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l0ExghDSRxU2g55sc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l0ExghDSRxU2g55sc/giphy.gif" alt="Angela Lansbury in a sparkling gown and fur stole."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thirty minutes into the interview I left shattered and destroyed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/mj7TfOF3VqvN6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/mj7TfOF3VqvN6/giphy.gif" alt="Kristen Bell crying in a car."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What happened?&lt;/p&gt;

&lt;p&gt;I bombed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I bombed the interview
&lt;/h2&gt;

&lt;p&gt;Bombing an interview can happen at any stage of your career. It can happen if you prep. It can happen if you don't prep.&lt;/p&gt;

&lt;p&gt;Bombing isn't always just on you. It can also be because of the team that you are interviewing for, their lack of preparation, and the interview style.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepping
&lt;/h3&gt;

&lt;p&gt;I prepped by reviewing everything on the job listing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/10LziGOBkifXzO/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/10LziGOBkifXzO/giphy.gif" alt="Meryl Streep in all pink, outside at a table, typing on an early laptop."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of the bullet points were about Agile, testing, and leading a project so I reviewed those skills and made sure they were mentioned on my resume.&lt;/p&gt;

&lt;p&gt;There was a single line mentioning the &lt;a href="https://en.wikipedia.org/wiki/MEAN_(solution_stack)"&gt;MEAN stack&lt;/a&gt; so I brushed up on my MongoDB and Express.js.&lt;/p&gt;

&lt;p&gt;I felt confident and prepared.&lt;/p&gt;

&lt;p&gt;I washed my power suit, picked out my jewelry, and settled on a simple red lipstick and gold eyeshadow look.&lt;/p&gt;

&lt;h3&gt;
  
  
  The interview
&lt;/h3&gt;

&lt;p&gt;I had been to the location before, so I knew how to get there and the amount of time needed to walk to the building. I arrived a few minutes before my interview to check in and calm myself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/9Cmhx762qStoc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/9Cmhx762qStoc/giphy.gif" alt="Meryl Streep removing her sunglasses."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I met one of the interviewers and went to the conference room. He started to explain the the team and project.&lt;/p&gt;

&lt;p&gt;And I realized something wasn't right.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/h4Z6RfuQycdiM/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/h4Z6RfuQycdiM/giphy.gif" alt="Britney Spears looking confused and side to side with her eyes."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;He was going into a deep dive of security terms. I rolled with it. I did security years ago so I was able to keep up and add to the banter.&lt;/p&gt;

&lt;p&gt;He then talked about the tool the team was making and their stack, Java 8 and Oracle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/J6nvHxHrUJZcI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/J6nvHxHrUJZcI/giphy.gif" alt="Emma Stone horrified in terror."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They wanted a senior Java engineer who knew a little about front end development not a senior front end developer.&lt;/p&gt;

&lt;p&gt;It had been years since I worked with Java. It wasn't even listed on my resume.&lt;/p&gt;

&lt;p&gt;They carried on and quizzed me about JavaScript and everything left my brain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ma7VlDSlty3EA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ma7VlDSlty3EA/giphy.gif" alt="Little girl with pigtails looking back and forth in confusion."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I couldn't remember the base datatypes, hoisting left my mind, first class functions were not there. I scribbled frantically on my scratch paper trying to pull up anything. These were terms I knew, things I just did without thinking, I could not define them anymore. They were just a part of how I coded.&lt;/p&gt;

&lt;p&gt;Then I was asked about databases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/wz8OwIImMOaOc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/wz8OwIImMOaOc/giphy.gif" alt="Kristen Bell fake laughing and then crying."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I finally asked them what they were looking for. It was obvious I was not it. They agreed and we ended it early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Afterwards
&lt;/h3&gt;

&lt;p&gt;I calmly walked out and drove home.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/9u1qjMzl9rj5Frk5sg/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/9u1qjMzl9rj5Frk5sg/giphy.gif" alt="Woman awkwardly looking back as she leaves a house."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I called my recruiter and they apologized to me. They didn't know the team wanted a Java engineer as that is not what they asked for. The recruiter was also confused to why they even interviewed me as my resume has none of the skills they actually wanted.&lt;/p&gt;

&lt;p&gt;I bombed because I should not have been interviewed for that position. It still hurts and is frustrating. Remember not every job is for you.&lt;/p&gt;

</description>
      <category>interview</category>
      <category>failure</category>
      <category>career</category>
    </item>
    <item>
      <title>One year later.... </title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Sat, 22 Feb 2020 22:05:00 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/one-year-later-12jf</link>
      <guid>https://forem.com/geekgalgroks/one-year-later-12jf</guid>
      <description>&lt;p&gt;One year ago, I bought a domain, &lt;a href="https://jenn.dev"&gt;jenn.dev&lt;/a&gt;. I wanted to have to have a space to show off my skills and an excuse to learn new things.&lt;/p&gt;

&lt;p&gt;I've used Gatsby, React, various Firebase SDKs and APIs, to add a blog and portfolio of web apps to the site. It is place for me play and learn.&lt;/p&gt;

&lt;p&gt;The only thing I wished I had done more of while making this little teal wonderland, is blogging. Sharing the joys and frustrations of various decisions and whims I made along the way. It is my goal for this year to make more tutorials and blog posts about the random little projects I made.&lt;/p&gt;

&lt;p&gt;Not everything needs to be polished right away. The process is just as important as the final product.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why I prefer rem over em</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Tue, 11 Feb 2020 22:53:12 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/why-i-prefer-rem-over-em-1obn</link>
      <guid>https://forem.com/geekgalgroks/why-i-prefer-rem-over-em-1obn</guid>
      <description>&lt;p&gt;I always prefer to use rem units over em units in CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definitions
&lt;/h2&gt;

&lt;p&gt;Rem represents the root element's font-size. The root element for a HTML document is almost always the &lt;code&gt;html&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Em represents the current element's font-size. This value can and does change while CSS rules are being applied.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I prefer rem
&lt;/h2&gt;

&lt;p&gt;There are two main reasons why I prefer rems:&lt;/p&gt;

&lt;h3&gt;
  
  
  Rem's value does not change in a CSS rule
&lt;/h3&gt;

&lt;p&gt;Take for example this rule with inherited font-size 18px:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.page&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;How many pixels wide is the font-size?&lt;/li&gt;
&lt;li&gt;How many pixels wide is the margin?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are both 36px wide even those the number of ems are different!&lt;/p&gt;

&lt;p&gt;This is because the font-size for the element changed and all properties calculated after that will use this new font-size.&lt;/p&gt;

&lt;p&gt;In comparison, the same rule with rems instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.page&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;How many pixels is the font-size?&lt;/li&gt;
&lt;li&gt;How many pixels wide is the margin?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The font-size is 36px and margins are width 18px.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rems scale the whole page
&lt;/h3&gt;

&lt;p&gt;If a user sets their browser or personal css file to use a larger base font, it is inherited everywhere!&lt;/p&gt;

&lt;p&gt;The browser/personal css is the last file in the cascade (minus anything styled directly on an element).&lt;/p&gt;

&lt;p&gt;Don't fight users. They know what font-size works best for their eyes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not breaking the flow
&lt;/h2&gt;

&lt;p&gt;Having a website that can work with this kind of resizing requires using other &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/length"&gt;CSS length units&lt;/a&gt; like percentages and viewport height/width (vh &amp;amp; vw). Using these other units will help to keep the rhythm and flow of the page similar at vastly different base font-sizes.&lt;/p&gt;

&lt;p&gt;It is important to remember not everyone uses super high resolution monitors or the same resolution as you.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>Crafting as Code</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Tue, 04 Feb 2020 19:27:55 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/crafting-as-code-2f1i</link>
      <guid>https://forem.com/geekgalgroks/crafting-as-code-2f1i</guid>
      <description>&lt;p&gt;Knitting and crocheting has had a recent comeback. I can be spotted knitting at conferences and meetups. What makes knitting so similar to programming?&lt;/p&gt;

&lt;p&gt;The patterns!&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello World
&lt;/h2&gt;

&lt;p&gt;Squares and rectangles are the "Hello world" of knitting and crochet. These first projects are easy to travel with, often use a small number of stitches, and can be completed quickly enough to give the crafter an idea if they want to keep making things.&lt;/p&gt;

&lt;h2&gt;
  
  
  A typical dishcloth pattern
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.ravelry.com/patterns/library/grammas-dishcloth-grandmothers-2nd-favorite"&gt;Gramma's Dishcloth&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cast on 3 stitches.
Knit one row.
K1, Kf&amp;amp;b, K1.
*K2, yo, knit to end.* (For no holes,*K1, kf&amp;amp;b, knit to end.*)
Repeat from * to * until there are 45 stitches on your needle.
K2, yo, k2tog, knit to end; repeat this row two more times.
(Knit three rows plain for no holes.)
^K1, ssk, yo, k2tog, knit to end.^ (For no holes,^K2, k2tog, K to end.^)
Repeat from ^ to ^ until 5 stitches remain.
K2, k2tog, K1.
K1, k2tog, K1 (3 stitches remain).
Bind off, cut yarn and weave in ends at corners.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Knitting is manipulating arrays
&lt;/h2&gt;

&lt;p&gt;Each row is an array carried on a needle. Stitches are popped, spliced, and shifted as per the pattern.&lt;/p&gt;

&lt;p&gt;Knitting patterns are code!&lt;/p&gt;

&lt;h3&gt;
  
  
  Stitches are array methods
&lt;/h3&gt;

&lt;p&gt;In knitting there are two main stitches: knit and purl. They are made in opposite directions of each other (knit is in front of the needle, purl is in the back). They can be thought of as 1's and 0's.&lt;/p&gt;

&lt;p&gt;All the different knitting patterns and stitches are combinations of these two stitches.&lt;/p&gt;

&lt;p&gt;The above pattern has five different stitches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;K: knit&lt;/li&gt;
&lt;li&gt;Kf&amp;amp;b: knit the front and the back of the same stitch&lt;/li&gt;
&lt;li&gt;K2tog: knit two stitches together&lt;/li&gt;
&lt;li&gt;Ssk: Slip, slip, knit (this also knits two stitches together)&lt;/li&gt;
&lt;li&gt;yo: Yarn over&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knitting is usually completed from left to right and then the work is flipped over. A pair of rows acts like a stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  A dishcloth is a multidimensional array
&lt;/h2&gt;

&lt;p&gt;In the pattern above, think of each row as an array. Each row is pushed into our parent array and knit stitches will be represented by a 1. All code is in JavaScript.&lt;/p&gt;

&lt;p&gt;Casting on is creating our initial array with three stitches.&lt;br&gt;
&lt;code&gt;var dishcloth = []&lt;br&gt;
 dishcloth.push(Array(3))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Knit one row: This is filling the first array.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dishcloth[0].fill(1)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The dishcloth isn't very impressive at this point:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
[[1, 1, 1,]]&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;K1, Kf&amp;amp;b, K1: Knit one stitch, knit and insert a stitch into our array, then knit the last stitch.&lt;br&gt;
&lt;code&gt;dishcloth.push([1,1,1,1])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;K2, yo, knit to end: This is our first loop. Knit two stitches, insert a null value, and knit to the end.&lt;br&gt;
&lt;code&gt;dishcloth.push([1, 1, null, 1, 1])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Repeat from * to * until there are 45 stitches on your needle: Repeat the previous row until the array length is 45.&lt;/p&gt;

&lt;p&gt;Here is an example of code that could be written for the instructions so far.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/jennz/embed/abOoLJY?height=600&amp;amp;default-tab=js,result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;You may notice that I reversed the odd numbered rows, I did this to represent how a pair of rows in knitting acts like a stack. This is how the dishcloth gets a decorative edge on both sides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other similarities to programming
&lt;/h2&gt;

&lt;p&gt;Patterns can require stitches to be knit out of order (cabling), be multithreaded (knitting with multiple colors at the same time), and use different languages (decorative crochet borders).&lt;/p&gt;

&lt;p&gt;There are several kinds of debugging. Two popular ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frogging: Is removing one or more rows (it is called frogging because you "rip it, rip it", knitters love bad puns). &lt;/li&gt;
&lt;li&gt;Tinking: is knitting backwards to the bad stitch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Patterns can be written out or drawn as charts.&lt;/p&gt;

&lt;p&gt;Every knitter has their own opinion on the best tools, e.g. wood, acrylic, or metal needles and yarns. It can be compared to the language and editor wars.&lt;/p&gt;

&lt;p&gt;Knitting is code, you just have to learn the language first.&lt;/p&gt;




&lt;p&gt;Photo by Rebecca Grant on Unsplash&lt;/p&gt;

</description>
      <category>knitting</category>
      <category>programming</category>
    </item>
    <item>
      <title>My code review checklist</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Wed, 04 Dec 2019 23:39:24 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/my-code-review-checklist-122l</link>
      <guid>https://forem.com/geekgalgroks/my-code-review-checklist-122l</guid>
      <description>&lt;p&gt;I like reviewing code. I often learn new tips or tricks whenever I review. It doesn't matter if it expert or novice code, I learn from each.&lt;/p&gt;

&lt;p&gt;Keeping track of everything can be difficult when doing reviews, so here is my personal list of things I check for.&lt;/p&gt;

&lt;h2&gt;
  
  
  My reviewing code checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Spelling: Spelling things correctly helps others search the code more effectively when debugging, refactoring, or writing enhancements.&lt;/li&gt;
&lt;li&gt;Transposed text: If it is a content update, I double check the source material.&lt;/li&gt;
&lt;li&gt;Links: Any relative links? Absolute links? SSL? The correct answer depends on the project but I always click through to check that they work.&lt;/li&gt;
&lt;li&gt;Overly complex code: I do not like &lt;a href="https://en.wikipedia.org/wiki/Code_golf"&gt;code golf&lt;/a&gt;. Writing overly elegant code just makes it more brittle and harder to support. If I can’t understand it at 3am during an incident, I want it refactored or documented thoroughly.&lt;/li&gt;
&lt;li&gt;Does it meet code standards? I like code standards. If everyone knows what the requirements are, the more likely code will be accepted. Also known as, "Argue once over tabs versus spaces, decide as a team, and WRITE IT DOWN."&lt;/li&gt;
&lt;li&gt;Comments: Format and length depends on the team (see code standards). Some teams prefer commit messages or READMEs over comments in code. This step could be renamed, "Is it complex or messy but documented?"&lt;/li&gt;
&lt;li&gt;Security: Is this resource only for admins or editors? How is that checked? Are inputs sanitized?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Actions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run the linter: Sometime people forget.&lt;/li&gt;
&lt;li&gt;Run the tests: Yes, even if there is automated testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run the code: Did the change work?&lt;/li&gt;
&lt;li&gt;Check existing behavior: Does this change affect something else unrelated? I usually check only two or three key paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If everything looks good, I approve the pull request.&lt;/p&gt;

&lt;p&gt;Reviewing code shouldn't be a chore. It is a great learning opportunity for everyone.&lt;/p&gt;

</description>
      <category>review</category>
      <category>test</category>
    </item>
    <item>
      <title>Demystifying Docker Compose</title>
      <dc:creator>Jenn</dc:creator>
      <pubDate>Thu, 21 Nov 2019 23:45:28 +0000</pubDate>
      <link>https://forem.com/geekgalgroks/demystifying-docker-compose-b62</link>
      <guid>https://forem.com/geekgalgroks/demystifying-docker-compose-b62</guid>
      <description>&lt;p&gt;My latest project used Rails and Postgres. After fighting my Mac and the Postgres install for an hour, I gave up and used &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;Docker has always seemed kinda mystical to me and I really wanted to understand my setup this time instead of just copying a quick start. And since there is no better way to learn something than to teach it, I will share it with you!&lt;/p&gt;

&lt;p&gt;There are three key files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dockerfile&lt;/li&gt;
&lt;li&gt;docker-compose.yml&lt;/li&gt;
&lt;li&gt;database.yml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And two main commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker-compose build&lt;/li&gt;
&lt;li&gt;docker-compose up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will need to have Docker installed.&lt;/p&gt;

&lt;p&gt;I prefer to use the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker"&gt;Docker extension&lt;/a&gt; for &lt;a href="https://code.visualstudio.com/"&gt;VSCode&lt;/a&gt; to interact with my running containers, so there are more commands you may need if you are not using that extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerfile
&lt;/h2&gt;

&lt;p&gt;The Dockerfile is where the dependencies for your container are defined. I'll go through mine line by line.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;FROM ruby:2.6.3&lt;/code&gt;&lt;br&gt;
This is from what &lt;a href="https://hub.docker.com/search/?type=image"&gt;image&lt;/a&gt; it is starting from, in this case the Ruby 2.6.3 image.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN apt-get update -qq &amp;amp;&amp;amp; apt-get install -y nodejs postgresql-client &lt;br&gt;
RUN gem install bundler&lt;/code&gt;&lt;br&gt;
Docker containers are VERY bare bones and the next lines were installing Nodejs, a Postgres client, and Bundler.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WORKDIR /my-api&lt;/code&gt;&lt;br&gt;
This should be the folder where all your application code is in. It is setting the working directory so the next commands are easier.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY Gemfile /my-api/Gemfile&lt;br&gt;
COPY Gemfile.lock /my-api/Gemfile.lock&lt;/code&gt;&lt;br&gt;
Copying the Gemfiles to the working space in the container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RUN bundle install&lt;/code&gt;&lt;br&gt;
This will run bundler in the container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY . /my-api&lt;/code&gt;&lt;br&gt;
Copy all the other files into the working space.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;COPY Docker/database.yml config/database.yml&lt;/code&gt;&lt;br&gt;
This line can be skipped. The project was shared with others who had local installations of Postgres and I didn't want to override the config/database.yml in the repository. I created a Docker folder for my specific database.yml file. This command copies my database.yml file to the standard location for the container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EXPOSE 3000&lt;/code&gt;&lt;br&gt;
Only open the ports you need to have open.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Start the main process.&lt;br&gt;
CMD ["rails", "server", "-b", "0.0.0.0"]&lt;/code&gt;&lt;br&gt;
Start rails.&lt;/p&gt;

&lt;p&gt;So for this Rails and Postgres project, my full Dockerfile looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ruby:2.6.3
RUN apt-get update -qq &amp;amp;&amp;amp; apt-get install -y nodejs postgresql-client 
RUN gem install bundler
WORKDIR /my-api
COPY Gemfile /my-api/Gemfile
COPY Gemfile.lock /my-api/Gemfile.lock
RUN bundle install
COPY . /my-api
COPY Docker/database.yml config/database.yml 
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  docker-compose.yml
&lt;/h2&gt;

&lt;p&gt;This file connects our newly defined Rails container to a Postgres database.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;version: '3'&lt;/code&gt;&lt;br&gt;
All docker-compose files start with this. Don't change the version, just leave it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;services:&lt;/code&gt;&lt;br&gt;
This defines the services (aka containers) to be built and how they are connect.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;db:&lt;br&gt;
    image: postgres&lt;/code&gt;&lt;br&gt;
The first service is a Postgres database. This is pulling the base Postgres image. I do not need any add-ons or customizations for my database so a Dockerfile for this service isn't necessary.&lt;br&gt;
If you want data to persist, you will have to add a volume.&lt;br&gt;
&lt;code&gt;volumes:&lt;br&gt;
      - ./tmp/db:/var/lib/postgresql/data&lt;/code&gt;&lt;br&gt;
I didn't need persistent storage for this project so I didn't use a volume.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;web:&lt;/code&gt;&lt;br&gt;
The next service is the Rails web server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`build: .`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Build the image from our newly defined Dockerfile.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;command: bash -c "rm -f tmp/pids/server.pid &amp;amp;&amp;amp; bundle exec rails s -p 3000 -b '0.0.0.0'"&lt;/code&gt;&lt;br&gt;
Run this bash command in our new service container. This command is removing the pid file and telling rails what ports to use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`ports:
  - "3000:3000"`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Mapping the Rails service port 3000 to my local machine's 3000.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`depends_on:
  - db`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This service will not start until the database service has started.&lt;/p&gt;

&lt;p&gt;Once again, a &lt;code&gt;volumes&lt;/code&gt; line could be here but I didn't need it.&lt;/p&gt;

&lt;p&gt;All together my docker-compose.yml looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash -c "rm -f tmp/pids/server.pid &amp;amp;&amp;amp; bundle exec rails s -p 3000 -b '0.0.0.0'"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  database.yml
&lt;/h2&gt;

&lt;p&gt;This is the database connection file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
  &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unicode&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-api_development&lt;/span&gt;


&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-api_test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  docker-compose build
&lt;/h2&gt;

&lt;p&gt;This command builds the containers. With my set up, anytime I update the Gemfile I have to rebuild.&lt;/p&gt;

&lt;p&gt;Building can take some time. Each line in the docker-compose.yml is a step in the building process that can be cached so each build afterwards is faster.&lt;/p&gt;

&lt;p&gt;Example output from a quick rebuild.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose build
db uses an image, skipping
Building web
Step 1/11 : FROM ruby:2.6.3
 ---&amp;gt; d529acb9f124
Step 2/11 : RUN apt-get update -qq &amp;amp;&amp;amp; apt-get install -y nodejs postgresql-client
 ---&amp;gt; Using cache
 ---&amp;gt; 32e807d05964
Step 3/11 : RUN gem install bundler
 ---&amp;gt; Using cache
 ---&amp;gt; 0c1c757c6905
Step 4/11 : WORKDIR /my-api
 ---&amp;gt; Using cache
 ---&amp;gt; b305131f9c6b
Step 5/11 : COPY Gemfile /my-api/Gemfile
 ---&amp;gt; Using cache
 ---&amp;gt; 657fc337d0c4
Step 6/11 : COPY Gemfile.lock /my-api/Gemfile.lock
 ---&amp;gt; Using cache
 ---&amp;gt; 09154df316d8
Step 7/11 : RUN bundle install
 ---&amp;gt; Using cache
 ---&amp;gt; 7915826ca085
Step 8/11 : COPY . /my-api
 ---&amp;gt; 4ff482944c8e
Step 9/11 : COPY Docker/database.yml config/database.yml
 ---&amp;gt; 101f814ba0bc
Step 10/11 : EXPOSE 3000
 ---&amp;gt; Running in 45f91676a8f3
Removing intermediate container 45f91676a8f3
 ---&amp;gt; 9426b9097b78
Step 11/11 : CMD ["rails", "server", "-b", "0.0.0.0"]
 ---&amp;gt; Running in 3f262f9679a2
Removing intermediate container 3f262f9679a2
 ---&amp;gt; a056b93d4bc8
Successfully built a056b93d4bc8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  docker-compose up
&lt;/h2&gt;

&lt;p&gt;This command starts the containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up
Starting my-api_db_1 ... done
Recreating my-api_web_1 ... done
Attaching to my-api_db_1, my-api_web_1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then visit &lt;code&gt;localhost:3000&lt;/code&gt; in a browser to confirm it is up and running.&lt;/p&gt;

&lt;p&gt;For more information, check out the official &lt;a href="https://docs.docker.com/compose/rails/"&gt;quickstart&lt;/a&gt; guide for Rails.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>docker</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
