Featured

Planet Fitness Cancellation Template

Update: I put this in the mail on Tuesday and got cancellation confirmation less than 48 hours later.

Also, your mileage may vary, some locations are a little more of a stickler about certified mail, but you should get a confirmation from the location within a few days if they’ve accepted.

I needed to cancel Planet Fitness in the middle of the COVID-19 pandemic and I really didn’t want to be showing up at a gym in person right now (time and unnecessary risk) just to cancel.

I called up my local Planet Fitness and was told I could either come in in person *or* send a [yes, snail mail] letter to the Planet Fitness I signed up at.

So, I’ve thrown together a cheap template to help you include everything you need to cancel (it’s just name, date of birth, and signature, according to the person on the phone.)

You can download the doc I put together at: Planet Fitness Cancellation Word Doc template

XML Handler Bypassing ParseError Handling Rack Middleware on Rails 6

In a project using actionpack-xml_parser, and implementing a modified version of Catching Invalid JSON Parse Errors with Rack Middleware to also handle XML errors, an upgrade to Rails 6 broke the handling of the ParseError. Interestingly enough, JSON was being handled as expected.

TL;DR: The short answer to this is that config/initializers/wrap_parameters.rb only had json mentioned as a format. This can be remedied in the initializer or on a per controller basis. The entire app fix was just adding :xml to the initializer:

ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json, :xml]
end

