Dan Ott

Today I Learned

Notes from the field of day to day software development.

Wed 16 Oct 2019

Rails 6 introduces a new auto loader called zeitwork. The literature has me convinced transitioning to this new loader will be worth the effort. The legacy of the Rails apps we’ve built has us opting for the :classic loader in the immediate.

A nice thing about using :classic, is that Rails provides some deprecations that point toward getting ready for :zeitwork. As an example, autoloading constants in an initializer (config/initializers/[something].rb) emits a deprecation warning in Rails 6.

DEPRECATION WARNING: Initialization autoloaded the constants Sortable, Foldable, and Launderable.

Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails.

Reloading does not reboot the application, and therefore code executed during initialization does not run again. So, if you reload Sortable, for example, the expected changes won't be reflected in that stale Class object.

`config.autoloader` is set to `classic`. These autoloaded constants would have been unloaded if `config.autoloader` had been set to `:zeitwerk`.

Please, check the "Autoloading and Reloading Constants" guide for solutions.

Following the Autoloading and Reloading Constants guide, and some supplementary Duck Duck Go’ing, I learned about the Rails.configuration.to_prepare API. Where initializers run once when the app is booting, blocks passed to to_prepare will be run before every request in development, and once before eager loading in production.

For a handful of constants that are being autoloaded in an initializer, using to_prepare did the trick.

-ActiveRecord::Base.send :include, Sortable
+Rails.configuration.to_prepare { ActiveRecord::Base.send :include, Sortable }

Thu 3 Oct 2019

I'm intentionally slow to script things away in my day-to-day development. I understand the benefits of scripting away repetitive tasks. But I've found that they come at the cost of empathy.

I used to have an alias of be for running bundle exec. When I'd pair with newer developers, they'd begin to think that be is a command they should know, when in fact it was a customization they'd need to borrow. So I've formed the habit of always typing out bundle exec.

It takes a little more time. But when someone is looking over my shoulder, their learning how to do do the thing rather than learning my abstraction about how to do the thing.

Fri 27 Sep 2019

While authoring a very boring HTML form I learned that you do not need a for/id pair on a label that is wrapping a radio input.

-<label for="radio_option_1">
-  <input id="radio_option_1" type="radio" name="cheese" value="cheddar"> Cheddar
+<label>
+  <input type="radio" name="cheese" value="cheddar"> Cheddar

Thu 26 Sep 2019

React Native's <StatusBar /> component behaves similarly to <Helmet /> from react-helmet. Render them anywhere in the tree. Every time an element is rendered, it's props get pushed onto the top of a stack that will reconcile as a cascade of props for imperatively updating outside values.

The source of StatusBar.js was particularly helpful, because I discovered it just as I was arriving at a near-identical solution for doing something similar in an app I'm working on.

Wed 18 Sep 2019

git rebase --exec is a handy tool for ensuring that each commit is keeping the tests passing.

Ensuring that each commit keeps the tests passing is in service of an ideal I've been pursuing. I don't like submitting commits with the phrase "fix tests" in the description. I like refactoring old functionality or introducing new functionality under green tests.

To do this, I tend to work in two phases lately.

Phase one is an exploratory branch where I'm figuring things out. This is me thrashing through the jungle with a machete.

Phase two is a refined branch, where I'm communicating to others what I figured out. This is more surgical with detailed commits, explaning what changed, why, and alternatives that were considered in my exploratory thrashing branch.

Before submitting phase two for consideration, I'm starting to get in the habit of making sure the tests passed each step of the way.

$ git rebase --exec "bin/rails test" master

That will run the Rails tests on every commit that contributed to the current branch.

Mon 19 Aug 2019

React's Developer Tools are accessible from Safari!

For the last three years I've been developing day-to-day in Safari. Whenver this practice comes up in conversation the first question is "what about React Developer Tools?" Until today my answer was to drop into Chrome.

Today I discovered that react-devtools can be launched as a standalone application that can be connected to.

I wired this up in my Rails app with some development only logic.

<% # app/views/application.html.erb %>
<% if Rails.env.development? && ENV["CONNECT_TO_REACT_DEVTOOLS"] == "yep" %>
  <%= javascript_include_tag "http://localhost:8097" %>
<% end %>

With this in place, starting a Rails server with the appropriate environment flag does the trick. bin/rails server CONNECT_TO_REACT_DEVTOOLS=yep

Thu 1 Aug 2019

