Auto Open Rails Generator Files

Published on . Ruby Workflow

The Rails generator is a great tool to speed up your workflow, but having to manually open just-generated files gets tiresome fast. We’ll see how to automatically open up all generated files in your editor, accelerating your workflow even more.

TL;DR

Don’t care about how it works, just want to use it?

  • Download the small script file
  • Change the subl editor command to fit whatever editor you’re using
  • Make the file executable (chmod +x rails_g_and_open.rb)
  • Create a shell alias that you’ll use as a substitute for rails generate, for example:
alias rg="~/scripts/rails_g_and_open.rb"

# Now you can run:
# $> rg model user

If you’re using Tmux, follow these additional steps to make it work.

Read on for details on how it works!

First Implementation

The idea is simple enough: run the rails generate command, capture the output, and load all generated files. As a reminder, the output of a rails generate command looks like this:

$> rails generate model device
      invoke  active_record
      create    db/migrate/20150720054739_create_devices.rb
      create    app/models/device.rb
      invoke    test_unit
      create      test/models/device_test.rb
      create      test/fixtures/devices.yml

We want to extract all paths that start with create and pass those to our editor. We also need to ignore created directories, since they might cause the editor to open a new window. A simple solution is easy:

#!/usr/bin/ruby

def extract_created_files(lines)
  created_items = lines.map do |line|
    command, file = line.split
    file if command == "create"
  end.compact

  created_files =
    created_items.reject { |file| File.directory?(file) }

  created_files.reverse
end

output = `rails generate #{$*.join(" ")}`
lines = output.split
files = extract_created_files(lines)

puts output

if files.any?
  puts "\nOpening files #{files.join(", ")}..."
  exec("subl #{files.join(" ")}")
end

This does exactly what we want it to, except that for some reason the colouring has been removed from the output, and I really like the colours.

Adding Colours Back

Turns out that Rails uses the gem thor as the framework for its command-line interface, and thor only enables color when it runs in a terminal. Since we’re running the generate command in our own script, there is no terminal and thor disables the shell formatting.

To fix this we have to capture the output using a virtual terminal or pseudo terminal which is just a fake terminal that we can control with code.

Here’s the updated, final script with colours added back in. Note that we have to filter out the colours again before parsing the output.

#!/usr/bin/ruby

require 'pty'

def extract_created_files(lines)
  created_items = lines.map do |line|
    command, file = colorless(line).split
    file if command == "create"
  end.compact

  created_files =
    created_items.reject { |file| File.directory?(file) }

  created_files.reverse
end

def colorless(str)
  str.gsub /\033\[\d+m/, ""
end

command = "rails generate #{$*.join(" ")}"
lines = []

# Use PTY to force Thor to output coloured text
PTY.spawn(command) do |r, w, pid|
  begin
    while line = r.readline
      puts line
      lines << line
    end
  rescue EOFError
    # noop
  end
end

files = extract_created_files(lines)

if files.any?
  puts "\nOpening files #{files.join(", ")}..."
  exec("subl #{files.join(" ")}")
end

Addendum: Tmux Fix

If you’re using Tmux on Mac, you have some additional hoops to jump through to make this work. You’ll need to install reattach-to-user-namespace to be able to pass through the file names.

brew install reattach-to-user-namespace

Then add the following line to your .tmux.conf

set-option -g default-command "reattach-to-user-namespace -l zsh"

Use it by running it with the sublime command as a parameter:

reattach-to-user-namespace /usr/local/bin/subl

I alias it to subl so I can just use subl without having to think about it.

David Verhasselt

Senior full-stack engineer with 5 years of experience building web applications for clients all over the world.

Interested in working together?

Find out what I can do for you or get in touch!

Like this? Sign up to get regular updates