<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Aaron Van Bokhoven - Software Developer]]></title><description><![CDATA[Software developer working with Ruby, Go, and JavaScript. Hobbyist street photographer and climber.]]></description><link>https://aaronvb.com</link><generator>GatsbyJS</generator><lastBuildDate>Fri, 23 Jul 2021 02:57:46 GMT</lastBuildDate><item><title><![CDATA[Request Hole, Testing Endpoints and Webhooks]]></title><description><![CDATA[Request Hole is a CLI tool and web UI for creating temporary endpoints for testing requests and webhooks.]]></description><link>https://aaronvb.com/articles/request-hole</link><guid isPermaLink="false">https://aaronvb.com/articles/request-hole</guid><pubDate>Thu, 22 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been working on a few side projects where I needed to inspect webhooks and initially used RequestBin to do this. However, I had issues with their service being down a few times and had some concerns about privacy. The alternative CLI tools I found required language dependencies and were a bit of a hassle to set up.&lt;/p&gt;
&lt;p&gt;This felt like a good excuse to make a CLI tool in Go, which would require no dependencies or versions of languages; just compile, or download, the executable for your operating system.&lt;/p&gt;
&lt;p&gt;My initial goal for the tool was to expose an endpoint and print incoming requests to the terminal output and/or write to a log file. This was the first release of &lt;a href=&quot;https://github.com/aaronvb/request_hole&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Request Hole&lt;/a&gt;. (Also known as &lt;code class=&quot;language-text&quot;&gt;rh&lt;/code&gt;, which is the CLI executable.)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dc994fb9fde2ed45f620b3dfe607cc06/80f66/rh-terminal-ui.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 67.08333333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAABAADBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAExe7kiJMv/xAAaEAACAwEBAAAAAAAAAAAAAAACAwABBBMU/9oACAEBAAEFAtDDp/VkwlZIZjo2eAYhPEP/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAfEAACAQMFAQAAAAAAAAAAAAAAAQIREiEiMTIzUZH/2gAIAQEABj8CnSct/Tsl9NTbyOV7yc2Wp1P/xAAYEAEBAQEBAAAAAAAAAAAAAAABEQAh8f/aAAgBAQABPyEIAHAZ7vJFS+rcuwqyby8FZ1a7/9oADAMBAAIAAwAAABB8D//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8QP//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAgEBPxChf//EABsQAQEBAAIDAAAAAAAAAAAAAAERAFHRMcHw/9oACAEBAAE/EHvxEQDgN9J708jqlIc55O7BQuW6ubhVyTzOt//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/dc994fb9fde2ed45f620b3dfe607cc06/8ac56/rh-terminal-ui.webp 240w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/d3be9/rh-terminal-ui.webp 480w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/e46b2/rh-terminal-ui.webp 960w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/f992d/rh-terminal-ui.webp 1440w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/db549/rh-terminal-ui.webp 1576w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/dc994fb9fde2ed45f620b3dfe607cc06/09b79/rh-terminal-ui.jpg 240w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/7cc5e/rh-terminal-ui.jpg 480w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/6a068/rh-terminal-ui.jpg 960w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/644c5/rh-terminal-ui.jpg 1440w,
/static/dc994fb9fde2ed45f620b3dfe607cc06/80f66/rh-terminal-ui.jpg 1576w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/dc994fb9fde2ed45f620b3dfe607cc06/6a068/rh-terminal-ui.jpg&quot;
          alt=&quot;Request Hole Terminal UI&quot;
          title=&quot;Request Hole Terminal UI&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Building the first release was relatively straight forward. It was working great and with the help of &lt;a href=&quot;https://pterm.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pterm&lt;/a&gt; I was able to make the terminal output look modern. I later realized it would be a bit more useful to have a web interface that could show me the incoming requests, in real time, similar to RequestBin.&lt;/p&gt;
&lt;p&gt;This required a lot of refactoring as the architecture at that point didn’t support having two Goroutines (http servers in our case, one for the incoming requests and one to serve the web UI) running in parallel.&lt;/p&gt;
&lt;p&gt;From the very beginning I wanted Request Hole to have an interface for outputs called &lt;code class=&quot;language-text&quot;&gt;renderers&lt;/code&gt; that could be implemented by any renderer we want to add. This could be a terminal printer, a log writer, or a web UI. To add the web UI, I had to change the way renderers were called, which at the time was synchronous, and decided to move them into individual Goroutines where they could be run in parallel.&lt;/p&gt;
&lt;p&gt;By moving the renderers to Go routines, they needed a way to communicate with the incoming request endpoint so I utilized channels. I then moved all the renderer logic into a higher level function that manages all the Goroutines and channels.&lt;/p&gt;
&lt;p&gt;After the big refactor, the next step was to start serving the web UI. I decided on &lt;a href=&quot;https://github.com/facebook/create-react-app&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;create-react-app&lt;/a&gt; which provided mostly everything I needed to start building the front-end. Thankfully Go introduced the embed feature in 1.16 which made embedding and serving the front-end static files easy.&lt;/p&gt;
&lt;p&gt;The next big decision was to use GraphQL instead of a standard REST API. Pushing incoming requests to the front-end through websockets was a requirement and I had read about GraphQL’s subscriptions, so going with GraphQL felt like a good solution, and a fun experiment. Although not the easiest to setup, compared to a traditional REST API, and facing some weird quirks and nuances, I did manage to get all the plumbing sorted out with &lt;a href=&quot;https://github.com/99designs/gqlgen&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;gqlgen&lt;/a&gt; and &lt;a href=&quot;https://www.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One thing I learned was that mapping Go types to GraphQL types don’t always, and most likely won’t, work out. The solution to that is to create custom types using scalars and once I figured that out everything went swimmingly. After getting GraphQL sorted out all that was left to do was to create the UI. I decided on Tailwind so I didn’t have to write any CSS.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d135462eb79eae7206e3892d16e925a6/c1595/rh-web-ui.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.16666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAOABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAIBBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAeuvAof/xAAaEAACAwEBAAAAAAAAAAAAAAAAAQIRExIx/9oACAEBAAEFAs4syiJcllnp/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGRAAAgMBAAAAAAAAAAAAAAAAARAAEaEx/9oACAEBAAY/ArI2c1U//8QAHRAAAgEEAwAAAAAAAAAAAAAAAAERITFBYXGBkf/aAAgBAQABPyHcGw8CehaQqdjjYcRZH//aAAwDAQACAAMAAAAQuA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAdEAEBAAEEAwAAAAAAAAAAAAABEQAhMcHhQWGx/9oACAEBAAE/EGpCqr2ZpLD2fOKSi2DfuRVt0tw38+sswB43z//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/d135462eb79eae7206e3892d16e925a6/8ac56/rh-web-ui.webp 240w,
/static/d135462eb79eae7206e3892d16e925a6/d3be9/rh-web-ui.webp 480w,
/static/d135462eb79eae7206e3892d16e925a6/e46b2/rh-web-ui.webp 960w,
/static/d135462eb79eae7206e3892d16e925a6/f992d/rh-web-ui.webp 1440w,
/static/d135462eb79eae7206e3892d16e925a6/882b9/rh-web-ui.webp 1920w,
/static/d135462eb79eae7206e3892d16e925a6/d3f94/rh-web-ui.webp 2272w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/d135462eb79eae7206e3892d16e925a6/09b79/rh-web-ui.jpg 240w,
/static/d135462eb79eae7206e3892d16e925a6/7cc5e/rh-web-ui.jpg 480w,
/static/d135462eb79eae7206e3892d16e925a6/6a068/rh-web-ui.jpg 960w,
/static/d135462eb79eae7206e3892d16e925a6/644c5/rh-web-ui.jpg 1440w,
/static/d135462eb79eae7206e3892d16e925a6/0f98f/rh-web-ui.jpg 1920w,
/static/d135462eb79eae7206e3892d16e925a6/c1595/rh-web-ui.jpg 2272w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/d135462eb79eae7206e3892d16e925a6/6a068/rh-web-ui.jpg&quot;
          alt=&quot;Request Hole Web UI&quot;
          title=&quot;Request Hole Web UI&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can view the project at &lt;a href=&quot;https://github.com/aaronvb/request_hole&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/aaronvb/request_hole&lt;/a&gt; where the source and release files are available to download.&lt;/p&gt;
&lt;p&gt;If you’re on a Mac, you can install it with &lt;code class=&quot;language-text&quot;&gt;brew install aaronvb/request_hole/rh&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Rails Inspired Logging in Go]]></title><description><![CDATA[Verbose logging in development is one of the few things I miss about Ruby on Rails. I made two libraries to bring that to Go and explain how to set that up with middleware.]]></description><link>https://aaronvb.com/articles/rails-inspired-logging-in-go</link><guid isPermaLink="false">https://aaronvb.com/articles/rails-inspired-logging-in-go</guid><pubDate>Mon, 18 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the few things I miss from &lt;a href=&quot;https://rubyonrails.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ruby on Rails&lt;/a&gt; is the verbose logging the framework provides to you in development. If you’re not coming from Ruby on Rails I believe this will still be applicable and beneficial to your development in Go. &lt;/p&gt;
&lt;p&gt;Before we jump into the details, I’d like to preface this by saying this guide to logging is meant to be used in the &lt;strong&gt;development&lt;/strong&gt; environment. While I think logging should be done in production as well, the depth of logging should be limited in any production environment where we need to take security into account. (Side note: The param logging package I’ve released will automatically filter passwords.)&lt;/p&gt;
&lt;h3 id=&quot;the-rails-log&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-rails-log&quot; aria-label=&quot;the rails log permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Rails Log&lt;/h3&gt;
&lt;p&gt;Below is an example of a standard log output by Rails.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Started GET &quot;/users?foo=bar&quot; for ::1 at 2021-01-18 22:16:58 -1000
Processing by UsersController#index as HTML
  Parameters: {&quot;foo&quot;=&gt;&quot;bar&quot;}
  User Load (0.2ms)  SELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT ?  [[&quot;LIMIT&quot;, 1]]
  ↳ app/controllers/users_controller.rb:3:in `index&apos;
Completed 200 OK in 4ms (Views: 0.7ms | ActiveRecord: 0.2ms | Allocations: 973)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Breaking it down we start with the method used in the request, followed by the path and a timestamp.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Started GET &quot;/users?foo=bar&quot; for ::1 at 2021-01-18 22:16:58 -1000&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we have the parameters passed to the request.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Parameters: {&quot;foo&quot;=&gt;&quot;bar&quot;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that we have any queries processed during the request and the line where the query was run.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;User Load (0.2ms)  SELECT &quot;users&quot;.* FROM &quot;users&quot; ORDER BY &quot;users&quot;.&quot;id&quot; ASC LIMIT ?  [[&quot;LIMIT&quot;, 1]]
  ↳ app/controllers/users_controller.rb:3:in `index&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the last line is the end of the request that contains the response status and time to completion.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Completed 200 OK in 4ms (Views: 0.7ms | ActiveRecord: 0.2ms | Allocations: 973)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, all of these together provide very helpful details into every request.&lt;/p&gt;
&lt;h3 id=&quot;the-go-log&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-go-log&quot; aria-label=&quot;the go log permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Go Log&lt;/h3&gt;
&lt;p&gt;Out of the box Go won’t give you that kind of logging for web requests. &lt;/p&gt;
&lt;p&gt;Enter &lt;a href=&quot;https://github.com/aaronvb/logrequest&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;logrequest&lt;/a&gt; and &lt;a href=&quot;https://github.com/aaronvb/logparams&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;logparams&lt;/a&gt;. I wrote these two packages to bring the above logging to your Go project. With one exception, the SQL query logging has to be handled manually, but no worries I’ll explain how I handle that at the end.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;app_1      | Started GET &quot;/&quot; 172.19.0.1:64368 HTTP/1.1 at 2021-01-19 08:30:08
app_1      |    SELECT id, name, email, created, active FROM users WHERE id = $1 [8]
app_1      |    ↳ /app/pkg/models/postgres/users.go:75
app_1      | Completed 200 in 2.1124ms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;middleware&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#middleware&quot; aria-label=&quot;middleware permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Middleware&lt;/h4&gt;
&lt;p&gt;To get &lt;a href=&quot;https://github.com/aaronvb/logrequest&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;logrequest&lt;/a&gt; and &lt;a href=&quot;https://github.com/aaronvb/logparams&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;logparams&lt;/a&gt; working, we’ll need to use it as middleware within the http request. If you’re not familiar with middleware, Alex Edwards has a great blog post covering it: &lt;a href=&quot;https://www.alexedwards.net/blog/making-and-using-middleware&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.alexedwards.net/blog/making-and-using-middleware&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And we’ll be using &lt;a href=&quot;https://github.com/gorilla/mux&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;gorilla/mux&lt;/a&gt; to handle the middleware.&lt;/p&gt;
&lt;h4 id=&quot;using-logrequest-and-logparams&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#using-logrequest-and-logparams&quot; aria-label=&quot;using logrequest and logparams permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Using logrequest and logparams&lt;/h4&gt;
&lt;p&gt;Below is an example application which I’ll break down.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;log&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;net/http&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;os&quot;&lt;/span&gt;

    &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aaronvb/logparams&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;github.com/aaronvb/logrequest&quot;&lt;/span&gt;

    &lt;span class=&quot;token string&quot;&gt;&quot;github.com/gorilla/mux&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; application &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    errorLog &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Logger
    infoLog  &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Logger
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    infoLog &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stdout&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INFO\t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ldate&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ltime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    errorLog &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stderr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ERROR\t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ldate&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ltime&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Lshortfile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    app &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        errorLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; errorLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        infoLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  infoLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    srv &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Server&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Addr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;token string&quot;&gt;&quot;:8080&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ErrorLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; errorLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        Handler&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    infoLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Starting server on %s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;:8080&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; srv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    errorLog&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    r &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; mux&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HandleFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/foobar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foobar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Methods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Middleware&lt;/span&gt;
    r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logParams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; r
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foobar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fprintln&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello world&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Middleware&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HandlerFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lr &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; logrequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogRequest&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Writer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Handler&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; NewLine&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Timestamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        lr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infoLog&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HandlerFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lp &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; logparams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogParams&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        lp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infoLog&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        next&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ServeHTTP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First we need to make sure we create the log outputs and pass it to the application struct. These will be used by logrequest and logparam. Alternatively, you can have either package return a string instead of printing to a log output.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;infoLog &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stdout&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INFO\t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ldate&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ltime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
errorLog &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stderr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ERROR\t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ldate&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Ltime&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Lshortfile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

