<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Code is Sisyphean</title>
  <id>https://armahillo.dev</id>
  <link href="https://armahillo.dev" />
  <link href="https://armahillo.dev/atom.xml" rel="self" />
  <updated>2026-09-07T21:50:26+00:00</updated>
  <rights>Copyright 2014, Aaron H</rights>
  <author>
    <name>Aaron H</name>
  </author>
  
    <entry>
      <title>Claude Notify</title>
      <link href="https://armahillo.dev/2026/08/23/claude-notify/" />
      <summary type="html">&lt;p&gt;Has this ever happened to you? You write out an elaborate prompt that you know is going to take a while but you have to choose between either&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;(a) babysitting the session while it churns to occasionally say “yes, proceed” when it asks for confirmation, or&lt;/li&gt;
  &lt;li&gt;(b) doing something else and checking back on it later only to realize it needed confirmation moments after you clicked away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want a third way.&lt;/p&gt;

&lt;h2 id=&quot;pass-1-terminal-tab-renaming&quot;&gt;Pass 1: Terminal tab renaming&lt;/h2&gt;

&lt;p&gt;My first pass on this was to have Claude auto-rename the tab when it required input. It would do something like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Indicating normal processing: “🟢 { normal tab title }”&lt;/li&gt;
  &lt;li&gt;Indicating prompt for a decision: “🟡 Decision { normal tab title }”&lt;/li&gt;
  &lt;li&gt;Indicating prompt for permission: “🔴 Permission { normal tab title }”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I forget the exact prompt I gave Claude, but it was something like:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I want you to change the tab title of my iTerm2 tab depending on whether you are processing a prompt response, needing my input for a decision, or needing my permission. It should use { the options I wrote out above }.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ultimately, this ended up not working because Claude is normally already modifying the tab title with a small status icon that it animates when it’s doing things (this does not clearly indicate confirmation prompts though).&lt;/p&gt;

&lt;p&gt;Forcing this approach seems like it would likely be a lot of effort and maintenance so there’s probably an easier way.&lt;/p&gt;

&lt;h2 id=&quot;pass-2-toast&quot;&gt;Pass 2: Toast?&lt;/h2&gt;

&lt;p&gt;Remember “Toast” notifications in MacOS? This was an earlier version, in the early 2010s, I think. Little rectangle pop-ups in the upper right corner that anything could tap into. I wasn’t yet a Mac user then, but on Linux I had some window manager plugins installed that emulated MacOS Toast.&lt;/p&gt;

&lt;p&gt;“Toast” is no longer available, but there is the &lt;a href=&quot;https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/DisplayNotifications.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;osascript&lt;/code&gt; library for AppleScript&lt;/a&gt;. This is a MacOS native library (that effectively replaces Toast, I guess?). You can open a terminal right now and enter this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;osascript &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;display notification &quot;Hello world&quot;&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;It Just Works!™&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve not written AppleScript before and haven’t needed it enough to justify prioritizing learning it, but this is a pretty simple task so it seems like a good instance of having Claude make it for me.&lt;/p&gt;

&lt;p&gt;The prompt I gave was similar to the above, but with the directive to “use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;osascript&lt;/code&gt; to display the notification (providing that URL above)” instead of changing the terminal tab.&lt;/p&gt;

&lt;h3 id=&quot;files&quot;&gt;Files&lt;/h3&gt;

&lt;p&gt;After a couple iterations, I got it to produce these two files:&lt;/p&gt;

&lt;h4 id=&quot;claudehooksclaude-notifysh&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/hooks/claude-notify.sh&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/bash
# Posts a macOS notification when Claude Code needs a decision from the user.
# Registered on the PermissionRequest hook, which carries tool_name so we can
# tell a plain tool-permission ask apart from an AskUserQuestion prompt.
# Fires once, immediately, when the prompt first comes up (no idle delay)

input=&quot;$(cat)&quot;
event=&quot;$(printf &apos;%s&apos; &quot;$input&quot; | jq -r &apos;.hook_event_name // empty&apos; 2&amp;gt;/dev/null)&quot;

notify_permission() {
  osascript -e &apos;display notification &quot;Claude needs permission to proceed&quot; with title &quot;🔴 Permission Needed&quot;&apos; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
}

notify_decision() {
  osascript -e &apos;display notification &quot;Claude is waiting on a decision&quot; with title &quot;🟡 Decision Required&quot;&apos; &amp;gt; /dev/null 2&amp;gt;&amp;amp;1
}

if [ &quot;$event&quot; = &quot;PermissionRequest&quot; ]; then
  tool=&quot;$(printf &apos;%s&apos; &quot;$input&quot; | jq -r &apos;.tool_name // empty&apos; 2&amp;gt;/dev/null)&quot;
  if [ &quot;$tool&quot; = &quot;AskUserQuestion&quot; ]; then
    notify_decision
  else
    notify_permission
  fi
fi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;claudesettingsjson&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/settings.json&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{ 
  #
  # possibly other stuff here, ending with a comma: ,
  #
  &quot;hooks&quot;: {
    &quot;PermissionRequest&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;~/.claude/hooks/claude-notify.sh&quot;
          }
        ]
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;usage&quot;&gt;Usage&lt;/h3&gt;

&lt;p&gt;If you’d like to incorporate this you can either:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Do it yourself&lt;/li&gt;
  &lt;li&gt;Have Claude do it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;diy&quot;&gt;DIY&lt;/h3&gt;

&lt;p&gt;Both the snippets above have filename headings. You probably just want to use those.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; ~/.claude/settings.json
&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; ~/.claude/hooks &lt;span class=&quot;c&quot;&gt;# if this gives an error about it already existing, that&apos;s fine&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; ~/.claude/hooks/claude-notify.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open those files in your preferred editor. Copy the contents of the above snippets into their respective files. Read over the contents of these files to have a general understanding of how it works. While I am not familiar enough with AppleScript to &lt;em&gt;write&lt;/em&gt; it, I would definitely agree it is very readable.&lt;/p&gt;

&lt;p&gt;You can execute the system commands (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;osascript -e ...&lt;/code&gt;) yourself to see what the output looks like. I recommend trying this out. This might all seem like overkill, but I think it’s a good habit to get into to challenge / validate the code that we are given by our LLM tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;N.B.&lt;/strong&gt; the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.json&lt;/code&gt; file almost certainly already exists for you. If you’re familiar with JSON, you’ll know how to integrate that segment into it. If you aren’t, I recommend having Claude help you do it (see below).&lt;/p&gt;

&lt;p&gt;Once those are saved, in your terminal, you’ll need to make the shell script executable:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x ~/.claude/hooks/claude-notify.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;confirming&quot;&gt;Confirming&lt;/h4&gt;

&lt;p&gt;Getting Claude to actually help you test it might be a little tricky. When I tried it on this device (I had generated it on a different one, initially, so my current device lacked that greater context), it took a few attempts to get it to &lt;em&gt;actually&lt;/em&gt; do something that triggered the confirmation prompt correctly.&lt;/p&gt;

&lt;p&gt;A prompt like this should work:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I just changed @~/.claude/settings.json and added @~/.claude/hooks/claude-notify.sh - please review these to see that you understand what they do. I would like you to test them out by giving me three dummy prompts that would cause these triggers. First ask my to confirm a decision, then ask for permission, then ask me to confirm a decision again. Space these requests out by 3 seconds between each.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should see the three notifications appear, in order.&lt;/p&gt;

&lt;h4 id=&quot;prompt&quot;&gt;Prompt&lt;/h4&gt;

&lt;p&gt;Choose a temporary working folder. I’ll use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/tmp&lt;/code&gt; here but you can use whatever you like. Create a space in it:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/tmp/claude-notify
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then create two stub files in it:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; ~/tmp/claude-notify/settings.json
&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; ~/tmp/claude-notify/claude-notify.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the files in your preferred editor and copy the contents of the above into the corresponding file.&lt;/p&gt;

&lt;p&gt;Now start a new session in that folder and authorize it to access the contents:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/tmp/claude-notify
claude
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The prompt you give it might look like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I’ve written out two files here: @settings.json and @claude-notify.sh – please review these files to see if you understand them. I want you to integrate these into your settings and hooks, respectively, so that I am then notified anytime a confirmation prompt comes up. Once you’ve integrated them, please confirm with me that they work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It will ask you for additional authorization, most likely.&lt;/p&gt;

</summary>
      <id>https://armahillo.dev/2026/08/23/claude-notify/</id>
      <updated>2026-08-23T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Simple Link Decoration using Fuzzy Attribute Selectors</title>
      <link href="https://armahillo.dev/2026/05/29/simple-link-decoration/" />
      <summary type="html">&lt;style type=&quot;text/css&quot;&gt;
.ex1 {
  a[href*=&quot;spotify.com&quot;] {
    transition: 0.5s;
    background: url(/assets/img/post_images/2026-05-29-spotify-icon.png) no-repeat;
    background-position: 0.5rem;
    background-size: 2rem;

    border-radius: 4px;
    font-weight: bold;
    background-color: #e91ed7;
    color: #fdff00;
    border-color: #fff;
    display: inline-block;
    padding: 1rem 1rem 1rem 3rem;
    margin: 0;
    border: 0.5px solid #666;

    &amp;:hover {
      background-size: 5rem;
      background-position: -2.25rem;
      background-color: #fdff00;
      color: #8f6afd;
      transition: 0.5s;
    }
  }
}
&lt;/style&gt;

&lt;p&gt;Over on my &lt;a href=&quot;https://12things.me/2024/02/buttons&quot;&gt;project journal&lt;/a&gt; I wrote a post a while back about doing dynamically-styled buttons. They look like this, for example:&lt;/p&gt;

&lt;fieldset class=&quot;ex1&quot;&gt;
  &lt;a href=&quot;https://open.spotify.com/user/armahillo&quot;&gt;Listen on Spotify&lt;/a&gt;
&lt;/fieldset&gt;

&lt;p&gt;The animation is neat and all (go dive into that link if you’re curious) but what I wanted to talk about here was &lt;em&gt;writing selectors&lt;/em&gt; that focus on the intent of the document and decouple the content layer from the presentation layer.&lt;/p&gt;

&lt;p&gt;On one side of the spectrum would be this very simplistic example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://open.spotify.com/user/armahillo&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;spotify m-1 pt-1 pb-1 pl-2 pr-2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  @armahillo on Spotify
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/armahillo&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;github m-1 pt-1 pb-1 pl-2 pr-2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  @armahillo on Github
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://instagram.com/armahillo&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;instagram m-1 pt-1 pb-1 pl-2 pr-2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  @armahillo on Instagram
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This isn’t specifically &lt;em&gt;wrong&lt;/em&gt; HTML. It’s valid HTML, well-formatted, and all things considered is fine.&lt;/p&gt;

&lt;p&gt;But I think we can do better.&lt;/p&gt;

&lt;p&gt;Two issues I see here are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Having a class “spotify” for a Spotify link is redundant.&lt;/li&gt;
  &lt;li&gt;Class-stuffing adds noise and conflates content/presentation layers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What if it looked like this, but displayed identically, instead?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://open.spotify.com/user/armahillo&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  @armahillo on Spotify
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://github.com/armahillo&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  @armahillo on Github
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://instagram.com/armahillo&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  @armahillo on Instagram
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This would be the simplest possible HTML we could write that is still functional. I like this approach because it lets the document remain as unmuddied as possible, letting it stay lithe and adaptable.&lt;/p&gt;

&lt;p&gt;The first thing we can do can be done in native CSS, using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Attribute_selectors&quot;&gt;fuzzy attribute selectors&lt;/a&gt;. To add icons for each of these links without using classes, we could use this CSS:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;spotify.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(/assets/img/post_images/spotify-icon.png)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* remainder of styles */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;github.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(/assets/img/post_images/github-icon.png)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* remainder of styles */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;instagram.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(/assets/img/post_images/instagram-icon.png)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* remainder of styles */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The selector &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a[href*=&quot;spotify.com&quot;]&lt;/code&gt; means “find any &lt;em&gt;a&lt;/em&gt; tag that has the &lt;em&gt;href&lt;/em&gt; property containing the substring &lt;em&gt;spotify.com&lt;/em&gt;”. You can be as specific as you like, though note that you will likely need to escape your slashes (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a[href*=&quot;docs.google.com\/document]&lt;/code&gt; for Google Docs).&lt;/p&gt;

&lt;p&gt;The next step is most easily done using &lt;a href=&quot;https://sass-lang.com/&quot;&gt;SCSS&lt;/a&gt;. You can accomplish something similar using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@apply&lt;/code&gt; in Tailwind, as well. Using the styles from the example in the beginning:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;span class=&quot;k&quot;&gt;@mixin&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;icon-prepend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;middle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This defines a reusable block of styles. We can than incorporate those with the fuzzy-matched selectors:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;spotify.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(/assets/img/post_images/spotify-icon.png)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;icon-prepend;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;github.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(/assets/img/post_images/github-icon.png)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;icon-prepend;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;instagram.com&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(/assets/img/post_images/instagram-icon.png)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;icon-prepend;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which would render a link that looks more like this:&lt;/p&gt;

&lt;style type=&quot;text/css&quot;&gt;
.ex2 {
  display: flex;
  flex-direction: row;
  justify-content: space-around;

  a[href*=&quot;spotify.com&quot;]::before {
    background: url(/assets/img/post_images/2026-05-29-spotify-icon.png) no-repeat;
    content: &apos;&apos;;
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    vertical-align: middle;
  }
  a[href*=&quot;github.com&quot;]::before {
    background: url(/assets/img/github.svg) no-repeat;
    content: &apos;&apos;;
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    vertical-align: middle;
  }
  a[href*=&quot;instagram.com&quot;]::before {
    background: url(/assets/img/instagram-symbol.svg) no-repeat;
    content: &apos;&apos;;
    display: inline-block;
    width: 1.5rem;
    height: 1.5rem;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    vertical-align: middle;
  }
}
&lt;/style&gt;

&lt;fieldset class=&quot;ex2&quot;&gt;
&lt;a href=&quot;https://open.spotify.com/user/armahillo&quot;&gt;
  @armahillo
&lt;/a&gt;
&lt;a href=&quot;https://github.com/armahillo&quot;&gt;
  @armahillo
&lt;/a&gt;
&lt;a href=&quot;https://instagram.com/armahillo&quot;&gt;
  @armahillo
&lt;/a&gt;
&lt;/fieldset&gt;
</summary>
      <id>https://armahillo.dev/2026/05/29/simple-link-decoration/</id>
      <updated>2026-05-29T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Shovels and Ladders</title>
      <link href="https://armahillo.dev/2026/05/11/shovels-and-ladders/" />
      <summary type="html">&lt;p&gt;Imagine programming to be like a process of mining a flat-surface by digging straight down. The desired program behavior is somewhere below the surface, and the further down you dig, the more complexity you can mine.&lt;/p&gt;

&lt;p&gt;So to dig downwards and add complexity, you need a shovel. But you can’t live in a hole; you need to be able to build ladders to get back out.&lt;/p&gt;

&lt;p&gt;In this metaphor, I’m loosely equating “shoveling” with “the application of code” and “building ladders” with “the comprehension of code”.&lt;/p&gt;

&lt;p&gt;(I suppose you could also compare it to “speaking a language” vs. “understanding a language”)&lt;/p&gt;

&lt;h2 id=&quot;all-ladder-no-shovel&quot;&gt;All ladder, no shovel&lt;/h2&gt;

&lt;p&gt;This could be more practically called “tutorial hell.” A dev who has done a zillion or two coding exercises and has a general understanding of what the various parts of a body of code mean. Perhaps you can even read existing code and more or less understand what it’s doing. You’ve spun up a zillion or two projects by following a tutorial.&lt;/p&gt;

&lt;p&gt;But you still feel stuck. What do you &lt;em&gt;do&lt;/em&gt; with this knowledge? Other than serving as a QA person for a tutorial generating company, people don’t typically get paid to just follow tutorials.&lt;/p&gt;

&lt;p&gt;So this user is &lt;em&gt;really good&lt;/em&gt; at building ladders, but has no shovel.&lt;/p&gt;

&lt;h2 id=&quot;all-shovel-no-ladder&quot;&gt;All shovel, no ladder&lt;/h2&gt;

&lt;p&gt;This is typically the metaphor I use to describe folks that are &lt;em&gt;purely&lt;/em&gt; vibe coders. These are the users that have the vision, they have an instinct that the gold they’re mining is beneath their feet and they just need to dig far enough to get it. You’re skilled prompt-writers and are comfortable with various tools to generate sites and apps at a quick pace.&lt;/p&gt;

&lt;p&gt;But maybe you’re introducing bugs and don’t know it. Or you do know it, but can’t seem to get the LLM to remove / resolve the bugs. Maybe the apps you’re handing off are wrought with technical debt / ugly code that is not extensible / maintainable. More typically, what I see &lt;em&gt;quite a lot&lt;/em&gt; is “how do I fix this code?” – users that have dug themselves into a hole with no exit. Or “I’ve generated a site, how do I get it onto the Internet?”&lt;/p&gt;

&lt;p&gt;This user is &lt;em&gt;really good&lt;/em&gt; at digging, but they’re in over their head and can’t get out.&lt;/p&gt;

&lt;h2 id=&quot;a-measured-hybrid-approach&quot;&gt;A measured hybrid approach&lt;/h2&gt;

&lt;p&gt;I &lt;em&gt;really&lt;/em&gt; don’t mean this to sound like “old man yells at cloud,” or “back in my day…”, but:&lt;/p&gt;

&lt;p&gt;Before LLMs, before passive video tutorials, we used books, documentation, and iteration.&lt;/p&gt;

&lt;p&gt;One of the first websites I ever made was using HTML I wrote in a text editor, saved to file, and uploaded via an FTP client to my webhost, back when your ISP would give you 1MB of free space. Every “Hello World” program involves compilation/execution of the program to complete the lifecycle. This is a fundamental part of the process – we learned how to dig and how to get back out.&lt;/p&gt;

&lt;p&gt;This is one of the reasons I advise against beginners using LLMs at all – it’s far too tempting to hop on that LLM excavator and start digging before you’ve learned how to get back out, or to even realize that this is something you’ll eventually have to do.&lt;/p&gt;

&lt;p&gt;On the other side of things, you can’t &lt;em&gt;only&lt;/em&gt; do tutorials – it’s fine to copy a “Hello World” program because the point is to show you “here’s how you dig a hole and then also climb out of it.” But following a tutorial on making a Tasklist, or a Bookmark manager, or a Phone directory – this is akin to following a map to a digsite and being told that “digging a hole that is 35’ deep” is the goal; you’re missing out on both understanding the &lt;em&gt;how&lt;/em&gt; to dig and also the &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;if-youve-got-a-shovel-but-need-to-learn-how-to-build-ladders-vibes-only&quot;&gt;If you’ve got a shovel but need to learn how to build ladders (Vibes only!)&lt;/h3&gt;

&lt;p&gt;Set the LLM aside for just a minute, or at least start a new session and start with a much simpler approach: How do you make a Hello World and get it where you want it to be?&lt;/p&gt;

&lt;p&gt;For web development this means writing a perfunctory page, deploying it somewhere, and seeing it in your browser at a public URL.&lt;/p&gt;

&lt;p&gt;For application development this means writing a Hello World program, running the compiler and executing the compiled binary.&lt;/p&gt;

&lt;p&gt;Once you’ve got that, start chunking off pieces of your Shovel-ready-program and integrating it into your Hello World. Maybe it uses an external library or collects input or displays a dataset. Start small. This how you learn how to build a ladder, one rung at a time.&lt;/p&gt;

&lt;p&gt;Avoid using your LLM at all for this part. It may feel frustrating (at first!), but I promise it will make you a stronger developer.&lt;/p&gt;

&lt;h3 id=&quot;if-youve-got-all-the-ladders-but-no-shovel-for-digging-tutorial-hell&quot;&gt;If you’ve got all the ladders but no shovel for digging (Tutorial hell!)&lt;/h3&gt;

&lt;p&gt;The typical answer I see (and likely have given) is to “identify a problem and spell out the behavior, then start iterating on it.” But this is maybe a bit too much hand-waving.&lt;/p&gt;

&lt;p&gt;If you’ve already done some tutorials, pause on starting any new ones. Go dig up an old codebase and read back through it. What does it do? Run it. What is it missing? If you were a user, would you use it? What additional features does it need to actually be useful? Can you make it look different? Can you add new behaviors?&lt;/p&gt;

&lt;p&gt;Example: You have a Tasklist / To-Do app. Add deadlines. Add handle prerequisite tasks (task A cannot be started until task B is completed). Add a task archive for completed tasks but hide them from the main interface. Add priority values and add ranked sorting based on different traits.&lt;/p&gt;

&lt;p&gt;You want to have an objective that is just a few feet deeper from where you are right now. It needs to have some depth where you have to evaluate the problem and devise a solution, but you don’t want it to be so deep that it feels impossibly exhausting.&lt;/p&gt;

&lt;h3 id=&quot;if-youre-starting-completely-fresh-and-arent-sure-where-to-begin&quot;&gt;If you’re starting completely fresh and aren’t sure where to begin&lt;/h3&gt;

&lt;p&gt;In all seriousness, find a book on the language you want to learn. There are still many that are relevant today, and even if it’s a little outdated, programming hasn’t changed &lt;em&gt;that&lt;/em&gt; much, so the information will still be transferrable. Ideally, a book that has exercises throughout the chapters or at the end are best – the chapter itself helps you learn how to build ladders, and the exercises teach you how to dig.&lt;/p&gt;

&lt;p&gt;If you don’t have access to a library or your library doesn’t have the books (most libraries can do Interlibrary loans, ask your circulation / reference desk person!), look for online resources that both offer instruction &lt;em&gt;and&lt;/em&gt; exercises. I personally like &lt;a href=&quot;https://exercism.org&quot;&gt;Exercism&lt;/a&gt; but there are other similar resources. You want to have &lt;em&gt;both&lt;/em&gt; instruction and exercises, though, and you will want to &lt;em&gt;do the exercises&lt;/em&gt; without looking up how to do it. Definitely do &lt;em&gt;not&lt;/em&gt; ask the LLM to do it for you, if you use one.&lt;/p&gt;

&lt;p&gt;If you are deadset on using an LLM, then try giving it this prompt:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; I am a new programmer who is trying to learn ___ language. Please give me 5 appropriately-difficult exercises I can do. I want to ask you questions about these exercises but I want you not to give me solutions, so that I can learn to write the programs myself.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then dig in to the first exercise.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2026/05/11/shovels-and-ladders/</id>
      <updated>2026-05-11T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Reflections on LLM usage</title>
      <link href="https://armahillo.dev/2026/05/04/reflections-on-llm-usage/" />
      <summary type="html">&lt;p&gt;I suppose this was inevitable.&lt;/p&gt;

&lt;p&gt;In the previous posts I made about LLMs, which I’ve condensed down to just three posts with audience focus, I spoke stridently and with great vigor against the usage of LLMs &lt;em&gt;at all&lt;/em&gt;, essentially teetotalling.&lt;/p&gt;

&lt;p&gt;I’ve re-read it, and I still stand by everything I wrote, which is why it remains up. But I realize I’ve been a bit one-sided. In my programming practice I try to hold pragma over dogma, and on further reflection I came to realize that I was being a bit dogmatic in my resistance.&lt;/p&gt;

&lt;p&gt;I am definitely not an “AI drunkard”, but I would say that I am less a teetotaller now and more a “having a drink in moderation is probably ok.” I still have ethical misgivings about &lt;em&gt;how&lt;/em&gt; we’ve introduced this technology, particularly the absurd greed-driven fervor that is &lt;a href=&quot;https://www.youtube.com/watch?v=_bP80DEAbuo&quot;&gt;spawning data-centers with little regard to the communities&lt;/a&gt; and various ecosystems. I still think that this technology should not belong in the hands of private companies, nor do I trust any of them to be responsible about its usage.&lt;/p&gt;

&lt;p&gt;I &lt;em&gt;still&lt;/em&gt; stand by every comment I made about the implicit harm it can induce on the learning process of a budding junior in their journey to becoming a competent senior.&lt;/p&gt;

&lt;p&gt;HOWEVER.&lt;/p&gt;

&lt;p&gt;In February or March, around the time that Anthropic released Claude 4.6, I tried out using it with a coworker while we were pairing. Previously I had experimented a little bit with Opus 4.3 to mild success. It was neat but still felt unreliable and stunted. Opus 4.6, on the other hand… well. I get it now.&lt;/p&gt;

&lt;h3 id=&quot;delegating-to-claude&quot;&gt;Delegating to Claude&lt;/h3&gt;

&lt;p&gt;One of my earlier tenets of using LLMs was to “treat it as a junior, not a senior.” Typically, this means delegating work to it and then reviewing / checking that work. Here is a short list of things I’ve done where I’ve delegated work to Claude:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Bug hunting&lt;/li&gt;
  &lt;li&gt;Data combining and permutating&lt;/li&gt;
  &lt;li&gt;Data diving / complicated find-and-replace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all these cases, this is work I could have done myself but was able to turn it over more quickly by delegating it.&lt;/p&gt;

&lt;h4 id=&quot;bug-hunting&quot;&gt;Bug hunting&lt;/h4&gt;

&lt;p&gt;My coworker and I both had a general idea of where the bug lay, so we felt confident in our ability to check Claude’s conclusions. We described the normal behavior, described the bug, described approximately the behavior we were expecting to see, and listed out the files of where it could start looking. It churned for 5-10 minutes and then came back with a specific analysis and proposed fix. Surprisingly, it also found an additional bug that we didn’t know about but that was related.&lt;/p&gt;

&lt;p&gt;Realistically, this probably would have taken us about an hour to do and I’m confident we would have figured it out. So there was a substantial time-saving there. The bug was also small enough, conceptually, that we could individually integrate it into our understanding of the application’s function.&lt;/p&gt;

&lt;h4 id=&quot;data-combining-and-permutating&quot;&gt;Data combining and permutating&lt;/h4&gt;

&lt;p&gt;There is a tabletop board game I’ve been working on for a while. It involves a lot of traits/qualities that are permutated / combined in various ways, and it had a few holes. I gave Claude my seed list of what I had already, described the resultset I was aiming for, and then told it to fill out the list using that scheme.&lt;/p&gt;

&lt;p&gt;This case took 15-20 minutes, but it searched the Internet to research more terms / concepts related to the dataset and came back with some very informed opinions (with actual URLs for support) about how to structure this data. I recorded the findings to a static file for reference, then had it apply this to the dataset.&lt;/p&gt;

&lt;p&gt;The end result was very elaborate. This would have probably taken me considerably longer to generate this, and honestly I’m not sure I would have thought to look for some of the approaches it took. This was a very impressive win.&lt;/p&gt;

&lt;h4 id=&quot;data-diving&quot;&gt;Data-diving&lt;/h4&gt;

&lt;p&gt;Normally for this kind of task I would use a tool like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ripgrep&lt;/code&gt; (or just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt;), and some regex, maybe. But sometimes the needle I’m looking for isn’t a simple string.&lt;/p&gt;

&lt;p&gt;There have been a few cases where I’ve given it tasks like “Find me every test in the test suite that uses an order with two or more items”, or “Look through my YML files and find me every top-level record key that has mixed-case” (that one &lt;em&gt;could&lt;/em&gt; be regexed but I wanted to see if it could do it).&lt;/p&gt;

&lt;p&gt;I’ve also used it to do textual summaries. There are a few books I own and have read already, but also have the PDFs. By using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pdf&lt;/code&gt; skill, I can have Claude convert a PDF to text format, then have it create a summary index of the book, and then ask it questions about things I don’t remember and can’t find in the book’s physical index.&lt;/p&gt;

&lt;p&gt;I actually ended up doing this with several books I owned and creating expert skills for both programming and also game design – I am aware that the LLMs have been trained on all of these books already, but that ocean of knowledge is so vast the individual perspectives have been diluted. By concentrating a skill onto 5-7 books specifically, I can get concrete reference examples when asking for opinions.&lt;/p&gt;

&lt;h4 id=&quot;two-exceptions&quot;&gt;Two Exceptions&lt;/h4&gt;

&lt;p&gt;In two cases, I &lt;em&gt;have&lt;/em&gt; treated it as a subject-matter-expert:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Converting a web application to an iOS app written in Swift&lt;/li&gt;
  &lt;li&gt;Creating a &lt;em&gt;de novo&lt;/em&gt; multi-platform mobile app using Unity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have very little prior mobile development experience, so I’m breaking away from the original tenet in this case. I &lt;em&gt;can&lt;/em&gt; however, at least read the generated code and leverage my experience in a way that an Engineering Manager might – knowing what questions to ask, how to phrase them, and what things to look for.&lt;/p&gt;

&lt;p&gt;I find it a little easier to learn new things if I can see examples that are familiar material so it’s more of a diagonal move upwards instead of an orthogonal one. As the apps have taken shape, I’ve been able to use this to build some competency about how an iOS or Unity app is structured, where files are located, and the general flow of working these apps.&lt;/p&gt;

&lt;h3 id=&quot;ideating-with-claude&quot;&gt;Ideating with Claude&lt;/h3&gt;

&lt;p&gt;A coworker showed me how to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/plan&lt;/code&gt; feature and I’ve found it to be very useful.&lt;/p&gt;

&lt;p&gt;When coding, this has been useful for both bughunting (see above) as well proposing it enhancements or greenfield ideas. In both cases, Claude CLI is capable of seeing the full app tree and its contents.&lt;/p&gt;

&lt;p&gt;I find myself pushing back a lot more on this, than with the more more surgical bug-fixing mentioned previously. Particularly in the way it generates code.&lt;/p&gt;

&lt;p&gt;Most of the generated code I’ve seen so far has been “technically correct” (the best kind of correct, I’m told), but has always felt too atomic / “contained”. By contrast, what I would like to see is code that more clearly integrates / synthesizes with the existing codebase.&lt;/p&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/plan&lt;/code&gt; does help to alleviate this somewhat, because I can get a better sense of what it’s proposing before it executes. But just to be safe, I always ensure I have a clean &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git status&lt;/code&gt; before beginning, so I can review the changes in detail. Sometimes I treat the Claude contribution as a spike and then go back and rewrite it myself, refactoring it to integrate better into the code, as I prefer it.&lt;/p&gt;

&lt;h3 id=&quot;assisting-with-claude-rather-being-assisted-by&quot;&gt;Assisting with Claude (rather: being assisted by)&lt;/h3&gt;

&lt;p&gt;There’s two ways I want to approach this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;How I have it assist me, broadly speaking, via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;How I use it as an Assistive Technology, as a neurodivergent person&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;claudemd&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Claude, specifically, looks for a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLAUDE.md&lt;/code&gt;. You can put one in the root of any of your applications. You can also put one in your home folder (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/CLAUDE.md&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In both cases, it’s a document that describes, in plain english, what you’re trying to do and how you want Claude to behave.&lt;/p&gt;

&lt;h5 id=&quot;claudeclaudemd&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/h5&gt;

&lt;p&gt;I treat this one like a very broad “get to know me” directive. It describes the kinds of work I will be using Claude for, and any specifics about how I want it to help me.&lt;/p&gt;

&lt;p&gt;For my development work, I describe the kind of work I do (web development using HTML, CSS, and JS, as well as doing a lot of development in Ruby and Rails). I describe my coding practices and preferences in broad terms, as well as what level of specificity I would like it to have in its responses.&lt;/p&gt;

&lt;p&gt;For my game design work, I describe the types of games I often make, some general thoughts about my approach towards design, and also the document structure of my design documents. I have some ruby scripts I wrote up a while ago that I use to convert YML files to CSV files that I can ingest into dextrous.com.au to export cards from. Additionally it has designated areas for ideation docs, playtest logs, etc. All of this is described in the doc.&lt;/p&gt;

&lt;p&gt;Finally it has some general statements of the kind of attitude I want it to have (TL;DR - avoid sycophancy and be concise but with clarity).&lt;/p&gt;

&lt;h5 id=&quot;repoclaudemd&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.../{repo}/CLAUDE.md&lt;/code&gt;&lt;/h5&gt;

&lt;p&gt;In these files, typically for development projects, I will generally first summarize the project at the 10,000 foot level, then do subheadings with more specific details about implementation that it would need to know. I treat this as a sort of “onboarding” document.&lt;/p&gt;

&lt;p&gt;My best advice here, if you do this, is to create the file, write a summary of a few sentences about the project, and then spin up a Claude CLI session in that directory and give it this prompt:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; Please read through @CLAUDE.md and the rest of this project and let me know what more you need me to clarify.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Even better, do it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/plan&lt;/code&gt; mode so it can give you a Q&amp;amp;A and come up with all the edits it needs to make, and then it can write out the updated doc itself. (Proofread it!)&lt;/p&gt;

&lt;h4 id=&quot;as-an-assistive-technology&quot;&gt;As an Assistive Technology&lt;/h4&gt;

&lt;p&gt;Other examples of “Assistive Technologies” would be things like screen-readers for vision impaired, subtitles for hearing impaired, curb-cuts for people in wheelchairs / mobility issues, etc.&lt;/p&gt;

&lt;p&gt;For me, the issues I have are cognitive. When I have challenging days, it feels like the desktop in my brain is very cluttered / has little free space. Sometimes this means it can be harder to lay out a lot of different moving parts in abstraction to synthesize the ideas together. I’ve found Claude to be &lt;em&gt;very&lt;/em&gt; helpful with this, particularly in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/plan&lt;/code&gt; mode – I can lay out a problem, reference files, give it additional contexts, and have it help me better visualize the whole problemspace more effectively.&lt;/p&gt;

&lt;p&gt;I can tell this is helpful on these days because I feel a sense of mental &lt;em&gt;relief&lt;/em&gt; where normally it would be anxiety/frustration.&lt;/p&gt;

&lt;p&gt;In the past, the way I would approach this is typically through writing a lot more things down, drawing diagrams, etc. It helped, though with limitations. I can also take transcripts from calls and import those transcripts into the Claude dialogue and integrate those into the abstraction.&lt;/p&gt;

&lt;p&gt;Honestly, this relief alone is worth the $20 a month I’m paying.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2026/05/04/reflections-on-llm-usage/</id>
      <updated>2026-05-04T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Code is Sisyphean</title>
      <link href="https://armahillo.dev/2025/11/03/code-is-sisyphean/" />
      <summary type="html">&lt;p&gt;This eponymous post applies to developers of all skill levels, but especially to newer developers.&lt;/p&gt;

&lt;p&gt;The metaphor I keep coming back to is to imagine your journey as a developer as similar to one as a mountain climber, or perhaps more on-brand for this blog: the Sisyphean task of pushing a boulder up a mountain.&lt;/p&gt;

&lt;h3 id=&quot;pushing-a-boulder&quot;&gt;Pushing a Boulder&lt;/h3&gt;

&lt;p&gt;If we can imagine getting the boulder to the top of the mountain as “solving a problem with technology”, then the act of pushing the boulder up is “identifying the problem, charting a path towards a solution, and writing the code to solve the problem”.&lt;/p&gt;

&lt;p&gt;Using an LLM would, in its most ideal case, be something like using a cable-pulley system to pull the boulder up, or using a tractor to push it, etc. In reality maybe the LLM takes you up to the peak of the wrong mountain, or to a sub-summit peak, or pulls only part of the boulder, etc.&lt;/p&gt;

&lt;p&gt;In this metaphor, the benefit of pushing the boulder yourself is that it builds your leg and arm strength. It builds your determination. It builds your self-confidence (noted in the CMU study, above). It hones your ability to identify the smoothest path up the mountain.&lt;/p&gt;

&lt;p&gt;Utilizing external forces – the cable pulley, the tractor – to do this work for you, that’s not going to build those traits that you need to do it on your own. Worse yet, the underutilization of those traits will likely weaken them.&lt;/p&gt;

&lt;p&gt;Every mountain is different, even if they all have a summit. Some mountains are not going to be able to utilize the cable-pulley or tractor for some or all of the ascent. In those scenarios, you will be &lt;em&gt;forced&lt;/em&gt; to push it yourself, relying on your bodily strength, situational path awareness, and force of will to continue that challenging ascent.&lt;/p&gt;

&lt;h3 id=&quot;becoming--staying-strong&quot;&gt;Becoming &amp;amp; Staying Strong&lt;/h3&gt;

&lt;p&gt;You aren’t going to forget everything you know because you had the boulder dragged up the mountain once, or even twice. But be vigilant about your habits: are you finding yourself consistently taking the easy way out?&lt;/p&gt;

&lt;p&gt;For newer devs, my advice is to &lt;em&gt;completely&lt;/em&gt; eschew all LLMs while you are still learning. “Treat them as a junior, not a senior” means that while you are a junior yourself, you don’t use them because there aren’t any tasks you can delegate that you’ve yourself mastered.&lt;/p&gt;

&lt;p&gt;You are probably going to see your peers that &lt;em&gt;are&lt;/em&gt; doing things with generative LLMs appear to be advancing more quickly. These will be the same peers that inevitably &lt;a href=&quot;https://www.reddit.com/r/AskProgramming/search/?q=can&apos;t+program+without&quot;&gt;post on reddit&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“&lt;a href=&quot;https://www.reddit.com/r/AskProgramming/comments/1kl70rx/got_hired_for_my_potential_now_struggling_to_keep/&quot;&gt;Got Hired for my Potential, now struggling to keep up without AI&lt;/a&gt;”&lt;/li&gt;
  &lt;li&gt;“&lt;a href=&quot;https://www.reddit.com/r/AskProgramming/comments/1i20wj4/help_i_can_not_code_without_ai/&quot;&gt;Help! I can not code without AI!&lt;/a&gt;”&lt;/li&gt;
  &lt;li&gt;“&lt;a href=&quot;https://www.reddit.com/r/AskProgramming/comments/1f46dv6/programmers_have_you_ever_had_a_coworker_that_had/&quot;&gt;Programmers, have you ever had a coworker that had no clue what he/she was doing and always had to use AI to get the work done?&lt;/a&gt;”&lt;/li&gt;
  &lt;li&gt;“&lt;a href=&quot;https://www.reddit.com/r/AskProgramming/comments/1mqgus5/i_cant_code_anything_without_chatgpt_or_a/&quot;&gt;I can’t code anything without ChatGPT or a tutorial - how do I break this&lt;/a&gt;”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start strong. Stay strong.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2025/11/03/code-is-sisyphean/</id>
      <updated>2025-11-03T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Advice to Seniors regarding LLMs</title>
      <link href="https://armahillo.dev/2025/10/27/advice-to-seniors-re-llms/" />
      <summary type="html">&lt;h2 id=&quot;beware-skill-atrophy&quot;&gt;Beware skill atrophy&lt;/h2&gt;

&lt;p&gt;By “skill atrophy” I mean “skill rot”, “becoming rusty”, “dulling your blade” or any of the other colorful metaphors people use. It’s analogous to a runner getting out of practice and losing speed, or a litigator who hasn’t seen the courtroom in a long time and doesn’t have quite the same edge.&lt;/p&gt;

&lt;h3 id=&quot;cell-phone-contacts&quot;&gt;Cell Phone Contacts&lt;/h3&gt;

&lt;p&gt;A more banal comparison that those of us of a certain age might know:&lt;/p&gt;

&lt;p&gt;Those of us that grew up without cellphones probably used to have many phone numbers memorized. Since cellphones (“dumb” phones, included!) have contact lists that reference numbers by name instead of digits, our brains have no reason to memorize new phone numbers.&lt;/p&gt;

&lt;p&gt;I still remember most phone numbers from my childhood, but only have a handful of phone numbers memorized now (close family members).&lt;/p&gt;

&lt;p&gt;In development, it might look like forgetting syntax more frequently, or finding it harder to refactor, or even as severe as getting executive dysfunction when confronting a programming problem.&lt;/p&gt;

&lt;h3 id=&quot;self-confidence-and-critial-thinking-in-the-age-of-ai&quot;&gt;Self-confidence and critial thinking in the age of “AI”&lt;/h3&gt;

&lt;p&gt;In this 2025 Carnegie Mellon study &lt;a href=&quot;https://www.microsoft.com/en-us/research/wp-content/uploads/2025/01/lee_2025_ai_critical_thinking_survey.pdf&quot;&gt;The Impact of Generative AI on Critical Thinking: Self-Reported Reductions in Cognitive Effort and Confidence Effects From a Survey of Knowledge Workers&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Specifically, higher confidence in GenAI is associated with less critical thinking, while higher self-confidence is associated with more critical thinking. Qualitatively, GenAI shifts the nature of critical thinking toward information verification, response integration, and task stewardship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The study is careful to acknowledge:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Our analysis does not establish causation. However, based on our evidence, it is possible that fostering workers’ domain expertise and associated self-confidence may result in improved critical thinking when using GenAI. Task confidence significantly influences how users engage with AI tools, particularly in the context of human-AI “collaboration” (not withstanding objections to that term).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;LLM-driven development is still a very novel concept, so we really don’t have any longitudinal studies objectively showing skill atrophy.&lt;/p&gt;

&lt;h3 id=&quot;opportunities-for-growth&quot;&gt;Opportunities for growth&lt;/h3&gt;

&lt;p&gt;Personally, I &lt;em&gt;enjoy&lt;/em&gt; programming. The depth of knowledge I have about my particular slice of the coding world is something that is consistently useful at my job, especially with bug hunting and resolution. I would be disappointed to lose that edge or become reliant on a third party service to be able to perform my job.&lt;/p&gt;

&lt;p&gt;I have yet to be convinced that using an LLM to generate a solution will save me a significant amount of time, when you consider the “total cost of ownership” for that solution – factoring in review time, corrections, and implementation time. It feels more jarring and stunted than approaching it more conventionally.&lt;/p&gt;

&lt;p&gt;Given that, I see every opportunity to program as an opportunity for me to hone my skills a bit more, to become a bit sharper, and to learn new things.&lt;/p&gt;

&lt;h2 id=&quot;over-reliance-on-an-llm-means-undermines-your-value-add-as-an-employee&quot;&gt;Over-reliance on an LLM means undermines your value-add as an employee&lt;/h2&gt;

&lt;p&gt;This applies more for juniors, but seniors should beware too:&lt;/p&gt;

&lt;p&gt;I see a lot of organizations that have “LLM-required” mandates from management. I see this as a huge red flag that those developer jobs are in danger of being replaced or reduced. For whatever acceleration they may tout they are targeting, the real danger here is that if your job becomes purely prompt engineering, you are being forced into potential skill atrophy and also your role can be replaced by someone who can be paid less for doing the same work.&lt;/p&gt;

&lt;h3 id=&quot;ai-driven-layoffs&quot;&gt;“AI”-driven Layoffs&lt;/h3&gt;

&lt;p&gt;Recently, &lt;a href=&quot;https://www.cnbc.com/2025/10/28/amazon-layoffs-corporate-workers-ai.html&quot;&gt;Amazon laid off 14,000 corporate (read “white collar”) employees&lt;/a&gt;. It included employees in “Amazon’s cloud, grocery, video games, human resources, sustainability and communications, ads and devices”. Management at Amazon very much believes humans can be replaced by LLMs, and is acting on that belief.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fortune.com/2025/10/28/corporations-companies-mass-layoffs-2025-ai-tech-retail-tariffs/&quot;&gt;Fortune magazine&lt;/a&gt; quotes Jason Schloetzer:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“it’s not so much AI directly taking jobs, but AI’s appetite for cash that might be taking jobs”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The article notes these large corporations and their planned / executed layoffs:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Company&lt;/th&gt;
      &lt;th&gt;Layoffs&lt;/th&gt;
      &lt;th&gt;% of workforce&lt;/th&gt;
      &lt;th&gt;Source&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Amazon&lt;/td&gt;
      &lt;td&gt;14,000&lt;/td&gt;
      &lt;td&gt;4%&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/amazon-layoff-ai-14000-artificial-intelligence-cb64af47ebb794541fbdfa8fd264932c&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;UPS&lt;/td&gt;
      &lt;td&gt;34,000&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/ups-amazon-layoffs-turnaround-85afc1c459883f41a2283c8394ce1eaf&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Target&lt;/td&gt;
      &lt;td&gt;1,800&lt;/td&gt;
      &lt;td&gt;8%&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/target-corporate-layoffs-minneapolis-india-f4d8100fa2bf4af67e4fb4e388cedf50&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Nestlé&lt;/td&gt;
      &lt;td&gt;16,000&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/nestle-freixe-navratil-switzerland-purina-9de74ef338cdc95018cae859d5c8a332&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lufthansa Group&lt;/td&gt;
      &lt;td&gt;4,000&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/ai-therapy-ban-illinois-therabot-dfc5906b36fdd1fe8e8dbdb4970a45a7&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Novo Nordisk&lt;/td&gt;
      &lt;td&gt;9,000&lt;/td&gt;
      &lt;td&gt;11%&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/wegovy-novo-nordisk-ozempic-obesity-denmark-357fd9cbef29b324dfb059f35dc5ff6a&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ConocoPhillips&lt;/td&gt;
      &lt;td&gt;2,600-3,250&lt;/td&gt;
      &lt;td&gt;20-25%&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/conocophillips-layoffs-job-cuts-2025-f35528aad7d8c02895853d73bb12842e&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Intel&lt;/td&gt;
      &lt;td&gt;~22,000&lt;/td&gt;
      &lt;td&gt;15%&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/intel-chips-layoffs-a3a83e4bb8cd71e2615e964b432dd37f&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Microsoft&lt;/td&gt;
      &lt;td&gt;15,000&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/microsoft-layoffs-xbox-f44079957b12370f72e24edebe9fcc6b&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Proctor &amp;amp; Gamble&lt;/td&gt;
      &lt;td&gt;7,000&lt;/td&gt;
      &lt;td&gt;6%&lt;/td&gt;
      &lt;td&gt;&lt;a href=&quot;https://apnews.com/article/procter-gamble-jobs-tariff-cff3724e3c0a82408d3fbb0266014559&quot;&gt;AP News&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Note that not &lt;em&gt;all&lt;/em&gt; of these cuts are &lt;em&gt;strictly&lt;/em&gt; due to “AI”, but this was cited expressly as a factor by more than a few of them.&lt;/p&gt;

&lt;h3 id=&quot;llm-driven-skill-caps&quot;&gt;LLM-driven Skill Caps&lt;/h3&gt;

&lt;p&gt;If you’re a junior and all of your development is based on generated code, then the limitation of your capabilities is capped at what you are able to produce with an LLM. You will speed out of the gate in a vehicle that has rocket-fast acceleration but a lower overall max speed.&lt;/p&gt;

&lt;p&gt;The corrollary to this, of course, is that a developer who chooses NOT to use LLMs to drive their development will have a higher ceiling cap, though it may take longer to get to that point. Your metaphorical vehicle has a lower acceleration but ultimately a much higher skill ceiling. By choosing the slower route, you will ultimately be a stronger developer and ostensibly more competitive in the marketplace.&lt;/p&gt;

&lt;h3 id=&quot;market-recovery&quot;&gt;Market Recovery&lt;/h3&gt;

&lt;p&gt;I sincerely believe that when the hype finally dies down a bit, as it did for Blockchain, there will be a demand for skilled developers again.&lt;/p&gt;

&lt;p&gt;The abilities of LLMs are impressive, but they are predominantly better at  creation than maintenance, because creation can ostensibly be done &lt;em&gt;de novo&lt;/em&gt; whereas maintenance requires synthesis of context. As things get more and more complex and as systems grow, it will eventually be too large or costly to perform these functions, even with &lt;a href=&quot;https://en.wikipedia.org/wiki/Retrieval-augmented_generation&quot;&gt;RAG&lt;/a&gt; (Retrieval Augmented Generation).&lt;/p&gt;

&lt;p&gt;This may or may not be partly behind the ravenously hungry appetite that the various “AI” companies have for expansion and growth – in order to make good on the bets that are being made by corporate America, there will need to be an expansion of computing power.&lt;/p&gt;

&lt;h2 id=&quot;inevitable-enshittification-of-llms&quot;&gt;Inevitable Enshittification of LLMs&lt;/h2&gt;

&lt;p&gt;If you are a heavy user of LLMs, or are considering it, I would like you to consider:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;How much are you spending per month on LLM services?&lt;/li&gt;
  &lt;li&gt;How effective would you be if they were suddenly unavailable?&lt;/li&gt;
  &lt;li&gt;How much of a cost increase could you realistically absorb before it became untenable? How much would be a hardship?&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;enshittification&quot;&gt;Enshittification&lt;/h3&gt;

&lt;p&gt;In Cory Doctorow’s 2022 (and recently re-printed!) book &lt;a href=&quot;https://www.versobooks.com/products/3341-enshittification&quot;&gt;Enshittification&lt;/a&gt;, he proposes 3 stages that every online platform goes through:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The platform is good to users&lt;/li&gt;
  &lt;li&gt;The platform is bad to users, good to businesses&lt;/li&gt;
  &lt;li&gt;The platform is bad to users, bad to businesses, good to itself&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The progression occurs as a platform reaches critical mass for user lock-in (enough people are established as users that they cannot easily walk away). Consider, for example, the friction you would feel from walking away from using any particular online platform.&lt;/p&gt;

&lt;p&gt;We are somewhere between #1 and #2.&lt;/p&gt;

&lt;p&gt;When ChatGPT first came out, there was a rapid expansion of users finding different use-cases for it. The future seemed bright and exciting. But we are also seeing LLMs being the driving factor behind layoffs. We are seeing the job market dry up for Junior Devs, which AWS Ceo Matt Garman calls &lt;a href=&quot;https://www.finalroundai.com/blog/aws-ceo-matt-garman-says-replacing-junior-developers-with-ai-the-dumbest-thing&quot;&gt;the dumbest thing he’s ever heard&lt;/a&gt; in light of &lt;a href=&quot;https://www.finalroundai.com/blog/entry-level-jobs-disappearing-fast-because-of-ai&quot;&gt;entry level jobs disappearing because of “AI”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Mark my words: &lt;strong&gt;as soon as the AI companies feel they have enough users and businesses dependent on their services, they will jack up prices, &lt;em&gt;significantly&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.wheresyoured.at/why-everybody-is-losing-money-on-ai/&quot;&gt;Every single AI company is still operating at a loss (overall)&lt;/a&gt; despite raking in massive amounts of revenue. &lt;a href=&quot;https://www.cnbc.com/2025/10/13/oracle-ceo-magouyrk-of-course-openai-can-pay-60-billion-per-year.html&quot;&gt;OpenAI and Oracle have signed a 5-year $300 Billion cloud deal&lt;/a&gt;. For a company that is operating at a loss, their belief that they will be able to convert that to a net gain of $300 Billion (whether through revenues or investment rounds) means they have a plan for something.&lt;/p&gt;

&lt;p&gt;Additionally, they are planning on &lt;a href=&quot;https://techcrunch.com/2025/10/14/openai-has-five-years-to-turn-13-billion-into-1-trillion/&quot;&gt;spending $1 Trillion over the next decade to expand their computing infrastructure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By comparison, they are currently claiming 800 million regular users, with 5% (40 million) contributing to their $13 Billion of annualized revenue.&lt;/p&gt;

&lt;h3 id=&quot;covering-the-shortfall&quot;&gt;Covering the shortfall&lt;/h3&gt;

&lt;p&gt;Hypothetically speaking, let’s just say that OpenAI alone wants to satisfy their $60 Billion annual payments to Oracle. They need to make up for a $47 Billion shortfall. They would need to increase their subscription prices / costs PER ALL USER (even the 95% of the free tier users!) by $59 / user to recover that difference.&lt;/p&gt;

&lt;p&gt;Looking at their &lt;a href=&quot;https://chatgpt.com/pricing/&quot;&gt;current plans&lt;/a&gt;:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Tier&lt;/th&gt;
      &lt;th&gt;Current price&lt;/th&gt;
      &lt;th&gt;Adjusted price&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Free&lt;/td&gt;
      &lt;td&gt;$0&lt;/td&gt;
      &lt;td&gt;$59&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Plus&lt;/td&gt;
      &lt;td&gt;$20&lt;/td&gt;
      &lt;td&gt;$79&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Pro&lt;/td&gt;
      &lt;td&gt;$200&lt;/td&gt;
      &lt;td&gt;$259&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Granted, there are probably other ways they could re-distribute those values, perhaps by introducing a new tier between Free and Plus that costs $20 and requires a substantial portion of Free users to convert, but one way or another, the company needs to either extract $47 Billion from its 800 million users, find more users and also raise prices, or start screwing its business customers over too.&lt;/p&gt;

&lt;p&gt;Enshittification points towards some combination of all of those.&lt;/p&gt;

&lt;h3 id=&quot;dependency&quot;&gt;Dependency&lt;/h3&gt;

&lt;p&gt;This brings me back to my original point:&lt;/p&gt;

&lt;p&gt;If you’re currently using the Free tier, could you walk away? Could you afford a $59 increase?&lt;/p&gt;

&lt;p&gt;If you’re using Plus or Pro, could you afford a 300% or ~35% (respectively) increase in fees? Could you do your job if you had to walk away?&lt;/p&gt;

&lt;p&gt;The cynic in me thinks that the companies demanding their employees use LLMs at their jobs are &lt;em&gt;trying&lt;/em&gt; to get users dependent on these services, fattening them up for the inevitable slaughter.&lt;/p&gt;

&lt;p&gt;It then becomes an act of resistance to continue to be a strong developer in the absence of these generative tools.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2025/10/27/advice-to-seniors-re-llms/</id>
      <updated>2025-10-27T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Advice to Juniors / Newbies regarding LLMs</title>
      <link href="https://armahillo.dev/2025/10/20/advice-to-juniors-re-llms/" />
      <summary type="html">&lt;p&gt;I feel like a &lt;a href=&quot;https://www.reddit.com/user/armahillo/search/?q=llm&amp;amp;type=comments&quot;&gt;bit of a broken record&lt;/a&gt; about this topic at this point.&lt;/p&gt;

&lt;p&gt;I started my programming journey as a young kid, doing the LOGO Turtle/Terrapin programming on the Apple LC ][ computers in my elementary school. I didn’t really get into programming in earnest until I was a teen, starting out rather ambitiously with the C and C++ languages. A high school class taught me some fundamentals in Pascal, and I was learning how to do some Bash scripting as well, as a hobbyist linux user. I was a TA for my “Computer Programming &amp;amp; Methods” course in HS my senior year, because I had learned C++ well enough to help teach it.&lt;/p&gt;

&lt;p&gt;This isn’t to brag, but just to provide some background illustration.&lt;/p&gt;

&lt;p&gt;The point of learning is to learn. Reaching the solution is really only a small part, maybe 20-25%?, of software development. The real skills are in the research, hunting, and experimentation.&lt;/p&gt;

&lt;p&gt;I’ve compared this to using Google Translate while you’re trying to learn a new spoken language. Or using a calculator to get the answer to a practice set in a math class.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;process&lt;/em&gt; of finding the answer is &lt;strong&gt;critical&lt;/strong&gt; early on. You’re learning how to find answers, getting the correct answer itself is &lt;em&gt;less&lt;/em&gt; important.&lt;/p&gt;

&lt;p&gt;If you’re using an LLM to tell you how to get to the answer, you’re missing out on the necessary exploration.&lt;/p&gt;

&lt;p&gt;Additionally, and this applies to experienced devs as well: if you use an LLM to fast track you to the answer, you’re denying yourself the time you would have spent reading through API documentation, potentially picking up additional details that are unrelated. Anecdotally, I’ve definitely learned about new methods / modalities by spelunking API docs to find an answer.&lt;/p&gt;

&lt;p&gt;Every “wrong” answer to your problem is still something new you can learn and may be useful later. The LLM response is going to fast-track you directly to the actual answer.&lt;/p&gt;

&lt;p&gt;In the following posts, I am going to reiterate these themes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Learning “the hard way” is going to help you learn &lt;em&gt;better&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Use it or lose it: skills can atrophy&lt;/li&gt;
  &lt;li&gt;LLMs are imperfect assistants&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;treat-an-llm-as-a-junior-dev-not-a-senior-dev&quot;&gt;Treat an LLM as a Junior Dev, not a Senior Dev&lt;/h2&gt;

&lt;p&gt;One significant gripe I have with LLMs is that they are equally confident about their responses whether they are spot on or completely wrong. When you point out they’re mistaken they say “you’re correct, how about this?” and give you another answer that may or may not be correct. I’ve heard it called “mansplaining as a service” because it’s high-confidence in an answer that &lt;em&gt;looks&lt;/em&gt; convincing but without &lt;em&gt;actual&lt;/em&gt; basis of expertise.&lt;/p&gt;

&lt;h3 id=&quot;spot-the-error&quot;&gt;Spot the error&lt;/h3&gt;

&lt;p&gt;For example, if you were shown this after asking for some PHP code that would replace the word ‘fizz’ with the word ‘buzz’ from a POSTed param named ‘input’:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;strpos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;fizz&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str_replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;fizz&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;buzz&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;input&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Looks convincing, right? It’s even got semicolons in the right places!&lt;/p&gt;

&lt;p&gt;What’s the mistake here? &lt;a href=&quot;https://www.php.net/manual/en/function.strpos.php&quot;&gt;hint 1&lt;/a&gt;, &lt;a href=&quot;https://www.php.net/manual/en/function.str-replace.php&quot;&gt;hint 2&lt;/a&gt;.&lt;/p&gt;

&lt;details&gt;&lt;summary&gt;Answer&lt;/summary&gt;
  In my decade+ span of using PHP, the argument order of `str_replace` and `strpos` was perennially confusing to me. In this case, they are both reversed, the correct order: `str_replace($needle, $replace, $haystack)` and `strpos($haystack, $needle)`. So it _should_ be:

  `strpos($_POST[&apos;input&apos;], &apos;fizz&apos;)`

&lt;/details&gt;

&lt;h3 id=&quot;anecdotal-example&quot;&gt;Anecdotal example&lt;/h3&gt;

&lt;p&gt;I recently had a coworker throw up a quick PR to fix some Javascript that we had written for one of our intranet forms. There was a bug in the existing code and this fix was to address that new behavior. My coworker generated a fix using an LLM service because they weren’t comfortable with their level of Javascript skill.&lt;/p&gt;

&lt;p&gt;Upon review, it was pretty clear there were some issues. It added a new global-space class (appending to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window&lt;/code&gt;), for one. The fix was an atomic unit that technically fixed the issue, but did so by adding a bunch of extra cruft that essentially “tacked-on” the solution instead of actually correcting the underlying buggy behavior.&lt;/p&gt;

&lt;p&gt;It took me a couple hours, but I managed to detangle the generated Javascript, refactor the behavior into modules, and then integrate it into the existing code to both resolve the bug and add the enhanced behavior.&lt;/p&gt;

&lt;h3 id=&quot;trust-but-verify&quot;&gt;Trust, but verify&lt;/h3&gt;

&lt;p&gt;For this reason, my policy, and advice, around the use of LLMs in development is to treat it as you would a junior dev: trust it to do what you ask, but verify its correctness.&lt;/p&gt;

&lt;p&gt;By contrast, don’t treat it as a senior dev (or at least, one that is “senior to you”) – that means don’t ask it to do things for you that you do not know how to do yourself.&lt;/p&gt;

&lt;p&gt;In other words, if you want to delegate it work that you could have done yourself but will save you time by having it generated, where your time spend is just on the reviewing (and change-requests / modifications), then fine. But avoid delegating it work that you don’t know how to do yourself. There be dragons.&lt;/p&gt;

&lt;h2 id=&quot;an-llm-is-not-a-rubber-duck&quot;&gt;An LLM is &lt;em&gt;not&lt;/em&gt; a Rubber Duck&lt;/h2&gt;

&lt;p&gt;Harvard’s CS50 course, widely lauded as a great intro for nascent devs, and they have created a companion LLM product for it called &lt;a href=&quot;https://cs50.ai/&quot;&gt;CS50.ai&lt;/a&gt;, which is presented as a tool for “rubber duck debugging.”&lt;/p&gt;

&lt;p&gt;This may seem superficially correct, but if you have ever done actual &lt;a href=&quot;https://en.wikipedia.org/wiki/Rubber_duck_debugging&quot;&gt;rubber duck debugging&lt;/a&gt;, you might be able to detect why it is NOT that. From the wikipedia article, which I think gives a great summation:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Rubber duck debugging (or rubberducking) is a debugging technique in software engineering. A programmer explains their code, step by step, in natural language—either aloud or in writing—to reveal mistakes and misunderstandings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More specifically:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Programmers often discover solutions while explaining a problem to someone else, even to people with no programming knowledge. Describing the code, and comparing to what it actually does, exposes inconsistencies. Explaining a subject also forces the programmer to look at it from new perspectives and can provide a deeper understanding.
The programmer explaining their solution to an inanimate object (such as a rubber duck) has the advantage of not requiring another human, but also works better than thinking aloud without an audience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to specifically call attention to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“explaining to an inanimate object”&lt;/li&gt;
  &lt;li&gt;“describing the code … exposes inconsistencies”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A &lt;em&gt;critical&lt;/em&gt; aspect of this is that &lt;em&gt;the duck does not speak back to you!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Using an LLM as a coding-pair is another topic entirely, but for this case? Not a rubber duck.&lt;/p&gt;

&lt;h3 id=&quot;why-is-rubber-ducking-helpful&quot;&gt;Why is Rubber Ducking helpful?&lt;/h3&gt;

&lt;p&gt;The benefit of rubber ducking is that&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;By presuming no knowledge of the listener, you are forced to distill your understanding down to real words that can be verbalized, forcing abstractions to become concrete&lt;/li&gt;
  &lt;li&gt;By receiving no feedback, you are also forced to consider how that information is &lt;em&gt;received&lt;/em&gt; by the listener, acting as their proxy, which means you are now re-ingesting your understanding of the problem, creating a feedback loop.&lt;/li&gt;
  &lt;li&gt;By re-ingesting the information in that feedback loop, you then do the comparison yourself, identifying gaps and inconsistencies in your understanding, driving you towards the solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is similar to Jungian psychological principles about mental pluralism or the cognitive “senate” in parts work. &lt;em&gt;You gain the most benefit by embodying all the roles.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When using an LLM, you are now only performing the first step (expressing the problem in plain language) but are not doing the latter parts (receiving and synthesizing that information).&lt;/p&gt;

&lt;p&gt;I completely understand why someone might think that using an LLM as a rubber duck is an improvement – again, superficially it seems like “wouldn’t it be better if the duck could give you feedback?” But you will become stronger and more competent by embodying that full feedback loop yourself.&lt;/p&gt;

&lt;h2 id=&quot;beware-xy-problems-with-llms&quot;&gt;Beware XY Problems with LLMs&lt;/h2&gt;

&lt;p&gt;An &lt;a href=&quot;https://en.wikipedia.org/wiki/XY_problem&quot;&gt;XY problem&lt;/a&gt; is&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;where the question is about an end user’s attempted solution (X) rather than the root problem itself (Y or Why?).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a common consequence of situations where the attempted solution is borne from an incomplete understanding of the problem. These are usually spotted by being very specific inquiries that are devoid of context, and the correct way to address them is to first ask for the greater context, to understand why they chose to solve it in this way.&lt;/p&gt;

&lt;p&gt;The wikipedia article gives this example:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Asking about how to grab the last three characters in a filename (X) instead of how to get the file extension (Y), which may not consist of three characters&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This becomes a problem with LLMs because LLMs generally do not push back to look for XY problems. They will give you their best guess at what you are asking for, whether it’s the correct approach or not.&lt;/p&gt;

&lt;h4 id=&quot;anecdotal-example-1&quot;&gt;Anecdotal example&lt;/h4&gt;

&lt;p&gt;On another occasion, a different coworker used an LLM to generate a script that performed a series of commands over SSH to a remote server. We reviewed the script together, focusing on what we felt were the riskiest parts (the SSH commands, predominantly) and found it to be acceptable.&lt;/p&gt;

&lt;p&gt;However we ran into a bug that wasn’t immediately apparent. Most server IDs had 5 digits (the vast majority did). Some of them had 4 digits, though. In the generated script, one of the early steps was to parse that string to split off the last 3 digits to be used separately from the first digits.&lt;/p&gt;

&lt;p&gt;This happened because, in the initial prompt, the sample data provided had 5 digits so the generated code only accounted for parsing metadata from those 5 digits.&lt;/p&gt;

&lt;p&gt;This approach broke for server IDs with 4 digits because it was peeling off the first 2 (always) rather than the last 3 (always) – right solution for the wrong problem. e.g.:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;12345 =&amp;gt; 12, 345 (Correct)
1234 =&amp;gt; 12, 34 (Incorrect, should be: 1, 234)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We overlooked it in the initial pass because we gave it too much leeway and focused on the wrong aspect of the solution. We found an existing script that had already solved this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;part1 = serverID / 1000
part2 = serverID % 1000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This corrected the issue.&lt;/p&gt;

&lt;p&gt;This is perhaps a soft XY issue, but highlights the point about LLMs giving you exactly what you ask for, &lt;a href=&quot;https://en.wikipedia.org/wiki/Unintended_consequences#Perverse_results&quot;&gt;monkey’s paw style&lt;/a&gt;.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2025/10/20/advice-to-juniors-re-llms/</id>
      <updated>2025-10-20T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>The Rubygems / RubyCentral Fiasco</title>
      <link href="https://armahillo.dev/community/2025/09/19/rubygames-rubycentral/" />
      <summary type="html">&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: Please review &lt;a href=&quot;https://gem.coop&quot;&gt;gems.coop&lt;/a&gt; as a replacement for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source &apos;rubygems.org&apos;&lt;/code&gt; in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;updates&quot;&gt;Updates&lt;/h3&gt;

&lt;h4 id=&quot;07-sep-2026---a-year-later&quot;&gt;07 Sep 2026 - A Year Later&lt;/h4&gt;

&lt;p&gt;A lot has happened in the last year. &lt;a href=&quot;https://rubycentral.org/news/a-new-chapter-for-ruby-central/&quot;&gt;Leadership has changed&lt;/a&gt;. Per that update:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We have parted ways with our Executive Director, our PR agency, our CFO, and concluded several contractor engagements. These were not easy decisions, but they were necessary to ensure the long-term sustainability of Ruby Central.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Given that these were all (likely) the key players in the disastrous mishandling of these events, I think this was a wise move by RubyCentral.&lt;/p&gt;

&lt;p&gt;One notable addition was Brandon Weaver (as Secretary), aka KeystoneLemur. I had the pleasure of catching a car ride with him at Ruby By the Bay many years ago, and have kept up with his conference talks / blog posts. I think he’s a genuinely good person.&lt;/p&gt;

&lt;p&gt;Additionally, they have changed &lt;a href=&quot;https://www.theregister.com/software/2026/04/19/exec-director-of-ruby-central-gone-amid-financial-jeopardy/5228931&quot;&gt;from a governing board to a volunteer working board&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“That means instead of the board advising a full-time Executive Director, our board members have taken on direct roles and responsibilities alongside our hardworking team of staff and volunteers. We voted for some difficult personnel cuts and to reduce discretionary spending.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While I still think an apology is due, it isn’t due to me personally, and perhaps it was extended privately to the affected contributors. I had said previously that either a change in leadership or governance structure would be necessary for things to heal – it appears they have done both.&lt;/p&gt;

&lt;p&gt;Here’s hoping the future trends positively for the organization, and for Ruby at large.&lt;/p&gt;

&lt;h4 id=&quot;29-oct-2025---final-update&quot;&gt;29 Oct 2025 - Final Update&lt;/h4&gt;

&lt;p&gt;As of last week, &lt;a href=&quot;https://www.ruby-lang.org/en/news/2025/10/17/rubygems-repository-transition/&quot;&gt;ownership of RubyGems has been transferred to Matz&lt;/a&gt;. This decision was made by RubyCentral, who frankly shouldn’t really have the authority to make this decision &lt;em&gt;anyways&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But that’s where we’re at.&lt;/p&gt;

&lt;p&gt;RubyCentral published an absurdly lengthy &lt;a href=&quot;https://rubycentral.org/news/source-of-truth-update-friday-october-24-2025/&quot;&gt;Q&amp;amp;A Response&lt;/a&gt; where they continued their line of “sorry you’re mad, bro.”&lt;/p&gt;

&lt;p&gt;I am content with RubyGems (and Bundler) landing on Matz’s lap, but RubyCentral has done nothing to restore any confidence or goodwill, as far as I’m concerned. I’m disappointed and annoyed. I don’t intend to support them or anything they do in the future. I will advise others in the same way. I don’t expect things to change until there is a change in leadership or governance structure.&lt;/p&gt;

&lt;h4 id=&quot;10-oct-2025&quot;&gt;10 Oct 2025&lt;/h4&gt;

&lt;p&gt;André Arko &lt;a href=&quot;https://andre.arko.net/2025/10/09/the-rubygems-security-incident/&quot;&gt;responds&lt;/a&gt; to RubyCentral’s “incident report”. He provides a lot more transparency and clarity than RubyCentral did in their incident report, and it seems pretty apparent that his actions were aligned with his DevOps role in being an on-call for RubyCentral. He responsibly disclosed his access to Marty Haught.&lt;/p&gt;

&lt;p&gt;This quote struck me in particular:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have also noticed I am still, as of September 30, the owner of the GitHub organizations named “rubycentral” and “rubytogether”.
I am unable to transfer the HelpScout or PagerDuty accounts, as you have disabled my andre(at)rubygems.org Google account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Had RubyCentral acted responsibly, they would have (a) known all of the various administrative services being used by the team, and (b) coordinated with the existing team to ensure a seamless transfer of service before making that impossible by disabling their e-mail accounts.&lt;/p&gt;

&lt;p&gt;RubyCentral also apparently had accused André of fraudulent access:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ruby Central’s attorney was sending my lawyer a letter alleging I had committed a federal crime, on the theory that I had “hacked” Ruby Central’s AWS account.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I do not have confidence nor faith in RubyCentral’s leadership to be able to participate positively in this community.&lt;/p&gt;

&lt;h4 id=&quot;9-oct-2025&quot;&gt;9 Oct 2025&lt;/h4&gt;

&lt;p&gt;RubyCentral posts an “AWS Root Access Event” security incident post-mortem. Joel Drapper had reported previously how André Arko still had root access to the AWS instance for RubyGems, because RubyCentral failed to cover all their bases when they did their takeover.&lt;/p&gt;

&lt;p&gt;My editorialized opinion is: had RubyCentral acted in good faith and executed this seizure of power ethically, they could have avoided this by having everyone work collaboratively.&lt;/p&gt;

&lt;h4 id=&quot;5-oct-2025&quot;&gt;5 Oct 2025&lt;/h4&gt;

&lt;p&gt;Most of the maintainers behind the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems&lt;/code&gt; repository have just created &lt;a href=&quot;https://gem.coop&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem.coop&lt;/code&gt;&lt;/a&gt;, which is a functional replacement for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt; as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; entry in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt;. This is built on a governance model similar to the &lt;a href=&quot;https://brew.sh&quot;&gt;“Homebrew”&lt;/a&gt; project, aided by the lead maintainer of that project.&lt;/p&gt;

&lt;p&gt;Since RubyCentral refuses to take accountability or even acknowledge any kind of misconduct here, this feels like the absolute best path forwards. I strongly encourage any Ruby users to make this change.&lt;/p&gt;

&lt;h4 id=&quot;920am-et-26-sep-2025&quot;&gt;9:20AM ET (26 Sep 2025)&lt;/h4&gt;

&lt;p&gt;Based on the summary fact-checking followup by Joel Drapper (see link below), specifically:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;HSBT broke with the existing pact when he added Marty Haught as an owner. As a result, Ruby Central, at the request of Shopify, seized the RubyGems open source properties from their maintainers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I have read the &lt;a href=&quot;https://andre.arko.net/2025/09/25/bundler-belongs-to-the-ruby-community/merger-agreement.pdf&quot;&gt;merger document&lt;/a&gt; signed by Evan Phoenix on 16 August 2021 and André Arko on 20 August 2021. Bundler and RubyGems never belonged to Ruby Together and they were not transferred to Ruby Central as part of the merger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The transgressions appear to be:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Github user @HSBT was a rogue actor who violated community trust through a forcible change in ownership of a community resource&lt;/li&gt;
  &lt;li&gt;The maintainers were removed unjustly and as a rogue act&lt;/li&gt;
  &lt;li&gt;RubyCentral does not have the &lt;em&gt;de jure&lt;/em&gt; authority to justify their &lt;em&gt;de facto&lt;/em&gt; possession of this community resource.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The correcting actions seem to be:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Explicitly extend an offer to restore their positions as Repo maintainers to: André Arko, Colby Swandale, David Rodríguez, Ellen Dash, Josef Šimánek, Martin Emde and Samuel Giddins.&lt;/li&gt;
  &lt;li&gt;Removal or reduction of administrative privileges for HSBT, since this user has shown they cannot be trusted to act in accordance with the community&lt;/li&gt;
  &lt;li&gt;Reduction of Marty Haught’s Organizational role from Administrator to Member, and setting his privileges to one where he is able to actively contribute to the repository (acting as an advocate for Ruby Central) but unable to act on any hostile demands by RubyCentral or Shopify.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These items need to be satisfied to reset things to even. To repair some of the harm that has been done to community trust, I think these things should also happen so that we can heal and move forwards:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;An apology from Shan Cureton (on behalf of RubyCentral) explaining in detail that she understands &lt;em&gt;how&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; these actions were inappropriate within the context of this open source community.&lt;/li&gt;
  &lt;li&gt;An acknowledgement by Shan Cureton that she understands that the assertions made in the press release Ruby Central released were in fact untrue and misguided. (See Joel Drapper’s fact checking post)&lt;/li&gt;
  &lt;li&gt;An apology from HSBT to the maintainers that were removed and to the community as a whole.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;1246pm-et-19-sep-2025&quot;&gt;12:46PM ET (19 Sep 2025)&lt;/h4&gt;

&lt;p&gt;Still not a whole lot of clarity about the situation, but additional links added below.&lt;/p&gt;

&lt;p&gt;The people I’ve spoken with that know many of the technical folks involved (Marty et al.) have vouched for the character of those technical people. &lt;a href=&quot;https://bsky.app/profile/rhiannon.io/post/3lz6zcflg2s26&quot;&gt;Rhiannon Payne’s commentary&lt;/a&gt; suggests this is a leadership issue. Based on the rather tonedeaf and apparent doublespeak of the &lt;a href=&quot;https://rubycentral.org/news/strengthening-the-stewardship-of-rubygems-and-bundler/&quot;&gt;official word of Ruby Central&lt;/a&gt;, that seems to track pretty well.&lt;/p&gt;

&lt;p&gt;There is an ongoing &lt;a href=&quot;https://github.com/rubygems/rfcs/pull/61&quot;&gt;discussion about governance&lt;/a&gt; initiated by Martin Emde that is being actively mediated by Mike McQuaid (from Homebrew). However,&lt;/p&gt;

&lt;p&gt;The arms-length appearance of this is that it was a perhaps well-intentioned but horribly-executed maneuver. The security / safety of RubyGems as a service seems presently intact. Since the &lt;a href=&quot;https://github.com/rubygems/rfcs/pull/61#issuecomment-3309461815&quot;&gt;last update by Marty Haught&lt;/a&gt;, Emde appears to be &lt;a href=&quot;https://ruby.social/@martinemde/115231071745471283&quot;&gt;dissatisfied with the state of things&lt;/a&gt; and is, perhaps, also withdrawing in solidarity with Ellen Dash? This is unclear as there has not been a formal statement outside of that update.&lt;/p&gt;

&lt;h4 id=&quot;1015am-et-19-sep-2025&quot;&gt;10:15AM ET (19 Sep 2025)&lt;/h4&gt;

&lt;p&gt;Added a link to the open letter from RubyCentral. They are hosting an open &lt;a href=&quot;https://us06web.zoom.us/meeting/register/auIbrbS9RSS7Eukzj7b-kw#/registration&quot;&gt;Q&amp;amp;A Zoom on 23 September&lt;/a&gt;🔀.&lt;/p&gt;

&lt;h4 id=&quot;959am-et-19-sep-2025&quot;&gt;9:59AM ET (19 Sep 2025)&lt;/h4&gt;

&lt;p&gt;Will update with more info as I learn more. But here’s everything I could find so far.&lt;/p&gt;

&lt;h3 id=&quot;officially--openly-stated-withdrawal-from-rubygems--rubycentral&quot;&gt;Officially / Openly Stated withdrawal from RubyGems / RubyCentral&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rubygems/rubygems/pull/8987#issue-3433365698&quot;&gt;Ellen Dash&lt;/a&gt; (RubyGems)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://andre.arko.net/2025/09/19/goodbye-rubygems/&quot;&gt;André Arko&lt;/a&gt; (RubyGems)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ruby.social/@martinemde/115231071745471283&quot;&gt;Martin Emde(?)&lt;/a&gt; (RubyCentral)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/leaving-ruby-central-rhiannon-payne-f9axc/&quot;&gt;Rhiannon Payne&lt;/a&gt; (RubyCentral)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hachyderm.io/@segiddins/115152991524234421&quot;&gt;Sam Giddins&lt;/a&gt; (RubyCentral)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/simi/349d881d16d3d86947945615a47c60ca&quot;&gt;Josef Šimánek&lt;/a&gt; (RubyCentral)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;discussion-and-relevant-links-happening-presently&quot;&gt;Discussion and relevant links happening presently&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://pup-e.com/goodbye-rubygems.pdf&quot;&gt;@duckinator’s open letter of resignation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://weirder.earth/@puppy/115229870944806989&quot;&gt;@duckinator (as @puppy)’s thread on Mastodon&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rubygems/rubygems/pull/8987#issue-3433365698&quot;&gt;@duckinator’s PR self-removing from maintainers list&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://andre.arko.net/2025/09/19/goodbye-rubygems/&quot;&gt;André Arko’s letter of resignation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/ruby/comments/1nkzszc/ruby_centrals_attack_on_rubygems/&quot;&gt;Discussion thread on /r/ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=45299170&quot;&gt;Discussion thread on YCombinator&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ruby.social/@martinemde/115231071745471283&quot;&gt;Martin Emde (another Rubygems maintainer)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rubygems/rfcs/pull/61&quot;&gt;Martin Emde’s Governance Model proposal&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubycentral.org/news/strengthening-the-stewardship-of-rubygems-and-bundler/&quot;&gt;Letter re: the takeover from RubyCentral&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://thehackernews.com/2025/08/rubygems-pypi-hit-by-malicious-packages.html&quot;&gt;Article about the RubyGems supply-chain attack referenced in the RubyCentral open letter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://bsky.app/profile/rhiannon.io/post/3lz6zcflg2s26&quot;&gt;Rhiannon Payne (former Ruby Central employee) commentary&lt;/a&gt; (previous statements deleted)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://joel.drapper.me/p/rubygems-takeover/&quot;&gt;Joel Drapper writing about Shopify’s influence on RubyCentral&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://apiguy.substack.com/p/a-board-members-perspective-of-the&quot;&gt;Board member Freedom Dumlao’s “(…) perspective of the RubyGems controversy”&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://skillissue.jardo.dev/archive/rubygems-ruby-central-its-complicated/&quot;&gt;Ruby Central: It’s Complicated&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://jaredwhite.com/articles/ruby-central-is-not-operating-in-good-faith&quot;&gt;Jared White’s summary editorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://joel.drapper.me/p/ruby-central-fact-check/&quot;&gt;Fact-checking Ruby Central’s Press Release by Joel Drapper&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.404media.co/how-ruby-went-off-the-rails/&quot;&gt;How Ruby Went Off the Rails (404 Media)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://skillissue.jardo.dev/archive/fragmented/&quot;&gt;Fragmented (Skill Issue)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://andre.arko.net/2025/09/25/bundler-belongs-to-the-ruby-community/&quot;&gt;Andre Arko has registered &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundler&lt;/code&gt; as a trademark&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://joel.drapper.me/p/ruby-central/&quot;&gt;The RubyGems takeover from the perspective of an open-source developer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://rubycentral.org/news/rubygems-org-aws-root-access-event-september-2025/&quot;&gt;Rubygems.org AWS Root Access Event – September 2025&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://andre.arko.net/2025/10/09/the-rubygems-security-incident/&quot;&gt;The RubyGems “security incident”&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;ellen-dash-puppy--duckinators-initial-open-letter&quot;&gt;Ellen Dash (@puppy / @duckinator)’s initial open letter&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;## Ruby Central’s Attack on RubyGems

Hi! I’m Ellen, but you probably know me as duckinator or puppy.

I really wish I didn’t have to write this, but I feel the Ruby community needs to know it.

I have been part of the Ruby community since I was 13, and one of the RubyGems maintainers for the last decade.

This community has helped me through very hard times, and you mean the world to me.

One of the most important lessons I learned from y’all is this:

&amp;gt; A person’s character is determined not only by their actions,

&amp;gt; but also the actions they stay silent while witnessing.

## This Month Has Been A Fuck Of A Year

This is what unfolded between September 9 2025 and September 19 2025, as I understand it.

On September 9th, with no warning or communication, a RubyGems maintainer unilaterally:

renamed the “RubyGems” GitHub enterprise to “Ruby Central”, added non-maintainer Marty Haught of Ruby Central, and removed every other maintainer of the RubyGems project.

He refused to revert these changes, saying he would need permission from Marty to do so.

On September 15th, this maintainer said he restored the previous permissions after talking with Marty. Marty stated the deletion was a “mistake” and “should never have happened”.

The “restoration” kept a notable change: Marty was now an owner of the GitHub enterprise.

The RubyGems team responded by immediately began putting in place an overdue official governance policy, inspired by Homebrew’s.

On September 18th, with no explanation, Marty Haught revoked GitHub organization membership for all admins on the RubyGems, Bundler, and RubyGems.org maintainer teams.

By doing this, he took control for himself and other full-time employees of Ruby Central.

Later that day, after refusing to restore GitHub permissions, Ruby Central further revoked access to the bundler and rubygems-update gems on RubyGems.org

I will not mince words here: This was a hostile takeover.

## My Stance On This

I consider Ruby Central’s behavior a threat to the Ruby community as a whole.

The forceful removal of those who maintained RubyGems and Bundler for over a decade is inherently a hostile action. Ruby Central crossed a line by doing this.

When called out, these changes were mostly reverted. Then, it was done again.

By crossing that line a second time after being called out for it, Ruby Central has made it extremely clear to me that they are not engaging in good faith.

Ruby Central’s behavior has forced my hand. I refuse to watch this without speaking up.

I am resigning from my position at Ruby Central, effective immediately.

To remove any doubt: Ruby Central unilaterally, with no explanation, revoked all access to RubyGems against both my wishes and the wishes of the entire RubyGems team.

Ellen Dash (@duckinator)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</summary>
      <id>https://armahillo.dev/community/2025/09/19/rubygames-rubycentral/</id>
      <updated>2025-09-19T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Adding accessible Dark Mode to your site with prefers-color-scheme and CSS variables</title>
      <link href="https://armahillo.dev/fundamentals/2025/03/28/darkmode/" />
      <summary type="html">&lt;h2 id=&quot;dark-mode-is-about-accessibility-not-aesthetics&quot;&gt;Dark mode is about accessibility, not aesthetics&lt;/h2&gt;

&lt;p&gt;OK, it &lt;em&gt;does&lt;/em&gt; have a sleek look to it, also. But this is an accessibility issue, primarily.&lt;/p&gt;

&lt;p&gt;My eyes are pretty light sensitive. In most of the photos of me as a child, if it was taken outdoors, I was probably squinting. I can see pretty well in the dark, to where I don’t tend to turn lights on unless I need to read something.&lt;/p&gt;

&lt;p&gt;Needless to say, when I see a website that is a sea of white with black text on it, especially if the text is small, this is often my experience:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/post_images/spongebob-eyes.gif&quot; alt=&quot;My eyes. They burn.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Can’t I just turn the brightness of my monitor down? Sure, but that indirectly also reduces contrast, which makes it harder to read. This can be particularly challenging when the font is also small. This isn’t just inconvenient, it has caused physical pain (like when you look directly at a bright light), and has also induced headaches.&lt;/p&gt;

&lt;p&gt;To be absolutely clear, I am &lt;em&gt;not&lt;/em&gt; saying that the whole Internet needs to be made dark-mode. I &lt;em&gt;am&lt;/em&gt; saying that the whole Internet needs to be &lt;em&gt;adaptive&lt;/em&gt; to dark mode.&lt;/p&gt;

&lt;p&gt;Most modern devices, including desktops, are capable of setting a color-scheme preference for light or dark mode.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Users think about dark mode at the operating-system level, not at the individual application level. Once they’ve set their device to dark mode in the system settings, they generally assume that all applications and websites will automatically display in dark mode — that is, they basically expect to see a lot more black than white. They do not expect to have to enable dark mode within individual applications. (via NNG)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thankfully, there is a fairly easy way to implement this, using CSS.&lt;/p&gt;

&lt;h2 id=&quot;prefers-color-scheme&quot;&gt;Prefers Color Scheme&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; media query&lt;/a&gt; is fully supported in all major browsers, presently. It is a CSS media query that works like this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* styles to use if the user is in dark mode */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* styles to use if the user is in light mode */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! You don’t have to add anything to your document templates, no javascript or anything else. This &lt;em&gt;just works&lt;/em&gt;(*).&lt;/p&gt;

&lt;p&gt;Just kidding, there’s a little more to do. Thankfully, this implementation is also still pretty smooth.&lt;/p&gt;

&lt;p&gt;(* ok, so if your particular site does styling in your javascript, for example, or does not use traditional stylesheets for styling, it may be more complicated for you. But this is another reason why you should use conventional methods!)&lt;/p&gt;

&lt;h2 id=&quot;css-variables-for-light-and-dark-modes&quot;&gt;CSS Variables for light and dark modes&lt;/h2&gt;

&lt;p&gt;What we want to do here is kind of like polymorphism, but in CSS. Modern CSS allows us to define &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties&quot;&gt;CSS variables&lt;/a&gt; that we can then reuse. This does not require SASS/SCSS anymore, and is part of the CSS3 standard syntax.&lt;/p&gt;

&lt;p&gt;They look are defined like this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and are used like this:&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have to use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:root&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:root&lt;/code&gt; pseudo-selector&lt;/a&gt; because the variable definitions are technically style properties (custom properties) and need to be contained within a selector. Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:root&lt;/code&gt; makes them globally avaiable in your document.&lt;/p&gt;

&lt;h2 id=&quot;the-labors&quot;&gt;The Labors&lt;/h2&gt;

&lt;p&gt;We’re going to approach this in a few phases. It shouldn’t take “very long”, but honestly this depends a lot on the complexity of your site and how you organized your CSS. I am a long-time SCSS user who uses variable names for most styles, so my CSS is organized very modularly.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Gather all the colors you use&lt;/li&gt;
  &lt;li&gt;Define them as CSS variables&lt;/li&gt;
  &lt;li&gt;Define CSS variables for light mode&lt;/li&gt;
  &lt;li&gt;Replace all colors with your new variables&lt;/li&gt;
  &lt;li&gt;Define a dark-mode variant for those purposes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;h3 id=&quot;part-1-gathering-your-colors&quot;&gt;Part 1: Gathering your colors&lt;/h3&gt;

&lt;p&gt;OK so this &lt;em&gt;can&lt;/em&gt; be a little time-consuming. The first thing you have to do is collect all of the colors you use. You can do this pretty quickly with a RegEx and &lt;a href=&quot;https://github.com/BurntSushi/ripgrep?tab=readme-ov-file#installation&quot;&gt;RipGrep&lt;/a&gt;(*):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rg &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;#[a-fA-F0-9]{3,6}&quot;&lt;/span&gt; _scss/ | &lt;span class=&quot;nb&quot;&gt;uniq

&lt;/span&gt;_scss/base/_utilities.scss:#000
_scss/base/_utilities.scss:#666
_scss/base/_utilities.scss:#999
_scss/base/_utilities.scss:#333
_scss/base/_reset.scss:#ff0
_scss/base/_reset.scss:#000
_scss/base/_variables.scss:#e0e1e6
_scss/base/_variables.scss:#f8f3d2
// ...and so on...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;part-2-define-your-css-color-variables&quot;&gt;Part 2: Define your CSS color variables&lt;/h3&gt;

&lt;p&gt;From this, you can copy and paste it directly into your CSS and trim it down to something that looks more like this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-gray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#666&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-gray-light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-gray-dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#333&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#ff0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-silver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#e0e1e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-cream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f8f3d2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--color-dodger-blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#1E90FF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: if you are using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;darken()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lighten()&lt;/code&gt; (SCSS functions), or if you were defining your values using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rgb()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rgba()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hsl()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hsla()&lt;/code&gt; you will need to search your CSS for those separately. I’ll leave that as an exercise to the reader.&lt;/p&gt;

&lt;p&gt;For right now, the goal is to name the colors. If you need inspiration or are bad with color names, you can check the &lt;a href=&quot;https://en.wikipedia.org/wiki/Web_colors#Extended_colors&quot;&gt;extended colors list&lt;/a&gt; or &lt;a href=&quot;https://htmlcolorcodes.com/color-names/&quot;&gt;color names list&lt;/a&gt;. Or just make up your own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t use “primary-color”, “background”, etc. right now. That’s next. Just name the colors.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;part-3-defining-default-usage-light-mode&quot;&gt;Part 3: Defining default usage (light mode)&lt;/h3&gt;

&lt;p&gt;We’re going to treat light mode as the default case here. The goal here is to define your purposeful uses for these colors.&lt;/p&gt;

&lt;p&gt;Here’s a boilerplate example:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* color definitions, from above */&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;/* Currently, the media query is commented out because we don&apos;t want to enable it quite yet */&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* @media (prefers-color-scheme: light) { */&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-silver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-dodger-blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--link-hover-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* } */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Consider this a living list – you will likely need to add additional purposeful colors here.&lt;/p&gt;

&lt;h3 id=&quot;part-4-replace-your-color-variables-with-your-purposeful-variables&quot;&gt;Part 4: Replace your color variables with your purposeful variables&lt;/h3&gt;

&lt;p&gt;Re-using our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ripgrep&lt;/code&gt; query from earlier:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rg &lt;span class=&quot;s2&quot;&gt;&quot;#[a-fA-F0-9]{3,6}&quot;&lt;/span&gt; _scss/

_scss/base/_utilities.scss
15:    3px  3px 0 &lt;span class=&quot;c&quot;&gt;#000,&lt;/span&gt;
16:   &lt;span class=&quot;nt&quot;&gt;-1px&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1px&lt;/span&gt; 0 &lt;span class=&quot;c&quot;&gt;#000,&lt;/span&gt;
17:    1px &lt;span class=&quot;nt&quot;&gt;-1px&lt;/span&gt; 0 &lt;span class=&quot;c&quot;&gt;#000,&lt;/span&gt;
18:   &lt;span class=&quot;nt&quot;&gt;-1px&lt;/span&gt;  1px 0 &lt;span class=&quot;c&quot;&gt;#000,&lt;/span&gt;
19:    1px  1px 0 &lt;span class=&quot;c&quot;&gt;#000;&lt;/span&gt;
24:    3px  3px 0 &lt;span class=&quot;c&quot;&gt;#666,&lt;/span&gt;
25:    2px 3px 0 &lt;span class=&quot;c&quot;&gt;#999,&lt;/span&gt;
26:    5px 5px 0 &lt;span class=&quot;c&quot;&gt;#333;&lt;/span&gt;

_scss/base/_reset.scss
143:  background: &lt;span class=&quot;c&quot;&gt;#ff0;&lt;/span&gt;
144:  color: &lt;span class=&quot;c&quot;&gt;#000;&lt;/span&gt;
370:  border: 1px solid &lt;span class=&quot;c&quot;&gt;#1E90FF;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This gives you the filenames and line numbers of all of your colors. The job is now this process:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the file and find the line number indicated by the report&lt;/li&gt;
  &lt;li&gt;Find the corresponding CSS variable from the “purposeful” list you made in Part 3.&lt;/li&gt;
  &lt;li&gt;If there is no good variable already defined, then consider the usage define a new one&lt;/li&gt;
  &lt;li&gt;Repeat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, you’ll have something like this:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;and change it to:&lt;/p&gt;
&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When you’re done, you should not see explicit color codes in the scan of your CSS. It should look something like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;rg &lt;span class=&quot;s2&quot;&gt;&quot;#[a-fA-F0-9]{3,6}&quot;&lt;/span&gt; _scss/
_scss/base/_variables.scss
19:    &lt;span class=&quot;nt&quot;&gt;--border-color&lt;/span&gt;:      &lt;span class=&quot;c&quot;&gt;#e0e1e6;&lt;/span&gt;
20:    &lt;span class=&quot;nt&quot;&gt;--smoke-color&lt;/span&gt;:       &lt;span class=&quot;c&quot;&gt;#fafafa;&lt;/span&gt;
26:    &lt;span class=&quot;nt&quot;&gt;--bg-color&lt;/span&gt;:          &lt;span class=&quot;c&quot;&gt;#333;&lt;/span&gt;
29:    &lt;span class=&quot;nt&quot;&gt;--border-color&lt;/span&gt;:      &lt;span class=&quot;c&quot;&gt;#e0e1e6;&lt;/span&gt;

&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check your site in your development environment and make sure it looks the same, and that it loads correctly. If you’re using a CSS preprocessor that checks your syntax, it may give you helpful feedback if you have any errors.&lt;/p&gt;

&lt;p&gt;Fix any errors, then continue.&lt;/p&gt;

&lt;h3 id=&quot;part-5-define-your-dark-mode-variant&quot;&gt;Part 5: Define your dark mode variant&lt;/h3&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;/* color definitions, from above */&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;/* OK, let&apos;s enable it! */&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-silver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-dodger-blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--link-hover-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-silver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-yellow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;--link-hover-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-dodger-blue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the thing that does the thing.&lt;/p&gt;

&lt;p&gt;So the way this works for a user that has set their device-level setting to Light Mode, is this:&lt;/p&gt;

&lt;p&gt;Given this HTML:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;body&amp;gt;
  &amp;lt;p&amp;gt;Lorem ipsum dolor....&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The CSS that would style it:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;will consult the definitions for those variables:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;light&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And you would see black text on a white foreground.&lt;/p&gt;

&lt;p&gt;But if the user has their device-level settings set to Dark Mode, then it would instead use the other block:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--text-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;--background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;--color-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;They’ll see white text on a black background instead.&lt;/p&gt;

&lt;p&gt;Success!&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;If you want it to be toggleable, you can add that separately. But the point of this is to transparently adapt the display of your site to align with the preference of the user. The whole point of ubiquitous accessibility is that it fades into the background. You don’t have to make a big fanfare about offering dark mode, and ideally, your users realize, sometime after using the site “oh wait, it looks like they added dark mode!”&lt;/p&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prefers-color-scheme&lt;/code&gt; with CSS variables is a compromise for maintainablility of development with providing support for users that have opted into having lower luminosity.&lt;/p&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;p&gt;See also&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.nngroup.com/articles/dark-mode-users-issues/&quot;&gt;Dark Mode: How Users Think About It and Issues to Avoid&lt;/a&gt;, &lt;em&gt;Nielsen-Norman Group&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary>
      <id>https://armahillo.dev/fundamentals/2025/03/28/darkmode/</id>
      <updated>2025-03-28T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Minesweeper</title>
      <link href="https://armahillo.dev/2024/06/14/minesweeper-meditation-2/" />
      <summary type="html">&lt;h2 id=&quot;the-problem-minesweeper&quot;&gt;The Problem: Minesweeper&lt;/h2&gt;

&lt;p&gt;This exercise, one of the ones designated “Medium”, is called “&lt;a href=&quot;https://exercism.org/tracks/ruby/exercises/minesweeper&quot;&gt;Minesweeper&lt;/a&gt;”. It’s an exercise to populate a minesweeper grid with the number values for the eponymous computer game.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Your task is to add the mine counts to empty squares in a completed Minesweeper board. The board itself is a rectangle composed of squares that are either empty (‘ ‘) or a mine (‘*’). For each empty square, count the number of mines adjacent to it (horizontally, vertically, diagonally). If the empty square has no adjacent mines, leave it empty. Otherwise replace it with the adjacent mines count.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href=&quot;https://exercism.org/tracks/ruby/exercises/minesweeper/solutions/armahillo&quot;&gt;https://exercism.org/tracks/ruby/exercises/minesweeper/solutions/armahillo&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h3 id=&quot;pass-1-scaffolding&quot;&gt;Pass 1: Scaffolding&lt;/h3&gt;

&lt;p&gt;I usually like to start with creating a separate class, to keep the “execution flow” separate from the data modeling. From some preliminary test runs, I knew that the inputs would get passed in as a string and get passed out as a string. But I also knew that in order to leverage a lot of the Ruby enumerable methods, I would need to convert to Arrays. Since we care about setting a number, we’re going to initialize any blank space to a 0, and then reverse that if it’s still 0.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Minefield&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:grid&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@grid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; &apos;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Minesweeper&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this is the default class the tests expect&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;annotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Minefield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;


    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Due to some junk inputs, there was a little bit of robustness that had to be added to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;initialize&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to_s&lt;/code&gt; methods:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# In
@grid = input.map! { |i| i == &apos;&apos; ? &apos;&apos; : i.chars.map! { |c| c == &apos; &apos; ? 0 : c  } }

# Out
@grid.map { |i| i.is_a?(Array) ? i.map(&amp;amp;:to_s).join.gsub(&apos;0&apos;, &apos; &apos;) : i || &apos;&apos; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once I had this, it gave me an abstraction I could work with, and also get back out of.&lt;/p&gt;

&lt;h3 id=&quot;pass-2-finding-neighbors&quot;&gt;Pass 2: Finding neighbors&lt;/h3&gt;

&lt;p&gt;One of the early starts I had involved trying to find each empty square and then calculating how many mines were around it, but for most boards that would be more iterations since there are generally more empty spaces than mines.&lt;/p&gt;

&lt;p&gt;Instead, what I decided to do was find each mine and then consider all of its neighbors. A “neighbor” is any adjacent square (orthogonal, diagonal), like this (the 💣 is the mine):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;🔘🔘🔘
🔘💣🔘
🔘🔘🔘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The cool thing here is that any neighbor is adjacent, meaning it’s only 1 unit away on either row or column, so we can define any given neighbor set as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[row-1, col-1], [row-1, col], [row-1, col+1]
[row,   col-1],      💣       [row,   col+1]
[row+1, col-1], [row+1, col], [row+1, col+1]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we iterate through the field as a 2D array (rows, columns), we then can consider each individual cell (a ‘*’ is a mine, here):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Minesweeper&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this is the default class the tests expect&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;annotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Minefield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col_index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update_neighbors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;*&apos;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But what about when we’re looking at cells on the edge? Well, we know that we don’t want any negative values for either. We can also ensure that we’re not using values that are equal or greater than the number of rows / cols.  I called the method that determines this “viable_neighbors”.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;viable_neighbors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;               &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;any?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:negative?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# @grid.size is the number of rows&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# @grid.first.size is the number of columns&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This then returns an array of tuples that we can increment:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update_neighbors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;viable_neighbors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]][&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;neighbor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;pass-3-implementing-enumerable&quot;&gt;Pass 3: Implementing Enumerable&lt;/h3&gt;

&lt;p&gt;This is perhaps some syntactic sugar, but I really wanted to have a simpler API where I can just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:each&lt;/code&gt; instead of a nested loop. So I included &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt;, and then added this method to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Minefield&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@grid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cells&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cells&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_index&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This then let me change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.annotate&lt;/code&gt; method to:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;annotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Minefield&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;vi&quot;&gt;@field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;update_neighbors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cell&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;*&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;vi&quot;&gt;@field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Because of a bad-input gotcha, had to add this inside the outer loop of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:each&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@grid.each.with_index do |cells, row|
  next unless cells.is_a?(Array)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, the tests passed.&lt;/p&gt;

&lt;h3 id=&quot;pass-4-polish&quot;&gt;Pass 4: Polish&lt;/h3&gt;

&lt;p&gt;At this point I made two final changes:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Minefield
  MINE_CHAR = &apos;*&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The “mine” character is defined as a constant. Also:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;neighbor.any?(&amp;amp;:negative?) || !@grid.dig(*neighbor).is_a?(Integer)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; gives the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:dig&lt;/code&gt; method, which lets you specify a series of args and it will attempt to safely find its way into that element. All we care about is whether or not the field is an integer already. If the attempted cell is out of bounds, we just ignore it.&lt;/p&gt;

&lt;p&gt;A gotcha here is that we have to keep the “negative” check because technically you &lt;em&gt;can&lt;/em&gt; access negative indices in an array, it just wraps around in reverse; but not in the other direction.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;irb(main):001&amp;gt; array = [1,2,3]
=&amp;gt; [1, 2, 3]
irb(main):002&amp;gt; array[2]
=&amp;gt; 3
irb(main):003&amp;gt; array[-1]
=&amp;gt; 3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tests all passed. I added a couple comments to clarify, and submitted!&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2024/06/14/minesweeper-meditation-2/</id>
      <updated>2024-06-14T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Integration with Patreon subscription tier via OAuth</title>
      <link href="https://armahillo.dev/2023/07/31/using-oauth-with-patreon-subscriptions/" />
      <summary type="html">&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;
&lt;p&gt;I have a side-gig doing some tech stuff for a &lt;a href=&quot;https://stillfleet.com&quot;&gt;TTRPG&lt;/a&gt;. This TTRPG is backed partly by Patreon subscriptions. Patreon is a service that allows content creators to receive regular monthly “subscriptions” from their fans. It provides an API (GraphQL) that allows a consumer to receive information about the platform as well as functioning as an OAuth provider.&lt;/p&gt;

&lt;p&gt;We intended to allow users to sign-in via Patreon, use the membership information of their Patreon account to gatekeep access to certain features (persisting data to the database, early access to content and new features, etc.)&lt;/p&gt;

&lt;p&gt;This article details the specific platform-agnostic process for how to retrieve a dataset that will allow you to verify membership for a patreon tier, as well as discussion about how the data nodes fit together.&lt;/p&gt;

&lt;p&gt;The last section provides some sample Ruby classes that I wrote that demonstrate one approach to implementing a response handler for this API response.&lt;/p&gt;

&lt;h1 id=&quot;part-i-the-data-nodes&quot;&gt;Part I: the data nodes&lt;/h1&gt;

&lt;h2 id=&quot;definitions&quot;&gt;Definitions&lt;/h2&gt;

&lt;h3 id=&quot;member&quot;&gt;member&lt;/h3&gt;
&lt;p&gt;A “member” is a Patreon user. In this context, it is a Patreon user who has decided to subscribe, for any amount of money including nothing at all, to a content creator.&lt;/p&gt;

&lt;h3 id=&quot;campaign&quot;&gt;campaign&lt;/h3&gt;
&lt;p&gt;A “campaign” is the other end of the membership: it equates to the content creator. I don’t know if it’s possible for a content creator to have multiple campaigns or not, but for this article it doesn’t really matter.&lt;/p&gt;

&lt;h3 id=&quot;tier&quot;&gt;tier&lt;/h3&gt;
&lt;p&gt;A “tier” is the level of the membership. It has general traits (a name, a subscription fee, etc). It does not represent the individual user memberships, though.&lt;/p&gt;

&lt;h3 id=&quot;membership&quot;&gt;membership&lt;/h3&gt;
&lt;p&gt;This is the record that connect “member” to “tier”. This has &lt;em&gt;almost&lt;/em&gt; all of the data from the tier (and more) but does not have descriptive data like the tier name.&lt;/p&gt;

&lt;h2 id=&quot;raw-response-data&quot;&gt;Raw response data&lt;/h2&gt;

&lt;p&gt;This has been sanitized and also truncated to only show the relevant data nodes we care about when parsing user identity data. Note that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;included&lt;/code&gt; node has three objects under it: “member”, “campaign”, and “tier”. Locations where there would be additional chaff have been noted with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ ... }&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;vi&quot;&gt;@data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;email&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;alice@babbage.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;first_name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;is_email_verified&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;last_name&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Babbage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;vanity&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;alice_babbage&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
    &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1234567&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;s2&quot;&gt;&quot;relationships&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;memberships&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ab1c23de-f45a-6b78-90c1-2d3ef4567890&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
            &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;member&quot;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
    &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;user&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
  &lt;span class=&quot;s2&quot;&gt;&quot;included&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;pledge_url&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/join/stillfleet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;summary&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;&amp;lt; html description &amp;gt;&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.patreon.com/stillfleet&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;vanity&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;stillfleet&quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1234567&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;campaign&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet &quot;member&quot; node&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;campaign_lifetime_support_cents&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;69420&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;currently_entitled_amount_cents&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;is_follower&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;last_charge_date&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2023-07-01T12:00:00.000+00:00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;last_charge_status&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Paid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;lifetime_support_cents&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;69420&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;next_charge_date&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2023-08-01T12:00:00.000+00:00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;patron_status&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active_patron&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;pledge_cadence&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;pledge_relationship_start&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2020-01-01T00:00:00.000+00:00&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;will_pay_amount_cents&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;900&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ab1c23de-f45a-6b78-90c1-2d3ef4567890&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;relationships&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;campaign&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1234567&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet campaign ID&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;campaign&quot;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;links&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;related&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://www.patreon.com/api/oauth2/v2/campaigns/1234567&quot;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;currently_entitled_tiers&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3456789&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet Tier ID (Sr. Archivist)&lt;/span&gt;
              &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tier&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;member&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;amount_cents&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;published&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;remaining&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Senior Archivist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;unpublished_at&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/join/stillfleet/checkout?rid=3456789&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;s2&quot;&gt;&quot;user_limit&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;3456789&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tier&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; 
  &lt;span class=&quot;s2&quot;&gt;&quot;links&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.patreon.com/api/oauth2/v2/user/1234567&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# from above&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;part-2-parsing-the-response-data&quot;&gt;Part 2: Parsing the response data&lt;/h1&gt;

&lt;p&gt;This object is a wrapper that does some data preparation after consuming the raw patreon response data. The source is a graphQL query.&lt;/p&gt;

&lt;p&gt;This is the shape of a sample Patreon::Response object that has parsed raw response_data.&lt;/p&gt;

&lt;h2 id=&quot;summary-of-important-values&quot;&gt;Summary of Important values&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Location inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;includes[]&lt;/code&gt;&lt;/th&gt;
      &lt;th&gt;Meaning&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;campaign&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ node[&apos;id&apos;]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The campaign ID for this campaign (1234567)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;member&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ node[&apos;relationships&apos;][&apos;currently_entitled_tiers&apos;][*][&apos;id&apos;]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The Stillfleet campaign ID&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;member&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;attributes&apos;][&apos;patron_status&apos;]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;whether or not they are an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;active_patron&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;tier&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;attributes&apos;][&apos;title&apos;]&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Title of their tier&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;overview-of-response-parsing&quot;&gt;Overview of response parsing&lt;/h2&gt;

&lt;p&gt;Patreon uses a graphQL API. We query against the “identity” endpoint, and have it include the “member”, “campaign” and “tier” data. The main “data” node includes identifying data about the user themselves. The “includes” data node includes all of the Stillfleet data.&lt;/p&gt;

&lt;p&gt;The unique identifier of the user is the membership UUID:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;XXXXXXXX&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# This is a red herring!&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;relationships&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;memberships&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# There will be others that are similar&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ab1c23de-f45a-6b78-90c1-2d3ef4567890&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# UUID&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;member&quot;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;user&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;included&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This UUID is then reused in the “member” node.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;N.B.&lt;/strong&gt; As there is no identifying information within this node to indicate which campaign is associated with this, you have to look to the included data first. A single user response will very likely have many nodes under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;included&quot;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;campaign&quot;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;tier&quot;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;member&quot;&lt;/code&gt; nodes among them, from various campaigns the user may be a part of.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To find which UUID we need, we have to work backwards.&lt;/p&gt;

&lt;p&gt;Starting with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;campaign&quot;&lt;/code&gt; nodes:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;included&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet &quot;campaign&quot; node&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;vanity&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;stillfleet&quot;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1234567&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;campaign&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There is one &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;included[]&lt;/code&gt; where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; has: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;type&apos;] == &quot;campaign&quot;&lt;/code&gt;, and also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;attributes&apos;][&apos;vanity&apos;] == &quot;stillfleet&quot;&lt;/code&gt;. When we find this node, we extract the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;id&apos;]&lt;/code&gt; and save that away and never forget it.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Stillfleet Campaign ID: &lt;strong&gt;1234567&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; is thankfully universal for all memberships to this campaign, specifically.&lt;/p&gt;

&lt;p&gt;With that membership, we can now query for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;member&quot;&lt;/code&gt; node with details of the subscription tier.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;included&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet &quot;member&quot; node&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;patron_status&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active_patron&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;-- IMPORTANT&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a159f0de-7689-4b8b-ae89-6b59562589e6&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# membership UUID&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;relationships&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;campaign&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1234567&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet campaign ID&lt;/span&gt;
            &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;campaign&quot;&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;links&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;currently_entitled_tiers&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# there should not be other tiers here, but it&apos;s still a collection&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3456789&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Subscription tier ID (Sr. Archivist)&lt;/span&gt;
              &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tier&quot;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;member&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, we are looking in a sea of objects that include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;member&quot;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;tier&quot;&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;campaign&quot;&lt;/code&gt; nodes, and the only way to tell them apart is by looking into them. This time, we want to look through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;includes[]&lt;/code&gt; collection for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; that has: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;type&apos;] == &quot;member&quot;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;relationships&apos;][&apos;campaign&apos;][&apos;data&apos;][&apos;id&apos;] == &quot;1234567&quot;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt;, we can now look through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;relationships&apos;][&apos;currently_entitled_tiers&apos;]&lt;/code&gt; and gather all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currently_entitled_tier[&apos;id&apos;]&lt;/code&gt;s. There &lt;em&gt;should&lt;/em&gt; only be the one, but good to verify. There is a different tier ID for each subscription level, so this is less globally useful, and should any tiers be changed we would need to know that beforehand, so we don’t store this permanently, only transiently.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Stillfleet subscription tier ID: &lt;strong&gt;3456789&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, while examining this node we should also note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;attributes&apos;][&apos;patron_status&apos;]&lt;/code&gt; value – it should be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;active_patron&quot;&lt;/code&gt; if they are current.&lt;/p&gt;

&lt;p&gt;Now we can gather the actual tier level by looking at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;type&quot; =&amp;gt; &quot;tier&quot;&lt;/code&gt; nodes:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;data&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
   &lt;span class=&quot;s2&quot;&gt;&quot;included&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Stillfleet &quot;tier&quot; node&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;attributes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;amount_cents&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;900&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
        &lt;span class=&quot;s2&quot;&gt;&quot;title&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Senior Archivist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3456789&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tier&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time we are looking for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; that has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;type&quot; =&amp;gt; &quot;tier&quot;&lt;/code&gt; and also has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;id&apos;] == &quot;3456789&quot;&lt;/code&gt; (the subscription tier ID from earlier). From this we can extract both the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;attributes&apos;][&apos;amount_cents&apos;]&lt;/code&gt; (if that matters to us) and also the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node[&apos;attributes&apos;][&apos;title&apos;]&lt;/code&gt; which gives us the diegetic name of the tier.&lt;/p&gt;

&lt;p&gt;This this data collected, we can now assemble this into a meaningful determination of whether or not the user is (a) a subscribing patron, and (b) actively supporting.&lt;/p&gt;

&lt;h1 id=&quot;part-3-storing--using-the-data&quot;&gt;Part 3: Storing &amp;amp; using the data&lt;/h1&gt;

&lt;p&gt;The last phase of this is to take the data we are&lt;/p&gt;

&lt;h2 id=&quot;persisting-in-the-patreonresponse-parsed-object&quot;&gt;Persisting in the Patreon::Response parsed object&lt;/h2&gt;

&lt;p&gt;I created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Response&lt;/code&gt; class under a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Patreon&lt;/code&gt; module to persist the data for future requests.&lt;/p&gt;

&lt;p&gt;These are the fields I stored:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@status= :active_patron,

@user_data    = {
  &quot;patreon_id&quot;        =&amp;gt;  &quot;1234567&quot;,
  &quot;email&quot;             =&amp;gt;  &quot;alice@babbage.com&quot;, # Not all users have emails provided!
  &quot;first_name&quot;        =&amp;gt;  &quot;Alice&quot;, 
  &quot;is_email_verified&quot; =&amp;gt;  true, 
  &quot;last_name&quot;         =&amp;gt;  &quot;Babbage&quot;, 
  &quot;vanity&quot;            =&amp;gt;  &quot;alice_babbage&quot;
},

@campaign_id  = &quot;1234567&quot;,

@pledge_data  = {
  &quot;campaign_lifetime_support_cents&quot; =&amp;gt;  10000, 
  &quot;currently_entitled_amount_cents&quot; =&amp;gt;  900, 
  &quot;is_follower&quot;                     =&amp;gt;  false, 
  &quot;last_charge_date&quot;                =&amp;gt;  &quot;2023-07-01T12:00:00.000+00:00&quot;, 
  &quot;last_charge_status&quot;              =&amp;gt;  &quot;Paid&quot;, 
  &quot;lifetime_support_cents&quot;          =&amp;gt;  69420, 
  &quot;next_charge_date&quot;                =&amp;gt;  &quot;2023-08-01T12:00:00.000+00:00&quot;, 
  &quot;patron_status&quot;                   =&amp;gt;  &quot;active_patron&quot;, 
  &quot;pledge_cadence&quot;                  =&amp;gt;  1, 
  &quot;pledge_relationship_start&quot;       =&amp;gt;  &quot;2020-01-01T12:00:00.000+00:00&quot;, 
  &quot;will_pay_amount_cents&quot;           =&amp;gt;  900
},

@subscription_tier = {
  &quot;amount_cents&quot;      =&amp;gt;  900, 
  &quot;published&quot;         =&amp;gt;  true, 
  &quot;remaining&quot;         =&amp;gt;  nil, 
  &quot;title&quot;             =&amp;gt;  &quot;Senior Archivist&quot;, 
  &quot;unpublished_at&quot;    =&amp;gt;  nil, 
  &quot;url&quot;               =&amp;gt;  &quot;/join/stillfleet/checkout?rid=3456789&quot;, 
  &quot;user_limit&quot;        =&amp;gt;  nil
}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sample-patreonresponse-class-definition&quot;&gt;Sample &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Patreon::Response&lt;/code&gt; class definition&lt;/h2&gt;

&lt;p&gt;Here is the class used that provides the fields above. It’s written in Ruby and is mostly lifted verbatim from what we’re using. There 
are some small modifications made for readability.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Patreon&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# These custom exceptions are for slightly better granular&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# feedback on failures. There are many different ways to run&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# into issues during this processing phase, and some of them&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# are handled differently, or with different error messages.&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Patreon::NotAValidResponseError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Patreon::NotAPatronError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Patreon::NotAnActivePatronError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Patreon::DataParsingError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Response&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:campaign_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:pledge_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:subscription_tier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# @param data - a raw Patreon API response object&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:unknown&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@pledge_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@subscription_tier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# This will raise a JSON::ParserError if it is not JSON data&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# The strategy here is to bail out of execution if we encounter&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# any showstopping errors. Some of these exceptions are handled&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# upstream.&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAValidResponseErrorunless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;has_user_data?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;find_user&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAPatronError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;has_historical_subscription_data?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;find_pledge_data&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAnActivePatronError&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;has_current_subscription_data?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;find_subscription_tier&lt;/span&gt;
      
      &lt;span class=&quot;c1&quot;&gt;# This status is set implicitly if we have not encountered any&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# showstopping errors so far.&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:active_patron&lt;/span&gt;  
    
    &lt;span class=&quot;c1&quot;&gt;# These statuses cover those other circumstances. In the real&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# code in prod, there is some additional debug dumping happening&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# within these blocks as well, but the only required part is &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# that the statuses are set correctly.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAPatronError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:not_a_patron&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAnActivePatronError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:not_an_active_patron&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Technically, this is a known value but we&apos;re memoizing it &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# dynamically as an early smoke test.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# the approach of &quot;rescue StandardError, re-raise as a more&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# specific error&quot; is a strategy used throughout this class.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;campaign_id&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@campaign_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;campaign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAPatronError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;has_user_data?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;data&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# In these two has_*_data? methods, we&apos;re using `rescue false`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# because both helper methods that are called previously _may_&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# raise exceptions, depending on their behavior or the status of&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# the response. As this is a predicate, we always want to respond&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# with a true or false value.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;has_historical_subscription_data?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;historical_subscription_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;has_current_subscription_data?&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;current_subscription_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;present?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# The find_* methods all encapsulate the response object&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# traversal. Initially, I considered memoizing these methods&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# instead, but that approach didn&apos;t fit the flow above.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_user&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# This might read awkwardly as two steps, here. The reason&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# it&apos;s broken up is because there was a step between them&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# that I did for debugging reasons that isn&apos;t germane to this&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# article.&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;patreon_id&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;data&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;data&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_pledge_data&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@pledge_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;membership&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Because of how ruby behaves, the &quot;current&quot; data will be &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# applied on-top-of (ie. it will overwrite) any colliding&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# keys.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_subscription_tier&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@subscription_tier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;historical_subscription_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_subscription_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;historical_subscription_data&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;included&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;member&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;relationships&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;campaign&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;data&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;campaign_id&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;current_subscription_data&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;included&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;tier&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subscription_tier_id&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;subscription_tier_id&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@subscription_tier_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;membership&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                                  &lt;span class=&quot;s1&quot;&gt;&apos;relationships&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;s1&quot;&gt;&apos;currently_entitled_tiers&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                  &lt;span class=&quot;s1&quot;&gt;&apos;data&apos;&lt;/span&gt;
                                &lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# This method is memoized.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;membership&lt;/span&gt; 
      &lt;span class=&quot;vi&quot;&gt;@membership&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;included&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;member&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;relationships&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;campaign&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;data&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;id&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;campaign_id&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;campaign&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@campaign&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;included&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;type&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;campaign&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;attributes&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;vanity&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;stillfleet&apos;&lt;/span&gt; 
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;in-oauth-response-controller-action&quot;&gt;In OAuth response controller action&lt;/h2&gt;

&lt;p&gt;I’ll have to write a separate post about using Patreon for OAuth specifically, but here’s the important steps of the controller action that Patreon OAuth webhook hits when the user signs-in via Patreon:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OAuthController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;patreon_redirect&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ... do the OAuth request via an API client&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# these first two steps are included only for context, but &lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# use bespoke code. :fetch_identity returns the JSON response.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;api_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PatreonClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch_identity&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# this the class from above.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Do stuff with the response object -- instantiate objects, etc.&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# IRL, this is a little more complicated. Not every user puts&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# their name in the first/last name. So I actually have a method&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# that checks name, userID, email, etc and uses the best one it&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# finds.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;root_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;notice: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Welcome, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;first_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;!&quot;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DataParsingError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAPatronError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;root_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;ss&quot;&gt;notice: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Welcome, guest! Please consider subscribing to our Patreon!&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Patreon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAnActivePatronError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;root_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;ss&quot;&gt;notice: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Welcome back, friend! Please consider subscribing to our Patreon again!&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;StandardError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;root_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;notice: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;We had trouble signing-in.&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</summary>
      <id>https://armahillo.dev/2023/07/31/using-oauth-with-patreon-subscriptions/</id>
      <updated>2023-07-31T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Creating a Multimedia Promotional Site</title>
      <link href="https://armahillo.dev/2023/04/08/multimedia-promotional-site/" />
      <summary type="html">&lt;h1 id=&quot;overview--tldr&quot;&gt;Overview / TLDR&lt;/h1&gt;
&lt;p&gt;I have a hobby playing records at dance events. I needed a promotional site that I could direct people to which would have all my relevant information and media. The requirements were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A page of social media links&lt;/li&gt;
  &lt;li&gt;A page of general info&lt;/li&gt;
  &lt;li&gt;A page of upcoming and past events&lt;/li&gt;
  &lt;li&gt;The ability to host large audio files (50-80MB)&lt;/li&gt;
  &lt;li&gt;The ability to play the audio files on the site, without allowing hotlinking from elsewhere&lt;/li&gt;
  &lt;li&gt;Minimal maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the past, I probably would have used something like Wordpress on a VPS for this. Both VPSes and Wordpress both require regular maintenance and updates to keep them patched to harden them to attackers. A VPS also costs money.&lt;/p&gt;

&lt;p&gt;For this site, the content won’t change very often, and it’s read-only content so it doesn’t need a database or the ability to do request processing. Similar to this site, static files felt like the right path forward. Plus, render.com allows for free hosting of a limited number of static sites.&lt;/p&gt;

&lt;p&gt;The plan was:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create the site using Jekyll&lt;/li&gt;
  &lt;li&gt;Deploy the site to render.com&lt;/li&gt;
  &lt;li&gt;Host the large audio files on AWS S3&lt;/li&gt;
  &lt;li&gt;Use native HTML5 multimedia capabilities to play audio&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;bootstrapping&quot;&gt;Bootstrapping&lt;/h1&gt;

&lt;p&gt;There are literally hundreds of static-site generators. I’m not even sure if Jekyll is being actively maintained, but since it’s only used on the development side and the output is static content, I’m OK using deprecated-yet-familiar code. (If you want something actively maintained, I hear &lt;a href=&quot;https://www.bridgetownrb.com/&quot;&gt;BridgetownRB&lt;/a&gt; is pretty good ;))&lt;/p&gt;

&lt;h2 id=&quot;jekyll&quot;&gt;Jekyll&lt;/h2&gt;

&lt;p&gt;I use the standard jekyll setup process. I brought some configs and boilerplate over from another site to expedite the work. Nothing fancy.&lt;/p&gt;

&lt;p&gt;For layouts, I like to create a bare layout file and then build others off of that. This ensures that my HTML head and general structure templating is universally consistent.&lt;/p&gt;

&lt;p&gt;I considered doing Events as “Posts”, and may later decide to leverage that feature, but for right now I just want static files, so I created stub files &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;about.md&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;events.md&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demos.md&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;links.md&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;initial-redirects&quot;&gt;Initial Redirects&lt;/h2&gt;
&lt;p&gt;For the time being, I don’t have root-level content, so I initially wanted it to always redirect to the Social Links page. In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;http-equiv=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;refresh&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0;URL=&apos;events.html&apos;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;background-color: black&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;//events.html&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;For now, you&apos;ll be redirected here.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Very simple, classic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;meta-refresh&lt;/code&gt; HTTP redirect. Using this method let’s us avoid worrying about any kind of server-side request processing (where we typically do redirects now). The black background color reduces the flickering from the “flash of unstyleed content” (FOUC).&lt;/p&gt;

&lt;h2 id=&quot;event-widgets&quot;&gt;Event widgets&lt;/h2&gt;

&lt;p&gt;A month or so after those initial redirects, I added a “past events” section that shows a table of past bookings (with flyers, if possible). I also created a partial for both “upcoming” and “past” events that is rendered on the homepage and the “past events” page.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;figure&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;event&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  {% if include.url %}
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ include.url }}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ include.title }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  {% endif %}
    {% if include.img %}
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ include.img }}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;event thumbnail&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    {% else %}
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/assets/img/save-the-date.png&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;event thumbnail&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    {% endif %}
  {% if include.url %}
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  {% endif %}

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;figcaption&amp;gt;&lt;/span&gt;    
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;event-link&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ include.url }}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;More details for {{ include.title }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ include.title }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{{ include.text }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;time&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;datetime=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ include.datetime }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;{{ include.time }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/time&amp;gt;&lt;/span&gt;
    
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/figcaption&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/figure&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then it’s used with:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{% include upcoming_event.html 
url=&quot;https://url.to/the/event&quot;
img=&quot;/assets/img/events/YYYY-MM-DD-title.jpg&quot;
title=&quot;The event title&quot;
text=&quot;subtitle / location&quot;
datetime=&quot;2023-01-01T12:00:00-04:00&quot;
time=&quot;1 January 2023; 12p - 1pET&quot; %}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;design&quot;&gt;Design&lt;/h1&gt;

&lt;p&gt;I had previously designed signage and chosen a color scheme (sort of a synthwave / vaporwave vibe).&lt;/p&gt;

&lt;p&gt;Since the sound aesthetic is Retro (we vinyl DJs aren’t very common anymore!), I wanted the visual aesthetic to reinforce that brand.&lt;/p&gt;

&lt;h2 id=&quot;stylesheet-basis&quot;&gt;Stylesheet basis&lt;/h2&gt;

&lt;p&gt;I really, really like Tania Rascia’s &lt;a href=&quot;https://taniarascia.github.io/primitive/&quot;&gt;Primitive UI&lt;/a&gt; boilerplate. It’s clean, basic, and easily extended. I’ve used it on several sites (including this one) so I have a good workflow built around it.&lt;/p&gt;

&lt;p&gt;I modified the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_variables.scss&lt;/code&gt; file to integrate my color choices:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nv&quot;&gt;$aquamarine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#8bd4e1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$magenta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#ed5ca1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$amethyst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#73499d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;$background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;/assets/img/background.jpg&apos;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fixed&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;black&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$primary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$aquamarine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$secondary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$amethyst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$accent-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$magenta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$alternate-background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$alternate-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$magenta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$link-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$magenta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$link-hover-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;darken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$link-color&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$highlight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$aquamarine&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;typography&quot;&gt;Typography&lt;/h2&gt;

&lt;p&gt;Google offers many web fonts for free. I found one called “&lt;a href=&quot;https://fonts.google.com/specimen/Permanent+Marker&quot;&gt;Permanent Marker&lt;/a&gt;” that had the look I wanted.&lt;/p&gt;

&lt;p&gt;I used a standard sans-serif font for the body copy.&lt;/p&gt;

&lt;h2 id=&quot;imagery&quot;&gt;Imagery&lt;/h2&gt;

&lt;p&gt;This happened over two iterations, but to reinforce the “retro” aesthetic of the brand, I knew that I wanted to incorporate a record sleeve (for the navigation background) and a cassette tape / case (for the navigation on the demos page).&lt;/p&gt;

&lt;h3 id=&quot;record-sleeve&quot;&gt;Record sleeve&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/post_images/multimedia-site-sleeve.png&quot; alt=&quot;screenshot of record sleeve inlay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I used my scanner and scanned an actual record sleeve liner (White), then cropped and resized it in Photoshop. I placed it inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;header&amp;gt;&lt;/code&gt; tag like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;record-sleeve&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and in the SCSS with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;header&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nn&quot;&gt;#record-sleeve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;medium-breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-3rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;nl&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
      &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

      &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;/assets/img/record-sleeve.png&apos;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transform: rotate(3deg)&lt;/code&gt; gave it a subtle angle that helped it stand more prominently and broke up the layout’s linearity, visually.&lt;/p&gt;

&lt;p&gt;The individual links are written in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$rich-black&lt;/code&gt; color using the “Permanent Marker” font. I applied CSS rotation to each one using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nth-child()&lt;/code&gt; to make them seem more organic / hand-written.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;list-style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-wrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;space-between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;column-gap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;medium-breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Permanent Marker&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cursive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;inline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;   
    
    &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$primary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;medium-breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;odd&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;even&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;-6deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.current&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$primary-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  
          &lt;span class=&quot;nl&quot;&gt;text-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-6px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;m&quot;&gt;6px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-4px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I used flex-box to make them evenly spaced and more responsive. For “odd” items it tilts them slightly one way, and for “even” slightly the other way.&lt;/p&gt;

&lt;p&gt;Given that I was anticipating people to get to the site via a QR code, I needed to ensure it was usable on mobile browsers. I added breakpoints to coerce the navigation to present in a  stacked way at narrower breakpoints. (The record sleeve graphic is also hidden at smaller breakpoints).&lt;/p&gt;

&lt;h3 id=&quot;cassette-case&quot;&gt;Cassette case&lt;/h3&gt;

&lt;p&gt;The vision I had for the demos page was to have the list of individual demos resemble a stack of cassette tapes (back in the 90s and early 00s, we traded promos on cassette tapes). I scanned the spine of a cassette. It had a completely black background and I wanted it to have some transparency so it fit into the page more. In Photoshop, I carved the outline out and applied transparency to the background, then along the ends I applied a subtle gradient of transparency.&lt;/p&gt;

&lt;p&gt;The SCSS for this section was initially:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;  &lt;span class=&quot;nn&quot;&gt;#mixtapes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.tape&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;/assets/img/cassette-case.png&apos;)&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;no-repeat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;588px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;75px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      
      &lt;span class=&quot;nl&quot;&gt;list-style&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;@include&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;large-breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  
        &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;2n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;3n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;-0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.2rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;5n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.5rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nth-child()&lt;/code&gt; lines cause the tapes to have a staggered effect and breaks up the visual linearity of the layout.&lt;/p&gt;

&lt;h3 id=&quot;cassette-svg&quot;&gt;Cassette SVG&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/post_images/multimedia-site-cassette.png&quot; alt=&quot;screenshot of cassette inlay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I had this idea to have a graphic of a cassette tape indicate the demo that was being played. I initially considered using a scan of a cassette, but I had these lofty ideas of some kind of cool animation, and that would be more difficult.&lt;/p&gt;

&lt;p&gt;I found &lt;a href=&quot;https://codepen.io/deryatanriverdi88/pen/yLNZJdd&quot;&gt;a CodePen demo of a cassette-SVG used for an audio player&lt;/a&gt; and lifted the SVG code from it. It seemed a bit verbose, and I wanted to tweak it slightly, so I imported it into Illustrator.&lt;/p&gt;

&lt;p&gt;In Illustrator, I changed some of theh paths to add transparency so there was a transparent window in the center (to see the tape reels), then I placed two dark-brown circles on the bottom layer to be the tape reels, adjusted the overall size, and cropped it tight. After exporting, the resulting SVG was much more brief and cleaned up.&lt;/p&gt;

&lt;p&gt;In the SVG code itself, I added a few classes to some of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;path&amp;gt;&lt;/code&gt; elements so that I can manipulate them with CSS. Initially I thought I could do it as a separate SVG file, but I had to inline it.&lt;/p&gt;

&lt;p&gt;Two key parts here were the reels:&lt;/p&gt;

&lt;p&gt;Note the “r” attribute, which is the “radius” of the circle. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; was attribute was added as a hook for Javascript to attach to later.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;circle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cx=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;17.03&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;18.03&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;r=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill: #513000;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;left-reel&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;circle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cx=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;42&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;18.03&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;r=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill: #513000;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;right-reel&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and the gears:&lt;/p&gt;

&lt;p&gt;Here it was just the classes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;left-gear&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;right-gear&lt;/code&gt;, which are used as hooks for the CSS.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-xml&quot; data-lang=&quot;xml&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;g&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;left-gear&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M15,15l4,6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stroke: #30a996; stroke-width: .4px;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M19,15l-4,6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stroke: #30a996; stroke-width: .4px;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M13,18h8&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stroke: #30a996; stroke-width: .4px;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;circle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cx=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;17&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;18&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;r=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill: #f6dfda;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;g&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;right-gear&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M44,15l-4,6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stroke: #30a996; stroke-width: .4px;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M40,15l4,6&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stroke: #30a996; stroke-width: .4px;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;path&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;d=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;M38,18h8&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stroke: #30a996; stroke-width: .4px;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;circle&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cx=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;42&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cy=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;18&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;r=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;fill: #f6dfda;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/g&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The SCSS for the gears to animate theme spinning was also lifted from the CodePen, but I modified it slightly. The original code would add the animation if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.spin&lt;/code&gt; class was added to the gears (so hitting “play” would add the classes to the elements).&lt;/p&gt;

&lt;p&gt;I modified it to instead spin by default, but &lt;em&gt;not&lt;/em&gt; animate if the parent SVG has the class “pause” added to it.&lt;/p&gt;

&lt;p&gt;I also added a desaturation effect to the cassette when it was “paused” and set the transition for that to give it a soft fade effect when activated. (The classes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.plastic&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.label&lt;/code&gt; are classes I assigned to a few of the parts of the cassette SVG so that it didn’t look flat universally when applied).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;#svgCassette&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;nc&quot;&gt;.left-gear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.right-gear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;transform-origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;transform-box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill-box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rotate-wheel&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;infinite&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;nt&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nl&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.pause&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;        
        &lt;span class=&quot;nt&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.plastic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;desaturate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;#30a996&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;45%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;important&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;na&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;desaturate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;#e3dcaa&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;45%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;important&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;nc&quot;&gt;.left-gear&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.right-gear&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;animation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;@keyframes&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;rotate-wheel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;-360deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;polaroid-photo&quot;&gt;Polaroid photo&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/post_images/multimedia-site-polaroid.png&quot; alt=&quot;screenshot of faux polaroid inlay&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the /about page, I wanted to have a photo inlay that had a Polaroid appearance. I used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; tag as a wrapper for the image:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;figure&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;polaroid pull-right tilted&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;frame&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/assets/img/highstone-bio.jpg&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;20-something DJ playing records&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;large&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;figcaption&amp;gt;&lt;/span&gt;ca. 2005, Dayton OH&lt;span class=&quot;nt&quot;&gt;&amp;lt;/figcaption&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/figure&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;MDN docs say, about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure&quot;&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; tag&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; HTML element represents self-contained content, potentially with an optional caption, which is specified using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; element. The figure, its caption, and its contents are referenced as a single unit.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Usually a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; is an image, illustration, diagram, code snippet, etc., that is referenced in the main flow of a document, but that can be moved to another part of the document or to an appendix without affecting the main flow.
A caption can be associated with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; element by inserting a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; inside it (as the first or the last child). The first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; element found in the figure is presented as the figure’s caption.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The caption was important to me. I &lt;em&gt;could&lt;/em&gt; have done the image only and applied a wide solid-white border to the edges, but since I wanted to also have a caption I knew I would need to wrap it, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;figure&amp;gt;&lt;/code&gt; tag is the most semantically-appropriate for this use-case.&lt;/p&gt;

&lt;p&gt;The SCSS looks like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;figure&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.polaroid&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;white&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nc&quot;&gt;.frame&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.5rem&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#EEE&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;   
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;figcaption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;.5rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mh&quot;&gt;#EEE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Permanent Marker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$rich-black&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.tilted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rotate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;15deg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.pull-right&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Again, using the “Permanent Marker” font for brand consistency, and tilting it to break up the linearity and add more visual interest.&lt;/p&gt;

&lt;p&gt;It’s right-aligned via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt; (At this point, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt; feels almost archaic, but it’s the right thing to use in this case, for the behavior I wanted).&lt;/p&gt;

&lt;p&gt;The photo is technically a landscape-oriented photo, and significantly larger, but it’s cropped with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overflow: hidden&lt;/code&gt;. It could have also been cropped in Photoshop, pre-emptively.&lt;/p&gt;

&lt;h1 id=&quot;hosted-media&quot;&gt;Hosted Media&lt;/h1&gt;

&lt;p&gt;Long ago, I used to host all my mixes on my website directly. Occasionally they would get hotlinked from elsewhere. While the traffic is nice, if they’re only consuming your audio, you’re basically a CDN and I’m not paying for that level of service.&lt;/p&gt;

&lt;p&gt;What I wanted to do here was self-host the files so that I’m not beholden to another business’s constraints (if an individual artist / label wants to send me a DMCA takedown notice for one of theh tracks on a mix, so be it, but it’s less likely if it’s not on a big website). Also self-hosting is cheaper and avoids having to deal with other people pre-rolling ads.&lt;/p&gt;

&lt;h2 id=&quot;aws-s3-configuration&quot;&gt;AWS S3 configuration&lt;/h2&gt;

&lt;p&gt;AWS S3 storage is cheap and easy, and it allows for some more complicated configurations that prevent hotlinking. So I wrote this bucket policy for the S3 bucket.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Principal&quot;: &quot;*&quot;,
            &quot;Action&quot;: &quot;s3:GetObject&quot;,
            &quot;Resource&quot;: &quot;arn:aws:s3:::djhighstone/*&quot;,
            &quot;Condition&quot;: {
                &quot;StringLike&quot;: {
                    &quot;aws:Referer&quot;: [
                        &quot;http://www.djhighstone.com/*&quot;,
                        &quot;http://djhighstone.com/*&quot;,
                        &quot;https://www.djhighstone.com/*&quot;,
                        &quot;https://djhighstone.com/*&quot;
                    ]
                }
            }
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Superficially, what this does is ensure that any requests to any of my media files originates from my website domain. Obviously this isn’t foolproof and it’s still technically possible to spoof that. If someone wants to go through all that trouble to do it, well that’s just fine.&lt;/p&gt;

&lt;p&gt;One gotcha with S3 is that it defaults to very limited permissions for any uploaded file, so after uploading a file, you have to set it’s permissions to make it publicly accessible.&lt;/p&gt;

&lt;h1 id=&quot;deployment&quot;&gt;Deployment&lt;/h1&gt;

&lt;p&gt;Having built this site with Jekyll, the &lt;a href=&quot;https://render.com/docs/deploy-jekyll&quot;&gt;deployment process with render&lt;/a&gt; is well-documented and straightforward.&lt;/p&gt;

&lt;h1 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h1&gt;

&lt;p&gt;The next thing I want to add to this is the ability to jump forward / backwards to pre-determined timestamps for each mix (so that it works better as a demo), and to add playlists that display while the mix plays.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2023/04/08/multimedia-promotional-site/</id>
      <updated>2023-04-08T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Implementing Modular CSS in Ruby on Rails</title>
      <link href="https://armahillo.dev/2021/06/22/modular_css_in_rails/" />
      <summary type="html">&lt;h1 id=&quot;the-problem&quot;&gt;The Problem&lt;/h1&gt;

&lt;p&gt;CSS cascades. This is a great feature of it! But it can be tricky when you have a single monolithic CSS file and want some styles to apply to certain pages but not to other pages.&lt;/p&gt;

&lt;p&gt;For example, let’s say we had an article summary block on the homepage of the site:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-erb&quot; data-lang=&quot;erb&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@posts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;summary&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;cp&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link_to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Read More&apos;&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and wanted to apply these styles to the heading:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;h2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;brand-primary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;large&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;err&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(I threw this up on a &lt;a href=&quot;https://codepen.io/armahillo/pen/oNEberd?editors=1100&quot;&gt;codepen&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Cool! But maybe on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:posts&lt;/code&gt; resource itself we want to display multiple paragraphs at a larger width, and also use a different background color:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;article&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;50%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;err&quot;&gt;h2&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#654321&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(&lt;a href=&quot;https://codepen.io/armahillo/pen/mdXVBbv?editors=1100&quot;&gt;codepen&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Looks great! But uh-oh….&lt;/p&gt;

&lt;p&gt;With those changes, we now have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:posts&lt;/code&gt; resource styles applying to the homepage styling as well. Context collapse!&lt;/p&gt;

&lt;p&gt;(&lt;a href=&quot;https://codepen.io/armahillo/pen/poagWgE?editors=1100&quot;&gt;codepen&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Of course, this specific situation can be resolved by adding an article class (perhaps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;article class=&quot;summary&quot;&amp;gt;&lt;/code&gt; for the homepage styles and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;article class=&quot;full-text&quot;&amp;gt;&lt;/code&gt; for the full view version). This situation is also oversimplified, to illustrate the context collapse. What happens when you start adding in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!important&lt;/code&gt; styles, or want to do a style override that can’t be solved easily by changing load order or merely applying a class?&lt;/p&gt;

&lt;p&gt;What follows is a solution I developed a few years back and it will:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Allow you full modular control over all your CSS in an intuitive way&lt;/li&gt;
  &lt;li&gt;Be more or less future-proof (when you add a new resource, the SCSS file created for that resource is what you’ll use)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bonus: you can &lt;em&gt;technically&lt;/em&gt; use this to address similar modularization of JS snippets for &lt;em&gt;simple&lt;/em&gt; JS scripts (this is a bit harder to integrate with more complicated JS frameworks).&lt;/p&gt;

&lt;h1 id=&quot;modularity-hooks&quot;&gt;Modularity Hooks&lt;/h1&gt;

&lt;p&gt;To gain modularity in our CSS we need to give our application hooks that are contextual to the controller’s action that we’re viewing.&lt;/p&gt;

&lt;p&gt;We begin with these assumptions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Every HTML page has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element has both a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; and an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute (and we can modify those)&lt;/li&gt;
  &lt;li&gt;The Rails app uses the vanilla resource-oriented approach (see below for clarification)&lt;/li&gt;
  &lt;li&gt;The Rails stack will provide us with some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt; set at load-time, indicating which Controller and Action are being used for this request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reductive TL;DR is: We set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body#id&lt;/code&gt; to the Controller and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body.class&lt;/code&gt; to the Action.&lt;/p&gt;

&lt;p&gt;I think using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; tag makes the most sense, semantically, because CSS (practically) only really applies to the content, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body&lt;/code&gt; is the root of the content in the document. You could, in theory, use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag itself, though I personlly think that makes less semantic sense for this.&lt;/p&gt;

&lt;p&gt;In the code, the trivial / naive approach, that we will harden up a little later, is like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:controller] %&amp;gt;&quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:action] %&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then in your SCSS, you can now create controller- and even action-specific styles:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#users&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;.new&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;new-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.edit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;edit-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And those styles won’t conflict with other controller actions:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;projects&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#projects&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;.new&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;new-project.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.edit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;edit-project.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Boom.&lt;/p&gt;

&lt;p&gt;For very simple Rails applications that only have shallow top-level resources, this is it; you’re done.&lt;/p&gt;

&lt;h2 id=&quot;dealing-with-namespaces&quot;&gt;Dealing with Namespaces&lt;/h2&gt;

&lt;p&gt;Sometimes you want to do namespaces, like an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/admin/*&lt;/code&gt; namespace. Specifically, I’m talking about situations where you have something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app/controllers/admin/*.rb&lt;/code&gt;, the routes have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;namespace :admin do # ...&lt;/code&gt;, and the controllers within that each are like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Admin::SomeController&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;In these cases, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params[:controller]&lt;/code&gt; will give us: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin/users&lt;/code&gt;, which doesn’t evaluate so cleanly in SCSS. So let sanitize that using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:gsub&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:controller].gsub(/[^\w\d]+/,&apos;--&apos;) %&amp;gt;&quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:action] %&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And now in the SCSS, we can do things like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#admin--users&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;.edit&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;orange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;new-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;security-considerations&quot;&gt;Security Considerations&lt;/h2&gt;

&lt;p&gt;Since we’re dumping the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params[:action]&lt;/code&gt; directly into the source, we could pre-emptively apply a similar sanitization to it just to be safe:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:controller].gsub(/[^\w]+/,&apos;--&apos;) %&amp;gt;&quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:action].gsub(/[^\w]+/,&apos;&apos;) %&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Generally, you don’t want to reveal more about your surface than you need to. Exposing the name of the controller resource and the name of the action might feel like a security risk. Anyone interested in attacking your rails application can &lt;strong&gt;probably&lt;/strong&gt; ascertain your controller names by looking at other resources, so the marginal exposure from using these in the HTML source is very likely minimal.&lt;/p&gt;

&lt;p&gt;That said, &lt;em&gt;if&lt;/em&gt; your application’s routes do not directly mirror your controller names (eg. you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/players&lt;/code&gt; pointing to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UsersController&lt;/code&gt;, rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PlayersController&lt;/code&gt;), and &lt;em&gt;if&lt;/em&gt; you are doing that intentionally because you think this obscurity gives you additional security (it very likely does not), then this approach for modular assets likely won’t fit your needs.&lt;/p&gt;

&lt;h2 id=&quot;attribute-collision&quot;&gt;Attribute Collision&lt;/h2&gt;

&lt;p&gt;It’s possible you are already using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body#id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;body.class&lt;/code&gt; for other purposes – this is totally understandable but thankfully there is an easy work-around: Use data-attributes. You can even go this route anyways if you prefer to keep your data presentation more organized and are put off by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class&lt;/code&gt; attributes in this way.&lt;/p&gt;

&lt;p&gt;It might look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt;         &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;whatever_you_need_it_to_be&quot;&lt;/span&gt; 
           &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;this-too&quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;data-resource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:controller].gsub(/[^\w]+/,&apos;--&apos;) %&amp;gt;&quot;&lt;/span&gt; 
     &lt;span class=&quot;na&quot;&gt;data-method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:action].gsub(/[^\w]+/,&apos;&apos;) %&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and then referencing it in your CSS like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;users&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;[data-method=&quot;new&quot;]&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;new-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;edit&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;edit-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This accomplishes the same thing but with a little more specificity at the cost of a little more typing. I actually like this one a lot because the intention is a lot more semantically clear, though I also admit it’s not &lt;em&gt;quite&lt;/em&gt; as elegant as the initial solution.&lt;/p&gt;

&lt;h2 id=&quot;non-resourceful-routing&quot;&gt;Non-Resourceful Routing&lt;/h2&gt;

&lt;p&gt;If your Rails app uses non-resourceful controllers, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserSignupsController&lt;/code&gt;, but those controllers are manipulating a different resource, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt;, you can still use this approach though you’ll need to do a bit more maintenance to keep it up.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#users&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#user-signups&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;.new&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;new-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.edit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;header#banner&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;url(&apos;edit-user.jpg&apos;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
  &lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Personally, I dislike non-resourceful controllers. I would rather have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:signup&lt;/code&gt; action on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UsersController&lt;/code&gt; than a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:new&lt;/code&gt; action on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserSignupsController&lt;/code&gt;, &lt;em&gt;unless&lt;/em&gt; I’m creating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserSignup&lt;/code&gt; model…but in that case, it’s now a resourceful route again.&lt;/p&gt;

&lt;p&gt;This also assumes you’re wanting to present &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserSignups&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Users&lt;/code&gt; the same – but if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserSignups&lt;/code&gt; manipulates both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; and also, say, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Account&lt;/code&gt; models, you could be creating a limited context-collapse. This is part of the reason I dislike non-resourceful routes, though.&lt;/p&gt;

&lt;h1 id=&quot;implementation-summary&quot;&gt;Implementation Summary&lt;/h1&gt;

&lt;h2 id=&quot;step-1-add-this-to-your-main-layout&quot;&gt;Step 1: Add this to your main layout&lt;/h2&gt;

&lt;p&gt;Whichever file begins with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;html&amp;gt;&lt;/code&gt;, and has a big, expectant, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;%= yield %&amp;gt;&lt;/code&gt; somewhere within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; tag, that’s the one you want to modify:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you aren’t currently setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute of the Body tag&lt;/strong&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- eg. app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:controller].gsub(/[^\w]+/,&apos;--&apos;) %&amp;gt;&quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:action].gsub(/[^\w]+/,&apos;&apos;) %&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you have additional classes, append those after the ERB insert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are currently setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute of the Body tag&lt;/strong&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- app/views/layouts/application.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&lt;/span&gt;         &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;...&quot;&lt;/span&gt; 
           &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;...&quot;&lt;/span&gt; 
   &lt;span class=&quot;na&quot;&gt;data-resource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:controller].gsub(/[^\w]+/,&apos;--&apos;) %&amp;gt;&quot;&lt;/span&gt; 
     &lt;span class=&quot;na&quot;&gt;data-method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;lt;%= params[:action].gsub(/[^\w]+/,&apos;&apos;) %&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;step-2-wrap-your-resource-scss-files-with-the-appropriate-context&quot;&gt;Step 2: Wrap your resource SCSS files with the appropriate context&lt;/h2&gt;

&lt;p&gt;For “some_resource”, that has actions: “new”, “edit”, and “register”, you would use these wrappers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you aren’t currently setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute of the Body tag&lt;/strong&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;some_resource&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;#some_resources&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;.new&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.edit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.register&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;If you are currently setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; attribute of the Body tag&lt;/strong&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-css&quot; data-lang=&quot;css&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;assets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;stylesheets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;some_resource&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.scss&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-resource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;some_resources&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;[data-method=&quot;new&quot;]&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;edit&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data-method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&quot;register&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;err&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s it!&lt;/p&gt;

&lt;p&gt;If you already have a bunch of SCSS written and it’s scattered all over the place, this can be a non-trivial migration, but I promise it’s worth it for the organization alone.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2021/06/22/modular_css_in_rails/</id>
      <updated>2021-06-22T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>HTTP Statuses: The Bad Ones</title>
      <link href="https://armahillo.dev/fundamentals/2020/11/24/http_statuses-part-2/" />
      <summary type="html">&lt;p&gt;I recently learned that &lt;a href=&quot;https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/updateDataExtensionIDAsync.htm&quot;&gt;Salesforce’s API&lt;/a&gt; does this for one of its response payloads:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;HTTP/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;requestId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;524059b7-bf60-4fa6-9271-32aaf3479091&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resultMessages&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resultType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Operational&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resultClass&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;resultCode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;CustomObjectNotFound&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Failed to resolve the Custom Object from the provided ObjectReferenceIdentifier [Id: ed9b5f8e-ebb7-e711-8103-005056b37304, Key: ].&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;em&gt;(The key parts to look at are lines 1 (200 OK) and 8 (Not Found).)&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;400s-you-messed-up&quot;&gt;400s (You messed up)&lt;/h2&gt;

&lt;p&gt;In &lt;a href=&quot;/fundamentals/2020/11/02/http_statuses/&quot;&gt;part 1&lt;/a&gt;, we covered the status codes in the 200-300 range; the good ones. This article deals with “The Bad Ones”, the 400-series, also known as “You Messed Up”.&lt;/p&gt;

&lt;p&gt;These statuses are for when your request wasn’t formed correctly or the server is unable to process your request because you didn’t ask in the right way. This one has a lot more utility and can help steer your user towards providing better input.&lt;/p&gt;

&lt;h3 id=&quot;400-bad-request&quot;&gt;400 (Bad Request)&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400&quot;&gt;400&lt;/a&gt; is simple: “Bad Stuff Happened.”&lt;/p&gt;

&lt;p&gt;This means the user submitted a request that the server wasn’t able to correctly parse into an action. I typically see this when the user submits form data to a valid target, but their parameters are in a weird shape. It can also be used when the user is potentially submitting a malicious payload and the server is being polite in response.&lt;/p&gt;

&lt;p&gt;In a similar way to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt; being a universal “All good!” response, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;400&lt;/code&gt; can be a catch-all.&lt;/p&gt;

&lt;h3 id=&quot;401-unauthorized&quot;&gt;401 (Unauthorized)&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401&quot;&gt;401&lt;/a&gt; is one of two status codes around permissions.&lt;/p&gt;

&lt;p&gt;This requires a bit of definition.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Authentication&lt;/strong&gt; is when you identify yourself&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Authorization&lt;/strong&gt; is when your identity is assigned permissions in a system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;401&lt;/code&gt; code is &lt;em&gt;called&lt;/em&gt; “Unauthorized”, but really, it means “Unauthenticated”; the user hasn’t identified themselves and the resource they’re attempting to access requires authorization – but to authorize, you need an identify authenticated first.&lt;/p&gt;

&lt;p&gt;For the user, this is remedied by logging in.&lt;/p&gt;

&lt;h3 id=&quot;403-forbidden&quot;&gt;403 (Forbidden)&lt;/h3&gt;

&lt;p&gt;By contrast, a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403&quot;&gt;403&lt;/a&gt; is “Forbidden”, but this one is actually “Unauthorized.” The user has already weighed, measured, and found wanting.&lt;/p&gt;

&lt;p&gt;For the user, this is… well, they can’t really do anything about it. Ask nicely, maybe?&lt;/p&gt;

&lt;h3 id=&quot;404-not-found&quot;&gt;404 (Not Found)&lt;/h3&gt;

&lt;p&gt;Here’s the one everyone knows and loves, the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404&quot;&gt;404&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Your request was formed correctly, but the resource you’re looking for isn’t available. The important thing for this one is that the server doesn’t know whether or not the resource is intentionally missing, just that it’s missing.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;404&lt;/code&gt; status is a little different from the next one (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;410&lt;/code&gt;). Think of 404 as like if you called a mall’s customer service line and asked if your friend was there, giving customer service your friend’s name. They can tell you that &lt;em&gt;can’t find&lt;/em&gt; your friend, but they wouldn’t really have any way of knowing if your friend was there and was now “Gone”, or if they hadn’t ever been there at all.&lt;/p&gt;

&lt;h3 id=&quot;405-method-not-allowed&quot;&gt;405 (Method Not Allowed)&lt;/h3&gt;

&lt;p&gt;This is a slick one that you can use if you are doing &lt;a href=&quot;https://restfulapi.net/resource-naming/&quot;&gt;resourceful routing&lt;/a&gt;. If you aren’t familiar with REST, then this error message might be a little confusing.&lt;/p&gt;

&lt;p&gt;What the error message means is that a user has tried to use an HTTP action verb (GET, PUT, PATCH, POST, or DELETE) on a URL that doesn’t support that action. In a RESTful resource, a url that looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/articles/&lt;/code&gt; can support different kinds of requests – if you send &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /articles/ {...data...}&lt;/code&gt; that usually maps to creating a new resource. Sending &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET /articles/&lt;/code&gt; would probably retrieve a collection of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Article&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;In that example, if the server was only prepared to handle &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET /articles/&lt;/code&gt; and not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /articles/ {...data...}&lt;/code&gt; then sending the latter would warrant a response of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;405&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that if you’re going to send a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;405&lt;/code&gt; you’re required to also send an &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow&quot;&gt;Allow&lt;/a&gt; header that lists the actions that are allowed for that resource. eg. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Allow: GET&lt;/code&gt;, in that example.&lt;/p&gt;

&lt;h3 id=&quot;410-gone&quot;&gt;410 (Gone)&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/410&quot;&gt;410&lt;/a&gt;, by comparison, is when the server knows that something is gone. To extend the analogy above, it’s when you call the business where your friend works and they know that your friend’s “Gone” for the day.&lt;/p&gt;

&lt;p&gt;Practically speaking, this is the response you want to send when you “take an article down” and want to stop getting inbound traffic from search engines. When the search engine spiders crawl your site, if they go to a page they thought existed, but you send back a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;410&lt;/code&gt;, that informs the search engine to delete that from their index. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;404&lt;/code&gt; response might penalize the link as being flaky, but may not delete it outright.&lt;/p&gt;

&lt;h3 id=&quot;418-i-am-a-teapot&quot;&gt;418 (I am a teapot)&lt;/h3&gt;

&lt;p&gt;Including this one here mainly for trivia, as it was an April Fool’s Joke, though it’s also an official header with an RFC (&lt;a href=&quot;https://tools.ietf.org/html/rfc2324#section-2.3.2&quot;&gt;RFC 2324&lt;/a&gt;) and everything! This is the response you send back when your IoT coffee machine can’t brew coffee because it is, in fact, a Teapot.&lt;/p&gt;

&lt;h3 id=&quot;420-enhance-your-calm&quot;&gt;420 (Enhance Your Calm)&lt;/h3&gt;

&lt;p&gt;Yes, the Cannabis reference in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/420&quot;&gt;420&lt;/a&gt; is intentional. This is an unofficial response (as far as I know, there is no RFC documenting its official use) and I don’t think Twitter even uses it anymore. The contextual meaning, though, was as a response for when the client is making too many requests too rapidly, and so the server was throttling them.&lt;/p&gt;

&lt;p&gt;Officially, you should use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;429&lt;/code&gt; (see below).&lt;/p&gt;

&lt;h3 id=&quot;422-unprocessable-entity&quot;&gt;422 (Unprocessable Entity)&lt;/h3&gt;

&lt;p&gt;For those of you building APIs, you’ll want to know about &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422&quot;&gt;422&lt;/a&gt;. We usually use these in API responses, and it kind of sits at the intersection of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;400&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;500&lt;/code&gt;. It’s like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;400&lt;/code&gt; because the request can’t be processed, but the request isn’t malformed. It’s like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;500&lt;/code&gt; because the response is ultimately useless to the client, except the server isn’t encountering an error. It means “I don’t know what to do with what you gave me, so …. here.”&lt;/p&gt;

&lt;p&gt;One key difference here between a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;422&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;500&lt;/code&gt; is that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;422&lt;/code&gt; has not bailed and is still at the helm of the execution. So you have access to your in-memory objects like variables and other retrieved data. The typical response is just an empty hash with this status code.&lt;/p&gt;

&lt;h3 id=&quot;429-too-many-requests&quot;&gt;429 (Too Many Requests)&lt;/h3&gt;

&lt;p&gt;This is the &lt;em&gt;official&lt;/em&gt; way to respond when throttling. A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429&quot;&gt;429&lt;/a&gt; response indicates to a client that it is requesting too much. You can also send back a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After&quot;&gt;Retry-After&lt;/a&gt; header (with a parameter that is the number of seconds to wait) to tell the client how long to wait.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/fundamentals/2020/11/24/http_statuses-part-2/</id>
      <updated>2020-11-24T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>HTTP Statuses: The Good Ones</title>
      <link href="https://armahillo.dev/fundamentals/2020/11/02/http_statuses/" />
      <summary type="html">&lt;p&gt;Most people are familiar with what “404” means, at this point. Web browsers used to explicitly say “404 Not Found” when you’d type in a web address that was no good.&lt;/p&gt;

&lt;p&gt;Modern design tends towards omitting the status code, but it’s been made into memes and I think has a fair amount of cultural diffusion.&lt;/p&gt;

&lt;p&gt;HTTP Statuses are a portion of the data that’s sent back from the web server as part of the response. You request a document with your web browser, and it kicks back a response. They’re all 3-digit codes, and each code corresponds with a different kind of response. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt; means “success”, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;404&lt;/code&gt; means “not found”, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;500&lt;/code&gt; means “whoops!”&lt;/p&gt;

&lt;p&gt;Using the correct status is important because you get some behaviors for free when you use it. Using the correct redirect or “not found” status code will cause search engines to update their indexes. Web browsers implicitly understand certain certain status responses, as well.&lt;/p&gt;

&lt;p&gt;Proper HTTP statuses are also just the “right thing to do.” It’s part of the HTTP &lt;a href=&quot;https://tools.ietf.org/html/rfc7231&quot;&gt;RFC 7231&lt;/a&gt;. The web works because we follow the same standards, so the robots know how to talk with one another. It’s important that we keep sticking with these!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is not an exhaustive list&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I’m curating this list based on my experience as a backend engineer, sitting between the server and the application.&lt;/p&gt;

&lt;h2 id=&quot;100s-informational&quot;&gt;100s (Informational)&lt;/h2&gt;

&lt;p&gt;In my 15 years of web development, I’ve literally never explicitly used one of these.&lt;/p&gt;

&lt;h2 id=&quot;200s-success&quot;&gt;200s (Success!)&lt;/h2&gt;

&lt;p&gt;If you were to use only a single response code to indicate a successful request, it would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt;. This is, I imagine, the most common HTTP status response since everytime a web page is loaded successfully, it’s almost certainly the status that’s sent back.&lt;/p&gt;

&lt;h3 id=&quot;200-ok&quot;&gt;200 (OK)&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200&quot;&gt;200&lt;/a&gt; response means “OK”, as in “here’s your page, as requested!”&lt;/p&gt;

&lt;p&gt;When you’re writing backend code, and you’re covering the happy path, and everything is happy, this is &lt;em&gt;most likely&lt;/em&gt; the code you send back. More than likely, though, the server software you’re using will send it back for you.&lt;/p&gt;

&lt;h3 id=&quot;201-created&quot;&gt;201 (Created)&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201&quot;&gt;201&lt;/a&gt; response means that the resource you were trying to create was successfully created. The expectation is that new resources are created via a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; verb.&lt;/p&gt;

&lt;h3 id=&quot;204-no-content&quot;&gt;204 (No Content)&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204&quot;&gt;204&lt;/a&gt; is &lt;em&gt;almost&lt;/em&gt; like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt; except that there’s no body (content) to go with it, just the headers. When you are updating an existing resource, it’s generally done via that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt; verb, and this would commonly be the response.&lt;/p&gt;

&lt;h3 id=&quot;206-partial-content&quot;&gt;206 (Partial Content)&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206&quot;&gt;206&lt;/a&gt; is also similar to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt; except this one requires a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range&quot;&gt;Range&lt;/a&gt; header to be sent. This one is used when the client is requesting a large bit of content in piecemeal, such as a large video file, 500kB at a time.&lt;/p&gt;

&lt;h2 id=&quot;300s-your-princess-is-in-another-castle&quot;&gt;300s (Your princess is in another castle)&lt;/h2&gt;

&lt;p&gt;These are for when the request is valid but you’re not sending back actual content. Typically these are seen as the “redirects” though the lazy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;304&lt;/code&gt; (“you already know it”) also falls into this group.&lt;/p&gt;

&lt;h3 id=&quot;301-moved-permanently&quot;&gt;301 (Moved Permanently)&lt;/h3&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301&quot;&gt;301&lt;/a&gt; status is the code you send back when the document is somewhere else. This is what you use when you move a page from one site to another, or if you do a restructuring and everything is in a new place now. When you send this status back, be sure to also send a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location&quot;&gt;Location&lt;/a&gt; header back too.&lt;/p&gt;

&lt;p&gt;(There is another similar status code, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308&quot;&gt;308&lt;/a&gt;, that is almost identical to this except the request headers are immutable. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt; can change the HTTP verb used, but not with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;308&lt;/code&gt;. I can’t think of anytime I’ve ever seen or used a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;308&lt;/code&gt; in real life, though)&lt;/p&gt;

&lt;h3 id=&quot;302-found&quot;&gt;302 (Found)&lt;/h3&gt;

&lt;p&gt;This one is kind of funny. It’s called “Found” but we more commonly just call it a “Temporary Redirect”. A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302&quot;&gt;302&lt;/a&gt; is almost the same as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt;, otherwise. You have to also send a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; header with it. The content you were looking for is over here for now, but hang on to the old link. It’s called “Found”, but my coworkers and I always call this one the “temporary redirect” (even though that is technically &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307&quot;&gt;307&lt;/a&gt; – similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;308&lt;/code&gt;, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;307&lt;/code&gt; has immutable request headers, but is for a temporary redirect.)&lt;/p&gt;

&lt;h3 id=&quot;304-not-modified&quot;&gt;304 (Not Modified)&lt;/h3&gt;

&lt;p&gt;A very important status code to know, for performance! This requires some additional headers sent in with the request (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match&quot;&gt;If-None-Match&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Modified-Since&quot;&gt;If-Modified-Since&lt;/a&gt;). When the client requests a resource and provides one of those headers, if the resource hasn’t been modified since the date provided, it responds with this status code and nothing else! Much faster, and much more performant on the server-side.&lt;/p&gt;

&lt;h2 id=&quot;using-these-headers&quot;&gt;Using These Headers&lt;/h2&gt;

&lt;p&gt;Most frameworks come equipped with short-hand methods, or automatic means, of sending back the appopriate status message. Here are some examples for languages that I’ve used. You’ll want to check your docs for how to do it in your language / framework, or find out how yours handles status messages.&lt;/p&gt;

&lt;h3 id=&quot;php&quot;&gt;PHP&lt;/h3&gt;

&lt;p&gt;One strength that PHP has here is that the Apache web server actually fields a lot of the statuses. Since PHP is a file that is run through a pre-processor before responding (but it’s an actual file), if you request a file that doesn’t exist, Apache kicks back a 404 and will respond with whatever you tell it to use for 404s. If the document is found, it sends back a 200.&lt;/p&gt;

&lt;p&gt;For redirects, you can either use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.htaccess&lt;/code&gt; for your redirects, or you can use direct PHP:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Location: /foo/bar/baz.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;301&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// a 301 (permanent) redirect&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Location: /foo/bar/baz.php&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;302&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// a 302 (permanent) redirect&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You can technically use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;header()&lt;/code&gt; method to respond with pretty much &lt;em&gt;any&lt;/em&gt; status code (&lt;a href=&quot;https://www.php.net/manual/en/function.header.php&quot;&gt;Ref&lt;/a&gt;), including the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4xx&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5xx&lt;/code&gt; headers (covered in Part 2), but most commonly I see this used for redirects. One thing you &lt;em&gt;could&lt;/em&gt; do that I don’t see very often is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;header()&lt;/code&gt; to send back a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;201&lt;/code&gt;/&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;204&lt;/code&gt; when handling a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt;, respectively. Typically it just falls through as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;ruby-on-rails&quot;&gt;Ruby on Rails&lt;/h3&gt;

&lt;p&gt;Rails is a little more complicated because every request at least hits the Rails stack before it’s handled. All the routes are dynamically generated and handled, so before it can know if the response should be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt;, it has to see what’s available.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;201&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;204&lt;/code&gt; are all sent back implicitly with resourceful actions. If you do a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST#create&lt;/code&gt;, the response will be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;201&lt;/code&gt;. If you do a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT#update&lt;/code&gt;, the response will be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;204&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have caching enabled, and a request is cached, it will automatically respond to the request with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;304&lt;/code&gt;. No additional action required.&lt;/p&gt;

&lt;p&gt;Redirection is offered with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_to&lt;/code&gt; method.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# using the literal status code&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alternate_resource_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;301&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# using the friendly-looking text version&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alternate_resource_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:moved_permanently&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and for temporary redirects:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# using the literal status code&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alternate_resource_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;302&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# using the friendly-looking text version&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;redirect_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;alternate_resource_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:found&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

</summary>
      <id>https://armahillo.dev/fundamentals/2020/11/02/http_statuses/</id>
      <updated>2020-11-02T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Input Checkbox Arrays</title>
      <link href="https://armahillo.dev/fundamentals/2020/10/27/input_checkbox_arrays/" />
      <summary type="html">&lt;p&gt;This post covers an unobvious method for submitting checkbox selections, but it also covers, conceptually, how to shape data sent in an HTTP Request params so that it is received by the server as a collection, instead of as a separate value in parallel with the other values.&lt;/p&gt;

&lt;h2 id=&quot;checkboxes-as-a-list&quot;&gt;Checkboxes as a List&lt;/h2&gt;

&lt;p&gt;A while back, I saw a collection of checkboxes that looked like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors_blue&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors_green&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors_red&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Whether or not you see what the problem is depends a lot on how closely you’ve worked with posting form data.&lt;/p&gt;

&lt;p&gt;My first server-side language I used, professionally, was PHP. For all of its faults, it actually does teach you a lot about how form data is posted (per HTTP standards) and how you can work with it.&lt;/p&gt;

&lt;p&gt;For those of you who don’t see the issue, let me first show what those checkboxes would look like when posted to a PHP handler. Let’s say we want it to respond back with what the color(s) selected were. Obviously there are many ways to do this, and this is only one possibility.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$blue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$red&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$green&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;colors_blue&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$blue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;blue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;colors_green&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$green&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;green&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;colors_red&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$red&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Your choices were: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$blue&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$red&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$green&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Maybe you’re clever and realize you can refactor that into a function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;colors_&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nv&quot;&gt;$blue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;blue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$red&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;red&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$green&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;green&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Your choices were: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$blue&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$red&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$green&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It’s actually the same number of lines of code, but will scale better with more colors added: Each color added to the array will add 3 lines to the first example, but only one line to the second.&lt;/p&gt;

&lt;p&gt;Another approach would be to iterate over ALL the values in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_POST&lt;/code&gt; and use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;switch()&lt;/code&gt; statement to determine the handling.&lt;/p&gt;

&lt;p&gt;Those are all “wrong” solutions.&lt;/p&gt;

&lt;p&gt;What I mean by “wrong” is that they’re not utilizing the tools correctly. It would be similar to say that it’s “wrong” to hang a framed picture by hammering the frame itself to the wall (instead of using the picture string on the back and hanging that from a nail). Yes, it technically works, but you’re making your life harder by doing it that way.&lt;/p&gt;

&lt;h2 id=&quot;checkboxes-as-an-array&quot;&gt;Checkboxes as an Array&lt;/h2&gt;

&lt;p&gt;If you do the HTML like this and they select one more options:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors[]&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;red&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors[]&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blue&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors[]&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;green&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then you can write your PHP handler like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$color_choices&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;colors&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$color_choices&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Your choices were: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$color_choices&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Five lines. And really, you could shrink it even further if you don’t mind a bit of sloppy implied string interpolation.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Your choices were:&quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_POST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;colors&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;One line.&lt;/p&gt;

&lt;p&gt;And it will scale infinitely, no matter how many colors you add.&lt;/p&gt;

&lt;p&gt;This works because when you pass a named value that is suffixed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]&lt;/code&gt; it is treated as a collection and allows for multiple values to be associated with it. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_POST[&apos;colors&apos;] == array(&quot;red&quot;, &quot;blue&quot;, &quot;green&quot;)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Were you to do this, and someone checked all the boxes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;red&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;blue&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;checkbox&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colors&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;green&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;(Note the lack of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]&lt;/code&gt;s on the name)&lt;/p&gt;

&lt;p&gt;The posted array would only contain the last chosen value. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_POST[&apos;colors&apos;] == &quot;green&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The best way to learn more about this is to experiment with processing forms. PHP is probably the best way to do this because it sits immediately on the server layer and is readily available on most hosts (and easy to install on your own local machine!). But any server that expects you to handle the requests directly would work.&lt;/p&gt;

&lt;p&gt;Further reading on the &lt;a href=&quot;https://www.php.net/manual/en/faq.html.php&quot;&gt;PHP site&lt;/a&gt;.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/fundamentals/2020/10/27/input_checkbox_arrays/</id>
      <updated>2020-10-27T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Between the Server and the Stack</title>
      <link href="https://armahillo.dev/fundamentals/2020/10/25/between_the_server_and_the_stack/" />
      <summary type="html">&lt;h2 id=&quot;my-background&quot;&gt;My Background&lt;/h2&gt;

&lt;p&gt;I’ve been doing web development professionally since 2004, though I’ve been working with HTML since… I want to say 1995. When NCSA Mosaic launched, and Netscape 1.0 shortly after that (they would later evolve into Microsoft Internet Explorer and Mozilla Firefox, respectively), I had an O’Reilly book on the HTML 1.0 spec and was learning how to make my own web pages.&lt;/p&gt;

&lt;p&gt;My first server-side language was PHP, and like many PHP web developers, I was self-taught. In the early 00s, we didn’t really have a lot of web frameworks; Wordpress was still in 1.0 and was almost exclusively for blogging (it didn’t even have pages yet!).&lt;/p&gt;

&lt;p&gt;If you wanted to write form processing, or handle user interaction with a database-backend, or do any kind of session management, you had to basically write it yourself (or grab copypasta from someone else’s code, which was also very common).&lt;/p&gt;

&lt;h2 id=&quot;more-recently&quot;&gt;More Recently&lt;/h2&gt;

&lt;p&gt;In the past 5 to 10 years, with the emergence of web frameworks, a lot of the work happening between the server layer and the application layer gets abstracted away. All of the awesome new folks who are entering the wonderful world of web development aren’t forced to learn those things that we had to learn initially.&lt;/p&gt;

&lt;p&gt;The upside is that they can get into writing features for awesome web applications sooner, but the downside is the lack of knowledge about what’s going on under the hood.&lt;/p&gt;

&lt;h2 id=&quot;this-series&quot;&gt;This Series&lt;/h2&gt;

&lt;p&gt;What I would like to do with this series is to write some articles to share some of this knowledge. Initially, it will be driven by things I’ve noticed in working with other people.&lt;/p&gt;

&lt;p&gt;The content is intended to fill in that area between the web server (where the initial request is received) and the stack that’s running the application. I might note a framework if there’s something relevant to a specific one, but largely this information should be stack-agnostic. After all, behind any stack or server is a platform-agnostic, open source, HTTP and HTML specification!&lt;/p&gt;

&lt;p&gt;The target audience for this is probably people who are either working through a coding bootcamp or self-teaching and are focusing primarily on a “framework-first” curriculum (e.g. Wordpress, React, Rails, .NET). Other web developers, even seasoned ones, may find some of this information useful, as well.&lt;/p&gt;

</summary>
      <id>https://armahillo.dev/fundamentals/2020/10/25/between_the_server_and_the_stack/</id>
      <updated>2020-10-25T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Introducing common API with Aliases</title>
      <link href="https://armahillo.dev/2020/08/22/rails-refactor-aliases/" />
      <summary type="html">&lt;h2 id=&quot;scenario&quot;&gt;Scenario&lt;/h2&gt;

&lt;p&gt;Let’s say that you have an application that has three very similar models, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CompactDisc&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CassetteTape&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VinylRecord&lt;/code&gt;. They all have very similar behaviors, and are all related to one another, though they each have separate tables. STI may or may not be possible.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Code smell&lt;/em&gt; Similar to “data clump”, “feature envy”; though in this case it’s models that are similar enough to always be aggregated together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# app/models/compact_disc.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CompactDisc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tracks&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# app/models/cassette_tape.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CassetteTape&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:songs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;through: :sides&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# app/models/vinyl_record.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VinylRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:grooves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;through: :record_sides&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;They might be collected together like this (where it’s happening isn’t really important):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;vi&quot;&gt;@compact_discs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CompactDisc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@cassette_tapes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CassetteTape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@vinyl_records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;VinylRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@compact_discs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@cassette_tapes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@vinyl_records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CompactDisc&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;partial: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tracklist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;collection: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tracks&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CassetteTape&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;partial: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;liner_notes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;collection: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;songs&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;VinylRecord&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;partial: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;tracklist&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;collection: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;grooves&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Whether or not this could actually be refactored into an STI relationship is unknown, but we will assume it cannot be, for one reason or another. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;case&lt;/code&gt; statement, particularly with type-checking, is generally a code smell on its own.&lt;/p&gt;

&lt;p&gt;Let’s assume that what we would like the block above to look like is this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;vi&quot;&gt;@compact_discs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CompactDisc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@cassette_tapes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CassetteTape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@vinyl_records&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;VinylRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@compact_discs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@cassette_tapes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@vinyl_records&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;partial: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;album&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;object: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_album.html.erb&lt;/code&gt; partial looks like this (just to show that we’re not just hiding the type checking). This is simplified for clarity.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# app/views/albums/_album.html.erb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;album&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;songs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;song&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;partial: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;song&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;object: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;song&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The most direct change would be to modify the association name, but because this is a legacy app, we want to make the least-impactful change. That  We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:alias&lt;/code&gt; method here. So we might change the models from earlier to look like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# app/models/compact_disc.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CompactDisc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tracks&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:songs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:tracks&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# app/models/cassette_tape.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CassetteTape&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:songs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;through: :sides&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# unchanged&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# app/models/vinyl_record.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;VinylRecord&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:grooves&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;through: :record_sides&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:songs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:grooves&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can assume that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Track&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Song&lt;/code&gt; quack in similar enough ways that they can function interchangeably in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_song&lt;/code&gt; partial. (If not, additional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alias&lt;/code&gt; can be used)&lt;/p&gt;

</summary>
      <id>https://armahillo.dev/2020/08/22/rails-refactor-aliases/</id>
      <updated>2020-08-22T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Introducing EmojiSub</title>
      <link href="https://armahillo.dev/2020/08/22/emoji_sub/" />
      <summary type="html">&lt;p&gt;I wrote a gem. It’s called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EmojiSub&lt;/code&gt; (the similarity to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gsub&lt;/code&gt; is intentional).&lt;/p&gt;

&lt;p&gt;You can find the gem on rubygems: &lt;a href=&quot;https://rubygems.org/gems/emoji_sub&quot;&gt;rubygems.org/gems/emoji_sub&lt;/a&gt;, or see the source on &lt;a href=&quot;https://github.com/armahillo/emoji_sub&quot;&gt;github.com/armahillo/emoji_sub&lt;/a&gt;. As of this writing, it is at version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.2.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s a very simple script that takes a text string and does a global substitution of any emoji shortcodes (a la Slack, Github, etc) and replaces them with the actual HTML entity code for the emoji.&lt;/p&gt;

&lt;p&gt;In the process, I’ve had to learn a lot about Emoji, unicode, and how they work, it’s been a surprising topic that’s far deeper than I anticipated!&lt;/p&gt;

&lt;p&gt;I’ll write more about the gem a bit later, once it’s more stable. There’s been a lot of steps involved in this whole process that I’ll talk about too.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2020/08/22/emoji_sub/</id>
      <updated>2020-08-22T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Book Store</title>
      <link href="https://armahillo.dev/2020/07/20/book-store-meditation-1/" />
      <summary type="html">&lt;p&gt;I’ve recently been doing some coding drills to create some opportunities for code refactors. I was reminded of &lt;a href=&quot;https://exercism.io&quot;&gt;Exercism.io&lt;/a&gt; and, having not checked them out in several years, was delighted to see how much they’ve improved since then.&lt;/p&gt;

&lt;p&gt;Feel free to follow / friend / stalk me over there: &lt;a href=&quot;https://exercism.io/profiles/armahillo&quot;&gt;https://exercism.io/profiles/armahillo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would like to do a blog series on some of the exercises I work through over there, to discuss my thought process on how I approach the problems. I’m currently working through the Ruby track, so all these problems will be using Ruby code, but software design discussion is largely language agnostic, and you should be able to follow along even if you prefer a different language.&lt;/p&gt;

&lt;h2 id=&quot;the-problem-book-store-checkout&quot;&gt;The Problem: Book Store (Checkout)&lt;/h2&gt;

&lt;p&gt;This exercise, one of the ones designated “Hard”, is called “&lt;a href=&quot;https://exercism.io/my/solutions/08d30003d7f347639f31d31930ff4b1e&quot;&gt;Book Store&lt;/a&gt;”. This problem felt very realistic because, at first glance, it seems almost trivial (I solved the primary use cases in about 5 minutes), but there’s an optimization requirement underneath the surface that is a bit insidious.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;To try and encourage more sales of different books from a popular 5 book series, a bookshop has decided to offer discounts on multiple book purchases.One copy of any of the five books costs $8.If, however, you buy two different books, you get a 5% discount on those two books.If you buy 3 different books, you get a 10% discount.If you buy 4 different books, you get a 20% discount.If you buy all 5, you get a 25% discount.Note: that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs $8.Your mission is to write a piece of code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href=&quot;https://exercism.io/my/solutions/08d30003d7f347639f31d31930ff4b1e&quot;&gt;https://exercism.io/my/solutions/08d30003d7f347639f31d31930ff4b1e&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;

&lt;h3 id=&quot;pass-1-naive-interpretatation&quot;&gt;Pass 1: Naive Interpretatation&lt;/h3&gt;

&lt;p&gt;Not &lt;em&gt;too&lt;/em&gt; complicated, right? My initial reading of it was “oh, look at how many books are in the cart, and calculate the discount from that, easy peasy”. The input you get is an array (this much is given), and looks like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[2,3,5]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;My first solution looked a bit like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BookStore&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;DISCOUNTED_TOTAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;NOMINAL_PRICE_PER_UNIT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;8.0&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate_price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;NOMINAL_PRICE_PER_UNIT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;DISCOUNTED_TOTAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A few of the tests passed. My approach here was a trivial (and naive, as I later discovered) approach: multiply the nominal cost times the number of books, and then apply a discount.&lt;/p&gt;

&lt;p&gt;I used a class constant array to represent the discount – the discount level is a scalar that I can have serve double-duty as the index in the array. If you have 1 book, you pay 100% (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0&lt;/code&gt;); if you have 3 books, you get a 10% discount (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.00 - 0.10 =&amp;gt; 0.90&lt;/code&gt;), etc.&lt;/p&gt;

&lt;p&gt;I had overlooked one important thing: the discount wasn’t on how many books were purchased overall, it was applied individually to books in a set. A set was 5 possible books, represented as the numerals 1 through 5. The 5-book discount was if you made your set from all 5, the 3-book discount was from if you had 3 of the books, etc.&lt;/p&gt;

&lt;p&gt;The test that first failed was given input like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[1,2,3,3,4,5,5]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Whoops!&lt;/p&gt;

&lt;h3 id=&quot;pass-2-allowing-for-multiple-sets&quot;&gt;Pass 2: Allowing for multiple sets&lt;/h3&gt;

&lt;p&gt;So the problem then was to calculate the largest possible sets, group the purchase together into those sets, and then calculate the discount and apply it to each group, and total up the result.&lt;/p&gt;

&lt;p&gt;This was what I came up with:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BookStore&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;DISCOUNTED_TOTAL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.90&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.75&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;NOMINAL_PRICE_PER_UNIT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;8.0&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate_price&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;basket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;calculate_price_for_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate_price_for_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;NOMINAL_PRICE_PER_UNIT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;DISCOUNTED_TOTAL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So this got a bit more interesting.&lt;/p&gt;

&lt;p&gt;The first thing I did was to extract the price calculation to a separate method, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calculate_price_for_set&lt;/code&gt;. I applied a dependency injection refactor to it where it only needs to know the size, instead of knowing that the argument it receives is an argument that can receive a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:size&lt;/code&gt; message.&lt;/p&gt;

&lt;p&gt;After that, my thinking for the main algorithm was this: I wanted to produce the largest possible sets possible. I envisioned making multiple passes over the collection, where you make a maximized combination on each pass.&lt;/p&gt;

&lt;p&gt;There is no provision that the arguments will be ordered. The example from above could also be written: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[3,1,2,5,3,4,5]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Applying a &lt;a href=&quot;https://apidock.com/ruby/Enumerable/group_by&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;group_by&lt;/code&gt;&lt;/a&gt; to the collection will create the necessary clustering. This will convert the above example into:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If we have it clustered, then we can ask each type to give us 1 book, at a time, sort of like peeling off a layer at a time. Asking an empty book type for an element will yield &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;, which can be compacted away easily.&lt;/p&gt;

&lt;p&gt;The powerhouse is here:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while&lt;/code&gt; conditional is verbose (and not &lt;em&gt;technically&lt;/em&gt; a Law of Demeter violation because each method returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt;), but straightforward: if there’s anything left in the groupings, keep chugging along.&lt;/p&gt;

&lt;p&gt;The inside of the block is a bit trickier, so let’s break it down.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grouped.values&lt;/code&gt; is referencing the Hash &lt;a href=&quot;https://ruby-doc.org/core-2.7.0/Hash.html#method-i-values&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;values&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;Applying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map!&lt;/code&gt; to it means “edit this in-place” by applying the block to it. Typical usage of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map! &lt;/code&gt;would look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mouse&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The end result would yield: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&quot;DOG&quot;, &quot;CAT&quot;, &quot;MOUSE&quot;]&lt;/code&gt;: The original array with each element being transformed in the same way. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; does in-place modification, so that we don’t have to do:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;mouse&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;upcase&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:pop&lt;/code&gt; method, when applied to an array, does a LIFO desertion of an element from the collection. Though this does not have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!&lt;/code&gt; on it, it is destructive and does modify in-place:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dog&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;cat&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;mouse&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pet&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;animals&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;dog&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;cat&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since each array in the groupings is homogeneous, we don’t care about the order (I think &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:pop&lt;/code&gt; is more fun-sounding than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:shift&lt;/code&gt;, but they’re effectively the same here.) What we want to happen here is this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pop&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since the same operation is being applied directly to each element, uniformly, with no conditions or additional operations, we can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc&lt;/code&gt; shorthand. The above code can be re-written like this, and it will behave identically:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:proc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With the array input of: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[3,1,2,5,3,4,5]&lt;/code&gt;, the contents of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all_sets&lt;/code&gt; will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[[1,2,3,4,5], [3,5]]&lt;/code&gt;. With this, we pass it on to the sum calculator, which is similar to before:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;all_sets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;calculate_price_for_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sum&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:collect&lt;/code&gt; is similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:map&lt;/code&gt;, in that it’s an iterator; I think they may actually just be aliases for one another. Contextually, I like to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:collect&lt;/code&gt; when I’m gathering the results, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:map&lt;/code&gt; when I’m applying a transformation, but that’s a personal preference.&lt;/p&gt;

&lt;p&gt;This block of code does the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:calculate_price_for_set&lt;/code&gt; method, discussed earlier, gathering the result into an array result. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;end.sum&lt;/code&gt; at the end means “take the result and send the &lt;a href=&quot;https://ruby-doc.org/core-2.7.1/Enumerable.html#method-i-sum&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:sum&lt;/code&gt;&lt;/a&gt; message to it”&lt;/p&gt;

&lt;p&gt;Narratively, the original code for this pass means:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Cluster the list into like terms&lt;/li&gt;
  &lt;li&gt;Peel one entry off of each and store it in one set&lt;/li&gt;
  &lt;li&gt;Repeat 2 if there are any entries left&lt;/li&gt;
  &lt;li&gt;Take the sets and calculate their price (using the formula from the first pass), then sum up the total for all the sets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I have to admit, I felt pretty clever here. It’s concise and elegant. And &lt;em&gt;almost&lt;/em&gt; passed all the tests.&lt;/p&gt;

&lt;h3 id=&quot;pass-3-optmization&quot;&gt;Pass 3: Optmization&lt;/h3&gt;

&lt;p&gt;Remember the insidious problem underneath the surface I mentioned earlier?&lt;/p&gt;

&lt;p&gt;One way of grouping these 8 books is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; - 1 group of 5 --&amp;gt; 25% discount (1st,2nd,3rd,4th,5th)
 -- +1 group of 3 --&amp;gt; 10% discount (1st,2nd,3rd)

 This would give a total of:

  - 5 books at a 25% discount
  -- +3 books at a 10% discount
  - Resulting in:5 x (8 - 2.00) == 5 x 6.00 == $30.00
  -- +3 x (8 - 0.80) == 3 x 7.20 == $21.60
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For a total of $51.60&lt;/p&gt;

&lt;p&gt;However, a different way to group these 8 books is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; - 1 group of 4 books --&amp;gt; 20% discount (1st,2nd,3rd,4th)
 -- +1 group of 4 books --&amp;gt; 20% discount (1st,2nd,3rd,5th)

This would give a total of:

 - 4 books at a 20% discount
 -- +4 books at a 20% discount

 Resulting in:4 x (8 - 1.60) == 4 x 6.40 == $25.60

 -- +4 x (8 - 1.60) == 4 x 6.40 == $25.60
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For a total of $51.20&lt;/p&gt;

&lt;p&gt;And $51.20 is the price with the biggest discount (confirmed by the tests).&lt;/p&gt;

&lt;p&gt;These were the tests that were failing.&lt;/p&gt;

&lt;p&gt;This is also a slightly different, and significantly more complicated, problem: We now have to teach the script how to optimize the groupings. Comparing the grouping costs once they’re assembled is trivial, but establishing the groupings first is not.&lt;/p&gt;

&lt;p&gt;Allocation algorithms is a well-documented and explored facet of computer science (and mathematics). There’s the Travelling Salesman problem (optimize the best path given different weights along the edges), the Knapsack problem (optimize inclusion in a final set based on limited space), etc. This problem is a little similar to both of those.&lt;/p&gt;

&lt;p&gt;The first thing I did was to look at the original definition of the discount breakdown to understand why there might be a difference:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 of any of the five books gets 0% discount ($8)&lt;/li&gt;
  &lt;li&gt;2 different books, gives a 5% discount on those two books ($8 + $8 = $16 * 95%).&lt;/li&gt;
  &lt;li&gt;3 different books, you get a 10% discount.&lt;/li&gt;
  &lt;li&gt;4 different books, you get a 20% discount.&lt;/li&gt;
  &lt;li&gt;5, you get a 25% discount.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The jump from 1 to 2 is a 5% decrease in cost. The difference from 2 to 3 is also a 5% decrease. However, the difference from 3 to 4 is a whole 10% decrease. Going from 4 to 5 is only an additional 5% decrease.&lt;/p&gt;

&lt;p&gt;So really, any situation where we can get a 4-book set is potentially a bigger discount. Designing for this scenario specifically will still be somewhat complicated and would likely result in over-fitting (making it harder to modify in the future).&lt;/p&gt;

&lt;p&gt;The strategy I was imagining was one where we try to balance out the sets as much as possible. One of the tests is named:  “two groups of four is cheaper than a group of five and a group of three”, which further supports this hypothesis.&lt;/p&gt;

&lt;p&gt;The question then became, how do I turn this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Into this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;My first pass on this strategy was very brutish. I don’t have the code anymore and it would be a pain to recreate it, but basically it set up two pointers, a “set pointer” and a “book pointer”. The set pointer was responsible for pointing to which set we were currently adding to, and the book pointer pointed to the book that was a candidate for addition. Each iteration of the loop would advance the set pointer if a book had been added, but would always advance the book pointer.&lt;/p&gt;

&lt;p&gt;I also realized that the number of sets we would need was (perhaps naively) the size of the largest collection of books. Logically, we would at least need that many; given &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[1,1,1,1,1,2,3,4,5]&lt;/code&gt; even the “largest set” algorithm would yield:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This would prove important later.&lt;/p&gt;

&lt;p&gt;Overall, though, this was clunky. It was immediately apparent that, in all the ways the “largest set” solution was elegant, this was the opposite. I kept running into “wait, but what if…” cases.&lt;/p&gt;

&lt;p&gt;“What if the set already has the book that’s pointed to?”, “What if the set is full already?”, “What if the book that’s pointed to is all gone?”&lt;/p&gt;

&lt;p&gt;I put it away for the evening and returned to it in the morning.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sleep helped! I realized that I didn’t need to pre-group like with the largest set strategy. The approach for “largest set” was to focus on the set first and fill it up with as much as possible; what we needed to do here was focus on each book type and spread them out as much as possible.&lt;/p&gt;

&lt;p&gt;If we began with the book types sorted by how many there were, highest quantity first, we would get this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;order_by_quantity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This tells us that we will need 2 sets (we have two 3s), and if we process this list in order, we will ensure that the highest available books will be distributed before the rest of the books are distributed. In pseudocode, this might look like:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Order the list by its quantity, highest quantity first&lt;/li&gt;
  &lt;li&gt;Initialize a number of sets equal to the highest number of books of a single type&lt;/li&gt;
  &lt;li&gt;Starting with the highest quantity books in the ordered list:
   a. Add the book to the current set
   b. Move to the next set in the series (wrapping around if necessary)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 1 could be approached a few different ways. I did it like this, using the grouping from the “largest set” strategy:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;pile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort_by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We’re using the clustering approach from “largest set”, and then applying &lt;a href=&quot;https://ruby-doc.org/core-2.7.1/Enumerable.html#method-i-sort_by&quot;&gt;:sort_by&lt;/a&gt; to it. We’re sorting based on the size. This puts the largest groups first.&lt;/p&gt;

&lt;p&gt;Step 2, initializing the arrays, is a trivially normal, though there is a gotcha here that I learned (continuing from previous sample):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;number_of_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;grouped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;balanced_sets&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_sets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array.new&lt;/code&gt; can technically accept two arguments: how many elements to initialize, and a second argument that is an initial value for each.&lt;/p&gt;

&lt;p&gt;HOWEVER, look at this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[])&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[[], [], []]&quot;&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object_id&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;004&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object_id&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object_id&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;180&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cloned_elements&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Whoops! That’s no good. The documentation for &lt;a href=&quot;https://ruby-doc.org/core-2.7.0/Array.html#method-c-new&quot;&gt;Array.new&lt;/a&gt; notes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When sending the second parameter, the same object will be used as the value for all the array elements. Since all the &lt;a href=&quot;https://ruby-doc.org/core-2.7.0/Array.html&quot;&gt;Array&lt;/a&gt; elements store the same hash, changes to one of them will affect them all. If multiple copies are what you want, you should use the block version which uses the result of that block each time an element of the array needs to be initialized.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]]&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;010&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object_id&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;011&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object_id&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;220&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;012&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;object_id&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;240&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;013&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;2.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;014&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique_elements&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This looks like what we want. I actually got tripped up by this behavior initially – I was adding a single book to one set and it was adding to all the sets. How confusing!&lt;/p&gt;

&lt;p&gt;Step 3 from above is a straightforward approach, instead of using book pointers and set pointers:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;set_pointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# stuff coming soon&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We’re using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ordered&lt;/code&gt; from the previous example. We &lt;em&gt;are&lt;/em&gt; going to use a set pointer here, but we no longer need the book pointer as well.&lt;/p&gt;

&lt;p&gt;Steps 3a and 3b will use that set pointer to distribute the book:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;ordered&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;balanced_sets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_pointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;balanced_sets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;book&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;set_pointer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_pointer&lt;/code&gt; modulo math is a trick I learned in C, many years ago. You can continuously increase and ensure that you wrap around with the right cycle by taking the modulus against the size of the array.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Since arrays are 0-indexed, this trick maps well, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;balanced_sets[set_pointer % balanced_sets.size]&lt;/code&gt;should never land out-of-bounds.&lt;/p&gt;

&lt;p&gt;This solution worked. I wrapped the whole business in a method called “balanced” and put it alongside the other method “largest”.&lt;/p&gt;

&lt;p&gt;The last thing to do was to apply both strategies to the same basket and see which one produced the lower cost:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:largest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:balanced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;calculate_price_for_combinations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;book_pile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;apply_strategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;min&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The approach I took here was to do some meta-programming: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:apply_strategy&lt;/code&gt; accepts a label of some kind, and then invokes the method that matches that name:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;apply_strategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@pile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@pile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;NoMethodError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; isn&apos;t a supported strategy&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This pre-emptively resolves any situation where the inbound array (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@pile&lt;/code&gt;) is empty. It uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:send&lt;/code&gt; method, passing the strategy provided, with a duplication of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@pile&lt;/code&gt;. Using a duplication ensures we don’t accidentally produce mutations as side effects.&lt;/p&gt;

&lt;p&gt;One advantage of this approach is that we can add additional optimization strategies (maybe the store offers additional discounts on odd-numbered volumes) and add them alongside these other two, and minimal changes will be required to the overall structure – add a new method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:discount_odd_volumes&lt;/code&gt;, add that to the array of methods, and that’s it!&lt;/p&gt;

&lt;p&gt;Similarly, it separates the cost calculation from the grouping algorithm, so changes to the former will not affect the latter, and vice versa.&lt;/p&gt;

&lt;p&gt;I’m happy with this result, and so was the test suite.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://exercism.io/my/solutions/08d30003d7f347639f31d31930ff4b1e&quot;&gt;Check out my solution&lt;/a&gt; and let me know what you think.&lt;/p&gt;

&lt;p&gt;I’ll try to post some more of these in the future.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;Header Image Credit: &lt;a href=&quot;https://www.needpix.com/photo/download/928828/books-book-bookshelf-library-read-literature-shelf-free-pictures-free-photos&quot;&gt;https://www.needpix.com/photo/download/928828/books-book-bookshelf-library-read-literature-shelf-free-pictures-free-photos&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2020/07/20/book-store-meditation-1/</id>
      <updated>2020-07-20T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Modifying a has_many to include nils</title>
      <link href="https://armahillo.dev/2017/10/26/modifying-has_many-to-include-nils/" />
      <summary type="html">&lt;p&gt;I had this interesting use case come up.&lt;/p&gt;

&lt;p&gt;We have a multi-tenant app that has an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Organization&lt;/code&gt;  model. An &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Organization&lt;/code&gt; has many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BarcodeItem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Originally, the query was direct: We only wanted an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Organization&lt;/code&gt;  to return the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;barcode_items&lt;/code&gt;  that belonged to it.  Then we decided to pivot on this particular feature and have an organization include both its own &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;barcode_items&lt;/code&gt;  but also any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BarcodeItem&lt;/code&gt;  that was explicitly set to nil . The idea here was that some &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BarcodeItem&lt;/code&gt;  would be “global”, and the least complicated way to do that would be to de-associate them from any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Organization&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I started with a spec to test this out:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;barcode_items&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;returns both this organizations barcodes as well as global ones&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Test that it can see globals&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:barcode_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;organization_id: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# global&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;barcode_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Test that it can also see its own items&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:barcode_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:for_organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;organization: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;barcode_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;My first thought was to use an association model, something like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Organization&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:barcode_items&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;all&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# this doesn&apos;t work but it&apos;s the idea I started with&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;organization_id: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But even after just writing that it just smelled wrong. Surely there’s an easier way to do this. Consulting the docs, I was reminded of using association scopes. So I tried this instead:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Organization&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:barcode_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;organization_id: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;organzation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Still fails. I needed to see what it’s actually doing behind the scenes, so I dumped the SQL before the test:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;returns both this organizations barcodes as well as global ones&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:barcode_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;organization_id: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# global&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;barcode_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_sql&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;barcode_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;RSpec displayed the query as:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;barcode_items&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;barcode_items&quot;&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;barcode_items&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&quot;organization_id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; 
  &lt;span class=&quot;k&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&quot;barcode_items&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&quot;organization_id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;OR&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;barcode_items&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&quot;organization_id&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;-- returns both this organizations barcodes as well as global ones (FAILED - 1)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A-ha!&lt;/p&gt;

&lt;p&gt;The additional scoping added to the association is a boolean AND, but we need a boolean OR. After several failed attempts at mimicking that functionality, I found a StackOverflow answer that suggested unscoping the query first.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Organization&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:barcode_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;unscope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;where: :organization_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;organization_id: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;organization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Success!&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2017/10/26/modifying-has_many-to-include-nils/</id>
      <updated>2017-10-26T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Authenticating against WordPress in Rails 3</title>
      <link href="https://armahillo.dev/2012/03/13/authenticating-against-wordpress-in-rails-3/" />
      <summary type="html">&lt;blockquote&gt;
  &lt;p&gt;N.B.
Note the date on this article. It was written before Wordpress Version 5. I have not worked with Wordpress much since writing this, so I don’t know if it still works.&lt;/p&gt;

  &lt;p&gt;That said, I think the post still has value in its cross-application methodology.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been working on a Rails app that needs to integrate with an existing userbase found in a WordPress installation.&lt;/p&gt;

&lt;p&gt;One problem I’ve encountered is that her main website that manages subscriptions and users is in WordPress. Users will register with that main website and may or may not have access to the Rails application, separately (depending on their subscription level). What I needed was a method to handle User Authentication in Rails that slaves to the subscription / account information held in WordPress. Ideally, this should all keep with the DRY principle to make it nice and Rails-y.&lt;/p&gt;

&lt;p&gt;I wanted to avoid using any messy and potentially unsecure session cookie sharing, and I also wanted to avoid doing any database shadowing. I suspected that since all the data is up on databases on the same server, I should be able to just query it out, right?&lt;/p&gt;

&lt;p&gt;Right!&lt;/p&gt;

&lt;p&gt;Ok, so this was a little tricky and it took a couple hours to suss it all out, but here’s what I did. For this example, we’ll say that all of your databases are on the same host (mysql.host.com), though they don’t need to be.&lt;/p&gt;

&lt;h2 id=&quot;defining-the-settings&quot;&gt;Defining the settings&lt;/h2&gt;

&lt;p&gt;In your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/database.yml&lt;/code&gt; file, you need to specify separate entries for your WordPress database. Thanks to &lt;a href=&quot;https://gist.github.com/884428/fe79a3976a83a29e2d57e0414eaf99c73186b201&quot;&gt;fnichol&lt;/a&gt; for this gist.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf8&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rails_proj_development&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rp_dev&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wootwoot&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;wp_development&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf8&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wordpress&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wp_user&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hackable&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;table_prefix_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wp_dev_&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Warning: The database defined as &quot;test&quot; will be erased and&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# re-generated from your development database when you run &quot;rake&quot;.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Do not set this db to the same as development or production.&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf8&lt;/span&gt;
 &lt;span class=&quot;na&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rails_proj_test&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rp_test&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wootwoot&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;wp_test&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf8&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wordpress&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wp_user&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hackable&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;table_prefix_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wp_test_&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf8&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rails_proj_production&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rp_prod&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wootwoot&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;wp_production&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;adapter&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mysql2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;utf8&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;database&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wordpress&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pool&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wp_user&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;hackable&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;localhost&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;table_prefix_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;wp_&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Note that in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp_*&lt;/code&gt; entries, I added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;table_prefix_name&lt;/code&gt; entry. This will help keep things clean (inclusion alone does not do all the heavy lifting, but it puts it in an obvious place that we can reference later). The prefixes allow you to optionally put all your WordPresses into one database, prefixing them with different prefixes. I wouldn’t say this is necessarily advisable but it’s at least an option, if your configuration is already like that.&lt;/p&gt;

&lt;h2 id=&quot;creating-the-model&quot;&gt;Creating the Model&lt;/h2&gt;

&lt;p&gt;We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt; to connect to this database, so that everything is seamless. You will need to create a different model for each table you want to connect to (this is consistent with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt; pattern anyways). You can either create the model manually, using your favorite text editor, or you can use the rails generator, but be sure to pass it –skip-migration, since you don’t want it to create a duplicate table in your Rails DB.&lt;/p&gt;

&lt;p&gt;Here is the model I used for “User”. (fnichol does one for Post on &lt;a href=&quot;https://gist.github.com/884428/fe79a3976a83a29e2d57e0414eaf99c73186b201#file_app%2Fmodels%2Fword_press%2Fpost.rb&quot;&gt;his gist&lt;/a&gt;).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# define any &quot;has_many&quot; relationships here&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;establish_connection&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;wp_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;vi&quot;&gt;@prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;database_configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;wp_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;table_prefix_name&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;set_table_name&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@prefix&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;users&quot;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;             &lt;span class=&quot;s2&quot;&gt;&quot;ID&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;login&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;          &lt;span class=&quot;s2&quot;&gt;&quot;user_login&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;pass&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;           &lt;span class=&quot;s2&quot;&gt;&quot;user_pass&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;nicename&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;s2&quot;&gt;&quot;user_nicename&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;          &lt;span class=&quot;s2&quot;&gt;&quot;user_email&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;            &lt;span class=&quot;s2&quot;&gt;&quot;user_url&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;created_at&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;s2&quot;&gt;&quot;user_registered&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;activation_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;user_activation_key&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;         &lt;span class=&quot;s2&quot;&gt;&quot;user_status&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;alias_attribute&lt;/span&gt;  &lt;span class=&quot;s2&quot;&gt;&quot;display_name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;s2&quot;&gt;&quot;display_name&quot;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;readonly?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;before_destroy&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, first things first, do it just like you would any Rails AR model. Just make it a new class inheriting from AR:Base.&lt;/p&gt;

&lt;p&gt;This first line creates the connection:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;establish_connection &quot;wp_#{Rails.env}&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you used the naming scheme from the example database.yml file above, AND if your webhost handles Rails environments properly (mine sadly doesn’t 🙁 ), this should “just work.”&lt;/p&gt;

&lt;p&gt;The prefix we created in the database.yml file earlier will now be pulled in:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@prefix = Rails.application.config.database_configuration[&quot;wp_#{Rails.env}&quot;][&apos;table_prefix_name&apos;]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We then use that prefix to specify the table name. If you were wanting to create a Post model, you would obviously just change it to posts. A taxonomy model might use terms.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_table_name &quot;#{@prefix}users&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The lines in the middle are just aliases to make the model more Rails-y. The only critical one is the first — you need to alias ID to be id so that Rails can locate the id field when doing relationships.&lt;/p&gt;

&lt;p&gt;The last part is optional, though I recommend including it. Since Rails is a guest in the house of WordPress, you don’t want it knocking over vases, making out with WordPress’s daughter, or eating all the expired cupcakes and cookies in their cupboards like I used to do to my best friends in grade school (only that last part, not the second one and the first one only once).&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;005&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;006&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;007&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stacktrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;destroy&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stacktrace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_login&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;test&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;011&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;save&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ReadOnlyRecord&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Keeping the foreign table read-only will help ensure your Rails App doesn’t accidentally eat a Swiss Cake Roll that wasn’t yet expired.&lt;/p&gt;

&lt;h2 id=&quot;authenticating-against-the-database&quot;&gt;Authenticating Against the Database&lt;/h2&gt;

&lt;p&gt;Password “hashing” is the process of encrypting the password to make it so bad guys and curious monkeys can’t easily tell what your passwords are.&lt;/p&gt;

&lt;p&gt;It gets a little tricky here. If your wordpress was created AFTER version 2.5, then the account passwords will be hashed using a third-party library called “Phpass“. If they were created before, they will use an MD5 hash.&lt;/p&gt;

&lt;p&gt;Since you are more than likely upgraded to the current version (because you are an awesome and smart wordpress user), bear in mind that even if your old accounts were created prior to v2.5 (because you’re old school, too!), if those passwords are changed or if new users are created, the new passwords will be hashed with &lt;a href=&quot;http://www.openwall.com/phpass/&quot;&gt;Phpass&lt;/a&gt;. (If you login to your blog with your account, it will automatically update your password to the more secure encryption).&lt;/p&gt;

&lt;p&gt;Like I said, tricky.&lt;/p&gt;

&lt;p&gt;Either way, you’ll need Phpass for ruby. Fortunately, someone has already made a Gem for it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;phpass-ruby&lt;/code&gt;. Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem &apos;phpass-ruby&apos;&lt;/code&gt; to your Gemfile, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;, and you should be all set with that.&lt;/p&gt;

&lt;p&gt;MD5 digests can be done using&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Digest::MD5.hexdigest(&quot;plain text password&quot;)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Consider this an incomplete solution, yet at least a starting point.&lt;/p&gt;

&lt;p&gt;Add this block to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; model.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;check_password&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;plaintext_password&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;phpass&apos;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Phpass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plaintext_password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’m still researching the code to see if this will need to factor in the presence of any salts that you specify in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp-config.php&lt;/code&gt;. Supposedly those salts are factored into the passwords, but in &lt;a href=&quot;http://core.trac.wordpress.org/browser/tags/3.3.1/wp-includes/pluggable.php&quot;&gt;pluggable.php&lt;/a&gt;, the only place I see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wp_salt()&lt;/code&gt; being used is in cookies and nonces. All this means is that you shouldn’t have to do anything extra provided your password is updated.&lt;/p&gt;

&lt;h2 id=&quot;testing-it-out&quot;&gt;Testing it out&lt;/h2&gt;

&lt;p&gt;Once you’ve got that done. Test it out by jumping into your rails console.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Loading&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;development&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;last&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;002&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;check_password&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bad_password&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;003&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;check_password&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;good_password&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you cannot get the password to verify and you are absolutely positive you’re using the correct password, it likely means that your password is old-skool and needs to be updated. An MD5 password will appear in your database as only 0-9 and a-f, if you see any other characters, then it’s been modernized) You can make it hip by simply logging into your dashboard; WordPress will fix it when it checks.&lt;/p&gt;

&lt;p&gt;If you’re getting errors about a “table not existing”, you can check to see if it’s using the correct table_name with this console command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;irb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_name&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;wp_dev_users&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;My model took some tweaking here mainly because my host has a very dysfunctional relationship with its dev/test/prod environments.&lt;/p&gt;

&lt;h2 id=&quot;pitfall-of-belongs_to&quot;&gt;Pitfall of Belongs_to&lt;/h2&gt;

&lt;p&gt;I have tested it with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_many&lt;/code&gt; (which requires no modification to the table), but not with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;belongs_to&lt;/code&gt;. Since the latter requires that the table actually have an additional reference column for the foreign key, do this at your own risk. WordPress may modify the user table without you knowing and that could break your relationships.&lt;/p&gt;

&lt;p&gt;My suggestion would be to create a proxy ownership model (“liaison”?) that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;belongs_to&lt;/code&gt; your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; model. That proxy ownership model could then handle all other Rails relationships autonomously from the WordPress database. It makes your AREL a little less pretty (eg. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;some_group.liaisons.first.nicename&lt;/code&gt;, assuming a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Group&lt;/code&gt; model &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;has_many :liaisons&lt;/code&gt;)&lt;/p&gt;

&lt;h2 id=&quot;applying-it&quot;&gt;Applying it&lt;/h2&gt;

&lt;p&gt;In the real world context, you can now use it in whatever authentication framework you like. Since it’s using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord&lt;/code&gt;, you can employ it any place you would if it was in a Rails DB instead.&lt;/p&gt;

&lt;p&gt;Don’t forget you also have access to the other details in the WP user table, such as “nice name”, their e-mail, etc. If you want to include their profile details, you can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usermeta&lt;/code&gt; model:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt; 
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:usermeta&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Usermeta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Usermeta already contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user_id&lt;/code&gt; column in Rails-friendly format. You’ll have to write some model methods to extract all the data easily. I suggest making that one readonly as well.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2012/03/13/authenticating-against-wordpress-in-rails-3/</id>
      <updated>2012-03-13T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
    <entry>
      <title>Using Virtualbox with an Existing Partition</title>
      <link href="https://armahillo.dev/2010/01/27/using-virtualbox-with-an-existing-partition/" />
      <summary type="html">&lt;blockquote&gt;
  &lt;p&gt;N.B.
This is another throwback to an &lt;a href=&quot;https://web.archive.org/web/20160314074911/http://geekery.amhill.net/2010/01/27/virtualbox-with-existing-windows-partition/&quot;&gt;old blog I ran&lt;/a&gt;. It was the most-commented post on that blog, and a local friend recently found it useful, so I’m re-posting it!&lt;/p&gt;

  &lt;p&gt;I don’t know how specifically relevant it is, but feel free to contact me if you’ve found updates. I will credit you!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Last week, a friend of mine needed me to do an audio file conversion, but the app that I use is installed on my windows partition. I really don’t ever boot into Windows unless I have a good reason for it — I’m much happier tooling around in Linux — there’s just something satisfying and comfortable about being able to pop open a shell at any time.&lt;/p&gt;

&lt;p&gt;Anyways – it got me thinking: I’ve booted into a Windows XP image,  why can’t I use VirtualBox to boot from a whole partition? Surely that is possible…&lt;/p&gt;

&lt;p&gt;Tonight I finally got to play with it. And as you can see from the image here, I got success. :)&lt;/p&gt;

&lt;p&gt;It’s a little challenging, but it’s doable. I had to spend some time to iron out the kinks, but you can reap the benefits!&lt;/p&gt;

&lt;h3 id=&quot;legacy-update-notices&quot;&gt;Legacy Update notices&lt;/h3&gt;

&lt;p&gt;UPDATE: Sandeep has submitted screenshots with instructions on getting this to work with Windows 7, see below, at the very end.&lt;/p&gt;

&lt;p&gt;UPDATE: If you are getting the error message: Offset must be a number: rce
I have found the fix for it. See the instructions below.&lt;/p&gt;

&lt;p&gt;UPDATE: Bogdan (see comments) was able to get Windows Vista working under Virtualbox OSE, using the method below. See his comments for specifics on Windows Vista.&lt;/p&gt;

&lt;p&gt;UPDATE: Dan has found some tricks for getting this to work with Win7 if you are getting a BSOD on bootup.&lt;/p&gt;

&lt;p&gt;UPDATE: Julian has successfully gotten it to work with Windows 8 Pro, see below, near the end of the post content.&lt;/p&gt;

&lt;p&gt;UPDATE: user PCWorld has submitted some suggestions to get this to work with Windows 7&lt;/p&gt;

&lt;p&gt;UPATE: user ComputingFroggy has cross-posted some tips on Windows 7 integration with Lubuntu.&lt;/p&gt;

&lt;h3 id=&quot;install-virtual-box&quot;&gt;Install Virtual Box&lt;/h3&gt;

&lt;p&gt;Ok, for starters, you need the CLOSED SOURCE version of VirtualBox. As in — do not install the one from the Ubuntu Software Center. Go directly to &lt;a href=&quot;https://www.virtualbox.org/wiki/Linux_Downloads&quot;&gt;~Sun~ Oracle’s&lt;/a&gt; website and download the appropriate version for your OS.&lt;/p&gt;

&lt;p&gt;Ubuntu users should use the PPA instead of downloading the binaries. Be sure to check the link above for the most recent PPA. This sample applies to 12.04, Precise Pangolin, but there will quite possibly be a newer version on the website when you read this.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open up Ubuntu Software Center&lt;/li&gt;
  &lt;li&gt;Click on the Edit top menu, and select “Software Sources“&lt;/li&gt;
  &lt;li&gt;Choose the “Other Software” tab&lt;/li&gt;
  &lt;li&gt;Click on “Add…“&lt;/li&gt;
  &lt;li&gt;Paste: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deb http://download.virtualbox.org/virtualbox/debian precise contrib&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Click on “Add Source”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you’ll want to update your repo cache and get the key:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc &lt;span class=&quot;nt&quot;&gt;-O-&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-key add -
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then install the package of your choice.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;virtualbox-4.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Whether you do binary-based install or package based install — install the most recent version of the personal edition (NOT “OSE”, which is the open-source version.) As of this post, the current version for Precise Pangolin (12.04) is VirtualBox 4.2.&lt;/p&gt;

&lt;p&gt;The Virtualbox documentation also indicates that Ubuntu/Debian users should install the dkms package.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dkms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;install-mbr&quot;&gt;Install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mbr&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;In order to successfully trick Windows into booting into a confined space, you need to fake your mbr (no grub). Fortunately this is WAY easier than it sounds:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mbr
install-mbr &lt;span class=&quot;nt&quot;&gt;--f&lt;/span&gt; ~/.VirtualBox/FAKE.mbr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it! We’ll use that later.&lt;/p&gt;

&lt;p&gt;Your system will NOT be affected by this — all it does is copy the MBR from your computer, dump it into a file and that’s it.&lt;/p&gt;

&lt;p&gt;NOTE: Originally, I had used the full name flag (–force) which uses two dashes. Due to the way my stylesheet renders italicized fonts, it looked like it was a single dash, understandably confusing some people. If you are getting the error message:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;install-mbr: Offset must be a number: rce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then either use just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; instead, or ensure that you are using two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;s before “force”.&lt;/p&gt;

&lt;h3 id=&quot;debugging-an-issue-with-install-mbr-by-using-the--e1-option&quot;&gt;Debugging an issue with install-mbr by using the -e1 option&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;H/T to Ken D&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;install-mbr &lt;span class=&quot;nt&quot;&gt;-e1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt; ~/.VirtualBox/FAKE.mbr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;His windows partition is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda1&lt;/code&gt;, so if yours is not, you need to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-eN&lt;/code&gt;  where “N” is the number of the partition where windows is installed. Or not. (&lt;a href=&quot;https://manpages.ubuntu.com/manpages/focal/man8/install-mbr.8.html&quot;&gt;More info&lt;/a&gt;)&lt;/p&gt;

&lt;h3 id=&quot;create-a-vdmk-file&quot;&gt;Create a VDMK file&lt;/h3&gt;

&lt;p&gt;This is actually the trickiest part. A VDMK, which I am not sure what that is an acronym for, is essentially a micro-image that contains instructions to tell MBR where it’s booting from. If your computer is modern, your hard drives are likely SATA drives (and thus represented as /dev/sda). If you’re unsure, just go into a shell and type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fdisk -l&lt;/code&gt; (that last part is a “dash lowercase-L” not “dash one”). What you want is the device name for your harddrive… mine is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt;  — depending on how many hard drives you have and what type they are, it might be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/hda/&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sdb/&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;It’s worth noting that if you are currently mounting your windows partition in Linux (I do) so that you can access your Windows filesystem while in Linux, you will need to unmount (eg. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo umount /windows&lt;/code&gt;) it first.&lt;/p&gt;

&lt;p&gt;You’ll want to determine which partition contains Windows, so we can restrict Windows to ONLY using its own partition — this is actually a Linux-exclusive ability (the Windows version of Vbox can’t do this, because Windows is a wuss). So to do this, first you need to know what partition(s) Windows is on. This command will tell you:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;VBoxManage internalcommands listpartitions &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For the last part, use whatever you determined from above. It’s PROBABLY &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt; for you too, that’s pretty common.&lt;/p&gt;

&lt;p&gt;The command will produce output that looks something like this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VirtualBox Command Line Management Interface Version 3.1.2
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2009 Sun Microsystems, Inc.
All rights reserved.

Number  Type   StartCHS       EndCHS      Size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;  Start &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Sect&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
1       0x83  0   /1  /1   1023/254/63         53348           63
2       0x82  1023/254/63  1023/254/63          4000    109258065
3       0x83  1023/254/63  1023/254/63        155998    117451215
4       0x07  1023/0  /1   1023/254/63         91895    436935870
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;(&lt;em&gt;I have to admit, I feel a little naked showing the whole Internet my partition table&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;The last line is the one you want to look for, although yours may not be your last line. Whichever one(s) have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x07&lt;/code&gt; as their “Type” are the one(s) you want. Jot down the number(s) for those. (In this case, my number is just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now there’s one last thing we have to do that may make you a little uncomfortable, if you’re a paranoid person. We need to change the permissions on the hard drive device nodes. If you don’t know what that means, then you are probably not paranoid about that. :)&lt;/p&gt;

&lt;p&gt;Fortunately, we only need to change the permissions slightly, and only on the Windows partitions. So it’s not THAT big of a deal.&lt;/p&gt;

&lt;p&gt;In a terminal, type:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;666 /dev/sda
&lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;660 /dev/sda4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That second line should reflect whichever hard drives you are using (remember that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fdisk -l&lt;/code&gt; we did?) You’ll need to repeat the command for each hard drive that you want Windows to be able to access.&lt;/p&gt;

&lt;p&gt;For those of you that are paranoid: You should be able to change the permissions back to 600 after we’ve created the VDMK file — VirtualBox needs access to your partition table so it can do its job.&lt;/p&gt;

&lt;p&gt;There’s one last thing we need to do, and that’s adding you to the “disk” group, so you can access the partitions you just opened up. You’ll need to logout and log back in after doing this, so that your permissions are reset. (Previously forgot to include the username — thanks Ken!)&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-G&lt;/span&gt; disk yourusername
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, now you’re ready to actually create the VDMK file. Get ready because this is a handful.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 4 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First off, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda&lt;/code&gt; is whatever you determined you’re using, from earlier. It’s probably &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda&lt;/code&gt;, as noted.&lt;/p&gt;

&lt;p&gt;The number next to the partitions is the numbers you jotted down from the previous “listpartitions” command (the one where I said I felt naked). If you are using more than one partition, you will need to list them as comma-separated values. So if you wanted partitions “1” and “2” for Windows, you would replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4&lt;/code&gt; I wrote there with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1,2&lt;/code&gt;. Partitions 2, 3 and 4 would be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2,3,4&lt;/code&gt; and so on.&lt;/p&gt;

&lt;p&gt;The “partitions” parameter is what tells VirtualBox “only give Windows access to these partitions, and no where else!”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Got a Win7 64bit Home Professional, with an ACER Recovery Partition as partition 1, the “boot loader”(?) partition as 2, and the actual win7 partition as 3, so i did a “-partitions 1,2,3″. Had to use the .iso of the win7 recovery disc afterwards. &lt;em&gt;H/T to Heix&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just to break down what’s going on here:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;this portion runs the program to “Create Raw VMDK”. Self-explanatory. &lt;strong&gt;DO NOT USE SUDO HERE&lt;/strong&gt; (h/t to Javier, below!)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-filename ~/.VirtualBox/winxp.vmdk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;this is the output file that actually holds the micro-image. You can call it whatever you like, and you can put it whereever you like. If you have multiple Virtual Machines, you may like to file them away in a separate folder. Whatever you like.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-rawdisk /dev/sda -partitions 4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This tells it that the raw Windows installation is located on device &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt; and that you only want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;partition 4&lt;/code&gt; (yours may be different, as noted above)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-mbr ~/.VirtualBox/FAKE.mbr -relative
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-mbr&lt;/code&gt; parameter tells it to use that Fake MBR we created earlier. It won’t work without this. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-relative&lt;/code&gt; is a parameter that works in conjunction with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions&lt;/code&gt; parameter, to allow you to explicitly specify which partition you want to use.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-register
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This used to tell VirtualBox to register it in its database of available images, but has since been deprecated. Do not use this with version 4 of VBox. (Thanks, Ken D!)&lt;/p&gt;

&lt;p&gt;That’s it. Chances are, if you are going to get errors anywhere, it’s likely to be here.&lt;/p&gt;

&lt;p&gt;Possible causes, based on my experience and what I’ve read about on the Internet, is that you didn’t set permissions correctly, didn’t unmount your device, are using the Open-source edition (which doesn’t have createrawvdmk) instead of the closed-source personal edition. If you get an error, you can post it in a comment and I’ll see if I can help you, but google around — there are a LOT of resources online for this. Below, I’ll include links to the places I looked.&lt;/p&gt;

&lt;h3 id=&quot;load-your-new-vdmk-into-virtual-box&quot;&gt;Load your new VDMK into Virtual Box&lt;/h3&gt;

&lt;p&gt;Load up Virtualbox, if you installed it through the automated installation (rather than compiling it manually), you should see it by opening dash (click on the Unity icon) and typing “Virtual”. If you don’t see it there, try logging out and logging back in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/post_images/2010-01-27-Screenshot-Create-New-Virtual-Machine-300x228.png&quot; alt=&quot;Screenshot, described in text&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In VirtualBox, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;New&lt;/code&gt;, and follow the instructions for the first couple of screens. Name it whatever you like and select your OS (these instructions have all applied to Windows XP so far — caveat operor on any other versions / OSs). Once you get to the “Virtual Hard Disk” screen (pictured) select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Use existing hard disk&lt;/code&gt; and you should see the VDMK file we just created. If you don’t, click the little folder icon next to the drop down list. It will allow you to “add” your vdmk file that you created.&lt;/p&gt;

&lt;p&gt;Once that’s done, you’ll be back at the main screen.&lt;/p&gt;

&lt;p&gt;Click once on your new Virtual Machine, and click the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Settings&lt;/code&gt; icon at the top. You need to do a few more things first.&lt;/p&gt;

&lt;p&gt;Configuring your Virtual Machine
In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System&lt;/code&gt; area, be sure to check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enable IO APIC&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Motherboard&lt;/code&gt; tab. In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Processor&lt;/code&gt;, specify how many CPUs you want to use (ie. I had a Core 2 Duo, so I could choose more than one). You really only need 1 — if you’re doing stuff that requires more, you should probably boot into Windows natively.&lt;/p&gt;

&lt;p&gt;In the Acceleration tab — some CPUs have the native instructions built for virtualization — modern Intel CPUs do. If you paid a little extra for your CPU and you bought it in the past year or two, you might have these. I forget the specific models that have it, but it’s something that’s easy to overlook. :)&lt;/p&gt;

&lt;p&gt;Under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Display&lt;/code&gt; area, adjust the slider to specify how much Video RAM you want to give your VM. I picked &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;64MB&lt;/code&gt; for mine… very modest. You can also check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enable 2d Acceleration&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enable 3d acceleration&lt;/code&gt; simply because it won’t hurt to do so… but again, if you’re doing things that require hardware acceleration, a VM probably isn’t the best way to do it. :) (sidebar: with computing power as strong as it is nowadays, you can realistically run 3D accelerated games in a VM, provided you can devote enough resources to it)&lt;/p&gt;

&lt;p&gt;That’s it — the rest of the stuff is just icing. You can tinker with it if you want, but you’re good to go.&lt;/p&gt;

&lt;p&gt;Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt; to get out of there, then click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start&lt;/code&gt; (green arrow at the top). The first time you boot up, it may take a little while longer than usual.&lt;/p&gt;

&lt;p&gt;This is the other point that is prone to errors. The first time I did this, I had errors because I didn’t do the -partitions parameter in my VDMK creation, which gave me “disk read” errors. You might get a blue screen of death or some other nonsense. If you get any errors — google it. Like I said earlier, there’s lots of support for it.&lt;/p&gt;

&lt;h3 id=&quot;additional-notes-for-windows-7-users&quot;&gt;Additional Notes for Windows 7 Users&lt;/h3&gt;

&lt;p&gt;To anyone struggling to get this running with Windows 7, I might be able to save you some time:&lt;/p&gt;

&lt;p&gt;If Windows bombs straight to a blue screen of death on boot, in your VM go to settings-&amp;gt;storage. If your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vmdk&lt;/code&gt; file is listed under SATA, then remove the attachment, then add a new hard disk under IDE, selecting your vmdk file. Next – and this is the crucial bit, in the storage settings again, select the IDE controller, then change its type to ICH6.&lt;/p&gt;

&lt;p&gt;Save your settings, and your Windows 7 partition should boot – given you’ve followed all the previous instructions (particularly the windows 7 disc repair option part, which will get you past the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MBR 1FA&lt;/code&gt; problem.)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Dan (@lazydan) for the feedback!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://wiki.ubuntuusers.de/Dualboot-Windows_virtualisieren&quot;&gt;User-submitted Walkthrough for Windows 7 (German)&lt;/a&gt;
describes a solution for Windows 7 so that you won’t need to do a “Repair Installation” each time you want to switch from native to the VM and vice versa (you won’t need any Windows 7 Installation Disk at all)&lt;/p&gt;

&lt;p&gt;First, run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;fdisk &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;on your host system, you’ll need the value behind “Disk identifier:” of the hard drive your Windows 7 is on.
After you’ve created the VM (leave out the step telling you to boot from the Windows 7 Installation Disk), attach an Ubuntu (or any other Linux) live CD image to the VM:&lt;/p&gt;

&lt;p&gt;Settings -&amp;gt; Storage -&amp;gt; Controller: IDE -&amp;gt; Add CD Device (first icon) -&amp;gt; Choose Disk -&amp;gt; select the image of your live CD
Then boot the VM, it should boot the live CD image. Once you’re in there, enter:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;fdisk /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;to start fdisk’s interactive mode (replace /dev/sda with the hard drive your Windows 7 is on). First, enter&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;then&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;i
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Put in the disk identifier you’ve gathered in the first step, then press&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;w
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to rewrite the MBR and quit fdisk.&lt;/p&gt;

&lt;p&gt;After this, shut down the VM and remove the live CD image from the VM’s storage devices.
And note that the device name (e.g. /dev/sda) you have to use on the guest system might differ from its name on the host system.&lt;/p&gt;

&lt;p&gt;Now you should be able to boot Windows 7 both natively and through the VM without Windows complaining.&lt;/p&gt;

&lt;p&gt;To everyone else that has problems, I suggest you to read the wiki article linked at the top of my comment, it’s very comprehensive and has many troubleshooting tips at the end.&lt;/p&gt;

&lt;p&gt;UPDATE: For Windows 8 Professional users
Julian says:&lt;/p&gt;

&lt;p&gt;I’ve managed to get Windows 8 Pro (64bit) to boot in my 64bit version of Ubuntu 12.04.&lt;/p&gt;

&lt;p&gt;I had a few issues getting it booting to start with, but using the windows 8 installation disk to get into command prompt and run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; bootrec.exe /fixboot
 bootrec.exe /fixmbr
 bootrec.exe /rebuildBCD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;got this working. I couldn’t access my other SATA HDD that carries all my media, (And adding this as a separate vmdk in my virtual machine’s options made it borky and not detect the original Windows installation), but I’ll try and work it out.&lt;/p&gt;

&lt;p&gt;Once You’re In Windows
Some of the sites I’ve read have suggested creating an alternate hardware profile for when you boot into Vbox. This is a good idea if you plan on booting into it normally once in a while (for games or whatnot). To create a new Hardware Profile, right click on “My Computer” and click “Properties”. Select “Hardware” then at the bottom, select “Hardware Profiles”. Create a new one and name it “Raw Boot”, and rename the current one to “VBox Boot”.&lt;/p&gt;

&lt;p&gt;Windows will go through some growing pains this first boot sequence, as it maps all the hardware drivers to the VirtualBox extensions provided — You will likely need to restart. No guarantees on all the hardware working as it does in a native boot. You should be able to use things like Word, browse the web, use Photoshop (if you allocated enough RAM…you can always change that later), etc. Anything relatively lightweight should be fine.&lt;/p&gt;

&lt;p&gt;Working with Windows 7 (Update!)
One of the commenters below, Sandeep (“Sandy”), has figured out how to get a Windows 7 partition to work with this method. There are a couple small steps that need to be done, and he was cool enough to take screen shots for this blog:&lt;/p&gt;

&lt;p&gt;Before you begin, be sure your VDMK is configured, per the instructions above [screenshot]. You will also need to have the Windows 7 Rescue ISO loaded, which can be done within the VirtualBox configuration  (before starting a VM instance)
Once that’s ready, boot up the Windows  7 VM [screenshot] Sandeep is using a newer version of VirtualBox, apparently, but AFAIK, it should still work even if yours says “Sun” instead of “Oracle”
When prompted, boot to the rescue CD by pressing the appropriate key. [screenshot]
At the menu, select “Repair Installation” [screenshot]
Select “Repair &amp;amp; Restart” [screenshot]
I have not personally done this, but based on the comments below, this seems to work for most users. I gather that the problem is an issue of Windows 7 not liking being second banana in the MBR.&lt;/p&gt;

&lt;p&gt;I am pretty sure that if you alternately boot raw and boot virtual, you will have to do this rescue disk procedure each time. Be careful with doing a repair on a raw boot, though, if you use Grub. Currently, I do not know of any way to use the partition for both Windows 7 raw and virtual boots without using the rescue disc fix.&lt;/p&gt;

&lt;p&gt;Congratulations!
That’s it! Tinkering and optimizing aside, you’re done.&lt;/p&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further Reading&lt;/h3&gt;

&lt;p&gt;I would not have been able to do this without help from many other bloggers and hackers out there. Here are some of the resources I used to get this working:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;N.B.&lt;/em&gt; Some of these links may be dead now, but you may find them using &lt;a href=&quot;https://archive.org&quot;&gt;archive.org&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://ubuntuforums.org/archive/index.php/t-664692.html&quot;&gt;Trouble running VirtualBox with “real” partition&lt;/a&gt; (UbuntuForums)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.virtualbox.org/manual/UserManual.html&quot;&gt;The VirtualBox user manual&lt;/a&gt; (search for “vdmk”)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://mesbalivernes.blogspot.com/2008/01/virtual-box-booting-from-existing.html&quot;&gt;VirtualBox: How to boot from an existing Windows XP partition in Ubuntu&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blarts.wordpress.com/2007/12/06/how-to-run-virtualbox-using-a-physical-partition-using-ubuntu-feisty-fawn/&quot;&gt;How to Run Virtual Box using a Physical Partition in Feisty Fawn&lt;/a&gt; (an older distro, but the methods haven’t changed THAT much)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://forums.virtualbox.org/viewtopic.php?f=1&amp;amp;t=333&amp;amp;start=15&quot;&gt;Accessing RAW Partitions with VDMK&lt;/a&gt; (VirtualBox forums)&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;comments&quot;&gt;Comments&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;N.B.&lt;/em&gt; Including the comments from the original blog post, at it included some additional contributions, questions, ideas, and feedback:&lt;/p&gt;

&lt;h4 id=&quot;javier---february-2-2010&quot;&gt;Javier - February 2, 2010&lt;/h4&gt;

&lt;p&gt;I’m getting stuck at one point:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-G&lt;/span&gt; disk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m using ubuntu Karmic, and I get an error message:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;usermod: user ‘disk’ does not exist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Is there an equivalent user in 9.10?&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-2-2010&quot;&gt;Aaron - February 2, 2010&lt;/h5&gt;

&lt;p&gt;I did this in Karmic as well — the “disk” is actually a group, not a user. what was the command you typed, exactly? was it verbatim?&lt;/p&gt;

&lt;h5 id=&quot;javier---february-2-2010-1&quot;&gt;Javier - February 2, 2010&lt;/h5&gt;

&lt;p&gt;I copied and pasted from the article:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-G&lt;/span&gt; disk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Thanks for the quick answer!&lt;/p&gt;

&lt;h5 id=&quot;javier---february-2-2010-2&quot;&gt;Javier - February 2, 2010&lt;/h5&gt;

&lt;p&gt;Nevermind, I got past that. I added my user to the disk group using the user and group manager on gnome. However, now I’m stuck at adding the VDMK file: When I hit add, I find two files. I tried with both, and I get a&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;“Failed to open the hard disk /home/javierd/.VirtualBox/winxp-pt.vmdk.
Could not open the medium ‘/home/javierd/.VirtualBox/winxp-pt.vmdk’.
VD: error VERR_ACCESS_DENIED opening image file ‘/home/javierd/.VirtualBox/winxp-pt.vmdk’ &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VERR_ACCESS_DENIED&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;.”
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Best regards&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-2-2010-1&quot;&gt;Aaron - February 2, 2010&lt;/h5&gt;

&lt;p&gt;Could you paste the command you typed? I think I know what you did wrong, but knowing your command would help me know for sure. :)&lt;/p&gt;

&lt;p&gt;IIRC, the issue might simply be a permissions issue with your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sd*&lt;/code&gt;devices (your hard drives). Show me the command you used to create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VDMK&lt;/code&gt; and I should be able to identify the problem.&lt;/p&gt;

&lt;h5 id=&quot;javier---february-2-2010-3&quot;&gt;Javier - February 2, 2010&lt;/h5&gt;

&lt;p&gt;Nevermind that last comment too… :) Just a correction for this CLI:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 4 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Must be run WITHOUT the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;… otherwise, the permissions will be for root only 😉&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-2-2010-2&quot;&gt;Aaron - February 2, 2010&lt;/h5&gt;

&lt;p&gt;Ah!&lt;/p&gt;

&lt;p&gt;thank you — I will correct that above.&lt;/p&gt;

&lt;p&gt;cheers for that!&lt;/p&gt;

&lt;h5 id=&quot;javier---february-2-2010-4&quot;&gt;Javier - February 2, 2010&lt;/h5&gt;

&lt;p&gt;Sorry to pester again, but I run into another issue: My Windows now asks me for activation!! &amp;gt;:( I have tried manually assigning the MAC ID’s in order to have them match the same as the fisical ones… but no luck! :(&lt;/p&gt;

&lt;p&gt;Native XP still boots up without issues, only when I start it through VB I get prompted to activate it…&lt;/p&gt;

&lt;p&gt;Any ideas?&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-2-2010-3&quot;&gt;Aaron - February 2, 2010&lt;/h5&gt;

&lt;p&gt;hmm… I didn’t have that problem.&lt;/p&gt;

&lt;p&gt;Can you tell me a little more about your situation? You’re using an existing Windows XP installation, right?&lt;/p&gt;

&lt;p&gt;On my system, I had a Windows partition that I was already using for dual-booting — that’s like what you have, right?&lt;/p&gt;

&lt;h5 id=&quot;javier---february-3-2010&quot;&gt;Javier - February 3, 2010&lt;/h5&gt;

&lt;p&gt;Yes, exactly the same.&lt;/p&gt;

&lt;p&gt;Hmmm… It may have to do with licensing… mine is a OEM with a laptop. Maybe you have a retail version? For OEM, it checks some hardware serial numbers and uses that as a fingerprint for a machine… I’m guessing that because it doesn’t recognize the new values it believes I may have reinstalled it in another machine. I tried matching the MAC ID’s of the virtual interfaces, but didn’t work.&lt;/p&gt;

&lt;p&gt;The other thing I thought of, was having my VB use both processors (I have a core 2 duo laptop) instead of 1 as I did it at the beginning… but for some reason, it didn’t work with 2, only accepted one… I may just erase the windows partition and start running everything from VB, it may solve my problem, and I don’t really use Windows for much, just to access a Microsoft CRM web… ies4linux and IE with wine are not stable.&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-3-2010&quot;&gt;Aaron - February 3, 2010&lt;/h5&gt;

&lt;p&gt;Ahhhh… yeah — I have a retail (well, semi-retail — I bought it from my univ.) version.&lt;/p&gt;

&lt;p&gt;Not sure how to address that problem, but perhaps someone else on the Internet has had that experience? Windows is such a fickle beast w/r/t hardware and whatnot… it’s pretty ridiculous.&lt;/p&gt;

&lt;p&gt;The only time I ever really boot into windows is to play the 2 games I can’t get to work in Wine, use Reason 4, and use adobe CS4. that’s it. And I rarely do those things, even.&lt;/p&gt;

&lt;h4 id=&quot;balwinder-s-dheeman---february-6-2010&quot;&gt;Balwinder S Dheeman - February 6, 2010&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s|winxp|/HardDisks/&amp;amp;|g&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;matt---february-11-2010&quot;&gt;Matt - February 11, 2010&lt;/h4&gt;

&lt;p&gt;Great post, Aaron. I’ve had a similar setup on my desktop / workstation since I first installed Linux a couple years ago. I must admit, you make the process seem much easier than I remember it being! Definitely useful though, as I do have a couple programs that from time to time I need from that partition. I’ve created a “true” virtual XP machine for the one program I use most often, mainly because when you run the physical WinXP install you don’t get features like snapshots and saving the machine state.&lt;/p&gt;

&lt;p&gt;Javier, I’ve encountered the exact same problem as you. I did the same thing with my laptop, using the WinXP key it came with. For some reason it just doesn’t like being booted up both physically and virtually. It always asks for activation when I’ve switched from one to the other, even though I have set up separate hardware profiles. So frustrating. For a while I was having to call Microsoft each time (over some threshold I guess) but now I’ve gotten back to where I just tell it to activate each time and it works. Annoying, but usable in a pinch. My desktop (which has an OEM licensed but self-purchased version of XP) has never once had this problem. Go figure. Good luck!&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-11-2010&quot;&gt;Aaron - February 11, 2010&lt;/h5&gt;

&lt;p&gt;Matt:&lt;/p&gt;

&lt;p&gt;I can totally see why those features would be helpful — really the only reason I am doing this is so that I can pop into InDesign, Dreamweaver, or SoundForge in a pinch. :) I’ve heard that Vbox can translate 3d hardware to the vm, but never actually experimented with that yet — I’m kind of skeptical.&lt;/p&gt;

&lt;p&gt;I’m guessing that you and Javier’s problem with activation is that you were using an OEM copy of WinXP, rather than a purchased copy? The OEM copies tend to be nerfed slightly, since they’re subsisdized.&lt;/p&gt;

&lt;h4 id=&quot;peter---february-11-2010&quot;&gt;Peter - February 11, 2010&lt;/h4&gt;

&lt;p&gt;Hello Aaron:&lt;/p&gt;

&lt;p&gt;I followed all the steps trying to boot my Win7 partition with a few issues of typing but no big deals. The last 2 steps were:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;peter@Mymachine:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;install-mbr &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/.VirtualBox/FAKE.mbr
peter@Mymachine:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/win7.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
VirtualBox Command Line Management Interface Version 3.1.2
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2009 Sun Microsystems, Inc.
All rights reserved.

RAW host disk access VMDK file /home/phoboz/.VirtualBox/win7.vmdk created successfully.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When i get the VirtualBox GUI i only found an existing hard disk named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Win7.vdi&lt;/code&gt; so i took it and follow all the next steps.&lt;/p&gt;

&lt;p&gt;When i started the VM i get this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FATAL: No bootable medium found! System Halted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Could you tell me which is my mistake?&lt;/p&gt;

&lt;p&gt;Regards&lt;/p&gt;

&lt;h5 id=&quot;peter---february-11-2010-1&quot;&gt;Peter - February 11, 2010&lt;/h5&gt;

&lt;p&gt;Hello Aaron:&lt;/p&gt;

&lt;p&gt;I followed all the steps trying to boot my Win7 partition with a few issues of typing but no big deals. Some of the important steps were:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;peter@Mymachine:~&lt;span class=&quot;nv&quot;&gt;$VBoxManage&lt;/span&gt; internalcommands listpartitions &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda
VirtualBox Command Line Management Interface Version 3.1.2
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2009 Sun Microsystems, Inc.
All rights reserved.

Number Type StartCHS EndCHS Size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Start &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Sect&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
1 0x07 0 /32 /33 1023/254/63 84434 2048

peter@Mymachine:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;install-mbr &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/.VirtualBox/FAKE.mbr
peter@Mymachine:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/win7.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
VirtualBox Command Line Management Interface Version 3.1.2
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2009 Sun Microsystems, Inc.
All rights reserved.

RAW host disk access VMDK file /home/peter/.VirtualBox/win7.vmdk created successfully.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When i get the VirtualBox GUI i only found an existing hard disk named Win7.vdi so i took it and follow all the next steps.&lt;/p&gt;

&lt;p&gt;When i started the VM i get this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FATAL: No bootable medium found! System Halted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Could you tell me which is my mistake?&lt;/p&gt;

&lt;p&gt;Regards&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-11-2010-1&quot;&gt;Aaron - February 11, 2010&lt;/h5&gt;

&lt;p&gt;I don’t know how much help I can be, since I havent used Win7 at all — my first guess is that Win7 has different reqs than XP as far as the MBR is concerned; Windows tends to be very jealous and wants a monogamous relationship with your hard disks. XP wont run unless you spoof its presence in the mbr (as you’ve seen). If Win7 is even more jealous, then your best bet is to start googlin’! :) If you DO find anything interesting regarding this particular issue, I’d love to hear about it!&lt;/p&gt;

&lt;p&gt;On the off chance its something else, can you post the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fdisk -l&lt;/code&gt; for me? I might be able to help if I can see your partitiion table.&lt;/p&gt;

&lt;h5 id=&quot;peter---february-12-2010&quot;&gt;Peter - February 12, 2010&lt;/h5&gt;

&lt;p&gt;Tks Aaron!! Sure… here it is:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Disk /dev/sda: 120.0 GB, 120034123776 bytes
255 heads, 63 sectors/track, 14593 cylinders
Units &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; cylinders of 16065 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 512 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8225280 bytes
Disk identifier: 0x57c6d2a2

Device Boot Start End Blocks Id System
/dev/sda1 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 1 10764 86460806 7 HPFS/NTFS
/dev/sda2 10765 14593 30756442+ 5 Extended
/dev/sda5 10765 14429 29439081 83 Linux
/dev/sda6 14430 14593 1317298+ 82 Linux swap / Solaris

Disk /dev/sdb: 41.1 GB, 41110142976 bytes
255 heads, 63 sectors/track, 4998 cylinders
Units &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; cylinders of 16065 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 512 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8225280 bytes
Disk identifier: 0xa8418303
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Im googling and changing some parameters, if i success ill be happy to share… :)&lt;/p&gt;

&lt;h4 id=&quot;jeans---february-24-2010&quot;&gt;jean.s - February 24, 2010&lt;/h4&gt;

&lt;p&gt;Hey,&lt;/p&gt;

&lt;p&gt;Some users having problems to activating their windows-xp after migrating it to virtualbox due to connectivity problems. The original windows seems to remember the MAC-Address of your network adapter, be sure to set the mac-address of your virtualbox adapter to the one you physically have – worked for me.&lt;/p&gt;

&lt;p&gt;Another note: If you experience Bluescreen after migration, reboot into windows, apply the MergeIDE drivers to your current windows installation. See &lt;a href=&quot;http://www.virtualbox.org/wiki/Migrate_Windows&quot;&gt;MergeIDE&lt;/a&gt;. MergeIDE populates your windows installation with standard drivers which should work with Virtualbox. After having MergeIDE successfully installed, boot your windows with Virtualbox.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-24-2010&quot;&gt;Aaron - February 24, 2010&lt;/h5&gt;

&lt;p&gt;Awesome!&lt;/p&gt;

&lt;p&gt;Thanks for the notes, Jean — very helpful!&lt;/p&gt;

&lt;h4 id=&quot;bogdan---march-30-2010&quot;&gt;Bogdan - March 30, 2010&lt;/h4&gt;

&lt;p&gt;Thanks for this tutorial !!&lt;/p&gt;

&lt;p&gt;Based on it I was able to load an existing Windows Vista partition in VirtualBox OSE !!&lt;/p&gt;

&lt;p&gt;I don’t know why you said not to use OSE. I work to a company and OSE was so much required because it is free and even if it’s old it does it’s job.
Using grub image didn’t work for me (would receive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;“Unknown partition type 0x7″&lt;/code&gt;`) and I removed it from booting sequence. After creating the vmdk file I just power the machine (no grub).&lt;/p&gt;

&lt;p&gt;The only thing though I need to repair Vista boot with Vista’s CD using Repair option. Otherwise would say that it needs repaired and cannot boot. Another thing was that I needed to activate Windows again after booting in VirtualBox with the same key it was before (key written on my computer box). I had some problems (blue screens) with the programs that had attached all kinds of drivers to them (drivers that failed in VirtualBox). After removing them from the system Vista worked, I used them anyway on Linux.&lt;/p&gt;

&lt;p&gt;So, I have a Vista licensed activated and working in Linux. By the way I hate Windows…&lt;/p&gt;

&lt;h5 id=&quot;aaron---march-30-2010&quot;&gt;Aaron - March 30, 2010&lt;/h5&gt;

&lt;p&gt;Terrific! I’m glad you were able to do it!&lt;/p&gt;

&lt;p&gt;My reason for advising against OSE is for two reasons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Sun does not keep the OSE up to date as much as the closed source version, so it’s going to be older.&lt;/li&gt;
  &lt;li&gt;Options such as Guest Additions are not available in OSE (this is more of an extension of #1, since I think it’s simply a version issue.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;OSE &lt;em&gt;is&lt;/em&gt; perfectly functional, but unless you plan on hacking the code or modifiying it in some way, there’s really no reason to limit yourself to the OSE version.&lt;/p&gt;

&lt;p&gt;I think the reason you needed to repair the boot was because Vista is very jealous about the MBR — it doesn’t like not being top dog.&lt;/p&gt;

&lt;p&gt;Your hardware drivers issue:&lt;/p&gt;

&lt;p&gt;Try creating a separate hardware profile. I don’t know how to do it in Vista, but in XP it’s under My Computer -&amp;gt; Properties. This step is somewhat critical if you plan on being able to boot into Vista directly still — separate hardware profiles will keep the drivers that work fine in solo boot working, but also allow you to disable the drivers that DON’T work in a vbox boot.&lt;/p&gt;

&lt;p&gt;Glad to hear you got it working though, I’ll put a note up in the post.&lt;/p&gt;

&lt;h4 id=&quot;yazlanka---april-11-2010&quot;&gt;Yazlanka - April 11, 2010&lt;/h4&gt;

&lt;p&gt;My question for you is this: How do you make a fake MBR file in Fedora 12? There is no MBR program for it that I was able to find. I have a good feeling that’s why my Windows XP will not start. I get the windows splash, but then the blue crash screen seconds later. If you can tell me where to find a program to generate the FAKE.mbr file for Fedora, please let me know.&lt;/p&gt;

&lt;h5 id=&quot;aaron---april-17-2010&quot;&gt;Aaron - April 17, 2010&lt;/h5&gt;

&lt;p&gt;The program is pretty basic, you could &lt;em&gt;probably&lt;/em&gt; download the .deb of mbr from one of the debian / ubuntu repositories, then use “alien” (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yum install alien&lt;/code&gt;, I think) to convert it to an RPM.&lt;/p&gt;

&lt;p&gt;I haven’t used Fedora core since FC9, unfortunately, and I don’t have it installed on any systems right now. :/&lt;/p&gt;

&lt;p&gt;Try that out, though — I don’t think it should give you any problems. Let me know if you have any success — I’ll update the blog post with your findings. Thanks!&lt;/p&gt;

&lt;h4 id=&quot;funicorn---april-21-2010&quot;&gt;funicorn - April 21, 2010&lt;/h4&gt;

&lt;p&gt;I tried the above method and failed. All the preparations go well. And the MBR seems working because I can see the boot loader interface. But when booting the windows XP error shows up with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file ntldr&lt;/code&gt; and a standard error code &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0xc000000e&lt;/code&gt;. I think the reason is VM can not read any information from the VMDK file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;winxp.vmdk&lt;/code&gt;, because I am sure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ntldr&lt;/code&gt; file is there in the windows partition.&lt;/p&gt;

&lt;p&gt;I have both windows xp and win7 installed on different partitions, and use win7 boot loader. Could that be the reason ?&lt;/p&gt;

&lt;h5 id=&quot;aaron---april-21-2010&quot;&gt;Aaron - April 21, 2010&lt;/h5&gt;

&lt;p&gt;I don’t know that the instructions above will work for Windows 7, my guess is that they revised the bootloader for Windows 7 — since it’s not based on the 2000 kernel, that’s a pretty strong hunch. :)&lt;/p&gt;

&lt;p&gt;I unfortunately don’t have Windows 7, so I have no way of testing out a new solution for it. :/&lt;/p&gt;

&lt;p&gt;HOWEVER&lt;/p&gt;

&lt;p&gt;The bootloader is only necessary to fake the MBR for the win XP partition — I’m pretty sure you can fake it for just the XP partition and have Win7 be completely unaware of it. What is your partition scheme for your computer? I bet we can get a VBox working for you with WinXP, but I can’t promise anything for win7.&lt;/p&gt;

&lt;h5 id=&quot;funicorn---april-24-2010&quot;&gt;funicorn - April 24, 2010&lt;/h5&gt;

&lt;p&gt;OK. Here is my partition list&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Device Boot Start End Blocks Id System
/dev/sda1 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 1 1912 15358108+ 7 HPFS/NTFS
/dev/sda2 1913 35089 266494221 5 Extended
/dev/sda3 35090 38913 30716280 7 HPFS/NTFS
/dev/sda5 1913 15332 107796118 7 HPFS/NTFS
/dev/sda6 15333 17320 15968578+ 83 Linux
/dev/sda7 17321 21654 34812823+ 83 Linux
/dev/sda8 21655 21859 1646631 82 Linux swap / Solaris
/dev/sda9 21860 30384 68477030+ 7 HPFS/NTFS
/dev/sda10 30385 35089 37792881 83 Linux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda1&lt;/code&gt; is where windows xp is , and you see it’s also the default boot partition. I also have windows7 install in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda9&lt;/code&gt;, and the win7 loader is installed on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt; as the global boot loader, which means I have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt; installed on MBR.&lt;/p&gt;

&lt;p&gt;Actually thre is one thing I can not understand. I tried the above method and I can see the win7 boot loader interface. But I believe the real MBR in my disk should be the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt; boot code and it’s stage1. So where does this win7 loader boot interface come from ? Or maybe the real question is : what did this command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install-mbr -f ~/.VirtualBox/FAKE.mbr&lt;/code&gt; really do ?&lt;/p&gt;

&lt;h5 id=&quot;aaron---april-24-2010&quot;&gt;Aaron - April 24, 2010&lt;/h5&gt;

&lt;p&gt;I think the fake MBR file tricks Windows XP into thinking that it was booted up normally. IIRC, without the fake MBR it will begin booting but ultimately fail. The documentation for install-mbr said that it just copies your existing MBR into a file. VBox will then use that file to spoof the bootup process.&lt;/p&gt;

&lt;p&gt;It’s not like creating the VDMK where you have to specify the partitions and all — the fake MBR is basically a dummy file that simply needs to be there to reassure windows that “it’s ok, you’re not being booted into a virtual console, I promise,”&lt;/p&gt;

&lt;p&gt;As far as Win7 is concerned — I don’t know if this will work or not, particularly since it’s built off of a different kernel.&lt;/p&gt;

&lt;p&gt;One thing that puzzles me about your situation is that I think you said you are getting the boot loader (GRUB, right?) interface? I don’t get that when I do mine, even though I &lt;em&gt;do&lt;/em&gt; have GRUB loaded for conventional dual-booting. FWIW, My linux partition is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda1&lt;/code&gt; and winXP is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda4&lt;/code&gt;. I don’t know that will make a difference, though.&lt;/p&gt;

&lt;p&gt;The error code you’re getting sounds really familiar, and I wish could remember it.&lt;/p&gt;

&lt;p&gt;I just reviewed some of my resources and did a quick google search — you’re kind of pioneering new territory with the Win7 hybrid machine.&lt;/p&gt;

&lt;p&gt;How much different were the steps listed above from those that you had to take to get to where you’re at? Were there any deviations?&lt;/p&gt;

&lt;p&gt;when mine boots up, I see the Sun Vbox splash screen, then a black screen with “MBR” in the top left corner, then the windows XP loading splash — no grub or anything.&lt;/p&gt;

&lt;h5 id=&quot;funicorn---april-24-2010-1&quot;&gt;funicorn - April 24, 2010&lt;/h5&gt;

&lt;p&gt;First let me clarify some points&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;I’m using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt; as the default boot loader, and have windows xp ,win7 and Ubuntu installed.&lt;/li&gt;
  &lt;li&gt;When I boot in VM, I first see the black screen with “MBR”, then the win7 boot loader interface, not the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt;’s.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After reading your explanation I understand why that happens: The fake mbr boots the system to trigger the boot sector of the vmdk disk, which is exactly where win7 boot loader is located. In your case the fake mbr triggered the winxp boot loader, and if you only have one system installed, there should be no loader interface, you got into windows xp directly. If you add to the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;boot.ini&lt;/code&gt; another line of boot entry, I think you’ll also see the loader interface, i.e., the loader interface of windows xp.&lt;/p&gt;

&lt;p&gt;When I follow the steps, I do encountered one deviation. I ran the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo usermod -a -G disk&lt;/code&gt;, and the system said “no user disk”.
So I open the users-admin gui, and added myself into the disk group.
Everything else is OK.&lt;/p&gt;

&lt;h4 id=&quot;puzzled---june-4-2010&quot;&gt;Puzzled - June 4, 2010&lt;/h4&gt;

&lt;p&gt;At the very beginning, I am having difficulty with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install-mbr&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;install-mbr –force ~/.VirtualBox/FAKE.mbr

Offset must be a number: rce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;robert---june-25-2010&quot;&gt;Robert - June 25, 2010&lt;/h4&gt;

&lt;p&gt;Hey there, thanks a ton for the guide, and the laughs :) Only problem is I’m hitting a road block and am thinking it’s because Oracle might have changed VB around a bit since you wrote this? anyway, i’m pretty sure i’m doing everything right, but am getting this error:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Number Type StartCHS EndCHS Size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Start &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Sect&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
1 0x07 0 /1 /1 1023/254/63 152626 63
root@ubuntu:/home/owner# &lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;666 /dev/sda1
root@ubuntu:/home/owner# &lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;660 /dev/sda1
root@ubuntu:/home/owner# VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/wixxxxxnxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
WARNING: The vboxdrv kernel module is not loaded. Either there is no module
available &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;the current kernel &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2.6.32-22-generic&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; or it failed to
load. Please recompile the kernel module and &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;it by

&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; /etc/init.d/vboxdrv setup

You will not be able to start VMs &lt;span class=&quot;k&quot;&gt;until &lt;/span&gt;this problem is fixed.
WARNING: The compilation of the vboxdrv.ko kernel module failed during the
installation &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;some reason. Starting a VM will not be possible.
Please consult the User Manual &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;build instructions.
Oracle VM VirtualBox Command Line Management Interface Version 3.2.4
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2010 Oracle Corporation
All rights reserved.

RAW host disk access VMDK file /root/.VirtualBox/wixxxxxnxp.vmdk created successfully.

and upon following their instructions, &lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; /etc/init.d/vboxdrv setup, i get this error

Number Type StartCHS EndCHS Size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Start &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Sect&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
1 0x07 0 /1 /1 1023/254/63 152626 63
root@ubuntu:/home/owner# &lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;666 /dev/sda1
root@ubuntu:/home/owner# &lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;660 /dev/sda1
root@ubuntu:/home/owner# VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/wixxxxxnxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
WARNING: The vboxdrv kernel module is not loaded. Either there is no module
available &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;the current kernel &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;2.6.32-22-generic&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; or it failed to
load. Please recompile the kernel module and &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;it by

&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; /etc/init.d/vboxdrv setup

You will not be able to start VMs &lt;span class=&quot;k&quot;&gt;until &lt;/span&gt;this problem is fixed.
WARNING: The compilation of the vboxdrv.ko kernel module failed during the
installation &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;some reason. Starting a VM will not be possible.
Please consult the User Manual &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;build instructions.
Oracle VM VirtualBox Command Line Management Interface Version 3.2.4
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2010 Oracle Corporation
All rights reserved.

RAW host disk access VMDK file /root/.VirtualBox/wixxxxxnxp.vmdk created successfully.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;i think this is the good stuff from the log:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;echo ” ERROR: Kernel configuration is invalid.”;
echo ” include/linux/autoconf.h or include/config/auto.conf are missing.”;
echo ” Run ‘make oldconfig &amp;amp;&amp;amp; make prepare’ on kernel src to fix it.”;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;anyway, i’m looking forward to your response! hopefully sooner than later because i would love to see this baby work 😉&lt;/p&gt;

&lt;h5 id=&quot;aaron---june-25-2010&quot;&gt;Aaron - June 25, 2010&lt;/h5&gt;

&lt;p&gt;Hi! Glad you enjoyed it :)&lt;/p&gt;

&lt;p&gt;Ok — first off, which version of VBox are you using?&lt;/p&gt;

&lt;p&gt;I had to use Virtualbox closed-source, downloaded directly from them. If you are using the Vbox OSE package, I can’t guarantee this will work.&lt;/p&gt;

&lt;p&gt;Did you try doing: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo /etc/init.d/vboxdrv setup&lt;/code&gt; as it suggested? What was the result?&lt;/p&gt;

&lt;h5 id=&quot;robert---june-25-2010-1&quot;&gt;Robert - June 25, 2010&lt;/h5&gt;

&lt;p&gt;Nice! Wasn’t expecting such a timely response :)&lt;/p&gt;

&lt;p&gt;I am using this version downloaded direct from VB’s website, I believe this not to be the OSE edition as provided in the Ubuntu Software Repository. Specifically, this is what I installed: http://download.virtualbox.org/virtualbox/3.2.6/virtualbox-3.2_3.2.6-63112~Ubuntu~lucid_amd64.deb&lt;/p&gt;

&lt;p&gt;Second, this is the error in the log file:&lt;/p&gt;

&lt;p&gt;i think this is the good stuff from the log:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; ” ERROR: Kernel configuration is invalid.”&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; ” include/linux/autoconf.h or include/config/auto.conf are missing.”&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; ” Run ‘make oldconfig &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make prepare’ on kernel src to fix it.”&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

&lt;h5 id=&quot;aaron---june-25-2010-1&quot;&gt;Aaron - June 25, 2010&lt;/h5&gt;

&lt;p&gt;Let me check on that when I get home — I want to see what version I’m running. It sounds like it may just be a dependencies issue — my first guess is that you need the kernel-source package installed — If you check in synaptic for them, you should be able to load those.&lt;/p&gt;

&lt;p&gt;I’d start there. If it doesn’t fix the problem, no harm done — it’s just space on your drive. :)&lt;/p&gt;

&lt;h5 id=&quot;robert---june-25-2010-2&quot;&gt;Robert - June 25, 2010&lt;/h5&gt;

&lt;p&gt;I will definitely do some more strolling around the site sometime soon, I am sure to find some more awesome content :)&lt;/p&gt;

&lt;p&gt;So not that I am intending on being spoon fed or anything as I opened the Synaptic Package Manager and searched for kernel-source, but see a ton. Some make sense to me, like the ones with VSE, but I’m not sure which ones to enable/install.&lt;/p&gt;

&lt;p&gt;Thanks again&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-3-2010&quot;&gt;Aaron - July 3, 2010&lt;/h5&gt;

&lt;p&gt;The package you want is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux-source&lt;/code&gt;, sorry — Try installing that package (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get install linux-source&lt;/code&gt;) and that should do it. Let me know if it doesn’t work.&lt;/p&gt;

&lt;h4 id=&quot;stephen---july-1-2010&quot;&gt;Stephen - July 1, 2010&lt;/h4&gt;

&lt;p&gt;Aaron,
Thanks for publishing this, I’m running into the same problem as Puzzled in comment 31:&lt;/p&gt;

&lt;p&gt;At the very beginning, I am having difficulty with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install-mbr&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;install-mbr –force ~/.VirtualBox/FAKE.mbr

Offset must be a number: rce
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Could you let me know how to get around that?&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-3-2010-1&quot;&gt;Aaron - July 3, 2010&lt;/h5&gt;

&lt;p&gt;Stephen – please see my most recent comment below, or check the original article. I have updated it with the fix.&lt;/p&gt;

&lt;h4 id=&quot;shauna---july-3-2010&quot;&gt;Shauna - July 3, 2010&lt;/h4&gt;

&lt;p&gt;Hey there I’ve run into a problem when I try and set up the VMDK file. Here is the command I ran pretty sure its exactly the same as what you have listed above and then I also included the output:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda1 &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;

ERROR: VMDK: incorrect partition data area ordering &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;up by the &lt;span class=&quot;nb&quot;&gt;caller &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; ‘/home/shauna/.VirtualBox/winxp2.vmdk’
Error code VERR_INVALID_PARAMETER at /home/vbox/vbox-3.2.6/src/VBox/Devices/Storage/VmdkHDDCore.cpp&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3492&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in function &lt;/span&gt;int vmdkCreateRawImage&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VMDKIMAGE&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, VBOXHDDRAW&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, uint64_t&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Error &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;creating the raw disk VMDK: VERR_INVALID_PARAMETER
The raw disk vmdk file was not created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Thanks for any help you could provide.&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-3-2010-2&quot;&gt;Aaron - July 3, 2010&lt;/h5&gt;

&lt;p&gt;@Shauna - Can you please comment back with your partition table data? Type:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fdisk -l&lt;/code&gt; and paste the output here. If your partition table is different from mine (certainly possible), you will have to setup that VMDK file differently.&lt;/p&gt;

&lt;h4 id=&quot;aaron---july-3-2010-3&quot;&gt;Aaron - July 3, 2010&lt;/h4&gt;

&lt;p&gt;To everyone experiencing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offset must be a number: rce&lt;/code&gt; problem — I realized why this was:&lt;/p&gt;

&lt;p&gt;In my instructions, the font doesn’t make it clear, but you must use TWO dashes (-) before the “force” for it to work. I will update my instructions above to reflect that.&lt;/p&gt;

&lt;p&gt;Alteranately, and this is what I will change it to above, you can just use a single dash and use the “f” flag.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install-mbr -f ~/.VirtualBox/FAKE.mbr&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I just tested it and it works. :)&lt;/p&gt;

&lt;p&gt;What was happening was that it was seeing:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-force&lt;/code&gt; and interpreting it as if you had typed:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f -orce&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-o&lt;/code&gt; flag specifies an offset, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rce&lt;/code&gt; is clearly not a number.&lt;/p&gt;

&lt;p&gt;Hope that helps!&lt;/p&gt;

&lt;h4 id=&quot;shauna---july-3-2010-1&quot;&gt;Shauna - July 3, 2010&lt;/h4&gt;

&lt;p&gt;Here you go. Thanks&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Disk /dev/sda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; cylinders of 16065 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 512 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8225280 bytes
Sector size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;logical/physical&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
I/O size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;minimum/optimal&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
Disk identifier: 0x95aa95aa

Device Boot Start End Blocks Id System
/dev/sda1 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 1 5100 40959968+ 7 HPFS/NTFS
/dev/sda2 9025 9729 5654880 c W95 FAT32 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;LBA&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Partition 2 does not end on cylinder boundary.
/dev/sda3 5100 9025 31531009 5 Extended
/dev/sda5 5100 8858 30186496 83 Linux
/dev/sda6 8858 9025 1343488 82 Linux swap / Solaris
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;aaron---july-3-2010-4&quot;&gt;Aaron - July 3, 2010&lt;/h5&gt;

&lt;p&gt;Based on what I read on the Vbox forums here:
http://forums.virtualbox.org/viewtopic.php?p=26912&lt;/p&gt;

&lt;p&gt;It looks like you aren’t the only one with this problem. I &lt;em&gt;think&lt;/em&gt; the crux of your problem is the fact that you’re using an extended partition, but the note that “Partition 2 does not end on a cylinder boundary” seems to be more damning.&lt;/p&gt;

&lt;p&gt;See this comment from that link above, written by one of Vbox’s site moderators:
http://forums.virtualbox.org/viewtopic.php?p=26912&amp;amp;sid=5a7f106e3c227378f9323e67d9b51486#p26912&lt;/p&gt;

&lt;p&gt;The problem that you guys are having is actually because of a bug in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp&lt;/code&gt;. Function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;partRead&lt;/code&gt;builds an internal partition table from the partition sector and then adds the extended partitions. It then does a simple exchange sort to order these, checking for overlaps etc. as it goes (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lines 595-641&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;What it doesn’t do is to exclude the Extended Partition Entry from these checks, and of course this does overlap and start at the same place as other partitions, so of course this validation fails. So why doesn’t fail all the time? Ah, that’s because a second bug means that these checks aren’t always applied, so if the Extended Partition is the last one, we have a case of two wrongs making a right, or maybe in this case a write&lt;/p&gt;

&lt;p&gt;It’s a bug in VBox, I guess.&lt;/p&gt;

&lt;p&gt;As far as what you can do:&lt;/p&gt;

&lt;p&gt;You’re only &lt;em&gt;really&lt;/em&gt; using 4 partitions, so the extended partition is unnecessary — If you have the luxury of being able to dust off the whole drive and starting fresh, that would be ideal.&lt;/p&gt;

&lt;p&gt;If that’s &lt;em&gt;not&lt;/em&gt; the case, and you are able to backup / re-install Linux, what I’d suggest is to boot into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gParted&lt;/code&gt; [gparted.sourceforge.net], blow away partitions &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt;, then create partitions 3 (type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;83&lt;/code&gt;) and 4 (type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;82&lt;/code&gt;) as primary partitions.&lt;/p&gt;

&lt;p&gt;That &lt;em&gt;should&lt;/em&gt; fix the problem. :) Sorry it’s not something easier :/&lt;/p&gt;

&lt;h5 id=&quot;sandy---july-5-2010&quot;&gt;Sandy - July 5, 2010&lt;/h5&gt;

&lt;p&gt;I’m new to open source..&lt;/p&gt;

&lt;p&gt;I want to use my other partitions too while using virtual box…
will i be able to use the other partitions…&lt;/p&gt;

&lt;p&gt;PS:I have installed Windows 7 in virtual box
Currently using Ubuntu 10.04&lt;/p&gt;

&lt;p&gt;thnx in advance&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-5-2010&quot;&gt;Aaron - July 5, 2010&lt;/h5&gt;

&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; a way to do that, but it’s somewhat tricky, and potentially dangerous.&lt;/p&gt;

&lt;p&gt;The step where you create the VDMK, you can specify other partitions with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions&lt;/code&gt; flag. I’ve never done that PERSONALLY, but I think it would be something like: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions 1 2 3 4&lt;/code&gt; instead of just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions 4&lt;/code&gt; if you wanted the VBox guest to have access to partitions 1-4.&lt;/p&gt;

&lt;p&gt;the danger here is that you should &lt;em&gt;not&lt;/em&gt; give it access to to any partitions that are currently mounted — ie. do not specify your active linux partitions!&lt;/p&gt;

&lt;p&gt;If, for example, your partition table was something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1 – Linux Native
2 – Swap
3 – Linux Native
4 – [extended]
5 – NTFS (Win XP)
6 – FAT32 (Win 95)
7 – FAT16 (Win 3.x)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And you wanted the VBox session to have access to partitions 5, 6, and 7 — that’s probably OK to do. But do &lt;em&gt;not&lt;/em&gt; give it access to partitions 1-3 (assuming those are all mounted).&lt;/p&gt;

&lt;p&gt;If you are simply wanting to share files between Linux &amp;amp; windows, your best bet is to use the internal VBox “Share a folder” feature, then map a network drive within the Windows VBox session — that’ll allow you to easily copy stuff to/from the host OS (Linux). I strongly discourage mounting additional partitions unless they are absolutely necessary for the guest OS to function, however.&lt;/p&gt;

&lt;h4 id=&quot;gianpaolo---july-5-2010&quot;&gt;Gianpaolo - July 5, 2010&lt;/h4&gt;

&lt;p&gt;Hi Aaron,&lt;/p&gt;

&lt;p&gt;Great blog post! All of the steps work very smoothly, except for the step where I was supposed to add myself to the disk group- for some reason it complained there wasn’t a group called disk. I added myself via the gui group manager though. I’m getting back to linux after a long, dark night in Windows-land (long story), and bloggers like you are one of the reasons this has been easy and fun! Thanks for taking the time to share what you know.&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-5-2010-1&quot;&gt;Aaron - July 5, 2010&lt;/h5&gt;

&lt;p&gt;Glad it’s worked well for you so far!&lt;/p&gt;

&lt;p&gt;A number of people have had issues with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usermod&lt;/code&gt; command — the GUI group manager seems to be the more successful route to take; glad you were able to figure it out on your own. I’ll update my original post to reflect that.&lt;/p&gt;

&lt;p&gt;Oh, and be sure to look into WinE — it’s changed a &lt;em&gt;LOT&lt;/em&gt; in the past few years and with the easy installation of DirectX you can play many modern games in linux — they actually run better without all the Windows overhead. :)&lt;/p&gt;

&lt;h4 id=&quot;sandy---july-6-2010&quot;&gt;Sandy - July 6, 2010&lt;/h4&gt;

&lt;p&gt;WOW dat was a quick reply…
Basically all i want is to access all my installed softwares(Installed in WIndows 7) in the Windows7( guest OS), which will help me in not to reinstall the softwares again….&lt;/p&gt;

&lt;p&gt;hope u r getting my pt. :)&lt;/p&gt;

&lt;p&gt;i did tried d 2nd option dat u suggested(sharing) but came of no use…
&amp;amp; plz can u ellaborate on “then map a network drive within the Windows VBox session…”
thnx *_*&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-11-2010&quot;&gt;Aaron - July 11, 2010&lt;/h5&gt;

&lt;p&gt;Once you have the stuff in the original post set up (with your VDMK and all that), you can do this:&lt;/p&gt;

&lt;p&gt;There is an option inside of the Virtualbox software (load it in Linux from the main VirtualBox interface). Click on your Virtualbox guest OS (Windows XP in my case — Windows 7 in yours) and click “Settings”
then choose the last option, “Shared Folders”&lt;/p&gt;

&lt;p&gt;Click on the folder that has a “+” on it, on the right side, to add a shared folder. On my system, I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/aaron/Public&lt;/code&gt; — but you can use whatever you like (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/sandy/Documents/&lt;/code&gt; for example).
Once you’ve selected all the folders you want to share, click “OK” to get out of there.&lt;/p&gt;

&lt;p&gt;when you start the Virtual Machine (&lt;em&gt;not&lt;/em&gt; when you boot into Windows from a clean boot), you will see those shared folders under your Network Places — it should show up as if it were another computer on your home network. :)&lt;/p&gt;

&lt;p&gt;Which Linux OS are you using? Ubuntu? Fedora core? I can give you more specific information to help you if I know what you’re running. :)&lt;/p&gt;

&lt;h5 id=&quot;shauna---july-7-2010&quot;&gt;Shauna - July 7, 2010&lt;/h5&gt;

&lt;p&gt;Aaron,&lt;/p&gt;

&lt;p&gt;Thanks for all your help I went a head and blew away my linux and the extended partition as well here is what I get when I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fdisk -l&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Disk /dev/sda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; cylinders of 16065 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 512 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8225280 bytes
Sector size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;logical/physical&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
I/O size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;minimum/optimal&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
Disk identifier: 0x95aa95aa

Device Boot Start End Blocks Id System
/dev/sda1 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 1 5100 40959968+ 7 HPFS/NTFS
/dev/sda2 5100 9476 35155968 83 Linux
/dev/sda3 9476 9730 2034688 82 Linux swap / Solaris
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve tried to get the command to run:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda1 &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But I still get the same error output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Error code VERR_INVALID_PARAMETER at /home/vbox/vbox-3.2.6/src/VBox/Devices/Storage/VmdkHDDCore.cpp(3492) in function int vmdkCreateRawImage(VMDKIMAGE*, VBOXHDDRAW*, uint64_t)
Error while creating the raw disk VMDK: VERR_INVALID_PARAMETER
The raw disk vmdk file was not created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Anything else I could try?&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-11-2010-1&quot;&gt;Aaron - July 11, 2010&lt;/h5&gt;

&lt;p&gt;Yes, I see one tiny little thing you can do differently that might make it work:&lt;/p&gt;

&lt;p&gt;You typed:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda1 &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But try this instead (changing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda1&lt;/code&gt; to just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sda&lt;/code&gt;)&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You only need to specify the device itself for the “rawdisk” option — the “partitions” option takes care of specifying the partitions on that disk. :)&lt;/p&gt;

&lt;p&gt;Try that out — I bet it’ll work for you.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

&lt;h5 id=&quot;sandy---july-11-2010&quot;&gt;Sandy - July 11, 2010&lt;/h5&gt;

&lt;p&gt;Awesome tutorial….
Well For WIndows 7 users all you have to do is&lt;/p&gt;

&lt;p&gt;“To use your Windows Installer and select Repair option…”&lt;/p&gt;

&lt;p&gt;Nice work Aaron&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-11-2010-2&quot;&gt;Aaron - July 11, 2010&lt;/h5&gt;

&lt;p&gt;That will more than likely restore the actual MBR to boot Windows primarily, but will probably not help with getting the VirtualBox VDMK set up — what did you use that for, in your situation?&lt;/p&gt;

&lt;h5 id=&quot;sandy---july-11-2010-1&quot;&gt;Sandy - July 11, 2010&lt;/h5&gt;

&lt;p&gt;It works for me…&lt;/p&gt;

&lt;p&gt;Rebooted my system…&amp;amp; no changes were made to the actual MBR…&lt;/p&gt;

&lt;p&gt;I have a query &amp;amp; i want to post a screenshot of my query…&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-11-2010-3&quot;&gt;Aaron - July 11, 2010&lt;/h5&gt;

&lt;p&gt;I think you might be misunderstanding me —&lt;/p&gt;

&lt;p&gt;This post isn’t about doing a “dual boot” setup, where you have both Windows and Linux on the same computer. In those cases, yes, doing a “Repair windows installation” would help to fix the problem by resetting the MBR to be windows-friendly.&lt;/p&gt;

&lt;p&gt;This post is about using &lt;a href=&quot;virtualbox.org&quot;&gt;VirtualBox&lt;/a&gt; to run an existing Windows partition &lt;em&gt;while currently booted into Linux&lt;/em&gt;. ie. I boot into Linux, click a single icon in my applications menu, and then Windows boots up in a separate window. Check out here and here for examples of what I mean.&lt;/p&gt;

&lt;p&gt;Normally, you have to create a separate “disk image” to use with VirtualBox, but with the instructions above, you can use an existing partition completely, without modifying it.&lt;/p&gt;

&lt;p&gt;As far as how to post a screen shot — the comments area unfortunately doesn’t support uploading, but if you can find some way to post it online (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twitpic.com&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imageshack&lt;/code&gt;, and other online storage systems will all work) you can link to it from here and ask your question. :) I’d be happy to take a look!&lt;/p&gt;

&lt;h5 id=&quot;sandy---july-12-2010&quot;&gt;Sandy - July 12, 2010&lt;/h5&gt;

&lt;p&gt;http://picasaweb.google.com/sandyridgeracer/DropBox?authkey=Gv1sRgCJLU8Kbnt97oNA#5492938771966452978&lt;/p&gt;

&lt;p&gt;I want the VBox to utilize the whole screen..i.e. i dont want those white void spaces…is there any way by which i can chage the “resolution”.&lt;/p&gt;

&lt;p&gt;in previous posts I meant “to boot your windows7 installer dvd in VBox and then select repair ”&lt;/p&gt;

&lt;p&gt;:)&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-12-2010&quot;&gt;Aaron - July 12, 2010&lt;/h5&gt;

&lt;p&gt;Ah! Ok — glad we cleared that up. That’s good to know, I’ll add it to the original post above!&lt;/p&gt;

&lt;p&gt;As far as VBox utilizing the whole screen, there are a couple things you can try:&lt;/p&gt;

&lt;p&gt;– While in your session, click on “Machine” then “Fullscreen Mode”
– Install “Guest Additions” (requires you to &lt;em&gt;not&lt;/em&gt; use the OSE version), then enable “Seamless Mode” (also found under “Machine”&lt;/p&gt;

&lt;p&gt;Fullscreen mode may require Guest Additions to enable it, I’m not sure.&lt;/p&gt;

&lt;p&gt;Fullscreen mode will use up all your screen real estate, but it might take a couple minutes before the Windows desktop auto-resizes to fit the screen.&lt;/p&gt;

&lt;p&gt;Fullscreen mode works kind of like Remote Desktop in Windows — there’s a tab at the top that will emerge if you move your mouse to the top of the screen. You can also just press the Host key (Right-Ctrl on my system) and “F” at the same time, that’ll exit out of it.&lt;/p&gt;

&lt;p&gt;Hope that helps!&lt;/p&gt;

&lt;h5 id=&quot;sandy---july-12-2010-1&quot;&gt;Sandy - July 12, 2010&lt;/h5&gt;

&lt;p&gt;thnx a lot….&lt;/p&gt;

&lt;h4 id=&quot;sony-ak---july-30-2010&quot;&gt;Sony AK - July 30, 2010&lt;/h4&gt;

&lt;p&gt;I am using Ubuntu 10.04 that installed using Wubi on my Windows Vista (double booting). Now mostly I am working in Ubuntu and I want to access Windows Vista using VB. I just share Ubuntu and Windows on the same partition /dev/sda2 mounted to /host, so how can I run my existing Windows Vista using VB inside my Ubuntu? Please help :)&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-30-2010&quot;&gt;Aaron - July 30, 2010&lt;/h5&gt;

&lt;p&gt;My advice here will be somewhat limited as I have never &lt;em&gt;personally&lt;/em&gt; loaded Windows Vista in Linux VBox — but I know a couple of the commenters have — be sure to read through the comments for some extra insight. (Check Bogdan’s comment specifically)&lt;/p&gt;

&lt;p&gt;As far as how to do it:&lt;/p&gt;

&lt;p&gt;Start out by going through the steps listed above — AFAIK, the process of getting a Vista VDMK registered with Virtual box is nearly identical to doing it with Win XP except there are some last minute finessing steps w/r/t getting Vista to play nice with being second banana.&lt;/p&gt;

&lt;p&gt;Once your VDMK is registered with VBox, you can adjust the settings to establish a specific folder / network drive inside of your Vista guest session so that you can access the host (Linux) — this is useful for when you want to copy files into or out of the VBox guest session.&lt;/p&gt;

&lt;p&gt;Let me know where you’re at with the process. Don’t be too intimidated by it — the commands listed above aren’t really capable of inflicting any damage on your system. :) You can do it!&lt;/p&gt;

&lt;h4 id=&quot;priss---august-4-2010&quot;&gt;Priss - August 4, 2010&lt;/h4&gt;

&lt;p&gt;Hi, I tried everything and can’t get passed the winxp splash screen. It just freezes there, no error messages or anything. When I disabled the intelppm in the registry I got the blue loading bar to appear, but nothing happened. Any help for me?&lt;/p&gt;

&lt;h5 id=&quot;aaron---august-14-2010&quot;&gt;Aaron - August 14, 2010&lt;/h5&gt;

&lt;p&gt;You can’t get past the splash screen in a VBox boot? How long have you waited? For some reason, it occasionally takes me 5 minutes to boot completely — other times it’s much quicker.&lt;/p&gt;

&lt;p&gt;I recommend “Saving the Machine State” when you quit, rather than shutting down outright — although I don’t know what kind of effect that would have if you saved the machine state and then booted into it normally. I don’t &lt;em&gt;think&lt;/em&gt; it would be a problem, but I have not experimented with that yet.&lt;/p&gt;

&lt;p&gt;So for now — try booting it and just let it sit for a while — give it 10-15 minutes (go make some coffee or something :) and if nothing changes STILL, we can try something else.&lt;/p&gt;

&lt;p&gt;oh btw — be sure you “Create a new Hardware Profile” — I think it’s in the same area as Device Manager? (Right-click My Computer, select “Properties”, then “Add new hardware profile”). Name one of them “VirtualBoxBoot” and the other “NormalBoot” — set it to default to whichever one you are likely to use more often (I generally select the VBoxBoot one simply for convenience).&lt;/p&gt;

&lt;p&gt;VirtualBox uses some different hardware adapter configurations than a normal boot will, since it has some of its hardware virtualized. Having separate hardware profiles should decrease your boot time substantially, I think. You will need to boot into it successfully once before you can get the VBox configuration made though.&lt;/p&gt;

&lt;h5 id=&quot;martin---august-14-2010&quot;&gt;martin - August 14, 2010&lt;/h5&gt;

&lt;p&gt;yo, hey, nice tutorial so far, but for me it seems not to be as easy as it is for some guys. i have windows 7 on partitions 1, 2 and 3 and all commands in linux worked without any flaws. still i cant get past the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;windows-start-manager&lt;/code&gt; which appears directly after the mbr-screen.&lt;/p&gt;

&lt;p&gt;@ sandy: how did you repare inside the vb? thx&lt;/p&gt;

&lt;h5 id=&quot;aaron---august-14-2010-1&quot;&gt;Aaron - August 14, 2010&lt;/h5&gt;

&lt;p&gt;Make no mistake — it’s challenging for everyone. :) Success seems to be a direct function of persistence, in this case.&lt;/p&gt;

&lt;p&gt;IIRC, Sandy got it to work by booting the Virtual Machine and telling it to “Repair the MBR”? If we can get a final verified answer on this, I’ll add it to the original post.&lt;/p&gt;

&lt;h5 id=&quot;martin---august-14-2010-1&quot;&gt;martin - August 14, 2010&lt;/h5&gt;

&lt;p&gt;Well, reparing with a virtual cd-image works very well, as long as I stay inside the vb. Have to test starting the system from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt; though…&lt;/p&gt;

&lt;h5 id=&quot;aaron---august-14-2010-2&quot;&gt;Aaron - August 14, 2010&lt;/h5&gt;

&lt;p&gt;Martin — please post back here if it was successful, and if you remember, the exact steps you took — I’ll add it to the OP and credit you and Sandy for figuring it out. :)&lt;/p&gt;

&lt;h5 id=&quot;martin---august-14-2010-2&quot;&gt;martin - August 14, 2010&lt;/h5&gt;

&lt;p&gt;Hey Aaron, it’s not the final solution we hoped to find.&lt;/p&gt;

&lt;p&gt;But as long as you only want to migrate it’s what you need. If you still want to use it natively from time to time, then you will have to repair every time you change the way of starting the system.&lt;/p&gt;

&lt;p&gt;The steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Produce an iso-image from your recovery disk or get one in the net.&lt;/li&gt;
  &lt;li&gt;Go to the virtualbox settings, storage (I don’t know the exact name, have the german language installed), select the cd symbol, go to the drive dialog and choose your image.&lt;/li&gt;
  &lt;li&gt;Start the virtual machine –&amp;gt; a few options pop up, choose the one one top which is the repair option, restart and you are there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I started windows from scratch it updated the 27k entries in the registry and proposed to repair itself.&lt;/p&gt;

&lt;p&gt;The only thing which is left is to skip repairing and see if the virtual way is still working that way.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;

&lt;h5 id=&quot;aaron---august-14-2010-3&quot;&gt;Aaron - August 14, 2010&lt;/h5&gt;

&lt;p&gt;That’ll work for now – that sounds like what Sandeep did too. Will add to OP later today, thanks!&lt;/p&gt;

&lt;h5 id=&quot;martin---august-14-2010-3&quot;&gt;martin - August 14, 2010&lt;/h5&gt;

&lt;p&gt;“Use recovery tools that can help fix problems starting windows” 😀&lt;/p&gt;

&lt;h5 id=&quot;martin---august-14-2010-4&quot;&gt;martin - August 14, 2010&lt;/h5&gt;

&lt;p&gt;My persistence is at it’s end.&lt;/p&gt;

&lt;p&gt;It would be good to somehow find the diff between the windows-system before and after repairing, but I dont have the knowledge nor the time to do so atm.&lt;/p&gt;

&lt;p&gt;Greetings&lt;/p&gt;

&lt;h4 id=&quot;albert---august-17-2010&quot;&gt;Albert - August 17, 2010&lt;/h4&gt;

&lt;p&gt;Thanks a lot for your good, plain and so fun explanation, the better is that worked.&lt;/p&gt;

&lt;p&gt;The only one thing I made different was to make me a disk user:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo usermod -a -G disk&lt;/code&gt; says me that there wasn’t a user called : &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disk&lt;/code&gt; , but i made through the GUI .&lt;/p&gt;

&lt;p&gt;Well be sure whenever there is an award for better explanation on that subject I will vote you.&lt;/p&gt;

&lt;h5 id=&quot;aaron---august-17-2010&quot;&gt;Aaron - August 17, 2010&lt;/h5&gt;

&lt;p&gt;Other people have had trouble with that too, I think I will add some additional instructions to the post to cover that.&lt;/p&gt;

&lt;h4 id=&quot;pim---august-17-2010&quot;&gt;pim - August 17, 2010&lt;/h4&gt;

&lt;p&gt;Hello&lt;/p&gt;

&lt;p&gt;I tried this tutorial but once I have finished everything and I try to launch my windows 7 into VirtualBox, I only see :&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MBR 1FA:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What’s wrong ?
Thanks&lt;/p&gt;

&lt;h5 id=&quot;aaron---august-17-2010-1&quot;&gt;Aaron - August 17, 2010&lt;/h5&gt;

&lt;p&gt;Pim — Please see the previous comments by both Martin and Sandy — I think those will provide you with the info you need to solve it. :)&lt;/p&gt;

&lt;h4 id=&quot;sandy---august-17-2010&quot;&gt;Sandy - August 17, 2010&lt;/h4&gt;

&lt;p&gt;oops!!!&lt;/p&gt;

&lt;p&gt;Thanx to aaron, i hv been using windows 7 in the virtual box…&lt;/p&gt;

&lt;p&gt;One of the problem with this technique is dat u hv to keep on repairing..&lt;/p&gt;

&lt;p&gt;By repair I mean :suppose if u r using windows 7 in VB and den u plan to use the &lt;em&gt;actual&lt;/em&gt; windows installed(by rebooting and selecting windows 7 from d &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grub&lt;/code&gt;),&lt;/p&gt;

&lt;p&gt;den ur windows will automatically repair it once u boot it.&lt;/p&gt;

&lt;p&gt;Now one more scenario is dat u r using d &lt;em&gt;actual&lt;/em&gt; windows and u plan to use win 7 in VB den u hv to manually repair it.This is done by creating an WINDOWS INSTALLER ISO file and boot it in VB and boot windows in VB using dat ISO file and finally repair it.&lt;/p&gt;

&lt;p&gt;queries are welcome…
can also post screenshot if required…&lt;/p&gt;

&lt;h5 id=&quot;sandy---august-17-2010-1&quot;&gt;Sandy - August 17, 2010&lt;/h5&gt;

&lt;p&gt;@pim: just create an iso file of windows installer and boot it in your VB and repair it.This will fix the error… :)&lt;/p&gt;

&lt;p&gt;ur emal-id…&lt;/p&gt;

&lt;h4 id=&quot;mohammad---august-28-2010&quot;&gt;Mohammad - August 28, 2010&lt;/h4&gt;

&lt;p&gt;I tried this with windows 7 and got it working. Only had to repair once.
Also have successfully created an image from it and can use it as a regular vm image with everything in it. Thanks a lot for this post.&lt;/p&gt;

&lt;h4 id=&quot;ken-ng---september-23-2010&quot;&gt;Ken Ng - September 23, 2010&lt;/h4&gt;

&lt;p&gt;very nice tutorial! Thanks! :)&lt;/p&gt;

&lt;p&gt;just a small correction: missing user-name from usermod&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo usermod -a -G disk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;then perfect!&lt;/p&gt;

&lt;h5 id=&quot;aaron---september-23-2010&quot;&gt;Aaron - September 23, 2010&lt;/h5&gt;

&lt;p&gt;Thanks for the feedback, I’ve fixed it and credited you for the spot!&lt;/p&gt;

&lt;h4 id=&quot;dim---september-23-2010&quot;&gt;dim - September 23, 2010&lt;/h4&gt;

&lt;p&gt;First of all this is a great tutorial, the most informative in the web regarding how to boot an already installed windows partition while at the same time being able to boot it in the normal way when you want. I am also trying to implement this,however i get the following&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-register&lt;/span&gt;

Oracle VM VirtualBox Command Line Management Interface Version 3.2.8
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2010 Oracle Corporation
All rights reserved.

ERROR: VMDK: could not open raw partition file ‘/dev/sda1′
Error code VERR_DEV_IO_ERROR at /home/vbox/vbox-3.2.8/src/VBox/Devices/Storage/VmdkHDDCore.cpp&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3649&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in function &lt;/span&gt;int vmdkCreateRawImage&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VMDKIMAGE&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, VBOXHDDRAW&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, uint64_t&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Error &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;creating the raw disk VMDK: VERR_DEV_IO_ERROR
The raw disk vmdk file was not created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I am running Red Hat Enterprise 5.5, and i am trying to boot my native windows xp partition. (my hard drive is RAID with another hdd in a mirror configuration). Windows are indeed in partition sda1 while i have fixed the permission according to the instructions provided here, however i get the message above. Any ideas?&lt;/p&gt;

&lt;p&gt;thanks a lot&lt;/p&gt;

&lt;h5 id=&quot;aaron---september-24-2010&quot;&gt;Aaron - September 24, 2010&lt;/h5&gt;

&lt;p&gt;Dim, Thanks for stopping by!&lt;/p&gt;

&lt;p&gt;The first thing I would check is to see whether or not you have that partition already mounted. I forget if that makes a difference or not, but it’s worth trying.&lt;/p&gt;

&lt;p&gt;The second thing I would check is to (a) ensure that you have added yourself to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disk&lt;/code&gt; users group (or the equivalent for RH), and (b) that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disk&lt;/code&gt; users group (or its RH equivalent) has full access to that particular partition.&lt;/p&gt;

&lt;p&gt;It’s been a few years since I’ve used RH / FC, so the idiosyncratic differences between Deb/RH systems aren’t so fresh in my mind now. Check those things first then come on back. :)&lt;/p&gt;

&lt;h4 id=&quot;rodimus---september-30-2010&quot;&gt;Rodimus - September 30, 2010&lt;/h4&gt;

&lt;p&gt;To poster #9 ‘dim’&lt;/p&gt;

&lt;p&gt;I had the same error, and it was because I forgot to log out then log back in to reset my permissions. After that everything was good.&lt;/p&gt;

&lt;h4 id=&quot;bobc---october-19-2010&quot;&gt;BobC - October 19, 2010&lt;/h4&gt;

&lt;p&gt;WinXP is asking me to activate it again. Do I need to do this, or is there a way to make it see the activation I’ve been using for my physical boot?&lt;/p&gt;

&lt;h5 id=&quot;aaron---october-20-2010&quot;&gt;Aaron - October 20, 2010&lt;/h5&gt;

&lt;p&gt;Are you using the OEM version? I think some other people were having similar versions.&lt;/p&gt;

&lt;p&gt;You can try activating again, I don’t know firsthand what the consequence will be, however.&lt;/p&gt;

&lt;h4 id=&quot;marco---october-29-2010&quot;&gt;Marco - October 29, 2010&lt;/h4&gt;

&lt;p&gt;Aaron thanks a lot for this article.&lt;/p&gt;

&lt;p&gt;Everything went fine, including Windowns 7 32bit Rescue CD iso image.&lt;/p&gt;

&lt;p&gt;But when it goes to starting the Win7 itself, the startup crashes after the initial logo, gives a super fast green screen saing something about the display (wasn’t able to take the screenshot despite tried several times). I’ve tried changing display settings both on VB and Win7, no success :-(&lt;/p&gt;

&lt;p&gt;The strange thing is the rescue CD flows just fine in X mode&lt;/p&gt;

&lt;p&gt;Using Ubuntu 10.10, VB 3.2.10, Windows 7 Enterprise&lt;/p&gt;

&lt;p&gt;Any idea?&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;h4 id=&quot;karlabob---november-25-2010&quot;&gt;Karlabob - November 25, 2010&lt;/h4&gt;

&lt;p&gt;Hi!&lt;/p&gt;

&lt;p&gt;Thanks for this awesome and very easily readable guide. I have the same problem as pim with the text MBR 1FA, I have no ideal how to create a windows installer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.iso&lt;/code&gt; or a windows system rescue CD, any help?&lt;/p&gt;

&lt;h5 id=&quot;aaron---november-27-2010&quot;&gt;Aaron - November 27, 2010&lt;/h5&gt;

&lt;p&gt;Karlabob,&lt;/p&gt;

&lt;p&gt;Most CD Burning applications (Nero, Brasero, etc) should allow you to “create a disc image” of an existing CD. This is only really necessary if you are using Windows 7 (or, based on some comments I’ve read, Windows Vista). A Windows XP installation &lt;em&gt;shouldn’t&lt;/em&gt; need it, but it’s a good idea to have one around just in case anyways.&lt;/p&gt;

&lt;p&gt;Can you be more specific about the error you’re getting?&lt;/p&gt;

&lt;h4 id=&quot;randy---december-2-2010&quot;&gt;Randy - December 2, 2010&lt;/h4&gt;

&lt;p&gt;Your guide was very easy to follow and I received no errors until I tried to load the vmdk file in VirtualBox. I am new to this, so I may have miss something.&lt;/p&gt;

&lt;p&gt;I keep getting the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Failed to open the hard disk
/home/xxxxxx/.VirtualBox/HardDisks/winxp.vmdk.

Could not open the medium
‘/home/xxxxxx/.VirtualBox/HardDisks/winxp.vmdk’.

VD: error VERR_ACCESS_DENIED opening image file
‘/home/xxxxxx/.VirtualBox/HardDisks/winxp.vmdk’
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VERR_ACCESS_DENIED&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I believe my problem is that it appears to be two Virtual directories on my system. (not sure, but that is how it looks to me) if I issue a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.VirtualBox&lt;/code&gt; it takes me to a directory where the files I created are there. There appears to be a hidden directory under my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;home/xxxx/.VirtualBox/HardDisks&lt;/code&gt; it does not contain the files I created. I then went back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.VirtualBox&lt;/code&gt; and under that directory there is not HardDisks directory like the one VirtualBox sees when adding hard drives.&lt;/p&gt;

&lt;p&gt;Am I missing something from you guide? If I do have two different directories, what is the best way to correct?&lt;/p&gt;

&lt;h5 id=&quot;aaron---december-2-2010&quot;&gt;Aaron - December 2, 2010&lt;/h5&gt;

&lt;p&gt;Randy let me take a look at my configuration and ill get back to you here.&lt;/p&gt;

&lt;h4 id=&quot;ahmed---december-9-2010&quot;&gt;Ahmed - December 9, 2010&lt;/h4&gt;

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

&lt;p&gt;Professional Windows 7, 64 bit, existing installation running on Ubuntu Linux 10.04, 64 bit.&lt;/p&gt;

&lt;p&gt;To Aaron and Sandeep: &lt;em&gt;major MAJOR&lt;/em&gt; props.&lt;/p&gt;

&lt;h4 id=&quot;cris---january-7-2011&quot;&gt;Cris - January 7, 2011&lt;/h4&gt;

&lt;p&gt;Hi guys,&lt;/p&gt;

&lt;p&gt;amazing tutorial!! Got Win7 Home 32bit running within Ubuntu 10.10 ALMOST flawlessly.&lt;/p&gt;

&lt;p&gt;Almost? Here’s why…&lt;/p&gt;

&lt;p&gt;As I’m pretty new to Win7 I didn’t know that it creates it’s boot record on a partition seperate from the actual Windows partition. So what you actually have to do for creating a raw VMDK for a Win7 system is to integrate this pretty small boot partition to the vmdk file as well.&lt;/p&gt;

&lt;p&gt;For me for example the boot record was located on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda1&lt;/code&gt;, the Windows partition on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda2&lt;/code&gt;. When creating a vmdk only for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda2&lt;/code&gt; I could actually start the virtual machine and everythin with it, but when trying to repair the windows partition, the rescue CD said it wasn’t able to repair.&lt;/p&gt;

&lt;p&gt;Creating a VMDK for both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda2&lt;/code&gt; everything went fine, I could repair my Windows partition and run my Win7 virtual box.&lt;/p&gt;

&lt;p&gt;Now the only thing I have to do is see what happens if I reboot the whole system… Hopefully the Windows repair didn’t mess things up and destroy my GRUB or something…&lt;/p&gt;

&lt;p&gt;I’ll get back to you as soon as I tried this. Wish me luck and thanks again for the great tutorial!&lt;/p&gt;

&lt;h5 id=&quot;sudev---january-20-2011&quot;&gt;Sudev - January 20, 2011&lt;/h5&gt;

&lt;p&gt;You created two vmdk as two commands or one command?&lt;/p&gt;

&lt;p&gt;On by Netbook partition 1 is the small patition and then partition 2 is the main win7 partition&lt;/p&gt;

&lt;p&gt;I have tried using -partitions 1,2 parameter but no dice. Either I get failure with terminal showing MBR:&lt;/p&gt;

&lt;p&gt;Or I get Win7 repair disk message. Since the netbook does not have internal DVD/CDROM and external USB one is not getting recognised as a boot device I am stuck.&lt;/p&gt;

&lt;p&gt;On positive side Win7 continues to dual boot from grub.&lt;/p&gt;

&lt;h5 id=&quot;aaron---january-24-2011&quot;&gt;Aaron - January 24, 2011&lt;/h5&gt;

&lt;p&gt;IIRC, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions 1,2&lt;/code&gt; flag doesn’t create two VMDKs, it just specifies which partitions the VMDK should have access to. All partitions accessed must NOT be mounted at the time the VMDK is launched.&lt;/p&gt;

&lt;p&gt;Can you paste specifically what your error message was?&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

&lt;h5 id=&quot;alex---may-31-2011&quot;&gt;Alex - May 31, 2011&lt;/h5&gt;

&lt;p&gt;Here’s what I did for Win 7 Home Premium (64-bit running on a Lucid 32-bit PAE host) for VirtualBox 4.0.8 from Oracle (on an Asus UL30A, if that matters)&lt;/p&gt;

&lt;p&gt;Follow all steps above, except Instead of creating one VMDK, you create two for Win 7: One with an MBR, one without an MBR.&lt;/p&gt;

&lt;p&gt;For example, if your Win 7 partition is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda2&lt;/code&gt;, you would issue the following commands:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/HardDisks/Win7x64-mbr.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;AND&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/HardDisks/Win7x64-nombr.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, you attach the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Win7x64-mbr.vmdk&lt;/code&gt; as a primary IDE device, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Win7x64-nombr.vmdk&lt;/code&gt; as a primary SATA device (assuming you have a SATA controller, that is).&lt;/p&gt;

&lt;p&gt;Here’s how (potentially) to avoid the issue of needing to reactivate your Windows installation: http://forums.virtualbox.org/viewtopic.php?t=9697&lt;/p&gt;

&lt;p&gt;Follow the DMI decoding steps, then apply the DMI info to your virtual machine using the shell script there. It looked like it worked at first, but now I’m being told that my copy of windows is not genuine :(&lt;/p&gt;

&lt;p&gt;It seems that 4.0.8 is painfully slow compared to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vbox-ose 3.2.8&lt;/code&gt; using a raw disk partition. It took about 5 minutes for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;explorer.exe&lt;/code&gt; to launch after finally booting. (Then again, what should I expect from running a 64-bit VM on a 32-bit host with a measly SU7300 processor?)&lt;/p&gt;

&lt;p&gt;Of course, if anyone sees problems with using the same partition on two different controllers, please reply – I’m pretty sure that the IDE with an MBR just acts as a stepping stone to get the non-MBR SATA one working.&lt;/p&gt;

&lt;h4 id=&quot;cris---january-7-2011-1&quot;&gt;Cris - January 7, 2011&lt;/h4&gt;

&lt;p&gt;So, GRUB still there and switching between VirtualBox and DualBoot is fine.&lt;/p&gt;

&lt;p&gt;However there seems to be a problem with booting WIn7 directly from the scratch (=no virtualbox) when the VirtualBox Guest additions are installed. WIndows boots fine, however the mouse seems to stick to the lower left corner and after a minute or so Windows shuts down with a bluescreen of death.&lt;/p&gt;

&lt;p&gt;After booting from VirtualBox and uninstalling the guest additions however I could start Win7 from the scratch as if nothing happened… (apart from repairing of course 😉 )&lt;/p&gt;

&lt;p&gt;Did anyone else try to use the guest additions for such a dual boot configuration and run into similar problems? Not being able to use them seems like a pretty big deal breaker to me, but maybe there’s a way to circumvent this problem… Maybe it is possible to temporarily disable the additions before booting from the scratch or something?&lt;/p&gt;

&lt;h4 id=&quot;sudev---january-20-2011-1&quot;&gt;Sudev - January 20, 2011&lt;/h4&gt;

&lt;p&gt;Doing this for Win7&lt;/p&gt;

&lt;p&gt;Okay I get to boot but then Win asks for boot repair disk.&lt;/p&gt;

&lt;p&gt;Problem: I have a netbook as only way I can connect DVD drive is by USB cable. This is not recognised as boot device in BIOS.&lt;/p&gt;

&lt;h4 id=&quot;sandy---february-2-2011&quot;&gt;Sandy - February 2, 2011&lt;/h4&gt;

&lt;p&gt;Does anybody has windows 7 ultimate recovery iso file?(pls upload the file and post the link)&lt;/p&gt;

&lt;p&gt;I dont want to use my 2.3GB windows installation iso file…&lt;/p&gt;

&lt;h4 id=&quot;aaron---february-2-2011&quot;&gt;Aaron - February 2, 2011&lt;/h4&gt;

&lt;p&gt;Provided it’s not a copyright/license violation to post that ISO online, I would be happy to host it here and put the link in the main blog post.&lt;/p&gt;

&lt;p&gt;Anyone that has it should comment here with an email (put it in the email field) where they can be contacted and we can work out the logistics.&lt;/p&gt;

&lt;h4 id=&quot;michael---february-4-2011&quot;&gt;Michael - February 4, 2011&lt;/h4&gt;

&lt;p&gt;I did all the steps and set up the machine without any hiccups, until I tried running the machine. I am getting a terrible blue screen error on boot within VirtualBox. It’s issuing a stop command, which looks like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STOP: 0x0000007B (0xF78A6524, 0xC0000034, 0x00000000, 0x00000000)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I did a google search but couldn’t find anything concrete pertaining to virtualbox.&lt;/p&gt;

&lt;h5 id=&quot;aaron---february-6-2011&quot;&gt;Aaron - February 6, 2011&lt;/h5&gt;

&lt;p&gt;Hi Michael!&lt;/p&gt;

&lt;p&gt;Can you give us some more information about your setup? What’s the host OS (the one that’s running Virtualbox) and which Windows OS are you running as a guest OS? also — Are you using VirtualBox open source edition, or closed-source, and which version? Do you have anything funky with your partition table?&lt;/p&gt;

&lt;h4 id=&quot;deps---february-17-2011&quot;&gt;deps - February 17, 2011&lt;/h4&gt;

&lt;p&gt;Excellent tutorial!&lt;/p&gt;

&lt;p&gt;I’m trying to configure VB to run windows from ubuntu 10.4&lt;/p&gt;

&lt;p&gt;Initially I had windows installed, then I partitioned my hard drive to run ubuntu.&lt;/p&gt;

&lt;p&gt;I need to run windows software from ubuntu using VB.&lt;/p&gt;

&lt;p&gt;I went through the setup smoothly until I got to the repair phase. Everytime I click repair I get an error telling that my current iso image is not compatible with my windows installation. But I’m using my installation CD!&lt;/p&gt;

&lt;p&gt;Any help will be greatly appreciated :)&lt;/p&gt;

&lt;p&gt;I print my list of partitions:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Oracle VM VirtualBox Command Line Management Interface Version 3.2.12
&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;C&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 2005-2010 Oracle Corporation
All rights reserved.

Number Type StartCHS EndCHS Size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Start &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Sect&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
1 0x07 0 /32 /33 12 /223/19 100 2048
2 0x07 12 /223/20 1023/254/63 152485 206848
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;aaron---february-17-2011&quot;&gt;Aaron - February 17, 2011&lt;/h5&gt;

&lt;p&gt;What version of Windows are you using? This will matter greatly. Windows XP seems to be, in my experience, far easier (or at least far more cooperative) with this method than Windows 7 has been.&lt;/p&gt;

&lt;h4 id=&quot;tony---february-25-2011&quot;&gt;Tony - February 25, 2011&lt;/h4&gt;

&lt;p&gt;Great post!!! But I tryed everything and the best I acchieved was a Starting Windows screen running forever.. :S&lt;/p&gt;

&lt;p&gt;Any tip? I’m running dualboot with Windows 7 Pro 32bits and Ubuntu 10.04 32bits, with VirtualBox 4.0.4.&lt;/p&gt;

&lt;p&gt;Before you ask me to change the VB version I advice you that I’ve already it done… I tryed 3.12, 3.10 and 3.8 build releases, and OSEs too.&lt;/p&gt;

&lt;p&gt;Thanks,
Tony&lt;/p&gt;

&lt;p&gt;Here are some extra informations:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;fdisk &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; /dev/sda
Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders
Units &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; cylinders of 16065 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 512 &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 8225280 bytes
Sector size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;logical/physical&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
I/O size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;minimum/optimal&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
Disk identifier: 0x08000000
Device Boot Start End Blocks Id System
/dev/sda1 1 14 112423+ de Dell Utility
/dev/sda2 &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; 15 1292 10258432 7 HPFS/NTFS
/dev/sda3 1292 12118 86961668+ 7 HPFS/NTFS
/dev/sda4 12118 60802 391053313 5 Extended
/dev/sda5 12118 59676 382009344 83 Linux
/dev/sda6 59676 60802 9042944 82 Linux swap / Solaris

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;VBoxManage internalcommands listpartitions &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda
Number Type StartCHS EndCHS Size &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;MiB&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; Start &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Sect&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
1 0xde 0 /1 /1 13 /254/63 109 63
2 0x07 14 /5 /56 1023/254/63 10018 225280
3 0x07 1023/254/63 1023/254/63 84923 20742144
5 0x83 1023/254/63 1023/254/63 373056 194666496
6 0x82 1023/254/63 1023/254/63 8831 958687232
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;About partitions:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sda2 –&amp;gt; Windows 7 Pro 32bits (RecoveryImage)
sda3 –&amp;gt; Windows 7 Pro 32bits
sda5 –&amp;gt; Ubuntu 10.04 LTS 32bits
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;aaron---march-1-2011&quot;&gt;Aaron - March 1, 2011&lt;/h5&gt;

&lt;p&gt;Hi Tony!&lt;/p&gt;

&lt;p&gt;I really can’t make any warranty about this method working with Windows 7, unfortunately. I was able to get it successfully working with Windows XP, and it seems that some others have had luck with getting a Win7/Ubuntu hybrid going, but it’s rather inconsistent. Windows 7 is a bit more greedy w/r/t how it deals with booting virtually/normally.&lt;/p&gt;

&lt;p&gt;Have you tried Sandeep’s fix with the recovery disk?&lt;/p&gt;

&lt;h5 id=&quot;maho---march-4-2011&quot;&gt;Maho - March 4, 2011&lt;/h5&gt;

&lt;p&gt;Hi, I followed up all the instructions and everything looks OK. but right after the Boot window ( the one with the blue bar) I got a black screen and nothing happens.&lt;/p&gt;

&lt;p&gt;I’m using VB 4.0.4 on Ubuntu 10.10 and Windows XP as guest&lt;/p&gt;

&lt;p&gt;Any ideas?&lt;/p&gt;

&lt;h5 id=&quot;aaron---march-5-2011&quot;&gt;Aaron - March 5, 2011&lt;/h5&gt;

&lt;p&gt;How long did you wait? Sometimes it would take me as much as 5 or 10 minutes to boot into Win XP (either normally &lt;em&gt;OR&lt;/em&gt; vbox). Was there any feedback at all aside from the screen being black?&lt;/p&gt;

&lt;h5 id=&quot;maho---march-7-2011&quot;&gt;Maho - March 7, 2011&lt;/h5&gt;

&lt;p&gt;I leave it booting for a couple of hours.
Checking the logs I found this&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;aRC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;E_ACCESSDENIED
&lt;span class=&quot;nv&quot;&gt;aComponent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;Console&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aText&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;The virtual machine is being powered down&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;maho---march-7-2011-1&quot;&gt;Maho - March 7, 2011&lt;/h5&gt;

&lt;p&gt;More information I’ve got from logs&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VBox.log&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00:18:10.223 Changing the VM state from ‘DESTROYING’ to ‘TERMINATED’.
00:18:10.408 ERROR &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;COM]: &lt;span class=&quot;nv&quot;&gt;aRC&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;E_ACCESSDENIED &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0x80070005&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aIID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;515e8e8d-f932-4d8e-9f32-79a52aead882&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aComponent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;Console&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;aText&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;The virtual machine is not powered up&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, &lt;span class=&quot;nv&quot;&gt;preserve&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false

&lt;/span&gt;VBox.log.1
00:00:10.047 Guest Log: BIOS: Booting from Hard Disk…
00:00:16.709 RTC: &lt;span class=&quot;nv&quot;&gt;period&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x200 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;512&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 64 Hz
00:00:16.888 Display::handleDisplayResize&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;: uScreenId &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0, &lt;span class=&quot;nv&quot;&gt;pvVRAM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ae7e2000 &lt;span class=&quot;nv&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;640 &lt;span class=&quot;nv&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;480 &lt;span class=&quot;nv&quot;&gt;bpp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;nv&quot;&gt;cbLine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x140, &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x1
00:00:24.733 PIIX3 ATA: LUN#0: IDLE IMMEDIATE, &lt;span class=&quot;nv&quot;&gt;CmdIf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0xef &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; usec ago&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
00:00:24.733 PIIX3 ATA: LUN#0: aborting current &lt;span class=&quot;nb&quot;&gt;command
&lt;/span&gt;00:00:29.421 OHCI: Software reset
00:00:29.421 OHCI: USB Reset
00:00:29.421 OHCI: USB Operational
00:00:31.304 OHCI: USB Suspended
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VBox.log.2&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;01:52:36.581 Changing the VM state from ‘DESTROYING’ to ‘TERMINATED’.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;gary---march-11-2011&quot;&gt;Gary - March 11, 2011&lt;/h4&gt;

&lt;p&gt;THANKS! for the great instructions.&lt;/p&gt;

&lt;p&gt;I just installed virtual box 4.0.4 from the Oracle website.&lt;/p&gt;

&lt;p&gt;Make sure your type 7 partitions are not mounted and that the correcponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sd..&lt;/code&gt; devices have the permissions as specified in the instructions.&lt;/p&gt;

&lt;p&gt;I ran into problem when creating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vmmk&lt;/code&gt; file. My &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;D&lt;/code&gt; drive were partitions &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt;, so I thought that I could get away with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions 2,5&lt;/code&gt;. When I got all done I got a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MBR 25FA&lt;/code&gt; that I was not able to get it to boot from. But I also had a restore partition, partition 1, that was also type 7. So I created my vmdk again using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions 1,2,5&lt;/code&gt; and this worked great. So I would suggest using all of your type 7 partitions in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-partitions&lt;/code&gt; option or at least including the windows restore/diagnostic/whatever_it_is partition usually in partition 1.&lt;/p&gt;

&lt;p&gt;One thing you will notice is that there is no longer a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-register&lt;/code&gt; option to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VBoxMange&lt;/code&gt;. Just use the command in the instructions and leave this option off. I called my vmdk file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.VirtualBox/winxp.vmdk&lt;/code&gt; and noticed that it created this file along with another file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.VirtualBox/winxp-pt.vmdk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you get to the “Load your new VDMK” step you will need to use the browse option and select the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;winxp.vmdk&lt;/code&gt; file, not the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;winxp-pt.wmdk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This resulted in windows booting right into vVrtualBox.&lt;/p&gt;

&lt;p&gt;While booting I got a couple of “installing new hardware” messages and complaints about the video not being 32 bit. I would suggest ignoring all these and install the guest addtitions instead. Selecting “devices-&amp;gt;Install guest additions” from the VirtualBox menu. This will install drivers for your video (and something else I don’t remember) and thus clean up your device manager yellow “?” that result from the driver changes that running in VirtualBox requries will cause. It will set your resolution to 800×600, but in windows just right click on the background, choose properties and then settings and set the resolution to what you want it to be.&lt;/p&gt;

&lt;p&gt;If you need a “ctrl-alt-del” to login to windows, use the “Machine-&amp;gt;Insert Ctrl-Alt-Del” from the VirtualBox menu.&lt;/p&gt;

&lt;p&gt;I had previously run windows natively before I started the process and set up a second hardware profile for VirutalBox which I selected during boot. You do this by running “sysdm.cpl” from the run menu, selecting “hardware” and then “hardware profiles”. Use the “copy” button and copy your current profile and name the new one “virtualbox” or whatever you want. Then when windows boots in virtualbox, you will get a boot menu where you can chose the old one (which you use when you boot natively) or the “virtualbox” profile when you boot from VirtualBox. This keeps windows from wanting to switch your drivers back and forth. You can order your profiles using the up and down arrows on the “hardware profiles” pop-up and select the “select the first profile listed if I don’t select a profile” if you rarely use one of the profiles. It is safest to chose “Wait until I select a hardware profile” so that you won’t miss the menu and end up booting into the wrong profile and have windows mess with your drivers.&lt;/p&gt;

&lt;h4 id=&quot;steps---april-13-2011&quot;&gt;Steps - April 13, 2011&lt;/h4&gt;

&lt;p&gt;Hi there!&lt;/p&gt;

&lt;p&gt;First of all: great tutorial!&lt;/p&gt;

&lt;p&gt;But then: I followed your steps exactly, including the steps recommended for Windows 7 (as this is what I want to use in my VirtualBox, hosted by Ubuntu 10.10 64 bit), and finally got to boot the system. But right after the “Repair &amp;amp; Restart” part, I got a BSOD (which causes the system to reboot immediately) that won’t disappear (well, it re-appears every time I boot).&lt;/p&gt;

&lt;p&gt;I then tried to boot the system for real (I read somewhere that the MergeIDE-fix offered by VB could help), but the system seems to be broken beyond repair (the recovery won’t do anything).&lt;/p&gt;

&lt;p&gt;Can you please help me with that? Do you need any more info?&lt;/p&gt;

&lt;p&gt;Keep up the great work!&lt;/p&gt;

&lt;h4 id=&quot;heix---april-26-2011&quot;&gt;heix - April 26, 2011&lt;/h4&gt;

&lt;p&gt;Great tutorial! Worked like a charm.&lt;/p&gt;

&lt;p&gt;Got a Win7 64bit Home Professional, with an ACER Recovery Partition as partition 1, the “boot loader”(?) partition as 2, and the actual win7 partition as 3, so i did a “-partitions 1,2,3″. Had to use the .iso of the win7 recovery disc afterwards.&lt;/p&gt;

&lt;p&gt;Btw: On Kubuntu 10.10 64bit it seems to work with Virtualbox-OSE 3.2.8, no need to use the “non-OSE” .deb from virtualbox.org. Also, I didn’t do the chmod on the device nodes, only added myself to “disk”.&lt;/p&gt;

&lt;h5 id=&quot;aaron---april-26-2011&quot;&gt;Aaron - April 26, 2011&lt;/h5&gt;

&lt;p&gt;Thanks, Heix! I’ve added your comments to the guide, above!&lt;/p&gt;

&lt;h5 id=&quot;fran---may-10-2011&quot;&gt;Fran - May 10, 2011&lt;/h5&gt;

&lt;p&gt;when I try to boot appears black window saying:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Windows failed to start. A recent hardware or software change might be the cause. To fix the problem: ….
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My system uses 3 partitions, I assume 1 and 3 belongs to the HP recovery system. So I guess I may use the win7 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.iso&lt;/code&gt; recovery disc as you said.&lt;/p&gt;

&lt;p&gt;How should I do that?&lt;/p&gt;

&lt;h5 id=&quot;fran-0-may-10-2011&quot;&gt;Fran 0 May 10, 2011&lt;/h5&gt;

&lt;p&gt;Thanks for the tutorial&lt;/p&gt;

&lt;p&gt;My system have 3 windows partitions, two of them I guess should be the HP recovery system.&lt;/p&gt;

&lt;p&gt;I did not reach to boot, Windows said:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;“Windows failed to starts…
1. Insert your windows installation disc and restart
2. Choose your language settings
3. Click “repair your computer”
If you &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;not have this disc, contact your system adminstrator or computer manufacturer &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;assistance.
File: windwossystem32bootwinload.exe
Status: 0xc000035a
Info: Attempting to load a 64-bit application, however this CPU is not compatible with 64-bit mode.”
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Thanks in advance.&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-10-2011&quot;&gt;Aaron - May 10, 2011&lt;/h5&gt;

&lt;p&gt;To do the ISO method, you’ll need an ISO file of the Recovery disc, then specify to Vbox that you want to “load it” in the settings for your virtual machine (before starting the session).&lt;/p&gt;

&lt;p&gt;The “Info:” error at the bottom is puzzling, I’m not sure what that means, exactly, other than that Win7 is 64-bit and for some reason it thinks the virtualmachine is not.&lt;/p&gt;

&lt;p&gt;Bear in mind that when you boot into native Win7 (ie. not in a virtual machine) you will need to do the recovery disc thing again; it seems to require it anytime you switch the means which you boot into win7 with.&lt;/p&gt;

&lt;h4 id=&quot;quiman---may-2-2011&quot;&gt;€quiman - May 2, 2011&lt;/h4&gt;

&lt;p&gt;i had the same problem… i just restart and the problme it’s solved. Take a look again to your windows partition… and if they are mounted automatically, then unmount it.&lt;/p&gt;

&lt;p&gt;DP: Sorry my english isn’t vey good.&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-2-2011&quot;&gt;Aaron - May 2, 2011&lt;/h5&gt;

&lt;p&gt;It’s no problem, thanks for stopping by. :)&lt;/p&gt;

&lt;h4 id=&quot;josefrancisco---may-5-2011&quot;&gt;JoseFrancisco - May 5, 2011&lt;/h4&gt;

&lt;p&gt;Hi, I have installed suse 11.4 and vbox 4.0.6 r71344, as i didnt have install-mbr, I did the mbr file with&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo dd &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/dev/sda &lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/home/myuser/VirtualBox.mbr &lt;span class=&quot;nv&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;512 &lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1
&lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;666 /home/myuser/VirtualBox.mbr
&lt;span class=&quot;nb&quot;&gt;sudo chown &lt;/span&gt;myuser:users /home/myuser/VirtualBox.mbr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then i used it for the vmdk file and everything else, but when i start the winxp in virtualbox i just have black screen with&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Error No operating system
FATAL: INT18: BOOT FAILURE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;any idea pls?????&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-10-2011-1&quot;&gt;Aaron - May 10, 2011&lt;/h5&gt;

&lt;p&gt;Hola Jose,&lt;/p&gt;

&lt;p&gt;I’m really not sure how to fix this problem; I haven’t used Suse in a LONG time and haven’t experimented with methods outside of those listed in the original article. Perhaps another commenter will be able to pick that up, though?&lt;/p&gt;

&lt;h5 id=&quot;jaybofma---january-25-2012&quot;&gt;JayBofMA - January 25, 2012&lt;/h5&gt;

&lt;p&gt;@jose – Were you able to resolve the mbr issue under SUSE?&lt;/p&gt;

&lt;h4 id=&quot;john-westerdale---may-11-2011&quot;&gt;John Westerdale - May 11, 2011&lt;/h4&gt;

&lt;p&gt;Awesome tutorial, Aaron. When would you pick Wine vs Virtual box approach?&lt;/p&gt;

&lt;p&gt;Am assuming the VMDK-based OS can’t see the Ubuntu file systems.&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-11-2011&quot;&gt;Aaron - May 11, 2011&lt;/h5&gt;

&lt;p&gt;Wine is preferable, because it doesn’t block out system resources like VM’ing does, but it’s not always an option for certain software apps that require libraries that aren’t well-implemented yet.&lt;/p&gt;

&lt;p&gt;VM’ing Windoze as a guest OS works well when you have (a) an abundance of resources (ie 2 or more CPU cores, although Ideally you want at least 4) and (b) applications that can only natively run in Windows. You can configure most VM programs to make the host OS (Linux, in this case) expose one or more directories to the guest OS, often as “mapped drives” (allowing internal copying, which is much faster) — if you configure the network access for the guest OS to be bridged, the guest appears as its own entity on your network, and can also access any network shares on the guest OS, although it will do that via the router first.&lt;/p&gt;

&lt;p&gt;I’m also working on a tutorial for VMWare Express, which I’ve found to be a &lt;em&gt;little&lt;/em&gt; better performing than VBox.&lt;/p&gt;

&lt;h5 id=&quot;ted---may-3-2013&quot;&gt;Ted - May 3, 2013&lt;/h5&gt;

&lt;p&gt;We’ve been using this quite successfully, but would prefer VMware Player. Do you have any instructions for that? I found several dead links to an answer on advicesource.org, but those all depend on downloading a vmdk file that is no longer available. Any idea when you’ll have your guide ready?&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-3-2013&quot;&gt;Aaron - May 3, 2013&lt;/h5&gt;

&lt;p&gt;Ted,&lt;/p&gt;

&lt;p&gt;I would &lt;em&gt;think&lt;/em&gt; that you could use the instructions in this guide to generate the VMDK file, and then reference that with VM Ware Player? I don’t know for certain that all VMDKs are created equally, but it seems possible.&lt;/p&gt;

&lt;h4 id=&quot;brynley-mcdonald---may-15-2011&quot;&gt;Brynley McDonald - May 15, 2011&lt;/h4&gt;

&lt;p&gt;Whenever I try to add the vmdk to a new machine I get this&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Result Code: NS_ERROR_FAILURE &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;0x80004005&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
Component: Medium
Interface: IMedium &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;9edda847-1279-4b0a-9af7-9d66251ccc18&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
Callee: IVirtualBox &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;d2de270c-1d4b-4c9e-843f-bbb9b47269ff&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have no idea what it means. I’m using virtualbox on 11.04 and trying to get a Win 7 x64 image working. I’ve already tried making the image again.&lt;/p&gt;

&lt;h5 id=&quot;howard---may-15-2011&quot;&gt;Howard - May 15, 2011&lt;/h5&gt;

&lt;p&gt;I am also getting this error, attempting to load a 64-bit version of windows 7 on 11.04.&lt;/p&gt;

&lt;h5 id=&quot;howard---may-15-2011-1&quot;&gt;Howard - May 15, 2011&lt;/h5&gt;

&lt;p&gt;EDIT: I figured it out. Re-do the whole process from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 660, 600&lt;/code&gt; step. but make sure you’re not the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; user at any point.&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-16-2011&quot;&gt;Aaron - May 16, 2011&lt;/h5&gt;

&lt;p&gt;Howard, can you clarify what you mean by that? I will incorporate it into the original post. Do you mean that you did not use sudo? Or something else?&lt;/p&gt;

&lt;h5 id=&quot;stef---may-29-2012&quot;&gt;Stef - May 29, 2012&lt;/h5&gt;

&lt;p&gt;It seems that the device (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sdaX&lt;/code&gt;) that contains the Windows partition must have permission &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;660&lt;/code&gt;, and make sure your user is actually in the group &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disks&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls /dev/sd* -als&lt;/code&gt; to make sure of this.&lt;/p&gt;

&lt;h5 id=&quot;hari---february-3-2012&quot;&gt;Hari - February 3, 2012&lt;/h5&gt;

&lt;p&gt;I too get this same error. Anyone figured out a solution? Using Arch 64bit as host and XP 32bit as guest. VirtualBox is verson 4.1.8*&lt;/p&gt;

&lt;h5 id=&quot;mahkoe---july-4-2011&quot;&gt;Mahkoe - July 4, 2011&lt;/h5&gt;

&lt;p&gt;It says &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Invalid parameter ‘-register&apos;&lt;/code&gt;`.&lt;/p&gt;

&lt;p&gt;It also says (when I enter the create vmdk command without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-register&lt;/code&gt;, forcing me to create a new disk) that&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The medium ‘/home/username/.VirtualBox,winxp-pt.vmdk’ can’t be used as the requested device type.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also get this message for the other vmdk file.&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-4-2011&quot;&gt;Aaron - July 4, 2011&lt;/h5&gt;

&lt;p&gt;Did you use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;,&lt;/code&gt; in the filename? That might be why. Also please paste the full command you typed so i can help :)&lt;/p&gt;

&lt;h5 id=&quot;mahkoe---july-6-2011&quot;&gt;Mahkoe - July 6, 2011&lt;/h5&gt;

&lt;p&gt;No, that was just a typo in the comment. The whole command I used (to create the VMDK file) was&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; ~/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 1,2,3 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; ~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Neither of the two vmdk files created are accepted by vb.&lt;/p&gt;

&lt;h5 id=&quot;aaron---july-11-2011&quot;&gt;Aaron - July 11, 2011&lt;/h5&gt;

&lt;p&gt;hmm…. that’s really weird.&lt;/p&gt;

&lt;p&gt;Are you using a very recent version of VBox? Since Oracle bought out Sun, it’s possibly they’ve changed things around, although that would be really strange.&lt;/p&gt;

&lt;p&gt;You can certainly leave off the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-register&lt;/code&gt; flag and just register the VMDK with the VBox gui manually — though it looks like you’re still having problems even without that.&lt;/p&gt;

&lt;p&gt;One thing I find strange is that the error message is saying:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/username/.VirtualBox,winxp-pt.vmdk&lt;/code&gt; when your command was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.VirtualBox/winxp.vmdk&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the former, there is a “,” between “Box” and “winxp”, and also a “-pt” appended to “winxp”. Was that a typo or was that the actual output?&lt;/p&gt;

&lt;h5 id=&quot;göker-acar---july-13-2011&quot;&gt;Göker Acar - July 13, 2011&lt;/h5&gt;

&lt;p&gt;Same problem here&lt;/p&gt;

&lt;h5 id=&quot;raymond---march-27-2013&quot;&gt;Raymond - March 27, 2013&lt;/h5&gt;

&lt;p&gt;Same problem as well, except I’m using windows 7&lt;/p&gt;

&lt;p&gt;if i use the vdmk file without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-pt&lt;/code&gt; at the end I get this error:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The medium ‘/home/tasty/.VirtualBox/win7.vmdk’ can’t be used as the requested device type.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;if i use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pt&lt;/code&gt; vdmk file I get this error:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Failed to open the hard disk /home/tasty/.VirtualBox/win7-pt.vmdk.
Could not get the storage format of the medium ‘/home/tasty/.VirtualBox/win7-pt.vmdk’ &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VERR_NOT_SUPPORTED&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;raymond---march-27-2013-1&quot;&gt;Raymond - March 27, 2013&lt;/h5&gt;

&lt;p&gt;I figured it out, you get this error when you create a VDMK without logging in and out after changing your permissions.&lt;/p&gt;

&lt;h4 id=&quot;aman-sharma---august-22-2011&quot;&gt;aman sharma - August 22, 2011&lt;/h4&gt;

&lt;p&gt;thanks a lot for the post.&lt;/p&gt;

&lt;h4 id=&quot;jspelunker---august-30-2011&quot;&gt;Jspelunker - August 30, 2011&lt;/h4&gt;

&lt;p&gt;I’m using a 64-bit 10.04 Ubuntu host and attempted for two days the process described above for a 32-bit Vista guest, but all I get is a black screen of failure in starting the virtual machine in VirtualBox. Is it possible to virtualize a Vista OS?&lt;/p&gt;

&lt;h4 id=&quot;chrystian-townsley---september-24-2011&quot;&gt;Chrystian Townsley - September 24, 2011&lt;/h4&gt;

&lt;p&gt;Im also gettin the&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
FATAL: INT18: BOOT FAILURE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;error using debian and win7 ult x64&lt;/p&gt;

&lt;h4 id=&quot;ken---november-20-2011&quot;&gt;Ken - November 20, 2011&lt;/h4&gt;

&lt;p&gt;On Ubuntu 11.10 I get:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;install-mbr –-force ~/.VirtualBox/FAKE.mbr
Usage: install-mbr &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;options]
Try ‘install-mbr –help’ &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more information.

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;install-mbr –-force /tmp/FAKE.mbr
Usage: install-mbr &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;options]
Try ‘install-mbr –help’ &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;more information.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;aaron---november-21-2011&quot;&gt;Aaron - November 21, 2011&lt;/h5&gt;

&lt;p&gt;Be sure that you are using the correct number of dashes for the option flags. If you use the full name “force”, you need 2 dashes. If you use just “f”, you only use one dash.&lt;/p&gt;

&lt;h4 id=&quot;rob---december-26-2011&quot;&gt;Rob - December 26, 2011&lt;/h4&gt;

&lt;p&gt;Hi, tutorial goes well up until i try to create the VMDK, at which point I use the command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands createrawvmdk &lt;span class=&quot;nt&quot;&gt;-filename&lt;/span&gt; /home/gamedrift/.VirtualBox/winxp.vmdk &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda1 &lt;span class=&quot;nt&quot;&gt;-partitions&lt;/span&gt; 5 &lt;span class=&quot;nt&quot;&gt;-mbr&lt;/span&gt; /home/gamedrift/.VirtualBox/FAKE.mbr &lt;span class=&quot;nt&quot;&gt;-relative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and get the following result:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage: error: VMDK: incorrect partition data area ordering &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;up by the &lt;span class=&quot;nb&quot;&gt;caller &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; ‘/home/gamedrift/.VirtualBox/winxp.vmdk’
VBoxManage: error: Error code VERR_INVALID_PARAMETER at /home/vbox/vbox-4.1.8/src/VBox/Storage/VMDK.cpp&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;3656&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in function &lt;/span&gt;int vmdkCreateRawImage&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VMDKIMAGE&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, VBOXHDDRAW&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;, uint64_t&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
VBoxManage: error: Cannot create the raw disk VMDK: VERR_INVALID_PARAMETER
VBoxManage: error: The raw disk vmdk file was not created
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;My box is Gamedrift running Ubuntu 10.10
Closed source version in use.&lt;/p&gt;

&lt;p&gt;i look forward to your comments&lt;/p&gt;

&lt;h5 id=&quot;aaron---january-1-2012&quot;&gt;Aaron - January 1, 2012&lt;/h5&gt;

&lt;p&gt;Rob,&lt;/p&gt;

&lt;p&gt;I haven’t really been keeping up with VBox (see the top of the OP) so I’m not sure how to debug your problem. IIRC, though, you will want to make sure that the numeric argument for -partitions flag is the number of the partition where you have windows installed (ie. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5&lt;/code&gt; corresponds to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda5&lt;/code&gt;). I forget whether &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-rawdisk&lt;/code&gt; wants &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda1&lt;/code&gt; — you may want to try both.&lt;/p&gt;

&lt;h5 id=&quot;dan---january-5-2012&quot;&gt;Dan - January 5, 2012&lt;/h5&gt;

&lt;p&gt;To anyone struggling to get this running with Windows 7, I might be able to save you some time:&lt;/p&gt;

&lt;p&gt;If Windows bombs straight to a blue screen of death on boot, in your VM go to settings-&amp;gt;storage. If your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vmdk&lt;/code&gt; file is listed under SATA, then remove the attachment, then add a new hard disk under IDE, selecting your vmdk file. Next – and this is the crucial bit, in the storage settings again, select the IDE controller, then change its type to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ICH6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Save your settings, and your Windows 7 partition should boot – given you’ve followed all the previous instructions (particularly the windows 7 disc repair option part, which will get you past the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MBR 1FA&lt;/code&gt; problem.)&lt;/p&gt;

&lt;p&gt;Many thanks for this article and to everyone who has also contributed – all this plus a bit of fiddling finally got me there!&lt;/p&gt;

&lt;h5 id=&quot;aaron---january-5-2012&quot;&gt;Aaron - January 5, 2012&lt;/h5&gt;

&lt;p&gt;Thanks for the contribution, dan :) ill add this above to the OP!&lt;/p&gt;

&lt;h5 id=&quot;fuck---january-13-2012&quot;&gt;fuck - January 13, 2012&lt;/h5&gt;

&lt;p&gt;jesus fucking christ. everyone posts instructions for LINUX even though I searched WINDOWS. I want to mount a physical drive in virtual box running on windows in windows but there is no such thing as /dev/drive bullshit in windows. GOD DAMNIT&lt;/p&gt;

&lt;h5 id=&quot;aaron---january-16-2012&quot;&gt;Aaron - January 16, 2012&lt;/h5&gt;

&lt;p&gt;Hi there!&lt;/p&gt;

&lt;p&gt;Figure it out, piece together the instructions, and write your own how-to guide for windows users. Before this post, there really wasn’t any comprehensive instructions detailing all aspects of the process.&lt;/p&gt;

&lt;p&gt;I have no idea what is required to get it to work in windows, but I will assume that the process is roughly similar. I would probably start by looking through the VirtualBox application for something that lets you create a VMDK — you may need to do this at the command line, which surely has documentation somewhere.&lt;/p&gt;

&lt;p&gt;Good luck! If you do end up writing a post, please send me the link and I will link to you from this post. Thanks!&lt;/p&gt;

&lt;h4 id=&quot;maximiliano-carrizo---february-4-2012&quot;&gt;Maximiliano Carrizo - February 4, 2012&lt;/h4&gt;

&lt;p&gt;Very important issue:&lt;/p&gt;

&lt;p&gt;If you’re receiving this error message in boot:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;MBR
A disk &lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;error occurred
Press Ctrl+Alt+Del to restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should edit your generated file in step “Create a VDMK file” and change line:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ddb.geometry.biosHeads&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;”255″ to ddb.geometry.biosHeads&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;”240″
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Regards !&lt;/p&gt;

&lt;h4 id=&quot;ravi---february-7-2012&quot;&gt;Ravi - February 7, 2012&lt;/h4&gt;

&lt;p&gt;im hitting this error..&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The boot selection failed because required device is inaccesible
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I followed the steps mentioned above
let me know if there is any workaround for this problem&lt;/p&gt;

&lt;h5 id=&quot;ravi---march-6-2012&quot;&gt;Ravi - March 6, 2012&lt;/h5&gt;

&lt;p&gt;can someone help me with this error..
thanks in advance&lt;/p&gt;

&lt;h4 id=&quot;aaron---march-6-2012&quot;&gt;Aaron - March 6, 2012&lt;/h4&gt;

&lt;p&gt;Ravi, is the booting error occuring during VBox bootup or machine bootup?&lt;/p&gt;

&lt;h4 id=&quot;rick---february-18-2012&quot;&gt;Rick - February 18, 2012&lt;/h4&gt;

&lt;p&gt;You da man! This worked for me. I’m now running a WinXP VM (booted from an existing NTFS partition) in VBox on Linux. I have read and tried many other methods posted in Ubuntu and VBox forums, but this is by far the simplest.&lt;/p&gt;

&lt;p&gt;I did have to uninstall Symantec Endpoint Protection (SEP) to allow XP to boot in Normal mode. With SEP, it would boot in Safe mode, but would fail in Normal mode with exceptions in services.exe and lsass.exe. (Now to find Windows AV that is compatible with VBox!)&lt;/p&gt;

&lt;p&gt;My setup:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Toshiba Tecra M3 2Ghz single-core processor &amp;amp; 2 GB RAM&lt;/li&gt;
  &lt;li&gt;VMDK created with 2 NTFS partitions: C: &amp;amp; D:&lt;/li&gt;
  &lt;li&gt;Ubuntu Lucid Lynx 10.04 LTS&lt;/li&gt;
  &lt;li&gt;VirtualBox 4.1.8 r75467&lt;/li&gt;
  &lt;li&gt;Guest settings: 768 MB RAM, 64 MB video memory
– Motherboard Chipset: PIIX3, IO APIC enabled
– IDE Controller Type: PIIX3, Use host I/O cache&lt;/li&gt;
  &lt;li&gt;Guest OS: Windows XP Pro SP3&lt;/li&gt;
&lt;/ul&gt;

&lt;h5 id=&quot;aaron---february-23-2012&quot;&gt;Aaron - February 23, 2012&lt;/h5&gt;

&lt;p&gt;Rick&lt;/p&gt;

&lt;p&gt;Thanks for stopping by! Glad it was helpful for you — apologies for not keeping it current, but thankfully there are other VBox enthusiasts who are gracious enough to post their progress and hacks on here. :) If you had to overcome any difficulties or situations with any clever hacks or discoveries, please feel free to post here and I will add it (with attribution) to the OP above if I can fit it in.&lt;/p&gt;

&lt;p&gt;cheers!&lt;/p&gt;

&lt;h4 id=&quot;greg---march-20-2012&quot;&gt;Greg - March 20, 2012&lt;/h4&gt;

&lt;p&gt;Aaron: A million thanks to you and to everyone who contributed to these instructions as I finally got my VM up and running! These were the first instructions I found that were clear and didn’t leave pertinent details out.&lt;/p&gt;

&lt;p&gt;Gratefully,
Greg&lt;/p&gt;

&lt;h5 id=&quot;aaron---march-20-2012&quot;&gt;Aaron - March 20, 2012&lt;/h5&gt;

&lt;p&gt;Glad to hear it’s still working for people :)&lt;/p&gt;

&lt;p&gt;If you run into any other problems or difficulties, please feel free to post here and I will incorporate the changes into the document.&lt;/p&gt;

&lt;h4 id=&quot;justathought---may-21-2012&quot;&gt;Justathought! - May 21, 2012&lt;/h4&gt;

&lt;p&gt;Excellent tutorial! Thank you Aaron. I found this late, and it clarified some of the finer points I was missing from another explanatiion.&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-21-2012&quot;&gt;Aaron - May 21, 2012&lt;/h5&gt;

&lt;p&gt;Excellent! Glad you found it useful. If you discover anything I overlooked, please comment here and I will integrate it into the article.&lt;/p&gt;

&lt;h4 id=&quot;sean---june-20-2012&quot;&gt;Sean - June 20, 2012&lt;/h4&gt;

&lt;p&gt;I created the VMDK but when I goto create the virtual disk and point virtualbox to the VMDK, I get the following error:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Failed to open the hard disk /home/myusername/.VirtualBox/win7.vmdk
Permission problem accessing the file &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;the medium ‘/home/myusername/.VirtualBox/win7.vmdk’ &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;VERR_ACCESS_DENIED&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;a-specialist---august-2-2012&quot;&gt;A Specialist - August 2, 2012&lt;/h4&gt;

&lt;p&gt;You should update this so that, after adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disk&lt;/code&gt; group to the user, that they should log out and back in so that this change takes effect.&lt;/p&gt;

&lt;h5 id=&quot;jorge---august-31-2012&quot;&gt;Jorge - August 31, 2012&lt;/h5&gt;

&lt;p&gt;thanks! reloging solved this, you should put it into the tutorial :) thanks for the work Aaron&lt;/p&gt;

&lt;h5 id=&quot;aaron---september-3-2012&quot;&gt;Aaron - September 3, 2012&lt;/h5&gt;

&lt;p&gt;Can you be more specific about the problem you encountered and what you did to resolve it? I’m happy to include it, thanks!&lt;/p&gt;

&lt;h4 id=&quot;julian---october-10-2012&quot;&gt;Julian - October 10, 2012&lt;/h4&gt;

&lt;p&gt;Aaron, I love you. I haven’t hammered out all the problems yet, (e.g awful awful booting speeds) but I’ve managed to get Windows 8 Pro (64bit) to boot in my 64bit version of Ubuntu 12.04.&lt;/p&gt;

&lt;p&gt;I had a few issues getting it booting to start with, but using the windows 8 installation disk to get into command prompt and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootrec.exe&lt;/code&gt; /&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fixboot&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/fixmbr&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/rebuildBCD&lt;/code&gt; got this working. I couldn’t access my other SATA HDD that carries all my media, (And adding this as a separate vmdk in my virtual machine’s options made it borky and not detect the original Windows installation), but I’ll try and work it out.&lt;/p&gt;

&lt;p&gt;Thanks again!&lt;/p&gt;

&lt;h5 id=&quot;aaron---october-10-2012&quot;&gt;Aaron - October 10, 2012&lt;/h5&gt;

&lt;p&gt;Julian, that’s excellent!&lt;/p&gt;

&lt;p&gt;I will add your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootrec.exe&lt;/code&gt; command to the post — please post here with anything else you might find and I’ll incorporate it into the post.&lt;/p&gt;

&lt;p&gt;Thanks! :)&lt;/p&gt;

&lt;h5 id=&quot;julian---october-10-2012-1&quot;&gt;Julian - October 10, 2012&lt;/h5&gt;

&lt;p&gt;Aye of course, I shall. At the moment it’s completely broken it seems but I’m working on it. Also, just a heads up, the commands you quoted from me are to be executed seperately, e.g;&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bootrec.exe /fixmbr
bootrec.exe /fixboot
bootrec.exe /rebuildBCD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Just to avoid any confusion!&lt;/p&gt;

&lt;h5 id=&quot;aaron---october-10-2012-1&quot;&gt;Aaron - October 10, 2012&lt;/h5&gt;

&lt;p&gt;OK, thanks for the clarification — I haven’t used windows in a couple years now. :)&lt;/p&gt;

&lt;p&gt;I went through and updated the post with some newer info, providing PPA details instead of binary-only, and re-formatted the text to highlight the terminal commands better.&lt;/p&gt;

&lt;p&gt;Thanks for helping to keep this post alive. :)&lt;/p&gt;

&lt;h5 id=&quot;julian---october-10-2012-2&quot;&gt;Julian - October 10, 2012&lt;/h5&gt;

&lt;p&gt;No problem. Happy to help, and I hope this page stays exactly where it is because it’s been a lot of help to me too.&lt;/p&gt;

&lt;p&gt;On a related note, I managed to completely murder my bootsector and grub so much to the extent that I ended up reinstalling windows and ubuntu. I’m gonna play it much safer this time, though.&lt;/p&gt;

&lt;p&gt;Any ideas on how to give the initial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.vmdk&lt;/code&gt; access to an additional SATA drive?&lt;/p&gt;

&lt;p&gt;Oh, and one more thing, you MUST also include the ‘System Reserved’ partition in the vmdk file for win8 to boot.&lt;/p&gt;

&lt;p&gt;I’m pretty much done with this for now I think.&lt;/p&gt;

&lt;p&gt;I followed exactly the same steps as before, created the fake mbr, and the vmdk image, but also created an additional vmdk image for my shared drive. My windows image contained both the main body and the system reserved partition, and I executed the bootrec.exe commands to repair the mbr and managed to boot successfully and also access my shared drive. I cannot however boot natively into windows 8, as expected, but I hope some time in the future someone will be able to find a workaround for this, since repairing the installation and then also repairing grub every single time is not a valid option for me.&lt;/p&gt;

&lt;h5 id=&quot;gustavo---may-4-2013&quot;&gt;Gustavo - May 4, 2013&lt;/h5&gt;

&lt;p&gt;Hi Aaron,&lt;/p&gt;

&lt;p&gt;Thanks for this great tutorial. I have been struggling for the last days and I got to a very bizarre point.&lt;/p&gt;

&lt;p&gt;So I got a new laptop with Windows 8 and Ubuntu installed with dual boot. I followed the instructions, included the recover partition as Julian said, and I was stuck with the MBR FA error. I tried running my Win recovery CD in the virtualbox, but I does not recognize the data (to be precise, I ask virtualbox to boot from there and it says there are no boot files. It runs ok from raw outside any OS).&lt;/p&gt;

&lt;p&gt;I also tried mounting the recovery files in a USB pendrive, and creating a VDMK for this partition and adding it to the fake.mbr. This actually DID work, and I could start the repair mode, went to the command prompt option and run the three options for bootre.exe. After that, I could boot into Windows, but only is the VDMK for the USB exists (if I remove the USB or the VDMK drive I created onVB I get again the MBR FA error).
I also trying creating a partition with the content of the USB pendrive and a VDMK, but it does not work.&lt;/p&gt;

&lt;p&gt;So, Do you have any idea how can I run Win without the USB? The weird thing is that I have no way of getting again into the recovery mode in the USB. Besides, the dual boot still works perfectly (without any other change).&lt;/p&gt;

&lt;p&gt;Thanks&lt;/p&gt;

&lt;h5 id=&quot;aaron---may-9-2013&quot;&gt;Aaron - May 9, 2013&lt;/h5&gt;

&lt;p&gt;Gustavo,&lt;/p&gt;

&lt;p&gt;I wish I could be more helpful with this, but honestly, the last version of windows I used (and own a legit copy of) was XP. I know MS has changed a lot of things with how the FS works, particularly with regard to the MBR and booting process.&lt;/p&gt;

&lt;p&gt;As I essentially use Linux full time now, save for those extremely rare times when I boot into a pure VM to debug a web app in IE, I’m not sure I can be much help. :( perhaps other commenters might be more helpful?&lt;/p&gt;

&lt;h5 id=&quot;jacopo-alabiso---july-30-2013&quot;&gt;Jacopo Alabiso - July 30, 2013&lt;/h5&gt;

&lt;p&gt;Hi Julian, how did you finally manage to access the shared drive? I am booting win7. When booting nativelly, it can access the shared partition.
But when booting in VirtualBox, it says the disk is RAW, cannot access it (and neither can format it).&lt;/p&gt;

&lt;p&gt;Any clue on how to make it see the shared partition?
thanks!&lt;/p&gt;

&lt;h4 id=&quot;kowalikus---october-30-2012&quot;&gt;Kowalikus - October 30, 2012&lt;/h4&gt;

&lt;p&gt;Hi i’ve got BSOD with my win7 on ubuntu 12.10&lt;/p&gt;

&lt;p&gt;http://imgur.com/6NpkU&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;00:00:03.379450 Display::handleDisplayResize&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;: uScreenId &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0, &lt;span class=&quot;nv&quot;&gt;pvVRAM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0000000000000000 &lt;span class=&quot;nv&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;720 &lt;span class=&quot;nv&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;400 &lt;span class=&quot;nv&quot;&gt;bpp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;nv&quot;&gt;cbLine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x0, &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x1
00:00:08.499302 Display::handleDisplayResize&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;: uScreenId &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0, &lt;span class=&quot;nv&quot;&gt;pvVRAM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;00007f0de4000000 &lt;span class=&quot;nv&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1024 &lt;span class=&quot;nv&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;768 &lt;span class=&quot;nv&quot;&gt;bpp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;24 &lt;span class=&quot;nv&quot;&gt;cbLine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0xC00, &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x1
00:00:14.695398 RTC: &lt;span class=&quot;nv&quot;&gt;period&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x200 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;512&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 64 Hz
00:00:16.140139 AHCI#0: Reset the HBA
00:00:16.220738 Display::handleDisplayResize&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;: uScreenId &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 0, &lt;span class=&quot;nv&quot;&gt;pvVRAM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;00007f0de4000000 &lt;span class=&quot;nv&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;640 &lt;span class=&quot;nv&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;480 &lt;span class=&quot;nv&quot;&gt;bpp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;nv&quot;&gt;cbLine&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x140, &lt;span class=&quot;nv&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x1
00:00:16.859567 Reset initiated by ACPI
00:00:16.859671 Changing the VM state from &lt;span class=&quot;s1&quot;&gt;&apos;RUNNING&apos;&lt;/span&gt; to &lt;span class=&quot;s1&quot;&gt;&apos;RESETTING&apos;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
00:00:16.922379 CPUMSetGuestCpuIdFeature: Enabled APIC
00:00:16.922473 CPUMClearGuestCpuIdFeature: Disabled x2APIC
00:00:16.922532 PIT: &lt;span class=&quot;nv&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x10000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;65536&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; - 18.20 Hz &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
00:00:16.970444 AHCI#0: Reset the HBA
00:00:16.970530 PIIX3 ATA: Ctl#1: finished processing RESET
00:00:16.970637 PIIX3 ATA: Ctl#0: finished processing RESET
00:00:16.970803 PDMR3Reset: after    48 ms, 1 loops: 1 async tasks - piix3ide/0
00:00:16.971010 TM: Aborting catch-up attempt on reset with a 89 839 813 ns lag on reset&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; new total: 89 839 813 ns
00:00:16.971065 Changing the VM state from &lt;span class=&quot;s1&quot;&gt;&apos;RESETTING&apos;&lt;/span&gt; to &lt;span class=&quot;s1&quot;&gt;&apos;RUNNING&apos;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
00:00:16.975508 Guest Log: BIOS: VirtualBox 4.2.4
00:00:16.975688 PIT: &lt;span class=&quot;nv&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x10000 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;65536&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; - 18.20 Hz &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ch&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
00:00:16.986508 PIIX3 ATA: Ctl#0: RESET, &lt;span class=&quot;nv&quot;&gt;DevSel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;nv&quot;&gt;AIOIf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;nv&quot;&gt;CmdIf0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x34 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; usec ago&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;CmdIf1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0x00 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt; usec ago&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;aaron---november-17-2012&quot;&gt;Aaron - November 17, 2012&lt;/h5&gt;

&lt;p&gt;Hi Kowalikus,&lt;/p&gt;

&lt;p&gt;Win7 is kind of pioneer territory with this. I just had some new comments come in with info, I’m going to integrate it into the main guide. Perhaps that will help?&lt;/p&gt;

&lt;h4 id=&quot;charles-kozler---november-3-2012&quot;&gt;Charles Kozler - November 3, 2012&lt;/h4&gt;

&lt;p&gt;Hi Aaron,&lt;/p&gt;

&lt;p&gt;I am wondering – if I boot virtual and have to do the rescue disk option it is probably safe to assume that I will need to do the rescue disk &lt;em&gt;again&lt;/em&gt; if I boot raw, correct? You seem to make that implication in your post but I just want to be 100% clear before I go this route. I don’t boot in to windows often but when I do it is for gaming and I also do not want it corrupting grub every single time either. Can you tell me the possible overhead for someone who would boot consistently between virtual and raw? Would it be worth doing this or no because you have to effectively repair it each time?&lt;/p&gt;

&lt;p&gt;Also, I would like to be clear – if I have to only do the rescue option on &lt;em&gt;only&lt;/em&gt; virtual boots then I am ok with that but I want to understand the impacts it may have on a raw boot (eg: conflict with grub, have to do a repair each raw boot as well as each virtual boot, etc…)&lt;/p&gt;

&lt;p&gt;Thanks again for the tutorial!&lt;/p&gt;

&lt;h4 id=&quot;pcworld---november-17-2012&quot;&gt;pcworld - November 17, 2012&lt;/h4&gt;

&lt;p&gt;(Use at your own risk.)
http://wiki.ubuntuusers.de/Dualboot-Windows_virtualisieren (translated: http://translate.google.de/translate?sl=auto&amp;amp;tl=en&amp;amp;js=n&amp;amp;prev=_t&amp;amp;hl=en&amp;amp;ie=UTF-8&amp;amp;layout=2&amp;amp;eotf=1&amp;amp;u=http%3A%2F%2Fwiki.ubuntuusers.de%2FDualboot-Windows_virtualisieren&amp;amp;act=url) describes a solution for Windows 7 so that you won’t need to do a “Repair Installation” each time you want to switch from native to the VM and vice versa (you won’t need any Windows 7 Installation Disk at all):&lt;/p&gt;

&lt;p&gt;Solution #1 (though that didn’t work for me):
Right after install-mbr, but before VBoxManage:
Run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;fdisk &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;, you’ll need the value behind “Disk identifier:” of the hard drive your Windows 7 is on.&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;YOURDISKIDENTIFIER | xxd &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;dd &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/.VirtualBox/FAKE.mbr &lt;span class=&quot;nv&quot;&gt;bs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 &lt;span class=&quot;nv&quot;&gt;seek&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;440 &lt;span class=&quot;nv&quot;&gt;conv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;notrunc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then continue with VBoxManage etc.&lt;/p&gt;

&lt;p&gt;Solution #2 (should work for everyone):
First, run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;fdisk &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;on your host system, you’ll need the value behind “Disk identifier:” of the hard drive your Windows 7 is on.&lt;/p&gt;

&lt;p&gt;After you’ve created the VM (leave out the step telling you to boot from the Windows 7 Installation Disk), attach a Ubuntu (or any other Linux) live CD image to the VM:&lt;/p&gt;

&lt;p&gt;Settings -&amp;gt; Storage -&amp;gt; Controller: IDE -&amp;gt; Add CD Device (first icon) -&amp;gt; Choose Disk -&amp;gt; select the image of your live CD&lt;/p&gt;

&lt;p&gt;Then boot the VM, it should boot the live CD image. Once you’re in there, run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;fdisk /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to start fdisk’s interactive mode (replace /dev/sda with the hard drive your Windows 7 is on).&lt;/p&gt;

&lt;p&gt;Enter&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then enter&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;i
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Put in the disk identifier you’ve gathered in the first step, then press&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;w
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to rewrite the MBR and quit fdisk.&lt;/p&gt;

&lt;p&gt;EDIT (added by admin): After this, shut down the VM and remove the live CD image from the VM’s storage devices.&lt;/p&gt;

&lt;p&gt;And note that the device name (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda&lt;/code&gt;) you have to use on the guest system might differ from its name on the host system.&lt;/p&gt;

&lt;p&gt;Now you should be able to boot Windows 7 both natively and through the VM without Windows complaining.&lt;/p&gt;

&lt;p&gt;Feel free to update the article with this information.
To everyone else that has problems, I suggest you to read the wiki article linked at the top of my comment, it’s very comprehensive and has many troubleshooting tips at the end.&lt;/p&gt;

&lt;p&gt;Something else that took me a few hours to find out:
I also have a Windows XP partition on my hard drive, and I didn’t realize that the “Boot” folder (which is needed for Windows’ bootloader) was only on that partition but not on the Windows 7 partition (I, naturally, installed Windows 7 after I installed WinXP).
So I didn’t add WinXP’s partition number to VBoxManage’s “-partitions” list, which prevented the VM from booting.&lt;/p&gt;

&lt;p&gt;This wiki article also describes how to prevent Windows from asking you to activate again (translated [see section “Align virtual hardware of the physical hardware”; yes, that’s Google Translator’s English :P])&lt;/p&gt;

&lt;h5 id=&quot;aaron---november-17-2012-1&quot;&gt;Aaron - November 17, 2012&lt;/h5&gt;

&lt;p&gt;Thanks for the info! I’ve integrated it into the guide. :)&lt;/p&gt;

&lt;h5 id=&quot;ned---september-12-2014&quot;&gt;Ned - September 12, 2014&lt;/h5&gt;

&lt;p&gt;just a precision : after entering the new disk identifier, you have to press ENTER, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt; to return to FDISK’s main menu, and then press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt; to rewrite MBR.&lt;/p&gt;

&lt;p&gt;My win guest doesn’t repair anything anymore when I reboot it in “real/host” mode.&lt;/p&gt;

&lt;p&gt;But it goes BSOD in guest mode, no matter what I do (and I did respect ALL instructions here). I just can’t figure out what’s happening :(&lt;/p&gt;

&lt;h5 id=&quot;ned---september-15-2014&quot;&gt;Ned - September 15, 2014&lt;/h5&gt;

&lt;p&gt;theres’ a slight error here : after entering the new disk identifier + ENTER, you have to press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m&lt;/code&gt; (back to main menu) before being able to press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt;&lt;/p&gt;

&lt;h4 id=&quot;prateek---january-8-2013&quot;&gt;Prateek - January 8, 2013&lt;/h4&gt;

&lt;p&gt;Very helpful tutorial. Everything worked till booting windows on VM.&lt;/p&gt;

&lt;p&gt;But on boot, windows boots till “Windows is loading..” stage and progress bar loads twice and restarts and gets in this loop forever. Anyone know what could be the issue.? I have been trying with different settings but still no solution.&lt;/p&gt;

&lt;h5 id=&quot;aaron---march-3-2013&quot;&gt;Aaron - March 3, 2013&lt;/h5&gt;

&lt;p&gt;Thanks, Prateek — which version of Windows are you using?&lt;/p&gt;

&lt;h4 id=&quot;shane---january-8-2013&quot;&gt;Shane - January 8, 2013&lt;/h4&gt;

&lt;p&gt;Followed this recipe and it seemed to work out. Question: I would ALSO like to be able to run the Ubuntu when I boot up in the windows partition (raw disk method). How might one do this?&lt;/p&gt;

&lt;h4 id=&quot;aaron---march-3-2013-1&quot;&gt;Aaron - March 3, 2013&lt;/h4&gt;

&lt;p&gt;Shane — I haven’t tried to do that yet — I’m not sure if Windows is capable of doing this. The tutorial was intended mainly for people that use Ubuntu as their primary OS but need to occasionally switch to Windows (anymore, I just do VM’ing only).&lt;/p&gt;

&lt;p&gt;If you use Windows primarily, you could use Wubi or Cygwin to run Ubuntu in parallel.&lt;/p&gt;

&lt;h4 id=&quot;ashley-moore---april-25-2013&quot;&gt;Ashley Moore - April 25, 2013&lt;/h4&gt;

&lt;p&gt;Great Howto. Got my W7 install working perfectly thru VB 4.2. It boots natively as well without a hitch.&lt;/p&gt;

&lt;h4 id=&quot;diq---june-8-2013&quot;&gt;DiQ - June 8, 2013&lt;/h4&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vboxmanage&lt;/code&gt; not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VBoxManage&lt;/code&gt;, and I think it needs root access too&lt;/p&gt;

&lt;h4 id=&quot;erhannis---june-11-2013&quot;&gt;Erhannis - June 11, 2013&lt;/h4&gt;

&lt;p&gt;I’ve managed to get Win7 working (though it complains the copy of windows isn’t valid, and uses generic display drivers). I kept getting the BSoD on boot, even though I’d tried changing the hard drive chipset to several combinations of settings. What finally worked was taken partially from http://sanbarrow.com/p2v2008r2.html.&lt;/p&gt;

&lt;p&gt;The steps were as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;In virtualbox, go to Settings.&lt;/li&gt;
  &lt;li&gt;Go to Storage.&lt;/li&gt;
  &lt;li&gt;Replace all controllers with one SAS controller (I don’t know if it’s necessary to remove all the others or what).&lt;/li&gt;
  &lt;li&gt;Add your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vmdk&lt;/code&gt; under that controller, and hit ok.&lt;/li&gt;
  &lt;li&gt;Now open a terminal. (This is assuming installation under Ubuntu; if not, try analagous steps in whatever OS you’re using).&lt;/li&gt;
  &lt;li&gt;Install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registry-tools&lt;/code&gt; package, either with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; or whatever.&lt;/li&gt;
  &lt;li&gt;Mount your windows partition, say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/mnt/WIN/&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/mnt/WIN/Windows/System32/config/&lt;/code&gt;`.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regshell -F SYSTEM&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd ControlSet001&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd services&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd LSI_SAS&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set “Start” REG_DWORD 0x00000000&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Then unmount the partition and try booting up. Hopefully it will work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I guess the idea is to get windows to use the right drivers for however virtualbox is letting it access the drive.&lt;/p&gt;

&lt;p&gt;Please note that I haven’t tried actually booting my computer into Win7 yet (after the registry change), so it’s possible that I borked it for normal booting up.&lt;/p&gt;

&lt;h5 id=&quot;erhannis---june-11-2013-1&quot;&gt;Erhannis - June 11, 2013&lt;/h5&gt;

&lt;p&gt;Tried it; still works.&lt;/p&gt;

&lt;h5 id=&quot;ned----september-15-2014&quot;&gt;Ned  - September 15, 2014&lt;/h5&gt;

&lt;p&gt;Thank you very much !!!!&lt;/p&gt;

&lt;p&gt;all this article + PCWorld’s comment + your comment saved my day&lt;/p&gt;

&lt;h4 id=&quot;amir-marandi---july-14-2013&quot;&gt;Amir Marandi - July 14, 2013&lt;/h4&gt;

&lt;p&gt;One problem I ran into was access denied when I did&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;VBoxManage internalcommands listpartitions &lt;span class=&quot;nt&quot;&gt;-rawdisk&lt;/span&gt; /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;command, so I did the next step whis is&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;666 /dev/sda
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then executed that command and it fixed the problem.&lt;/p&gt;

&lt;h4 id=&quot;karthik---july-19-2013&quot;&gt;Karthik - July 19, 2013&lt;/h4&gt;

&lt;p&gt;I have been searching for this kind of solution for a long time now. Every site I visited always suggests creating a Hardware Profile and goes on to say, HPs are not present from Vista on onwards and thus native partiton boot in VM is not possible.&lt;/p&gt;

&lt;p&gt;But, your soultion worked like a charm, upto the details of each and every error message that you had mentioned we might get. followed your instructions and every step worked as expected. Now able to boot my Windows partition from VirtualBox.&lt;/p&gt;

&lt;p&gt;Thanks a lot.&lt;/p&gt;

&lt;h5 id=&quot;jacopo-alabiso---july-30-2013-1&quot;&gt;Jacopo Alabiso - July 30, 2013&lt;/h5&gt;

&lt;p&gt;great tutorial, it all worked well.&lt;/p&gt;

&lt;p&gt;One problem: when I boot Win7 within virtualbox, it cannot access the primary partition I use for documents, it says it’s RAW and needs to be formated (but if I try formatting it, it’d fail).&lt;/p&gt;

&lt;p&gt;The same partition is accessible when booting win7 natively.&lt;/p&gt;

&lt;p&gt;Any clue?&lt;/p&gt;

&lt;h4 id=&quot;karthik---august-13-2013&quot;&gt;Karthik - August 13, 2013&lt;/h4&gt;

&lt;p&gt;I was constantly getting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VERR_ACCESS_DENIED&lt;/code&gt; error. If all else fails
follow the following procedure.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo chmod 666 /dev/sda&lt;/code&gt; before the start of all commands&lt;/li&gt;
  &lt;li&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda1&lt;/code&gt; do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 666&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;660&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;berkov---august-24-2013&quot;&gt;berkov - August 24, 2013&lt;/h4&gt;

&lt;p&gt;HI, all configured as above but i get stuck when i run the VB. a new, black emulation window gets open with and a sec later one word appears: “MBR”. any ideas why?&lt;/p&gt;

&lt;p&gt;Reply&lt;/p&gt;

&lt;h4 id=&quot;anonymous---october-19-2013&quot;&gt;Anonymous - October 19, 2013&lt;/h4&gt;

&lt;p&gt;I did this on another harddrive rather than the same harddrive but different partition. If you get a uuid mismatch, you could just open the ``.vmdk` and change it to match the expected uuid.&lt;/p&gt;

&lt;h4 id=&quot;leo---november-5-2013&quot;&gt;Leo - November 5, 2013&lt;/h4&gt;

&lt;p&gt;Question: If you have a Windows 7 recovery disc, but not an install disc, can you still repair the installation once you’ve done this?&lt;/p&gt;

&lt;h5 id=&quot;aaron---january-9-2014&quot;&gt;Aaron - January 9, 2014&lt;/h5&gt;

&lt;p&gt;Leo, As far as I know, the VM treats the guest OS pretty transparently, so if you use the recovery disc it &lt;em&gt;should&lt;/em&gt; work in whatever way the recovery disc was intended to work. (ie. if the recovery disc repairs installations on a normal computer, it should also repair the installation on a VM, I &lt;em&gt;think&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;No guarantees, though.&lt;/p&gt;

&lt;h4 id=&quot;beemoon---january-9-2014&quot;&gt;beemoon - January 9, 2014&lt;/h4&gt;

&lt;p&gt;Hi,
nice job !!!
I manage to launch my win seven 7 Pro N (natively installed) in my CrunchBang (wheezy) with the last stable version of virtualbox v4.3.6&lt;/p&gt;

&lt;p&gt;thanks&lt;/p&gt;

&lt;h4 id=&quot;john---january-9-2014&quot;&gt;John - January 9, 2014&lt;/h4&gt;

&lt;p&gt;So I used WUBI to install my ubuntu alongside windows. I am now trying to use this to access my windows while in ubuntu.&lt;/p&gt;

&lt;p&gt;Will this still work? I think my windows is on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda2&lt;/code&gt; because when I use fdisk -l it is the only bootable drive, and when I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;df -h&lt;/code&gt; it says &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/host/&lt;/code&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; which contains my windows files is mounted from &lt;/code&gt;/dev/sda2`.&lt;/p&gt;

&lt;p&gt;However when I run the createrawvmdk it says cannot read partition information from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/sda2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Am I doing something wrong here? Also I cannot unmount /host/ apparently because thats also where my linux files are cause thats apparently how wubi works.&lt;/p&gt;

&lt;p&gt;PLEASE HELP.&lt;/p&gt;

&lt;h4 id=&quot;v1adimir---january-26-2014&quot;&gt;v1adimir - January 26, 2014&lt;/h4&gt;

&lt;p&gt;Hi, just passing through; curious: as to why you’ve switched-over to the VMWare Player.&lt;/p&gt;

&lt;p&gt;Any particular reason, to do so?!??&lt;/p&gt;

&lt;h5 id=&quot;aaron---march-15-2014&quot;&gt;Aaron - March 15, 2014&lt;/h5&gt;

&lt;p&gt;I’ve actually switched &lt;em&gt;BACK&lt;/em&gt; to VirtualBox, now that Canonical finally updated their repos to use the more current version.&lt;/p&gt;

&lt;p&gt;The repo version is pretty solid and there is now even a package for guest additions. :)&lt;/p&gt;

&lt;h4 id=&quot;laura-frasca---april-22-2014&quot;&gt;Laura Frasca - April 22, 2014&lt;/h4&gt;

&lt;p&gt;Thanks so much for maintaining this document! I am now able to run Windows 7 Pro (64-bit) on Debian jessie/testing in VirtualBox 4.3.10. I did have to repair the installation (ok by me because I am planning never to boot Windows raw again).&lt;/p&gt;

&lt;p&gt;I also found that Windows would not give back the mouse after capturing it. This was exceedingly frustrating since VirtualBox displayed a couple of popups to me that obscured my view of Windows and that I couldn’t dismiss. I had to SSH into my machine and kill the VM a couple of times before I got it sorted.&lt;/p&gt;

&lt;p&gt;The mouse capture issues were resolved by installing the Windows additions. Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/virtualbox/VBoxGuestAdditions.iso&lt;/code&gt; as a CD drive before starting the VM, and then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VBoxWindowsAdditions.exe&lt;/code&gt; from the CD/DVD drive inside the guest. Extra points for navigating Windows with the keyboard so you don’t get your mouse captured.&lt;/p&gt;

&lt;h4 id=&quot;jonathon---may-16-2014&quot;&gt;Jonathon - May 16, 2014&lt;/h4&gt;

&lt;p&gt;Thanks to Aaron and to all who posted this excellent tutorial!&lt;/p&gt;

&lt;p&gt;I got everything to work fine (in terms of accessing the partition through VB), but I have a couple VB problems:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Guest Additions CD doesn’t install properly&lt;/li&gt;
  &lt;li&gt;Host Key and Disable/Enable Capture Mouse function not working.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I realize these are more about VB than about virtualizing a windows partition, but can anyone help me out?&lt;/p&gt;

&lt;p&gt;I’m still very happy that it works, though! :)&lt;/p&gt;

&lt;h4 id=&quot;raz---june-3-2014&quot;&gt;Raz - June 3, 2014&lt;/h4&gt;

&lt;p&gt;Excuse me,&lt;/p&gt;

&lt;p&gt;I would like to ask the way to modify disk identifier, if applicable windows8.1?&lt;/p&gt;

&lt;h4 id=&quot;computingfroggy---june-19-2014&quot;&gt;ComputingFroggy - June 19, 2014&lt;/h4&gt;

&lt;p&gt;Hi,&lt;/p&gt;

&lt;p&gt;Great post … but it did not completely work for me.
I am running Lubuntu 14.04 on a Asus EeePC 1215B with Windows 7 pre-installed.
I followed the given instructions including the instructions for Windows 7.&lt;/p&gt;

&lt;p&gt;The instructions with fdisk (User-submitted Walkthrough for Windows 7) did not work for me. I had to download a repair disk ISO and to boot with it (and rebooted a couple of time) … to eventually get a Blue Screen of Death with an error message quickly disappearing !&lt;/p&gt;

&lt;p&gt;Then I remembered I started with another post that gave a solution for that: running a registry fix.&lt;/p&gt;

&lt;p&gt;I give you the post (the registry fix is at the end):
http://in-the-attic.com/2010/06/05/booting-windows-7-from-an-existing-partition-inside-ubuntu-virtual-box/&lt;/p&gt;

&lt;p&gt;And now, it works: I can run Windows from my Lubuntu … all of that on a small netbook !&lt;/p&gt;

&lt;p&gt;Thanks again for all your work !&lt;/p&gt;

&lt;p&gt;Reply&lt;/p&gt;

&lt;h5 id=&quot;aaron---june-19-2014&quot;&gt;Aaron - June 19, 2014&lt;/h5&gt;

&lt;p&gt;Thanks for your contribution!&lt;/p&gt;

&lt;p&gt;The original guide was for winXP with Ubuntu, so I make no warranty about it working with any other configuration. Glad you were able to figure it out:)&lt;/p&gt;

&lt;h4 id=&quot;computingfroggy---june-19-2014-1&quot;&gt;ComputingFroggy - June 19, 2014&lt;/h4&gt;

&lt;p&gt;I have created a post, on my blog (in French ) summarizing all of this
http://computing.travellingfroggy.info/article171/double-boot
In the English version, I will just point out to your post (and a few others), if that’s ok with you.&lt;/p&gt;

&lt;h5 id=&quot;aaron---june-20-2014&quot;&gt;Aaron - June 20, 2014&lt;/h5&gt;

&lt;p&gt;Absolutely! I’ll give you a link near the top here, too :)&lt;/p&gt;

&lt;h4 id=&quot;computingfroggy---june-28-2014&quot;&gt;ComputingFroggy - June 28, 2014&lt;/h4&gt;

&lt;p&gt;Strangely, I had my partition table garbled: my first Ubuntu partition has been moved from sda7 to sda9. Thankfully, that was a Ubuntu 12.02 that I am not using any more. I am running from a Lubuntu 14.04 (on sda8) where I have installed VirtualBox.
Anyway, that’s just a note in case, someone is interested.&lt;/p&gt;

&lt;p&gt;But I have now a problem, on my VirtualBox I have no network, when it worked before ! Any ideas on how to fix it ?&lt;/p&gt;

&lt;p&gt;Reply&lt;/p&gt;

&lt;h5 id=&quot;aaron---june-28-2014&quot;&gt;Aaron - June 28, 2014&lt;/h5&gt;

&lt;p&gt;Which style of networking are you trying to use? I typically either go NAT (if I want it strictly internal) or Bridged (if I want to interface with other computers on LAN)&lt;/p&gt;

&lt;h5 id=&quot;computingfroggy---june-29-2014&quot;&gt;ComputingFroggy - June 29, 2014&lt;/h5&gt;

&lt;p&gt;I left everything to the default: NAT with Intel PRO/1000 MT Desktop !&lt;/p&gt;

&lt;p&gt;I think the network went pear shape when I installed the guest additions (I wanted to share the clipboard between Ubuntu and Windows).&lt;/p&gt;

&lt;h5 id=&quot;computingfroggy---june-30-2014&quot;&gt;ComputingFroggy - June 30, 2014&lt;/h5&gt;

&lt;p&gt;An interesting forum thread on virtualbox.org:
https://forums.virtualbox.org/viewtopic.php?f=2&amp;amp;t=31759
with a how-to similar to yours !&lt;/p&gt;

&lt;p&gt;It’s claimed they’ve got a method avoiding to use of the repair CD … although I haven’t tried it, yet.&lt;/p&gt;

&lt;h5 id=&quot;computingfroggy---july-3-2014&quot;&gt;ComputingFroggy - July 3, 2014&lt;/h5&gt;

&lt;p&gt;I have added another network card with bridge access. That triggered drivers installation, and the network is working now.&lt;/p&gt;

&lt;h5 id=&quot;igor-melnyk---july-10-2014&quot;&gt;Igor Melnyk - July 10, 2014&lt;/h5&gt;

&lt;p&gt;Thanks a lot!!&lt;/p&gt;

&lt;h4 id=&quot;markham---january-10-2015&quot;&gt;Markham - January 10, 2015&lt;/h4&gt;

&lt;p&gt;I successfully followed the steps to open my Win7 partition in VirtualBox — thanks for the brilliant walkthrough — but when I try to load a Netflix video in my browser in the Win7 VM, I get an error:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Whoops something went wrong… Digital Rights Management (DRM) Error. Error Code: N8156-6013.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do you (does anyone) have any idea how I can get around that? (Using Silverlight was the only reason I wanted to access my Win7 installation from my Linux installation.) “Repairing” Silverlight doesn’t seem to help.&lt;/p&gt;

&lt;h5 id=&quot;markham---january-19-2015&quot;&gt;Markham - January 19, 2015&lt;/h5&gt;

&lt;p&gt;Nevermind. Tried it in Chrome with success.&lt;/p&gt;
</summary>
      <id>https://armahillo.dev/2010/01/27/using-virtualbox-with-an-existing-partition/</id>
      <updated>2010-01-27T00:00:00+00:00</updated>
      <author>
        <name>Aaron H</name>
      </author>
    </entry>
  
</feed>
