db:migrate:down and Bash completion

05 Apr 2018

As a busy Rails developer, you are probably inconvenienced at least twice a year when you have to run a migration that's not the last one in the db/migrate directory. You have to ls -lrt the contents of that directory, copy the version number, and then paste it into your terminal so you can do that down migration:

ber db:migrate:down VERSION=20180319191352

I mean! Those are precious seconds wasted. Well, no more. Just put this snippet in your .bash_profile:

_rake_migrate() {
  COMPREPLY=()
  if [ $3 != "db:migrate:down" ]; then
    return 0
  fi
  local cur="${COMP_WORDS[COMP_CWORD]}"
  IFS='=' read -r -a array <<< "$cur"
  local version_number_part=${array[1]}
  if [ "$COMP_CWORD" -eq 2 ]; then
    local res=$(find db/migrate/ -type f -exec basename {} \; | grep ${version_number_part})
    COMPREPLY=($(compgen -W "$res" ))
  fi
}

complete -F _rake_migrate rake

Now when you type rake db:migrate:down VERSION=2018 and hit the tab key Bash will list out all the migration version files from 2018. And when you select one, even though we're placing the entire filename to the right of the VERSION= it still works. That's because the db:migrate:down Rake task uses String#to_i to process the VERSION environment variable, and that handles it. Per the documentation, String#to_i "Returns the result of interpreting leading characters in str as an integer base base (between 2 and 36). Extraneous characters past the end of a valid number are ignored."

>> "42_asdf".to_i
=> 42

Hats off to Mike English for his very helpful Bash completion article!