Today I Learned

A Hashrocket project

View your outdated packages

To see which packages in a mix app need updating, you can run mix hex.outdated

$ mix hex.outdated
Dependency           Current  Latest  Update possible
appsignal            1.3.2    1.3.3   Yes
basic_auth           2.1.4    2.1.4
cachex               2.1.0    2.1.0
...

It prints out a handy table letting you quickly view the current and latest versions and if they can be updated. If Update possible is No, check your semantic version lockdown of that package in mix.exs.

mix hex.outdated accepts a few arguments:

  • --all which shows all outdated packages, including children of packages defined in mix.exs
  • --pre which include pre-releases when checking for newer versions (be adventurous!)

Happy updating!

List Commits That Change a File

Today I’m wrapping a PR with a large Git rebase. There are many commits on my branch that change the same file (.circleci/config.yml) and aren’t valuable on their own. In the end, I want them all squashed into a single commit. The challenge is that I changed other files in between; it’s kind of a mess. How can I squash the branch down to something manageable?

One technique is to view just changes to my file:

$ git log --follow --oneline .circleci/config.yml

c9f7108 Try to decode file before unzipping
87c8092 Quick push
327d419 Try this!

Using this list, I can execute a rebase that just squashes the commits that touch the file, leaving everything else intact.

Getting BetterErrors in Rails While Using Ngrok

By default, the BetterErrors gem only works for localhost. If you’re using ngrok to access your rails server and you want to have access to BetterErrors, you’ll need to whitelist the IP of the machine that ngrok is running on.

Add the following to development.rb:

BetterErrors::Middleware.allow_ip!(NGROK_MACHINE_PUBLIC_IP)
# - or -
# Use an IP finding service to grab your public IP each time you start the server:
# (ipecho.net, api.ipify.org, etc.)
BetterErrors::Middleware.allow_ip!(open('http://api.ipify.org').read)

Generate New Phoenix App Without Brunch

By default when you create a new Phoenix app using phx.new, a set of files and configurations will be generated for Brunch. Though the Phoenix team decided to use Brunch, you don’t have to. You may not want Phoenix to handle asset building or you may just prefer another build tool. Either way, if you’d like to opt out, you can include the --no-brunch flag when generating the project.

$ mix phx.new --no-brunch my_app

If you have an existing project that you’d like to remove Brunch from, there is some information in Phoenix’s Static Assets documentation.

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

Fix The Spelling Of A Word

If there is a misspelled word (:set spell to see what is misspelled), you can navigate the cursor over that word and hit (from normal mode) z=. This will open up a screen of possible corrections. The one you are most likely looking for will be toward the top. Each possible spelling will have a number next to it. Enter the number of the word you are looking for and hit enter. The spelling of that word will have been updated.

Try misspelling something and give it a try yourself.

See :h z= for more details.

h/t Jake Worth

Visualize Your Elixir Dependencies

To visualize your Elixir dependencies, try this:

$ mix deps.tree

This prints a tree showing your dependencies (and their dependencies):

$ mix deps.tree
tilex
├── gettext ~> 0.13 (Hex package)
├── hackney 1.8.0 (Hex package)
│   ├── certifi 1.1.0 (Hex package)
│   ├── idna 4.0.0 (Hex package)
│   ├── metrics 1.0.1 (Hex package)
│   ├── mimerl 1.0.2 (Hex package)
│   └── ssl_verify_fun 1.1.1 (Hex package)

This is really handy when trying to track down where a deprecation warning or error is coming from.

h/t Dorian Karter

Serve Static Assets From Custom Phoenix Directory

When you new up a Phoenix project, an endpoint.ex file will be generated. This file is full of different plugs for handling incoming traffic. The Plug.Static declaration specifies how your application will handle and serve requests for static files.

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

The from options declares where these static files are located. In this case it references our application (:my_app) as the target which will translate to its priv/static directory.

If you instead want to serve your files from a different, custom directory, you can replace it with the path to that directory.

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

source

Implied applications and `extra_applications`

In your mix file (mix.exs) the application function returns a keyword list. Two options in that list determine what applications are started at runtime.

