Today I Learned

A Hashrocket project

17 posts by briandunn @higgaion

Don't async await, especially in useEffect

Often I need to setState based on an async value. With hooks this is something like:

useEffect(async () => {
    const newVal = await asyncCall();
    setVal(newVal);
});

But wait!. This throws an error. React wants the return of useEffect to be a cleanup function.

The return type of an async function is Promise. So that won’t work. Best to just put on your big developer pants and use that promise.

useEffect(() => {
    asyncCall().then(setVal);
})

There. Now our useEffect returns undefined, and React is pleased.

Here is a sandbox if you want to see for yourself.

ProxyJump: simplest way to ssh with a jump host

Ever need to jump your ssh through an intermediate host? You may be familiar with using netcat like this in your ~/.ssh/config:

Host jumpy
  ProxyCommand ssh -q jump-host nc destination-host %p

ProxyCommand runs on our local machine. The command must open a tcp connection that ssh may then use for the session. Here we shell into our jump-host, connecting the file descriptors to nc, which will forward all data to destination-host on %p, the port you provided to -p on the cli.

Clear as mud, no?

Another slightly more readable way to achieve this is with -W

Host jumpy
  ProxyCommand ssh -W destination-host jump-host

This works the same way as the nc version, but now we are using ssh’s internal implementation of nc. So one less dependency on the jump host.

But behold, the most legit and legible version of jumping hosts:

Host jumpy
  Hostname destination-host
  ProxyJump jump-host

So same thing, but now the words say what it does.

When you get lost remember that .ssh/config has it’s own man page:

$ man ssh_config

Thanks Dillon!

Animate ReactNative in ClojureScript with Reagent

Reagent uses ratoms to refresh components. When the state of a ratom is changed, components that deref that state re-render. ReactNative Animation uses a similar technique: changes to an Animated.Value cause a re-render. I was afraid these concepts would collide, and I’d have to bypass Reagent using it’s low-level api to use animations. But much to my joyful surprise, Animated.Value changes trigger a re-render in normal Reagent components.

(ns my-app.animation
  (:require
    ["react-native" :as ReactNative]
    [reagent.core :as r]))

(def animated (.-Animated ReactNative))
(def text (r/adapt-react-class (.-Text ReactNative)))
(def animated-value (.-Value animated))
(def animated-view (r/adapt-react-class (.-View animated)))

(defn testview []
  (let [bounce-value (new animated-value 0)]
    (.setValue bounce-value 1.5)
    (-> bounce-value
        (animated.spring #js {:toValue 0.8 :friction 1})
        (.start))
    (fn [] [animated-view
            {:style {:flex 1
                     :transform [{:scale bounce-value}]
                     :background-color "red"
                     :border-radius 10
                     :shadow-color "#000000"
                     :shadow-opacity 0.7
                     :shadow-radius 2
                     :shadow-offset {:height 1 :width 0}}}
            [text "test"]])))

animation

This may look complex, but compare it to the low level Reagent api used in other examples.

Enable gzip for all phoenix responses

Enabling gzip for static assets in phoenix couldn’t be simpler. In lib/myapp_web/endpoint.ex change gzip from false to true:

 plug(Plug.Static, at: "/", from: :tilex, gzip: true, only: ~w(assets ...))

But why stop there? We can compress our dynamic document bodies just as easily. In config/prod.exs, add compress: true to the http config of the endpoint.

config :my_app, MyAppWeb.Endpoint,
  http: [port: {:system, "PORT"}, compress: true]

Rails on ruby 2.4: Silence Fixnum/Bignum warnings

Wanna use the latest ruby, but don’t really need to be perpetually reminded that rails hasn’t caught up with this latest ruby design change? Change the top of your config/application.rb thusly:

require_relative 'boot'

verbose = $VERBOSE
$VERBOSE = nil
require 'rails/all'
require 'active_support/core_ext/numeric/conversions'
require 'active_job/arguments'
$VERBOSE = verbose
Bundler.require(*Rails.groups)

module MyApp
  class Application < Rails::Application
  end
end

Yes, this will also suppress any other ruby warnings from the loading of rails code. ¯\_(ツ)_/¯

Postgres, varchar, and silent white space trimming

Given this table:

# create table initials (monogram varchar(2) primary key);

Insert a three character monogram.

# insert into initials(monogram) values ('BPD');
ERROR:  value too long for type character varying(2)

Ok, cool. Thanks Postgres. But what if my string ends in a space?

# insert into initials(monogram) values ('BP ');
INSERT 0 1

What? This caused me great pain when trying to do an insert. Here in my example I’ve used a CTE for brevity, but imagine you can’t see the input table, and it contains thousands of records. My insert went something like this:

with input as (
        select 'BD '::text as data
)
insert into initials(monogram)
select data from input where not exists (select 1 from initials where monogram = input.data)
;
ERROR:  duplicate key value violates unique constraint "initials_pkey"
DETAIL:  Key (monogram)=(BD) already exists.

A spot check based on the error message turns up no results:

select count(*) from input where data = 'BD';
count
-------
     0
(1 row)

But of course 'BD ' = 'BD', if you keep in mind that Postgres will silently truncate trailing whitespace when casting to a varchar.

Rerun only failed specs with tmux

You don’t want to run your whole sweet again, just those failures?

Copy the bit below Failed examples: into your tmux buffer:

rspec ./spec/mailers/order_mailer_spec.rb:5 # OrderMailer reciept includes the support email and phone number
rspec ./spec/features/customer_checks_out_spec.rb:43 # customer checks out happy path
...

Now make command line args for rspec out of it:

tmux showb | ag -o '[^\s]+:\d+' | tr '\n' ' ' > rerun.txt

Run only this set of specs with xargs:

xargs rspec < rerun.txt