app &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    errorLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; errorLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    infoLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  infoLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;code class=&quot;language-text&quot;&gt;routes&lt;/code&gt; function, which we use as the &lt;code class=&quot;language-text&quot;&gt;Handler&lt;/code&gt;, we create a new mux router and pass the two middleware functions to it. Both of those middleware functions will use the libraries.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    r &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; mux&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewRouter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HandleFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/foobar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foobar&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Methods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Middleware&lt;/span&gt;
    r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logParams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; r
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;logRequest&lt;/code&gt; middleware function creates a new logrequest struct where we pass the http &lt;code class=&quot;language-text&quot;&gt;Request&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Writer&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Handler&lt;/code&gt;, and two optional arguments to configure the output. The reason why we pass the &lt;code class=&quot;language-text&quot;&gt;Handler&lt;/code&gt; is so that we can hook into the end of the request to show the response status and total time for the request.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Timestamp&lt;/code&gt; (boolean) can be used to hide/show the timestamp at the start of the request. (&lt;code class=&quot;language-text&quot;&gt;Started GET &quot;/&quot; 172.19.0.1:64368 HTTP/1.1 at 2021-01-19 08:30:08&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;NewLine&lt;/code&gt; (int) can be used to add line breaks between each request log output(Rails does this by default).&lt;/p&gt;
&lt;p&gt;We call &lt;code class=&quot;language-text&quot;&gt;ToLogger&lt;/code&gt; on the struct to print the request to the logger we created above.&lt;/p&gt;
&lt;p&gt;Note: We don’t need to call &lt;code class=&quot;language-text&quot;&gt;next&lt;/code&gt; like you would normally do with middleware — logrequest package will call this automatically.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logRequest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HandlerFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lp &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; logrequest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogRequest&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Writer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Handler&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; NewLine&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Timestamp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        lp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infoLog&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;logParams&lt;/code&gt; middleware function creates a new logparams struct where we pass just the &lt;code class=&quot;language-text&quot;&gt;Request&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;By default logparams will filter out &lt;code class=&quot;language-text&quot;&gt;password&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;password_confirmation&lt;/code&gt; params. This can be turned off by passing &lt;code class=&quot;language-text&quot;&gt;ShowPassword: True&lt;/code&gt; (do NOT recommend this).&lt;/p&gt;
&lt;p&gt;Also by default, logparams will hide empty parameters. This can be turned off by passing &lt;code class=&quot;language-text&quot;&gt;ShowEmpty: true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We call &lt;code class=&quot;language-text&quot;&gt;ToLogger&lt;/code&gt; on the struct to print the params to the logger we created above. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;logParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;next http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Handler &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HandlerFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lp &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; logparams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;LogParams&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        lp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;infoLog&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        next&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ServeHTTP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;sql-query-logging&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sql-query-logging&quot; aria-label=&quot;sql query logging permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SQL Query Logging&lt;/h4&gt;
&lt;p&gt;The final part of this is going to be subjective based on how you’ve structured your application. In my latest project the structure was influenced by &lt;a href=&quot;https://lets-go.alexedwards.net&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Alex Edwards Let’s Go!&lt;/a&gt; book, which is a great dive into building a web application in Go. If you’ve read that, or use a similar structure, this should be easy to apply.&lt;/p&gt;
&lt;p&gt;In the &lt;code class=&quot;language-text&quot;&gt;main.go&lt;/code&gt; we’ll need to create a new log output, and pass that to the application model struct. In this example that’s going to be a User. We’re going to use this log output in the model to print the query.&lt;/p&gt;
&lt;p&gt;Note: &lt;code class=&quot;language-text&quot;&gt;postgres&lt;/code&gt; is a custom postgres package(ORM), that the models belong to and is not related to any official package.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;requestLog &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Stdout&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

app &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    errorLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;   errorLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    infoLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;    infoLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    requestLog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; requestLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    session&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;    session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    users&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;postgres&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UserModel&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        AppModels&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AppModels&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            DB&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  db&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            Log&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; requestLog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In my custom &lt;code class=&quot;language-text&quot;&gt;postgres&lt;/code&gt; package I’ve created functions that wrap the &lt;code class=&quot;language-text&quot;&gt;database/sql&lt;/code&gt; query functions which will be used to print to the log output and execute the query.&lt;/p&gt;
&lt;p&gt;The line &lt;code class=&quot;language-text&quot;&gt;_, file, line, _ := runtime.Caller(1)&lt;/code&gt; is what we use to print the file and line number that is calling the function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Package postgres is the ORM for the db&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; postgres

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;database/sql&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;log&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;runtime&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// AppModels is the struct that shares the functions between&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// each model. Requires DB and Log.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; AppModels &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    DB  &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DB
    Log &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Logger
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;AppModels&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;queryRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Row &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\t\u001b[34;1m%s \u001b[0m%v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Caller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\t\u21B3 %s:%d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;QueryRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;AppModels&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Rows&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\t\u001b[34;1m%s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Caller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\t\u21B3 %s:%d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    row&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; row&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;AppModels&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\t\u001b[34;1m%s \u001b[0m%v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Caller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\t\u21B3 %s:%d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DB&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; err
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And now we can use the above functions in the models to do things like find a User.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; postgres

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; UserModel &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    AppModels
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;UserModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;User&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    u &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;User&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    stmt &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`SELECT id FROM users WHERE id = $1`&lt;/span&gt;
    row &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;queryRow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stmt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; row&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Scan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;u&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ID&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Is&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sql&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ErrNoRows&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; models&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ErrNoRecord
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; u&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I hope that helps you get started with some awesome Rails inspired logging. There’s quite a lot going on in this blog post so please comment below or contact me if you have any questions.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Simple Website Text Scraping with Go and AWS Lambda]]></title><description><![CDATA[In this article I’ll be showing you how to compile the Go script, setup the AWS Lambda function, and configure a cron type job to run the script every hour.]]></description><link>https://aaronvb.com/articles/simple-website-text-scraping-with-go-and-aws-lambda</link><guid isPermaLink="false">https://aaronvb.com/articles/simple-website-text-scraping-with-go-and-aws-lambda</guid><pubDate>Sun, 17 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/07079eba5247fdfc0d3d72d354fa6c86/d165a/header.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.916666666666668%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAGABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAIDBP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAc8AoB//xAAYEAADAQEAAAAAAAAAAAAAAAAAAQISA//aAAgBAQABBQJNOOs5NUf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAABBQAAAAAAAAAAAAAAAAAAAQIRMTL/2gAIAQEABj8ColppT//EABoQAQADAAMAAAAAAAAAAAAAAAEAEUExYZH/2gAIAQEAAT8hA3sL6h4ByBcek//aAAwDAQACAAMAAAAQAA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAwADAAAAAAAAAAAAAAABABFhIUGR/9oACAEBAAE/ECUrKxbbcj7BhFSK6cyCUExz/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/07079eba5247fdfc0d3d72d354fa6c86/8ac56/header.webp 240w,
/static/07079eba5247fdfc0d3d72d354fa6c86/d3be9/header.webp 480w,
/static/07079eba5247fdfc0d3d72d354fa6c86/e46b2/header.webp 960w,
/static/07079eba5247fdfc0d3d72d354fa6c86/7f403/header.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/07079eba5247fdfc0d3d72d354fa6c86/09b79/header.jpg 240w,
/static/07079eba5247fdfc0d3d72d354fa6c86/7cc5e/header.jpg 480w,
/static/07079eba5247fdfc0d3d72d354fa6c86/6a068/header.jpg 960w,
/static/07079eba5247fdfc0d3d72d354fa6c86/d165a/header.jpg 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/07079eba5247fdfc0d3d72d354fa6c86/6a068/header.jpg&quot;
          alt=&quot;header&quot;
          title=&quot;header&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Recently I needed to know when certain websites were updated with specific text. I decided to utilize AWS Lambda to save on cost of hosting a server, and use Go because it’s fast, and also because it’s one of the supported languages on AWS Lambda. I am also using AWS SES to send me e-mail notifications when results are found.&lt;/p&gt;
&lt;p&gt;Bellow I’ll be showing you how to compile the Go script, setup the AWS Lambda function, and configure a cron type job to run the script every hour.&lt;/p&gt;
&lt;p&gt;First, clone the repo contains the script.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/aaronvb/aws_lambda_go_scraper&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/aaronvb/aws_lambda_go_scraper&lt;/a&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ git clone git@github.com:aaronvb/aws_lambda_go_scraper.git
$ cd aws_lambda_go_scraper&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we’ll build the Go script and zip it up for AWS Lambda.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ GOOS=linux GOARCH=amd64 go build -o main lambda_scraper.go
$ zip main.zip main&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Create an AWS Lambda function with the Go runtime, and select or create a role that has access to AWS SES. We’ll be using AWS SES to send out the e-mail notification.&lt;/p&gt;
&lt;p&gt;Once the AWS Lambda function is created, upload the zip file and make sure the handler is set to &lt;code class=&quot;language-text&quot;&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b669105ac7d78b72574bd30db06a9d2d/2cefc/function.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.583333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAiElEQVQY042PWQ7DIAxEff9rthFS1hbbScAsnRJFlZKfPlkGhhkZaF4W59wwjuu27SchhP3GXSTvue8HFs25pJQyllJExMxyA2KtFUl42v0PUtVpmuFGBgF0WEU0xpga2EBBhxPhQ0QSIr3e/vHsmBmHY2z9GxJVz3iRolgE/45mmPPtl7qI0T6FFCRdB9VzCQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/b669105ac7d78b72574bd30db06a9d2d/8ac56/function.webp 240w,
/static/b669105ac7d78b72574bd30db06a9d2d/d3be9/function.webp 480w,
/static/b669105ac7d78b72574bd30db06a9d2d/e46b2/function.webp 960w,
/static/b669105ac7d78b72574bd30db06a9d2d/7f403/function.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/b669105ac7d78b72574bd30db06a9d2d/8ff5a/function.png 240w,
/static/b669105ac7d78b72574bd30db06a9d2d/e85cb/function.png 480w,
/static/b669105ac7d78b72574bd30db06a9d2d/d9199/function.png 960w,
/static/b669105ac7d78b72574bd30db06a9d2d/2cefc/function.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/b669105ac7d78b72574bd30db06a9d2d/d9199/function.png&quot;
          alt=&quot;function&quot;
          title=&quot;function&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Next, create 3 environment variables: &lt;code class=&quot;language-text&quot;&gt;RECIPIENT&lt;/code&gt; will be the email which receives the notification, &lt;code class=&quot;language-text&quot;&gt;SENDER&lt;/code&gt; which will be the email address that sends the notification, and last &lt;code class=&quot;language-text&quot;&gt;SES_LOCATION&lt;/code&gt; which is the location of your SES(ie: us-west-2).&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6a6b199b17cdff6ca587ad56f51cf998/2cefc/env_variables.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.583333333333332%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAIAAABM9SnKAAAACXBIWXMAAAsSAAALEgHS3X78AAAAoklEQVQY022PCRKDIAxFuf9BRauyyKIhwemX1umibwIZyPajZmM6rTvd98NAF0opOHQTOC4VYzTG+gWE7WTFIcp5TSlluBfbh5S3mEmFEGZjRaQwv+c0w3MJAYIe44js7xBsthZVCjORgb51ryzMIofB1+qc11pDUeHzvxmYpglNFcTEJg465ZdjZea673JhJUKhwkrOOWOt957vaBv808TxE6tyW8VppCfdAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/6a6b199b17cdff6ca587ad56f51cf998/8ac56/env_variables.webp 240w,
/static/6a6b199b17cdff6ca587ad56f51cf998/d3be9/env_variables.webp 480w,
/static/6a6b199b17cdff6ca587ad56f51cf998/e46b2/env_variables.webp 960w,
/static/6a6b199b17cdff6ca587ad56f51cf998/7f403/env_variables.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/6a6b199b17cdff6ca587ad56f51cf998/8ff5a/env_variables.png 240w,
/static/6a6b199b17cdff6ca587ad56f51cf998/e85cb/env_variables.png 480w,
/static/6a6b199b17cdff6ca587ad56f51cf998/d9199/env_variables.png 960w,
/static/6a6b199b17cdff6ca587ad56f51cf998/2cefc/env_variables.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/6a6b199b17cdff6ca587ad56f51cf998/d9199/env_variables.png&quot;
          alt=&quot;env_variables&quot;
          title=&quot;env_variables&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Don’t forget to add the email address to SES and verify it so it can receive emails.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c92871db7e8a540ae89742ce91113ac0/2cefc/ses.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAZ0lEQVQI15XNORKAIBBEUe5/QUulalgGtHBEYUgFl0hDX/CTDlqEEJxziGitIaJ1JWWwkxqdn6cPtGhuVmsFAN57wZyXhYYRul6CMimlnHPhK+lt2zepxusjxsh8z6UU0R61tqO2v07fn6jLUKQ7FgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/c92871db7e8a540ae89742ce91113ac0/8ac56/ses.webp 240w,
/static/c92871db7e8a540ae89742ce91113ac0/d3be9/ses.webp 480w,
/static/c92871db7e8a540ae89742ce91113ac0/e46b2/ses.webp 960w,
/static/c92871db7e8a540ae89742ce91113ac0/7f403/ses.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/c92871db7e8a540ae89742ce91113ac0/8ff5a/ses.png 240w,
/static/c92871db7e8a540ae89742ce91113ac0/e85cb/ses.png 480w,
/static/c92871db7e8a540ae89742ce91113ac0/d9199/ses.png 960w,
/static/c92871db7e8a540ae89742ce91113ac0/2cefc/ses.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/c92871db7e8a540ae89742ce91113ac0/d9199/ses.png&quot;
          alt=&quot;ses&quot;
          title=&quot;ses&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now we can create a test event. In the event data pass a JSON hash which has a key &lt;code class=&quot;language-text&quot;&gt;urls&lt;/code&gt; and a string value with the urls you want to scrape, separated by commas, and a key &lt;code class=&quot;language-text&quot;&gt;words&lt;/code&gt;, with a string value of comma separated words you wish to scrape.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;urls&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://aaronvb.com,https://aaronvb.com/articles/selection-sort-in-ruby.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;words&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ruby,Hawaii,foobar&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/789af4485ee1ca0725fdcfee6d0c2de4/2cefc/test_event.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.08333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABE0lEQVQoz4VRi26FIAz1/z9xuVnmA1Gg8roKFHUrRHO3ZMtOTprDSUtLabz3jI3zLKZpBoCUEiLGGANhC9sWLn0jFiNuFY27i8eRE9u2+2g7OlrrtNbLop3zxhhrrdaGHFukJSfn3KwhSgV8mkbOyTrPM+87xc//sO97o59BSOCcU7d1Xf9KPX/iKn56rxRsIdCBrOM4viflgh0x/9b5aGhDtDPaAmXFFDEjxYSprCXFsrJYSD6ZlXQV5pzKm2NKjLF2ED1fOt6/dY9H/05xlFxqmEHOIGpUkxICpFiAfLEo6lA6DwMTyoJZF/OU2oHxSnvjNuuD9bEy3LoKVyINUIrpAxBTRqwTXSQHX5PeGl+ksb8AXckFb5Ut6dsAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/789af4485ee1ca0725fdcfee6d0c2de4/8ac56/test_event.webp 240w,
/static/789af4485ee1ca0725fdcfee6d0c2de4/d3be9/test_event.webp 480w,
/static/789af4485ee1ca0725fdcfee6d0c2de4/e46b2/test_event.webp 960w,
/static/789af4485ee1ca0725fdcfee6d0c2de4/7f403/test_event.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/789af4485ee1ca0725fdcfee6d0c2de4/8ff5a/test_event.png 240w,
/static/789af4485ee1ca0725fdcfee6d0c2de4/e85cb/test_event.png 480w,
/static/789af4485ee1ca0725fdcfee6d0c2de4/d9199/test_event.png 960w,
/static/789af4485ee1ca0725fdcfee6d0c2de4/2cefc/test_event.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/789af4485ee1ca0725fdcfee6d0c2de4/d9199/test_event.png&quot;
          alt=&quot;test_event&quot;
          title=&quot;test_event&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Click the test button and you should receive a successful function execution with logs and an email.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/682e74fb27bd72244ea95a04a1217cc9/2cefc/test_action.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 7.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsSAAALEgHS3X78AAAAYElEQVQI1x2LUQ6AIAxDvf8hVYwIbmw4RMA/G5N+NH19k+Z8EvsjaL5ENJuFeKJgt1JUtdx1vG8fA3lau8zwJGbQKYm4bZ8XR5xYJImubgshwgcmYryhtd6R+stQdn+gfJ1rb7tN60quAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/682e74fb27bd72244ea95a04a1217cc9/8ac56/test_action.webp 240w,
/static/682e74fb27bd72244ea95a04a1217cc9/d3be9/test_action.webp 480w,
/static/682e74fb27bd72244ea95a04a1217cc9/e46b2/test_action.webp 960w,
/static/682e74fb27bd72244ea95a04a1217cc9/7f403/test_action.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/682e74fb27bd72244ea95a04a1217cc9/8ff5a/test_action.png 240w,
/static/682e74fb27bd72244ea95a04a1217cc9/e85cb/test_action.png 480w,
/static/682e74fb27bd72244ea95a04a1217cc9/d9199/test_action.png 960w,
/static/682e74fb27bd72244ea95a04a1217cc9/2cefc/test_action.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/682e74fb27bd72244ea95a04a1217cc9/d9199/test_action.png&quot;
          alt=&quot;test_action&quot;
          title=&quot;test_action&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The logs will contain the results, message ID from SES, and any errors while parsing or sending the email.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/2cefc/email_example.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 7.916666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAACCAIAAADXZGvcAAAACXBIWXMAAAsSAAALEgHS3X78AAAAUUlEQVQI1z2LOQ7AIAwE+f8rTTCOMVSYo80KpEwx2mInuPtaE+y9W2sxRmZ+DncQUUop5wyrainFzPQQeu9zDoC+1vrHaDDwEBH4PSDD5/bwBxLzbSLhchPSAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/8ac56/email_example.webp 240w,
/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/d3be9/email_example.webp 480w,
/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/e46b2/email_example.webp 960w,
/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/7f403/email_example.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/8ff5a/email_example.png 240w,
/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/e85cb/email_example.png 480w,
/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/d9199/email_example.png 960w,
/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/2cefc/email_example.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/f48c00a8a89fc7a2a95f6b9f54a94c8e/d9199/email_example.png&quot;
          alt=&quot;email_example&quot;
          title=&quot;email_example&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Success. The websites were scraped and my words were found!&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;lets-automate-this&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#lets-automate-this&quot; aria-label=&quot;lets automate this permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Let’s Automate This&lt;/h2&gt;
&lt;p&gt;Now that the AWS Lambda function is working, it’s time to automate this and have it run every hour. We’ll pick different words because we know those exist. Let’s pretend we want to know when my personal website will be updated with the words “swift, java, and angular.”&lt;/p&gt;
&lt;p&gt;For this we’ll be using AWS CloudWatch events. So let’s head over there and create a new events rule.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/2cefc/create_rule.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 55.833333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAIAAADwazoUAAAACXBIWXMAAAsSAAALEgHS3X78AAABMElEQVQoz31SzU6GMBDs+z+JdxPPHk18CSM/H1Ao/aPdQikObTQmgnPYkGVmZ3aB9X0vhHg8Hk3TSKlijOu6btuWy5ZSOo4jXQF9JqWEeJ5nPBhjlmWx1jrnSgUJI44bnOJxHLU2aT+xZcAcEfCaiOq6tnYJIay/AA7IzHuC7SRmPopx5HAj71HBgBiaLm+ktcYgtzhEw1tU7z1ztE5CnC1Pp5KIc66UAhvmiI2ltFZGa+d9yvtjKJxBYNZR1w0A5wMiYAq6JqMkl99Abijzcnu5GXPOg4zzoHV5FXhWnxXuUoKUZpnCjnsUojauqdu6qrquQ/KYUfTs56Nd61PCRXEFKM/kISAjLobgMf7rXA4z9H3btoHorwfTSmHY3W8EhIyijPs+aXp+a55eP17e2y9WYn7S4y8xbAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/8ac56/create_rule.webp 240w,
/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/d3be9/create_rule.webp 480w,
/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/e46b2/create_rule.webp 960w,
/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/7f403/create_rule.webp 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/8ff5a/create_rule.png 240w,
/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/e85cb/create_rule.png 480w,
/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/d9199/create_rule.png 960w,
/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/2cefc/create_rule.png 1400w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/6bb9b08f4aaa8dbe56c84bf67e7103d5/d9199/create_rule.png&quot;
          alt=&quot;create_rule&quot;
          title=&quot;create_rule&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;First we set the schedule to a fixed rate of 1 hour. Next, choose the Lambda function we created earlier. And finally, the most important part, select Configure input &gt; Constant (JSON text), and paste in the JSON with the data to send to our function (see code below).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;urls&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://aaronvb.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;words&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;swift,java,angular&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you fill that in, click Configure details to name the rule and then create it. We now have the script running every hour, scraping our website, searching for the text we provided, and alerting us when it finds it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Chaining API requests with redux-thunk]]></title><description><![CDATA[I recently came upon a simple problem where I couldn’t find an easy solution to, on the Google or SO. However, I did find the parts to the answer, and put them together to get this working.]]></description><link>https://aaronvb.com/articles/chaining-api-requests-with-redux-thunk</link><guid isPermaLink="false">https://aaronvb.com/articles/chaining-api-requests-with-redux-thunk</guid><pubDate>Wed, 07 Feb 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently came upon a simple problem where I couldn’t find an easy solution to, on the Google or SO. However, I did find the parts to the answer, and put them together to get this working.&lt;/p&gt;
&lt;p&gt;My problem was finding a way to chain API requests using redux-thunk and axios. My use case was to gather an array of objects(containing id’s) on the first API request, and then subsequently gather each individual objects’ data from the array above, using the id, through another API request, all asynchronously.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/1f155b660dcfb01cc399bcdb15ca672e/thunk_example.gif&quot; alt=&quot;thunk_example&quot;&gt;&lt;/p&gt;
&lt;p&gt;I was able to solve this in redux-thunk by making two actions; one to fetch all, and one to fetch each individually. And, to chain them together, we treat each action as a promise(they return axios promises), then let redux-thunk do it’s magic. To get the array of objects in the first API request, we can use getState() from the first promise.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;requestIssuesAndIssueData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;dispatch&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; getState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestIssues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; issuesArr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issues&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;issuesById&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      issuesArr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;issue&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;requestIssueCommentsHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;issue&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the snippet above I am using GitHub issues as an example.&lt;/p&gt;
&lt;p&gt;To see the entire example, checkout my repo at &lt;a href=&quot;https://github.com/aaronvb/redux-thunk-chain-api-requests&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/aaronvb/redux-thunk-chain-api-requests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To see it in action I setup a demo project at &lt;a href=&quot;https://aaronvb.github.io/redux-thunk-chain-api-requests&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://aaronvb.github.io/redux-thunk-chain-api-requests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy hacking!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A Simple React Native Redux Example]]></title><description><![CDATA[Working on my latest React Native project, I decided to make the jump from React’s state to Redux.]]></description><link>https://aaronvb.com/articles/a-simple-react-native-redux-example</link><guid isPermaLink="false">https://aaronvb.com/articles/a-simple-react-native-redux-example</guid><pubDate>Sun, 18 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Working on my latest React Native project, I decided to make the jump from React’s state to Redux.&lt;/p&gt;
&lt;p&gt;I looked through all the examples I could find, the majority were from 2015, and couldn’t find something simple and minimal. Also, a lot of things have changed since 2015, and even 2016 - giving me a good excuse to start from scratch. My first step was to go through the Redux docs and tutorials to give myself a clean slate, instead of just copying code from my previous projects.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://redux.js.org/docs/basics/ExampleTodoList.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://redux.js.org/docs/basics/ExampleTodoList.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The above link has a minimal example of a Redux implementation, and is what I used as base for my project.&lt;/p&gt;
&lt;figure&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 498px;&quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/4c507ed003dbabe61d437fefff684af8/79e1b/1.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 66.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&amp;apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsSAAALEgHS3X78AAABeUlEQVQoz4WRW1OCUBSFz2/oMpOaaZYEJgKCcOSiHK4CB6/gpbzUg/XQY/3/afcDgplvznnaa++1FlKsyBzHr4qFrWi2ft8eP0b+XBr6fT0oBXHSSFad17E98qZpfprnJy/Jn/sWJ1qsYBaD6u3+E49DTzNI4kRrN85kI7yosjdNvvYgFoPuGJkVhtTFAzPw6Y5MVl6c09VhYIaV+x5I3z5K/4HqjMzwOBhriuHb4YqESxIudEIZXgftgsm/4caTwgk69bBmTXy6pYs92DYcCmfDUbC5AAQCDK9FRAXPsh5c1Njreuem2au2hFIQJpRM5sd1DAs3h88g3USzFzhbHcdaGSic7mbZ/vO43BzO56+fj69veNPV3qdrECoGqpKbrMx0JQn7aXZK5i9DO7mqd8ASeC4GQaTwtTpY0Fw3zp0oM5zUdGiLUystoaRnCAyWN55tEXswDEBb2e6dVwgEXtLz7aPYaGt89ySo3mLzli73qhle1rjSkoFflByiIxVOWToAAAAASUVORK5CYII=&amp;apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source srcset=&quot;/static/4c507ed003dbabe61d437fefff684af8/8ac56/1.webp 240w,
/static/4c507ed003dbabe61d437fefff684af8/d3be9/1.webp 480w,
/static/4c507ed003dbabe61d437fefff684af8/71ce1/1.webp 498w&quot; sizes=&quot;(max-width: 498px) 100vw, 498px&quot; type=&quot;image/webp&quot;&gt;
        &lt;source srcset=&quot;/static/4c507ed003dbabe61d437fefff684af8/8ff5a/1.png 240w,
/static/4c507ed003dbabe61d437fefff684af8/e85cb/1.png 480w,
/static/4c507ed003dbabe61d437fefff684af8/79e1b/1.png 498w&quot; sizes=&quot;(max-width: 498px) 100vw, 498px&quot; type=&quot;image/png&quot;&gt;
        &lt;img class=&quot;gatsby-resp-image-image&quot; src=&quot;/static/4c507ed003dbabe61d437fefff684af8/79e1b/1.png&quot; alt=&quot;directory structure&quot; title=&quot;directory structure&quot; loading=&quot;lazy&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;
  &lt;figcaption&gt;
    This is the directory structure of the app which contains Components, Containers, and Reducers, pretty common with a standard React/Redux app.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img src=&quot;/0e0211f619a5030b980b4f549e0d77df/2.gif&quot; alt=&quot;example&quot;&gt;
  &lt;figcaption&gt;Simple React Native Redux Example app running in iPhone Simulator.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To show the list of items, I’m using the React Native ListView that is using a Redux Store(items) as its dataSource. There are two actions in the Reducer; ADD_ITEM and REMOVE_ITEM.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Start the sequence of item ID&apos;s at 0&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; nextItemId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Items reducer&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;items&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ADD_ITEM&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; nextItemId&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          bgColor&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bgColor
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;REMOVE_ITEM&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Find index of item with matching ID and then&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// remove it from the array by its&apos; index&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; action&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The ListView requires a dataSource, and must be re-rendered when the dataSource is updated. To do this, the dataSource gets mapped to a prop using mapStateToPros, and then we connect the Component to it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ItemList&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;handleDestroyItem &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handleDestroyItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;handleDestroyItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;REMOVE_ITEM&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ListView
        style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        enableEmptySections&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        dataSource&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataSource&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        renderRow&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;rowData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Item
              rowData&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;rowData&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
              handleDestroyItem&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handleDestroyItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Handle data source change from redux store&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dataSource &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ListView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DataSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;rowHasChanged&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;r1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; r1 &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; r2
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapStateToProps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    dataSource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; dataSource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cloneWithRows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
ItemList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;propTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  dataSource&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  dispatch&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;func
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mapStateToProps&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ItemList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;View the entire code at &lt;a href=&quot;https://github.com/aaronvb/react-native-redux-example&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/aaronvb/react-native-redux-example&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A 60% Keyboard w/ Arrow Keys Build Log]]></title><description><![CDATA[A 60% Mechanical Keyboard build log featuring arrow keys.]]></description><link>https://aaronvb.com/articles/60-percent-keyboard</link><guid isPermaLink="false">https://aaronvb.com/articles/60-percent-keyboard</guid><pubDate>Thu, 18 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My 60% keyboard with arrow keys XD60 build, with more cat!&lt;/p&gt;
&lt;p&gt;With the &lt;a href=&quot;https://www.massdrop.com/buy/xd60-xd64-custom-mechanical-keyboard-kit?mode=guest_open&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;drop&lt;/a&gt; happening right now hopefully I can provide a little insight on this KB.&lt;/p&gt;
&lt;p&gt;A little background; I love the idea of arrow keys on 60% keyboards. My daily driver was a modified Infinity with arrow keys:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/39a5f67ff1f9b2bb6639864671e486b9/eea4a/bZDkW9E.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIBAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAADAv/aAAwDAQACEAMQAAAB3zSwog4d/wD/xAAZEAACAwEAAAAAAAAAAAAAAAAAAQIREhD/2gAIAQEAAQUCoa5tXpSKP//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAEDAQE/Aaf/xAAVEQEBAAAAAAAAAAAAAAAAAAAQIv/aAAgBAgEBPwGT/8QAFxABAAMAAAAAAAAAAAAAAAAAEAARMf/aAAgBAQAGPwKaW//EABsQAQACAwEBAAAAAAAAAAAAAAEAESExYUFR/9oACAEBAAE/Idk+cghhEvtwPwYm3cKGKn//2gAMAwEAAgADAAAAEADP/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAEhMXH/2gAIAQMBAT8Q2KpP/8QAGBEBAAMBAAAAAAAAAAAAAAAAAQARMSH/2gAIAQIBAT8Qs6Md5P/EABsQAQACAgMAAAAAAAAAAAAAAAEAESFxMUGB/9oACAEBAAE/EAIEypvKKFwOUKg0H0iSyvdMAC9yFUwan//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/39a5f67ff1f9b2bb6639864671e486b9/8ac56/bZDkW9E.webp 240w,
/static/39a5f67ff1f9b2bb6639864671e486b9/d3be9/bZDkW9E.webp 480w,
/static/39a5f67ff1f9b2bb6639864671e486b9/e46b2/bZDkW9E.webp 960w,
/static/39a5f67ff1f9b2bb6639864671e486b9/af3f0/bZDkW9E.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/39a5f67ff1f9b2bb6639864671e486b9/09b79/bZDkW9E.jpg 240w,
/static/39a5f67ff1f9b2bb6639864671e486b9/7cc5e/bZDkW9E.jpg 480w,
/static/39a5f67ff1f9b2bb6639864671e486b9/6a068/bZDkW9E.jpg 960w,
/static/39a5f67ff1f9b2bb6639864671e486b9/eea4a/bZDkW9E.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/39a5f67ff1f9b2bb6639864671e486b9/6a068/bZDkW9E.jpg&quot;
          alt=&quot;bZDkW9E.jpg&quot;
          title=&quot;bZDkW9E.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I was lucky to get 1.25u arrow keys on the first Granite drop to make that build happen. I’ve had my eye on the XD60 for awhile but did not like the idea of moving the ? key or finding a 2u shift so I held off until I had a reason to build another KB.&lt;/p&gt;
&lt;p&gt;I picked up the PCB and two plates from Taobao, it took about 2 weeks to get delivered. My original plan was to go with the 2u shift, but at the last moment I decided to try 2x 1u shift. It’s pretty much impossible to get a 2u shift now days. I also decided to try PCB mount switches, my first on all the keyboards I’ve built.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/eea4a/aw8RQZj.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEDBP/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHAqMpFQf/EABsQAAEFAQEAAAAAAAAAAAAAAAMAAQIREwQU/9oACAEBAAEFAnMRbzpjTT8xL8pKwmv/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFH/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BV//EABsQAAEEAwAAAAAAAAAAAAAAAAABESExMqHh/9oACAEBAAY/AslLL0Wg8MdP/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERIUGRUf/aAAgBAQABPyGaMEiB43BskWU6ER//2gAMAwEAAgADAAAAECcv/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAERUf/aAAgBAwEBPxCMrD//xAAXEQADAQAAAAAAAAAAAAAAAAAAARFR/9oACAECAQE/EKiNP//EAB4QAQACAgEFAAAAAAAAAAAAAAEAESFRMUFhcZHB/9oACAEBAAE/EGhIBM69QpkDZcAObPVPkZVS90hLL4NZ61LiAHiP/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/8ac56/aw8RQZj.webp 240w,
/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/d3be9/aw8RQZj.webp 480w,
/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/e46b2/aw8RQZj.webp 960w,
/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/af3f0/aw8RQZj.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/09b79/aw8RQZj.jpg 240w,
/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/7cc5e/aw8RQZj.jpg 480w,
/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/6a068/aw8RQZj.jpg 960w,
/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/eea4a/aw8RQZj.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/6daa6a5d3be79da73a0c8d9f6f71f9ef/6a068/aw8RQZj.jpg&quot;
          alt=&quot;aw8RQZj.jpg&quot;
          title=&quot;aw8RQZj.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Ordered from Taobao, took about 3 weeks.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d8f8f7629a774ab407e0a2218aa1f69c/eea4a/THVIc91.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAQFAv/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAFtHKkUyYV//8QAGxAAAgIDAQAAAAAAAAAAAAAAAgMAAQQREhT/2gAIAQEAAQUCyXUofRRQGgIvWwj1K6n/xAAWEQEBAQAAAAAAAAAAAAAAAAAAESH/2gAIAQMBAT8B1X//xAAWEQADAAAAAAAAAAAAAAAAAAAAARH/2gAIAQIBAT8BUIj/xAAcEAADAAEFAAAAAAAAAAAAAAAAAQIhEBESMkH/2gAIAQEABj8Ccz38FylYNh1S0wf/xAAeEAACAQMFAAAAAAAAAAAAAAABEQAhQWExUXGBof/aAAgBAQABPyEkSMdIi1N1YgB7AtWc6QkLI8wk25n/2gAMAwEAAgADAAAAEEfP/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAEDAQE/EKaf/8QAFREBAQAAAAAAAAAAAAAAAAAAAGH/2gAIAQIBAT8QEH//xAAcEAACAgMBAQAAAAAAAAAAAAABEQAhMUFxUZH/2gAIAQEAAT8QBOdIH1CCn4HAytwAoMm3yX5MX1BDT7oQ+Gjk7QNF6W5//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/d8f8f7629a774ab407e0a2218aa1f69c/8ac56/THVIc91.webp 240w,
/static/d8f8f7629a774ab407e0a2218aa1f69c/d3be9/THVIc91.webp 480w,
/static/d8f8f7629a774ab407e0a2218aa1f69c/e46b2/THVIc91.webp 960w,
/static/d8f8f7629a774ab407e0a2218aa1f69c/af3f0/THVIc91.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/d8f8f7629a774ab407e0a2218aa1f69c/09b79/THVIc91.jpg 240w,
/static/d8f8f7629a774ab407e0a2218aa1f69c/7cc5e/THVIc91.jpg 480w,
/static/d8f8f7629a774ab407e0a2218aa1f69c/6a068/THVIc91.jpg 960w,
/static/d8f8f7629a774ab407e0a2218aa1f69c/eea4a/THVIc91.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/d8f8f7629a774ab407e0a2218aa1f69c/6a068/THVIc91.jpg&quot;
          alt=&quot;THVIc91.jpg&quot;
          title=&quot;THVIc91.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Decided to go with PCB mount switches.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d132e6d37696ef21fb990cc18fb06249/eea4a/Ebfn7m5.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAMCBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAABbCsqNEQL/8QAGRAAAgMBAAAAAAAAAAAAAAAAAQIABBET/9oACAEBAAEFAg0NgCdGWO6oew0XBn//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAXEQEAAwAAAAAAAAAAAAAAAAAAARFR/9oACAECAQE/AVzj/8QAGhAAAwADAQAAAAAAAAAAAAAAAAECEBEhMf/aAAgBAQAGPwI05ZyWON+Y7J//xAAdEAEAAwABBQAAAAAAAAAAAAABABEhMUFRgZGx/9oACAEBAAE/IRN+SgsVoalOvBjEYunOWBYX1OoD3Mn/2gAMAwEAAgADAAAAEIcP/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARUf/aAAgBAwEBPxBbDt//xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAECAQE/EEspAf/EABwQAQACAwADAAAAAAAAAAAAAAEAESFBUTFhsf/aAAgBAQABPxDLAvaLYhp7oeQAcG1o+bTnAmDt4t7rg9cGcwUD7k+J/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/d132e6d37696ef21fb990cc18fb06249/8ac56/Ebfn7m5.webp 240w,
/static/d132e6d37696ef21fb990cc18fb06249/d3be9/Ebfn7m5.webp 480w,
/static/d132e6d37696ef21fb990cc18fb06249/e46b2/Ebfn7m5.webp 960w,
/static/d132e6d37696ef21fb990cc18fb06249/af3f0/Ebfn7m5.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/d132e6d37696ef21fb990cc18fb06249/09b79/Ebfn7m5.jpg 240w,
/static/d132e6d37696ef21fb990cc18fb06249/7cc5e/Ebfn7m5.jpg 480w,
/static/d132e6d37696ef21fb990cc18fb06249/6a068/Ebfn7m5.jpg 960w,
/static/d132e6d37696ef21fb990cc18fb06249/eea4a/Ebfn7m5.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/d132e6d37696ef21fb990cc18fb06249/6a068/Ebfn7m5.jpg&quot;
          alt=&quot;Ebfn7m5.jpg&quot;
          title=&quot;Ebfn7m5.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After switches soldered I decided to add LED’s. I’m not a huge fan of back-lighting but I had 200 white LED’s from an old an project.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/29e2cf2ccece07a1c07047bf69fc0bc6/eea4a/vdyqZCq.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQCAwX/xAAWAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAV7Gc4dJmL//xAAZEAADAQEBAAAAAAAAAAAAAAAAAQIDBBP/2gAIAQEAAQUCaUUt0j25ztknJ2TjCX//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFH/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAEREv/aAAgBAgEBPwG4af/EABwQAAMAAQUAAAAAAAAAAAAAAAABIRICETFBkf/aAAgBAQAGPwJppQ2xhdK8M10WI4P/xAAZEAADAQEBAAAAAAAAAAAAAAAAAREhUaH/2gAIAQEAAT8hsz+kbJGhTaQiJO4NqOKIUf/aAAwDAQACAAMAAAAQsN//xAAWEQEBAQAAAAAAAAAAAAAAAAABECH/2gAIAQMBAT8QBXJ//8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAgEBPxBA7Zv/xAAeEAEAAgEEAwAAAAAAAAAAAAABABEhMUFRcYGh8P/aAAgBAQABPxC0aKEHJopxBcUgCg8bR7bbL7JjWygascfdxStLZtsMBPKhP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/29e2cf2ccece07a1c07047bf69fc0bc6/8ac56/vdyqZCq.webp 240w,
/static/29e2cf2ccece07a1c07047bf69fc0bc6/d3be9/vdyqZCq.webp 480w,
/static/29e2cf2ccece07a1c07047bf69fc0bc6/e46b2/vdyqZCq.webp 960w,
/static/29e2cf2ccece07a1c07047bf69fc0bc6/af3f0/vdyqZCq.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/29e2cf2ccece07a1c07047bf69fc0bc6/09b79/vdyqZCq.jpg 240w,
/static/29e2cf2ccece07a1c07047bf69fc0bc6/7cc5e/vdyqZCq.jpg 480w,
/static/29e2cf2ccece07a1c07047bf69fc0bc6/6a068/vdyqZCq.jpg 960w,
/static/29e2cf2ccece07a1c07047bf69fc0bc6/eea4a/vdyqZCq.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/29e2cf2ccece07a1c07047bf69fc0bc6/6a068/vdyqZCq.jpg&quot;
          alt=&quot;vdyqZCq.jpg&quot;
          title=&quot;vdyqZCq.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;LED’s installed and working. Note that there were 3 switches which had the positive and negative solder points switched. Always double check the LED’s before soldering.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0debe9f21dea522fbe10971041daa5d3/eea4a/3QYUQ9w.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEFBP/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAY6oMnm8P//EABkQAAIDAQAAAAAAAAAAAAAAAAATAhARIf/aAAgBAQABBQKuCBEcQf/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AYj/xAAWEQADAAAAAAAAAAAAAAAAAAAAARL/2gAIAQIBAT8BpFI//8QAFxABAQEBAAAAAAAAAAAAAAAAADIQof/aAAgBAQAGPwLaVxT/xAAbEAADAAIDAAAAAAAAAAAAAAAAAREhMUFRkf/aAAgBAQABPyHdzilG02myEpcfELrGonv4f//aAAwDAQACAAMAAAAQmC//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPxClP//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAECAQE/EGB//8QAHBABAAMBAAMBAAAAAAAAAAAAAQARITFBcaHR/9oACAEBAAE/EFLOD0HjAANl15vp+koDMsRTIJU+gwykBLrTfexai8ML4+z/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/0debe9f21dea522fbe10971041daa5d3/8ac56/3QYUQ9w.webp 240w,
/static/0debe9f21dea522fbe10971041daa5d3/d3be9/3QYUQ9w.webp 480w,
/static/0debe9f21dea522fbe10971041daa5d3/e46b2/3QYUQ9w.webp 960w,
/static/0debe9f21dea522fbe10971041daa5d3/af3f0/3QYUQ9w.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/0debe9f21dea522fbe10971041daa5d3/09b79/3QYUQ9w.jpg 240w,
/static/0debe9f21dea522fbe10971041daa5d3/7cc5e/3QYUQ9w.jpg 480w,
/static/0debe9f21dea522fbe10971041daa5d3/6a068/3QYUQ9w.jpg 960w,
/static/0debe9f21dea522fbe10971041daa5d3/eea4a/3QYUQ9w.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/0debe9f21dea522fbe10971041daa5d3/6a068/3QYUQ9w.jpg&quot;
          alt=&quot;3QYUQ9w.jpg&quot;
          title=&quot;3QYUQ9w.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/79ff830cb4c99b2a7931533a95833ece/eea4a/xGy38EL.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAgX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAv/aAAwDAQACEAMQAAABbshEuoLg/wD/xAAaEAADAAMBAAAAAAAAAAAAAAABAgMABBQT/9oACAEBAAEFAvREPRPFdXFNc0fjOSmZr//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAEDAQE/AYj/xAAXEQEAAwAAAAAAAAAAAAAAAAAAARES/9oACAECAQE/Abal/8QAGhAAAwADAQAAAAAAAAAAAAAAAAERAhIyIf/aAAgBAQAGPwKPJU6RcXR5bL07RNj/xAAcEAADAAEFAAAAAAAAAAAAAAAAARFhITFR4fH/2gAIAQEAAT8huRGT1yACYI0OEF0A/wA3rdj/2gAMAwEAAgADAAAAECPP/8QAGBEBAQADAAAAAAAAAAAAAAAAAQARMVH/2gAIAQMBAT8QcnUHl//EABcRAQEBAQAAAAAAAAAAAAAAAAERACH/2gAIAQIBAT8QAEuacd//xAAcEAEAAwACAwAAAAAAAAAAAAABABEhQXFh0fD/2gAIAQEAAT8QCmpo1TJyvx6jC4atZcQ4VYlmRRgPePWy7Hiep//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/79ff830cb4c99b2a7931533a95833ece/8ac56/xGy38EL.webp 240w,
/static/79ff830cb4c99b2a7931533a95833ece/d3be9/xGy38EL.webp 480w,
/static/79ff830cb4c99b2a7931533a95833ece/e46b2/xGy38EL.webp 960w,
/static/79ff830cb4c99b2a7931533a95833ece/af3f0/xGy38EL.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/79ff830cb4c99b2a7931533a95833ece/09b79/xGy38EL.jpg 240w,
/static/79ff830cb4c99b2a7931533a95833ece/7cc5e/xGy38EL.jpg 480w,
/static/79ff830cb4c99b2a7931533a95833ece/6a068/xGy38EL.jpg 960w,
/static/79ff830cb4c99b2a7931533a95833ece/eea4a/xGy38EL.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/79ff830cb4c99b2a7931533a95833ece/6a068/xGy38EL.jpg&quot;
          alt=&quot;xGy38EL.jpg&quot;
          title=&quot;xGy38EL.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f9136db7c49b88b994ff682a8a368cbe/eea4a/n9eX7ih.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAIA/9oADAMBAAIQAxAAAAG75ilaRIN//8QAGRABAAMBAQAAAAAAAAAAAAAAAgABAxMh/9oACAEBAAEFAj5UqPK0uCmQsn//xAAXEQEAAwAAAAAAAAAAAAAAAAAAARES/9oACAEDAQE/Acwp/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8Bqv/EABkQAAIDAQAAAAAAAAAAAAAAAAABEBEhQf/aAAgBAQAGPwKW8OFM/8QAHBAAAgICAwAAAAAAAAAAAAAAAAERITFBkbHw/9oACAEBAAE/Id/sT9JbkipZifoUcyf/2gAMAwEAAgADAAAAEDgv/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARIf/aAAgBAwEBPxDTrBv/xAAXEQADAQAAAAAAAAAAAAAAAAAAAREh/9oACAECAQE/EFmUbU//xAAcEAADAAIDAQAAAAAAAAAAAAAAAREhMVFxkaH/2gAIAQEAAT8QfBtXpCu0vBCOTDL6KY7JX1ChBYInTi4P/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/f9136db7c49b88b994ff682a8a368cbe/8ac56/n9eX7ih.webp 240w,
/static/f9136db7c49b88b994ff682a8a368cbe/d3be9/n9eX7ih.webp 480w,
/static/f9136db7c49b88b994ff682a8a368cbe/e46b2/n9eX7ih.webp 960w,
/static/f9136db7c49b88b994ff682a8a368cbe/af3f0/n9eX7ih.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/f9136db7c49b88b994ff682a8a368cbe/09b79/n9eX7ih.jpg 240w,
/static/f9136db7c49b88b994ff682a8a368cbe/7cc5e/n9eX7ih.jpg 480w,
/static/f9136db7c49b88b994ff682a8a368cbe/6a068/n9eX7ih.jpg 960w,
/static/f9136db7c49b88b994ff682a8a368cbe/eea4a/n9eX7ih.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/f9136db7c49b88b994ff682a8a368cbe/6a068/n9eX7ih.jpg&quot;
          alt=&quot;n9eX7ih.jpg&quot;
          title=&quot;n9eX7ih.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Under-glow LED’s working as well. Too bad I wont see them with my case.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/041b6f130778eb4b536b1f918c7eab94/eea4a/k73nMHW.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAIDAQT/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABnN9LoEB//8QAGhABAAIDAQAAAAAAAAAAAAAAAQACAwQUEv/aAAgBAQABBQJ2yGxUKZvZxs42VwNT/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQIBAT8Bh//EABkQAAIDAQAAAAAAAAAAAAAAAAARARIyof/aAAgBAQAGPwLHRU6OpqDcCuf/xAAdEAACAgEFAAAAAAAAAAAAAAAAAREhMWFxkcHx/9oACAEBAAE/IWE1JaoRQ73EhRcZKOo8Arxwf//aAAwDAQACAAMAAAAQSO//xAAVEQEBAAAAAAAAAAAAAAAAAAAAIf/aAAgBAwEBPxCq/8QAFREBAQAAAAAAAAAAAAAAAAAAAQD/2gAIAQIBAT8QiF//xAAfEAEAAgEDBQAAAAAAAAAAAAABABEhMUFRYYGhsdH/2gAIAQEAAT8QftorBTvULhhsyvqCI6YKux9g1vMlAanRxiEtvD4J/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/041b6f130778eb4b536b1f918c7eab94/8ac56/k73nMHW.webp 240w,
/static/041b6f130778eb4b536b1f918c7eab94/d3be9/k73nMHW.webp 480w,
/static/041b6f130778eb4b536b1f918c7eab94/e46b2/k73nMHW.webp 960w,
/static/041b6f130778eb4b536b1f918c7eab94/af3f0/k73nMHW.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/041b6f130778eb4b536b1f918c7eab94/09b79/k73nMHW.jpg 240w,
/static/041b6f130778eb4b536b1f918c7eab94/7cc5e/k73nMHW.jpg 480w,
/static/041b6f130778eb4b536b1f918c7eab94/6a068/k73nMHW.jpg 960w,
/static/041b6f130778eb4b536b1f918c7eab94/eea4a/k73nMHW.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/041b6f130778eb4b536b1f918c7eab94/6a068/k73nMHW.jpg&quot;
          alt=&quot;k73nMHW.jpg&quot;
          title=&quot;k73nMHW.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;PCB mount stabilizers.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d3912cd8eda803e0a52ef78abdb9dd3b/eea4a/DppmbKM.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAMEAQX/xAAWAQEBAQAAAAAAAAAAAAAAAAABAAL/2gAMAwEAAhADEAAAAWRdKXNgwH//xAAaEAEAAgMBAAAAAAAAAAAAAAACAAEDBBIj/9oACAEBAAEFAszHXnDyqya4t3rmEUa//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQIBAT8Bh//EABkQAAMAAwAAAAAAAAAAAAAAAAABEQIhMf/aAAgBAQAGPwJ4u6OsqHlXs6yH/8QAHBAAAgICAwAAAAAAAAAAAAAAAREAIRBBMVGR/9oACAEBAAE/IdzTQjAks6qolAV5LlJPnEFe5//aAAwDAQACAAMAAAAQbz//xAAVEQEBAAAAAAAAAAAAAAAAAAABEP/aAAgBAwEBPxBGf//EABcRAQEBAQAAAAAAAAAAAAAAAAEAESH/2gAIAQIBAT8QHHGb/8QAHhABAAICAQUAAAAAAAAAAAAAAQARITFhQVFxgaH/2gAIAQEAAT8QW0CrkNnjMvRTdMR6GIgF2X4uMCxI0tibKDhgtEHVn//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/d3912cd8eda803e0a52ef78abdb9dd3b/8ac56/DppmbKM.webp 240w,
/static/d3912cd8eda803e0a52ef78abdb9dd3b/d3be9/DppmbKM.webp 480w,
/static/d3912cd8eda803e0a52ef78abdb9dd3b/e46b2/DppmbKM.webp 960w,
/static/d3912cd8eda803e0a52ef78abdb9dd3b/af3f0/DppmbKM.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/d3912cd8eda803e0a52ef78abdb9dd3b/09b79/DppmbKM.jpg 240w,
/static/d3912cd8eda803e0a52ef78abdb9dd3b/7cc5e/DppmbKM.jpg 480w,
/static/d3912cd8eda803e0a52ef78abdb9dd3b/6a068/DppmbKM.jpg 960w,
/static/d3912cd8eda803e0a52ef78abdb9dd3b/eea4a/DppmbKM.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/d3912cd8eda803e0a52ef78abdb9dd3b/6a068/DppmbKM.jpg&quot;
          alt=&quot;DppmbKM.jpg&quot;
          title=&quot;DppmbKM.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ea268629a6170a0abcaadc3f5ba13d4f/eea4a/YZnvLds.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwAE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAgAD/9oADAMBAAIQAxAAAAHULkYJYaf/xAAaEAEAAgMBAAAAAAAAAAAAAAABAAIREiEy/9oACAEBAAEFAre2clg2sDME/8QAFREBAQAAAAAAAAAAAAAAAAAAECH/2gAIAQMBAT8Bp//EABURAQEAAAAAAAAAAAAAAAAAABAh/9oACAECAQE/AYf/xAAVEAEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAQAGPwJr/8QAGhAAAgMBAQAAAAAAAAAAAAAAAAERIUExUf/aAAgBAQABPyF7UqR6hEtcGmk6As0f/9oADAMBAAIAAwAAABAML//EABcRAQEBAQAAAAAAAAAAAAAAAAEAETH/2gAIAQMBAT8QBcsv/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARMf/aAAgBAgEBPxDT2W//xAAbEAEAAwEAAwAAAAAAAAAAAAABABEhQTFRcf/aAAgBAQABPxAKTIH3sICjDVWAaY+RGwZTXZjtQ54giqCf/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/ea268629a6170a0abcaadc3f5ba13d4f/8ac56/YZnvLds.webp 240w,
/static/ea268629a6170a0abcaadc3f5ba13d4f/d3be9/YZnvLds.webp 480w,
/static/ea268629a6170a0abcaadc3f5ba13d4f/e46b2/YZnvLds.webp 960w,
/static/ea268629a6170a0abcaadc3f5ba13d4f/af3f0/YZnvLds.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/ea268629a6170a0abcaadc3f5ba13d4f/09b79/YZnvLds.jpg 240w,
/static/ea268629a6170a0abcaadc3f5ba13d4f/7cc5e/YZnvLds.jpg 480w,
/static/ea268629a6170a0abcaadc3f5ba13d4f/6a068/YZnvLds.jpg 960w,
/static/ea268629a6170a0abcaadc3f5ba13d4f/eea4a/YZnvLds.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/ea268629a6170a0abcaadc3f5ba13d4f/6a068/YZnvLds.jpg&quot;
          alt=&quot;YZnvLds.jpg&quot;
          title=&quot;YZnvLds.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6a379a60f309071d86aadd9f49af925e/eea4a/TdlYBKF.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIEAf/EABUBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAGxVyWcrB//xAAYEAADAQEAAAAAAAAAAAAAAAAAARETAv/aAAgBAQABBQIcRormzPknJ//EABYRAAMAAAAAAAAAAAAAAAAAAAABEf/aAAgBAwEBPwGMjP/EABYRAAMAAAAAAAAAAAAAAAAAAAABEf/aAAgBAgEBPwGoqP/EABkQAQACAwAAAAAAAAAAAAAAAAAhMQECQf/aAAgBAQAGPwJLid8qU//EABsQAAMAAgMAAAAAAAAAAAAAAAABESFBcZGx/9oACAEBAAE/IXnQy7ih0RwPwNYt9ciRp0f/2gAMAwEAAgADAAAAEMsf/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAFh/9oACAEDAQE/EItn/8QAFxEBAAMAAAAAAAAAAAAAAAAAAAFRYf/aAAgBAgEBPxCamT//xAAcEAEBAQACAwEAAAAAAAAAAAABEQAhQTFRYbH/2gAIAQEAAT8QHmpyIj3O5oKZXrKAYBIkfmC2k75YSAHzP//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/6a379a60f309071d86aadd9f49af925e/8ac56/TdlYBKF.webp 240w,
/static/6a379a60f309071d86aadd9f49af925e/d3be9/TdlYBKF.webp 480w,
/static/6a379a60f309071d86aadd9f49af925e/e46b2/TdlYBKF.webp 960w,
/static/6a379a60f309071d86aadd9f49af925e/af3f0/TdlYBKF.webp 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/6a379a60f309071d86aadd9f49af925e/09b79/TdlYBKF.jpg 240w,
/static/6a379a60f309071d86aadd9f49af925e/7cc5e/TdlYBKF.jpg 480w,
/static/6a379a60f309071d86aadd9f49af925e/6a068/TdlYBKF.jpg 960w,
/static/6a379a60f309071d86aadd9f49af925e/eea4a/TdlYBKF.jpg 1280w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/6a379a60f309071d86aadd9f49af925e/6a068/TdlYBKF.jpg&quot;
          alt=&quot;TdlYBKF.jpg&quot;
          title=&quot;TdlYBKF.jpg&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;A few of my thoughts so far: The under-glow is neat but I’ll need a new case. It’s weird having 2x 1u shift, still not used to it yet. PCB mount feels more unstable than plate mount, might be louder also. The cat logo is really cool. I should have used SIP sockets. Down and Right arrow LED have opposite positive and negative terminals.&lt;/p&gt;
&lt;p&gt;I’ve open-sourced the keymap for my keyboard, check it out at &lt;a href=&quot;https://github.com/aaronvb/xd60-keymap&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/aaronvb/xd60-keymap&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Street Photography Class]]></title><description><![CDATA[Introduction to Street Photography is a photography class aimed toward new photographers who want to learn the basics of street photography]]></description><link>https://aaronvb.com/articles/intro-street-photography</link><guid isPermaLink="false">https://aaronvb.com/articles/intro-street-photography</guid><pubDate>Fri, 05 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week I taught a photography class at Hawaii Camera aimed at new photographers who were interested in learning the basics of street photography. It was my third photography class, and my first street photography class.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.eventbrite.com/e/street-photography-an-introduction-for-new-photographers-tickets-33878881647&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;www.eventbrite.com/e/street-photography-an-introduction-for-new-photographers-tickets-33878881647&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Overall the turnout was better than I expected and everyone had walked out with a new perspective on photography.&lt;/p&gt;
&lt;p&gt;I would like to share the PDF of my presentation: &lt;a href=&quot;/0d7cbaa0532a927c947697a3c415b891/streetphotography_by_aaronvb.pdf&quot;&gt;streetphotography by aaronvb.pdf&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ember.js Flash Messages]]></title><link>https://aaronvb.com/articles/ember-js-flash-messages</link><guid isPermaLink="false">https://aaronvb.com/articles/ember-js-flash-messages</guid><pubDate>Wed, 30 Mar 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is a simple way to create flash messages that are similar to the Ruby on Rails flash messages. I took from Yehuda Katz’s answer on &lt;a href=&quot;https://stackoverflow.com/a/14301065/141190&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this Stack Overflow post&lt;/a&gt;, and extended it to work with the updated controllerFor/needs changes and closing on transitions.&lt;/p&gt;
&lt;h4 id=&quot;applicationhandlebars&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#applicationhandlebars&quot; aria-label=&quot;applicationhandlebars permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application.handlebars&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;handlebars&quot;&gt;&lt;pre class=&quot;language-handlebars&quot;&gt;&lt;code class=&quot;language-handlebars&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token block keyword&quot;&gt;#if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;bindAttr&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;:alert notification.type&quot;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;notification&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;btn-close&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;closeNotification&quot;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token block keyword&quot;&gt;#if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token block keyword&quot;&gt;/if&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token block keyword&quot;&gt;/if&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;outlet&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;application_controllerjscoffee&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#application_controllerjscoffee&quot; aria-label=&quot;application_controllerjscoffee permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;application_controller.js.coffee&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;coffee&quot;&gt;&lt;pre class=&quot;language-coffee&quot;&gt;&lt;code class=&quot;language-coffee&quot;&gt;App&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApplicationController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Ember&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Controller&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;extend&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# close notification alert&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# bind to action in template, example: {{action &quot;closeNotification&quot;}}&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# detects if persists and closes on next transition&lt;/span&gt;
  #
  &lt;span class=&quot;token property&quot;&gt;closeNotification&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    notification &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;notification&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; notification
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persists
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log &lt;span class=&quot;token string&quot;&gt;&quot;Notification detected, clearing alert notification after next transition&quot;&lt;/span&gt;
        notification&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persists &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;log &lt;span class=&quot;token string&quot;&gt;&quot;Notification detected, clearing alert notification now&quot;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;notification&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# notification alert&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# type can be: error, info, success&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# example: @get(&apos;controllers.application&apos;).notify({title: &quot;Error!&quot;, message: &quot;An error occurred in foobar.&quot;, type: &quot;alert-error&quot;})&lt;/span&gt;
  #
  &lt;span class=&quot;token property&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;notification&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;posts_controllerjscoffee&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#posts_controllerjscoffee&quot; aria-label=&quot;posts_controllerjscoffee permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;posts_controller.js.coffee&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;coffee&quot;&gt;&lt;pre class=&quot;language-coffee&quot;&gt;&lt;code class=&quot;language-coffee&quot;&gt;App&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PostsNewController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Ember&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ObjectController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;extend&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;application&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# allows access to application controller functions&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# post save&lt;/span&gt;
  #
  &lt;span class=&quot;token property&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    @&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;model.title&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;title&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    @&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;model.description&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;description&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;model&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# if post is not valid, show flash message&lt;/span&gt;
    #
    post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;becameInvalid&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;controllers.application&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;There was an error creating this post.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alert-error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# if post was created transition to posts index, show flash message and persist it to next transition&lt;/span&gt;
    #
    post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;didCreate&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;controllers.application&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A new post!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation variable&quot;&gt;#{@get(&apos;model.title&apos;)}&lt;/span&gt; was created.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;alert-success&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;persists&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      @&lt;span class=&quot;token function&quot;&gt;transitionToRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;posts.index&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# commit post to server&lt;/span&gt;
    #
    @&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;model.transaction&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id=&quot;routerjscoffee&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#routerjscoffee&quot; aria-label=&quot;routerjscoffee permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;router.js.coffee&lt;/h4&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;coffee&quot;&gt;&lt;pre class=&quot;language-coffee&quot;&gt;&lt;code class=&quot;language-coffee&quot;&gt;App&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BeforeRoute &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Ember&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Route&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;extend&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# close any open notifcations before a route loads&lt;/span&gt;
  #
  &lt;span class=&quot;token property&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    @&lt;span class=&quot;token function&quot;&gt;controllerFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;application&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closeNotification&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

App&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PostsIndexRoute &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; App&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;BeforeRoute&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;extend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the application.handlebars template I create a condition that checks if the notification variable is set, and if it is, display it with it’s message and alert-type class(error, success, info, etc).&lt;/p&gt;
&lt;p&gt;In the application controller, there’s a function that creates the notification and passes options to it, and there’s also a function that closes the notification. The closeNotification first checks for a ‘persists’ flag and if it doesn’t exist it sets the notification variable to null, which closes it. If a ‘persists’ flag does exist, it sets it the flag to null and skips closing the notification. On the next transition, the ‘persists’ flag wont exist and the notification will be closed.&lt;/p&gt;
&lt;p&gt;In the posts new controller, at the top I have a needs array, which let’s the controller know that we need access to the application controller. The notifications are hooked into the ‘on’ callbacks for the post model, and called by using @get(‘controllers.application’).notify(). In earlier versions of Ember.js you would directly use the controllerFor() function, but that was deprecated for ‘needs’.&lt;/p&gt;
&lt;p&gt;Lastly in the router, I created an App.BeforeRoute extension of Ember.Route which all of my routes extend from. This way every route will execute the activate function, which executes the closeNotification function, before every route loads.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[KWM & Ubersicht Bottom Bar]]></title><description><![CDATA[A simple bottom bar widget for OS X and Ubersicht.]]></description><link>https://aaronvb.com/articles/ubersicht-bottombar</link><guid isPermaLink="false">https://aaronvb.com/articles/ubersicht-bottombar</guid><pubDate>Wed, 30 Mar 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently made the switch to a tiling window manager in OS X. After watching my  friend use i3 on his Linux machine, OS X started to feel outdated to me, ironically. OS X really lacks a solid window management experience. I’ve messed around with the built in Spaces, but would revert back to one space, and run every app at max size, relying on cmd+tab to move around.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/eczarny/spectacle&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Spectacle&lt;/a&gt; became my pseudo tiling manager for a short while, mainly for split screen work(which I became very fond of). But Spectacle, for me, wasn’t enough. It provided easy to use hotkeys for controlling window sizes, but I wanted it to be automated. I also wanted to control padding around windows, because I like padding!&lt;/p&gt;
&lt;p&gt;The same friend who showed me i3 suggested I try &lt;a href=&quot;https://github.com/koekeishiya/kwm&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;KWM&lt;/a&gt;, a real tiling manager built for OS X. It took me a bit to get used to the mouse focus control, and KWM can sometimes do weird things, but overall I’m pretty satisfied with it.&lt;/p&gt;
&lt;p&gt;Before my switch to KWM, I was using iStat Menus for system information and with the menu bar hidden, it became useless to run. The KWM repo shows an example of  nice menu bar replacement, with system information, and I thought that would be nicer if it was at the bottom, like i3. I used that as a starting point, added network traffic and weather data, and ended up with this:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/aaronvb/ubersicht-bottombar&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/aaronvb/ubersicht-bottombar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You’ll need to download &lt;a href=&quot;http://tracesof.net/uebersicht/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Übersicht&lt;/a&gt;, which is the widget app this uses.&lt;/p&gt;
&lt;p&gt;Here’s a screenshot of it in use:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 960px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9ad075460be27e914fce64a7014a46c8/29114/ubersicht3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAADGElEQVQ4y02SXUxTZxjHz503izOL2YWCVNrSgrSlraUrQkEjKptOjYkXejuzGJNt2RVxQV1hRgwRxSJKZEJgavzIENHEC6Mx0RF3gYlRE7MbDcR+wWmhp9DTj5/vOYehb/LPOU+evL/8n//zSp39N2ntu8HP3ddo67lK+K9Rzg6P8mPXCD+dGuRI721+uXSHY8MPaBNqv/6QM+P/0Knp7jNOjz2le/wpPaOPOX31HpLdF6TK30Ddjl007dpJ/bcteJu24go04W1sxlzj5/LAEFAkJctkFYV0KsViRiGnZsnnVF0ziQQjt24ilVW5WGNzYHJ6qN3WiGdzPVavj3UbPJS7fKwqNTNwZRDt5PMFCsUikQ8RIrEZsqIuilo7Gvzh3/1INrePsmoPNl8AR90m4ahWB2myer7ha7ONoeEh/dJidhFFUclqRSHDwmyMhYUFvacuKrwZP45UUSOAwo2roRF3sBGL27cE3KgDV5usDPw5YFzKFVHjXaT+PcXl8CD3Ww+hvn+r9+bnUpz4dR/SWjFuSaWL6roGAQ0KiH/ZYYU3wJdi5LPhPsOhAKYfdTH2wx5WfOGgb28Q3r/Se0mR79aWZqRSu1MHVvqD1AQ3i9E36bD1zo1UCIcrS8o50xPWL2VFTonX75iaeM2jkWtMj10hI8cNh/NzbP9uh+GwVADN7lqRn0+MbGRocnixuv2sXFtO9/kLxlIKReSZOC+ePGZOOCpoUS4tZT49x4EDuz8BNUefy1hKgK/KLPT09uqX5GSSaCRKclbWR0wlZdLpNLlcnmRyltDRg0gldgOoLeH/7PR/p7EUDRi+uAQUkA+RGLF4QjhKCykomYx4TnkS4h0279xrZKgDnZ8DPz0bDXhu2aHM9NQ7otEYU1PTJGIxFJGdqqrEBXDPwcNIa5ZHNhZh0uQwvlax5VWlFnov9utAinmibyYpKGlePp/gvxeT5OSE3tJG9zVsQTLZ7KyvtGN2VGNxbKDK48RT78XucWOpdrLObuf7/fsJdZ7kt/bfaeto51hHiON/dIi6naMh8RVqPREi0NzCR2tQoKFwj6diAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/9ad075460be27e914fce64a7014a46c8/8ac56/ubersicht3.webp 240w,
/static/9ad075460be27e914fce64a7014a46c8/d3be9/ubersicht3.webp 480w,
/static/9ad075460be27e914fce64a7014a46c8/e46b2/ubersicht3.webp 960w,
/static/9ad075460be27e914fce64a7014a46c8/f992d/ubersicht3.webp 1440w,
/static/9ad075460be27e914fce64a7014a46c8/882b9/ubersicht3.webp 1920w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/9ad075460be27e914fce64a7014a46c8/8ff5a/ubersicht3.png 240w,
/static/9ad075460be27e914fce64a7014a46c8/e85cb/ubersicht3.png 480w,
/static/9ad075460be27e914fce64a7014a46c8/d9199/ubersicht3.png 960w,
/static/9ad075460be27e914fce64a7014a46c8/07a9c/ubersicht3.png 1440w,
/static/9ad075460be27e914fce64a7014a46c8/29114/ubersicht3.png 1920w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/9ad075460be27e914fce64a7014a46c8/d9199/ubersicht3.png&quot;
          alt=&quot;ubersicht3.png&quot;
          title=&quot;ubersicht3.png&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[BitBar, Tarsnap, and Ruby]]></title><description><![CDATA[BitBar support for automated Tarsnap backups with Ruby.]]></description><link>https://aaronvb.com/articles/tarsnap-bitbar</link><guid isPermaLink="false">https://aaronvb.com/articles/tarsnap-bitbar</guid><pubDate>Sat, 27 Feb 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I came across a really cool OSX menu bar library and thought it would be a really good solution to show my Tarsnap backup statuses.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/matryer/bitbar&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/matryer/bitbar&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I love how easy it is to write a plugin for BitBar in Ruby.&lt;/p&gt;