The applications is by default implied based on your app’s dependencies. The default list is thrown away through if this option is set in your mix.exs file.

If you want to add an extra application without disrupting the default, implied list then you can add the optionextra_applications. This leaves the default, implied list of applications untouched.

H/T Jose Valim PR

def application do
[
  mod: {Tilex, []},
  extra_applications: [:logger]
]
end

Specifying The Digest Directory For Phoenix

By default, Phoenix targets priv/static when preparing digested assets for production. This process happens when running mix phx.digest.

If you are doing some custom work with your assets such that they are in a different location, you’ll need to tell Phoenix where to look. To do this, just include an optional path argument.

$ mix phx.digest path/to/my/assets

The digests will be put in that target directory. If you’d like to specify a different output directory, such as priv/static, include the -o flag.

$ mix phx.digest path/to/my/assets -o priv/static

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"

`cd` in subshell

With many of our projects sequestering the front end javascript code into an assets directory I find myself moving between the root project directory and the assets directory to perform all the npm or yarn related tasks in that assets dir. Inevitably I’ll start doing something like this:

cd assets; npm install; cd ..

or this

pushd assets; npm install; popd

In both cases using ; instead of && puts me back in the original directory regardless of the result of the npm command.

I just learned that using cd in a subshell does not change the directory of the current shell, so I can also do this:

(cd assets; npm install)

Ruby srand returns the previous seed

srand is a method on Kernel that seeds the pseudo random number generator. It takes a new seed as an argument or calls Random.new_seed if you don’t pass an argument. What’s interesting about it is that it returns the old seed. This has the effect of return a new large random number every time you call srand.

2.4.1 :007 > srand
 => 94673047677259675797540834050294260538
2.4.1 :008 > srand
 => 314698890309676898144014783014808654061
2.4.1 :009 > srand
 => 102609070680693453063563677087702518073
2.4.1 :010 > srand
 => 81598494819438432908893265364593292061

Which can come in handy if you’re playing some Ruby golf and need to generate a huge random number in as few characters as possible.

H/T Dillon Hafer

Capitalize All The Words In PostgreSQL

PostgreSQL provides the string function initcap() as a way of capitalizing all words. In the process, it cleans up the casing of the remaining parts of the words.

Here are some examples of how it works.

> select initcap('hello, world');
   initcap
--------------
 Hello, World

> select initcap('HELLO, WORLD');
   initcap
--------------
 Hello, World

See the String Functions and Operators docs for more details.

Conditional Variables in Phoenix Templates

A common Ruby on Rails technique is setting instance variables in a controller action, then using them in the view layer:

# app/controllers/users_controller.rb
def show
  @show_button = true
end
# app/views/users/show.html.haml
- if @show_button
  .button

Doing the same thing in Elixir/Phoenix is a little different.

Since Phoenix 0.14.0, the framework raises on missing assigns (link). Thus our conditional will fail on any loaded template whose controller function that does not define the variable.

One solution is to check if the variable is assigned:

# lib/my_app_web/templates/users/show.html.eex
<%= if assigns[:show_button] do %>
  <div class="button"></div>

If show_button is assigned in your controller function (via assign), the button will be displayed. If not, the button will not be displayed, and the application will not raise an error.

Run an Elixir function on Module load

If you want to run a function on Module load, you can turn to Modules @on_load hook.

#hello_world.ex
defmodule Example do
  @on_load :hello_world

  def hello_world do
    IO.puts("Hello World!")
    :ok
  end
end
$ elixir hello_world.ex
Hello World!

Your function must return :ok otherwise the module load will be aborted.

Using @on_load can be an efficient way to run examples, quick scripts, setup requirements, benchmarking or load in libraries from other languages.

Converting strings to atoms safely

If your elixir system accepts any outside inputs and takes any part of those outside inputs and calls String.to_atom with the input as an argument then your elixir system is subject to a denial of service attack.

Malicious actors can submit input designed to dynamically create a large number of atoms until the atom limit is reached, knocking out your elixir applications.

Consider using String.to_existing_atom instead. If the argument to this function cannot be converted to an existing atom then an exception will be thrown.