Updating for security vulnerabilities with yarn can be tricky. For example, I got a Github warning that my version of js-yaml needed to be updated. js-yaml does not appear in my package.json. It is one of my dependencies' dependencies.

yarn update js-yaml@secure-version adds js-yaml into my dependencies, which is not what I want to communicate to the team.

Trying to solve for this communication problem brought me to Yarn's selective dependency resolutions. This seems to fit the exact bill of what I'm trying to achieve. Among the reason's to use selective dependency resolution is listed:

A sub-dependency of your project got an important security update and you don’t want to wait for your direct-dependency to issue a minimum version update.

Force yarn.lock to update appropriately, without communicating to a future maintainer that this is actually a dependency of our app. Works on my machine.

Fri 11 Jan 2019

Use date.to_s(:db) when you need to use a date in a Rails YAML fixture. My muscle memory reached for date.format(:db), but that's wrong. It's .to_s

cookie_monster:
  last_wanted_cookies_at: <%= 1.second.ago.to_s(:db) %>

Mon 15 Oct 2018

When testing ActiveJob, jobs performed with SomeJob.perform_now will not show up in the performed_jobs test helper method.

Thu 30 Aug 2018

Today I was attempting to validate that three variables all held the same value. My clever approach was:

a = 1
b = 1
c = nil

[a, b, c].uniq.one?

I expected that to return false. It was returning true.

🤔

Showing my reasoning, elementary math style:

[a, b, c].uniq.one?

# becomes =>

[1, 1, nil].uniq.one?

# becomes =>

[1, nil].one?

There are two values, right? Here's my mistake. In my quest for object-oriented-message-sending-purity I interpreted one? to be about the size of the array. But reading the docs, I'm reminded it's not a message about the size of the array. It's a message that passes each item in the array to a block, and returns true if exactly one of those return values is truthy. Without an explicit block, the implicit block is the element itself. 1 is truty. nil is not, so yes, there is one truthy value.

What I really wanted is [1, nil].count == 1. In the end, I reverted to some obvious as day boolean logic of a == b && b == c

I'm glad I wrote a test for something so small, because this particular error in this particular place would have led to a substantial security hole.

Lesson learned: just because there's a nicely named message that sounds right doesn't mean it's the message I want to send. Also, quit trying to be clever.

Wed 11 Jul 2018

At some point in time Rails added a nicety to ActionMailer that I've really appreciated. The nicety I mention is that an html formatted email will be automatically translated into a plaintext email counterpart that is not perfect, but passable when iterating on a blossoming application.

As our applications mature we're upping our efforts on accessibilty. Within that effort, the automatically generated plaintext emails are being replaced by artisinal plaintext emails.

We're good about writing tests, but our tests broke when we moved from providing the single part to providing multiple parts.

Today I rel-learned how to get to the different parts of the email when they're being provided explicitly.

-assert_includes email.body.encoded, "some expected text"
+assert_includes email.html_part.body, "some expected text"
+assert_includes email.text_part.body, "some expected text"

Thanks again Stackoverflow.

Tue 30 May 2017

Ruby's defined? method is useful, but the syntax is a little surprising at first glance!

# These two are similar, but you call them different
defined? @whatever # call with the actual identifier
instance_variable_defined? :@whatever # call with a symbol of the identifier

defined? returns a string of what identifier is. In this case, nil or "instance-variable".

instance_variable_defined? returns a bool.

When nil is an acceptable memoized value, I often reach for a pattern of.

def whatever
  return @whatever if instance_variable_defined? :@whatever
  @whatever = some_expensive_computation
end

For tersness that communicates just as much, this could become

def whatever
  return @whatever if defined? @whatever
  @whatever = some_expensive_computation
end

Wed 31 Aug 2016

Today I needed to re-raise an error in ruby, but change the error message slightly. It turns out this is baked right in to the use of raise!

do
  # something that raises
rescue => e
  raise e, "This is my modified message"
end

The docs!

Tue 3 May 2016

import statements can be picky When using ES2015. With the variety of Babel plugins and configurations it's easy to have some technically invalid syntax that compiles just fine in that configuration.

// family.js
export default {
  george: "Grandpa",
  michael: "Dad",
  maeby: "Cousin",
}

// uses_family.js
import { maeby } from "./family.js"

I don't know what plugin I was relying on but previously this import statement worked. It turns out that destructuring off a default export is not actually a thing.

Option 1:

// family.js
export const george = "Grandpa"
export const michael = "Dad"
export const maeby = "Cousin"
export default { george, michael, maeby }

// uses_family.js
import { maeby } from "./family.js"

Option 2:

// family.js
export default {
  george: "Grandpa",
  michael: "Dad",
  maeby: "Cousin",
}

// uses_family.js
import Family from "./family.js"
const { maeby } = Family

Wed 9 Mar 2016

Rails will save associations by default. If you build an in-memory representation of a record that's not intended to be saved with a call to update you need to do one of two things.

  1. Set autosave to false. Example: has_one :other_thing autosave: false.
  2. Don't associate the built record with the object you're saving

This default behavior manifested itself because I was doing something of the form:

class Thing
  has_one :other_thing # 1. Use autosave: false

  def optimistic_version_of_other_thing
    OtherThing.new(thing: self) # 2. don't associate the record with self
  end
end

thing = Thing.last
thing.optimistic_version_of_other # I don't want this persisted
thing.update(anything: "else")

Wed 27 Jan 2016

I'm almost always wanting to add configuration hooks to Rails engines when I build them. Up to this point I've never taken the time to figure out the most succinct way to provide configuration hooks. It turns out that it's much simpler than I even imagined.

module MyGem
  class Engine < ::Rails::Engine
    isolate_namespace MyGem

    config.my_gem = ActiveSupport::OrderedOptions.new
    config.my_gem.some_setting = true
  end
end

That's it! Setting config directly in the class definition will expose the configuration in the consuming Rails application.

Thu 7 Jan 2016

Today I re-learned about ActionController::TestCase. It's pretty common to send the request parameters in as the second argument. What I always forget is that the third and fourth parameters represent the session and the flash respectively.

test "something that depends on the session and the flash" do
  post :create, { always: "money" }, { in: "the" }, { banana: "stand" }
  assert_equal params[:always], "money"
  assert_equal session[:in], "the"
  refute_equal flash[:banana], "stand" # because the flash will have been flushed!
end

Mon 7 Dec 2015

Multiple calls to ReactDOM.render can be a super powerful tool. Given a state-ful component Counter

// Initial render into document.body
React.render(<Counter count=0 />, document.body)
// => { state: { count: 0 }, props: { count: 0 } }

// Something internal happens that changes state to 50
// => { state: { count: 50 }, props: { count: 0 } }

// Re-render without unmounting into document.body
React.render(<Counter count=100 />, document.body)
// => { state: { count: 50 }, props: { count: 100 } }

In many use-cases, flux is introduced to distinguish between props and state. If props are changing on the server, this is a nice light-weight way re-render everything, while maintaining some client-side state. (Super helpful in rails in my usage)

Fri 27 Nov 2015

While playing with webpack in a Rails environment, there are still some variables I want to expose to the window. I came up with this 2-liner to treat exports from the top most bundle as global variables!

export { Whatever, You, Want }
Object.assign(window, exports)

Sat 14 Nov 2015

When using elm make, the output is determined by the extension.

elm make Main.elm --output main.html # generates html
elm make Main.elm --output main.js # generates js

Mon 9 Nov 2015

Put it in a Rakefile. Why incur the overhead of ruby my_script, when you can just type rake? It takes a little more time to write, but being able to return to a small folder and type rake -T by convention makes you faster in the long run.

Tue 29 Sep 2015

You are not allowed to complain about what you are not willing to confront. I saw that in a tweet a while ago, and it is a good word.

Prefixing methods that will change what is on output to the screen with render_ sounds verbose, but it has been helpful as I've started to use it more. Another React convention that is informing other areas of development.

Wed 12 Aug 2015

CORS is real. The part that really tripped me up, is the preflight OPTIONS request.

You can handle this options request in Rails with a nice routing constraint.

match '*', to: 'cors#handle', via: %i(options)

Then set your Access-Control-[Whatever] headers accordingly, and you're in business.

Wed 22 Jul 2015

There are a lot of ways that payment can fail. Thankfully, Stripe provides ways to trigger almost every conceivable failure. And they put the credit card values that will trigger these failures into a handy doc.

It's important to remember, some of these failures will occur when attempting to get a Stripe token. Others will fail when attempting to use the Stripe token.

Mon 20 Jul 2015

When using Bugsnag within a Rails app, you must use the before_bugsnag_notify macro within your ApplicationController (or similar) to add custom info. I tried to be clever, using the Bugsnag.before_notify_callbacks manually in an initializer. This didn't work. My assumption is that Bugsnag.before_notify_callbacks.clear is called somewhere internally in the Railtie.

