Today I Learned

A Hashrocket project

How Rails Responds to `*/*`

Yesterday I fixed a bug in TIL. This application has a Twittercard, but it's never worked. Twitter's card validator confusingly claims the site lacks Twitter meta tags.

After experimenting, I realized that when cURL-ing our site, the response type is application/json. Why is Rails giving me JSON?

When an HTTP request's accept headers equal */*, any MIME type is accepted. And, when a respond_to block is present:

Rails determines the desired response format from the HTTP Accept header submitted by the client.

Determined how? I learned that the first MIME type declared is used for */* requests.

Notice the difference (HTML first):

# app/controllers/posts_controller.rb
 def index
    @posts = Post.all
    respond_to do |format|
      format.html
      format.json { render json: @posts }
    end
  end

Request/response:

$ curl -vI localhost:3000/
...
> Accept: */*
>
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...

JSON first:

# app/controllers/posts_controller.rb
 def index
    @posts = Post.all
    respond_to do |format|
      format.json { render json: @posts }
      format.html
    end
  end

Request/response:

$ curl -vI localhost:3000/
...
> Accept: */*
>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
...

Reversing the order (HTML first) solved the issue.

Grep For A Pattern On Another Branch

Git has a built-in grep command that works essentially the same as the standard grep command that unix users are used to. The benefit of git-grep is that it is tightly integrated with Git. You can search for occurrences of a pattern on another branch. For example, if you have a feature branch, my-feature, on which you'd like to search for occurrences of user.last_name, then your command would look like this:

$ git grep 'user\.last_name' my-feature

If there are matching results, they follow this format:

my-feature:app/views/users/show.html.erb:  <%= user.last_name %>
...

This formatting is handy because you can easily copy the branch and file directive for use with git-show.

See man git-grep for more details.

Viewing A File On Another Branch

Sometimes you want to view a file on another branch (without switching branches). That is, you want to view the version of that file as it exists on that branch. git show can help. If your branch is named my_feature and the file you want to see is app/models/users.rb, then your command should look like this:

$ git show my_feature:app/models/users.rb

You can even tab-complete the filename as you type it out.

See man git-show for more details.

source

New PostgreSQL 9.6 slice syntax

PostgreSQL arrays support slice operations.

jack=# select ('{a,b,c,d,e,f}'::text[])[2:4];
  text
---------
 {b,c,d}

New in PostgreSQL 9.6 is slice syntax with implicit lower and upper bounds.

jack=# select ('{a,b,c,d,e,f}'::text[])[:4];
   text
-----------
 {a,b,c,d}
jack=# select ('{a,b,c,d,e,f}'::text[])[3:];
   text
-----------
 {c,d,e,f}

Previously, array_lower and array_upper was needed to slice from a particular index to the bounds of an array.

Images in markdown

Images in markdown are the same as links except for the bang (!) character preceding the syntax.

The syntax for links is squares-parens. I have a hard time remembering this, and I try parens-squares as often as not, but its squares-parens, like this:

[My text](https://mylink.com)

The syntax for images is also squares-parens but preceded by a bang (!), like this:

![My alt text](https://mylink.com/oh_my.png)

Installing the Golang tools with Vim

Go has a set of tools to help aid in go development such as a test coverage tool for go or go guru (a tool for answering questions about the go source code.

While in vim if you would like to install these tools just use the command:

:GoInstallBinaries

And likewise if you would like to update these tools use the command

:GoUpdateBinaries

These commands are provided by the vim-go vim plugin. The binaries are installed into your $GOPATH directory or if you'd like to override that dir set the g:go_bin_path vim variable.

Git Garbage Collection - optimize local repository

As you work with git a lot of garbage gets accumulated in your .git folder such as file revisions and unreachable objects.

On large repositories and long running projects this negatively affects both operating performance and disk space utilization.

To clean up the garbage git provides a command:

git gc

This will not impact the remote repository and will only optimize the local copy so it is safe to run on any git repo you might have. (In fact this operation is already run for you automatically after some git commands that leave behind too many loose objects)

If you want a deeper cleaning (which you should every few hunderd changesets or so), run:

git gc --aggressive

This will take longer but will provide better results.

To learn more:

git help gc

Default netrw To Tree Liststyle

The built-in netrw plugin is a great way to browse files and directories within a Vim session. netrw supports four ways of displaying files and directories. That is, there are four liststyles. You can toggle through these by hitting i.

I prefer the tree liststyle, which is not the default. I can set the tree liststyle as the default by adding the following line to my .vimrc file.

let g:netrw_liststyle = 3

Now, every time I visit or revisit a netrw window, I'll see everything nicely displayed as a tree.

Between Symmetric in PostgreSQL

PostgreSQL's between construct allows you to make a comparison between two values (numbers, timestamps, etc.).

> select * from generate_series(1,10) as numbers(a)
    where numbers.a between 3 and 6;
 a
---
 3
 4
 5
 6

If you supply an empty range by using the larger of the two values first, an empty set will result.

> select * from generate_series(1,10) as numbers(a)
    where numbers.a between 6 and 3;
 a
---

Tacking symmetric onto the between construct is one way to avoid this issue.

> select * from generate_series(1,10) as numbers(a)
    where numbers.a between symmetric 6 and 3;
 a
---
 3
 4
 5
 6

BETWEEN SYMMETRIC is the same as BETWEEN except there is no requirement that the argument to the left of AND be less than or equal to the argument on the right. If it is not, those two arguments are automatically swapped, so that a nonempty range is always implied.

Start rails with a different web server

The webserver for my current project is puma, which is a multi-threaded ruby server. This multithreaded nature makes it hard to but a pry statement in and break in specific places. There are multiple threads that will listen to the input the user provides at the REPL.

Using webrick would allow us to debug and step through our code but changing the applications configuration in order to enable that seems unreasonable.

Fortunately, rails provides an easy way to change servers at the command line.

> rails server webrick

Just pass the name of the server you would rather use as a command line argument.

When typing rails server --help you'll see this option available on the first line:

Usage: rails server [mongrel, thin etc] [options]

group by 1, 2, 3 🔢

Postgres allow group by and order by to reference column order in a sql statement. This is particularly useful when an aggregate result needs to be referenced in the group by or order by statement.

-- group by aggregate
select
  (created_at at time zone 'UTC' at time zone 'America/Chicago')::date,
  count(*)
from posts
group by (created_at at time zone 'UTC' at time zone 'America/Chicago')::date
order by (created_at at time zone 'UTC' at time zone 'America/Chicago')::date
;

becomes

-- group by 1, 2, 3
select
  (created_at at time zone 'UTC' at time zone 'America/Chicago')::date,
  count(*)
from posts
group by 1
order by 1
;

Raid 0 offers performance with worse reliability

RAID is defined:

Redundant Array of Independent Drives

Whenever I think of RAID I think of data drives that each contain the same information, for fault tolerance. Different RAID configurations are given different numeric identifiers. Recently, when shopping for motherboards I saw RAID 0 listed as a selling point for many of the boards and I assumed this was a fault tolerance feature on these motherboards meant for gamers.

RAID 0 has nothing to do with fault tolerance. Nor redundance. In a Raid 0 configuration data is spread across multiple drives with the intention of increasing the bandwidth of data from the motherboard/CPU to the drives. It is a technique for increasing performance that actually reduces fault tolerance.

Read more about RAID here -> https://en.wikipedia.org/wiki/Standard_RAID_levels

First and last arguments to a command line command

If I start with the command:

> echo a b c
a b c

Sometimes I want the last argument to a command, which in this case is c but many times can be a long file path or a hard to spell word. In that case I would use !$:

> echo !$
c

This substitutes !$ with the last argument of the previous command.

To substitute the first argument instead, use !^ like so:

> echo !^
a  

The ^ character and the $ character are ofter used as first and last. In vim those commands move the cursor to the beginning and the end of the line respectively. In regex those characters match the beginning or end of the line.

What Changed?

If you want to know what has changed at each commit in your Git history, then just ask git whatchanged.

$ git whatchanged

commit ddc929c03f5d629af6e725b690f1a4d2804bc2e5
Author: jbranchaud <jbranchaud@gmail.com>
Date:   Sun Feb 12 14:04:12 2017 -0600

    Add the source to the latest til

:100644 100644 f6e7638... 2b192e1... M  elixir/compute-md5-digest-of-a-string.md

commit 65ecb9f01876bb1a7c2530c0df888f45f5a11cbb
Author: jbranchaud <jbranchaud@gmail.com>
Date:   Sat Feb 11 18:34:25 2017 -0600

    Add Compute md5 Digest Of A String as an Elixir til

:100644 100644 5af3ca2... 7e4794f... M  README.md
:000000 100644 0000000... f6e7638... A  elixir/compute-md5-digest-of-a-string.md

...

This is an old command that is mostly equivalent to git-log. In fact, the man page for git-whatchanged says:

New users are encouraged to use git-log(1) instead.

The difference is that git-whatchanged shows you the changed files in their raw format which can be useful if you know what you are looking for.

See man git-whatchanged for more details.

Use Mkdir! to create dir of present file in vim

In vim its easy to :edit, :write, or :saveas a file to a path that doesn't yet exist. Such as:

:edit non/existant/path/file.txt

Vim will let you have a buffer with that path, but when you try to write the file vim will throw an error:

E212: Can't open file for writing

Enter the Mkdir! command. The Mkdir command has been added by the eunuch.vim plugin to make creating directories easier from vim. The bang (!) variant takes the path of the current buffer and creates the directory from that path.

When we get the E212 error we can type:

:Mkdir!

And walla! The path will be created. No more errors when writing the file.

Compute md5 Hash Of A String

To compute the md5 digest of a string, we can use Erlang's top-level md5 function.

> :erlang.md5("#myelixirstatus")
<<145, 148, 139, 99, 194, 176, 105, 18, 242, 246, 37, 69, 142, 69, 226, 199>>

This, however, gives us the result in the raw binary representation. We would like it in a base 16 encoding, as md5 digests tend to be.

We can wrap (or pipe) this with Base.encode16 to get the result we are looking for.

> Base.encode16(:erlang.md5("#myelixirstatus"), case: :lower)
"91948b63c2b06912f2f625458e45e2c7"

source

Yarn global

Just like npm install -g, Yarn provides yarn global add. I found however that it did not work right out of the box to register executable binaries/CLIs.

To fix this add the following to your .zshrc/.bashrc:

# set yarn binaries on path
export PATH="$HOME/.config/yarn/global/node_modules/.bin:$PATH"

Now all binaries installed from yarn should be on your system PATH.

Save disk space with Yarn

Yarn is a fast, reliable and secure replacement for npm. Those are all important attributes in my book for a tool I use daily for development, but that's not all Yarn offers.

The node_modules directory is often a resource consuming hog both in space and number of files. It is full of junk such as test files, build scripts and example directories.

To clean some of those files Yarn offers the clean command. To run it:

yarn clean

Once run, the command will create a .yarnclean file with patterns of type of files to be deleted. By default yarn clean will delete the following:

# test directories
__tests__
test
tests
powered-test

# asset directories
docs
doc
website
images
assets

# examples
example
examples

# code coverage directories
coverage
.nyc_output

# build scripts
Makefile
Gulpfile.js
Gruntfile.js

# configs
.tern-project
.gitattributes
.editorconfig
.*ignore
.eslintrc
.jshintrc
.flowconfig
.documentup.json
.yarn-metadata.json
.*.yml
*.yml

# misc
*.gz
*.md

With this file in your project root directory Yarn will ensure to run a cleaning task after every install and report how much space it saved you.

Alias Loaders in webpack

A loader might not be named conveniently for your project. Maybe the path to it is too long and obscures the actual configuration of the loader, in that case you can create an alias with the resolveLoader.alias webpack configuration.

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'smrt',
      }
    ]
  },
  resolveLoader: {
    alias: {
      'smrt': require.resolve(__dirname, 'really', 'long', 'path', 'smart-loader.js')
    }
  }
}

In addition, you may only want to transform one file. Its possible to do that in a require statement and use the bang character (!) prefix syntax to declare the loader like this.

require 'smrt!dumbfile';

Clearly in this case a long loader name would obscure intent.

Export from old school libraries in Webpack

As you are refactoring your legacy Rails project towards webpack and away from the asset pipeline you discovered an old version of the facebook_sdk that is absolutely critical to the ongoing success of the legacy application. This file doesn't play nicely with CommonJS though and exports its constant with global declarations like:

var FB = {};

This isn't very global in CommonJS and your app doesn't have access to that constant anymore.

The problem can be solved with the exports-loader used like so:

module.exports = {
  module: {
    rules: [
      {
        test: /facebook_sdk/,
        loader: 'exports-loader?FB',
      }
    ]
  }
}

This is just a cute way of tacking an export line to the bottom of the file like this:

module.exports = FB;

Custom loaders for webpack

Perhaps there is a transformation you'd like to perform on your javascript files that is unique to your project. Perphaps replacing the word dumb with smart is a requirement but you don't have control over the actual files and can't make that change to the source files themseles.

In this situation, you can use a custom loader.

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: './smart-loader',
      }
    ]
  },
}

The loader itself (smart-loader.js) would look something like this.

module.exports = function(source) {
  return source.replace('dumb', 'smart');
}

Exclude files from webpack transformation

Sometimes you want every file that has the .dumb extension to be transformed by the dumb-loader except for one particularly dumb file that should remain as is, as a lesson for to all the other dumb files. Just use the handy exclude configuration on modules.rules.loader to make sure that file doesn't get transformed.

module.exports = {
  module: {
    rules: [
      {
        test: /\.dumb$/,
        loader: 'dumb-loader',
        exclude: /really\.dumb/
      }
    ]
  }
}

Now that really dumb file won't get transformed by the dumb loader.

Deprecated Dynamic Actions

Rails routes have supported dynamic actions in the past, like this:

# config/routes.rb

 get 'ui(/:action)', controller: 'ui'

The 'Today I Learned' application uses code like this to create design templates.

This will be deprecated in Rails 5.1. If you want to address the change now, while still supporting a variety of routes, one solution is to name them explicitly:

# config/routes.rb

namespace 'ui' do
  %w(index edit show).each do |action|
    get action, action: action
  end
end

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. ¯\_(ツ)_/¯

Counting Records With Ecto

Sometimes you want to know how many records there are in a table. Ecto gives us a couple ways to approach this.

We can use the count\1 function that the Ecto query API provides.

> Repo.one(from p in "people", select: count(p.id))

16:09:52.759 [debug] QUERY OK source="people" db=1.6ms
SELECT count(p0."id") FROM "people" AS p0 []
168

Alternatively, we can use the fragment/1 function to use PostgreSQL's count function.

> Repo.one(from p in "people", select: fragment("count(*)"))

16:11:19.818 [debug] QUERY OK source="people" db=1.5ms
SELECT count(*) FROM "people" AS p0 []
168

Lastly, Ecto.Repo has the aggregate/4 function which provides a :count option.

> Repo.aggregate(from(p in "people"), :count, :id)

16:11:23.786 [debug] QUERY OK source="people" db=1.7ms
SELECT count(p0."id") FROM "people" AS p0 []
168

Unique Indexes With Ecto

You can create a unique index in a migration for one or more columns using the unique_index/3 function.

For example, if you are creating a join table for followers and want to ensure that duplicate follower entries are prevented, you may want to include a unique index like so:

create table(:followers) do
  add :followed_user, references(:users), null: false
  add :following_user, references(:users), null: false
end

create unique_index(:followers, [:followed_user, :following_user])

Keep in mind that unique_index/3 is a shorthand for index/3 when you set unique: true.

Rerun Only Failures With RSpec

After running a big test suite, I may have a bunch of output on the screen including the results of a couple test failures. I like to bring the context of the test failures front and center and make sure they are consistent test failures (not flickering failures). Instead of copying and pasting each failure, I can rerun rspec in a way that executes only the test cases that failed.

$ rspec --only-failures

This feature requires that you set a file for RSpec to persist some state between runs. Do this in the spec/spec_helper.rb file. For example:

RSpec.configure do |config|
  config.example_status_persistence_file_path = "spec/examples.txt"
end

See more details here.

h/t Brian Dunn

Polymorphic Path Helpers

Underlying many of the path helpers that we use day to day when building out the views in our Rails apps are a set of methods in the ActionDispatch::Routing::PolymorphicRoutes module.

The #polymorphic_path method given an instance of a model will produce the relevant show path.

> app.polymorphic_path(Article.first)
  Article Load (0.5ms)  SELECT  "articles".* FROM "articles"  ORDER BY "articles"."id" ASC LIMIT 1
=> "/articles/2"

Given just the model's constant, it will produce the index path.

> app.polymorphic_path(Article)
=> "/articles"

Additionally, there are variants with edit_ and new_ prefixed for generating the edit and new paths respectively.

> app.edit_polymorphic_path(Article.first)
  Article Load (0.6ms)  SELECT  "articles".* FROM "articles"  ORDER BY "articles"."id" ASC LIMIT 1
=> "/articles/2/edit"
> app.new_polymorphic_path(Article)
=> "/articles/new"

Clean untracked files in Git

Given I am a developer
And I am working on a new branch in an existing project
And during one of my commits I introduced a few files/folders
And those files/folders are untracked (in .gitignore)
And those files/folders are automatically generated (e.g. node_modules/ webpack_bundle.js)
When I switch back to the main branch
Then I see those files
And I don't want to...

If you find yourself in the above situation, you may want to clean your untracked files. Git provides a command for that: git clean.

This command comes with a way to see which files/folders are going to be deleted (DRY RUN):

git clean -n

You may notice that the above command does not show any untracked directories. To add directories to that list use the -d switch:

git clean -dn

Alternatively you may choose to only remove files/dirs that are in .gitignore with the -X option:

git clean -X -dn

If you are ready to take action use the -f switch and remove the -n switch:

git clean -fd

Mark For Destruction

Do you have some complicated logic or criteria for deleting associated records? ActiveRecord's #mark_for_destruction may come in handy.

Let's say we have users who author articles. We want to delete some of the user's articles based on some criteria -- those articles that have odd ids.

> user = User.first
#=> #<User...>
> user.articles.each { |a| a.mark_for_destruction if a.id.odd? }
#=> [#<Article...>, ...]
> user.articles.find(1).marked_for_destruction?
#=> true
> user.articles.find(2).marked_for_destruction?
#=> false

We've marked our articles for destruction and confirmed as much with the #marked_for_destruction? method. Now, to go through with the destruction, we just have to save the parent record -- the user.

> user.save
   (0.2ms)  BEGIN
  User Exists (0.8ms)  SELECT  1 AS one FROM "users" WHERE ("users"."email" = 'person1@example.com' AND "users"."id" != 1) LIMIT 1
  SQL (3.0ms)  DELETE FROM "articles" WHERE "articles"."id" = $1  [["id", 1]]
  SQL (0.2ms)  DELETE FROM "articles" WHERE "articles"."id" = $1  [["id", 3]]
   (2.1ms)  COMMIT
=> true

Note: the parent record must have autosave: true declared on the association.

class User < ActiveRecord::Base
  has_many :articles, autosave: true
end

Convert A Symbol To A Constant

If you have a symbol and need to convert it to a constant, perhaps because of some metaprogramming induced by a polymorphic solution, then you may start off on an approach like the following. In fact, I've seen a number of StackOverflow solutions like this.

:module.to_s.capitalize.constantize
#=> Module

That is great for one-word constant names, but what about multi-word constants like OpenStruct. This approach will not work for the symbol :open_struct. We need a more general solution.

The key is to ditch #capitalize and instead use another ActiveSupport method,#classify`.

:open_struct.to_s.classify.constantize
#=> OpenStruct

Cascading function calls

Cascading function calls can be useful for building up properties on an object. We can simply return this at the end of each cascading function so that we always end up with the object when we're done.

Example - building a flash message after an ajax request in rails:

$.ajax({
        method: 'DELETE',
        url: url,
        contentType: 'application/json',
        error: function(status, data, jqxhr){
          var message = JSON.parse(status.responseText).error;
          var flash = new FlashBuilder('div');

          flash.text(message)
            .addClass('notification-error')
            .append('.flash-messages', '#some_container');
        }
      });
function FlashBuilder(el){
  this.element = $('<' + el + '>');

  this.text = function(text){
    this.element.text(text);
    return this;
  };

  this.addClass = function(klass){
    this.element.addClass(klass);
    return this;
  };

  this.append = function(target, container){
    if(container){
      $(container).find(target).append(this.element);
    } else {
      $(target).append(this.element);
    }

    return this;
  }
}

e.target vs this in delegated events

When attaching click handlers to elements, I used to think I could use e.target interchangeably with this. That is not true when we use event delegation.

Given the following JS:

    $('body').on('click', "[id^=delete_thing]", function(e){
      // do stuff
    });

And the following HTML:

<a href="javascript:void(0)" id="delete_thing_184">
      <img  src="/assets/ui/icon_delete.png">
      <strong> Delete Thing </strong>
</a>

When we click this link, we're actually clicking either the <strong> or the <img>. When the event e is passed to the callback, e.target is the element we actually clicked, and this is the element the click event bubbled up to, so use this instead of e.target.parent.

Responding with :stop allows call of `terminate`

A GenServer in Elixir has two lifecycle methods, init and terminate. init is called when the GenServer is started with GenServer.start_link.

What I learned today is that there is a terminate method in case there's any resources that need to be cleaned up when the GenServer is shut down. The terminate method is called when the :stop message is returned from either handle_cast or handle_call:

defmodule Cache do
  use GenServer

  #... a lot of other code

  def handle_cast({:something, 1}, state) do
    IO.puts "This executes first"
    {:stop, "This is my reason for stopping", state}
  end

  def terminate(reason, state)
    IO.puts "Then this executes"
  end
end

Break Up Lines with Splitjoin

One style-guide idea I try to maintain is sticking to a reasonable line length. 72 characters, 80 characters, whatever your preference; long lines are hard to read. They're a code smell. They need to have a reason for existing.

A task I find myself repeating a lot to achieve this is breaking apart Ruby blocks and hashes into multiple lines, with a combination of jumping forward, entering insert mode, and hitting enter. Turning this:

FactoryGirl.create(:developer, username: 'jake_skid', superpower: 'technical writing and style-guide compliance')

into this:

FactoryGirl.create(:developer, {
  username: 'jake_skid',
  superpower: 'technical writing and style-guide compliance'
})

This task has been automated for me by the Splitjoin plugin for Vim, which is included in the Hashrocket dotfiles. Typing gS in normal mode over the first example turns it into the second example. It's opinionated, and probably won't satisfy every tangent of your personal style (I'd leave off the curly braces and add a trailing comma, for instance). But it's already saved me a lot of time.

h/t Dorian Karter

Understanding Github's Language Calculations

Ever noticed this at the top of a Github repo?

This filetype percentage is calculated by Github's Linguist project.

There's just one problem: 'Today I Learned' isn't a JavaScript project; it's a Rails application. I would expect the majority of the files to be Ruby files. Labeling 'Today I Learned' a JavaScript project misrepresents it to the Github community.

I found several techniques to correct the Linguist evaluation; this pull request explains one I tried in greater detail. TL;DR – once I moved some vendored JS files into a directory with a name that Linguist ignores (ace-builds/), the calculation started to look more like a Rails app.

Here's the updated header:

Creating Indexes With Ecto

Using indexes in the right places within relational databases is a great way to speed up queries.

To add a basic index in an Ecto migration, use Ecto.Migration.index\2:

create index(:users, [:email])

Creating a composite index doesn't require jumping through any hoops; just put the relevant column names in the list:

create index(:posts, [:user_id, :title])

See h Ecto.Migration.index for more details.

Custom Phoenix Validations

Today I learned how to write a custom Phoenix model validation. It came from the Phoenix port of this very application, which requires that posts be 200 words or less.

Phoenix doesn't have a word length validation, so I had to make my own. Here it is, minus some irrelevant lines of code:

# web/models/post.ex

def changeset(struct, params \\ %{}) do
  struct
  |> cast(params, [:title, :body, :channel_id])
  |> validate_length_of_body
end

defp validate_length_of_body(changeset) do
  body = get_field(changeset, :body)
  validate_length_of_body(changeset, body)
end

defp validate_length_of_body(changeset, body) do
  if length(String.split(body, ~r/\s+/)) > 200 do
    add_error(changeset, :body, "should be at most 200 word(s)")
  else
    changeset
  end
end

When the post body is greater than 200 words (as defined by splitting on whitespace characters), the validation adds an error to the body field that mimics the errors Phoenix provides for character-length validation failures. Including the (s), which I kind of like.

When the body is short enough, changeset is returned from the function of the same name, a form of success.

Pattern matching FTW.

Open images in vim with iTerm 🖼

iTerm 3 has a built in shell script called imgcat for displaying images in the terminal. With one simple autocmd in my vim configuration I can open images*:

:autocmd BufEnter *.png,*.jpg,*gif exec "! ~/.iterm2/imgcat ".expand("%") | :bw

In my command I wipe the image buffer(:bw) because I don't want large images sitting around in buffers, but this is easy to change.

imgcat

*imgcat does not work with tmux

Use A Case Statement As A Cond Statement

Many languages come with a feature that usually takes the name cond statement. It is essentially another way of writing an if-elsif-else statement. The first conditional in the cond statement to evaluate to true will then have its block evaluated.

Ruby doesn't have a cond statement, but it does have a case statement. By using a case statement with no arguments, we get a cond statement. If we exclude arguments and then put arbitrary conditional statements after the when keywords, we get a construct that acts like a cond statement. Check out the following example:

some_string = "What"

case
when some_string.downcase == some_string
  puts "The string is all lowercase."
when some_string.upcase == some_string
  puts "The string is all uppercase."
else
  puts "The string is mixed case."
end

#=> The string is mixed case.

source

rel attribute for performance and security

The rel attribute on HTML links need to be set to noopener if you're using target=_blank in your links, otherwise the new tab contains a reference to the other tab's window object, and can redirect, manipulate dom, etc.

Read more and see a demo here: https://mathiasbynens.github.io/rel-noopener

There are also some performance benefits: https://jakearchibald.com/2016/performance-benefits-of-rel-noopener/

List The Enqueued Jobs

Many Rails apps need to delegate work to jobs that can be performed at a later time. Both unit and integration testing can benefit from asserting about the jobs that get enqueued as part of certain methods and workflows. Rails provides a handy helper method for checking out the set of enqueued jobs at any given time.

The enqueued_jobs method will provide a store of all the currently enqueued jobs.

It provides a number of pieces of information about each job. One way to use the information is like so:

describe '#do_thing' do
  it 'enqueues a job to do a thing later' do
    Processor.do_thing(arg1, arg2)
    expect(enqueued_jobs.map { |job| job[:job] }).to match_array([
      LongProcessJob,
      SendEmailsJob
    ])
  end
end

To use this in your Rails project, just enable the adapter in your test configuration file:

Rails.application.config.active_job.queue_adapter = :test

Vimscript String Coercion

An unique feature of Vimscript is coercion of strings into integers.

Here's an example:

:echom "six" + 6
:echom "6six" + 6
:messages
6
12

Our messages show that the first string was coerced to 0 (0 + 6 = 6), and the second string was coerced to 6 (6 + 6 = 12).

Following this logic a little further, because 0 is falsey and 1 (or greater than 1) is truthy, our first string is falsey and our second string is truthy.

Spellcheck Certain Filetypes

I don't like misspellings in my documentation, yet make a lot of them. Spelling is disabled in all my buffers by default; I turn it on when reading or editing docs. When I forget to do so, it's easy to miss typos.

While reading Learn VimScript The Hard Way I figured out a better solution to this problem. This now lives in my ~/.vimrc.local:

augroup spellcheck_documentation
  autocmd BufNewFile,BufRead *.md setlocal spell
  autocmd BufNewFile,BufRead *.rdoc setlocal spell
augroup END

Anytime a markdown or RDoc file is created or read, I set a local configuration to turn spelling on. This has already proved invaluable after only a day or two.