In looking through the stack traces/call stack for where the parse error occurs (ActionDispatch::Http::Parameters#parse_formatted_parameters) using the caller array in the gem source and backtrace in the rescue, I was able to catch that ActionController::Metal::ParamsWrapper#_wrapper_enabled? was called for JSON, but not for XML. Ultimately, I was able to track down the difference to ActionController::Metal::ParamsWrapper#_wrapper_formats?, which was only returning [:json], which led to the wrap_parameters functionality in Rails.

Pandemic Brisket

Sliced BrisketAt the beginning of the pandemic last year, I wasn’t dealing well with it. The scare TP and *everything else* phase hit a little hard on top of all of the other panic. So I smoked a brisket. Here’s the general procedure:

Equipment / supplies

Rub

  • 1/4 c packed brown sugar
  • 2 tbsp smoked paprika
  • 2 tbsp sea salt
  • 1 tbsp garlic powder
  • 1 tsp onion powder
  • 1 tbsp chili powder
  • 1 tsp black pepper

Brisket

I had better luck with brisket from Sam’s Club than Costco, but this was at the early phases of the pandemic, so that might have been part of the problem. 10 lb brisket, good marbling and a fairly trim layer of fat on one side.

Smoking

40% oak wood and 60% charcoal aiming for 250-275℉ on the hood thermometer. I let the oak wood burn fuel for the fire with whatever smoke it produced. I laid the brisket fat side up at the other end of the charcoal grill chamber from the smoker box and maintained charcoal/wood fire for 5 hours.

Low grill indirect heat

After about 5 hours, I’d gotten as much smoke as I wanted, so I put the brisket on an old cookie sheet and wrapped fairly tightly with heavy duty aluminum foil and put in the propane grill chamber opposite the burner that was lit as low as it would burn. At this point I stuck in the digital thermometer and monitored for 195℉. Once it hit target temperature, I brought it inside and let it cool a bit (mostly to make sure everything else is ready).

Leftovers

I know it will be blasphemy to most, but you can carve up extra thick slices (~1 inch thick) and freeze, for later slow warming in the toaster oven.

Vortex Race 3 TKL LED Programming for Mac

I decided to switch to my Vortex Race 3 TKL for a more programming-centric job from the Vortex Tab 90M that felt a little better for email and spreadsheets. The Race 3 has Cherry MX Silent Reds vs. the Vortex Tab 90M’s Cherry MX Silent Blacks.

A couple of things that were a little bit difficult to follow were:

  1. How to set up the Race 3 for Mac (it had been used on a Windows PC most recently and never really used on a Mac). This Reddit thread gives some suggestions on how to optimize that.
  2. How to program the LEDs (for just basic effects):

The LED programming was fairly simple once I understood it (see here for a copy of the manual). The cool LED effects when you type are accessible by pressing the Pn key + one of the ‘1’ through ‘5’ keys. ‘4’ and ‘5’ have multiple effects:

Pn + ‘4’ cycles different “display single color LED mode”s which means that the interactive LED effects appear in context with your

  • [no effect?]
  • Interactive mode – keys light up as you type.
  • Flash vortex mode – keys pulse around the keyboard originating from your keystrokes
  • Aurora mode – keys pulse in color around the keyboard originating from your keystrokes

Pn + ‘5’ cycles different “Display full color LED mode”s which means “display on all the keys regardless of context”:

  • [no effect]
  • Full key light mode – just 100% on
  • Breath mode – keyboard pulses
  • Vortex mode – RGBs cycle in different phases with each other.
  • Rain drop mode – random drops of “color” appear on the keys

Pn + X – brightness down for the above effects

Pn + V – brightness up for the above effects

Pn + [< key] and Pn + [> key] are LED speed up and down.

AWS Certified Developer – Associate (DVA-C01) Experience

It’s been a little over 1 month since my AWS Certified Solutions Architect – Associate (SAA-C02) exam. After finishing the Solutions Architect Associate exam, I immediately registered for the Developer Associate exam, which I took yesterday and passed.

Wasting Time

I took a practice exam (through Linux Academy) shortly after passing the SAA exam, having actually gone through most of a Developer Associate training course. DO NOT DO THIS UNLESS YOU ARE READY TO DIVE DEEP INTO ANYTHING YOU MISSED. I managed to get about 72% on the practice exam, which is essentially a passing score so I let myself lose focus over the holidays.

Next mistake was going through a couple of versions of the course (30 hours each) instead of just using the practice test results to read the white papers, FAQs, and AWS service pages I needed to review. (Linux Academy/aCloudGuru courses provide links to associated AWS reference for this.)

Foundations in SAA

I highly recommend getting the Solutions Architect – Associate first. It covers a good breadth of topics that will give you a solid foundation. I recommend the Linux Academy courses that have labs that you can walk through via a transcript just to traverse all the areas you need to know.

Differences from SAA

All of the AWS Code* services factor into a significant chunk of the DVA exam. If you have solid experience with docker, k8s, and git, you’ll probably have a better than 50% chance on questions about services that you didn’t look into, but there are also things that are about the “AWS way” or the “way that AWS services push you to do things” that I shook my head at. (Physical team organization? Wat?)

Also…

  • Learn how to set up X-Ray on the various compute environments.
  • Create and deploy a Serverless Application Model project and dig into the configuration files for it.
  • 0 bytes is the minimum object size, 100 MB is the recommended threshold to use multipart upload to S3, 5GB it is required, 5TB is the object size limit.
  • cfn-init
  • View Protocol Policy in CloudFront (https/http)
  • Bucket policies for enforcing server side encryption
  • Learn supported platforms for CodeDeploy
  • Learn lifecycle hooks for CodeDeploy (just look at the diagrams a few times)
  • Learn which services trigger Lambda asynchronously and synchronously
  • Lambda requires dependency packaging, and CPU is controlled by RAM allocation.
  • Dead letter queues
  • Lambda, by definition, does not do in-place deploy (because in-place requires a tangible server)

Reinstalling a Clean Copy of Mac OS X Yosemite

Use How to get old versions of macOS to download an installer for OS X Yosemite while running on Yosemite. Run the installer and it will install the full installer to /Applications/Install OS X Yosemite.app

Use the command line from How to create a bootable installer for macOS substitution El\ Capitan for Yosemite in the El Capitan instructions.

Hold down option while powering on the Mac to get the option to boot from USB Recovery.

Reviews of M1 Macs for Development

So far, the key interest for me have been the potential drastic improvement in battery life. I’m almost willing to sacrifice a bit of short term productivity to get there. I don’t know that the experiences sound any worse that trying to get my favorite development environments productive on Windows or a non-Debian flavor of Linux:

JetBrains and Xcode-centric perspective (11/23/2020):
https://medium.com/before-semicolon/is-m1-mac-worthy-or-good-for-developers-developer-review-3ed832f4105e

Rubyist perspective (11/25/2020):

https://www.driftingruby.com/episodes/a-rubyist-s-apple-m1-review

macOS 11 Big Sur compatibility on Apple Silicon · Issue #7857 · Homebrew/brew · GitHub (closed as of 12/26/2020):

https://github.com/Homebrew/brew/issues/7857

TypeScript/JavaScript/Rust (12/3/2020):

https://dev.to/redhoodjt1988/how-to-setup-development-environment-on-new-macbook-pro-m1-3kh

Rails’ DHH’s own experience (12/28/2020):

DriftingRuby tweet:

How to Cancel Humble Choice

I tried to cancel Humble Choice a couple of months ago, after getting sucked in to a membership a few months ago, but either I didn’t complete the cancellation, or I didn’t have enough time to completely follow-through. I had a little bit of a challenge finding the cancellation link this time, so I wrote it up here just in case anyone else experienced the frustration. No idea if it works on mobile, I went to the desktop this time to make sure that I could see the entire menu.

  • Manage Your Plan
  • Scroll down to [Cancel Your Subscription] button
  • Scroll down to [Cancel My Membership]
  • Select a reason and [Cancel My Membership]
  • Click [Cancel My Membership] on the popup.

Fuji EnviroMAX AA vs. Sunbeam Batteries

So we have a battery hungry front door lock that requires 4-AA batteries to power. I noticed that the Sunbeam batteries purchased from Dollar Tree (3 pack for $1, or $1.33 per replacement) were getting replaced every month or so, so I was curious if that was normal as well as how much I was really paying for them.

Tracking in a spreadsheet, the Sunbeam batteries lasted from 23 to 36 days, with an average of 28.8 days. At $1.33 per replacement / 28.8 days… that door cost 4.61¢ per day with the Sunbeam batteries.

Thinking maybe I could do better, I bought a 96 pack of EnviroMAX Batteries (affiliate link) on Woot (but for 25¢ each instead of being similar in price to the Sunbeam… so it was $1 per replacement instead of the $1.42 at the current moment for that link.) The batteries lasted 13-25 days, with a generous average of 21 days, or about 4.7¢ per day… The last replacements were more like 2 weeks and I stopped tracking them, but there were sets in the middle that made it 3 weeks or better, but it was horribly inconsistent.

I just bought a large pack of Kirkland batteries, so I’ll be sure to track the performance of those and see how it lines up with the other batteries.

Sylvania vs. Amazon Basics LED bulbs

I’ve been tracking the Sylvania 60W 2700K Equivalent A29 (affiliate link) vs. the AmazonBasics 60W Soft White Non-Dimmable (affiliate link) over the past year and a half after noticing that I was replacing bulbs a lot. With 40+ bulbs in the house, it’s not as uncommon as I would have initially expected to be replacing a bulb on any given day.

I have a limited amount of data so far, but definitely have an apples to apples (same socket in same fixture) comparison, and it looks like the Amazon Basics bulb lasted 242 days vs. the Sylvania’s 308. While I have no other apples-to-apples comparisons, the remaining Sylvania bulbs have lasted at least ten months.

The above links have a 24-pack of AmazonBasics at $27.99, or 0.48¢ per day vs. the Sylvania at $22.88, or 0.31¢ per day.

ASCII/Punycode TLDs for Internationalized Domains

IANA has a list of TLDs alphabetically by domain. One thing notable are domains of in the following format:

XN--VERMGENSBERATER-CTB
XN--VERMGENSBERATUNG-PWB
XN--VHQUV
XN--VUQ861B

These are internationalized TLDs, specified in a Punycode encoding for the unicode domain name. Some of these representations are just for non-ascii characters:

XN--VERMGENSBERATER-CTB,vermögensberater,wealth consultant

Others are representing fully non-Latin alphabets:

XN--FPCRJ9C3D,భారత్,India

Most of these non-Latin representations are either country designations, brand names, or a telecom-centric term.

If you want to experiment on your own, install simpleidn gem and sign up for a Rapid API key and the Microsoft Translator API (it’s a little simpler interface than navigate individual vendor APIs)

# http://data.iana.org/TLD/tlds-alpha-by-domain.txt
require 'net/http'
require 'uri'
require 'simpleidn'
require 'openssl'
require 'json'


def translate(source)
  return source if source =~ /^[A-Za-z]+$/
  # using rapidapi for somewhat uniform API access (via a single key!)
  url = URI("https://microsoft-translator-text.p.rapidapi.com/translate?to=en&amp;api-version=3.0&amp;profanityAction=NoAction&amp;textType=plain")
  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE

  request = Net::HTTP::Post.new(url)
  request["content-type"] = 'application/json'
  request["x-rapidapi-key"] = ENV['RAPIDAPIKEY']
  request["x-rapidapi-host"] = 'microsoft-translator-text.p.rapidapi.com'
  request.body = "[
      {
          \"Text\": \"#{source}\"
      }
  ]"

  response = http.request(request)
  read_body = JSON.parse(response.read_body)
  [read_body[0]["detectedLanguage"]["language"],read_body[0]["translations"][0]["text"]]
end

puts 'ascii/punycode TLD,Unicode version,language,translation'
File.read(File.open('tlds.txt'))
#Net::HTTP.get(URI('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')) # I saved this locally
  .each_line
  .map { |a| a.strip }
  .reject {|n| n =~ /^#/ }
  .map { |a| [a, SimpleIDN.to_unicode(a), translate(SimpleIDN.to_unicode(a))].flatten }
  .reject {|n| n[1] =~ /^[A-Za-z]/ }
  .sort { |a, b| a[1].length <=> b[1].length }
  .each { |l| puts l.join(',') }