It pays to read the Bugsnag ruby docs.

Mon 22 Jun 2015

In Rails' Mailer Previews, you can append .text to the URL to view the text version by default. Way better than reloading, and having to select text from the dropdown!

Mon 15 Jun 2015

Rails.application.routes.url_helpers can be used to access your URL helpers from anywhere.

Wed 20 May 2015

In JavaScript, document.activeElement gives you the active element. How did I never know this?

Tue 21 Apr 2015

<<-SOMETHING.strip_heredoc in Ruby, to strip the heredoc, and align to the current level of indentation. Doc

Tue 3 Mar 2015

When working in Ruby, JSON.parse(payload, symbolize_names: true) will automaticaly symbolize the names.

Mon 2 Feb 2015

There is no magic for Rails' enum in fixtures. If you want to use the string value (which is the entire point of using the enum), you need to ERB it up. Something like

michael:
  name: Michael Bluth
  occupation: <%= Person.occupations[:actor] %> # because 'actor' won't do it

Fri 9 Jan 2015

You can use %i[some symbols] in ruby to generate [:some, :symbols]

Tue 25 Nov 2014

You can use reorder on an ActiveRecordRelation to replace all existing order methods. It's super useful when merging scopes that may have imported their own ordering that is no longer applicable.

Mon 24 Nov 2014

Study the docs. As an example, there are so many methods on Enumerable that you can leverage to eliminate temporary variables. Don't spend all your time reading the docs, but every once and a while it's good to review what's already built in the standard library that you don't need to re-invent.

If you're looking up a record by anything other than the primary_key, don't accept arbitrary arguments. Lazily looking up a record is great, but if you're using something like

def destroy_do_dad(expecting_an_id_but_an_object_was_passed_in)
  DoDad.where(foreign_system_id: expecting_an_id_but_an_object_was_passed_in).destroy
end

you have a huge vulnerability. #to_param, will come into play, and you're now potentially finding a completely arbitrary record and destroying it.

Fri 21 Nov 2014

Let the database do all of the heavy lifting for counting your ActiveRecord objects.

Record.pluck(:owner_id).each_with_object(Hash.new(0)) { |id, counts|
  counts[id] += 1
}

Sums up the number of records for a owner in Ruby. The same can be achieved via ActiveRecord.

Record.group(:owner_id).count

Mon 10 Nov 2014

When accessing the Rails flash messages, use strings instead of symbols. I thought it was a HashWithIndifferentAccess...but it doesn't appear to be.

Sat 8 Nov 2014

Responding to http in Go on Dreamhost is pretty easy. The trick is to use FactCGI to serve it up.

I did this via a trivial conditional for my setup of developing on OSX, and deploying to Linux.

if runtime.GOOS == "linux" {
  fcgi.Serve(nil, nil)
} else {
  http.ListenAndServe(":2112", nil)
}

To cross-compile to Linux, you use the Go runtime flags.

GOOS=linux GOARCH=386 go build

Fri 7 Nov 2014

I was reminded how evil DST is. It's just the worst. Writing tests around it is the best. Just a reminder that 2am-3am on March 8, 2015 DOES NOT EXIST.

Thu 6 Nov 2014

Some random commands from tinkering with Vagrant today:

  • dscacheutil -flushcache # Flush the DNS cache

JavaScript Array's map function takes a optional second argument, of what to bind to this.

Wed 5 Nov 2014

Gulpfiles. I don't know what I learned about them, but I helped a friend troubleshoot why gulp.watch() and gulp-sass combined to produce a Segmentation Fault 11. Something with libsass, and rolling back the node-sass version fixed it.

Computers.

Tue 4 Nov 2014

Gourmet Service Objects is a post worth returning to from time to time. After slogging through some code to understand what it did, I wrote it into a service. The code wasn't substantially different, but the fact that it had a name as first-class citizen instantly made it easier to understand.

Because my dudepal @jabronus gave me an SVG of my avatar, I started learning how to use SVG in the browser. I even made my avatar blink using CSS animations. Supes cool.

Mon 3 Nov 2014

git log --pretty=oneline gives you the one-line per a commit output.

git log --pretty=format:"%an" gives you formatting on commits. All the interpolations available are listed in the man git-log under "PRETTY FORMATS".