&lt;p&gt;It’s as simple as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#!/usr/bin/ruby&lt;/span&gt;
puts &lt;span class=&quot;token string&quot;&gt;&quot;Cool Menubar&quot;&lt;/span&gt;
puts &lt;span class=&quot;token string&quot;&gt;&quot;---&quot;&lt;/span&gt;
puts &lt;span class=&quot;token string&quot;&gt;&quot;The time is: &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strftime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;%T&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Anyway, I updated my git repo to include a BitBar plugin and also updated the main backup script to output a nice JSON file for it to read: &lt;a href=&quot;https://github.com/aaronvb/tarsnap_backup&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/aaronvb/tarsnap_backup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And here’s a screenshot of it in use:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 570px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3c2f61f3269749f6a011a22033444644/432e7/bitbar_ss.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.66666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAAC6ElEQVQ4y12Tu28cZRTFp6YHSmpIG1o6JApERQOIAok2+QOoKEGx4oiAcIogKpqIgkcA2yLGWdvr9c7O7Lx23jM7j5197zrRehES4sfdsRwUiqM73/3uPffco/mU6dO/SAYT9hoq73275o2v/uGte39LhPuHE6I4om36/PBwDycq+O77n3DCnJOOzXBxQTZaMD7/EydI0ewAJc4GOH7EwVGLu7tDtvaecGd/we39JbtaiR/FWH5C41QlSEuO2jpunNPthULcp2O6hFmFbrl0BYqqqniei2ka+IFHnAqBY+O4PSm26EiRZntCGqE7Xo2WbtI2bOwgIeiXeEmGZpjYloXyzocfs8Hb73/Ew0cNWSsTFYasZKEK0Znh0NTMGqe6RdeLsITI9ONnMZQtP7u7w5vvfoDy0rXXefG167x87ToPft4lKoYY0mS4l412mNaxK2fNCZ7LXeXjouLGJ5/ywiuvomzv3Gf73jfc+nKnVqE7Pr83mhw2VZqdrnjX4eC4JeulRHmFnxb/Q04iIh78+Auff/E1ysXqgvV6zWw2I5VJThALSZsTMf9U1VG7No+bZ/jiUzmekQ8nFFcYTetzXk3IigGr1QpltlgyX54zqIakecloumA4mTMYT//DSJoGI/oysF9eIq9GFNWY8XzJZH5OlPQpBxXKaatF66zNwR+Hl8nhGDeI8MOYnh8SxGmNUO6CKJG/ICMSJFkh9RkDqZ/MFmQiphqOUG5tbXF7+w43bt5E65qEccJh4xhV69LzAh4fNWm22uzuP+LopEUlisfTOdPFOSP5/vW3PeyeRyHqasK8KOhnOW21Qy6rxGmf42aLE/Gx3dFrnKmaNAxrorGomc43Nj3h6WotVo1q1aXc14Sz+YLFcjNtQtLPZK2YrmHVajXdQJe4Ubtp2hifFeUlZMXNkFis8MWiVHprwqIc1AqzvMD1fExLXknPfRY36LnyUuRsyMvRNL2OV3lPevwgrPsn0xn/AmX8gCzWuqb+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/3c2f61f3269749f6a011a22033444644/8ac56/bitbar_ss.webp 240w,
/static/3c2f61f3269749f6a011a22033444644/d3be9/bitbar_ss.webp 480w,
/static/3c2f61f3269749f6a011a22033444644/048c8/bitbar_ss.webp 570w&quot;
          sizes=&quot;(max-width: 570px) 100vw, 570px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/3c2f61f3269749f6a011a22033444644/8ff5a/bitbar_ss.png 240w,