> String.to_existing_atom("I don't exist")
** (ArgumentError) argument error
    :erlang.binary_to_existing_atom("nothere", :utf8)
> String.to_atom("I don't exist")
:"I don't exist"
> String.to_existing_atom("I don't exist")
:"I don't exist"

Current number of atoms in the atoms table

In Elixir and Erlang there is a hard limit on the number of atoms you can create. Atoms are not garbage collected so its important to ensure you don’t exceed the limit. You can check what the limit is with:

> :erlang.system_info(:atom_limit)
1048576

Likewise, you can check the current number of atoms in the atoms table with:

> :erlang.system_info(:atom_count)
9654

On my system using Elixir 1.5.1 I use 9654 atoms just to start iex.

Lighten And Darken With CSS Brightness Filter

CSS has a filter property that can be used with a variety of filter functions. One of them is the brightness() filter. By feeding a percentage less than 100% to brightness(), the target element will be made darker. Inversely, feeding a percentage greater than 100% to brightness() will make the element brighter.

.brighter-span {
  filter: brightness(150%);
}

.darker-span {
  filter: brightness(50%);
}

brighter, regular, and darker spans

See this CSS Tricks Article on the filter property for more details. Check out the browser support story here.

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. 🎩

Looking at the state of processes in Elixir

When debugging gen_server, gen_statem and gen_event processes it can be helpful to take a look at the state.

Its easy to do this in Elixir by calling out to Erlangs sys.get_state/1:

iex(1)> defmodule Example, do: use GenServer
iex(2)> {:ok, pid} = GenServer.start_link(Example, %{ping: "pong"})
iex(3)> :sys.get_state(pid)
%{ping: "pong"}

For more common sys debugging functions, take a look here

Xargs from a file

I’ve struggled with xargs conceptually for long time, but actually its pretty easy conceptually. For commands that don’t read from stdin but do take arguments, like echo or kill, you can turn newline separated values from stdin in into arguments.

Piping to echo does not work.

> echo 123 | echo
# nothing

Using xargs it does.

> echo 123 | xargs echo
123

xargs can also read a file with the -a flag, turning each line of the file into an argument.

> echo "123\nabc" > test.txt
> cat test.txt
123
abc
> xargs -a test.txt echo
123 abc

H/T Brian Dunn

Kill rogue shell processes

There is a particular type of attack where an inserted usb stick can act like a keyboard, open a terminal, and start something like this:

while (true); do something_malicious; sleep 3600; done & disown

This process endlessly loops and wakes every hour to do something malicious. The & puts it in the background and the disown will end its attachment to the current terminal. When the terminal is closed the process will get a parent of 1.

This process is still detectable and killable at the command line by finding all shell programs with a parent pid of 1 and killing them with -9.

ps ax -o pid,command,ppid | grep '.*zsh.*\s1$' | awk '{print $1}' | xargs kill -9

This will kill all running rogue zsh processes. There may be reasons why you’d want a process to be detached from its parent terminal, but you could easily decide that this isn’t something you want ever and place the above command into a cron job that runs every 2 seconds.

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.

Run vim command from .... the command line!

Generally vim is started at the command line with the vim command and it comes equipped with the -c flag for running commands.

-c will open vim, run a command, and stay open, so if you wanted to open vim with a smile you could run:

vim -c ':smile'

If you wanted to run a subsitution on a file with vim’s substitute command, then save, then quit, that would look like:

vim -c '%s/frown/upside-down/g | write | quit' frowns.txt

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

Capturing stderr when shelling out in ruby

All of the usual methods of shelling out in ruby (backticks, system, etc.) don’t capture stderr, but there’s an oddly named library open3 that can help with that.

require 'open3'

result_or_err, process_status = Open3.capture2e('cat abc')
puts result_or_err
# 'cat: abc: No such file or directory'
puts process_status
# <Process::Status: pid 6729 exit 1>]

In this case cat abc errors out because there is no abc file, so the command writes to stderr. If there was an abc file, result_or_err would contain the contents of the abc file.

source

Viewing the Git Leaderboard

