Today I Learned

A Hashrocket project

129 posts by doriankarter @dorian_escplan

Copy files with progress in terminal w/rsync

When you need to transfer a lot of files from one location to another it’s sometimes useful to have some progress indication and maybe even a speed measure, or time remaining.

I recently had to transfer a few gigabytes of data from one computer to another. For this task I chose to use Rsync, since it is a command line utility that can preserve file metadata (permissions) and easily resume in case of an error.

Rsync ships with macOS by default, but if you want to get a more recent version, you can install it from homebrew.

There are two options for showing progress:

If you are transferring a few really big files you can use the --progress flag.

rsync -ah --progress source destination

This will list each file as it being transferred and show you the progress and speed in which the file is being transferred.

In my case I had a lot of small files so I chose to use --info=progress2.

rsync -ah --info=progress2 source destination

This will output something like this

2.26G  16%    6.13MB/s    0:05:51 (xfr#375313, to-chk=0/1165396)

Which represents the progress, speed and estimated time remaining for the entire transfer.

Show escaped bash color codes in less #linux

My ls command colors directories and files according to their type and permissions:

ls with color

But when the window is too small to fit the content I pipe the result into less:

less broken

Which cannot correctly parse the escape code from ls and turn them into color. To fix that add -r to the less command:

solution

Notes:

My l alias is gls -F -G --color --group-directories-first -lah (gls is GNU ls)

You can alias less=less -r if you want this to be the default behavior for less.

Vim: Peek horizontally without moving the cursor

Some people like wordwrapping in Vim. Personally I find it annoying for code because it can destroy the meaning of the code in the context that it was formatted in by the original author, making it hard to understand where one line begins and ends.

Turning wordwrap off though, can put you in a situation where you have a line of text slightly longer than the screen can show, and you may want to scroll a bit to the right/left for reference but not move your cursor and lose your position with ^ or $.

Vim allows you to do that with zs and ze mappings.

DEMO demo

Learn more with :h zs or :h ze.

Show materialized view definition in #postgresql

PSQL offers a handy \dv function for showing the definition of a view. That function does not seem to work on materialized views.

To see the definition of a materialized view use the following instead:

select pg_get_viewdef('search_documents');

Another option is to use \d+ search_documents which shows both the query and the columns.

(Replace search_documents with the name of your view.)

Hide all other windows on #macOS

Sometimes your desktop gets cluttered with windows of different running applications which makes it difficult to concetrate on the task at hand.

This can also come in handing when sharing your screen or presenting.

Fortunately macOS offers some shortcuts to remediate this common problem:

⌘+h - hide current window

⌘+⌥+h - hide all other windows

⌃+⌘+f - toggle full screen mode

Using `yarn global` w/ Node through asdf (mac)

The asdf version manager is an awesome tool for managing many different language runtime versions. I recently started using it for Node and noticed that yarn global no longer plays nice with it.

Turns out that if you install yarn through homebrew - your global directory will not take into account the asdf node version and path.

To correct this, first uninstall yarn from homebrew:

brew uninstall yarn

Then in your .zshrc or equivalent remove the yarn global path (see my previous post about Yarn global)

Now you want to find where asdf is being sourced into your .zshrc and insert a dynamic yarn global path:

~/.zshrc

# asdf global version manager
source "$HOME/.asdf/asdf.sh"
source "$HOME/.asdf/completions/asdf.bash"

# set yarn binaries on path
# must be below the .asdf source commands ^
export PATH="$(yarn global bin):$PATH"

You can now install yarn again from npm which will put it in your asdf versioned node:

npm i -g yarn

When done restart your terminal and test to see that everything worked. I had to delete the ~/.config/yarn/global to make this work.

echo $PATH should contain something that looks like /Users/dkarter/.asdf/installs/nodejs/9.11.1/.npm/bin which should match yarn global bin.

Custom errors in JavaScript ⚠️

Javascript provides the ability to create custom errors by modifying the prototype of a function to the Error protorype. This is how one would create a custom error:

function ValidationError(message) { 
  this.name = 'ValidationError'; 
  this.message = message; 
}
ValidationError.prototype = Error.prototype;

// USAGE
throw new ValidationError('the form is invalid');

To catch it you can check the class of an error using instanceof:

try {
    // do stuff
  throw new ValidationError('the form is invalid');
} catch (ex) {
    if (ex instanceof ValidationError) {
    alert(ex.message); // the form is invalid
  } else {
    // crash and burn
    throw ex;
  }
}

📅 Get begining and end of week with Moment.js

To get the date for benining of week for a date in moment you can use the startOf and endOf functions on moment objects.

const today = moment();
const from_date = today.startOf('week');
const to_date = today.endOf('week');
console.log({
  from_date: from_date.toString(),
  today: moment().toString(),
  to_date: to_date.toString(),
});

// {
//   from_date: "Sun Apr 22 2018 00:00:00 GMT-0500",
//   today: "Thu Apr 26 2018 15:18:43 GMT-0500",
//   to_date: "Sat Apr 28 2018 23:59:59 GMT-0500"
// }

NOTE: Remember that the start of week will depend on the user’s locale settings on their machine. If you want to use start of week based on ISO 8601 you can use moment().startOf('isoWeek');

The same technique can be used for start of month (startOf('month')), start of year (startOf('year')) and even quarter (startOf('quarter');).

Case insensitive `in` query in #postgres #psql

If you have a list of strings and you want to query a column to get all the matching records, but you do not care about the casing, Postgres offers a cool and easy way of doing that with the citext extension.

Given this table:

id | company_name
 1 | Abibas
 2 | Nykey
 3 | Pumar

We want to match the following:

select company_name
from vendors 
where company_name in ('Abibas', 'NyKey', 'PUMAr');

First you will have to make sure you have the citext extension created if you haven’t already:

create extension citext;

Then you can cast the searched field to citext:

select company_name
from vendors 
where company_name::citext in ('Abibas', 'NyKey', 'PUMAr');

h/t joshbranchaud for helping me find this

Delete all node_modules dirs recursively with find

If you have hundreds of past JavaScript projects sitting in your workspace folder, you probably also have hundreds of node_modules folders nested inside of them, and hundreds of thousands actual npm modules resting peacefully in those.

Often enough all you care about is the code that uses the modules and not the modules themselves, so to save yourself some precious laptop diskspace you can just delete all those folders! When you need them again cd into the project directory and run yarn install or npm install.

First let’s do a dry run:

find . -name "node_modules" -type d -prune

and now that you checked the output of the above command you can delete all the nested node_module folders.

If you are still feeling paranoid (and you’re on macOS) you can simply move those to the Trash:

find . -name "node_modules" -type d -prune -exec trash '{}' +

If you feel a little braver just unlatch the airlock and toss them into a black hole 🕳 using rm -rf

find . -name "node_modules" -type d -prune -exec rm -rf '{}' +

I saved a whopping 80GB with this technique 🤑. Hope you find it helpful.

Use a proxy on curl/wget commands

Using a proxy can be a good way to debug http issues. Unfourtunately setting the proxy on macOS globally does not apply to all command line utilities.

On Curl for example you can set the proxy using the --proxy flag:

curl http://example.com --proxy 127.0.0.1:8080

Or by adding the following to your ~/.curlrc configuration file for a more persistent setting:

proxy = 127.0.0.1:8080

A similar thing can be done with the wget utility by editing the ~/.wgetrc and adding:

http_proxy = http://127.0.0.1:8080

Get application's current version in production

If you use Distillery to produce Elixir releases for production you may be in a situation where your application is deployed and running but you are not sure what version is loaded into memory.

To verify the version:

  1. SSH into your server
  2. navigate into the bin directory of your release (e.g. app_release/your_project/bin)
  3. run the remote console ./your_project remote_console
  4. Run the spec function on Application: Application.spec(:your_project, :vsn)

You will get back a string such as ‘0.0.1’.

Difference between output of two commands #linux

Recently I’ve been playing around with ripgrep (rg) which is a tool similar to Ack and Ag, but faster than both (and written in Rust FWIW).

I noticed that when I ran a command in Ag to list all file names in a directory, and counted the number of files shown, I was getting a different number than the comparable command in ripgrep.

ag -l -g "" | wc -l
# =>      29
rg -l "" | wc -l
# =>      33

This led me to wonder if there is an easy way to view the diff between the output of the two commands.

I know I can save the output into a file and then compare the two files with the builtin diff command in linux, however I don’t see a reason to write to disk for such a comparison.

This is how you would do that without writing to disk:

diff <(ag -l -g "") <(rg -l "")

The diff printed out by this command is inaccurate, so you will need to add a sort to each command:

diff <(ag -l -g "" | sort) <(rg -l "" | sort)

Pipe text into Vim from stdin

You can pipe text into vim directly from stdin. This can be helpful if you want to edit the output of a long bash command, or if you want to see what an installation script contains before piping it into bash.

curl -fsSL https://raw.github.com/cknadler/vim-anywhere/master/install | vim -

The key is to use the - after the vim command to make it read from stdin. This will open a new buffer with the output of the previous command.

Postgres automatically appends timezone

Dates can be notoriously hard, particularly when it comes to timezones.

If you select a date without timezone information and cast it to a data type w/timezone, Postgres will assume the timezone of the server:

select '2000-01-01'::timestamptz;
┌────────────────────────┐
│      timestamptz       │
├────────────────────────┤
│ 2000-01-01 00:00:00-06 │
└────────────────────────┘

I’m located in CST hence the -06 at the end of the time specification (6 hours before UTC time).

To use a specific timezone such as UTC, instead of your server’s timezone which can be pretty arbitrary:

select '2000-01-01'::timestamptz at time zone 'UTC';
┌─────────────────────┐
│      timezone       │
├─────────────────────┤
│ 2000-01-01 06:00:00 │
└─────────────────────┘

Postgres now displays the time in UTC. Still probably not what you expected (6am?) - Postgres infers the 00:00:00 time in your timezone and converts it to UTC (in my case adding 6 hours).

So how would you get it to show 00:00:00 and still be in UTC timezone?

select '2000-01-01 00:00:00 UTC'::timestamptz at time zone 'UTC';
┌─────────────────────┐
│      timezone       │
├─────────────────────┤
│ 2000-01-01 00:00:00 │
└─────────────────────┘

If you use timezone aware data types it is recommended to always specify the timezone when inserting data, otherwise you are in for a world of trouble.

Quick syntax reference for #SQL directly in #psql

Whenever I forget the syntax for a certain SQL command in Posgres I usually reach for Dash or simply search DuckDuckGo for the the specific command. That usually yields the Postgres official documentation website which is great…

Wouldn’t it be nice though if I could stay right inside psql and get the documentation I am looking for?

It would.. and it’s possible:

\h create index

create index screenshot

Use \h followed by the SQL command - this is not the full verbose documentation that you would find on the Postgres docs website but it’s more of a syntax reference - which is most of the time what you need.

If you are not sure what to type or simply want to explore new commands try typing \h without anything after it - you will see something like this:

slash h by itself screenshot

Treat words with dash as a word in Vim

By default vim treats two words connected with an underscore e.g. search_tag as a vim word text-object.

This is very useful because you can then use motions and commands on them such as daw to delete a word, or ciw to change inside the word.

That is not the case for dash separated words e.g. search-tag. These types of words are very common in CSS class names and HTML IDs.

To make vim treat dash separated words as a word text-object simply add the following to your .vimrc:

set iskeyword+=-

And source it again.

Jump between #git hunks in Vim with vim-gitgutter

One of my favorite Vim plugins is vim-gitgutter - it shows gutter markings for changes in the current buffer when that file is tracked by git.

It looks like this:

+ = new lines - = deleted line ~ = changed line

It also comes with a few very useful commands for working with hunks, sections of changed code in your file.

To jump between hunks you can use ]c and [c. Since I don’t really use the return and backspace keys in normal mode I have mapped those instead:

nnoremap <silent> <cr> :GitGutterNextHunk<cr>
nnoremap <silent> <backspace> :GitGutterPrevHunk<cr>

This is especially useful on big files (more than a bufferful) with scattered changes.

Simple text file #encryption with Vim

Vim provides a simple text file encryption feature. To make use of it add the following to your .vimrc:

set cryptmethod=blowfish2

This will set the encryption to the strongest algorithm vim supports.

Now to use it simply start editing a file with the -x flag:

$ vim -x mysecret.txt

You will be prompted for a password, and password confirmation. After that you should be able to edit the file and save it normally.

When you open the file again with vim (even without the -x flag) you will be asked to type your password to decrypt the file. If you enter the wrong password all you’ll see is gibberish.

This is not the strongest encryption out there but it works and should suffice for most personal use cases.

NOTE: this will not work with NeoVim.

List all available extensions in #Postgres

Postgres comes packed with extensions just waiting to be enabled!

To see a list of those extensions:

select * from pg_available_extensions;

This will list the extension’s name, default_version, installed_version, and the comment which is a one liner description of what the extension does.

Here’s an interesting one for example:

name              | earthdistance
default_version   | 1.1
installed_version | ø
comment           | calculate great-circle distances on the surface of the Earth

To enable an extension, simply call create extension on the name:

create extension if not exists earthdistance;

whatis command and apropos in #linux #bash

Ever wonder what a command you are using is? Turns out linux has the answers for you!

Simply type whatis followed by the name of the command.

Examples:

$ whatis bc
bc(1)         - An arbitrary precision calculator language
$ whatis brew
brew(1)       - The missing package manager for macOS
brew-cask(1)  - a friendly binary installer for macOS

whatis uses the man pages to search your entered query.

There is also a reverse search, which searches the descriptions of commands. For example say you are looking for a calculator:

$ apropos calculator
bc(1)         - An arbitrary precision calculator language
dc(1)         - an arbitrary precision calculator

h/t this tweet by Peter Cooper

Stop #bash script on error #linux #zsh

If you are writing a procedural bash script, you may want to stop execution if one of the steps errored out.

You can write error handling for each step, but that can get quite verbose and make your script hard to read, or you might even miss something.

Fortunately bash provides another option:

set -e

Simply place the above code at the top of your script and bash should halt the script in case any of them returns a non-true exit code.

Caveats: this will not work in all cases, for example it does not work for short circuited commands using &&/||.

If you want it to work when one of your operations in a pipe fails you will need to add the pipefail flag (not supported on some systems set -o | grep pipefail to check your system):

set -e -o pipefail

If you have a script that always returns a non true return code and that’s fine you can override set -e for that command with:

set +e
your_command_goes_here
set -e

At this point I consider it a best practice to include this statement in every script I write.

List filenames of multiple filetypes in project

Ag (aka The Silver Searcher) is an amazing piece of software. It allows you to define file types (like Ack) and comes prepackeged with some file types.

Using this feature you can list all files of a specific type in your project. For example say we want to list all Ruby and JavaScript files:

ag --ruby --js -l

Ag has the added benefit over Ack, that it ignores gitignored files, so you only get the files that matter (and not stuff from node_modules etc).

To see what filetypes Ag supports:

ag --list-file-types

The list is pretty extensive! Unlike Ack however, there is currently no way to add new file types or extend the list.

Make console.log stand out with custom css style

I know your browser console is full of messages because you are debugging something, and that creates a lot of noise. Now you are adding a new console.log, and you need it to stand out above the rest.

Maybe you are like facebook and just want to warn your users from pasting in code in the browser in social engineering attacks.

facebook

To style a console.log message use the %c interpolation and pass it a css style. e.g.

console.log('%c%s', 'color:red;font-size:5em', alert)

In the example above %s means inerpolate the object into the output string.

preview

Compatibility: tested to work on Firefox, Chrome, and Safari.

h/t Dillon Hafer

Storing recurring schedules in #Rails + #Postgres

If you have a scheduling component to your Rails application you may need to store the day of week and time of day in the database.

One way to store the day of week is to use an integer column with a check constraint that will check that the value is between 0 and 6.

create table schedules (
  id serial primary key,
  day_of_week integer not null check(day_of_week in (0,1,2,3,4,5,6)),
  beg_time time not null,
  end_time time not null
);

Then when you read it back from the database and need to convert it back to day name you can use Date::DAYNAMES. e.g.:

[2] pry(main)> require 'date'
=> true
[3] pry(main)> Date::DAYNAMES
=> ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
[4] pry(main)> Date::DAYNAMES[0]
=> "Sunday"
[5] pry(main)>

If you need to store time of day as entered (in a time without timezone column - as specified above) check out the wonderful Tod gem by Jack Christensen

More useful Homebrew searches #macOS #homebrew

Homebrew, the third-party package manager on macOS, allows searching for packages by name, but the list that comes out only contains package names. That’s not always very useful, particulary when you are not sure what you are looking for.

To get the package description along with the package name simply add --desc to your brew search command.

For example, let’s look for a library for performing file diffs with color highlighting:

$ brew search --desc diff
apgdiff: Another PostgreSQL diff tool
cdiff: View colored diff with side by side and auto pager support
cern-ndiff: Numerical diff tool
colordiff: Color-highlighted diff(1) output
cppad: Differentiation of C++ Algorithms
dhex: Ncurses based advanced hex editor featuring diff mode and more
diff-so-fancy: Good-lookin' diffs with diff-highlight and more
...

You can also search using regex in both the description and name of the package as long as you supply the --desc option:

$ brew search --desc /[cC]olor.*[dD]iff/
cdiff: View colored diff with side by side and auto pager support
colordiff: Color-highlighted diff(1) output
icdiff: Improved colored diff

Joining URI parts in Elixir

Elixir 1.3 introduced a standard way to join URIs.

For example, say we have a base URI for an API: https://api.hashrocket.com and different endpoints on that URI: events, developers, applications.

To join the URI into one properly formatted string:

def endpoint_uri(endpoint) do
  "https://api.hashrocket.com"
  |> URI.merge(endpoint)
  |> URI.to_string()
end

# then call it

endpoint_url("events") # => "https://api.hashrocket.com/events"

URI.merge accepts both strings and URI structs as the first object so you can easily continue adding URI parts to the pipeline including query params:

"https://test.com"
|> URI.merge("events") 
|> URI.merge("?date=today") 
|> URI.to_string()

# => "https://test.com/events?date=today"

Magically insert `iex -S` in front of a command

Often times you need to execute an elixir function with iex to enable pry breakpoints.

I found that I was doing a lot of fumbling in zsh to go back to the previous command, jump to the beginning of it and type out iex -S.

Since I like to automate repeatitive processes, I came up with this:

bindkey -s "^Xi" "^[Iiex -S ^[A"

Dump this line in your .zshrc or .bashrc and then all you have to do is Ctrl+xi to insert iex -S in front of the previously ran command.

preview

Magic. 🎩

Download all of humble bundle books in parallel

Humble Bundle is a great site which offers technical book bundles. The problem is that they present the user with a huge list of links for all the different formats and it is a tedious task to right click each link and save it to your hard drive.

In order to solve this you can open the Developer Tools while on the download page and paste the following:

var pattern = /(MOBI|EPUB|PDF( ?\(H.\))?|CBZ|Download)$/i;
var nodes = document.getElementsByTagName('a');
var downloadCmd = '';
for (i in nodes) {
    var a = nodes[i];
    if (a && a.text && pattern.test(a.text.trim()) && a.attributes['data-web']) {
        downloadCmd += 'wget --content-disposition "' + a.attributes['data-web'].value + "\"\n";
    }
}
var output = document.createElement("pre");
output.textContent = downloadCmd;
document.getElementById("papers-content").prepend(output);

credit: https://gist.github.com/graymouser/a33fbb75f94f08af7e36

This will add a pre tag to the page with a bunch of wget commands. Go ahead and copy those to your clipboard.

Using GNU Parallel (brew install parallel). First save the contents of your clipboard into a file, for example download_jobs then run the following command:

parallel -j 4 < download_jobs

Replace 4 with the number of cores you have on your machine.

Then sit back and watch your directory get populated with files.

Surround every line in a file using sed

To replace every line in a file you can use linux’s built in sed utility:

For example given a file like this:

dkarter/backpack
junegunn/fzf
junegunn/fzf.vim
junegunn/vim-peekaboo
junegunn/gv.vim
terryma/vim-multiple-cursors
scrooloose/nerdtree
dyng/ctrlsf.vim
haya14busa/incsearch.vim
killphi/vim-legend
neomake/neomake

If we want to surround each line with Plug '$content_of_line' we can run the following command:

sed -e "s/\(.*\)/Plug '\1'/" .vimbundle.local

Output:

Plug 'dkarter/backpack'
Plug 'junegunn/fzf'
Plug 'junegunn/fzf.vim'
Plug 'junegunn/vim-peekaboo'
Plug 'junegunn/gv.vim'
Plug 'terryma/vim-multiple-cursors'
Plug 'scrooloose/nerdtree'
Plug 'dyng/ctrlsf.vim'
Plug 'haya14busa/incsearch.vim'
Plug 'killphi/vim-legend'
Plug 'neomake/neomake'

If the result is what we expected we can add -i flag to write the file in place, updating it with our changes:

sed -ie "s/\(.*\)/Plug '\1'/" .vimbundle.local

Pass name of function as a block

In Ruby it is possible to pass a function name instead of a block to a function expecting a block using &method in the last argument.

For example:

def blocky_fun(foo)
  bar = "hello #{foo}"
  yield(bar)
end

blocky_fun('blocky', &method(:puts))

The result of the above would be that the puts function will be called with “hello blocky” as the first argument.

Prettier ignore! 💁 #javascript

Prettier is super helpful but sometimes you just want to format things your way if the output of prettier is not very readable.

To solve this, prettier provides a comment that you can put above any “node” in the resulting javascript AST.

For example:

BEFORE (w/ prettier)

const street_number = this.findAddressComponent(
  resultObj,
  'street_number'
).long_name;
const route = this.findAddressComponent(resultObj, 'route').long_name;
const zip_code = this.findAddressComponent(resultObj, 'postal_code')
  .long_name;
const city = this.findAddressComponent(resultObj, 'locality').long_name;
const state = this.findAddressComponent(
  resultObj,
  'administrative_area_level_1'
).short_name.toUpperCase();

The above is a result of prettier formatting and is not very readable or pretty - so I would need to turn it into a single AST node and put the prettier-ignore comment over it:

AFTER (w/ prettier-ignore)

// prettier-ignore
const address = {
  street_number: this.findAddressComponent(resultObj, 'street_number').long_name,
  route: this.findAddressComponent(resultObj, 'route').long_name,
  zip_code: this.findAddressComponent(resultObj, 'postal_code').long_name,
  city: this.findAddressComponent(resultObj, 'locality').long_name,
  state: this.findAddressComponent(resultObj, 'administrative_area_level_1').short_name.toUpperCase(),
}

Now the address components will be accessible from the address object (e.g. address.route) and while still not the prettiest, it is a lot more readable IMO.

Enable history in IEX through #erlang (OTP 20) ⏳

If you are using the latest version of Erlang, OTP 20 now ships with shell history, so you can use Ctrl-p / Ctrl-n or the up/down arrow keys.

The shell history is turned off by default though, so you will have to turn it on by adding the following to your .zshrc/.bashrc etc.

export ERL_AFLAGS="-kernel shell_history enabled"

Once you do that make sure to source your bash config file or open a new window.

Every subsequent iex session will now have shell history. 🚀

"experience uses an unsupported version of Expo"

error

If this happens when developing a React Native app with Expo and trying to test it in the iOS Simulator, it means the version of Expo on the iOS Simulator is out of date.

To fix that try the following:

  1. Quit the Expo app on the simulator
  2. Uninstall the Expo app on the simulator
  3. Lunch the app again in the simulator from the Expo XDE

This will cause the Expo app on the simulator to reinstall with the latest version.

Move window (tab) in tmux

To move a new window to the left do this:

tmux-prefix, :swap-window -t -1

To move window to the right

tmux-prefix, :swap-window -t +1

You can also bind a key to that command:

bind-key S-Left swap-window -t -1
bind-key S-Right swap-window -t +1

Now it will be tmux-prefix, Shift + Left and tmux-prefix, Shift + Right

Require local package in mix.exs

In Elixir as you are writing your application it is recommended to split parts of it into smaller applications (also can be called micro-services if you want to be buzzword compliant).

You don’t however need to publish those dependencies to the Hex package manager in order to load them, instead you can use the path argument when defining a dependency.

In this example we have our main application Foo and in the directory above it we have an application called Bar.

To make the Bar module available in Foo we can do it like so:

mix.exs

defp deps do
  [
    {:bar, path: "../bar"},
  ]
end

Module attribute constants nil in Elixir

Module attributes in elixir (@something) can be used as constants and assigned a value, however one must make sure that the value assigned to the constant is available at compile time.

For example when using a constant that loads environment variables if the value is not available in compile time they will resolve to nil.

@salt System.get_env(:your_app_name, "AUTH_SALT")

So either export the environment variables before compiling, or don’t use module attributes, instead you can use a function:

defp salt do
  System.get_env(:your_app_name, "AUTH_SALT")
end

Consider:

  • The downside to using a function is that it will be re-evaluated each time.
  • The downside of exporting env-vars before compile is that they you might forget and your app will crash in production. You can circumvent that by writing a script for compilation.
  • You can also call System.get_env from your config.exs but make sure to run mix clean after doing so since the compiler seems to cache the config compilation.

Convert nested JSON object to nested OpenStructs

If you are parsing a nested JSON string such as this:

{
   "vendor": {
       "company_name": "Basket Clowns Inc",
       "website": "www.basketthecloon.com"
 }

And want to access it with dot notation, simply doing:

OpenStruct.new(JSON.parse(json_str))

will not do!

Turns out there is a cool option on JSON.parse called object_class:

JSON.parse(json_str, object_class: OpenStruct)

Now you can access the resulting object with dot notation all the way down:

obj.vendor.website #=> "www.basketthecloon.com"

Set filetype/settings for a specific file in Vim

Given a file with a weird extension but an underlying known filetype e.g. yaml file with a .config extension. It is possible to force vim to set the filetype to yaml for that file:

At the top/bottom of the file add a comment (with the filetypes acceptable comment syntax - for yaml it is #):

# vi: ft=yaml
baz:
  foo: 'bar'

Re-open the file and vim will automatically set the filetype to yaml for that file.

This can also be used for setting other setting such as shiftwidth, tabstop etc

I’ve been using this trick for a while but keep forgetting it the exact syntax, usually using # vim: instead of # vi:. Hopefully my wording will make it more easily duckduckgoable.

Set foreign key to null on delete in #Postgres

Say you have a table with a foreign key:

posts
------
id serial primary key
...
primary_image_id references images (id)

images
------
id serial primary key
post_id references posts (id)

If you attempted to delete an image from the images table while that image’s ID is referenced in one of the posts you will receive an error preventing you from deleting the image. Postgres is trying to protect your data integrity by preventing foreign keys that point to records which don’t exist.

To prevent errors and allow deleting records from the images table freely you must define the on delete strategy on the posts table.

One of the options available to you is set null so if you are creating a new table it will look like this:

create table posts (
  id int serial primary key,
  -- ...
  primary_image_id int references images (id) on delete set null
);

Now if the primary image is deleted it will set the primary_image_id to null.

This an alternative to on delete cascade which in this case will delete the post from the posts table and is not what we want.

Read the full documentation under ‘5.3.5. Foreign Keys’

Serve static files/directories in Phoenix

Phoenix will by default server some files and directories from priv/static. More specifically css/, fonts/, images/, js/, favicon.ico and robots.txt. If you need to add a new directory however simply creating it in priv/static will not make Phoenix serve it. You need to explicitly add it.

Say your project is called ChicagoElixir you will need to go to your endpoint configuration, typically in: lib/chicago_elixir/web/endpoint.ex. There you will find the following configuration:

  plug Plug.Static,
    at: "/", from: :chicago_elixir, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

Simply add the new folder or file name to the list in only and restart your Phoenix server.