/static/3c2f61f3269749f6a011a22033444644/e85cb/bitbar_ss.png 480w,
/static/3c2f61f3269749f6a011a22033444644/432e7/bitbar_ss.png 570w&quot;
          sizes=&quot;(max-width: 570px) 100vw, 570px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/3c2f61f3269749f6a011a22033444644/432e7/bitbar_ss.png&quot;
          alt=&quot;bitbar_ss.png&quot;
          title=&quot;bitbar_ss.png&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ruby on Rails Wrapper for react-stdio]]></title><description><![CDATA[A simple Ruby on Rails wrapper for react-stdio.]]></description><link>https://aaronvb.com/articles/rails-react-stdio</link><guid isPermaLink="false">https://aaronvb.com/articles/rails-react-stdio</guid><pubDate>Mon, 01 Feb 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the past weekend I was exploring a different method to server side render(aka prerender) react components in Ruby on Rails.&lt;/p&gt;
&lt;p&gt;Right now the two most popular  Rails + React gems are &lt;a href=&quot;https://github.com/reactjs/react-rails&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;react-rails&lt;/a&gt; and &lt;a href=&quot;https://github.com/shakacode/react_on_rails&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;react on rails&lt;/a&gt;. Both of them rely on ExecJS(using therubyracer), with react-rails also supporting sprockets, to prerender react components.&lt;/p&gt;
&lt;p&gt;If you’re not familiar with prerendering react components on the server, it’s simply Rails rendering the react component in the view before the DOM is loaded. This can be beneficial for SEO reasons.&lt;/p&gt;
&lt;p&gt;I came across &lt;a href=&quot;https://github.com/mjackson/react-stdio&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;react-stdio&lt;/a&gt; (not to be confused with studio) while working on issues in the react&lt;em&gt;on&lt;/em&gt;rails repo: &lt;a href=&quot;https://github.com/shakacode/react_on_rails/issues/183&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;#183&lt;/a&gt; and decided to see if I could easily implement it without rewriting a lot of the server rendering code. I started with react-rails as they have support for adding your own server renderer class.&lt;/p&gt;
&lt;p&gt;Long story short, I was not able to easily integrate react-stdio into either libraries due to the way they render JS code. They both take the compiled components JS file and pass the string of JS code to be evaluated and returned, while also passing polyfills before the JS code. In react-stdio, you can only provide it with a path to the component, not a string of JS code. While I think it’s definitely possible to integrate it react-stdio, I don’t believe it’s worth pursing as the current methods work perfectly fine.&lt;/p&gt;
&lt;p&gt;Still, I think there could be uses outside of those libraries, which is why I decided to create this gem.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a simple Ruby on Rails wrapper for react-stdio.
— &lt;a href=&quot;https://github.com/aaronvb/rails_react_stdio&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;rails&lt;em&gt;react&lt;/em&gt;stdio&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Using rails&lt;em&gt;react&lt;/em&gt;stdio is actually extremely simple, just like react-stdio!&lt;/p&gt;
&lt;p&gt;First make sure you have NPM installed and have react-stdio installed.&lt;/p&gt;
&lt;p&gt;Add &lt;code class=&quot;language-text&quot;&gt;rails_react_stdio&lt;/code&gt; to your gemfile:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &lt;span class=&quot;token string&quot;&gt;&apos;rails_react_stdio&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~&gt; 0.1.0&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If your path to react-stdio is not installed in the default location &lt;code class=&quot;language-text&quot;&gt;/usr/local/bin/react-stdio&lt;/code&gt;, add a configuration file to your initializers folder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;RailsReactStdio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;configure &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;config&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;react_stdio_path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/your/path/to/react-stdio&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;path_to_component &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;application&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;components/HelloWorld&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;filename

&lt;span class=&quot;token constant&quot;&gt;RailsReactStdio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;React&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;render&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path_to_component&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aaron&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;p data-reactid=\&quot;.1on4o1jtdds\&quot; data-react-checksum=\&quot;359665029\&quot;&gt;Hello, aaron&amp;lt;/p&gt;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you find this useful give me feedback or open a PR. Also, thanks to react-stdio for providing a neat way to render react components.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Automate Tarsnap in OSX with Ruby]]></title><description><![CDATA[Automate Tarnsap backups, Tarsnap logging, and Tarsnap backup rotations for OSX, with Ruby.]]></description><link>https://aaronvb.com/articles/automate-tarsnap-with-ruby</link><guid isPermaLink="false">https://aaronvb.com/articles/automate-tarsnap-with-ruby</guid><pubDate>Sat, 06 Dec 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I finally found the time to setup proper backups with Tarsnap, &lt;a href=&quot;https://www.tarsnap.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;tarsnap.com&lt;/a&gt;. After following their installation guide, which was very easy to do, I was stuck with a dilemma - how can I automate, rotate, and log this?&lt;/p&gt;
&lt;p&gt;My first attempt to automate tarsnap backups was a bash shell script and cron, which worked okay, but still not exactly how I wanted it. For one, I’m not the best at writing bash shell scripts. And two, cron isn’t the best option for automated tasks in OSX. My solution was to use Ruby for the script, and Launchd for automation. What’s really great about Launchd, versus cron, is that if my laptop is sleeping when my script is supposed to run, Launchd will run the job as soon as my laptop awakes.&lt;/p&gt;
&lt;p&gt;My github repository for this is at &lt;a href=&quot;https://github.com/aaronvb/tarsnap_backup&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/aaronvb/tarsnap_backup&lt;/a&gt;. If you just want to install and run it, head there and clone the repository. Otherwise, I’m going to explain a little about what the script is doing and how Launchd works.&lt;/p&gt;
&lt;p&gt;The script is fairly simple and relies on Ruby’s system command call. The back up portion is straight forward, run tarsnap and append a timestamp to the backup name. The prune part is where the script will remove any backups after a certain period, in my case 3 days. It does this by getting a list of backups from tarsnap, and interating over each one, parsing the timestamp and checking if the date is past the prune date(3 days in the past from now).&lt;/p&gt;
&lt;p&gt;The plist, which is the Launchd file, automates this script in OSX. The one provided in my github repo is what I use, and you’ll need to modify it to work for you.&lt;/p&gt;
&lt;p&gt;Give your job an appropriate, unique, name. This will be used to start/stop your job.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;com.aaronvb.tarsnap-backup&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we have the location to the Ruby exec, and the actual backup.rb file. In my case, I use RVM so my latest Ruby version was in the RVM folder. The backup.rb file is in my tarsnap folder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;/Users/aaronvb/.rvm/rubies/ruby-2.1.5/bin/ruby&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;/Users/aaronvb/.tarsnap/tasks/backup.rb&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the location for your Launchd logs. This is totally optional, and mainly used for debugging. The tarsnap Ruby script will handle it’s own logs.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;StandardOutPath&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;/Users/aaronvb/.tarsnap/logs/launchd.log&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;StandardErrorPath&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;/Users/aaronvb/.tarsnap/logs/launchd.log&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;StartCalendarInterval&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is when Launchd will run this job. I have it set to run every day at Midnight, 8a, 12p, 4p, and 8p. Like I mentioned above, if your computer is sleeping while this job is supposed to run, it will run when it wakes. If you prefer to have it run every N seconds, instead of at exact times, you can use StartInterval.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;StartCalendarInterval&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hour&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Minute&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hour&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;8&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Minute&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hour&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;12&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Minute&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hour&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;16&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Minute&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hour&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;20&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Minute&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;dict&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;StartInterval example. This will run every hour.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;xml&quot;&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;StartInterval&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;3600&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://www.launchd.info/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;launchd.info&lt;/a&gt; is a great resource for Launchd.&lt;/p&gt;
&lt;p&gt;The last part to getting it running is placing the plist in the correct folder and running it properly. I have my plist in /Library/LaunchDaemons because I want it to run system wide and not based on the user. If you place it in this folder, you NEED to load and start the job using &lt;code class=&quot;language-text&quot;&gt;sudo&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Selection Sort in Ruby]]></title><description><![CDATA[Implementing Selection Sort in Ruby]]></description><link>https://aaronvb.com/articles/selection-sort-in-ruby</link><guid isPermaLink="false">https://aaronvb.com/articles/selection-sort-in-ruby</guid><pubDate>Sat, 01 Nov 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my version of Selection Sort in Ruby, I start with the first object in the array and compare it to the next one and so on until I find lowest value, swapping it with the first objects position. I then repeat that on the second object, and the next, until the whole list is sorted.&lt;/p&gt;
&lt;p&gt;Here’s an &lt;a href=&quot;https://en.wikipedia.org/wiki/File:Selection-Sort-Animation.gif&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;animated sequence&lt;/a&gt; of Selection Sort that I found on the &lt;a href=&quot;https://en.wikipedia.org/wiki/Selection_sort&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Selection Sort Wikipedia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As always, start with the tests.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;test/unit&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./selection_sort.rb&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SortingTests&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TestCase&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test_selection_sort&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@unsorted&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shuffle
    &lt;span class=&quot;token variable&quot;&gt;@sorted&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@unsorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort
    selection_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SelectionSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; selection_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@unsorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    assert_equal &lt;span class=&quot;token variable&quot;&gt;@sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And next, the Selection Sort function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SelectionSort&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to_sort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    arr_to_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; to_sort
    length_of_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length
    position_in_array &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; position_in_array &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; length_of_sort
      &lt;span class=&quot;token variable&quot;&gt;@min_val&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;position_in_array&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;length_of_sort &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; position_in_array&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;position_in_array &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@min_val&lt;/span&gt;
          &lt;span class=&quot;token variable&quot;&gt;@min_val&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;position_in_array &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;token variable&quot;&gt;@min_val_index&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; position_in_array &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; i
        &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@min_val&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;position_in_array&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;@swap_value&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;position_in_array&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete_at&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@min_val_index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;insert&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position_in_array&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@min_val&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete_at&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position_in_array&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;insert&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@min_val_index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@swap_value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
      position_in_array &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arr_to_sort
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Intro to Elasticsearch and Ruby on Rails, Part 1]]></title><description><![CDATA[An introduction to Elasticsearch and Ruby on Rails 4: searching, associations, and method values.]]></description><link>https://aaronvb.com/articles/intro-to-elasticsearch-ruby-on-rails-part-1</link><guid isPermaLink="false">https://aaronvb.com/articles/intro-to-elasticsearch-ruby-on-rails-part-1</guid><pubDate>Fri, 17 Oct 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This will be a two-part guide on my first experiences with &lt;a href=&quot;https://www.elastic.co/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Elasticsearch&lt;/a&gt; and Ruby on Rails 4. I’ve been a long time fan of &lt;a href=&quot;https://lucene.apache.org/solr/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Solr&lt;/a&gt;, and &lt;a href=&quot;https://sunspot.github.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sunspot&lt;/a&gt; with Ruby on Rails, as Sunspot has always been easy to implement without a big learning curve. There’s also a lot of information out there for Sunspot.&lt;/p&gt;
&lt;p&gt;Elasticsearch, on the other hand, seems to be less popular with Ruby on Rails. From my research, there were a few gems that were popular but are no longer active, and any google searches on information regarding them are dated at least a year back. It seems that the &lt;a href=&quot;https://github.com/elasticsearch/elasticsearch-rails&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;go-to gem for Elasticsearch&lt;/a&gt; is now from Elasticsearch itself, which actually consist of 3 separate gems.&lt;/p&gt;
&lt;p&gt;In the first part, I will setup a basic search using the default Elasticsearch config, while also configuring the search of ActiveRecord associations and indexing a value obtained by a method. In the second part I will cover more advanced searches using custom analyzers and filters, such as ngram, edgengram, and lowercase filters on specific columns.&lt;/p&gt;
&lt;p&gt;To get started, grab the latest Ruby on Rails, which at the time of this post is 4.1.6, and add these gems to your Gemfile. Notice we are only using two of the gems.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &lt;span class=&quot;token string&quot;&gt;&apos;elasticsearch-model&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  git&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;git://github.com/elasticsearch/elasticsearch-rails.git&apos;&lt;/span&gt;
gem &lt;span class=&quot;token string&quot;&gt;&apos;elasticsearch-rails&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  git&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;git://github.com/elasticsearch/elasticsearch-rails.git&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some information on the two gems we are using taken from their Github repo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;elasticsearch-model, which contains search integration for Ruby/Rails models such as ActiveRecord::Base and Mongoid&lt;/li&gt;
&lt;li&gt;elasticsearch-rails, which contains various features for Ruby on Rails applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We don’t need to use elasticsearch-persistence because elasticsearch-model will handle everything we need in our Ruby on Rails app.&lt;/p&gt;
&lt;p&gt;The easiest way to install Elasticsearch is by using &lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt;, which I hope you are already using.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; brew install elasticsearch
&gt; elasticsearch --config=/usr/local/opt/elasticsearch/config/elasticsearch.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By default, our Rails app will connect to Elasticsearch at localhost:9200, but if you need to connect to a different server, create a .rb file in your config/initializers and add this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# config/initializers/elasticsearch.rb&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# ENV[&apos;ELASTICSEARCH_ADDRESS_INT&apos;] is the environment variable&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# for the elasticsearch server, replace with IP address if not using ENV&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__elasticsearch__&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; host&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;ELASTICSEARCH_ADDRESS_INT&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alright, so if you haven’t already figured it out from the code above, we’re going to be searching a &lt;em&gt;User&lt;/em&gt; model. Create a User table if you don’t already have one, and make sure it has a first name and email column.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; rails g model user first_name:string email:string
&gt; rake db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Open the model file at &lt;em&gt;app/models/user.rb&lt;/em&gt;, and add the Elasticsearch includes. This will allow us to use the Elasticsearch methods on our model, and will also enable the Elasticsearch callbacks for events such as creating, updating, and destroying our model objects, which will keep the Elasticsearch index up to date.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Callbacks&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s pretty much all you need to do to start searching the User model. By default this will search all the columns in this model. Open up the rails console to see how easy it is.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; rails c
Loading development environment (Rails 4.1.6)
irb(main):001:0&gt; User.import
=&gt; 0
irb(main):001:0&gt; User.search(&quot;aaron&quot;).results.count
=&gt; 10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One thing you’ll notice is the &lt;em&gt;import&lt;/em&gt; method that I’m calling on the User model. This is needed because our search index is empty and we need to populate it with our current data. Any subsequent changes to the User table will automatically be added to the index by the Elasticsearch callbacks, so you wont need to run the import method again, unless changes are made outside of the Rails app.&lt;/p&gt;
&lt;p&gt;If you read up on the documentation, you’ll be able to do some neat things like find the score each result. One thing I want to mention is that if you use the &lt;em&gt;records&lt;/em&gt; method instead of the &lt;em&gt;results&lt;/em&gt; method, you will get a collection of ActiveRecord objects instead of Elasticsearch objects.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;irb(main):001:0&gt; User.search(&quot;aaron&quot;).results.first
=&gt; #&amp;lt;Elasticsearch::Model::Response::Result:0x007fd200e71380 @result=#&amp;lt;Hashie::Mash _id=&quot;155&quot; _index=&quot;users&quot; _score=0.5878618...
&gt; User.search(&quot;aaron&quot;).results.first._score
=&gt; 0.5878618
&gt; User.search(&quot;aaron&quot;).records.first
=&gt; #&amp;lt;User id: 155, first_name: &quot;aaron&quot;, email: &quot;bokhoven@gmail.com&quot;...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Cool, so now we can search our User table. But what if we have associations that need to be searched as well? Elasticsearch has a model function for that. Let’s start by creating our association model: PhoneNumber, and adding the associations to our models.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; rails g model phone_number number:string user_id:integer
&gt; rake db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PhoneNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:user&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Callbacks&lt;/span&gt;

  has_many &lt;span class=&quot;token symbol&quot;&gt;:phone_numbers&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we need to configure how Elasticsearch creates the index document for each object. In our &lt;em&gt;User&lt;/em&gt; model add this function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Callbacks&lt;/span&gt;

  has_many &lt;span class=&quot;token symbol&quot;&gt;:phone_numbers&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;as_indexed_json&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    as_json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      only&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:first_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:phone_numbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I’ll explain what’s going on here. When Elasticsearch indexes our User object, we are telling it that we only want to search the id, first name, and email attribute, and all phone numbers associated with this object. To better visualize what the document looks like, open up the rails console.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; rails c
irb(main):001:0&gt; User.first.as_indexed_json
=&gt; {&quot;id&quot;=&gt;1, &quot;email&quot;=&gt;&quot;bokhoven@gmail.com&quot;, &quot;first_name&quot;=&gt;&quot;aaron&quot;,
  &quot;phone_numbers&quot;=&gt;[{&quot;id&quot;=&gt;1, &quot;user_id&quot;=&gt;1, &quot;number&quot;=&gt;&quot;123-456-7890&quot;}]}]}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note, when we search for a Phone Number, we aren’t searching a separate phone number index, we are still searching our User index that has a collection of phone numbers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Elasticsearch, like most NoSQL databases, treats the world as though it were flat. An index is a flat collection of independent documents. A single document should contain all of the information that is required to decide whether it matches a search request or not.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/guide/current/relations.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Elasticsearch Relations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;To create a separate Phone Number index and have Elasticsearch emulate the association, look into &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-objects.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nested Objects&lt;/a&gt;. While both have their advantages and disadvantages, sticking with the flat document will work well most of the time. Possibly in another article I will go over Nested Objects.&lt;/p&gt;
&lt;p&gt;Let’s rebuilt and test our new search index.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;irb(main):001:0&gt; User.import
=&gt; 0
&gt; query = User.search(&quot;123-456-7890&quot;).records.first
=&gt; #&amp;lt;User id: 155, first_name: &quot;aaron&quot;, email: &quot;bokhoven@gmail.com&quot;...
&gt; query.phone_numbers.first
=&gt; #&amp;lt;ActiveRecord::Associations::CollectionProxy [#&amp;lt;PhoneNumber id: 1, user_id: 155, number: &quot;123-456-7890&quot;&gt;]&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Easy, everything works. Not quite. When using associations, it’s not a good idea to use the &lt;em&gt;records&lt;/em&gt; method on the search object. The reason for this is because the collection of records from the search object are ActiveRecord objects, and using the phone numbers method on it will cause ActiveRecord to look up each phone number object for each search record. What you want to use is the &lt;em&gt;results&lt;/em&gt; method on the search object, this will use the “flat” document and not create any more queries for the phone numbers as they are included in the results object. To make this clearer, I’ll show you an example.&lt;/p&gt;
&lt;p&gt;This is with records.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;irb(main):001:0&gt; User.search(&quot;aaron&quot;).records.first
  User Search (10.1ms) {index: &quot;users&quot;, type: &quot;user&quot;, q: &quot;aaron&quot;}
  User Load (3.0ms)  SELECT &quot;users&quot;.* FROM &quot;users&quot;  WHERE &quot;users&quot;.&quot;id&quot; IN (155, 156, 157, 158)
=&gt; #&amp;lt;User id: 155, first_name: &quot;aaron&quot;, email: &quot;bokhoven@gmail.com&quot;...