Everyone knows that writing code is a competition in which she with the most commits wins. So, obviously, you want to periodically check the leaderboard to see who the 10x developer on the team is.

How do you do that?

git shortlog

This will give you a list of all the contributors to the repository and the number of commits they have made. You can add the -s flag (for ‘summary’) to just see names and numbers.

Once you have acquired the evidence that you are indeed the mythical 10x developer, demand a raise from your employer and threaten to go work for Google if you don’t get it.

If you already work for Google, threaten to, I don’t know, get a job at SpaceX or something.

Go To File With Line Number

I often use gf as supported by Vim or with the help of plugins like rails.vim as a way of quickly navigating to existing files. For unloaded files, this loads a buffer with the cursor at the top of the buffer. For existing buffers, it opens to that buffer with the cursor where it was when you left.

Vim also supports a slightly fancier goto file command, gF. If this command is used while the cursor is over a file with a line number appended to the end, it will not only open up a buffer with that file, it will move the cursor to the specified line number.

With this repository, I could try it out by moving the cursor over the following text and hitting gF.

README.md:100

This will open up a buffer for README.md with the cursor at line 100.

See :h gF for more details.

Access A Value Logged To The DevTools Console

Did your app just log an object to the dev tools console and you’d like to interact with that object? It’s not straightforward, but you can do it.

Assuming you already have dev tools opened to the console tab, right click on the value that has been logged to the console. Select the Store as Global Variable option. This will re-log the value assigning it to the temp1 variable.

You can now reference that object as temp1 accessing its values and calling functions.

You can even do this with multiple logged values, each subsequent one will be assigned incrementing variable names: temp2, temp3, etc.

source

Examine or Diff a file on another #git branch

At times, I’m curious about the changes that have been made for a particular file on a branch that is not the branch I’m on.

The vim-fugitive plugin provides an easy way to open a buffer for a file on another branch with the :Gedit command.

For instance:

:Gedit readme_updates:README.md

Will open a buffer with the contents of the README.md file from the readme_updates branch.

You may want to view that file in comparison to the current file. The :Gdiff command provides us with that functionality:

:Gdiff readme_updates:README.md

Rails Ignore Pending Migrations

Ruby on Rails gives us a nice warning in development when we haven’t run a pending database migration. But what if we’re iterating on a migration, and the results can best be viewed in the browser? We might want to temporarily disable this warning.

Here’s how to do it:

# config/environments/development.rb
config.active_record.migration_error = false

Don’t forget to turn it back on when you’re finished.

Better Rails SQL Migrations

Some people on our team, including me, write raw SQL in Ruby on Rails migrations. But they can get unwieldy when iterating on a complex migration that does multiple things.

Mitigate this is to break up your statements into separate HERDOCs, like so:

def up
  execute <<-SQL
    update pixaxes set metal = 'diamond' where metal = 'iron';
    -- many things...
  SQL

  execute <<-SQL
    update swords set metal = 'diamond' where metal = 'iron';
    -- many more things...
  SQL
end

If the migration fails, we’ll get an error pointing to the specific problematic HEREDOC, instead of essentially ‘the entire statement is invalid’. You then put a debugger between any HEREDOC to iterate on the issue.

h/t Jack Christensen

Keep Your Brews Bubbly

Life is too short to have Homebrew problems. Run these commands to keep your brews bubbly.

Checks your system to make sure that future installs go smoothly:

brew doctor

Upgrades Homebrew to the latest version:

brew update

Gets a list of what packages are outdated:

brew outdated

Looks through your installed packages and deletes any old versions that may still be hanging around:

brew cleanup

You could alternatively add the --dry-run flag to cleanup to see all the outdated packages that would be removed.

Deletes old symlinks:

brew prune

Updates packages to the latest version:

brew upgrade

You can add the --cleanup flag to delete older versions of the packages you are updating.

Export a synchronous method in React Native module

If you are working with an existing brownfield iOS app you may already have some code that is really complicated to change and sometimes you want a bridge module to export some native methods to JS that return synchronous values right way. In iOS, you can use the macro RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD:

