<?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>DEV Community: crespire</title>
    <description>The latest articles on DEV Community by crespire (@crespire).</description>
    <link>https://dev.to/crespire</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%2F761596%2F7c432c71-f294-4b80-a365-ed0f16c39fc4.jpg</url>
      <title>DEV Community: crespire</title>
      <link>https://dev.to/crespire</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/crespire"/>
    <language>en</language>
    <item>
      <title>One Year of Toronto Ruby</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Sat, 26 Oct 2024 01:48:48 +0000</pubDate>
      <link>https://dev.to/crespire/one-year-of-toronto-ruby-147</link>
      <guid>https://dev.to/crespire/one-year-of-toronto-ruby-147</guid>
      <description>&lt;p&gt;&lt;strong&gt;Editor's Note:&lt;/strong&gt; This blog post was also published on my &lt;a href="https://crespire.dev/blog/2024/10/25/one-year-of-toronto-ruby/" rel="noopener noreferrer"&gt;personal blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As we approach November, it's hard to believe that it has been almost one year since our very first Toronto Ruby meetup! It all began as an idea to rebuild the local Ruby meetup scene, and has evolved into a dynamic and vibrant community effort that's met almost every month throughout 2024! It's been such a privilege to be at the helm of this effort, and it's been a rewarding journey filled with learning, collaboration, excitement and a whole lot of passion for the local Ruby and Rails community. I'm grateful for how far we've come, and I'm very excited for what's still to ahead.&lt;/p&gt;

&lt;p&gt;We've been really fortunate to be able to host events with a regular cadence and have been able to attract a great audience, as well as excellent speakers presenting a wide range of Ruby and Rails related topics. From newcomers to seasoned developers, it’s been inspiring to see such a diverse group come together each month. The enthusiasm and passion that everyone brings has been a driving force behind this growing community. Seeing everyone connect, share ideas and learn from others has definitely been a highlight!&lt;/p&gt;

&lt;p&gt;Reaching the one year mark is a testament to the community and everyone who has taken the time to attend and support our events. I also want to give a special shoutout to our speakers and presenters, who have freely shared their expertise and interests with the group. The meetup would not be as lively and interesting if you all didn't step up to share!&lt;/p&gt;

&lt;p&gt;As we look ahead, I'm excited about where we'll go, the places we'll see and all the new and interesting things we'll learn together. Hopefully, we'll have more varied topics and formats, as well as more community events.&lt;/p&gt;

&lt;p&gt;As for our one year anniversary, plans are afoot! If you want to come celebrate with us, join our mailing list at &lt;a href="https://toronto-ruby.com/" rel="noopener noreferrer"&gt;Toronto Ruby's website&lt;/a&gt; and we'll announce something when we're ready!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>meetups</category>
    </item>
    <item>
      <title>Launching Toronto Ruby</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Thu, 02 Nov 2023 01:35:19 +0000</pubDate>
      <link>https://dev.to/crespire/launching-toronto-ruby-3mh7</link>
      <guid>https://dev.to/crespire/launching-toronto-ruby-3mh7</guid>
      <description>&lt;p&gt;I'm so pleased to announce that we have our inaugural meeting booked and we are excited to see you! You can learn more about our event and sign up for our events mailing list at &lt;a href="https://toronto-ruby.com/"&gt;https://toronto-ruby.com/&lt;/a&gt; - I hope you'll consider joining us!&lt;/p&gt;

&lt;p&gt;I'm so excited to write this as it's been something I've dreamed about since I started my professional journey with Ruby and Rails.&lt;/p&gt;

&lt;p&gt;The one thing I know for sure, in tech, a community can take you very far! Whether you're learning or working, community is place where you can recharge, learn and grow. Finding like-minded folks to learn and grow_with_ is an integral part of sustained and happy professional growth.&lt;/p&gt;

&lt;p&gt;Without a community, new techniques or ideas may not come across your desk as much. You might not have an outlet to share and learn about the problems you're facing day to day, or to share your ideas about that one really cool feature that you've been working on.&lt;/p&gt;

&lt;p&gt;As a new Ruby dev, I was very disheartened to learn that the local Toronto Ruby community had been inactive for quite some time and I craved this kind of connection.&lt;/p&gt;

&lt;p&gt;I am so eager to connect with folks who love and use Ruby every day! I'm grateful to have the support of so many wonderful folks to launch this venture. The language is still alive and well, and while I felt alone, I am hoping that my big dream will help others. It's time we said it: We're here and we love Ruby, &lt;em&gt;still&lt;/em&gt;. Yes, in 2023!&lt;/p&gt;

&lt;p&gt;Our vision is to provide a place for the GTA Ruby community to flourish, learn and grow together; be a place where folks can make friends and find others that love Ruby!&lt;/p&gt;

&lt;p&gt;I'm really looking forward to seeing folks at our inaugural meetup, and I hope you'll consider joining us for more of our future events.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>meetups</category>
    </item>
    <item>
      <title>Hooking in to Devise controller actions</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Thu, 17 Aug 2023 16:25:26 +0000</pubDate>
      <link>https://dev.to/crespire/hooking-in-to-devise-controller-actions-3pk2</link>
      <guid>https://dev.to/crespire/hooking-in-to-devise-controller-actions-3pk2</guid>
      <description>&lt;p&gt;Devise is a fantastic authentication gem in the Rails ecosystem that I'm learning more about every day!&lt;/p&gt;

&lt;p&gt;I wanted to share how I was able to solve a problem effectively after reading some Devise source code, as it might come in handy for others.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I was working on a project using the Devise addition &lt;code&gt;invitable&lt;/code&gt; and I needed a way to update a user's profile after they accepted an invite so I could set the slug based on their name. When the user invite is created, we use a generic value as a filler for the name.&lt;/p&gt;

&lt;p&gt;I had originally used an ActiveRecord callback, but this felt clunky. Not only was it difficult to understand what it was doing, the side effect of the callback happened on the &lt;code&gt;Profile&lt;/code&gt; model! That would be hard to debug if it was your first time working on the app and something was going wrong with the Profile's slug generation.&lt;/p&gt;

&lt;p&gt;I began searching for ways to do what I wanted without callbacks and I decided to take a look at the Devise controllers source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solve
&lt;/h2&gt;

&lt;p&gt;It turns out, Devise anticipates that you might want to access a resource while it's doing its thing, and so almost every Devise controller action has a line like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;block_given?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fantastic! Because we often inherit from the base Devise controllers, what this line does is let you access the resource under operation if you pass in a block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# sample controller&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Users::RegistrationsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Devise&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RegistrationsController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a game changer! I now had a great way to update the profile's slug with the user's provided name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/controllers/users/registrations_controller.rb&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
  &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;slug: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parameterize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like that, I was able to get rid of an ActiveRecord callback! So, if you ever need to access the resource to do something inside a Devise controller, this is your ticket.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>devise</category>
      <category>todayilearned</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Dockerizing a Simple Rails Application for Deployment</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Sun, 08 Jan 2023 21:28:25 +0000</pubDate>
      <link>https://dev.to/crespire/dockerizing-a-simple-rails-application-for-deployment-3931</link>
      <guid>https://dev.to/crespire/dockerizing-a-simple-rails-application-for-deployment-3931</guid>
      <description>&lt;p&gt;New to docker and docker compose? It can be a lot to figure out, and while there are lot of great resources out there (at least 3 different articles on how to dockerize an existing Rails application!), many of them are fairly tearse or assume a very high level of comfort.&lt;/p&gt;