&gt; User.search(&quot;aaron&quot;).records.first.phone_numbers
  User Search (10.1ms) {index: &quot;users&quot;, type: &quot;user&quot;, q: &quot;aaron&quot;}
  User Load (3.0ms)  SELECT &quot;users&quot;.* FROM &quot;users&quot;  WHERE &quot;users&quot;.&quot;id&quot; IN (155, 156, 157, 158)
  PhoneNumber Load (2.9ms)  SELECT &quot;phone_numbers&quot;.* FROM &quot;phone_numbers&quot;  WHERE &quot;phone_numbers&quot;.&quot;user_id&quot; = $1  [[&quot;user_id&quot;, 155]]
=&gt; #&amp;lt;ActiveRecord::Associations::CollectionProxy [#&amp;lt;PhoneNumber id: 1, user_id: 155, number: &quot;123-456-7890&quot;...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can see that another call was made to the PhoneNumber model because we’re calling the phone_numbers method on the User ActiveRecord object which was in the &lt;em&gt;records&lt;/em&gt; collection.&lt;/p&gt;
&lt;p&gt;This is with results.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;irb(main):001:0&gt; User.search(&quot;aaron&quot;).results.first
  User Search (10.1ms) {index: &quot;users&quot;, type: &quot;user&quot;, q: &quot;aaron&quot;}
=&gt; #&amp;lt;Hashie::Mash id: 155, first_name: &quot;aaron&quot;, email: &quot;bokhoven@gmail.com&quot;...

&gt; User.search(&quot;aaron&quot;).records.first.phone_numbers
  User Search (10.1ms) {index: &quot;users&quot;, type: &quot;user&quot;, q: &quot;aaron&quot;}
=&gt; #&amp;lt;Hashie::Mash id: 155, first_name: &quot;aaron&quot;, email: &quot;bokhoven@gmail.com&quot;, phone_numbers=[#&amp;lt;Hashie::Mash number=&quot;123-456-7890&quot;...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With that said, let’s move on to the last topic I wanted to cover, adding values to the search document that are created by a method in the model. An example of this could be a &lt;em&gt;full name&lt;/em&gt; method that returns a first and last name column together. It makes more sense to search the full name value instead of both columns separately. We’ll use that example in our app.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; rails g migration add_last_name_to_users last_name:string
&gt; rake db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Elasticsearch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Callbacks&lt;/span&gt;

  has_many &lt;span class=&quot;token symbol&quot;&gt;:phone_numbers&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;full_name&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;first_name&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;last_name&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;as_indexed_json&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    as_json&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      only&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:phone_numbers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      methods&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I added the &lt;em&gt;full_name&lt;/em&gt; method to our User model, and then modified the as_indexed_json method to include the full_name method, and also added it to the document by including it in the &lt;em&gt;only&lt;/em&gt; collection. You can always double check what the document looks like by calling the the as_indexed_json method on a User model, I explained how earlier in this article.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;irb(main):001:0&gt; User.search(&quot;aaron van bokhoven&quot;).results.first
=&gt; #&amp;lt;Hashie::Mash id: 155, full_name: &quot;aaron van bokhoven&quot;, email: &quot;bokhoven@gmail.com&quot;...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the end of the first part. Elasticsearch is a powerful tool and I’ve only just begun to scratch the surface. Stay tuned for my next part on customizing Elasicsearch to use analyzers and filters.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Bubble Sort in Ruby]]></title><description><![CDATA[Implementing Bubble Sort in Ruby]]></description><link>https://aaronvb.com/articles/bubble-sort-in-ruby</link><guid isPermaLink="false">https://aaronvb.com/articles/bubble-sort-in-ruby</guid><pubDate>Sat, 20 Apr 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lately during my free time I’ve been reading through sorting algorithms for fun and decided to implement several in Ruby. I’ll be posting each one in a separate article as I go through them.&lt;/p&gt;
&lt;p&gt;Here’s a &lt;a href=&quot;https://en.wikipedia.org/wiki/Bubble_sort&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;bubble sort&lt;/a&gt; in Ruby.&lt;/p&gt;
&lt;p&gt;Bubble sort is a pretty fun and easy sorting algorithm. For each pass through an array of values, each value is compared to its adjacent value and swapped into the correct order and so on. Worst case is O(n^2) and best case is O(n) if the array is already sorted.&lt;/p&gt;
&lt;p&gt;Let’s start with the test.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;test/unit&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./bubble_sort.rb&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SortingTests&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TestCase&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test_bubble_sort&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@unsorted&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; rand&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 1000 random integers in an array&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@sorted&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@unsorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort
    bubble_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BubbleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bubble_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@unsorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    assert_equal &lt;span class=&quot;token variable&quot;&gt;@sorted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the test I create an array of random numbers and assert_equal to the result of the bubble sort.&lt;/p&gt;
&lt;p&gt;Next the code for the bubble sort.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BubbleSort&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to_sort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# move the array to sort into a variable, which will be used for recursion&lt;/span&gt;
    arr_to_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; to_sort
    &lt;span class=&quot;token comment&quot;&gt;# assume that we haven&apos;t swapped any values yet&lt;/span&gt;
    swapped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# lower the length by one because we can&apos;t compare the last value since it&apos;s at the end&lt;/span&gt;
    length_of_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# begin loop through each value&lt;/span&gt;
    length_of_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;times&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# if the value we&apos;re on is greater than the value to the left of it, swap&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# store values to be swapped&lt;/span&gt;
        a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# remove value we&apos;re on&lt;/span&gt;
        arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete_at&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# insert the value to the right, moving the lesser value to the left&lt;/span&gt;
        arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;insert&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;# swap is true since we did a swap during this pass&lt;/span&gt;
        swapped &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; swapped &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# no swaps, return sorted array&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; arr_to_sort
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# swaps were true, pass array to sort method&lt;/span&gt;
      bubble_sort &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BubbleSort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;
      bubble_sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr_to_sort&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I use recursion for each pass until swapped is equal to false, which means that the array is sorted.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Moving from Linode to Heroku]]></title><description><![CDATA[The last time I checked out Heroku was about 2 years ago, which I later dismissed because at the time it seemed like having your own VPS was the right thing to do.]]></description><link>https://aaronvb.com/articles/moving-from-linode-to-heroku</link><guid isPermaLink="false">https://aaronvb.com/articles/moving-from-linode-to-heroku</guid><pubDate>Wed, 16 Jan 2013 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently made the move from Linode to Heroku for my personal sites. The last time I checked out Heroku was about 2 years ago, which I later dismissed because at the time it seemed like having your own VPS was the right thing to do.&lt;/p&gt;
&lt;p&gt;Having your own VPS sounds good in theory, but in reality it’s not, for me at least. I liked being able to throw up random projects, host my friends projects, and tinker with new frameworks and languages. I maybe did that once or twice out of the 4 years I had my own VPS. In reality, I’m way to busy to do any of that. I started to get lazy on updates, and security patches. The traffic my personal sites receive are so minimal they don’t need a VPS.&lt;/p&gt;
&lt;p&gt;After my week at the Aloha Ruby Conf, I noted to myself to check out Heroku once again, to see what has changed. To my surprise, A LOT has changed. There’s the new cedar stack, updated gem which is really awesome to use, procfile, etc, I could go on.&lt;/p&gt;
&lt;p&gt;Anyway, I read through all their documents, which are amazingly good, and then signed up to try their free tier, 1 Dyno, which seems like a perfect size for my site.&lt;/p&gt;
&lt;p&gt;The first thing I had to do was switch my applications database from MySQL to PostgreSQL because Heroku has native support for PostgreSQL. You can use MySQL through one of their add-ons if you wish.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# gem &apos;mysql2&apos;&lt;/span&gt;
gem &lt;span class=&quot;token string&quot;&gt;&apos;pg&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I got my site up on Heroku by logging in and deploying.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;$ heroku login
$ heroku create
$ git push heroku master
$ heroku run rake&lt;span class=&quot;token symbol&quot;&gt;:db&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:migrate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I really like Unicorn(&lt;a href=&quot;https://yhbt.net/unicorn/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;yhbt.net/unicorn/&lt;/a&gt;) as my webserver and was excited to see that they fully support it, and all I needed to do was add one line to my Procfile. I also want to mention that even though 1 Dyno is supposed to only have 1 level of concurrency, with Unicorn I can have multiple concurrency.&lt;/p&gt;
&lt;p&gt;Add Unicorn the Gemfile.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;gem &lt;span class=&quot;token string&quot;&gt;&apos;unicorn&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I added a Unicorn config file to my project by creating &lt;code class=&quot;language-text&quot;&gt;config/unicorn.rb&lt;/code&gt;. In the file I can configure the number of workers and timeout.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;worker_processes &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
timeout &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;
preload_app &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

before_fork &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; worker&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Replace with MongoDB or whatever&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disconnect&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Disconnected from ActiveRecord&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# If you are using Redis but not Resque, change this&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Resque&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Resque&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quit
    &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Disconnected from Redis&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  sleep &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

after_fork &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; worker&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Replace with MongoDB or whatever&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;establish_connection
    &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Connected to ActiveRecord&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# If you are using Redis but not Resque, change this&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;defined&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Resque&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Resque&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;redis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;REDIS_URI&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Connected to Redis&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that Unicorn is set up, Heroku needs to use Unicorn instead of its default web server. To tell Heroku to use Unicorn, they’ve provided the Procfile. I created a file named &lt;code class=&quot;language-text&quot;&gt;Procfile&lt;/code&gt; in the project root directory. Inside the web server can be configured.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;web&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bundle exec unicorn &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token variable&quot;&gt;$PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;c &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;unicorn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that’s pushed to Heroku, I can check to make sure Unicorn is running by using &lt;code class=&quot;language-text&quot;&gt;$ heroku ps&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;$ heroku ps
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; web&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; `bundle exec unicorn &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;p &lt;span class=&quot;token variable&quot;&gt;$PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;c &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;config&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;unicorn&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rb`
   web&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; up &lt;span class=&quot;token number&quot;&gt;2013&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;h ago&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I also needed access to cron or something similar to schedule my nightly Rake that updates the photos on the front page to my latest Flickr photos. Once again that took me a few minutes to set up through their web interface.&lt;/p&gt;
&lt;p&gt;First I created my rake task in &lt;code class=&quot;language-text&quot;&gt;lib/tasks/flickr_update.rake&lt;/code&gt;. It’s a simple script that updates a table with the last 3 photos I posted to flickr. I’m using the &lt;a href=&quot;https://rubygems.org/gems/fleakr&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;gem ‘fleakr’&lt;/a&gt; to do this. I have a RecentPhoto model that has the attributes: title, url(url to the photos flickr page), image_url(url to the exact location of the image file).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;namespace &lt;span class=&quot;token symbol&quot;&gt;:flickr&lt;/span&gt;  &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  desc &lt;span class=&quot;token string&quot;&gt;&quot;update flickr photos on index page&quot;&lt;/span&gt;
  task &lt;span class=&quot;token symbol&quot;&gt;:update&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:environment&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Fleakr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;api_key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;xxxxxx&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# replace with your Flickr API key&lt;/span&gt;
    user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Fleakr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;xxxx&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# replace with your Flickr Username&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;RecentPhoto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;destroy_all &lt;span class=&quot;token comment&quot;&gt;# remove all photos from table&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;3.&lt;/span&gt;times &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;num&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      photo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;photos&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;num&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      image_url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; photo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;medium&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url
      image_url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; image_url&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_q.jpg&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# using the q version of this photo which is a square thumbnail.&lt;/span&gt;
      &lt;span class=&quot;token constant&quot;&gt;RecentPhoto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; photo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; photo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; image_url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; image_url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the Heroku web application settings add the &lt;a href=&quot;https://elements.heroku.com/addons/scheduler&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Scheduler add-on&lt;/a&gt;. Then add the heroku run command for that rake task.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 750px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8a38acff315d0a27b969ba816432a539/1d69c/heroku_scheduler.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA+0lEQVQY041PPW/CMBT0f2CJICH284dqO7ixE5KoFRRaNVLF0IUJpUM7s/X/S32JKQMT0ul076x790x+hmG7efd+03VvbfsKUDBm7wT56A9ts7e2M6bRes35I+du4htx1RcAOCJEFcILJnFASwivVC1EEKJUqpJyFMioUeDrFZyXhHPPWAmAcDk1jBVSVIytKLW4YjRzwwGXBnRuQKSsjNlau1NqjTOGAVYTO6zCW1DHm6MfQcc/FwQ7pWwQAD7ui7WUjvmokSdR/Heay5+XS6t1bUy9WPDZLOv7z/P593gc5nOVZTpNH5JEev98On1jGM001Ununtqv4A5/9dtEB/HEA+QAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/8a38acff315d0a27b969ba816432a539/8ac56/heroku_scheduler.webp 240w,
/static/8a38acff315d0a27b969ba816432a539/d3be9/heroku_scheduler.webp 480w,
/static/8a38acff315d0a27b969ba816432a539/08b4d/heroku_scheduler.webp 750w&quot;
          sizes=&quot;(max-width: 750px) 100vw, 750px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/8a38acff315d0a27b969ba816432a539/8ff5a/heroku_scheduler.png 240w,
/static/8a38acff315d0a27b969ba816432a539/e85cb/heroku_scheduler.png 480w,
/static/8a38acff315d0a27b969ba816432a539/1d69c/heroku_scheduler.png 750w&quot;
          sizes=&quot;(max-width: 750px) 100vw, 750px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/8a38acff315d0a27b969ba816432a539/1d69c/heroku_scheduler.png&quot;
          alt=&quot;heroku_scheduler&quot;
          title=&quot;heroku_scheduler&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;That pretty much covers my move from Linode to Heroku. I highly suggest reading through the Heroku documents.&lt;/p&gt;
&lt;p&gt;Side note: The only downside to Heroku that I encountered was not being able to write to disk. Since my site is entirely static, the comments are javascript, I relied on page caching to keep the load time fast. When I switched to Heroku I had to disable page caching because I’m not able to write to the cached html pages to disk.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Thunderbolt External Hard Drive with Seagate STAE128 Adapter]]></title><description><![CDATA[Yes, you can use the Seagate STAE128 Thunderbolt Adapter for Backup Plus with any SATA external 2.5" Hard Drive.]]></description><link>https://aaronvb.com/articles/thunderbolt-external-hard-drive-with-seagate-stae128-adapter</link><guid isPermaLink="false">https://aaronvb.com/articles/thunderbolt-external-hard-drive-with-seagate-stae128-adapter</guid><pubDate>Fri, 14 Dec 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Yes, you can use the Seagate STAE128 Thunderbolt Adapter for Backup Plus with &lt;strong&gt;any&lt;/strong&gt; SATA external 2.5” Hard Drive. You can also use the STAE121 but I’ve read that they discontinued that for the newer STAE128.&lt;/p&gt;
&lt;p&gt;Here’s a little review on my experience with the Seagate STAE128 Thunderbolt Adapter and a bare 2.5” SATA external hard drive. Since switching over to the Macbook Pro Retina with 256gb SSD, I’ve been a little worried about my hard drive space.&lt;/p&gt;
&lt;p&gt;Coming from the regular Macbook Pro with a 750gb hard drive I wasn’t used to worrying about space. I had to move all of my music to an external and reduced the amount of photos I could work on at a time. Since I scan my photos to raw uncompressed TIFF files at a fairly high DPI, the files are quite large. I used to keep about six months to a years worth of scanned negatives on my laptop at a time, and moved anything older to an external. With only 256gb to spare I’m limited to maybe half of that.&lt;/p&gt;
&lt;p&gt;So my research started when I was looking into an external in which I could use to edit directly on and was portable. This required something fast and small. I looked into USB 3.0 and Thunderbolt, to which the later provided minimal results. The best I could find were 3 or 4 heavy options for Thunderbolt external hard drives at really steep prices, considering I could buy a 1TB USB 3.0 portal external for a hundred dollars.&lt;/p&gt;
&lt;p&gt;After more research I came upon the Seagate Thunder Bolt Adapter, STAE 121, that would allow me to convert the Seagate enclosed external drives to Thunderbolt. A little more googling and I found out I could actually use the STAE 121 with a bare 2.5” SATA hard drive. A little skeptical at first mostly because of the lack of people talking about it on the internet, I went to Best Buy to see if they had it in stock.&lt;/p&gt;
&lt;p&gt;When I got there I found the newer version, the Seagate Thunderbolt Adapter &lt;em&gt;For Backup Plus&lt;/em&gt;, or STAE128. A little confused, I tried to google it but found nothing. I bought it, and a Thunderbolt plug by Apple.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 600px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/159883599976a5dec79444da6c5d18c1/b4294/1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAABAP/aAAwDAQACEAMQAAABbQWs0ogH/8QAHBAAAQMFAAAAAAAAAAAAAAAAAAEDEQISEyMk/9oACAEBAAEFAp6IoGpVu41K5mQ//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8Bp//EABwQAAAGAwAAAAAAAAAAAAAAAAABEBESMSFBcf/aAAgBAQAGPwJmxxClaT2LH//EABwQAQACAgMBAAAAAAAAAAAAAAEAETFxIVFhof/aAAgBAQABPyGz3p8SrxcTOG0qHLAsHVuhjfH/2gAMAwEAAgADAAAAEJMf/8QAFhEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/EBF//8QAFhEBAQEAAAAAAAAAAAAAAAAAABEh/9oACAECAQE/EKa//8QAHBABAAMBAAMBAAAAAAAAAAAAAQARITFBUYHR/9oACAEBAAE/EKRnM5c62omBDiclzHQab1qFpj62J7lC7APIV2ERr4/k/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/159883599976a5dec79444da6c5d18c1/8ac56/1.webp 240w,
/static/159883599976a5dec79444da6c5d18c1/d3be9/1.webp 480w,
/static/159883599976a5dec79444da6c5d18c1/e88ff/1.webp 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/159883599976a5dec79444da6c5d18c1/09b79/1.jpg 240w,
/static/159883599976a5dec79444da6c5d18c1/7cc5e/1.jpg 480w,
/static/159883599976a5dec79444da6c5d18c1/b4294/1.jpg 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/159883599976a5dec79444da6c5d18c1/b4294/1.jpg&quot;
          alt=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          title=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 600px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b4d98880fb460e16a0459431c7850cae/b4294/2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMEBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHXXCuNclD/xAAZEAACAwEAAAAAAAAAAAAAAAABAgADERP/2gAIAQEAAQUC6x7snVcsYrBfW5Rjn//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABoQAAIDAQEAAAAAAAAAAAAAAAAhAQIRAyL/2gAIAQEABj8CEM2eY6zp5qoP/8QAGhABAQEBAQEBAAAAAAAAAAAAAREAMSFhwf/aAAgBAQABPyEFk5qAGCFovx374N5UcemqC0nd/9oADAMBAAIAAwAAABBcL//EABYRAAMAAAAAAAAAAAAAAAAAAAEQEf/aAAgBAwEBPxARf//EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oACAECAQE/EFf/xAAcEAEAAwEAAwEAAAAAAAAAAAABABEhMUGBscH/2gAIAQEAAT8QttnCxNS6oXGkAuhvhLVJbrb+wyYEUPrIm4DyNO+Z/9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/b4d98880fb460e16a0459431c7850cae/8ac56/2.webp 240w,
/static/b4d98880fb460e16a0459431c7850cae/d3be9/2.webp 480w,
/static/b4d98880fb460e16a0459431c7850cae/e88ff/2.webp 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/b4d98880fb460e16a0459431c7850cae/09b79/2.jpg 240w,
/static/b4d98880fb460e16a0459431c7850cae/7cc5e/2.jpg 480w,
/static/b4d98880fb460e16a0459431c7850cae/b4294/2.jpg 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/b4d98880fb460e16a0459431c7850cae/b4294/2.jpg&quot;
          alt=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          title=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;At first I thought I would need to use a SATA cable to connect my hard drive, but it actually plugged right into it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 600px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2b130db42ebe9f811142c6839c200dfe/b4294/3.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAIDBAX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAv/aAAwDAQACEAMQAAABiSyhpDkv/8QAGxABAQABBQAAAAAAAAAAAAAAAwIAAQQRFCL/2gAIAQEAAQUCTxei4RzcLtkpemuFyZf/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAgEBPwFX/8QAGhAAAwEBAQEAAAAAAAAAAAAAAAECETFCUf/aAAgBAQAGPwKktak4KvpVLMZ5FL6j/8QAHRABAAIBBQEAAAAAAAAAAAAAAQARITFRgdHh8f/aAAgBAQABPyE3hAG2AJk8+RS3ebg6i6Wb1h9ENbuxP//aAAwDAQACAAMAAAAQI/8A/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQMBAT8QiP/EABcRAQEBAQAAAAAAAAAAAAAAAAEAEVH/2gAIAQIBAT8QE7Yv/8QAHhABAAICAgMBAAAAAAAAAAAAAREhADFBUXGBkdH/2gAIAQEAAT8QNMIhGUrjzjQsOzdzftiRCmlB1XSsp47lk9SRxitP1/MjHBFUm1z/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/2b130db42ebe9f811142c6839c200dfe/8ac56/3.webp 240w,
/static/2b130db42ebe9f811142c6839c200dfe/d3be9/3.webp 480w,
/static/2b130db42ebe9f811142c6839c200dfe/e88ff/3.webp 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/2b130db42ebe9f811142c6839c200dfe/09b79/3.jpg 240w,
/static/2b130db42ebe9f811142c6839c200dfe/7cc5e/3.jpg 480w,
/static/2b130db42ebe9f811142c6839c200dfe/b4294/3.jpg 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/2b130db42ebe9f811142c6839c200dfe/b4294/3.jpg&quot;
          alt=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          title=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 600px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/87d83890c8a54ea5e22dadc69c1e60ec/b4294/4.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAMFAf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAX7MQWyWH//EABwQAAICAgMAAAAAAAAAAAAAAAECAAMEFBITIf/aAAgBAQABBQK7KUNvTeEtPY3ERfB//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQIBAT8BJ//EABoQAAICAwAAAAAAAAAAAAAAAAABETIQIUH/2gAIAQEABj8C056UKEvP/8QAHBABAAIBBQAAAAAAAAAAAAAAAQARYSExoeHx/9oACAEBAAE/IQOgEFbMr6gnacJ4mKAoCf/aAAwDAQACAAMAAAAQBP8A/8QAFREBAQAAAAAAAAAAAAAAAAAAARD/2gAIAQMBAT8QZ//EABYRAQEBAAAAAAAAAAAAAAAAAAEAEf/aAAgBAgEBPxDAWl//xAAdEAEAAgICAwAAAAAAAAAAAAABABFR0TGRIUGB/9oACAEBAAE/ED9BCHwOfkF9u2oJy76jUhYUDgYIX7TNxc//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/87d83890c8a54ea5e22dadc69c1e60ec/8ac56/4.webp 240w,
/static/87d83890c8a54ea5e22dadc69c1e60ec/d3be9/4.webp 480w,
/static/87d83890c8a54ea5e22dadc69c1e60ec/e88ff/4.webp 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/87d83890c8a54ea5e22dadc69c1e60ec/09b79/4.jpg 240w,
/static/87d83890c8a54ea5e22dadc69c1e60ec/7cc5e/4.jpg 480w,
/static/87d83890c8a54ea5e22dadc69c1e60ec/b4294/4.jpg 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/87d83890c8a54ea5e22dadc69c1e60ec/b4294/4.jpg&quot;
          alt=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          title=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 600px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/934179d8b1e4fd62b7d4026834b5f6f0/b4294/5.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAIEA//EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAGbdaYQcj//xAAZEAEAAwEBAAAAAAAAAAAAAAABAAITEQP/2gAIAQEAAQUCamuBDwI0dus6z//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABwQAAEDBQAAAAAAAAAAAAAAAAABERIQMTJBof/aAAgBAQAGPwKCOZ8NE0YslP/EAB0QAAIBBAMAAAAAAAAAAAAAAAERACExUWFx0fH/2gAIAQEAAT8hWmRXc3K5CIVJcg9xcZMT0IBXn//aAAwDAQACAAMAAAAQ+P8A/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQMBAT8Qh//EABYRAQEBAAAAAAAAAAAAAAAAAAARAf/aAAgBAgEBPxCrr//EAB0QAQADAAIDAQAAAAAAAAAAAAEAESExQWGhsdH/2gAIAQEAAT8QLcwKxej4hRapirG9/Zeci6B9AjqgBu+iWl7d7/ISlLVc4J//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/934179d8b1e4fd62b7d4026834b5f6f0/8ac56/5.webp 240w,
/static/934179d8b1e4fd62b7d4026834b5f6f0/d3be9/5.webp 480w,
/static/934179d8b1e4fd62b7d4026834b5f6f0/e88ff/5.webp 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/934179d8b1e4fd62b7d4026834b5f6f0/09b79/5.jpg 240w,
/static/934179d8b1e4fd62b7d4026834b5f6f0/7cc5e/5.jpg 480w,
/static/934179d8b1e4fd62b7d4026834b5f6f0/b4294/5.jpg 600w&quot;
          sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/934179d8b1e4fd62b7d4026834b5f6f0/b4294/5.jpg&quot;
          alt=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          title=&quot;STAE 128 Seagate Thunderbolt Adapter for Backup Plus&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I’m using a Seagate Momentus 7200rpm 500gb hard drive which I pulled from my old Macbook Pro. I don’t have any benchmarks from when that was installed internally, but I did find a few through google and this seems to be about the average.&lt;/p&gt;
&lt;p&gt;My results using the same hard drive and the Seagate Thunderbolt Adapter STAE128 were very close to that.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 500px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/21675ca16bc4122d444311c77b278d66/0b533/6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAABYlAAAWJQFJUiTwAAABo0lEQVQoz31SiXKiQBD1//8pqUpwIURgwxFnBgWMCKKCHCIjeGRfZJfFlJWu6qmeN93z+hqoqrqJE15Vn1e5XC44i6Lwfb+1f5ABnGRZtkzLcd2K8/5bWZZ5ntd1DeN0Ot0JTuJY199M09Q07XA49PmTJLYsC79H0Wpflh3+PzjebARBmPv+bld2aOsURdFwODQMfblc3mcOg8BmDM/n87lDj02DLNIsZfaEEuq67t36B5SSlxeZMdocj5erAF2tVoqiVhXvsgBz//e/wXibeZ5pGITSIAhbNMsy0zQWi8X844NSinM6mULqurkJhlJCUPbT0zPa1jGjf7PZTJKkx4dH2DAE4TnL8n7bBk3TTCdfhXmeF4Zhi2+ThDKWpRnIHccJgmDh+67r5HlxE5ymqaoo7KtnUZIkLbper8HGOUcK+HF9Pbfp9vuooKhZGY3k11fbnrQohk+ZDUJdN5SRMh6/G7qOLhTF7oYZSshYFH+JkjQmpJswuu3P51heUZQQiex+a9r3tKGcVzusSFlW/zYcg0HO9VWq/R6bjyt8OD/00/4DnwHXW7wTtQcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/21675ca16bc4122d444311c77b278d66/8ac56/6.webp 240w,
/static/21675ca16bc4122d444311c77b278d66/d3be9/6.webp 480w,
/static/21675ca16bc4122d444311c77b278d66/b0a15/6.webp 500w&quot;
          sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/21675ca16bc4122d444311c77b278d66/8ff5a/6.png 240w,
/static/21675ca16bc4122d444311c77b278d66/e85cb/6.png 480w,
/static/21675ca16bc4122d444311c77b278d66/0b533/6.png 500w&quot;
          sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/21675ca16bc4122d444311c77b278d66/0b533/6.png&quot;
          alt=&quot;Seagate Thunderbolt Adapter for Backup Plus STAE128 Benchmark&quot;
          title=&quot;Seagate Thunderbolt Adapter for Backup Plus STAE128 Benchmark&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Compared to my 2TB Western Digital External HDD USB2.0.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 500px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/122289d0a38a4bccac0568db7356415c/0b533/7.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAIAAADtbgqsAAAACXBIWXMAABYlAAAWJQFJUiTwAAABdElEQVQoz3VRiY6CMBD1///KFVcwglyWq7VcCrToGg7Bc3cIm41B9iVtJvPy5ngz8X1fUZTD4di27fcLWJYdj18QPJ/P738wgReGgSSKhmFSur3dbq80Z6yuz2VZwD8uZoxpmqbrOkLor08fEIIdx83S9HQ6jYuzLF0sPvf75HK5DGhNVefCfGOajPH3FSYwJ0xr23bG2CtRVWVd13mep2kax3GeH0bEj8cDxJK0dBynrqrr9doTlmWBixA0TXu/35umgVpDcb8fxlhVYW8tSdOe8CmFUcFLyFsd0AYhGORNDH0Qmgmzj+mUUtpnTNOgvu+6rjATlsulLK8W4iIIgqEYnNQ0lXatWFEUPYE9L0mSqqrg4HmHA+d84Hknbs5nOJVhGLB2znlPeNgLw2i/29mWDXUJ7lAU5cjYjm1LAFEMw7DPbAmJophsibyS1+u1rmuyokRRNGIY1EbgCUJZ9nsw36eMc7gTXBF7mBDiei4s8ir+AcyJn97AMEVCAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/122289d0a38a4bccac0568db7356415c/8ac56/7.webp 240w,
/static/122289d0a38a4bccac0568db7356415c/d3be9/7.webp 480w,
/static/122289d0a38a4bccac0568db7356415c/b0a15/7.webp 500w&quot;
          sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/122289d0a38a4bccac0568db7356415c/8ff5a/7.png 240w,
/static/122289d0a38a4bccac0568db7356415c/e85cb/7.png 480w,
/static/122289d0a38a4bccac0568db7356415c/0b533/7.png 500w&quot;
          sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/122289d0a38a4bccac0568db7356415c/0b533/7.png&quot;
          alt=&quot;Compare&quot;
          title=&quot;Compare&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;and for fun, compared to the internal 256gb SSD that’s inside my Macbook Retina.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 500px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/47b3addb71a8f56eb8218e479d061ebe/0b533/8.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAABYlAAAWJQFJUiTwAAABoUlEQVQoz31SiW6jMBTk/z9qqyZaCGnKcgSCwdw293ZLDgUCJB0VbZYe2idh4bFn3pv3LKi/lD8vL23b3t7jer1ireoqCIK+72//DSEKw5UkGYbh+X7XdfOzpmkOhwN0T6fTJPqZXFcVmLquq6ra98M8P+eMEMIZq+v62yqEssgXi0UcJ5C/oxPZo/TxcWGaW8b4OI7fkJMkdhynKIp5YV3Xns/nsix83wsCHxeOx+Nd9B/ZIY4syzaxu7YbhmE6ppRKkgS32CIn1svl8rVyAWrQhmfTsjjnExpFkWVZjDEbqsSh1LNtm7p0bu2dfLvtLBO2fzw8mKY5ofACORB+IkRxs9ksl8unp03TvM6LF+DNdRzP81mCrv6e0CxNCXGqssyyjPMUHUk5z9IMk/uQGWJrWTaMbRiGRVFOKDyLohjFMYwAx+p5Xpbn6MDnsuMoUlXt+VnBq5rQPM9c6iZJYlk76MK5pms7m+z3+6+erdVKFiXJpfSvZ4YRmFtDUZT1eq0bBraapr82zQfP+DAMDGkYx/tLwA9mc53FBPbDMM/8Bp9K1uXP6VSzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/47b3addb71a8f56eb8218e479d061ebe/8ac56/8.webp 240w,
/static/47b3addb71a8f56eb8218e479d061ebe/d3be9/8.webp 480w,
/static/47b3addb71a8f56eb8218e479d061ebe/b0a15/8.webp 500w&quot;
          sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/47b3addb71a8f56eb8218e479d061ebe/8ff5a/8.png 240w,
/static/47b3addb71a8f56eb8218e479d061ebe/e85cb/8.png 480w,
/static/47b3addb71a8f56eb8218e479d061ebe/0b533/8.png 500w&quot;
          sizes=&quot;(max-width: 500px) 100vw, 500px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/47b3addb71a8f56eb8218e479d061ebe/0b533/8.png&quot;
          alt=&quot;Macbook Pro Retina 256gb SSD Benchmark&quot;
          title=&quot;Macbook Pro Retina 256gb SSD Benchmark&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Overall I’m definitely happy with the STAE128. I get to use my SATA 2.5” hard drives that I have laying around, I get speeds that match internal speeds, it’s portable and self powered, and best of all it was only &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;99&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;99(+&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1em;vertical-align:-0.25em;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;mopen&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;+&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;50 for the Thunderbolt cable). The price alone beats any of the other Thunderbolt options out right now.&lt;/p&gt;
&lt;p&gt;Next up, installing and booting Windows 7 off the STAE128 Thunderbolt drive.&lt;/p&gt;
&lt;p&gt;UPDATE:&lt;/p&gt;
&lt;p&gt;I successfully installed, and am now running, Windows 7 from my Thunderbolt external hard drive. I followed &lt;a href=&quot;https://forums.macrumors.com/threads/rmbp-bootcamp-off-thunderbolt-drive-more-complete-solution.1414769/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;these directions&lt;/a&gt; and it worked perfectly.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 400px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e6f101a52826d7b68c15ab1f20d901ad/066f9/windows_thunderbolt_benchmark.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 127.91666666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAaABQDASIAAhEBAxEB/8QAGQAAAgMBAAAAAAAAAAAAAAAAAAIBAwUE/8QAFgEBAQEAAAAAAAAAAAAAAAAAAAEC/9oADAMBAAIQAxAAAAHXaLBCQzrets1BjU//xAAbEAACAgMBAAAAAAAAAAAAAAABAgAQAxESIf/aAAgBAQABBQLU9oEbrKrF8PQKwX//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPwEp/8QAFREBAQAAAAAAAAAAAAAAAAAAEBH/2gAIAQIBAT8BIf/EABoQAQACAwEAAAAAAAAAAAAAABEAEAEgMUH/2gAIAQEABj8Cs9nKSZU1/8QAHhAAAgICAgMAAAAAAAAAAAAAAAERITFhEEFxkaH/2gAIAQEAAT8hlv0UQsZJauh5+hNPA0I8CdRKkRpXzZ//2gAMAwEAAgADAAAAECAN/P/EABURAQEAAAAAAAAAAAAAAAAAABEQ/9oACAEDAQE/EIwn/8QAFREBAQAAAAAAAAAAAAAAAAAAERD/2gAIAQIBAT8QYVn/xAAbEAEBAQADAQEAAAAAAAAAAAABEQAxYXEhQf/aAAgBAQABPxAWt8Y1K8GahY6xyMQVMvmKDAD7lFoPbl5YIhjhmsKi5CBG/vhkqi073HG5b//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/e6f101a52826d7b68c15ab1f20d901ad/8ac56/windows_thunderbolt_benchmark.webp 240w,
/static/e6f101a52826d7b68c15ab1f20d901ad/7f61c/windows_thunderbolt_benchmark.webp 400w&quot;
          sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/e6f101a52826d7b68c15ab1f20d901ad/09b79/windows_thunderbolt_benchmark.jpg 240w,
/static/e6f101a52826d7b68c15ab1f20d901ad/066f9/windows_thunderbolt_benchmark.jpg 400w&quot;
          sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/e6f101a52826d7b68c15ab1f20d901ad/066f9/windows_thunderbolt_benchmark.jpg&quot;
          alt=&quot;Windows 7 Thunderbolt STAE128 benchmark&quot;
          title=&quot;Windows 7 Thunderbolt STAE128 benchmark&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A Polymorphic Join Table]]></title><description><![CDATA[Not your ordinary polymorphic association, but a polymoprhic join table for Ruby on Rails.]]></description><link>https://aaronvb.com/articles/a-polymorphic-join-table</link><guid isPermaLink="false">https://aaronvb.com/articles/a-polymorphic-join-table</guid><pubDate>Thu, 29 Nov 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here’s an interesting problem I ran into today.&lt;/p&gt;
