twitterfacebookgithubgoogle-plusxinglinkedininstagram
Engineering

Don't make me think, rails (or rake, rspec, pry, rubocop, ruby-audit, ...)!

3 minutes reading time
Labyrinth Blogpost
  • Christoph Grothaus
    Christoph Grothaus

How to set up your shell with Oh My Zsh plugins and custom functions for a convenient usage of everyday Rails project tools.

You may know this situation: you are working on a couple of Rails projects, and each is slightly different than the other. Do I have to invoke rails here? Or is it bundle exec rails? Or is there a binstub, bin/rails? Or is this project Spring enabled, and it is spring rails?

Ok, ok, now you might say, "All of my Rails projects have a bin/rails binstub, so where is the problem?" and most likely, you will be right. That is really standard. But what about all the other executables that you use from time to time in a Rails project, like rake, rspec, pry, rubocop, ruby-audit, etc.? In my experience, the further down that list, the less likely a binstub is present. They may not even be listed in the Gemfile (recently, we switched many projects to always use the newest version of linters like Rubocop or RubyAudit in CI, via plain gem install — but that's a different story).

I grew tired of having to remember this for every tool in every project.

Oh My Zsh to the rescuce!

I am a happy Oh My Zsh user. It comes with lots of plugins, one of them being the rails plugin. It mainly adds a lot of shell aliases; I frequently use rs (rails server) and rc (rails console). But it also aliases rails and rake to smart functions that automatically do the right thing: they detect whether there is a binstub, or whether to call it via bundle exec, or just plain.

So now, I have solved my problem for two of the tools listed above. What about the others?

There is another useful Oh My Zsh plugin: bundler. Like the rails plugin, it adds a lot of shell aliases, and it also adds a smart wrapper function for many gems (see the plugin README for a list of wrapped gems). But that wrapper only knows two alternatives: it looks for a binstub, else it calls the gem via bundle exec. Naturally, you can't bundle exec a gem that is not managed by bundler, as in my use case where a gem intentionally is not listed in the Gemfile. The plugin also supports a list of UNBUNDLED_COMMANDS. Every command that I put into that list is not run through bundle exec. But as this is a global setting, this leaves me back where I started: having to remember how to invoke the command per project.

So I had to build my own smart wrapper functions.

My shell functions for the other tools

Without further ado, these are the functions and aliases:

###########################################################################
# Ruby tools without thinking about "bundle exec"
###########################################################################

function _smart_rspec_command() {
  _execute_command_with_binstub_or_bundle_or_plain rspec rspec $@
}
alias rspec='_smart_rspec_command'

function _smart_pry_command() {
  _execute_command_with_binstub_or_bundle_or_plain pry pry $@
}
alias pry='_smart_pry_command'

function _smart_rubocop_command() {
  _execute_command_with_binstub_or_bundle_or_plain rubocop rubocop $@
}
alias rubocop='_smart_rubocop_command'

function _smart_spring_command() {
  _execute_command_with_binstub_or_bundle_or_plain spring spring $@
}
alias spring='_smart_spring_command'

function _smart_ruby-audit_command() {
  _execute_command_with_binstub_or_bundle_or_plain ruby-audit ruby_audit $@
}
alias ruby-audit='_smart_ruby-audit_command'

function _execute_command_with_binstub_or_bundle_or_plain() {
  COMMAND=$1
  GEM=$2
  shift
  shift

  if [ -f bin/$COMMAND ]; then
    bin/$COMMAND $@
  elif bundle info $GEM > /dev/null 2>&1; then
    bundle exec $COMMAND $@
  else
    command $COMMAND $@
  fi
}

I use them in conjunction with the bundler plugin, but I made sure that in .zshrc, I source my own functions after the list of Oh My Zsh plugins so that in case of a conflict, my own aliases override those of the bundler plugin.

With that setup in place, now I can jump into any of our Rails projects and directly call my favorite tools without having to think 😌.

Do you like this article, or do you have any suggestions or comments for us? Why don't you recommend it within your bubble or give us feedback? ↘️

10/19/2021

Spread the news…

You are looking for the right partner for successful projects?
We'd love to help – let's get in touch!
Contact us