@define API_URL @"http://localhost:3000"

@implementation ConfigManager

RCT_EXPORT_MODULE();

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getApiUrl) {
  return API_URL;
}

@end

And in your JS file you can call:

import { NativeModules } from 'react-native';

const apiUrl = NativeModules.ConfigManager.getApiUrl();

There is a problem using that when your app is in debug mode. It will raise the error global.nativeCallSyncHook is not a function. Also this macro blocks the current thread, so only use it if you REALLY need it.

Non-ActiveRecord objects in FactoryGirl

Creating non-ActiveRecord objects with FactoryGirl is possible. Classically, a constructor is used to set all the data attributes of an object.

class ParsedString
  attr_reader :abc, :def
  def initialize(string)
    @abc, @def = string.split(?|)
  end
end

In the above class the attributes are set in the constructor by passing in a string that gets split into two parts on \|. Lets use instantiate_with to determine how the object gets instantiated in FactoryGirl.

factory :parsed_string do
  initial_value "123|456"

  initialize_with { new(initial_value) }
end

And then in the test we can FactoryGirl.build, like:

ps = FactoryGirl.build(:parsed_string)
expect(ps.abc).to eq 123
expect(ps.def).to eq 456

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.

Find an npm library from the command line

When looking for a new package to use with npm you can use npm search <string>. This will query npm and return 20 - 25 results that match your search.

> npm search react-router
NAME                      | DESCRIPTION          | AUTHOR          | DATE       | VERSION  | KEYWORDS
react-router              | Declarative routing… | =ryanflorence   | 2017-08-24 |          | react router 
react-router-dom          | DOM bindings for…    | =mjackson…      | 2017-08-24 |          | react router 
react-router-redux        | Ruthlessly simple…   | =jlongster…     | 2017-02-10 |          | react redux r
react-router-native       | React Native…        | =jmurzy…        | 2017-08-24 |          |
react-router-config       | Static route config… | =mjackson…      | 2017-08-24 |          | react router 
react-router-bootstrap    | Integration between… | =monastic.panic… | 2017-04-19 |          | react react-
react-router-scroll       | React Router scroll… | =taion          | 2017-04-10 |          | react react r
react-native-router-flux  | React Native Router… | =aksonov        | 2017-08-23 |          |
...

There doesn’t seem to be a way to search by date, version or popularity, but if you pass the --long flag you’ll be able to see the entire description. Without the --long flag the results will be truncated to fit onto one line.

Check that an executable exists on the $PATH

When writing a vim plugin interacting with different programs on the server might be necessary. For instance you might want to use ruby to make a network request or perform some file management. But first, the vim plugin should check to see if the program ruby exists which can be accomplished with the executable() vimscript function.

:echo executable('ruby')
1
:echo executable('doesnotexist')
0

This can be used in a condition before running the ruby command.

if executable('ruby')
  system('ruby -e "puts YAML"')
endif

CamelCase to underscore and back again w/Elixir

If you have a mix project name with multiple words, then those multiple words are generally separated with underscores in the project directory name, like honey_bears. The module name for the project however is HoneyBears.

Converting a string from underscore to CamelCase is built into Elixir. Its in the Macro module:

> Macro.camelize("honey_bear")
"HoneyBear"

The reverse case also can be solved with a Macro function.

> Macro.underscore("HoneyBear")
"honey_bear"

The Macro module has a number of convience functions for working with macros.

Elixir Static Code Analysis in Vim

Today I learned how to set up Credo, a static analysis tool for Elixir, to run on every Vim buffer write, using Neomake.

To start, I added Credo to my project:

# mix.exs
defp deps do
  {:credo, "~> 0.8", only: [:dev, :test], runtime: false}
end

Including it with:

$ mix deps.get

Next, I loaded and sourced the Neomake plugin with Pathogen:

" ~/.vimbundle
neomake/neomake

Next, I told Neomake to run on every file, enabling Credo for Elixir files:

" ~/.vimrc
let g:neomake_elixir_enabled_makers = ['credo']
autocmd! BufWritePost * Neomake

Now I have Credo style recommendations right in my text editor.