&lt;p&gt;Polymorphic associations in Ruby on Rails are actually quite easy to do, especially in Rails 3.2. If you need a refresher, there’s a great screencast over at Railscasts, which does require a subscription which I highly highly recommend: &lt;a href=&quot;http://railscasts.com/episodes/154-polymorphic-association-revised&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;railscasts.com/episodes/154-polymorphic-association-revised&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, my problem was a little different, and maybe a special case because I can’t think of many applications this would apply to.&lt;/p&gt;
&lt;p&gt;Say I have a Location and a Checkpoint, and the Location and Checkpoint can have notes, posted by Users. I would use a polymorphic association for the Notes to the Location and Checkpoint. But, I also want a single Note, to be posted to many Locations or Checkpoints, which for a single model-to-model relationship I could simply use a join table.&lt;/p&gt;
&lt;p&gt;Example, two Locations which are near each other, maybe they’re coordinates, could share a single Note describing the general area, and with the same Note model, one Note may describe multiple Checkpoints.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 300px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f3436e32e8e827999fa172b04233f0bf/f93b5/polymorphic_join_table_example_1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 87.91666666666667%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAASABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAe4mjLYUKD//xAAWEAEBAQAAAAAAAAAAAAAAAAARIAD/2gAIAQEAAQUCgxH/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/AR//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/AR//xAAUEAEAAAAAAAAAAAAAAAAAAAAw/9oACAEBAAY/Ah//xAAcEAACAQUBAAAAAAAAAAAAAAAAAREQIUFRcZH/2gAIAQEAAT8huYH2DT8FJDRkKv8A/9oADAMBAAIAAwAAABDjDwD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAeEAEBAAMAAQUAAAAAAAAAAAABEQAhMZEQQVGB8P/aAAgBAQABPxCUJs9yd+8t0qzxl0nDi5awLxfjCSkuk+ZnUXGnDxgAa9P/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/f3436e32e8e827999fa172b04233f0bf/8ac56/polymorphic_join_table_example_1.webp 240w,
/static/f3436e32e8e827999fa172b04233f0bf/c85cb/polymorphic_join_table_example_1.webp 300w&quot;
          sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/f3436e32e8e827999fa172b04233f0bf/09b79/polymorphic_join_table_example_1.jpg 240w,
/static/f3436e32e8e827999fa172b04233f0bf/f93b5/polymorphic_join_table_example_1.jpg 300w&quot;
          sizes=&quot;(max-width: 300px) 100vw, 300px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/f3436e32e8e827999fa172b04233f0bf/f93b5/polymorphic_join_table_example_1.jpg&quot;
          alt=&quot;example_1&quot;
          title=&quot;example_1&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Solution: Make the join table polymorphic.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 400px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ae6dd7bb0e630352b84b8e0311d85017/066f9/polymorphic_join_table_example_2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 60.416666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAMABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAdxqhFh//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQABBQJf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGBABAAMBAAAAAAAAAAAAAAAAAQAQEYH/2gAIAQEAAT8hewayFf/aAAwDAQACAAMAAAAQsA//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/ED//xAAaEAEAAwEBAQAAAAAAAAAAAAABABEhMRCB/9oACAEBAAE/EElt/EVE13FltHYm8x5cNDt2+f/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/ae6dd7bb0e630352b84b8e0311d85017/8ac56/polymorphic_join_table_example_2.webp 240w,
/static/ae6dd7bb0e630352b84b8e0311d85017/7f61c/polymorphic_join_table_example_2.webp 400w&quot;
          sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/ae6dd7bb0e630352b84b8e0311d85017/09b79/polymorphic_join_table_example_2.jpg 240w,
/static/ae6dd7bb0e630352b84b8e0311d85017/066f9/polymorphic_join_table_example_2.jpg 400w&quot;
          sizes=&quot;(max-width: 400px) 100vw, 400px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/ae6dd7bb0e630352b84b8e0311d85017/066f9/polymorphic_join_table_example_2.jpg&quot;
          alt=&quot;example_2&quot;
          title=&quot;example_2&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Notes model that contains the content and user_id, which I use to associate the User model with.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CreateNotes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Migration&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;change&lt;/span&gt;&lt;/span&gt;
    create_table &lt;span class=&quot;token symbol&quot;&gt;:notes&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token symbol&quot;&gt;:content&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;integer &lt;span class=&quot;token symbol&quot;&gt;:user_id&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timestamps
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note join model that is also polymorphic. notable&lt;em&gt;id and notable&lt;/em&gt;type is the polymorphic attributes used by ActiveRecord.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CreateNoteJoins&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Migration&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;change&lt;/span&gt;&lt;/span&gt;
    create_table &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;integer &lt;span class=&quot;token symbol&quot;&gt;:note_id&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;integer &lt;span class=&quot;token symbol&quot;&gt;:notable_id&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;string  &lt;span class=&quot;token symbol&quot;&gt;:notable_type&lt;/span&gt;
      t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timestamps
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    add_index &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:notable_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:notable_type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next I add the model associations to Location and Checkpoint models.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Location&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  has_many &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; as&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:notable&lt;/span&gt;
  has_many &lt;span class=&quot;token symbol&quot;&gt;:notes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; through&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Checkpoint&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  has_many &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; as&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:notable&lt;/span&gt;
  has_many &lt;span class=&quot;token symbol&quot;&gt;:notes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; through&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Above I set the note&lt;em&gt;joins model as the polymorphic association :notable, which will use the notable&lt;/em&gt;id and notable&lt;em&gt;type attributes to assign which model and ID the note join belongs to. Then I set the has&lt;/em&gt;many association on the Note model, through note_joins. This will let me use such methods as Location.first.notes to pull up all the notes that belong to that location. The same applies to Checkpoint.&lt;/p&gt;
&lt;p&gt;In the NoteJoin model I need specify that it belongs to the notable polymorphic association and that it also belongs to a Note. I do so by adding the following.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NoteJoin&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:notable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; polymorphic&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:note&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now for the Note model, it should belong to the notable polymorphic association, and also belong to a user(through the user_id attribute).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Note&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  attr_accessible &lt;span class=&quot;token symbol&quot;&gt;:content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:user_id&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:notable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; polymorphic&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:user&lt;/span&gt;

  has_many &lt;span class=&quot;token symbol&quot;&gt;:note_joins&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this point everything should work, and through the NoteJoin model I can have a single Note belong to many different Locations and even Checkpoints.&lt;/p&gt;
&lt;p&gt;Let’s quickly test this in console.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foobar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#&amp;lt; Note id: 1, content: &quot;foobar&quot;, user_id: 1, created_at: &quot;2012-11-30 02:19:24&quot;, updated_at: &quot;2012-11-30 02:19:24&quot; &gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;#&amp;lt; Note id: 1, content: &quot;foobar&quot;, user_id: 1, created_at: &quot;2012-11-30 02:19:24&quot;, updated_at: &quot;2012-11-30 02:19:24&quot; &gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Great it works, but now I want to see what Locations or Checkpoints this Note belongs to through NoteJoin. To do that I need to update my Note model to include a has&lt;em&gt;many locations and checkpoints. Notice I’m using the source and source&lt;/em&gt;type option, to pass my polymorphic association :notable, since that’s how we translate which model it belongs to.&lt;/p&gt;
&lt;script src=&quot;https://gist.github.com/4173290.js?file=revised_note.rb&quot;&gt;&lt;/script&gt;
&lt;p&gt;And now I can find the locations which my note belongs to.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;locations
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;#&amp;lt; Location id: 1, name: &quot;foobar&quot;, created_at: &quot;2012-11-30 02:19:24&quot;, updated_at: &quot;2012-11-30 02:19:24&quot; &gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Another problem came up after this point. If I were to create a Note, how could I easily add many Locations to it? My first thought was to create a method that would update the join table, but I knew there had to be an easier way already built in ActiveRecord to do this.&lt;/p&gt;
&lt;p&gt;I did some digging around and found a simple solution:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; note &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Note&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user_id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foobar2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#&amp;lt; Note id: 2, content: &quot;foobar2&quot;, user_id: 1, created_at: &quot;2012-11-30 02:19:24&quot;, updated_at: &quot;2012-11-30 02:19:24&quot; &gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;location&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
     note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;locations &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; location
   &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I’m using &amp;#x3C;&amp;#x3C; to push location objects into the note.locations array, and ActiveRecord will handle the creation of the NoteJoin record. Pretty neat.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Coachellagram]]></title><link>https://aaronvb.com/articles/coachellagram</link><guid isPermaLink="false">https://aaronvb.com/articles/coachellagram</guid><pubDate>Sun, 15 Apr 2012 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My co-worker, Chris, &lt;a href=&quot;https://twitter.com/ckalima&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;twitter.com/ckalima&lt;/a&gt;, and I hacked up a little project hours before Coachella 2012 Weekend 1 started: &lt;a href=&quot;http://coachellagram.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;coachellagram.com&lt;/a&gt; . It displays, in real-time, a feed of Instagram photos taken at Coachella.&lt;/p&gt;
&lt;p&gt;It was a great experience learning about pubsub technology, pushing data to browsers, and working with the Instagram API. Instagram has a very rich API and I’m excited to see what people are going to with it in the future.&lt;/p&gt;
&lt;p&gt;Stay tuned for Weekend 2! at &lt;a href=&quot;http://coachellagram.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;coachellagram.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: Coachellagram has been brought down. Sorry if you missed out. We did get some exposure at TNW: &lt;a href=&quot;https://thenextweb.com/shareables/2012/04/15/check-out-coachellagram-a-rocking-collection-of-instagram-shots-from-indio-california/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;
Check out Coachellagram: A rocking collection of Instagram shots from Indio, California&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Pagination and page cache sweepers in Rails 3]]></title><link>https://aaronvb.com/articles/pagination-and-page-cache-sweepers-in-rails-3</link><guid isPermaLink="false">https://aaronvb.com/articles/pagination-and-page-cache-sweepers-in-rails-3</guid><pubDate>Sat, 15 Oct 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is how I handle page cache sweeping in Ruby on Rails 3.1 with pagination. This should work with earlier versions of Ruby on Rails.&lt;/p&gt;
&lt;p&gt;First of all, I’m using the &lt;code class=&quot;language-text&quot;&gt;gem &apos;will_paginate&apos;, &apos;~&gt; 3.0&apos;&lt;/code&gt; and have my routes setup to match the url and replace the &lt;code class=&quot;language-text&quot;&gt;?page=&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;/page/2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In routes.rb:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;match &lt;span class=&quot;token string&quot;&gt;&apos;/articles/page/:page&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;articles#index&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So now you want to do page caching on your site, but the problem is you can’t sweep the folder and the page numbers inside of them normally within the sweeper.&lt;/p&gt;
&lt;p&gt;For example, you have your index action which uses pagination:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;@articles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paginate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:per_page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:published&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:order&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;created_at DESC&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and have it properly cached and watched by the sweeper:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;caches_page &lt;span class=&quot;token symbol&quot;&gt;:index&lt;/span&gt;
cache_sweeper &lt;span class=&quot;token symbol&quot;&gt;:article_sweeper&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the pages get cached, they’ll be placed in &lt;code class=&quot;language-text&quot;&gt;/public/articles/page/1.html&lt;/code&gt; and so forth. The problem is getting the sweeper to delete the pages when there’s a change to the Article model.&lt;/p&gt;
&lt;p&gt;Solution: Manually remove each page on the sweep.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ArticleSweeper&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActionController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Caching&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Sweeper&lt;/span&gt;
  observe &lt;span class=&quot;token constant&quot;&gt;Article&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# This sweeper is going to keep an eye on the article model&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# If our sweeper detects that a article was created call this&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;after_create&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    expire_cache_for&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# If our sweeper detects that a article was updated call this&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;after_update&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    expire_cache_for&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    expire_cache_for_single&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# If our sweeper detects that a article was deleted call this&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;after_destroy&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    expire_cache_for&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    expire_cache_for_single&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expire_cache_for_single&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    expire_page&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:controller&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;articles&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;show&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;expire_cache_for&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;article&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Expire the index page now that we added or modified an article&lt;/span&gt;
    expire_page&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:controller&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;articles&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;index&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# expire the index.html which is created by root_path&lt;/span&gt;
    expire_page &lt;span class=&quot;token string&quot;&gt;&apos;/index.html&apos;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# manually remove pages(1.html, 2.html, etc)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exist&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;root&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/public/articles/page&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# check to make sure directory exists&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foreach&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;root&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/public/articles/page&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;entry&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;unless&lt;/span&gt; entry &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; entry &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;..&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; entry &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.gitignore&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# ignore dot files&lt;/span&gt;
          &lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;root&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/public/articles/page/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;entry&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Setting up an Ubuntu server with Ruby 1.9.2 and Rails 3.1]]></title><description><![CDATA[I just recently fired up a new Linode VPS, with Ubuntu 10.04 LTS, to stage a Ruby on Rails 3.1 app running Ruby 1.9.2 and I thought I would share my process.]]></description><link>https://aaronvb.com/articles/setting-up-an-ubuntu-server-with-ruby-1-9-2-and-rails-3-1</link><guid isPermaLink="false">https://aaronvb.com/articles/setting-up-an-ubuntu-server-with-ruby-1-9-2-and-rails-3-1</guid><pubDate>Sat, 24 Sep 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just recently fired up a new Linode VPS, with Ubuntu 10.04 LTS, to stage a Ruby on Rails 3.1 app running Ruby 1.9.2 and I thought I would share my process. Total time took about 1 hour. I’m also going to share my deploy method using Github and Capistrano.&lt;/p&gt;