&lt;p&gt;For me, I knew the high level &lt;em&gt;why&lt;/em&gt; behind docker, and even conceptually how it worked, but when I got into the weeds, I quickly got lost. So, dear reader, I thought I'd try to take a stab about explaining how to dockerize a Rails app, trying to fill in the gaps from the resources I was able to take in plus a lot of helpful guidance from various programming communities (Shoutout to Dave Kimura on Rails Link slack!).&lt;/p&gt;

&lt;h2&gt;
  
  
  What is docker?
&lt;/h2&gt;

&lt;p&gt;Docker is a way to containerize applications so that they are easy and portable to run or deploy. The basic idea is that you can build an &lt;em&gt;image&lt;/em&gt; based on some application code, which is then the basis for the container that later &lt;em&gt;runs&lt;/em&gt; the code.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Dockerfile&lt;/code&gt; is the first thing you'll likely come across when trying to containerize an application. Docker has an excellent documentation site that goes over the details of the many options available. But what made the &lt;code&gt;Dockerfile&lt;/code&gt; intelligible to me was to think of it as the &lt;em&gt;compile&lt;/em&gt; step. The &lt;code&gt;Dockerfile&lt;/code&gt;, in essence, is the recipe for how to assemble and set up your image, which is then run inside of a container. So, thinking about the &lt;code&gt;Dockerfile&lt;/code&gt; as the recipe that forms the basis of your image where you can define things like environment variables, what sorts of software you should also have available in the container when it's running, etc. All of that is defined in your &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One of the things I had trouble with when trying to figure out docker was how I should set up the container &lt;em&gt;after it's spun up&lt;/em&gt;. Some of the tasks I wanted to accomplish (like creating and migrating the database for a Rails app, for example) seemed to belong to the "setup" stage, and so I initially tried to put them in the &lt;code&gt;Dockerfile&lt;/code&gt;. This was not the right way to go about it, though I was somewhat close. You see, the trouble is when you are &lt;em&gt;building&lt;/em&gt; an image, the image under construction doesn't (and shouldn't!) have access to any other image. That means, you don't really have access to the database instance, as it isn't running! Remember, this is because the &lt;code&gt;Dockerfile&lt;/code&gt; is the recipe for building the image, and can be thought of like &lt;em&gt;compile&lt;/em&gt; time. It's not really compiling in the classical sense, but it's somewhat analogous. In that case, then, something like setting up the database could be thought of as a &lt;em&gt;run time&lt;/em&gt; concern. It requires that the database is available before we attempt to use it.&lt;/p&gt;

&lt;p&gt;Well, Docker does provide a handy &lt;code&gt;ENTRYPOINT&lt;/code&gt; option in the &lt;code&gt;Dockerfile&lt;/code&gt; and that is one of the ways you can solve this compile/run time, but both are setup tasks kind of issue. There are better tutorials out there than what I can offer, but all you need to do is make sure to include an entry point script inside the image, make sure it has execution privileges, and you can put setup commands in there. We'll re-visit &lt;code&gt;ENTRYPOINT&lt;/code&gt; in depth later in this article.&lt;/p&gt;

&lt;p&gt;So, that's the theory. Let's talk about how that applies to an actual Rails app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the Rails!
&lt;/h2&gt;

&lt;p&gt;Let's talk a little bit about what sort of app we're going to be setting up. The application I have in mind is a Rails 7 monolith that uses a PostgreSQL database to store data. The application that serves as the sample for this post won't include redis. As I said, it's a &lt;em&gt;simple&lt;/em&gt; Rails app.&lt;/p&gt;

&lt;p&gt;So, with that said, let's say we have a Rails app that fits the bill. You can run &lt;code&gt;rails s&lt;/code&gt; and puma dutifully fires up in development mode, and you can visit the app at &lt;code&gt;localhost:3000&lt;/code&gt; and it talks to a PostgreSQL database and everything works as you expect - great! Let's see if we can dockerize it!&lt;/p&gt;

&lt;p&gt;So, how do we go about containerizing this application? The first step is to set up a &lt;code&gt;Dockerfile&lt;/code&gt; which describes the recipe to make the entree, as it were.&lt;/p&gt;

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

&lt;p&gt;Here is a sample file from my simple application which we will talk through step by step after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Specify base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ruby:3.0.2&lt;/span&gt;

&lt;span class="c"&gt;# Add application code&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; . /rails-app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /rails-app&lt;/span&gt;

&lt;span class="c"&gt;# Install utilities&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;nano

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Precompile assets - only required for non-API apps&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;rake assets:precompile

&lt;span class="c"&gt;# Set up env&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_SERVE_STATIC_FILES true&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_LOG_TO_STDOUT true&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./bin/entrypoint.sh /usr/bin/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/bin/entrypoint.sh
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["entrypoint.sh"]&lt;/span&gt;

&lt;span class="c"&gt;# Expose port&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;

&lt;span class="c"&gt;# Run server when container starts&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["rails", "server", "-b", "0.0.0.0"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whew, that was a lot. Okay, so what does this file mean?? Let's break it down.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ruby:3.0.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line is simple, it specifies the &lt;em&gt;base image&lt;/em&gt; from which we build our final image. Here, because the application runs on Ruby 3.0.2, we specify that ruby version as a base image. These base images are maintained by folks much smarter than I, and you can check out all of the details on Docker Hub, which hosts these images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; . /rails-app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /rails-app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two lines work in concert to set the container up. The first line &lt;code&gt;ADD&lt;/code&gt; copies files from your local file system (notice the &lt;code&gt;.&lt;/code&gt; which means "this directory") to the container's file system: &lt;code&gt;/rails-app&lt;/code&gt;. After we've copied our code over, we set the container's working directory to what we just set up: &lt;code&gt;WORKDIR /rails-app&lt;/code&gt;. Right, simple so far!&lt;/p&gt;

&lt;p&gt;The next two lines are optional, but I find value in having &lt;code&gt;nano&lt;/code&gt; inside the container so I can edit stuff if required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;nano
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, we run these two shell commands to install the &lt;code&gt;nano&lt;/code&gt; command line editor inside the container.&lt;/p&gt;

&lt;p&gt;The next two lines deal with our Rails app specifically, but we just run the two commands as if we were in the working directory of the application on container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;rake assets:precompile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commands first install the gems we've specified in our &lt;code&gt;Gemfile&lt;/code&gt; which was copied from our host directory, and then precompiles assets. Note that this line is only required if you have assets in your Rails app (CSS, Javascript, etc). For API only applications, this line should be commented out.&lt;/p&gt;

&lt;p&gt;The next set of lines set up a few environment variables we want to use and are pretty straight forward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_ENV production&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_SERVE_STATIC_FILES true&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_LOG_TO_STDOUT true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One note is that it is typically a good idea when using docker to log to a container's &lt;code&gt;stdout&lt;/code&gt; because that enables us to use logging aggregation drivers. It's a bit more of an advanced usage, but docker offers us ways to easily access a container's &lt;code&gt;stdout&lt;/code&gt; and having logs go there mean we can use built-in docker methods to handle our logging in concert with the other containers.&lt;/p&gt;

&lt;p&gt;The next set of lines are interesting, and took me a bit to understand. Here they are again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./bin/entrypoint.sh /usr/bin/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/bin/entrypoint.sh
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for completeness sake, here is the entrypoint.sh that I am using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Exit on any error&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Remove a potentially pre-existing server.pid for Rails.&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ./tmp/pids/server.pid

&lt;span class="c"&gt;# Make sure db is ready to go&lt;/span&gt;
&lt;span class="c"&gt;# Adding '2&amp;gt;/dev/null' sends output to nowhere in the case of an error and the&lt;/span&gt;
&lt;span class="c"&gt;# error code also triggers the bash OR to run db:setup&lt;/span&gt;
bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails db:migrate 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails db:setup
bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails ds:update_clients

&lt;span class="c"&gt;# Then exec the container's main process (CMD in the Dockerfile).&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, taken together, the Dockerfile lines copy the script from the current project directory, and copies it to the &lt;em&gt;container's&lt;/em&gt; &lt;code&gt;/usr/bin/&lt;/code&gt; folder, and then sets the executable permission on it with &lt;code&gt;RUN chmod +x /usr/bin/entrypoint.sh&lt;/code&gt; so that the script is executable when the container is started. As the name implies, this script then serves as the entry into our container when it's running. That is to say, this script will run whenever we enter the container to run a command, or when it it is started up.&lt;/p&gt;

&lt;p&gt;Remember earlier how I had trouble setting up the database inside the &lt;code&gt;Dockerfile&lt;/code&gt; and kept failing? This is how you can overcome that. We set up this "additional instructions" inside the recipe, but they only make sense once we run the container. It's like serving gravy on the side with a meal with instructions. If we put the gravy on before serving it, the whole thing might get too soggy. Or maybe some guests want more or less gravy. So we put it on the side and let the guest know that they can use as much or as little gravy as they want when the time comes to eat. The &lt;code&gt;entrypoint&lt;/code&gt; allows us to provide additional instructions that get executed when the image is run as a container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The docker &lt;code&gt;ENTRYPOINT&lt;/code&gt; configuration is actually much more complicated than I have alluded to here. The goal of this section (and the entire article) is not a deep dive into docker, but intended to be a first brush with docker to make it intelligible for those dipping their toes in. I know my explanation of &lt;code&gt;ENTRYPOINT&lt;/code&gt; in particular likely falls short of explaining it in depth. That being said, the goal here is to get someone from zero knowledge to 1 containerized application, not full docker mastery.&lt;/p&gt;

&lt;p&gt;The contents of the &lt;code&gt;entrypoint.sh&lt;/code&gt; is pretty straight forward and is annotated for your learning.&lt;/p&gt;

&lt;p&gt;The next line simply tells docker that traffic on port 3000 of the container should be forwarded inside the container (so our Rails server can serve the request).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the last line!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["rails", "server", "-b", "0.0.0.0"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what is run when the container is spun up, and it's just a regular old Rails command with some parameters.&lt;/p&gt;

&lt;p&gt;With that, we have the Rails app mostly in order as far as Docker is concerned. Often times the resources I initially pursued would then jump right into docker compose, and that is indeed what we'll be doing in a beat, but the one thing that was missing for me was why. Looking back, we can see that our Rails &lt;code&gt;Dockerfile&lt;/code&gt; was mostly concerned with our Rails app. Up until then, we have just &lt;em&gt;assumed&lt;/em&gt; we'll have a database available. And when we are in our local development environment, that's usually the case. Your machine probably has a PostgreSQL server service running the background and all you have to worry about is setting up the Rails app.&lt;/p&gt;

&lt;p&gt;And yet, the Rails application is useless without its PostgreSQL companion. Remember how I said previously that the &lt;code&gt;Dockerfile&lt;/code&gt; is like the recipe for an image that will be run later? It turns out that there are recipes out there for PostgreSQL as well. What's the relevance for us? Well, if we set up our Rails recipe, and we can borrow a PostgreSQL recipe, we should be able to put them together to make them work in concert. And that's what docker compose does.&lt;/p&gt;

&lt;p&gt;By way of analogy, a Dockerfile is like a recipe for a particular dish, while using docker compose and its &lt;code&gt;docker-compose.yml&lt;/code&gt; is like setting the menu with its courses and their order.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Compose
&lt;/h2&gt;

&lt;p&gt;Okay, so what's up with this docker compose situation? Well, if we think about each section of our application as a container, then we have already successfully set up our Rails container. But as I mentioned before, we need to have a database for the Rails application to be of any use!&lt;/p&gt;

&lt;p&gt;So, as I said before, if the &lt;code&gt;Dockerfile&lt;/code&gt; is your recipe for each particular menu item, then the &lt;code&gt;docker-compose.yml&lt;/code&gt; is like your menu for the evening. It specifies what entrees are available, and how they relate to each other.&lt;/p&gt;

&lt;p&gt;Let me show you the &lt;code&gt;docker-compose.yml&lt;/code&gt; file I ended up setting up and we can go through it line by line to understand the big picture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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.8'&lt;/span&gt;
&lt;span class="c1"&gt;# Logging config&lt;/span&gt;
&lt;span class="na"&gt;x-logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="nl"&gt;&amp;amp;logging&lt;/span&gt;
  &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local"&lt;/span&gt;
  &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;max-size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5m"&lt;/span&gt;
    &lt;span class="na"&gt;max-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&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;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&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;postgres:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&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;pg_isready -U postgres -h 127.0.0.1&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*logging&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;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&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;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DATABASE_URL}&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;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*logging&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, again, this might be information overload but we'll step through it together.&lt;/p&gt;

&lt;p&gt;The first line is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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.8'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just defines the version of Docker Compose that this file uses. It is a shorthand that maps to a particular version of Docker engine, so that what you write is interpreted correctly. As of this writing (Jan 8, 2023), the current latest version you can specify is "3.8" which is what we've used.&lt;/p&gt;

&lt;p&gt;We are actually going to skip the &lt;code&gt;x-logging&lt;/code&gt; section and come back to later, because it's strictly necessary for getting an app up and running.&lt;/p&gt;

&lt;p&gt;Okay, onto the meat (pun intended) of the file. Let's go over the &lt;code&gt;services&lt;/code&gt; section.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;db&lt;/code&gt; service
&lt;/h3&gt;

&lt;p&gt;Because this is a regular YML file, we'll examine the contents of the &lt;code&gt;services&lt;/code&gt; section's sub-items in turn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;postgres&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&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;postgres:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&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;pg_isready -U postgres -h 127.0.0.1&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*logging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we define a service called &lt;code&gt;db&lt;/code&gt; which, predictably, runs our database. Recall that we can borrow recipes from Docker Hub, and you'll see that we actually do that here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line tells docker compose to borrow the &lt;code&gt;postgres&lt;/code&gt; image and use it to set up the container. Because we don't need anything custom with our PostgreSQL service, we can just use the pre-cooked image.&lt;/p&gt;

&lt;p&gt;The next line includes a little bit of configuration via a &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file actually makes a reprise later on in our Rails section, but what is important to know here is that by using a &lt;code&gt;.env&lt;/code&gt; file we don't store any important information inside our &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;There was one thing that confused me here that I want to call out. Because we are relying on the pre-cooked &lt;code&gt;postgre&lt;/code&gt; image, we should make sure we consult the documentation about what environment variables that image expects in order to function properly. In this case, the &lt;code&gt;postgre&lt;/code&gt; image expects there to be a &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; environment variable, which we have set in our &lt;code&gt;.env&lt;/code&gt; file. The value can be arbitrary, but must be included in the container's environment.&lt;/p&gt;

&lt;p&gt;One alternative to using the &lt;code&gt;env_file&lt;/code&gt; option is to use the &lt;code&gt;environment&lt;/code&gt; option and set each variable manually. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=somepassword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously, this method is a little more straightforward, but the trouble is that now we've included a plain text password for our database in our &lt;code&gt;docker-compose.yml&lt;/code&gt; and &lt;code&gt;docker-compose.yml&lt;/code&gt; itself is a version controlled file. Whether or not that's okay is up to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; You can also use &lt;em&gt;both&lt;/em&gt; if you want. Docker compose has a specific precedence it will follow if there are multiple sources for environment variables, which can be useful to understand: &lt;a href="https://docs.docker.com/compose/envvars-precedence/" rel="noopener noreferrer"&gt;https://docs.docker.com/compose/envvars-precedence/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next line creates or uses a docker volume:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;postgres:/var/lib/postgresql/data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line sets up a named volume &lt;code&gt;postgres&lt;/code&gt; which is then mounted inside the container at the path provided &lt;code&gt;/var/lib/postgresql/data&lt;/code&gt;. Docker will then persist this data to the host via its own magic. For example, the data is persisted to &lt;code&gt;/var/lib/docker/volumes/rails_app/_data&lt;/code&gt; on my local install because that's the application's name. Note that we've named the volume &lt;code&gt;postgres&lt;/code&gt; as we will use it later.&lt;/p&gt;

&lt;p&gt;The next few lines define a few features that are not strictly necessary, so we'll group them together and run through them as a block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&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;pg_isready -U postgres -h 127.0.0.1&lt;/span&gt;
  &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*logging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart specifies the conditions under which a container should automatically restart, here we specify &lt;code&gt;always&lt;/code&gt;. There are some other values as well. With &lt;code&gt;healthcheck&lt;/code&gt;, we define a test and how often it should be run to report on the health of the service. Because we are using PostgreSQL, it comes with a command line option &lt;code&gt;pg_isready&lt;/code&gt; which we run as the &lt;code&gt;postgres&lt;/code&gt; user on the local container. Finally, we set up log rotation via the the "extension fields" feature (which we skipped earlier). We'll talk about the extension field &lt;code&gt;x-logging&lt;/code&gt; in the last section.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;web&lt;/code&gt; service
&lt;/h3&gt;

&lt;p&gt;Okay, that was a quick tour of setting up our &lt;code&gt;db&lt;/code&gt; service. Let's move on to the other service we've specified, &lt;code&gt;web&lt;/code&gt;. Below is a recap of the &lt;code&gt;web&lt;/code&gt; subsection under &lt;code&gt;services&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&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;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&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;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*logging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have most of the same commands except three, which are new. Let's examine them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our web service, instead of using a pre-cooked image from Docker Hub, we've specified that we should &lt;em&gt;build&lt;/em&gt; an image from the current directory. Which means that this &lt;code&gt;docker-compose.yml&lt;/code&gt; will rely on the &lt;code&gt;Dockerfile&lt;/code&gt; to generate an image of our Rails app for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line maps the host post to our container's port. If you'll recall, we set our Rails app to listen on port 3000 in our &lt;code&gt;Dockerfile&lt;/code&gt; so we just make sure that the port is exposed to the host from our container.&lt;/p&gt;

&lt;p&gt;Finally, the last new command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;p&gt;This line does what it says on the tin, it tells Docker compose that we need our &lt;code&gt;db&lt;/code&gt; service to be available before trying to spin up this service.&lt;/p&gt;

&lt;p&gt;That about covers the services section of the &lt;code&gt;docker-compose.yml&lt;/code&gt; there's just one more section to cover:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember how we defined this named volume earlier as part of our &lt;code&gt;db&lt;/code&gt; service? Listing it again here at the top level tells &lt;code&gt;docker-compose&lt;/code&gt; to make the volume available for all services, so that our database can use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;Finally, we have all of our bits and pieces set up in order to use docker and docker compose to deploy this application.&lt;/p&gt;

&lt;p&gt;The final piece that's missing is the logging section. This section uses "extension fields" which start with a &lt;code&gt;x-&lt;/code&gt; that Docker will actually ignore. The reason for this is because we can then use YML anchors to then share configuration across a number of different services.&lt;/p&gt;

&lt;p&gt;In this example, we define a log rotation setup that we want to use across our &lt;code&gt;db&lt;/code&gt; and &lt;code&gt;web&lt;/code&gt; service. Then we use YML anchors to insert that configuration into each service.&lt;/p&gt;

&lt;p&gt;The last bit we have to sort out is how our secrets are configured. We already touched on &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; required by the &lt;code&gt;db&lt;/code&gt; service, but actually, we also require some secrets for the Rails application.&lt;/p&gt;

&lt;p&gt;Chiefly, we require that our Rails application can access &lt;code&gt;config/credentials.enc.yml&lt;/code&gt; which is a version controlled file. Typically, inside that file is the &lt;code&gt;secret_key_base&lt;/code&gt; which is used as the basis for all other encryption for a Rails app.&lt;/p&gt;

&lt;p&gt;If you're starting with a Rails app you've already created, the easy solution is to find the &lt;code&gt;config/master.key&lt;/code&gt; (or an environment specific &lt;code&gt;.key&lt;/code&gt; file if you'd prefer) and stick that value into your &lt;code&gt;.env&lt;/code&gt; so that Rails can read the file required. The rule is: whatever key encrypted your &lt;code&gt;config/credentials.enc.yml&lt;/code&gt; needs to be available inside your container in order to run the application.&lt;/p&gt;

&lt;p&gt;So make sure to add &lt;code&gt;RAILS_MASTER_KEY&lt;/code&gt; to your &lt;code&gt;.env&lt;/code&gt; file so that Rails can decrypt your &lt;code&gt;credentials.enc.yml&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Finally, it is a good idea to set up your Rails database configuration to also use an environment variable for connecting to the database. One easy way to do this is to set a &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable that you can embed the username and password into. This way, you don't have to specify separate variables for user and password.&lt;/p&gt;

&lt;p&gt;The reason for this is that both your containers will run in a virtual network and be able to talk to each other. Because we previously set up a &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; as required by the &lt;code&gt;postgres&lt;/code&gt; image, we can set up a &lt;code&gt;DATABASE_URL&lt;/code&gt; using that password as well for the Rails app inside the &lt;code&gt;web&lt;/code&gt; service to communicate with the &lt;code&gt;db&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;The format for this should be: &lt;code&gt;postgres://postgres:&amp;lt;chosen_password&amp;gt;@db:5432&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then you can add &lt;code&gt;url: &amp;lt;%= ENV['DATABASE_URL'] %&amp;gt;&lt;/code&gt; to the default section of your &lt;code&gt;database.yml&lt;/code&gt; and not have to define separate username and password variables.&lt;/p&gt;

&lt;p&gt;Because our containers run on the same virtual network, we can simply refer them by their service name and Docker will make sure they can resolve internally.&lt;/p&gt;

&lt;p&gt;Okay! Now that we have everything set up, we can see if everything works!&lt;/p&gt;

&lt;p&gt;In your command line, you can run &lt;code&gt;docker compose up -d&lt;/code&gt; and Docker should build and then run your containers. The &lt;code&gt;-d&lt;/code&gt; tells docker to run in detached mode, which will run the containers in the background.&lt;/p&gt;

&lt;p&gt;Here are some other commands to be aware of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker compose stop&lt;/code&gt; will stop the containers related to this application. You have to make sure you're in the project directory where &lt;code&gt;docker-compose.yml&lt;/code&gt; lives for this to work.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker compose run &amp;lt;service&amp;gt;&lt;/code&gt; will pull up logs for the chosen service. There are also options to get live logging, so check it out.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker compose build&lt;/code&gt; will re-build the images that are used to run the containers. Make sure that you use this command if you make any source changes to your Rails application, otherwise the changes will not be captured in the images and the app behaviour won't change as you expect.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running this shebang remotely
&lt;/h2&gt;

&lt;p&gt;Okay, now we have this docker compose setup working on our local development machine. How do we get this all up into the cloud or another server?&lt;/p&gt;

&lt;p&gt;There are a few approaches. The first is the most straight forward but potentially time consuming:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Either via git or a command like &lt;code&gt;scp&lt;/code&gt;, copy the repository over to the target host machine, install docker and and docker compose on the target host, and run the command via ssh. With this method, any changes made to code must be copied to the target host machine, and the images re-built and re-deployed. For a project which changes often, this may not be an ideal approach.&lt;/li&gt;
&lt;li&gt;Another option is to set up a docker context. This means you run your docker commands on a defined docker daemon that may not be local to your current environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Docker has a great blog post on this which is extremely straight forward: &lt;a href="https://www.docker.com/blog/how-to-deploy-on-remote-docker-hosts-with-docker-compose/" rel="noopener noreferrer"&gt;https://www.docker.com/blog/how-to-deploy-on-remote-docker-hosts-with-docker-compose/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this helps, feel free to comment any questions and I will do my best to reply! You can also use docker to set up development environments, so stay tuned for that!&lt;/p&gt;

</description>
      <category>django</category>
      <category>learning</category>
      <category>python</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Did I Learn Ruby on Rails?</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Wed, 04 Jan 2023 17:01:01 +0000</pubDate>
      <link>https://dev.to/crespire/why-did-i-learn-ruby-on-rails-1bp</link>
      <guid>https://dev.to/crespire/why-did-i-learn-ruby-on-rails-1bp</guid>
      <description>&lt;p&gt;"Why did you learn Ruby on Rails in 2022?"&lt;/p&gt;

&lt;p&gt;I heard this while interviewing at a shop trying to get into the industry. The interviewing engineer kind of chuckled while asking me. I suppose it was a fair question, even if the delivery was not great.&lt;/p&gt;

&lt;p&gt;In 2022 (and maybe beyond), node.js is the hot hot along with a whole slew of frontend Javascript frameworks all competing for installs. React and Next.js, Svelte and SvelteKit, vue and Nuxt.js and Angular... all of these are in the news right now, but they're all moving towards how Rails wants you to do things anyway (hydration and server-side rendering are what Rails is particularly good at, especially with the addition of Turbo and Stimulus).&lt;/p&gt;

&lt;p&gt;So, why did I choose to learn Ruby on Rails?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rails is very focused on what it does well. It's opinionated and expects you to do things a certain way. When you're learning an entirely new skill, this kind of structured approach gives you something to grab on to to build your knowledge behind the &lt;em&gt;why&lt;/em&gt; of things. At first, you'll be confused (I know I was), but once you get a level of comfort it's easy to dig into the why.&lt;/li&gt;
&lt;li&gt;Node packages have documentation that is very hit or miss. Some packages have excellent documentation, while others are tearse or missing. For someone new to the domain, it can be a challenge. In my experience with Ruby and Rails, documentation was typically much stronger. It makes navigating the new a little less painful.&lt;/li&gt;
&lt;li&gt;A lot of educational content out there for JS/Node tends to focus on the how and less the why. This is fine and dandy for building applications via assigned Jira tickets, but I found that the resources I consumed while learning Ruby on Rails really focused on both the how and the why. With the foundational knowledge of why Rails does something the way it does, you can gain insights into how other frameworks want you to do something. This provides a lot of value because it is directly building your problem solving skills. Even if I don't end up getting a job as a Rails developer, I have the fundamentals down that enable me to pick up the core "how and why" of a framework, regardless of the language: Go, TypeScript or C#.&lt;/li&gt;
&lt;li&gt;Finding a job is going to be tough no matter what's in your toolbox, so you might as well learn why behind the how. There are a lot of arguments about "well there are many more JS/Node jobs than Rails jobs" and this is objectively true, but there are also that many more people learning JS/Node, so in the end, the level of compete required may be the same.&lt;/li&gt;
&lt;li&gt;Ruby is an awesome language, and I love writing it. It's fun. Because it's fun, I could foresee myself writing Ruby for 20-30 years.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In then end, Ruby and Rails provided me with a strong foundation to go and pick up topics in computer science, whether it's in another language or another framework.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Setting up VSCode to write commit messages like a boss</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Sun, 09 Oct 2022 16:38:37 +0000</pubDate>
      <link>https://dev.to/crespire/setting-up-vscode-to-write-commit-messages-like-a-boss-2kk5</link>
      <guid>https://dev.to/crespire/setting-up-vscode-to-write-commit-messages-like-a-boss-2kk5</guid>
      <description>&lt;p&gt;So you've got your shiny code all ready to push up to remote, and you're about to write a commit message.&lt;/p&gt;

&lt;p&gt;I am sure you've heard all the tips about writing excellent commit messages, like making sure the title is only so long, that the body is only x characters per line and all that fun stuff. Did you know that you can use VSCode to make your commit messages?&lt;/p&gt;

&lt;p&gt;First, configure VSCode as the default editor for git:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git config --global core.editor "code --wait"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command sets git to open up VSCode when you type &lt;code&gt;git commit&lt;/code&gt; without passing in a &lt;code&gt;-m "message"&lt;/code&gt; so that you can provide more context about your commit message. The &lt;code&gt;--wait&lt;/code&gt; part just tells git to wait until the user closes the file before moving on.&lt;/p&gt;

&lt;p&gt;Now, when you type &lt;code&gt;git commit&lt;/code&gt; without &lt;code&gt;-m&lt;/code&gt;, you'll see a file open in your VSCode called &lt;code&gt;COMMIT_EDITMSG&lt;/code&gt; and git will wait until that file is saved and closed before continuing.&lt;/p&gt;

&lt;p&gt;Second, we can actually set up some rulers to help you make sure you stay within the recommended formatting! You'll want to open up your &lt;code&gt;settings.json&lt;/code&gt; and insert these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"[git-commit]"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"editor.rulers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will add two rulers to the editor that will open for any git commit! The ruler is the 50 character limit for the title and the second is the 72 character limit for the body of the commit.&lt;/p&gt;

&lt;p&gt;That's it, now you can write all the commit messages you want and know that they will render properly!&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>git</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Rails: Using Lots of ActionText Inputs on the Same Page</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Sat, 24 Sep 2022 01:49:46 +0000</pubDate>
      <link>https://dev.to/crespire/rails-using-lots-of-actiontext-inputs-on-the-same-page-6gi</link>
      <guid>https://dev.to/crespire/rails-using-lots-of-actiontext-inputs-on-the-same-page-6gi</guid>
      <description>&lt;p&gt;Here's a quick one!&lt;/p&gt;

&lt;p&gt;I used ActionText and Trix for a Facebook clone project, but I ran into a problem using multiple Trix input areas for different posts and comments on the same page.&lt;/p&gt;

&lt;p&gt;When a user had more than one post you could comment on, commenting on one post would cause some issues if you left that comment before saving it to comment or post elsewhere.&lt;/p&gt;

&lt;p&gt;The problem is how Trix handles input using a hidden field that contains the real data you'll be saving to your database. It turns out all the different Trix inputs were all writing to the same hidden input field!&lt;/p&gt;

&lt;p&gt;The fix was easy once I was able to figure out the issue: each &lt;code&gt;rich_text_area&lt;/code&gt; needed to also have its own unique ID, so that Trix could match each unique input with the corresponding hidden field.&lt;/p&gt;

&lt;p&gt;Hope that helps someone working with multiple ActionText inputs on the same page!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>trix</category>
    </item>
    <item>
      <title>Learning TDD was hard, but worth it.</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Tue, 11 Jan 2022 15:39:39 +0000</pubDate>
      <link>https://dev.to/crespire/learning-tdd-was-hard-but-worth-it-2eaj</link>
      <guid>https://dev.to/crespire/learning-tdd-was-hard-but-worth-it-2eaj</guid>
      <description>&lt;p&gt;&lt;sup&gt;Cover Photo by &lt;a href="https://www.pexels.com/@yankrukov?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels"&gt;Yan Krukov&lt;/a&gt; from &lt;a href="https://www.pexels.com/"&gt;Pexels&lt;/a&gt;.&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;As part of my learning journey with &lt;a href="https://theodinproject.com/"&gt;The Odin Project&lt;/a&gt;, I was exposed to test driven development (TDD). I wanted to write about my experience with it because while doing that lesson, I wanted nothing more than to skip it and move on to the next thing. In the end, I stuck with it and I do think it made for a fruitful learning experience. I am hoping some other learners might find this relatable and help to push them to stick with learning TDD. In the end, I would say I came out with an appreciation for TDD and how it helps you to write better code.&lt;/p&gt;

&lt;p&gt;I had a really hard time with the whole concept of TDD for a number of reasons. Tests-first and how to write tests was new, and using mocks and doubles was strange to me. And most of all, writing tests wasn't the same as writing my program!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning something new
&lt;/h2&gt;

&lt;p&gt;The first big barrier was that taking a TDD approach was completely different from what I was doing before. I'd write some code, then test it using IRB. It worked, so why bother with TDD? I had a lot of thoughts about how TDD was cumbersome and pointless during the first day with TDD. Why would I write tests first when I could just write the code, and then test it? It felt like a duplication of effort that led to the same end result.&lt;/p&gt;

&lt;p&gt;A large part of this feeling of tedium stemmed from actually having to learn something completely new. Tackling RSpec was like learning Ruby all over again and seem like a huge mountain to climb. Why should I climb it when I can just build the thing I really want to build anyway? At the time, I didn't recognize this barrier for what it really was. What I was frustrated with, really, was not knowing how to do the things I wanted to do. It wasn't that testing was difficult to pick up, but RSpec had very specific ways of doing things that I hadn't quite grasped yet. Things like &lt;code&gt;subject&lt;/code&gt; &lt;code&gt;context&lt;/code&gt; and all the different matchers for &lt;code&gt;expect&lt;/code&gt; were as foreign to me as &lt;code&gt;Array#reduce&lt;/code&gt; was when I first picked up Ruby. This feeling of frustration was so visceral, it really almost caused me to give up. I'm glad I didn't. Once I spent some time playing around with testing in RSpec, all the ways of doing things started to be added to my mental map. What was once a huge pain became easier, slowly but surely. Suddenly, writing a test in RSpec wasn't this huge pain in the ass where I'd have to look at the docs constantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duplication of effort?
&lt;/h2&gt;

&lt;p&gt;The second pain point was entangled with how familiar I was with the RSpec language, but I think it's worth calling out separately. I initially felt that it was a huge duplication of effort to write a test and then the code to pass that test. It would definitely have been faster to write my Ruby code first then figure out how to test it afterwards. At this stage, I knew how to write Ruby, but RSpec? At best, I'd have to spend some time figuring it out. That felt frustrating to me, and so it felt like a barrier to finishing the project which led to my feeling of want to can the whole thing and move on. Because RSpec was new to me, skipping the tests to write Ruby right away was very appealing. However, as I got more familiar with RSpec the barrier to figuring out tests lowered to the point where the whole idea of TDD started to make a lot more sense. After all, once you know how to use RSpec, writing a test is very simple.&lt;/p&gt;

&lt;p&gt;A test defines, in a very concrete way, what I am expecting my code to do. That's valuable when you're creating something from scratch. Putting into words (literally) what outcomes you're expecting articulates your small problem at hand. Then, you write another test. Add or refactor your code to pass both. In this way, you're building your code in small chunks of functionality.&lt;/p&gt;

&lt;p&gt;The other big benefit to learning how to use tests is something that you don't really broach working independently but is critical in a collaborative context: tests help to ensure a code base behaves in a particular way. When you're the only person working on a project, it isn't really consequential. You are the only one who works on the code and you know exactly what you want it to do. However, in a collaborative environment where you might be working on code someone else wrote? That is a whole new ball game. In this situation. if there's a test then you know what your code changes have to maintain in terms of behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the heck are mocks?
&lt;/h2&gt;

&lt;p&gt;The final barrier to testing involved the concept of mocks and doubles. I won't go into detail here their differences, as there are plenty of experts and resources on the topic. Generally, they are stand-ins for objects that you might not have written yet or resources that are external that you don't have direct control over like an API or external database. You don't want to send a bunch of requests to a live API when you're testing something in a development environment.&lt;/p&gt;

&lt;p&gt;Using mocks was difficult for me to wrap my head around at first. It was a bit of an echo of the second pain point I had: I didn't really know how to use these things, and why wouldn't I just write unit tests and then the actual class instead of using a mock? Again, I struggled here to really identify my core problem: I needed more familiarity with how to use this tool. One insight that helped me really understand this tool was the idea that you should &lt;em&gt;test the unit, not the integration&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This concept was a little baffling to me and it exposed a weakness in my familiarity with object oriented design in the first place. As a small note, this following section is based heavily on the things I learned from Sandi Metz through her talks at various Rails Confs as well as her two books on object oriented design. Using mocks was confusing to me because I realized I was testing the integration of my two objects, rather than testing the unit.&lt;/p&gt;

&lt;p&gt;An example here might be helpful, so let me set the stage. The project I was working on was a command line Connect4 game, and I was trying to test that my Game object would display the rules to the console. At this point in the project, &lt;code&gt;Display&lt;/code&gt; was not yet written so I was using a double as a stand-in.&lt;/p&gt;

&lt;p&gt;Here was my first go at the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Connect4&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#show_rules'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:c4_rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;described_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;display: &lt;/span&gt;&lt;span class="nb"&gt;display&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:display&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Display'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should print "Rules" to stdout'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;display&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:print_rules&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;and_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c4_rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show_rules&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Rules'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_stdout&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set up the class I am testing and an instance of it as well as a stand-in &lt;code&gt;Display&lt;/code&gt;. After the set up is complete, we allow &lt;code&gt;Display&lt;/code&gt; to receive the message &lt;code&gt;print_rules&lt;/code&gt; and return &lt;code&gt;nil&lt;/code&gt;. After all that, I assert (or expect) that the game object (&lt;code&gt;c4_rules&lt;/code&gt;) should send the message &lt;code&gt;show_rules&lt;/code&gt;, and then output something to the console.&lt;/p&gt;

&lt;p&gt;This test is bad. It is bad because it is testing integration between &lt;code&gt;Connect4&lt;/code&gt; and &lt;code&gt;Display&lt;/code&gt;. If you think about it, why should the game object know what the Display outputs to the console? In this case, I am testing for the output string &lt;code&gt;'Rules'&lt;/code&gt; but what if my rules change? This test would be broken because of a change in &lt;code&gt;Display&lt;/code&gt; but we were testing &lt;code&gt;Connect4&lt;/code&gt; here!&lt;/p&gt;

&lt;p&gt;Testing in this way makes mocks hard to use as any change anywhere else in your code has the potential to break tests related to another unit. It really does seem like you'd be better off writing Ruby rather than figuring out how to re-write your tests so they pass.&lt;/p&gt;

&lt;p&gt;How can we change this test so that it is actually a &lt;em&gt;unit&lt;/em&gt; test, rather than an integration test? After all, in TDD, the point is to make sure each class (or &lt;em&gt;unit&lt;/em&gt;) behaves as we expect it to. Changes in another unit shouldn't break tests for the unit we're looking at.&lt;/p&gt;

&lt;p&gt;Well, let's give it a go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Connect4&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'#show_rules'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:c4_rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;described_class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;display: &lt;/span&gt;&lt;span class="nb"&gt;display&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;let&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:display&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Display'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'should send Display the print_rules message'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;display&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:print_rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;display&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:print_rules&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;
      &lt;span class="n"&gt;c4_rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show_rules&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What changed? Well, a lot. Let's walk through it. All of the set up is mostly the same. We set up our test unit, then set up a stand-in &lt;code&gt;Display&lt;/code&gt; and allow it to receive the &lt;code&gt;print_rules&lt;/code&gt; message. This time, we don't care about what it returns. Then, we changed our test from expecting a certain output to expecting a certain message to be sent.&lt;/p&gt;

&lt;p&gt;This shifts the test away from testing integration to testing the unit we're interested in testing. It also means that all the &lt;code&gt;Display&lt;/code&gt; double is doing is verifying that it received a message from &lt;code&gt;Connect4&lt;/code&gt; like we expect.&lt;/p&gt;

&lt;p&gt;Thinking back, &lt;code&gt;Connect4&lt;/code&gt; shouldn't care what &lt;code&gt;Display&lt;/code&gt; &lt;em&gt;does&lt;/em&gt; with the &lt;code&gt;print_rules&lt;/code&gt; message in the first place. Whatever is the result of that message, that is the concern of &lt;code&gt;Display&lt;/code&gt; and its own unit tests. All we need to test here is that &lt;code&gt;Connect4&lt;/code&gt; sends such a message. Now, regardless of what &lt;code&gt;Display#print_rules&lt;/code&gt; outputs, it won't break our test. The behaviour we expect of &lt;code&gt;Connect4&lt;/code&gt; remains the same: it sends a message to the Display to do something.&lt;/p&gt;

&lt;p&gt;The key is to understand that each object in your program is a unit. These objects talk to each other in order to get things done. What's important in unit testing is to really focus on the &lt;em&gt;interface&lt;/em&gt; of your objects. What do we expect our objects to do when it receives a message? Should it send a message to another unit for information or to do something? Should it send information back to the sender in its own response?&lt;/p&gt;

&lt;p&gt;When you focus on testing the messages and responses of your objects rather than the end outcome of a message received or sent, the value of doubles becomes clear. In fact, thinking about the messages you want to send or receive, you either have to use mocks for objects you haven't written yet, or write them. In this way, a double is a nice way to figure out the messages &lt;em&gt;that other&lt;/em&gt; object should send and receive, before you even start writing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get testing!
&lt;/h2&gt;

&lt;p&gt;Even though this whole process of learning the TDD method has been hard, it has totally been worth the pain and struggle. I see the value in unit tests and have even undertaken other projects in this way. Give yourself a chance to become familiar with your testing framework. Once you know how to write the tests you want to write, it will all become so much easier.&lt;/p&gt;

&lt;p&gt;Beyond familiarity with your testing framework, make sure you're testing your unit as much as possible rather than integration with other units. It will help make testing much less painful, as changes far away from your current unit will be much less likely to break your tests.&lt;/p&gt;

&lt;p&gt;I hope my experience going through learning TDD encourages you to also stick with it. It was frustrating, and I wanted to skip the lesson many, many times. But in the end, learning how to do TDD not only helped me to get a stronger handle on some design fundamentals, but also helped me to write better code.&lt;/p&gt;

&lt;p&gt;One final thing, Sandi Metz has a fantastic talk on testing that really helped me to understand how to test better. This one video was hugely insightful and helped to tie everything I knew about testing and object oriented design together. If you're starting to learn TDD and hating it, give it a chance!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/URSWYvyc42M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>tdd</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to learn everything, and nothing at all.</title>
      <dc:creator>crespire</dc:creator>
      <pubDate>Sat, 04 Dec 2021 00:51:16 +0000</pubDate>
      <link>https://dev.to/crespire/how-to-learn-everything-and-nothing-at-all-h78</link>
      <guid>https://dev.to/crespire/how-to-learn-everything-and-nothing-at-all-h78</guid>
      <description>&lt;p&gt;&lt;sup&gt;Cover Photo by &lt;a href="https://unsplash.com/@aaronburden?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Aaron Burden&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/studying?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;I'm new to the development thing, and I've been working through &lt;a href="https://theodinproject.com"&gt;The Odin Project&lt;/a&gt;, a wonderful free resource that takes you from zero to a junior level developer in the context of web development. I might not know a lot, but I've been hanging around the community Discord server and a lot of people are constantly asking about code-along videos.&lt;/p&gt;

&lt;p&gt;A pretty standard exchange goes something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SomeUser: Hey I want to learn &amp;lt;some language or concept&amp;gt;, what's the best way to do that?&lt;br&gt;
OtherUser: You should look up a few videos and follow them to build something. You'll learn a lot!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let me be clear from the top: I believe code along videos have a certain value. They are invaluable for folks that already know what they're doing. These types of videos can introduce the viewer to some pretty complex things, and if you already know what is going on, there is value in the format as a quick introduction. The critical difference is that for someone with experience to fall back on, they can figure out what's what pretty easily.&lt;/p&gt;

&lt;p&gt;If you're still very early in your journey, a code along video is probably &lt;em&gt;not&lt;/em&gt; ideal for your learning. The main reason is that you'll be lacking the wealth of experience and knowledge to really understand what's happening.&lt;/p&gt;

&lt;p&gt;Let me get into this by way of example.&lt;/p&gt;

&lt;p&gt;When I first started The Odin Project, Javascript was arcane to me. I had (almost) no clue how it worked. I did have the advantage of picking up some basic Pascal in high school, and my group of friends were all nerdy and we all built basic HTML and CSS2 websites that used PHP scripts to make templates. Can you guess how old I am?&lt;/p&gt;

&lt;p&gt;Anyway, I never touched JavaScript then, so it was this newfangled thing to me when I finally got to that section of the Odin curriculum. So, I was eager to dive in. There's a very well known introductory course that has you make x things in y days based in Javascript, and one of the days was linked as a resource to consider, so I went ahead and opened it up.&lt;/p&gt;

&lt;p&gt;The basics of the video were essentially setting up an HTML file, some CSS, and the meat of it, using Javascript to set up an event handler which then played some audio, added a CSS class to the element, then removed it to get a transform effect.&lt;/p&gt;

&lt;p&gt;Here's the thing: these topics and techniques are all very basic JavaScript, CSS and HTML. Any web developer will know how to create and handle a browser event like the back of their hand. I think it's something I could have easily grasped if it were presented in a different format for me. I mean, to be real, maybe not, but I'd like to think so!&lt;/p&gt;

&lt;p&gt;I followed along with the video and nodded along, just typing into my own IDE what was in the video. Oh yeah, this is cool, and look at me build this thing! I finished the project and it worked as advertised, so I thought to myself, "Good job! You did it! You &lt;em&gt;made&lt;/em&gt; this!" I'm sure you're all familiar with &lt;a href="https://knowyourmeme.com/photos/1079173-i-made-this"&gt;this meme&lt;/a&gt; and in the moment, that was me. I had dilluded myself into thinking I made the project.&lt;/p&gt;

&lt;p&gt;The truth is, in that moment, I was suffering from the illusion of competence. The tutorial I had just watched gave me everything to succeed, and as a consequence, I didn't engage with the material at all. And why not? The solution was right there... even if I had wanted to come up with my own solution or next line of code, the temptation to just move along in the video was too easy to succumb to. The answer was just 1 second away! Sure, I had &lt;em&gt;something&lt;/em&gt; to show for it at the end of the video, but did I really know and understand what the event handler was doing? (&lt;em&gt;Narrator: He did not.&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;There's a lot of research about this phenomenon called &lt;em&gt;&lt;a href="https://aeon.co/ideas/what-know-it-alls-dont-know-or-the-illusion-of-competence"&gt;the illusion of competence&lt;/a&gt;&lt;/em&gt; and it can be a deadly menance when it comes to learning anything, but it is especially harmful for learning how to flourish as a developer. When you have the answer given to you, or you think you already know the answer, then you don't get the chance to really enter the &lt;a href="https://en.wikipedia.org/wiki/Four_stages_of_competence"&gt;second stage of competence&lt;/a&gt; by trying to solve the problem and failing. So, you finish one and look for the next great tutorial that will ~teach~ you something. But the results are the same, because tutorials almost always walk you through everything step by step. Some folks sometimes call this treadmill of tutorial videos &lt;em&gt;tutorial hell&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let me repeat this again, &lt;strong&gt;video codealongs do have their place as an educational tool&lt;/strong&gt; but this place, in my humble and noobie opinion, is once you've got a solid foundation in programming and are looking for a refresher on a topic you haven't touched in a while, or to figure out how to do something in a lanaguage that's new to you.&lt;/p&gt;

&lt;p&gt;My advice for those starting in their journey to learn how to write software?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Take your time. If you're in a hurry, you'll be prone to take shortcuts and fall into the illusion of competence much more easily. One way to avoid this is to give the material, concept or problem the time it deserves.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fail, and fail a lot. Get stuck, and Google the problem you're having and &lt;em&gt;not&lt;/em&gt; the solution! Once you're stuck you're in a prime position to learn something and make it memorable. I'll always remember when I finally figured out event handlers and the event object in JavaScript.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't be shy about asking for help in a thoughtful way after you fail or get stuck. Sometimes, you can figure a way out on your own, but if you feel like you're really stuck, asking thoughtful questions about your problem can often push you along the right direction. In this way, it's important to ask about your problem, rather than asking for a solution. It's the same problem: get given an answer, and you'll likely end up here again.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you're just starting your programming journey, code-along videos can seem like the best. You learn something, and end up with a project at the end. It really does feel like you can learn anything and everything this way, but just remember that the illusion of competence is a thing, and be extra careful to test your knowledge as you go!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
