Fixing Tabs in a Rails Project

05 Apr 2016 . praybook . Comments #magic

When I first started working on Praybook, I had a very interesting ‘development’ environment. This environment consisted of an Amazon Elastic Cloud Compute Instance running Ubuntu server and a collection of public computers running Windows 7 (think Library).

I was able to install Github for Windows locally (since it doesn’t require administrative access to the PC) but not ruby or a VM. I had Putty so I could SSH into the remote PC and execute commands. My editor of choice was Sublime Text. I kept to my modeless editors, and Nano wasn’t capable enough for me.

My workflow looked like this:

  1. Edit files in Sublime
  2. Make a commit in Github for Windows
  3. Push the commit to Github.com
  4. Pull the request from the server
  5. Run the tests

Obviously there were a lot of simple commits like ‘replaced semicolon with comma’, or more likely ‘@@#%@$% that newline’. It wasn’t perfect, but it worked. Sublime was happy to display tabs as two spaces, and I had no idea the difference. I happily pecked away at that tab key looking at how perfectly it looked.

I expected Sublime to be replacing each tab character with two spaces, not displaying tab characters as two spaces. When I no longer used a split enviornment, and started using Vim, I was happily typing two spaces, knowing I’d never figure out how to get Vim to display tabs as two spaces. (I mean console text editors aren’t powerful, right?)

I looked at my source code on github.com and it looked like my def lines were indented further than my definitions. I filed a bug and moved on. Things got worse, and I stoped commiting to my project because of all the broken windows.

I no longer have this problem because I made a difference in my project, fixing the tabs using a ruby script. My source code was simple and I was able to confirm I got the desired effect by executing this on a new branch. Don’t do this to your master branch and walk away, tabs may be in other parts of your files that are needed. Make sure you check the git diff and refactor once green.

TL;DR code!

Dir['app/**/*'].reject { |name| File.directory?(name) }.each do |file_name|
  contents = File.read(file_name)
  contents.gsub!(/\t/, '  ')
  File.open(file_name, 'w') { |file| file.write contents }
end

This script is executed from the root of the project, it only affects files in the app directory. If you only want to affect CSS or rb files, change the block following reject (|| !name.match(/\.rb$/)). The first line extracts the file names from all folders inside the app directory and enumerates them.

The contents variable is assigned the contents of the file, tabs are replaced (.gsub! performs mutable replacment, affecting the contents variable rather than returning a new object) with two spaces. The new contents are written back to the file.

The results for praybook can be seen in this commit.