&lt;p&gt;Just for reference, this is what’s going to be installed:&lt;/p&gt;
&lt;p&gt;Git
RVM
Ruby 1.9.2
Ruby on Rails 3.1
sqlite3
Apache 2
Passenger&lt;/p&gt;
&lt;p&gt;So the first thing to do is make sure you have your Ubuntu server setup properly with SSH and and all Ubuntu updates. I followed these steps: &lt;a href=&quot;https://web.archive.org/web/20180415154920/http://articles.slicehost.com/2010/4/30/ubuntu-lucid-setup-part-1&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;articles.slicehost.com/2010/4/30/ubuntu-lucid-setup-part-1&lt;/a&gt;. Also make sure you know your root password as we’ll need to &lt;code class=&quot;language-text&quot;&gt;su&lt;/code&gt; back into root later on.&lt;/p&gt;
&lt;p&gt;Once you have that done, the second article should end after you update. Next install Git.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo aptitude build-dep git-core
$ mkdir ~/sources
$ cd ~/sources
$ wget http://git-core.googlecode.com/files/git-1.7.7.2.tar.gz
$ tar xvzf git-1.7.7.2.tar.gz
$ cd git-1.7.7.2
$ ./configure
$ make
$ sudo make install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can verify the installation by typing:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ git --version&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After git is installed, we need to get the packages for Ruby 1.9.2 to be compatible with Rails 3.1.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo apt-get install openssl libssl-dev libreadline5-dev zlib1g-dev libncurses5-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we need to log into root to install RVM.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ su
$ bash &amp;lt; &amp;lt;(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once RVM is installed, time to install Ruby 1.9.2. Make sure you’re still logged in as root(through su). You’ll need to source the RVM script every time you log into root. Normally you wont be doing this often enough to have it auto load, but you can if you want to.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ source /usr/local/rvm/scripts/rvm&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you should have access to the command: &lt;code class=&quot;language-text&quot;&gt;rvm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Install packages for Ruby 1.9.2 that Rails 3.1 will require.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ rvm pkg install readline
$ rvm pkg install iconv
$ rvm pkg install openssl&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can install Ruby 1.9.2&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ rvm install 1.9.2 -C --with-openssl-dir=/usr/local/rvm/usr,--with-iconv-dir=/usr/local/rvm/usr,--with-readline-dir=/usr/local/rvm/usr&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once that’s finished, set Ruby 1.9.2 as the system default.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ rvm use 1.9.2 --default&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then exit root su.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ exit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You should be back to your normal user. Check to make sure rvm is loaded by typing ‘rvm’. If nothing happens exit SSH and log back in. At this point, Ruby should be installed:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ruby -v
-&gt; ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next install Bundler.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ rvmsudo gem install bundler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Before continuing, make sure your project is on a github repository.&lt;/p&gt;
&lt;p&gt;To be able to deploy the code from your github repo to this server, you need to have an rsa key that will be added to your github repository deploy key setting.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd ~/.ssh
$ ssh-keygen -t rsa -C &quot;youremailaddress@whatever.com&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once that’s done, copy the contents of the &lt;code class=&quot;language-text&quot;&gt;id_rsa.pub&lt;/code&gt;key.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cat id_rsa.pub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output should look like: &lt;code class=&quot;language-text&quot;&gt;ssh-rsa AASasdnl31nADLKAnrqppq...oeqnAE1== youremailaddress@whatever.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Copy that to your github project deploy key:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 687px;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/706516fa54a9d804090ed7cdb4cb3170/abd8c/deploy_key_ss.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAANABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAECAwX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAHdU6xjD//EABcQAQEBAQAAAAAAAAAAAAAAAAAREBL/2gAIAQEAAQUCXeUf/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwJf/8QAGhAAAwADAQAAAAAAAAAAAAAAAAEREFFhof/aAAgBAQABPyFJSpkb9L3CQk//2gAMAwEAAgADAAAAEBMP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARITFBUYH/2gAIAQEAAT8QyAuu4IcYo6v7EKSU85gOgJ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/706516fa54a9d804090ed7cdb4cb3170/8ac56/deploy_key_ss.webp 240w,
/static/706516fa54a9d804090ed7cdb4cb3170/d3be9/deploy_key_ss.webp 480w,
/static/706516fa54a9d804090ed7cdb4cb3170/38668/deploy_key_ss.webp 687w&quot;
          sizes=&quot;(max-width: 687px) 100vw, 687px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/706516fa54a9d804090ed7cdb4cb3170/09b79/deploy_key_ss.jpg 240w,
/static/706516fa54a9d804090ed7cdb4cb3170/7cc5e/deploy_key_ss.jpg 480w,
/static/706516fa54a9d804090ed7cdb4cb3170/abd8c/deploy_key_ss.jpg 687w&quot;
          sizes=&quot;(max-width: 687px) 100vw, 687px&quot;
          type=&quot;image/jpeg&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/706516fa54a9d804090ed7cdb4cb3170/abd8c/deploy_key_ss.jpg&quot;
          alt=&quot;deploy_key&quot;
          title=&quot;deploy_key&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;You can verify that you did it correctly:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ ssh -T git@github.com
-&gt; Hi aaronvb! You&apos;ve successfully authenticated, but GitHub does not provide shell access.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, Ubuntu dependencies for sqlite3:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo apt-get install sqlite3 libsqlite3-dev&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next steps will be on your LOCAL computer. If you already have your own way of deployment you can skip this. Install capistrano, or update to the latest version, and update to the newest bundler. The reason why you need the latest bundler is so that capistrano can work with the new asset pipeline in Rails 3.1. Let me explain how this can work.&lt;/p&gt;
&lt;p&gt;You have two options when deploying your app with the new asset pipeline. Compile your assets locally and include the files in your push to your repository, then deploy normally. Second option, push your code as you normally would to your repository, deploy, and have your assets compile on your server instead of locally. The later is the default way capistrano will handle the deploy, and in my opinion is the easiest way.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;On your LOCAL computer:&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ gem install capistrano
$ gem install bundler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Setup capistrano in your app:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd your/app/
$ capify .&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next, change capistrano settings to allow asset pipeline and RVM support. In your app directory, open up the file: &lt;code class=&quot;language-text&quot;&gt;Capfile&lt;/code&gt;and change it to look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;load &lt;span class=&quot;token string&quot;&gt;&apos;deploy&apos;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; respond_to&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# cap2 differentiator&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Uncomment if you are using Rails&apos; asset pipeline&lt;/span&gt;
load &lt;span class=&quot;token string&quot;&gt;&apos;deploy/assets&apos;&lt;/span&gt;

&lt;span class=&quot;token builtin&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;vendor/gems/*/recipes/*.rb&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;vendor/plugins/*/recipes/*.rb&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;plugin&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; load&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

load &lt;span class=&quot;token string&quot;&gt;&apos;config/deploy&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# remove this line to skip loading any of the default tasks&lt;/span&gt;

$&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;unshift&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expand_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./lib&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;rvm_path&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# Add RVM&apos;s lib directory to the load path.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rvm/capistrano&quot;&lt;/span&gt;                  &lt;span class=&quot;token comment&quot;&gt;# Load RVM&apos;s capistrano plugin.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next configure the capistrano deploy file by opening: &lt;code class=&quot;language-text&quot;&gt;your/app/config/deploy.rb&lt;/code&gt;. I’ve provided my &lt;code class=&quot;language-text&quot;&gt;deploy.rb&lt;/code&gt; as a reference:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;set &lt;span class=&quot;token symbol&quot;&gt;:application&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aaronvb&quot;&lt;/span&gt;
set &lt;span class=&quot;token symbol&quot;&gt;:repository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;git@github.com:aaronvb/aaronvb.git&quot;&lt;/span&gt;

set &lt;span class=&quot;token symbol&quot;&gt;:deploy_to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~/sites/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;application&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;

set &lt;span class=&quot;token symbol&quot;&gt;:scm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;git&quot;&lt;/span&gt;
set &lt;span class=&quot;token symbol&quot;&gt;:branch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;master&quot;&lt;/span&gt;

set &lt;span class=&quot;token symbol&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;aaronvb&quot;&lt;/span&gt;

role &lt;span class=&quot;token symbol&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yourserverip:yourserverportforssh&quot;&lt;/span&gt;
role &lt;span class=&quot;token symbol&quot;&gt;:web&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;yourserverip:yourserverportforssh&quot;&lt;/span&gt;
role &lt;span class=&quot;token symbol&quot;&gt;:db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;yourserverip:yourserverportforssh&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:primary&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# if you&apos;re still using the script/reaper helper you will need&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# these http://github.com/rails/irs_process_scripts&lt;/span&gt;

namespace &lt;span class=&quot;token symbol&quot;&gt;:deploy&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  desc &lt;span class=&quot;token string&quot;&gt;&quot;Restarting Passenger with restart.txt&quot;&lt;/span&gt;
  task &lt;span class=&quot;token symbol&quot;&gt;:restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:roles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:except&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:no_release&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
    run &lt;span class=&quot;token string&quot;&gt;&quot;touch &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;current_path&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/tmp/restart.txt&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;t&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
    desc &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;t&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt; task is a no-op with mod_rails&quot;&lt;/span&gt;
    task t&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:roles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:app&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now to setup the directories for capistrano on  your server. &lt;code class=&quot;language-text&quot;&gt;Still on your LOCAL computer&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cap deploy:setup
$ cap deploy:check
-&gt; You appear to have all necessary dependencies installed&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now to deploy JUST the code from your github repository:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cap deploy:update_code&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once that finishes, go back to your SERVER. Your app should now be residing in the folder you setup within the capistrano &lt;code class=&quot;language-text&quot;&gt;deploy.rb&lt;/code&gt; file. Go to that directory. If you used my &lt;code class=&quot;language-text&quot;&gt;deploy.rb&lt;/code&gt; then it will be in &lt;code class=&quot;language-text&quot;&gt;~/sites/yourappname/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When you find it, go to &lt;code class=&quot;language-text&quot;&gt;/releases/&lt;/code&gt; and go to the only folder in there. It should be a folder named with numbers(datetime stamp of the latest deploy). In that folder you should see your app code from your github repository. The way capistrano works is that every time you deploy your code, it uploads it to a new folder in &lt;code class=&quot;language-text&quot;&gt;/releases/&lt;/code&gt; which will be named by a datetime stamp, and then linked to &lt;code class=&quot;language-text&quot;&gt;/yourappname/current&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So at this point you should be sitting in your app root directory on your server. The next step is to get your apps gems installed, easy:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ bundle install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that finishes, time to install node.js. This is required to compile the assets for the asset pipeline in Rails 3.1. If you decided to compile your assets on your local computer, you can skip this. I followed the instruction from: &lt;a href=&quot;https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/joyent/node/wiki/Installing-Node.js-via-package-manager&lt;/a&gt; for this part.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:chris-lea/node.js
$ sudo apt-get update
$ sudo apt-get install nodejs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you’re using sqlite as the database, you might run into a little problem when deploying. Every time you deploy, your production.db will get removed. This is because your production.db isn’t in your github repository. You have a few options to handle this; have your deploy script copy the production.db from your old release to your latest release, or place your production.db outside of your releases. I use the later option. To do this I created the folder: &lt;code class=&quot;language-text&quot;&gt;/sites/yourappname/db/&lt;/code&gt; and in my &lt;code class=&quot;language-text&quot;&gt;database.yml&lt;/code&gt; config file I changed the path for the production database to: &lt;code class=&quot;language-text&quot;&gt;database: /home/yourserverusername/sites/yourappname/db/production.sqlite3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Time to set the database up in your app. Change directories back to your project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ rake db:create RAILS_ENV=production
$ rake db:migrate RAILS_ENV=production&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can test your app now by trying the console: &lt;code class=&quot;language-text&quot;&gt;$ rails c production&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Installing Apache 2 and Passenger:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo aptitude install apache2
$ rvmsudo gem install passenger
$ sudo apt-get install apache2-prefork-dev
$ sudo apt-get install libapr1-dev
$ sudo apt-get install libaprutil1-dev
$ rvmsudo passenger-install-apache2-module&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Follow the instructions that Passenger presents after the installation. It should ask you to place a few lines of code at the bottom of &lt;code class=&quot;language-text&quot;&gt;/etc/apache2/apache2.conf&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The last part is to create the virtual host for your app:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd /etc/apache2/sites-available
$ sudo nano yoursite.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inside that file add. Note: I’m basing this off of my deploy script I provided. If you have your app in a different directory I’m sure you can figure this out. The DocumentRoot and Directory MUST point to the public folder in the /current/ folder of your capistrano deploy directory:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;VirtualHost *:80&gt;
  ServerAdmin admin@yourdomain.com
  ServerName  yourdomain.com
  ServerAlias www.yourdomain.com

  DocumentRoot /home/yourserverusername/sites/yourappname/current/public

  &amp;lt;Directory /home/yourserverusername/sites/yourappname/current/public&gt;
    AllowOverride all
    Options -MultiViews
  &amp;lt;/Directory&gt;
&amp;lt;/VirtualHost&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Save the file, disable and enable a few things, then restart apache2.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ sudo a2dissite default
$ sudo a2ensite yoursite.com
$ sudo a2enmod rewrite
$ sudo apache2ctl restart&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hope this helps. It’s quite long and forgive me if I’ve misspelled something or skipped a step. Please let me know if you have any troubles.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Rails caching and undefined class/module]]></title><description><![CDATA[If you are using Rails.cache, and you are using it to store objects using Marshal.dump and Marshal.load, which is default, I'm sure you've experienced this error.]]></description><link>https://aaronvb.com/articles/rails-caching-and-undefined-class-module</link><guid isPermaLink="false">https://aaronvb.com/articles/rails-caching-and-undefined-class-module</guid><pubDate>Sun, 17 Jul 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Note: This doesn’t affect anything in production as classes are cached at load, see config/environments/production.rb: config.cache_classes = true&lt;/p&gt;
&lt;p&gt;I’ve run into this annoying little problem every time I start work on implementing caching into my rails projects using the built in Rails.cache in Activerecord. If you are using Rails.cache, and you are using it to store objects using Marshal.dump and Marshal.load, which is default, I’m sure you’ve experienced this error. I’m also pretty sure you’ve googled it and were a little confused as to why it keeps happening.&lt;/p&gt;
&lt;p&gt;You write an object to the Rails.cache, Rails.cache.write(“post&lt;em&gt;#{@post.id}”, @post), and then read the object, Rails.cache.read(“post&lt;/em&gt;#{@post.id}”), you get the object. Now this time you write the object to cache, restart your rails app, and try to read the object, but now you’ve received an error, ArgumentError: undefined class/module YOUR_MODEL. This is because rails is trying to Marshal the object, or model in this case, but rails hasn’t loaded the model yet!&lt;/p&gt;
&lt;p&gt;Go ahead, test it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;$ rails c
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@kitten&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Kitten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;kitten_&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@kitten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@kitten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OK&quot;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;kitten_1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#&amp;lt;Kitten id: 1, cute: &quot;no&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; exit

$ rails c
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;kitten_1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; undefined &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Kitten&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Kitten&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Kitten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; integer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cute&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;kitten_1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#&amp;lt;Kitten id: 1, cute: &quot;no&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So the solution is to just load all the damn models at load. I thought about using the production value, config.cache_classes = true, in the  development environment, but then I would have to restart my server every time I modified any code. Then I thought why can’t I just require it in an initialize file that only runs if it’s in development mode.&lt;/p&gt;
&lt;p&gt;Create a file in your config/initializers folder and put this in:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;development&quot;&lt;/span&gt;
  &lt;span class=&quot;token builtin&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foreach&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;root&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/app/models&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;model_name&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
    require_dependency model_name &lt;span class=&quot;token keyword&quot;&gt;unless&lt;/span&gt; model_name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; model_name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;..&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Recurring resque and redis with cron]]></title><description><![CDATA[Moving one of my projects over from delayed_job to resque/redis, for reasons I wont go into here, I needed to have a few of my workers on a cron job.]]></description><link>https://aaronvb.com/articles/recurring-resque-and-redis-with-cron</link><guid isPermaLink="false">https://aaronvb.com/articles/recurring-resque-and-redis-with-cron</guid><pubDate>Thu, 12 May 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Moving one of my projects over from delayed_job to resque/redis, for reasons I wont go into here, I needed to have a few of my workers on a cron job. I was initially going to use the resque-scheduler plugin, but the fact that it runs as a daemon made me a little nervous. I didn’t want to worry about watching the scheduler process for memory leaks and or crashes, and cron is a proven, reliable, scheduling service in itself.&lt;/p&gt;
&lt;p&gt;Basically, I took the same concept from my other post, &lt;a href=&quot;/articles/recurring-delayed-job-with-cron&quot;&gt;Recurring delayed_job with cron&lt;/a&gt; and applied it to redis, instead of using mysql as I did with delayed_job. It works by manually injecting jobs into the queue without using the Rails environment, saving memory, cpu, and time, and also able to be run externally by the system cron.&lt;/p&gt;
&lt;p&gt;In this example I’m going to use a Payment model, which holds payment information, and an ‘update’ method which should be run nightly at 0000. I’m using Rails 3 as well, which uses the ‘mysql2’ gem.&lt;/p&gt;
&lt;p&gt;This is the resque worker.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UpdatePayment&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;@queue&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:update_payment&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;perform&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payment_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    payment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Payment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_by_id&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payment_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; payment
      &lt;span class=&quot;token comment&quot;&gt;# update payment logic goes here&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is the ruby file that the cron will run. I usually place this in root.rails/lib/crons folder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;redis&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mysql2&apos;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# assuming redis is running on the default port.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# if not, example: redis = Redis.new(:host =&gt; &quot;10.0.1.1&quot;, :port =&gt; 6380)&lt;/span&gt;
redis &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Make sure queue exists, if not create it. When clearing a queue with the resque web interface, resque removes the queue, so here we just check to make sure it exists.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sismember&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resque:queues&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;update_payment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sadd&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resque:queues&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;update_payment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Mysql DB information&lt;/span&gt;
client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Mysql2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:host&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:username&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:database&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;your_project_development&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# query the db for all payment records&lt;/span&gt;
results &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;SELECT `payments`.* FROM `payments` ORDER BY id asc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# create a job in the update_payment queue that will update each payment, pass each payment id&lt;/span&gt;
results&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;row&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  redis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rpush&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;resque:queue:update_payment&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;{\&quot;class\&quot;:\&quot;UpdatePayment\&quot;,\&quot;args\&quot;:[&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;row&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;]}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And this is what goes in the crontab, which can be accessed by typing ‘crontab -e’ in the console.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# m h  dom mon dow   command&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;usr&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;bin&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ruby &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;path&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;to&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;your&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;file&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cron_payment_update&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For reference:&lt;/p&gt;
&lt;p&gt;Redis - &lt;a href=&quot;https://redis.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;redis.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Resque - &lt;a href=&quot;https://github.com/resque/resque&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/resque/resque&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Resque Intro - &lt;a href=&quot;https://github.blog/2009-11-03-introducing-resque&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.blog/2009-11-03-introducing-resque&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Redis Ruby Gem(should get installed when installing resque) - &lt;a href=&quot;https://github.com/redis/redis-rb&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/redis/redis-rb&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ajax checkbox in Ruby on Rails 2.3]]></title><description><![CDATA[Neat trick if you want to use Ajax on a checkbox and don't want to create a separate function for onclick.]]></description><link>https://aaronvb.com/articles/ajax-checkbox-in-ruby-on-rails-2-3</link><guid isPermaLink="false">https://aaronvb.com/articles/ajax-checkbox-in-ruby-on-rails-2-3</guid><pubDate>Sun, 06 Mar 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Neat trick if you want to use Ajax on a checkbox and don’t want to create a separate function for onclick.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Note model has a column, :important, :boolean, :default =&gt; false&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# This goes in your HTML erb&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;notes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:order&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;updated_at DESC&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;user_note&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
&amp;lt;% remote_form_for user_note, {:url =&gt;&lt;/span&gt;
  remote_toggle_important_user_note_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; user_note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;f&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
&amp;lt;%= f.check_box :important, :onclick =&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;$(&apos;edit_user_note_&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;user_note&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&apos;).onsubmit()&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;&amp;lt;%= f.label :important %&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
&amp;lt;% end %&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# routes&lt;/span&gt;

map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resources &lt;span class=&quot;token symbol&quot;&gt;:users&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;user&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resources &lt;span class=&quot;token symbol&quot;&gt;:notes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:member&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:remote_toggle_important&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:put&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# notes controller&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;remote_toggle_important&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;@note&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;UserNote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_by_id&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@note&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@note&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;update_attributes&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:user_note&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Recurring delayed_job with cron]]></title><description><![CDATA[This one is easy and I use it quite often - particularly to scrape data at certain times during the day, and generate nightly statistics and reports.]]></description><link>https://aaronvb.com/articles/recurring-delayed-job-with-cron</link><guid isPermaLink="false">https://aaronvb.com/articles/recurring-delayed-job-with-cron</guid><pubDate>Wed, 27 Oct 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Do you need a delayed_job job to happen at certain times or intervals?&lt;/p&gt;
&lt;p&gt;This one is easy and I use it quite often - particularly to scrape data at certain times during the day, and generate nightly statistics and reports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why would I use delayed_job to handle recurring jobs over rake task?&lt;/strong&gt;
Every time a rake task is issued, a new rails instance is spawned which takes time and memory to start and run. If a delayed_job daemon is already running, using that will save both. In my opinion, running a rake task that uses the rails environment is usually very costly in production and should be avoided if possible.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# put this somewhere in your project, ie: /lib&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# use:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# crontab -e&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# */30 * * * * /usr/bin/ruby /your_rails_project/lib/this_file.rb&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# that will insert this job into your delayed_job queue every 30 minutes.&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rubygems&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;mysql&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;parse_args&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    str &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[]\n\n&apos;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    str &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;\n-&apos;&lt;/span&gt;
    args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;v&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      str &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos; :&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_s &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;: &apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_s &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;\n&apos;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; str
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Connect to database&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# replace these values with your own:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# DB_USER is your database user&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# DB_PASSWORD is your database user password&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# DATABASE_NAME is your database name, ie: sample_app_development&lt;/span&gt;

dbh &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Mysql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;real_connect&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;localhost&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DB_USER&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;DB_PASSWORD&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;DATABASE_NAME&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Get the current time in db format&lt;/span&gt;
db_time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;now&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;strftime&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%Y-%m-%d %H:%M:%S&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Insert data into table&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# replace these values with your own:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# YOUR_MODEL is your model&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# YOUR_METHOD is your method, or function&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# YOUR_ARGUMENTS are your agrument(s), ie {:nws =&gt; 4, :asdf =&gt; &quot;haha&quot;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# leave blank if no arguments&lt;/span&gt;

model &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;YOUR_MODEL&quot;&lt;/span&gt;
model_method &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;YOUR_METHOD&quot;&lt;/span&gt;
args &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parse_args&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;YOUR_ARGUMENTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

dbh&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;INSERT INTO `delayed_jobs` (`failed_at`, `locked_by`, `created_at`, `handler`, `updated_at`, `priority`, `run_at`, `attempts`, `locked_at`, `last_error`) VALUES(NULL, NULL, &apos;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;db_time&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&apos;, &apos;--- !ruby/struct:Delayed::PerformableMethod \nobject: LOAD;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;model&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;\nmethod: :&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;model_method&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;\nargs: &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;args&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&apos;, &apos;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;db_time&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&apos;, 0, &apos;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;db_time&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&apos;, 0, NULL, NULL)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Facebook Connect Javascript Cookie and Ruby on Rails]]></title><description><![CDATA[I wrote this class to verify(using Facebook's cookie verification article) and parse the Facebook Javascript Connect cookie.]]></description><link>https://aaronvb.com/articles/facebook-connect-javascript-cookie-and-ruby-on-rails</link><guid isPermaLink="false">https://aaronvb.com/articles/facebook-connect-javascript-cookie-and-ruby-on-rails</guid><pubDate>Mon, 25 Oct 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Update: I’m currently working this into a gem. Feel free to watch/use/contribute on github: &lt;a href=&quot;https://github.com/aaronvb/fb_js_connect&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;github.com/aaronvb/fb_js_connect&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I wrote this class to verify(using Facebook’s cookie verification article) and parse the Facebook Javascript Connect cookie.&lt;/p&gt;
&lt;p&gt;This can definitely work without Ruby on Rails, but you may need to change a few things.&lt;/p&gt;
&lt;p&gt;Reference: Facebook Connect Javascript SDK &lt;a href=&quot;http://developers.facebook.com/docs/reference/javascript/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;developers.facebook.com/docs/reference/javascript/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To use this in Ruby on Rails, save this file into your RAILS_ROOT/lib folder. If you alter or use this code please contribute back!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;digest/md5&apos;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FbJsConnect&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# When using the Facebook Javascript SDK to connect users to your site,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# a cookie will be placed in your applications session store. This class&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# will verify your cookie, tell you if you&apos;re connected, and provide&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# you with a User and other information&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# cookie = cookies[&quot;fbs_YOUR FACEBOOK APP ID&quot;]&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# fb = FbJsConnect.new(cookie)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# fb.connected =&gt; true or false&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# fb.verified? =&gt; true or false&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# fb.user =&gt; User or nil (Assuming you have a User class with the&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# attribute fb_uid)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# fb.uid if fb.connected =&gt; uid if connected&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# fb.access_token if fb.connected =&gt; access_token if connected&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;# Verification works by combining cookie key + values, minus sig,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# appending app_secret, then MD5 hashing it.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# The value of the MD5 should equal the sig value.&lt;/span&gt;

  attr_accessor &lt;span class=&quot;token symbol&quot;&gt;:connected&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;initialize&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cookie&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@app_id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# YOUR FACEBOOK APP ID&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@app_secret&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# YOUR FACEBOOK APP SECRET KEY&lt;/span&gt;
    fb_cookie &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cookie
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; fb_cookie
      &lt;span class=&quot;token comment&quot;&gt;# remove any quotes in cookie. Facebook puts it&apos;s cookie in&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# quotes for some reason&lt;/span&gt;
      fb_cookie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gsub&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&quot;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# split cookie values into array by &apos;&amp;amp;&apos; symbol&lt;/span&gt;
      fb_cookie &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fb_cookie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;amp;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      fb_cookie_hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;
      fb_cookie&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
  	 key_value_arr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;=&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# split value into kv hash&lt;/span&gt;
  	 fb_cookie_hash&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key_value_arr&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; key_value_arr&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# add to new hash&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;token variable&quot;&gt;@fb_cookie_hash&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fb_cookie_hash &lt;span class=&quot;token comment&quot;&gt;# for verification&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;# apply connected and fb_cookie_hash to class attributes IF verified&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;verified&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;@attributes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fb_cookie_hash
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# no cookie found so return false&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;connected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;verified&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;debug&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\nFB: digested sig: &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;process&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;debug&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;FB: sig: &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@sig&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;\n\n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; process &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@sig&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;user&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# find user with UID&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#&lt;/span&gt;
    user &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:fb_uid&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token variable&quot;&gt;@fb_cookie_hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;uid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; user
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; user &lt;span class=&quot;token comment&quot;&gt;# a user was found with UID&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# a user was not found&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;method_missing&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    attribute &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_s
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; attribute &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;/=$/&lt;/span&gt;
      &lt;span class=&quot;token variable&quot;&gt;@attributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;attribute&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;chop&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;token variable&quot;&gt;@attributes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;attribute&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;process&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@sig&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@fb_cookie_hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;sig&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# get sig to compare final hash&lt;/span&gt;
    payload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@fb_cookie_hash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;push &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;k&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;=&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;v&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;unless&lt;/span&gt; k &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sig&quot;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    digest &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MD5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hexdigest&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sort&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_s &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@app_secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; digest
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[PDFKit and wkhtmltopdf without X Server, using qt]]></title><description><![CDATA[If your PDFKit and wkhtmltopdf is working on your development server, probably in OSX, but not working on your production server, which is probably running linux, it's because you're missing X Server.]]></description><link>https://aaronvb.com/articles/pdfkit-and-wkhtmltopdf-without-x-server-using-qt</link><guid isPermaLink="false">https://aaronvb.com/articles/pdfkit-and-wkhtmltopdf-without-x-server-using-qt</guid><pubDate>Thu, 15 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If your PDFKit and wkhtmltopdf is working on your development server, probably in OSX, but not working on your production server, which is probably running linux, it’s because you’re missing X Server.&lt;/p&gt;
&lt;p&gt;There are a few ways around this, some emulate X Server, but that seems hacky. I read through the wkhtmltopdf docs and read that a patched qt framework will allow you to use wkhtmltopdf without using X Server.&lt;/p&gt;
&lt;p&gt;Here’s what I did on my Ubuntu Box to get this to work (note: the compile time for qt was almost 2 hours, and you’ll also need to have git installed):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt-get build-dep libqt4-gui libqt4-network libqt4-webkit
sudo apt-get install openssl build-essential xorg git-core git-doc libssl-dev

mkdir ~/sources
cd ~/sources
git clone git://gitorious.org/+wkhtml2pdf/qt/wkhtmltopdf-qt.git wkhtmltopdf-qt
cd wkhtmltopdf-qt
./configure -nomake tools,examples,demos,docs,translations -opensource -prefix ../wkqt
make -j3
make install
cd ..&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next install wkhtmltopdf:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;git clone git://github.com/antialize/wkhtmltopdf.git wkhtmltopdf
cd wkhtmltopdf
../wkqt/bin/qmake
make -j3
make install&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run ‘wkhtmltopdf’ in shell and you should see it load correctly instead of seeing the x server error. Running ‘wkhtmltopdf-proxy’ should do the same, which is what PDFKit uses.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ruby Hash to Class Object and OpenStruct]]></title><link>https://aaronvb.com/articles/ruby-hash-to-class-object-and-openstruct</link><guid isPermaLink="false">https://aaronvb.com/articles/ruby-hash-to-class-object-and-openstruct</guid><pubDate>Mon, 12 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been working on a class that’s pulling information from an API and wanted to create dynamic attributes from a hash(json).&lt;/p&gt;
&lt;p&gt;I came across this link:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pullmonkey.com/2008/01/06/convert-a-ruby-hash-into-a-class-object&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It works pretty well, but I haven’t done much benchmarking. I’ve read in the past that define_method can be slow and has memory leaks.&lt;/p&gt;
&lt;p&gt;Following that post, I read the comments and came across OpenStruct:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ruby-doc.org/stdlib-2.5.1/libdoc/ostruct/rdoc/OpenStruct.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ruby-doc.org/stdlib-2.5.1/libdoc/ostruct/rdoc/OpenStruct.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have it working with what I need but there’s still more testing to be done. Just thought I would share.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ruby Array to Javascript Array]]></title><description><![CDATA[Here's a little gem I found a while ago that I'm reusing in my current project.]]></description><link>https://aaronvb.com/articles/ruby-array-to-javascript-array</link><guid isPermaLink="false">https://aaronvb.com/articles/ruby-array-to-javascript-array</guid><pubDate>Wed, 10 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Need to pass a Ruby Array to a Javascript Array?
Here’s a little gem I found a while ago that I’m reusing in my current project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; a_ruby_array &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;three&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;one&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;two&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;three&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[&apos;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;a_ruby_array&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;\&apos;,\&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&apos;]&quot;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[&apos;one&apos;,&apos;two&apos;,&apos;three&apos;]&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Validations For Multiple Nested Model Forms]]></title><description><![CDATA[Here's a little trick I found when handling multiple nested models in a form that require validations for each model.]]></description><link>https://aaronvb.com/articles/validations-for-multiple-nested-model-forms</link><guid isPermaLink="false">https://aaronvb.com/articles/validations-for-multiple-nested-model-forms</guid><pubDate>Sat, 06 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here’s a little trick I found when handling multiple nested models in a form that require validations for each model.&lt;/p&gt;
&lt;p&gt;Spec time. For example, we have a model, Author, which has&lt;em&gt;many Books and has&lt;/em&gt;many Magazines. Book and Magazine has validations. On a single page, we create a three forms for Book, Magazine, and Author. There is also an option select that a user can choose if the Author has a Book or a Magazine.&lt;/p&gt;
&lt;p&gt;Pretty straight forward. Let’s do some code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#models/author.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Author&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  has_many &lt;span class=&quot;token symbol&quot;&gt;:books&lt;/span&gt;
  has_many &lt;span class=&quot;token symbol&quot;&gt;:magazines&lt;/span&gt;

  accepts_nested_attributes_for &lt;span class=&quot;token symbol&quot;&gt;:books&lt;/span&gt;
  accepts_nested_attributes_for &lt;span class=&quot;token symbol&quot;&gt;:magazines&lt;/span&gt;

  validates_presence_of &lt;span class=&quot;token symbol&quot;&gt;:name&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#models/book.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Book&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:author&lt;/span&gt;
  validates_presence_of &lt;span class=&quot;token symbol&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token symbol&quot;&gt;:genre&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#models/magazine.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Magazine&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  belongs_to &lt;span class=&quot;token symbol&quot;&gt;:author&lt;/span&gt;
  validates_presence_of &lt;span class=&quot;token symbol&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;token symbol&quot;&gt;:genre&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Basic model setup with validations and accepts&lt;em&gt;nested&lt;/em&gt;attributes_for. More info on that can be found in the &lt;a href=&quot;https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ruby on Rails API - Nested Attributes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Moving on to the form setup..&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#views/authors/new.html.erb&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; form_for &lt;span class=&quot;token variable&quot;&gt;@author&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;f&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
&amp;lt;%= f.error_messages %&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= f.label :name %&gt;&amp;lt;br /&gt;
    &amp;lt;%=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text_field &lt;span class=&quot;token symbol&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
  &amp;lt;/p&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= f.radio_button(&quot;media_type&quot;, &quot;book&quot; %&gt;&amp;lt;%=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token symbol&quot;&gt;:media_type_book&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Book&apos;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt; [?]
    &amp;lt;%= f.radio_button(&quot;media_type&quot;, &quot;magazine&quot; %&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= f.label :media_type_magazine, &apos;Magazine&apos; %&gt; [?]
  &amp;lt;/p&gt;
  &amp;lt;% f.fields_for :books do |book| %&gt;
    &amp;lt;p&gt;
      &amp;lt;%=&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token symbol&quot;&gt;:title&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;&amp;lt;br /&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= book.text_field :title %&gt;
    &amp;lt;/p&gt;
    &amp;lt;p&gt;
      &amp;lt;%=&lt;/span&gt; book&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token symbol&quot;&gt;:genre&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;&amp;lt;br /&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= book.text_field :genre %&gt;
    &amp;lt;/p&gt;
  &amp;lt;% end %&gt;

  &amp;lt;% f.fields_for :magazines do |magazine| %&gt;
    &amp;lt;p&gt;
      &amp;lt;%=&lt;/span&gt; magazine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token symbol&quot;&gt;:title&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;&amp;lt;br /&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= magazine.text_field :title %&gt;
    &amp;lt;/p&gt;
    &amp;lt;p&gt;
      &amp;lt;%=&lt;/span&gt; magazine&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;label &lt;span class=&quot;token symbol&quot;&gt;:genre&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;&amp;lt;br /&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%= magazine.text_field :genre %&gt;
    &amp;lt;/p&gt;
  &amp;lt;% end %&gt;
  &amp;lt;%=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;submit &lt;span class=&quot;token string&quot;&gt;&apos;Submit&apos;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
&amp;lt;% end %&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That sets up the form with the nested model, now for the controller code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#controllers/authors_controller.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthorsController&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@author&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;
    books &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;books&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;build &lt;span class=&quot;token comment&quot;&gt;#this builds the nested form in the view&lt;/span&gt;
    magazines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;magazines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;build &lt;span class=&quot;token comment&quot;&gt;#this builds the nested form in the view&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;/span&gt;
    param_hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:media_type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;book&quot;&lt;/span&gt;
      param_hash&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;books_attributes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;elsif&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:media_type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;magazine&quot;&lt;/span&gt;
      param_hash&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;magazines_attributes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@author&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param_hash&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;save
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead of passing the params straight to Author.new, it’s put into a hash variable. Then the params get checked for a title and genre, if empty delete the key from the hash and pass to Author.new. Activerecord wont see the book param and will skip the validations for it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding A Simple Search In 5 Minutes to a Ruby on Rails Site]]></title><description><![CDATA[Prototyping a site in Ruby on Rails and need to add a simple search feature in less than 5 minutes?]]></description><link>https://aaronvb.com/articles/adding-a-simple-search-in-5-minutes-to-a-ruby-on-rails-site</link><guid isPermaLink="false">https://aaronvb.com/articles/adding-a-simple-search-in-5-minutes-to-a-ruby-on-rails-site</guid><pubDate>Sat, 24 Oct 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Prototyping a site in Ruby on Rails and need to add a simple search feature in less than 5 minutes?&lt;/p&gt;
&lt;p&gt;Note: This should be for smaller projects because I’ll be using regular SQL queries for searching. If you’re looking for something more advanced, try &lt;a href=&quot;https://freelancing-gods.com/thinking-sphinx/v4/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;thinking_sphinx(sphinx engine)&lt;/a&gt; or &lt;a href=&quot;https://sunspot.github.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;sunspot_solr(java lucene solr engine)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ll be using &lt;a href=&quot;https://github.com/binarylogic/searchlogic&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Searchlogic&lt;/a&gt; and &lt;a href=&quot;https://github.com/mislav/will_paginate&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;will_paginate&lt;/a&gt; in this demo.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#install searchlogic&lt;/span&gt;
sudo gem install searchlogic

&lt;span class=&quot;token comment&quot;&gt;#install will_paginate&lt;/span&gt;
gem sources &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;a http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;gemcutter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;org
sudo gem install will_paginate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then add the gem to the environment file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#config/environment.rb&lt;/span&gt;

config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gem &lt;span class=&quot;token string&quot;&gt;&quot;binarylogic-searchlogic&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:lib&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;searchlogic&quot;&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gem &lt;span class=&quot;token string&quot;&gt;&apos;will_paginate&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:version&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;~&gt; 2.3.11&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you prefer to use github or the plugin version, check out &lt;a href=&quot;https://rdoc.info/projects/binarylogic/searchlogic&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;their docs&lt;/a&gt;. I also suggest reading their docs to learn more about all the neat functions Searchlogic can do. It’s a very powerful and useful plugin.&lt;/p&gt;
&lt;p&gt;We’ll be adding the search to Posts, so let’s start by adding the search route to our posts.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#config/routes.rb&lt;/span&gt;

map&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resources &lt;span class=&quot;token symbol&quot;&gt;:posts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:collection&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:search&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:get&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will give you the path /posts/search GET and POST.&lt;/p&gt;
&lt;p&gt;Next add the search method to the PostsController.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#app/controllers/posts_controller.rb&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PostsController&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ApplicationController&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;index&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@posts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paginate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:order&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;created_at desc&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:per_page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;#pass the post into a get with the &apos;q&apos; param&lt;/span&gt;
      redirect_to search_posts_path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:q&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;elsif&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;unless&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blank&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;#Searchlogics title_like_all to search within titles in the Post model for any of the words in the query.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;#words in the string are split into an array by spaces&lt;/span&gt;
        search &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title_like_all&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to_s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;descend_by_created_at

        &lt;span class=&quot;token comment&quot;&gt;#paginate the results&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;@posts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; search&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;paginate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:per_page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;#pass the query object to the view to let the user know what they searched for&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;@query&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:q&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;#render the Post index.html.erb&lt;/span&gt;
      render &lt;span class=&quot;token string&quot;&gt;&apos;index&apos;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last part is the search form in your view.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; form_for &lt;span class=&quot;token symbol&quot;&gt;:search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:controller&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;posts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;search&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;f&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;%&gt;
		&amp;lt;%= f.text_field :query, :value =&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@query&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;%&gt;
		&amp;lt;%= f.submit &quot;Search&quot;, :disable_with =&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Searching...&quot;&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;%&gt;
	&amp;lt;% end %&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Reoccur Scheduling With Starling and Workling]]></title><description><![CDATA[How I moved from BackgrounDRb to Starling and Workling to not only manage my daily worker, but also everything else that needed regular background processing.]]></description><link>https://aaronvb.com/articles/reoccur-scheduling-with-starling-and-workling</link><guid isPermaLink="false">https://aaronvb.com/articles/reoccur-scheduling-with-starling-and-workling</guid><pubDate>Mon, 20 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the main reasons I first used BackgrounDRb was for it’s ability to use a cron like schedule that could reoccur at whatever time or date I specified. It was very easy infact, by just writing the cron schedule in the config file for the worker. I was satisfied and stuck with BDRb for just that reason, until I realized how much memory BDRb was using.&lt;/p&gt;
