<rss version="2.0">
  <channel>
    <title>ScottW/rites</title>
    <link>https://scottw.com/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Thu, 22 Feb 2024 14:09:21 -0500</lastBuildDate>
    <item>
      <title>Configuring the SQLite BackUp Script for Hatchbox</title>
      <link>https://scottw.com/2024/02/22/configuring-the-sqlite.html</link>
      <pubDate>Thu, 22 Feb 2024 14:09:21 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/02/22/configuring-the-sqlite.html</guid>
      <description>&lt;p&gt;Getting the &lt;a href=&#34;https://scottw.com/2024/02/22/sqlite-backup-to.html&#34;&gt;SQLite BackUp Script&lt;/a&gt; running on Hatbox took a little extra work.&lt;/p&gt;
&lt;p&gt;First, to get access to the ENV variables (assuming you are not hardcoding), you need to add the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;color:#24909d&#34;&gt;cd&lt;/span&gt; /home/deploy/YOUR_APP_NAME/current
&lt;span style=&#34;color:#24909d&#34;&gt;eval&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;$(&lt;/span&gt;/home/deploy/.asdf/bin/asdf vars&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Second, where to put the script? I had hoped to put it in the Rails bin directory. This works for getting it up to the server. However, anytime you deploy the execute permission on the script is lost.&lt;/p&gt;
&lt;p&gt;My next attempt was to add a folder called &lt;code&gt;bin&lt;/code&gt; to the shared directory. I set up a symlink to the file &lt;code&gt;ln -sf ../../current/bin/backup backup&lt;/code&gt; and then set the execute permission &lt;code&gt;chmod +x backup&lt;/code&gt;. This worked, but the execute permission was again lost after a deployment.&lt;/p&gt;
&lt;p&gt;Ultimately, I copied the script to the shared/bin directory and reset the execute permission. If I change it, I must remember to update the copy, but now it works.&lt;/p&gt;
&lt;p&gt;Finally, I went to the HatchBox cron page for my app, and configured the following to execute several times a day:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;(&lt;span style=&#34;color:#24909d&#34;&gt;cd&lt;/span&gt; ../shared/bin ; ./backup)
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;HatchBox cron jobs start in your &lt;code&gt;current&lt;/code&gt; directory. To get to the bin folder, we need to navigate there before we can finally execute the backup.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SQLite BackUp to S3</title>
      <link>https://scottw.com/2024/02/22/sqlite-backup-to.html</link>
      <pubDate>Thu, 22 Feb 2024 13:50:04 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/02/22/sqlite-backup-to.html</guid>
      <description>&lt;p&gt;I recently moved &lt;a href=&#34;https://howivscode.com&#34;&gt;HowIVSCode&lt;/a&gt; to &lt;a href=&#34;https://hatchbox.io&#34;&gt;HatchBox&lt;/a&gt;. As part of their setup, they provide a shared folder for each application persisted across deployments.&lt;/p&gt;
&lt;p&gt;However, at this time, there is no option to back up that data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Side Note:&lt;/strong&gt; Digital Ocean provides server backups, which would likely work, but I would rather my backups exist outside the network managing my servers.&lt;/p&gt;
&lt;p&gt;What I ended up doing was writing a script that does the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Loops through all the SQLite files in a given directory&lt;/li&gt;
&lt;li&gt;Uses the SQLite &lt;code&gt;.backup&lt;/code&gt; command to perform a backup safely&lt;/li&gt;
&lt;li&gt;Gzip the file&lt;/li&gt;
&lt;li&gt;Uses GPG to encrypt the backup&lt;/li&gt;
&lt;li&gt;Send the backup to a locked down bucket on S3 via &lt;code&gt;curl&lt;/code&gt; (so no aws cli dependency)&lt;/li&gt;
&lt;li&gt;Cleans up when done&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On S3, I have the bucket configured to delete any files older than 31 days. This should keep costs in check, and you should configure this to your needs.&lt;/p&gt;
&lt;p&gt;Before the script, I want to give a big shout-out to Paweł Urbanek and his &lt;a href=&#34;https://pawelurbanek.com/heroku-postgresql-s3-backup&#34;&gt;guide for doing this with PostgreSQL + Heroku&lt;/a&gt;. I have been running a similar setup for a couple of years now, and knowing my data is safe outside of Heroku is excellent. I also want to shout out this &lt;a href=&#34;https://gist.github.com/chrismdp/6c6b6c825b07f680e710&#34;&gt;Chris Parson&amp;rsquo;s gist&lt;/a&gt;, which paved the way for sending the data to S3 without needing to install the ASW CLI.&lt;/p&gt;
&lt;p&gt;The script uses five ENV variables (although you can hard code your values at the top)&lt;/p&gt;
&lt;p&gt;The one &lt;code&gt;BACKUP_S3_DB_PASSPHRASE&lt;/code&gt; must be saved somewhere you will remember. This is the passphrase used by GPG. The only thing worse than losing your database is having a backup you cannot decrypt. 😁&lt;/p&gt;
&lt;p&gt;Here is a &lt;a href=&#34;https://gist.github.com/scottwater/6159f258b0f3fd6cdbffe9ab5268f3f4&#34;&gt;gist of the script&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt; 9
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;10
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;11
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;12
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;13
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;14
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;15
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;16
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;17
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;18
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;19
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;20
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;21
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;22
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;23
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;24
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;25
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;26
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;27
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;28
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;29
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;30
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;31
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;32
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;33
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;34
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;35
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;36
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;37
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;38
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;39
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;40
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;41
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;42
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;43
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;color:#cd2828;font-weight:bold&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;span style=&#34;color:#cd2828;font-weight:bold&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#24909d&#34;&gt;set&lt;/span&gt; -e

&lt;span style=&#34;color:#40ffff&#34;&gt;s3_key&lt;/span&gt;=&lt;span style=&#34;color:#40ffff&#34;&gt;$BACKUP_S3_KEY&lt;/span&gt;
&lt;span style=&#34;color:#40ffff&#34;&gt;s3_secret&lt;/span&gt;=&lt;span style=&#34;color:#40ffff&#34;&gt;$BACKUP_S3_SECRET&lt;/span&gt;
&lt;span style=&#34;color:#40ffff&#34;&gt;bucket&lt;/span&gt;=&lt;span style=&#34;color:#40ffff&#34;&gt;$BACKUP_S3_BUCKET&lt;/span&gt;
&lt;span style=&#34;color:#40ffff&#34;&gt;backup_db_passphrase&lt;/span&gt;=&lt;span style=&#34;color:#40ffff&#34;&gt;$BACKUP_S3_DB_PASSPHRASE&lt;/span&gt;
&lt;span style=&#34;color:#40ffff&#34;&gt;data_directory&lt;/span&gt;=&lt;span style=&#34;color:#40ffff&#34;&gt;$SQLITE_DATABASE_DIRECTORY&lt;/span&gt;
&lt;span style=&#34;color:#999;font-style:italic&#34;&gt;# ensure each backup has the same date key&lt;/span&gt;
&lt;span style=&#34;color:#40ffff&#34;&gt;date_key&lt;/span&gt;=&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;$(&lt;/span&gt;date &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#39;+%Y-%m-%d-%H-%M-%S&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;)&lt;/span&gt;

&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;function&lt;/span&gt; backupToS3()
{
  &lt;span style=&#34;color:#40ffff&#34;&gt;database&lt;/span&gt;=&lt;span style=&#34;color:#40ffff&#34;&gt;$1&lt;/span&gt;

  &lt;span style=&#34;color:#40ffff&#34;&gt;database_file_name&lt;/span&gt;=&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;$(&lt;/span&gt;basename -- &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$database&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;)&lt;/span&gt;
  &lt;span style=&#34;color:#40ffff&#34;&gt;database_name&lt;/span&gt;=&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;database_file_name&lt;/span&gt;%.*&lt;span style=&#34;color:#ed9d13&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;

  &lt;span style=&#34;color:#40ffff&#34;&gt;backup_file_name&lt;/span&gt;=&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;/tmp/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$database_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;-backup-&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$date_key&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;.sqlite3&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#40ffff&#34;&gt;gpg_backup_file_name&lt;/span&gt;=&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$database_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$date_key&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;.gpg&amp;#34;&lt;/span&gt;

  sqlite3 &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$database&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;.backup &lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;
  gzip &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;
  gpg --yes --batch --passphrase=&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$backup_db_passphrase&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; --output &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;/tmp/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$gpg_backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; -c &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;.gz&amp;#34;&lt;/span&gt;

  &lt;span style=&#34;color:#40ffff&#34;&gt;date&lt;/span&gt;=&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;$(&lt;/span&gt;date +&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;%a, %d %b %Y %T %z&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;)&lt;/span&gt;
  &lt;span style=&#34;color:#40ffff&#34;&gt;content_type&lt;/span&gt;=&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#39;application/tar+gzip&amp;#39;&lt;/span&gt;
  &lt;span style=&#34;color:#40ffff&#34;&gt;string&lt;/span&gt;=&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;PUT\n\n&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$content_type&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$date&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;\n/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$bucket&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$gpg_backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#40ffff&#34;&gt;signature&lt;/span&gt;=&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;$(&lt;/span&gt;&lt;span style=&#34;color:#24909d&#34;&gt;echo&lt;/span&gt; -en &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;string&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; | openssl sha1 -hmac &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;s3_secret&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; -binary | base64&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;)&lt;/span&gt;
  curl -X PUT -T &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;/tmp/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$gpg_backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&lt;/span&gt;    -H &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;Host: &lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$bucket&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;.s3.amazonaws.com&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&lt;/span&gt;    -H &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;Date: &lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$date&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&lt;/span&gt;    -H &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;Content-Type: &lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$content_type&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&lt;/span&gt;    -H &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;Authorization: AWS &lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;s3_key&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$signature&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;\
&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;https://&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$bucket&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;.s3.amazonaws.com/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$gpg_backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;

  rm &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;.gz&amp;#34;&lt;/span&gt;
  rm &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;/tmp/&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$gpg_backup_file_name&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;
}

&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;for&lt;/span&gt; file in &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$data_directory&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;/*.sqlite3; &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;do&lt;/span&gt;
  backupToS3 &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#40ffff&#34;&gt;$file&lt;/span&gt;&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Quick Summary of the script&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lines 4-8 - grab the ENV variables&lt;/li&gt;
&lt;li&gt;Line 10 - grab a date we can use to append to the file name and avoid collisions&lt;/li&gt;
&lt;li&gt;Line 12, declare a function &lt;code&gt;backuToS3&lt;/code&gt; we will use at the end to iterate over each database in the directory&lt;/li&gt;
&lt;li&gt;Lines 14-17 - extract the database file name. A significant benefit to SQLite is there is no harm in having many databases for individual tasks. For HowIVSCode, I use LiteStack, which creates separate databases for Data, Cache, and Queue.&lt;/li&gt;
&lt;li&gt;Lines 22-24 - backup, zip, and encrypt&lt;/li&gt;
&lt;li&gt;Lines 26-35 - send the file to AW3. If you have the AWS CLI installed, you could probably replace that with &lt;code&gt;aws s3 cp &amp;quot;/tmp/${gpg_backup_file_name}&amp;quot; &amp;quot;s3://$bucket/$gpg_backup_file_name&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Lines  37-38 - clean up the tmp files&lt;/li&gt;
&lt;li&gt;Lines 41-43 - loop through any .sqlite3 files in the directory.&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title> 11 Things I Learned Migrating HowIVSCode to Rails 7.1</title>
      <link>https://scottw.com/2024/02/20/lessons-learned-migrating.html</link>
      <pubDate>Tue, 20 Feb 2024 09:30:04 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/02/20/lessons-learned-migrating.html</guid>
      <description>&lt;p&gt;For reasons I will share in another post, I was forced to move two personal projects off of Heroku and onto HatchBox. The first, ThocStock, went very smoothly. I deployed the code, set a couple of ENVs, verified everything worked as expected, and finally updated the DNS.&lt;/p&gt;
&lt;p&gt;The second app, HowIVSCode, proved to be a bit more of a challenge. The first commits were about 4.5 years ago. Like many side projects with no revenue, apart from some gem security updates, it has not seen many changes over the last four years.&lt;/p&gt;
&lt;p&gt;The app was primarily built on Rails 5, TailwindCSS, webpacker, Administrate, and Delayed Job. Deploying to HatchBox yielded errors related to Python via Webpacker and led me down the trail of ripping out Webpacker, CSS building, and JS bundling. After a while, it felt like I was running in circles, putting out new small fires. All solvable problems, but then it hit me. This app has a total of 3 models and a couple of controllers. There is little value in maintaining the source history (and I still have it if needed).&lt;/p&gt;
&lt;p&gt;I wanted to try out LiteStack, so I told myself I could do this in an hour or two if I started from scratch and copied over the models, controllers, and views.&lt;/p&gt;
&lt;p&gt;In typical developer fashion, 2 hours was not a realistic estimate (probably closer to 6 to 8), but I learned quite a bit along the way.&lt;/p&gt;
&lt;p&gt;So here is what I learned upgrading a mostly kludgy Rails 5/webpacker app to a fresh Rails 7.1 app using LiteStack + ImportMaps + Avo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;em&gt;I call this a migration instead of an upgrade because I am starting mostly fresh and pulling in the relevant pre-existing parts.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;running-rails-new-with-the---skip-bundle-flag-has-potentially-unintended-consequences&#34;&gt;Running Rails New with the &amp;ndash;skip-bundle flag Has Potentially Unintended Consequences&lt;/h2&gt;
&lt;p&gt;I had initially &lt;a href=&#34;https://twitter.com/scottw/status/1756387596220129283&#34;&gt;mentioned on X&lt;/a&gt; that using import maps required I execute the following on my own.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;bin/rails importmap:install tailwindcss:install stimulus:install:importmap turbo::install:importmap
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is something I would have expected Rails to just do based on the flags I had set when running new (and primarily based upon what was in my .railsrc)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;--css=tailwind
--javascript=importmap
--database=sqlite3
--asset-pipeline=propshaft
--template=~/rails_template.rb
--skip-jbuilder
--skip-bundle
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;While writing this, I conducted several tests and discovered a few issues that may be bugs or inadequacies in the documentation. When using the &amp;ndash;skip-bundle flag, the proper installation of Importmap, Tailwind, Stimulus, and Turbo is not facilitated. This might seem logical since installing them without bundling is challenging. However, I would have expected running the bundle manually later (or perhaps bin/setup) would resolve this issue.&lt;/p&gt;
&lt;p&gt;It also appears that with the &lt;code&gt;--skip-bundle&lt;/code&gt; flag, the Tailwind-Rails gem is not included when specified with the &amp;ndash;css=tailwind flag.&lt;/p&gt;
&lt;p&gt;Again, there is some chicken and egg here. It is on my list to dig into the source more to figure this out. But you will likely be up and running quicker if you do not use the skip-bundle flag.&lt;/p&gt;
&lt;h2 id=&#34;importmap-limitations&#34;&gt;ImportMap Limitations&lt;/h2&gt;
&lt;p&gt;The short answer here is that everything you typically bundle with JavaScript is always an option (today) with importmaps. This is something to consider beforehand, especially if you have a list of JavaScript libraries you need to use. In the case of HowIVSCode, I could not experiment with DaisyUI. However, the long-term benefit of an app that will not get many updates is too good to ignore.&lt;/p&gt;
&lt;h2 id=&#34;omniauth-login-links-likely-require-an-http-post&#34;&gt;OmniAuth Login &amp;lsquo;Links&amp;rsquo; (Likely) Require an HTTP Post&lt;/h2&gt;
&lt;p&gt;The only way to log in or create an account with HowIVSCode is via GitHub + OAuth. OmniAuth now recommends adding the gem &lt;code&gt;omniauth-rails_csrf_protection&lt;/code&gt;. I didn&amp;rsquo;t think much about it and just added it.&lt;/p&gt;
&lt;p&gt;However, once in place, you can no longer use a href as part of your sign-up flow. The reasoning for this makes sense, but in quickly trying to move to the most recent gem updates, I spun my tires here for far longer than I care to admit.&lt;/p&gt;
&lt;h2 id=&#34;no-arrays-in-sqlite&#34;&gt;No Arrays in SQLite&lt;/h2&gt;
&lt;p&gt;When relational data is small and often just a word or two, I use an array in PostgreSQL.&lt;/p&gt;
&lt;p&gt;For example, a migration for a small blog post table might look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;create_table &lt;span style=&#34;color:#ed9d13&#34;&gt;:posts&lt;/span&gt; &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;do&lt;/span&gt; |t|
	t.text &lt;span style=&#34;color:#ed9d13&#34;&gt;:title&lt;/span&gt;
	t.text &lt;span style=&#34;color:#ed9d13&#34;&gt;:body&lt;/span&gt;
	t.text &lt;span style=&#34;color:#ed9d13&#34;&gt;:tags&lt;/span&gt;, &lt;span style=&#34;color:#ed9d13&#34;&gt;array&lt;/span&gt;: &lt;span style=&#34;color:#6ab825&#34;&gt;true&lt;/span&gt;
&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;I no longer need a separate &lt;code&gt;tags&lt;/code&gt; table or a &lt;code&gt;tags_in_posts&lt;/code&gt; related table. PostgreSQL provides the necessary functions to query this as needed.&lt;/p&gt;
&lt;p&gt;Unfortunately, there are no Arrays in SQLite. However, all is not lost. SQLite does support JSON, so for now, I added a json column called data and the necessary arrays. This is just data I am recording, so we will have to see in the future if this holds up when querying by specific tags becomes necessary.&lt;/p&gt;
&lt;h2 id=&#34;apply-warnings&#34;&gt;@apply warnings&lt;/h2&gt;
&lt;p&gt;For better or worse, I occasionally use @apply with my Tailwind CSS. VSCode kept complaining about (although it still worked) an &lt;code&gt;Unknown Rule&lt;/code&gt;. The fix is to set the *.css file association to &lt;code&gt;tailwindcss&lt;/code&gt;. Full details &lt;a href=&#34;https://cssf1.com/how-to/fix-unknown-at-rule-tailwind-warning-vscode&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;default-layout-for-sitepress&#34;&gt;Default Layout for SitePress&lt;/h2&gt;
&lt;p&gt;My markdown content views previously used the markdown-views gem. This time around, I decided to go with SitePress. Overall, SitePress has been great to work with. However, one thing I struggled with was how to use a different layout for my content pages.&lt;/p&gt;
&lt;p&gt;With my SitePress pages, everything that is not the markdown body is set in a different Layout file. This way, I did not have to try and overly mix Markdown and ERB in my content (and as far as I can tell, you cannot even access SitePress&amp;rsquo;s page variables&lt;/p&gt;
&lt;p&gt;I tried various ways to make this work, but I settled on creating a new controller derived from Sitepress::SiteController. Then, in my routes file, I specified that this controller would be used &lt;code&gt;sitepress_pages(controller: &amp;quot;content&amp;quot;)&lt;/code&gt; for my pages.&lt;/p&gt;
&lt;h2 id=&#34;meta-tags-with-sitepress&#34;&gt;Meta Tags with SitePress&lt;/h2&gt;
&lt;p&gt;Similar to the above concern, I wanted to be still able to set various meta tags via the meta-tags gem.&lt;/p&gt;
&lt;p&gt;Again, trying to avoid any ERB in my Markdown as much as possible, I added a meta section to my frontmatter and wired it up like this in the SitePress layout.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&amp;lt;% if meta_data = current_page.data[&amp;#34;meta&amp;#34;] %&amp;gt;
  &amp;lt;% set_meta_tags(meta_data.to_h) %&amp;gt;
&amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;markdown-escaping-in-sitepress&#34;&gt;Markdown Escaping in SitePress&lt;/h2&gt;
&lt;p&gt;I have a Stimulus controller that adds your API key to the clipboard when you click on it. Previously, I rendered a partial which wired up the controller &lt;code&gt;&amp;lt;%= render partial: &amp;quot;auth_token&amp;quot; %&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The partial approach still worked (I know, ERB in MD), but the &lt;code&gt;#&lt;/code&gt; in  &lt;code&gt;data-action=&amp;quot;click-&amp;gt;copy-auth-key#copy&lt;/code&gt; is causing RedCarpret (SitePress&amp;rsquo;s markdown processer) to start escaping everything after. The markdown-views gem uses Commonmarker for processing markdown and doesn&amp;rsquo;t appear to have this issue (I have tests to compare if someone is interested).&lt;/p&gt;
&lt;p&gt;If I were using more stimulus in the project, I would probably need to dig deeper and/or swap Markdown libraries. My usage was simple enough; I just dropped the data-action attribute and wired up an event listener in my controller.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;7
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;8
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;import&lt;/span&gt; { Controller } from &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;@hotwired/stimulus&amp;#34;&lt;/span&gt;;
&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;extends&lt;/span&gt; Controller {
  copy() {
    navigator.clipboard.writeText(&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;this&lt;/span&gt;.element.innerText.trim());
  }
  initialize() {
    &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;this&lt;/span&gt;.element.addEventListener(&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;this&lt;/span&gt;.copy.bind(&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;this&lt;/span&gt;))
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Sometimes, being lazy is the correct answer.&lt;/p&gt;
&lt;h2 id=&#34;importing-data-from-pg-to-sqlite&#34;&gt;Importing Data from PG to Sqlite&lt;/h2&gt;
&lt;p&gt;Sadly, there does not appear to be an easy off-the-self option to go in this direction. Most articles recommended doing a pg_dump to SQL and then massaging the file to work with Sqlite. Depending on the data complexity, this might be your best option. I was going from the old to the new app with roughly the same ActiveRecord models (minus the arrays).&lt;/p&gt;
&lt;p&gt;I found generating a couple of JSON files from the original app and then looping over each of them with my new models to be the simplest repeatable option.&lt;/p&gt;
&lt;h2 id=&#34;configuring-where-litestack-puts-the-sqlite-databases-in-production&#34;&gt;Configuring Where LiteStack Puts The SQLite Databases in Production&lt;/h2&gt;
&lt;p&gt;Hatchbox provides each app with a persistent storage location. Using the database.yml, it is simple to set this as the folder for your data.sqlite3 file. However, I wanted to be sure that the other Sqlite databases, such as queue.sqlite3, are also correctly persisted. For the litequeue.yml, there is a db path option, but this is relative to the main app configuration.&lt;/p&gt;
&lt;p&gt;Looking at the LiteStack docs, there was no obvious answer. However, digging through the source, there is an ENV variable you can set: &lt;code&gt;LITESTACK_DATA_PATH&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The benefit of the ENV is it also handles the data.sqlite3 file as well, so I could remove the hardcoded production path from my database.yml.&lt;/p&gt;
&lt;p&gt;Side note: It still sticks the files in a production sub-directory. It is not the end of the world, but I hope it becomes optional.&lt;/p&gt;
&lt;h2 id=&#34;bundle-only-supports-arm64-darwin&#34;&gt;Bundle Only Supports &amp;ldquo;arm64-darwin&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;I believe this is related to the non-bundle issue I had previously. Essentially, my bundle was only valid as is for M1 Macs.&lt;/p&gt;
&lt;p&gt;The fix was as easy as &lt;code&gt;bundle lock --add-platform x86_64_linux&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you watch the output from a &lt;code&gt;rails new&lt;/code&gt; without the &lt;code&gt;--skip-bundle&lt;/code&gt; flag, you can see the &lt;code&gt;--add-platform&lt;/code&gt; flag is set.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>HTTP Post with a Link_To in Rails 7</title>
      <link>https://scottw.com/2024/02/14/http-post-with.html</link>
      <pubDate>Wed, 14 Feb 2024 06:58:07 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/02/14/http-post-with.html</guid>
      <description>&lt;p&gt;I have been migrating a small app to Rails 7.x. The app has not been touched in a couple of years (Rails 5-ish with webpacker). I decided it would be simpler to start anew and just copy over the parts I needed.&lt;/p&gt;
&lt;p&gt;One thing I noticed on a new app without RJS installed was that setting the HTTP method on links was not working.&lt;/p&gt;
&lt;p&gt;There are two ways to fix this. One is to just use &lt;code&gt;button_to&lt;/code&gt; instead. This is arguably better since it actually does a POST (and submit). However, if you need/want to continue using &lt;code&gt;link_to&lt;/code&gt;, you can instead swap the &lt;code&gt;method&lt;/code&gt; for a &lt;code&gt;turbo_method&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&amp;lt;li&amp;gt;
  &amp;lt;%= link_to &amp;#34;Sign Out&amp;#34;, logout_path, data: {turbo_method: :delete}, aria: {label: &amp;#34;Sign out of How I VSCode&amp;#34;}, class: &amp;#34;nav-link&amp;#34; %&amp;gt;
&amp;lt;/li&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>Enabling Debugging in Campfire</title>
      <link>https://scottw.com/2024/01/31/enabling-debugging-in.html</link>
      <pubDate>Wed, 31 Jan 2024 15:31:25 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/01/31/enabling-debugging-in.html</guid>
      <description>&lt;p&gt;Ruby&amp;rsquo;s debugging story has improved dramatically in 3.x (and Rails).&lt;/p&gt;
&lt;p&gt;I figured the best way to understand what&amp;rsquo;s happening in Campfire would be to attach the debugger and step through some of the more interesting parts.&lt;/p&gt;
&lt;p&gt;Unfortunately, I was greeted with a recurring error that often looked something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;5
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;6
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&amp;lt;Thread:0x00000001276a7750@DEBUGGER__::Server::reader /Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/debug-1.9.1/lib/debug/server.rb:44 aborting&amp;gt; terminated with exception (report_on_exception is true):
/Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/socket.rb:1128:in `unlink&amp;#39;: No such file or directory @ apply2files - /var/folders/6q/xz6r4tqd4sl9qpbqkjlqj3dr0000gn/T/rdbg-501/rdbg-29191 (Errno::ENOENT)
	from /Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/socket.rb:1128:in `ensure in unix_server_socket&amp;#39;
/Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/socket.rb:1128:in `unlink&amp;#39;	from /Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/socket.rb:1128:in `unix_server_socket&amp;#39;
	from /Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/3.3.0/socket.rb:1169:in `unix_server_loop&amp;#39;
	from /Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/debug-1.9.1/lib/debug/server.rb:502:in `accept&amp;#39;
	from /Users/scott/.asdf/installs/ruby/3.3.0/lib/ruby/gems/3.3.0/gems/debug-1.9.1/lib/debug/server.rb:49:in `block in activate&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;At first, I was convinced that debugging was busted on my computer. But I spun up a new project, and everything worked as expected.&lt;/p&gt;
&lt;p&gt;Then, I thought about the error above showing up multiple times and the &lt;a href=&#34;https://scottw.com/2024/01/30/getting-campfire-to.html&#34;&gt;problems with threads I had previously&lt;/a&gt; mentioned.&lt;/p&gt;
&lt;p&gt;I restarted the process without cluster mode enabled for Puma (WEB_CONCURRENCY=0) and could connect the debugger as expected.&lt;/p&gt;
&lt;p&gt;From here, I decided to compare the puma.rb file in Campfire to the one in the empty Rails 7.1 project I just spun up, and I found the problem.&lt;/p&gt;
&lt;p&gt;In the Puma configuration file, cluster mode is enabled if &lt;code&gt;workers&lt;/code&gt; is greater than 0.&lt;/p&gt;
&lt;p&gt;In a fresh Rails 7.1 puma.rb file, the worker configuration looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;3
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;4
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#40ffff&#34;&gt;ENV&lt;/span&gt;[&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;RAILS_ENV&amp;#34;&lt;/span&gt;] == &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;production&amp;#34;&lt;/span&gt;
  &lt;span style=&#34;color:#24909d&#34;&gt;require&lt;/span&gt; &lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;concurrent-ruby&amp;#34;&lt;/span&gt;
  worker_count = &lt;span style=&#34;color:#24909d&#34;&gt;Integer&lt;/span&gt;(&lt;span style=&#34;color:#40ffff&#34;&gt;ENV&lt;/span&gt;.fetch(&lt;span style=&#34;color:#ed9d13&#34;&gt;&amp;#34;WEB_CONCURRENCY&amp;#34;&lt;/span&gt;) { &lt;span style=&#34;color:#40ffff&#34;&gt;Concurrent&lt;/span&gt;.physical_processor_count })
  workers worker_count &lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;if&lt;/span&gt; worker_count &amp;gt; &lt;span style=&#34;color:#3677a9&#34;&gt;1&lt;/span&gt;
&lt;span style=&#34;color:#6ab825;font-weight:bold&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;However, in Campfire, the RAILS_ENV check is removed&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;worker_count = (Concurrent.processor_count * 0.666).ceil
workers ENV.fetch(&amp;#34;WEB_CONCURRENCY&amp;#34;) { worker_count }
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;My guess is that with Campfire being a chat app with lots of connectivity, they opted for Puma&amp;rsquo;s cluster mode by default.&lt;/p&gt;
&lt;p&gt;The good news is you can disable cluster mode in Campfire without changing any source. Just set the WEB_CONCURRENCY ENV to 0.&lt;/p&gt;
&lt;p&gt;Something like this should do the trick:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;WEB_CONCURRENCY=0 rdbg -n --open=vscode -c -- bin/rails server -p 3000&lt;/code&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setting Up Campfire on Localhost</title>
      <link>https://scottw.com/2024/01/30/getting-campfire-to.html</link>
      <pubDate>Tue, 30 Jan 2024 14:14:31 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/01/30/getting-campfire-to.html</guid>
      <description>&lt;p&gt;David Kimura at &lt;a href=&#34;https://www.driftingruby.com/episodes?tag=campfire&#34;&gt;Drifting Ruby&lt;/a&gt; has some good videos on setting Campfire up outside the Once installer.  Outside of the 3.3 RC1 and stringio issues, I was running into another issue: I could not generate thumbnails when running on localhost. The thumbnails generated as expected when using Puma Dev. Still, on localhost, they were failing, and worse, I would typically end up with one broken thumbnail variant per thread pool worker.&lt;/p&gt;
&lt;p&gt;First, here are my setup steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download the source&lt;/li&gt;
&lt;li&gt;Run bin/setup (I had to remove rbenv from this file since I use ASDF these days)&lt;/li&gt;
&lt;li&gt;From the rails console, run, &lt;code&gt;WebPush.generate_key&lt;/code&gt; and copy the keys into ENV variables &lt;code&gt;VAPID_PUBLIC_KEY&lt;/code&gt; and &lt;code&gt;VAPID_PRIVATE_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;msgpack&lt;/code&gt; gem to my GemFile&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With all of this in place, &lt;a href=&#34;https://campfire.test&#34;&gt;https://campfire.test&lt;/a&gt; worked as expected.&lt;/p&gt;
&lt;p&gt;However, when starting the server via &lt;code&gt;bundle exec rails server&lt;/code&gt; (on an M1 MBP with Sonoma 14.3), the thumbnails of images were missing. I could click on the thumbnails and see them in the lightbox but not in the chat window.&lt;/p&gt;
&lt;p&gt;Digging into the database, I could see they were not being analyzed, but I had no idea why.&lt;/p&gt;
&lt;p&gt;Then I saw the following in the logs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;
&lt;table style=&#34;border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block;&#34;&gt;&lt;tr&gt;&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;1
&lt;/span&gt;&lt;span style=&#34;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#686868&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style=&#34;vertical-align:top;padding:0;margin:0;border:0;;width:100%&#34;&gt;
&lt;pre tabindex=&#34;0&#34; style=&#34;color:#d0d0d0;background-color:#202020;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;objc[84578]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called.
objc[84578]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The thread running ActiveJob running the ActiveStorage Analyze job was crashing.&lt;/p&gt;
&lt;p&gt;A little searching led me to this &lt;a href=&#34;https://github.com/rails/rails/issues/38560&#34;&gt;bug thread&lt;/a&gt; and a suggestion to set &lt;code&gt;OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES&lt;/code&gt;. A quick server reboot, and everything works as expected.&lt;/p&gt;
&lt;p&gt;Before I got here, I also tried disabling cluster mode in Puma, &lt;code&gt;WEB_CONCURRENCY=0 bundle exec rails s&lt;/code&gt;, which solved the problem.&lt;/p&gt;
&lt;p&gt;I submitted this as a bug to 37Signals. I am not sure if this is just something on my computer or not, so I would try it without these changes first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; 37Signals confirmed this is a known issue. The env variable is actually set in the &lt;code&gt;.pumadev&lt;/code&gt; file which is why I did. not see the issue when using Puma Dev.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A few new apps I am using in 2024</title>
      <link>https://scottw.com/2024/01/18/a-few-new.html</link>
      <pubDate>Thu, 18 Jan 2024 09:20:09 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/01/18/a-few-new.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://typefully.com/?via=scott&#34;&gt;Typefully&lt;/a&gt; - a few options exist for posting to social media without logging in first. I am trying to get back into writing/sharing, and this seems like the best option.&lt;/p&gt;
&lt;p&gt;If Threads adds an API, all will be right.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://cronometer.com&#34;&gt;Cronometer&lt;/a&gt; - A replacement for MyFitnessPal. Short review: It is better in every way.  It is easier to use, quicker and costs less.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t track calories as much these days; with a change in my diet recently, I wanted to be a little more diligent.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.craft.do&#34;&gt;Craft&lt;/a&gt; is a notes app similar to Notion, but I find it more responsive and easier to use. Craft syncs across all my devices and has been a joy for the last few weeks.&lt;/p&gt;
&lt;p&gt;Via X: &lt;a href=&#34;https://twitter.com/scottw/status/1744751689666818462&#34;&gt;twitter.com/scottw/st&amp;hellip;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Hidden Month</title>
      <link>https://scottw.com/2024/01/18/the-hidden-month.html</link>
      <pubDate>Thu, 18 Jan 2024 09:14:41 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/01/18/the-hidden-month.html</guid>
      <description>&lt;p&gt;New Year, new ambitions: start your own business and take control of your time and finances.&lt;/p&gt;
&lt;p&gt;But here&amp;rsquo;s the harsh reality: the adrenaline fades, and most give up within weeks.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a suggestion&amp;hellip;&lt;/p&gt;
&lt;p&gt;Dedicate your weekends this year. Spend four hours each on Saturday and Sunday building your business.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a solid month (26 days) of focused work without sacrificing sleep or your regular schedule.&lt;/p&gt;
&lt;p&gt;Speed to market is overrated. Success comes from execution and continuous improvement, week after week, month after month.&lt;/p&gt;
&lt;p&gt;Posted initially to X: &lt;a href=&#34;https://twitter.com/scottw/status/1745104752818766318&#34;&gt;twitter.com/scottw/st&amp;hellip;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Troubleshooting Broken Software Tools Still Sucks</title>
      <link>https://scottw.com/2024/01/08/troubleshooting-broken-software.html</link>
      <pubDate>Mon, 08 Jan 2024 11:12:33 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/01/08/troubleshooting-broken-software.html</guid>
      <description>&lt;p&gt;20+ years of professionally building software, and here is how my last hour or so went.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;heroku login&lt;/code&gt; -&amp;gt; &lt;code&gt;zsh: killed heroku login&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;hmmm&amp;hellip;why did ZSH kill Heroku? It turns out it is just reporting what happened.&lt;/p&gt;
&lt;p&gt;Open the crash reports and get a bunch of text I barely understand.&lt;/p&gt;
&lt;p&gt;I remember that I have a second brain that knows what this crap means. &lt;strong&gt;ChatGPT, can you help me?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It does a decent job of educating me on what the reports (segfault on the main thread implies not even the hand of God can save this process).&lt;/p&gt;
&lt;p&gt;When in doubt, just re-install.&lt;/p&gt;
&lt;p&gt;No dice.&lt;/p&gt;
&lt;p&gt;Google shows me others with similar problems, but none related to Heroku, and nothing that looks promising.&lt;/p&gt;
&lt;p&gt;I wonder what the &lt;strong&gt;(brew) Doctor&lt;/strong&gt; thinks about this (Heroku was installed this way).&lt;/p&gt;
&lt;p&gt;The doctor takes her good old time getting back to me on this older Mac. But when she does respond, she says, &amp;ldquo;Dawg, your shit is F&amp;rsquo;ed Up!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I am paraphrasing, but there are unlinked things, kegs with no formula,, dry taps, an unbrewed dylibs, and likely most importantly, this:&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/92902/2024/installthetools.png&#34; width=&#34;600&#34; height=&#34;408&#34; alt=&#34;You missing the good tools message&#34;&gt;
&lt;p&gt;This is weird because I had previously checked for an update after an OS update, and I had tried to re-install the tools just to be safe.&lt;/p&gt;
&lt;p&gt;In both cases, it said, &amp;ldquo;there are no updates, go away&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Anywho, I run the following:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo rm -rf /Library/Developer/CommandLineTools&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo xcode-select --install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Eventually, notice the popup window (because why install them from the terminal), click OK to continue, and 10 minutes or so later, we are back in business.&lt;/p&gt;
&lt;p&gt;The key takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;This shit isn&amp;rsquo;t hard, it just takes patience.&lt;/li&gt;
&lt;li&gt;Even those who have done it for a long time still get stuck.&lt;/li&gt;
&lt;li&gt;There is nothing wrong with yelling profanities at a computer from the comfort of your basement.&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Finding Joy in Fitness: The Importance of Enjoying Your Training</title>
      <link>https://scottw.com/2024/01/08/finding-joy-in.html</link>
      <pubDate>Mon, 08 Jan 2024 09:40:30 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2024/01/08/finding-joy-in.html</guid>
      <description>&lt;p&gt;Last week&amp;rsquo;s early morning gym commutes were far from ideal. One morning, I attempted to leave and discovered I had a dead battery (actually, two dead batteries). Two days later, I had a close encounter with a deer while driving. Temperatures were in the low 30s at best.&lt;/p&gt;
&lt;p&gt;However, at approximately 4:35 am on a Monday (today), I found myself not only commuting to the gym again but also feeling quite excited to get to work.
It struck me then that it all comes down to finding the type of training you genuinely enjoy. While there may be exercises considered &amp;ldquo;the best&amp;rdquo; or &amp;ldquo;better,&amp;rdquo; even engaging in the most mediocre exercise is far better for you than doing nothing.&lt;/p&gt;
&lt;p&gt;This wasn&amp;rsquo;t always the case for me. It took some trial and error, along with the willingness to start something, fully commit to it, and then be open to finding something that I both enjoyed and that yielded better results.&lt;/p&gt;
&lt;p&gt;Ultimately, what truly matters is taking that initial step and sticking with it. It is crucial to listen to your body and determine what it will take to enjoy the work.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/12/12/sqlite-on-rails.html</link>
      <pubDate>Tue, 12 Dec 2023 09:38:49 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/12/12/sqlite-on-rails.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://fractaledmind.github.io/2023/12/12/sqlite-on-rails-litestream-ruby/&#34;&gt;SQLite on Rails: Introducing litestream-ruby&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/12/08/the-world-isnt.html</link>
      <pubDate>Fri, 08 Dec 2023 10:36:05 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/12/08/the-world-isnt.html</guid>
      <description>&lt;p&gt;Charlie Munge:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The world isn&amp;rsquo;t driven by greed. It&amp;rsquo;s driven by envy.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/10/13/ruby-enumerableany-at.html</link>
      <pubDate>Fri, 13 Oct 2023 08:48:26 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/10/13/ruby-enumerableany-at.html</guid>
      <description>&lt;p&gt;Ruby Enumerable#any?&lt;/p&gt;
&lt;p&gt;At first glance, this is not what I was expecting:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[false, false, false].any? =&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But a quick glance at the docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The method returns true if the block ever returns a value other than false or nil&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This can be handy for doing a bunch of small tests and detecting if anything passed.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/10/06/ai-code-review.html</link>
      <pubDate>Fri, 06 Oct 2023 09:03:23 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/10/06/ai-code-review.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://metabob.com&#34;&gt;AI Code Review via MetaBob&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Looks interesting. I will definitely try it out once they support Ruby.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/10/06/whos-harry-potter.html</link>
      <pubDate>Fri, 06 Oct 2023 09:01:24 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/10/06/whos-harry-potter.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.microsoft.com/en-us/research/project/physics-of-agi/articles/whos-harry-potter-making-llms-forget-2/&#34;&gt;Who’s Harry Potter? Making LLMs forget&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What do we do if we realize that some of our training data needs to be removed after the LLM has already been trained?&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/09/18/in-a-similar.html</link>
      <pubDate>Mon, 18 Sep 2023 14:31:53 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/09/18/in-a-similar.html</guid>
      <description>&lt;p&gt;In a similar vein to the &lt;a href=&#34;https://scottw.com/2023/09/18/a-truly-fantastic.html&#34;&gt;Bill Gurley&amp;rsquo;s video&lt;/a&gt;, Malcom Gladwell had a recent podcast highlighting the misinformation in bills that would &lt;a href=&#34;https://www.pushkin.fm/podcasts/revisionist-history/guns-part-3-a-shooting-lesson&#34;&gt;ban AR-15&amp;rsquo;s&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/09/18/a-truly-fantastic.html</link>
      <pubDate>Mon, 18 Sep 2023 14:27:20 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/09/18/a-truly-fantastic.html</guid>
      <description>&lt;p&gt;A truly fantastic talk by Bill Gurley on the regulatory landscape and the influence of special interests - &lt;a href=&#34;https://www.youtube.com/watch?v=F9cO3-MLHOM&#34;&gt;2,851 Miles&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Suggestions For Not Forgetting To Remove Unused Code</title>
      <link>https://scottw.com/2023/09/12/suggestions-for-not.html</link>
      <pubDate>Tue, 12 Sep 2023 08:38:36 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/09/12/suggestions-for-not.html</guid>
      <description>&lt;p&gt;I am working on a significant update/rewrite of part of KickoffLabs. I had a task to work through what we do in a Sidekiq worker. It was late on Friday when I got to this part of the code, and I decided to punt on it until Monday.&lt;/p&gt;
&lt;p&gt;Monday morning, I grab a cup of coffee and sit down to finish this section off and see a comment I missed (and had missed for a long time).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/92902/2023/f5wjcuawmaaokwf.jpeg&#34; width=&#34;600&#34; height=&#34;99&#34; alt=&#34;Code Comment, Remove in August 2019&#34;&gt;
&lt;p&gt;Outside the screenshot, there is an if statement, ensuring this code was not executed in the last four years. Still, it was frustrating that we had missed removing it for so long.&lt;/p&gt;
&lt;p&gt;I figured ChatGPT would have a good solution to stay on top of this, but it mostly just tried to explain to me how to use comments. :)&lt;/p&gt;
&lt;p&gt;Next up, I asked on &lt;a href=&#34;https://twitter.com/scottw/status/1701244816217038929&#34;&gt;Twitter&lt;/a&gt; (and &lt;a href=&#34;https://ruby.social/@Scottw/111047042095713444&#34;&gt;Ruby.Social&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Any suggestions (other than search) to ensure code like this gets cleaned up?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From there, I got a lot of good suggestions.&lt;/p&gt;
&lt;p&gt;Two Ruby gems looked interesting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/searls/todo_or_die&#34;&gt;todo_or_die&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/Shopify/smart_todo&#34;&gt;smart_todo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If I had to choose, I would go with todo_or_die since it would cause a failure/notification locally. I would rather not wait until there was a pull request/etc.&lt;/p&gt;
&lt;p&gt;The rest of the suggestions included if statements and date checks. My favorite, and the one I will likely adopt going forward, is to wrap it in a test that &lt;a href=&#34;https://ruby.social/@p8/111047287492099475&#34;&gt;fails after a specific date&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/09/11/a-nice-demo.html</link>
      <pubDate>Mon, 11 Sep 2023 08:46:27 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/09/11/a-nice-demo.html</guid>
      <description>&lt;p&gt;A nice demo of &lt;a href=&#34;https://www.youtube.com/watch?v=tGXe3xGW6to&#34;&gt;making an Alpine.js component&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/09/01/interesting-break-down.html</link>
      <pubDate>Fri, 01 Sep 2023 08:08:10 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/09/01/interesting-break-down.html</guid>
      <description>&lt;p&gt;Interesting break down of &lt;a href=&#34;https://sparktoro.com/blog/we-analyzed-millions-of-chatgpt-user-sessions-visits-are-down-29-since-may-programming-assistance-is-30-of-use/&#34;&gt;what people actually use ChatGPT for&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The last couple of days I have been working with TSRanges in PostgreSQL and it ChatGPT has made it extremely productive.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/08/25/nice-walk-through.html</link>
      <pubDate>Fri, 25 Aug 2023 13:40:32 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/08/25/nice-walk-through.html</guid>
      <description>&lt;p&gt;Nice walk through going from a &lt;a href=&#34;https://superails.com/posts/ruby-on-rails-144-why-i-am-replacing-turbo-frames-with-streams-for-actsasvotable&#34;&gt;Turbo Frame to Turbo Stream&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/07/20/fear-is-our.html</link>
      <pubDate>Thu, 20 Jul 2023 15:10:51 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/07/20/fear-is-our.html</guid>
      <description>&lt;p&gt;Fear is our true enemy&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;At the root of most fear is what other people will think of us. It&amp;rsquo;s paralyzing. It&amp;rsquo;s skewing. It distorts the very fabric of our reality &amp;ndash; makes us behave in such utterly insane and cowardly ways that it&amp;rsquo;s hard to even describe.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;- via Ryan Holiday in &lt;em&gt;Courage is Calling&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The older I get, the more I see this every day.  Science says we only use about 10% of brains.  But that is not what holds us back. We hold ourselves back because we are afraid of looking like a failure.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>From Minutes to Seconds</title>
      <link>https://scottw.com/2023/07/19/from-minutes-to.html</link>
      <pubDate>Wed, 19 Jul 2023 13:34:41 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/07/19/from-minutes-to.html</guid>
      <description>&lt;p&gt;I am tired of guessing the future of AI. What I am sure of, something routine like this used to take significantly longer than it does today.&lt;/p&gt;
&lt;p&gt;In the past:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Google a bit; maybe find someone asking a similar question somewhere (hopefully, do not get distracted by something else).&lt;/li&gt;
&lt;li&gt;Goto the docs (which often took Google to find)&lt;/li&gt;
&lt;li&gt;Try and find something related to what you were doing.&lt;/li&gt;
&lt;li&gt;(Sometimes) Apply what you learn in step #3 to your problem.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, we are back to a quick search, which is kind of like Google in the old days.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/92902/2023/braintree-payment-method-on-subs2x.png&#34; width=&#34;600&#34; height=&#34;517&#34; alt=&#34;Code showing how to update the payment method on a subscription in Braintree&#34;&gt;
</description>
    </item>
    
    <item>
      <title>The Fall of the 10X Developer</title>
      <link>https://scottw.com/2023/07/18/the-fall-of.html</link>
      <pubDate>Tue, 18 Jul 2023 10:08:22 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/07/18/the-fall-of.html</guid>
      <description>&lt;p&gt;This piece by Justin Searls has been making the rounds for a week or so.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://blog.testdouble.com/posts/2023-07-12-the-looming-demise-of-the-10x-developer/&#34;&gt;The Fall of the 10X Developer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My quick take: AI is leveling the developer playing field a bit. You still need to ask it proper questions to get good results. We have known for quite a while that colleges do not prepare you to be a good developer in the real world. AI won&amp;rsquo;t solve the preparation problem, but it will likely make new hires much more effective sooner.&lt;/p&gt;
&lt;p&gt;Still, like any other career, if you just mail it in, you will not truly grow.&lt;/p&gt;
&lt;p&gt;We must remember that today&amp;rsquo;s AI is much more pattern recognition than thinking. Maybe that will change, but for now, I still believe it is more assistive for most use cases than a replacement.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title></title>
      <link>https://scottw.com/2023/07/17/corsair-acquires-drop.html</link>
      <pubDate>Mon, 17 Jul 2023 15:51:46 -0500</pubDate>
      
      <guid>http://scottwater.micro.blog/2023/07/17/corsair-acquires-drop.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://drop.com/talk/131332/step-forward-in-our-evolution&#34;&gt;Corsair Acquires Drop&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Today, I have the honor of sharing with you yet another step in our evolution. Drop has been acquired by Corsair.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They say the brand will continue, but unfortunately, its value has been ruined over the last couple of years.&lt;/p&gt;
&lt;p&gt;I have had mostly good experiences with Drop, and I thought adding a desk accessory category recently was an intelligent move.&lt;/p&gt;
&lt;p&gt;My guess is Drop disappears in less than two years.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