attr_readonly exists in Rails for making fields immutable. But we're in Rails, so we don't use big words like immutable, we use readonly. Caveat: you can still change the values in memory, which has other consequences around validations and any derived values.

Wed 29 Oct 2014

Having opinions is more fun that trying to please everyone. Engage in helpful debate with reasonable people. Change your mind when presented with new data that should change your mind. Attack the problems together. Defend your teammates, not your opinions.

Thu 23 Oct 2014

You can be incredibly productive, but feel unproductive. Fixing lots of small bugs leads to this. Keeping a list of everything completed such as Trello helps you remember that you're making a lot of small progress when it feels like you're not making any large progress.

There are many ways to write even the simplest statement.

# Whitelisted arguments.  # Which is most readable while being concise?

# 1
trusted = %w[approve reject].find { |i| i == untrusted }

# 2
trusted = (%w[approve reject] & [untrusted]).first

# 3
trusted = %w[approve reject].include?(untrusted) ? untrusted : nil

# 4
trusted = case untrusted; when *%w[approve reject]; untrusted; end

Wed 22 Oct 2014

Aggressively eliminate state. After working with ReactJS for a bit, I am starting to see the merits of eliminating state. I'm even beginning to like methods prefixed with get and set. Eliminating state means eliminating side-effects.

Writing adolescent JS code seems to lead to better JS code. What I mean by adolescent code, is that it refuses to take responsibility. It lets somebody else own the state of the world. All it does is ask for values, and do something with them.

If you want to launch Vim from a Ruby script, use system 'vim'. Backticks won't work, because they want to consume STDOUT. exec takes over the process, so you couldn't do work afterwords. system works.

Tue 21 Oct 2014

Take breaks.

Lately, I've been taking breaks. Today I didn't. And at end-of-day, the difference in energy level left for friends & family is substantially depleted.

Mon 20 Oct 2014

Small changes are easier than big changes. Renaming things seems like a huge win, but when you're renaming big things, small subtleties will emerge. These pitfalls are easier to see in others' proposed changes than your own.

If you're going to rename something, have a good purpose for it.

Look for the dumb solution. Sometimes refactoring isn't needed yet. You don't know enough about the problem to find the better solution, yet.

Thu 16 Oct 2014

It's important to know how to revert a commit, because sometimes you accidentally commit strait to master.

If you caught yourself before pushing upstream, you're good. Just

git checkout -b temporary
git branch -D master
git checkout master

This assuming it automatically pulls master back down from upstream, which is my default setup.

If you did push upstream, you'll basically want to create a commit that reverts the latest commit. Hopefully it's only one commit.

git format-patch -1 HEAD
git apply [newly_created_patch_name] -R

Wed 15 Oct 2014

You have to mull through a lot of terrible names, and iterate on terrible code to arrive at a consistent, reasonable API. Today I was building an ActiveSupport::Concern whose purpose will be to cache attributes that are expensive to compute. The names of the methods changed a handful of times. And even now they may not be finalized.

I left the code for a few hours, and returned to it with a fresh set of eyes. The names were terrible. Their implied meaning was confusing. But because of this gap, I was able to see some of of the consistent shortcomings. I also saw patterns emerging, that lead to a more consistent API for the final iteration (of the day).

Tue 14 Oct 2014

I started making a before_filter that would redirect a request from a bot. After returning to the code, I decided it would be better to rescue_from SomeCustomError, with: :what_was_previously_a_before_filter so that the code did not have to run on every single request. This probably amounts to minimal performance gains in terms of actual execution time, but I think it brought clarity to the code by having the happy path occur first.

Taking the time to organize your folders/files into something predictable needs to be done quickly when you're working with branches of code. Every divergent branch means a delay in making it happen.

Mon 13 Oct 2014

Reminded of a proposal I made long ago. For responsive design, start at one size, and head in one direction, using either min-width or max-width, but not both.

I spent time today moving some behavior of a model into a concern, specific to that model. Rather than putting it into app/models/concerns/person_pending_request_count I put it into app/models/person/pending_request_count. I liked this change, because it communicated that only Person should be using this. It's not a general mixin, though it could easily become one. Then in the person class, I just included it with include Person::PendingRequestCount.

Tue 30 Sep 2014

Rails UJS' ajax:success and ajax:error only apply to XHRs originating from a data-remote="true" source. If you'd like them to apply to all XHRs in your page or application, you're gonna have to wire that up.

In slim, you can use <, > or both after your tag to add leading or trailing whitespace, respectively.