&lt;p&gt;Not only was I relying on BDRb to handle a worker that would reoccur nightly, I had also moved a lot of features that didn’t require immediate user feedback, to the background(ie. the message system). So not only was I looking for an alternative to handle my daily worker, but also everything else that needed regular background processing.&lt;/p&gt;
&lt;p&gt;I did some research on the current background processing methods  and came across an excellent blog post that provided me with a clean solution to all my problems. &lt;a href=&quot;https://web.archive.org/web/20090802045408/https://www.transfs.com/devblog/2009/04/06/goodbye-backgroundrb-hello-workling-starling/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;transfs.com/devblog/2009/04/06/goodbye-backgroundrb-hello-workling-starling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Starling and Workling.  There’s tons of information on getting it setup. I recommend watching the Railscast by Ryan Bates - &lt;a href=&quot;http://railscasts.com/episodes/128-starling-and-workling&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Starling and Workling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Starling is the queue server, which Workling polls and then executes workers depending on the task. Easy enough to understand. Even better, Starling is  a separate process outside of your rails application. Meaning, if your rails application gets restarted or stopped, or if Workling is off, everything in the Starling queue gets saved. That’s great and all, but the fact that Starling is a separate process and is using the the same design as memcache, means I can slip anything I want into the Starling queue outside of my rails application! To rephrase that, I’m able to start a background process in my rails application without having to be in my rails application.&lt;/p&gt;
&lt;p&gt;Let’s continue with some code examples.&lt;/p&gt;
&lt;p&gt;In my project I have a TemporaryShoppingCart model which handles temporary shopping carts that a user might create while visiting my site. At the start of each day(00:00:00 on my server), I want a background worker to clear the temporary shopping carts, but also create a record of the event and record how many carts were removed. The record would be stored in another model, say RemovedTemporaryShoppingCart so I can access that data later through an admin page. Note, I’m not sure how practical this is for a real application, I’m just creating this example off the top of my head.&lt;/p&gt;
&lt;p&gt;The first step is to create a method in TemporaryShoppingCart model that will do everything I stated above.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;empty_self&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;#create a new record in RemovedTemporaryShoppingCart model with the total carts removed&lt;/span&gt;
  &lt;span class=&quot;token constant&quot;&gt;RemovedTemporaryShoppingCart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;#deletes all records in this model. http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002275&lt;/span&gt;
  delete_all
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Nothing fancy there. Trying to keep as much code in the model rather than in the worker. Next I create the Workling worker.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#app/workers/daily_worker.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DailyWorker&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Workling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;empty_temporary_shopping_carts&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;TemporaryShoppingCart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;empty_self
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Make sure to restart Workling and the rails application to generate the new worker, and that Starling is running as well. Normally I can start the worker through the rails app by ‘DailyWorker.async&lt;em&gt;empty&lt;/em&gt;temporary&lt;em&gt;shopping&lt;/em&gt;carts()’ but that’s not what I want to do in my case.&lt;/p&gt;
&lt;p&gt;If you watched the Starling Workling Railscast by Ryan Bates, near the end of the cast, Ryan shows you how Starling works by connecting to it through a memcached protocol.  My next example is the exact same except with real data to start the worker.&lt;/p&gt;
&lt;p&gt;Load up irb from the terminal by typing ‘irb’.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rubygems&apos;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;returned &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; because rubygems is already initialized&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;memcache&apos;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; starling &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MemCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;localhost:22122&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;change ip&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;port &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; default&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MemCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; servers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ns&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ro&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; starling
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;MemCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; servers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ns&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ro&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; starling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set &lt;span class=&quot;token string&quot;&gt;&apos;daily_workers__empty_temporary_shopping_carts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;STORED\r\n&quot;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There should be a new record in RemovedTemporaryShoppingCart model which means the worker did the job. Great I just started a worker inside my rails application from outside my rails application. So now how do I get it to run on a schedule? Thankfully in that first link that I mentioned, the author realized they could use the built in cron feature in the linux operating system to run a ruby script similar to what we did in the irb above.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;#lib/nightly_worker_for_cron.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rubygems&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;memcache&quot;&lt;/span&gt;

host &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;localhost&quot;&lt;/span&gt;
port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;22122&lt;/span&gt;
starling &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;MemCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;host&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;port&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;ARGV&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;empty_temp_carts&apos;&lt;/span&gt;
    starling&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set &lt;span class=&quot;token string&quot;&gt;&apos;daily_workers__empty_temporary_shopping_carts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This file can be anywhere but for ease of editing access I threw it in my ‘lib’ folder and also named it ‘nightly&lt;em&gt;worker&lt;/em&gt;for_cron.rb’.&lt;/p&gt;
&lt;p&gt;Now to add it to my crontab I just type ‘crontab -e’ in my console and insert ‘0 0 * * * ruby /the&lt;em&gt;path&lt;/em&gt;to&lt;em&gt;my&lt;/em&gt;rails&lt;em&gt;app/lib/nightly&lt;/em&gt;worker&lt;em&gt;for&lt;/em&gt;cron.rb empty&lt;em&gt;temp&lt;/em&gt;carts’ on any line then save. Done!&lt;/p&gt;
&lt;p&gt;I wrote this to mainly show people how useful Starling and Workling can be. It might not be as easy to setup as delayed&lt;em&gt;job or straight forward, but there are a lot of neat things that it can do that delayed&lt;/em&gt;job cannot. Instead of trying to compare which one of these background methods is better, decide on what you NEED your application to do and figure out which one will work for you.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Paperclip Amazon S3 Background Upload Using Starling and Workling]]></title><description><![CDATA[We know it’s very easy to add S3 support into Paperclip, and there’s tons of information on setting that up. It works flawless and it’s simple, yet your upload now takes twice as long in comparison to using your servers filesystem for storage. The problem is pretty obvious and I’ll explain why in this article.]]></description><link>https://aaronvb.com/articles/paperclip-amazon-s3-background-upload-using-starling-and-workling</link><guid isPermaLink="false">https://aaronvb.com/articles/paperclip-amazon-s3-background-upload-using-starling-and-workling</guid><pubDate>Sun, 19 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I spent several days searching for ANY information on background uploads to Amazon S3 with &lt;a href=&quot;https://github.com/thoughtbot/paperclip&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Paperclip&lt;/a&gt; and I just couldn’t find anything concrete. There were a few posts on the Paperclip Google Code board that talked about it but with no clear examples of how to do it.&lt;/p&gt;
&lt;p&gt;Anyway, we know it’s very easy to add S3 support into Paperclip, and there’s tons of information on setting that up. It works flawless and it’s simple, yet your upload now takes twice as long in comparison to using your servers filesystem for storage. The problem is pretty obvious and I’ll explain why in this example in case you’re wondering.&lt;/p&gt;
&lt;p&gt;Example A(filesystem)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User uploads a file&lt;/li&gt;
&lt;li&gt;Application re sizes file into thumbnails&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Example B(Amazon S3)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User uploads a file&lt;/li&gt;
&lt;li&gt;Application re sizes file into thumbnails&lt;/li&gt;
&lt;li&gt;Application then connects to Amazon S3 and uploads thumbnail&lt;/li&gt;
&lt;li&gt;Repeat 3 until each thumbnail is uploaded&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you can see in Example B, the more thumbnails you need the longer your upload is going to take because Paperclip needs to send each file to Amazon S3 one at time. All the while a user is still waiting for the website to respond back. If you use New Relic RPM you’ll notice that your Apdex gets destroyed by this. Which is basically telling you that a user is waiting longer than a second(more like 3-5 seconds) for an upload process.&lt;/p&gt;
&lt;p&gt;Although there’s nothing that can be done about the initial upload, why does the user need to wait around while the application uploads to Amazon S3(which could take 2-5 seconds longer)?&lt;/p&gt;
&lt;p&gt;I worked on several ways to get Paperclip to process it’s Amazon S3 storage in the background that led me to more complications than a solution. I realized I wanted to try my best to not modify the original Paperclip code so there wouldn’t be any problems updating Paperclip in the future.&lt;/p&gt;
&lt;p&gt;The next idea was to have some sort of ‘syncing’ to Amazon S3 system. Let Paperclip upload to the filesystem, then have a background process move the files to Amazon S3, then delete the files off the filesystem.&lt;/p&gt;
&lt;p&gt;Let’s assume you already have your rails app, starling/workling installed and running, and paperclip installed and working on the model of your choice. Let’s also assume we’re applying this to a User model and the attachment is an avatar. You’ll also need the aws-s3 gem(&lt;a href=&quot;http://amazon.rubyforge.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://amazon.rubyforge.org&lt;/a&gt;) and add that to your environment.rb.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sudo gem install aws-s3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#environment.rb&lt;/span&gt;
config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gem &lt;span class=&quot;token string&quot;&gt;&apos;aws-s3&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:lib&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;aws/s3&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First let’s modify the Paperclip settings in our User model.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;has_attached_file   &lt;span class=&quot;token symbol&quot;&gt;:avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:styles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:large&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;800x800&amp;amp;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:medium&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;300x300&amp;amp;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:thumb&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;100x100#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:tiny&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;50x50#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:really_tiny&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;25x25#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:storage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:filesystem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://s3.amazonaws.com/webapps3bucketname/:attachment/:id/:style/:basename.:extension&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tmp/paperclip_uploads/:attachment/:id/:style/:basename.:extension&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The :url is set to the Amazon’s S3 link so that when we generate urls with object.avatar.url(:medium) it will output the Amazon S3 url instead of the filesystem path. I also set the :path to the location I want the file to be uploaded to and re sized. I chose the tmp folder since it seemed appropriate. (Note: I’m considering moving the location to system because it’s a shared folder across capistrano.)&lt;/p&gt;
&lt;p&gt;A new column is needed for this model to let us know when the background uploading is being processed.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;script/generate migration add_processing_upload_to_user processing_upload:boolean
rake db:migrate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It’s your choice if you want to set the default to true, assuming upon creation of a User record that an upload will be included. In my case, a user gets created first and if the user decides to upload an avatar, it’s done through the user settings later, so  I set the default to false.&lt;/p&gt;
&lt;p&gt;Next we’ll setup the worker which will handle the upload to Amazon S3.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;#app/workers/paperclip_background_upload_worker.rb&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PaperclipBackgroundUploadWorker&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;Workling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;upload_to_amazon_s3&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Begin uploading &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt; to Amazon S3...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#find the user and the avatar&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;find_by_id&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;avatar

    &lt;span class=&quot;token comment&quot;&gt;#connect to amazon s3&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Connecting to Amazon S3...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;S3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;establish_connection&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                                        &lt;span class=&quot;token symbol&quot;&gt;:access_key_id&lt;/span&gt;     &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ACCESSKEYID&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                        &lt;span class=&quot;token symbol&quot;&gt;:secret_access_key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;SECRETACCESSKEY&apos;&lt;/span&gt;
                                        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#first we store the original&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Uploading original image...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;S3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;S3Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path_s3&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:original&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; open&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:original&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;YOUR_AMAZONS3_BUCKET_NAME&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:access&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:public_read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#then thumbnails&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Uploading &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;key&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt; image...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token constant&quot;&gt;AWS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;S3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;S3Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path_s3&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; open&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;YOUR_AMAZONS3_BUCKET_NAME&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:access&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:public_read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#start clean up, files first then directories&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Removing original from local filesystem...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#remove original file&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:original&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#remove thumbnail files&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Removing &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;key&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt; from local filesystem...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#delete directories&lt;/span&gt;
    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Removing directories...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#remove original folder&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/tmp/paperclip_uploads/avatars/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/original&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#remove thumbnail folders&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;token builtin&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/tmp/paperclip_uploads/avatars/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;key&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#remove avatar user_id folder&lt;/span&gt;
    &lt;span class=&quot;token builtin&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;delete&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;/tmp/paperclip_uploads/avatars/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter tag&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token delimiter tag&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;#set processing to false now that we are finished&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;processing_upload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;save

    logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;info&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Finished uploading to Amazon S3.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is pretty simple. The worker uses the aws-s3 gem to upload the files and once it finishes they get removed from the filesystem. You’ll need to include your Amazon S3 Access Key ID and Secret Access Key and also fill in your Amazon S3 Bucket name that the files are getting uploaded to where it says ‘YOUR&lt;em&gt;AMAZONS3&lt;/em&gt;BUCKET&lt;em&gt;NAME”. You may also notice a new method named ’path&lt;/em&gt;s3’ which I had to add to Paperclips attachment file.&lt;/p&gt;
&lt;p&gt;Open up the ‘attachment.rb’ file in the ‘vendor/plugins/paperclip/lib/paperclip/’ folder.  Under ‘def initialize’ add the two ‘@path_s3 =’ lines code(around line 47):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;      &lt;span class=&quot;token variable&quot;&gt;@dirty&lt;/span&gt;             &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;#a new hash value and key for the s3 path we define in the model&lt;/span&gt;
      &lt;span class=&quot;token variable&quot;&gt;@path_s3&lt;/span&gt;           &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:path_s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token variable&quot;&gt;@path_s3&lt;/span&gt;           &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@path_s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;call&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@path_s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;is_a&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;Proc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

      normalize_style_definition
      initialize_storage&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then scroll down, in the the same file, to about line 113 where there’s a method named ‘path’(def path style = nil #:nodoc:). Under that method we are going to make a new one.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;/span&gt; style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;#:nodoc:&lt;/span&gt;
      original_filename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; interpolate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; style&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;#Added this for background uploading.&amp;lt;br /&gt;    #This returns the path to the attachment in the s3 bucket.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;path_s3&lt;/span&gt;&lt;/span&gt; style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt;
      original_filename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; interpolate&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;@path_s3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; style&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will let us define a new key value(:path&lt;em&gt;s3) in the has&lt;/em&gt;attached&lt;em&gt;file hash in the User model. If you’re wondering what the ‘path&lt;/em&gt;s3’ does, it determines the path within the Amazon S3 Bucket that the file gets saved to and by having it in our has&lt;em&gt;attached&lt;/em&gt;file options, we can change this if we need to later on. Which I’ll show you next.&lt;/p&gt;
&lt;p&gt;Back to the User model, we add a new line in the has&lt;em&gt;attached&lt;/em&gt;file hash:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;has_attached_file   &lt;span class=&quot;token symbol&quot;&gt;:avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:styles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:large&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;800x800&amp;amp;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:medium&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;300x300&amp;amp;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:thumb&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;100x100#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:tiny&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;50x50#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:really_tiny&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;25x25#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:storage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token symbol&quot;&gt;:filesystem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://s3.amazonaws.com/webapps3bucketname/:attachment/:id/:style/:basename.:extension&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tmp/paperclip_uploads/:attachment/:id/:style/:basename.:extension&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token symbol&quot;&gt;:path_s3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;:attachment/:id/:style/:basename.:extension&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Don’t forget to restart the server since the Paperclip plugin was modified and also restart workling to generate the new workling we created earlier.&lt;/p&gt;
&lt;p&gt;The last part is to call the worker through Starling. In this example, the file upload is being handled normally through the User controller and the Update action.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token method-definition&quot;&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;#only set the processing_upload to true if an avatar file was uploaded&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;#if the file doesn&apos;t pass valiations, @user wont get saved and processing_upload stays false&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;processing_upload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:avatar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;save
    flash&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:notice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Your profile was successfully updated!&quot;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#tell starling to start the background worker only if processing_upload is true&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#the id of the user gets passed to let our worker know which files to upload&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;PaperclipBackgroundUploadWorker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;async_upload_to_amazon_s3&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;processing_upload
    &lt;span class=&quot;token comment&quot;&gt;#redirect to the edit page&lt;/span&gt;
    redirect_to edit_user_path
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;#save failed&lt;/span&gt;
    redirect_to &lt;span class=&quot;token symbol&quot;&gt;:action&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;edit&apos;&lt;/span&gt;
    flash&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;:notice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Something went wrong while trying to update your profile.&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That’s it! The next step is to create a way to let your users know that the file is being processed. All you have to do is check if processing_upload = true.&lt;/p&gt;
&lt;p&gt;This is my first tutorial type of post so bear with me if I missed anything and please let me know asap! Also if you have any experience doing something similar to this, let me know!&lt;/p&gt;</content:encoded></item></channel></rss>