Craftsmanship

3 basic refactoring tools for your command line


Nowadays, I don't use any fancy editor or IDE, but for many years I worked with Eclipse, and I loved all the Java code refactoring tools it offered, even though most people only use pretty simple refactorings. So I used to believe that I needed things like RubyMine if I wanted to have the best refactoring tools, now I know I don't. I currently spend most of my time editing Ruby code with VIM, doing a lot of refactoring, and I have had a very satisfying experience.

The operations I usually do fall into 3 main categories/tools, I'm going to include a short description of each category followed by an example of its use.

tl; dr;

First, let's install the utilities:

-> brew install mmv the_silver_searcher ack

And now, a quick summary of the commands I use the most:

-> ack 'pattern'     # lists the places in the files where pattern appears
-> ag 'pattern'       # same as above but using Silver Searcher
-> ag -l 'pattern'    # lists names of the files where pattern appears
-> ag -l 'pattern' | xargs sed -i 'backup' 's/pattern/other_pattern/' # replaces pattern by other_pattern on all of the files where pattern appears, creates .backup files
-> ag -l 'A\s+Pattern' | xargs sed -i '' -E 's/A(\s+)Pattern/Some\1Pattern/' # same as above but using back references
-> mmv "spec/file_*_spec.rb" "spec/#1_file_spec.rb" # Mass rename

Now let's give a more in-depth explanation.

1. Searching file contents with ack/ag

Some people use grep, but I prefer ack because of three reasons: it is faster, easier and cleaner. Google that and confirm what I say. Nowadays, I'm using ag, the Silver Searcher more often, which claims to be 3-5x faster than ack. The ag and ack command line have a very similar interface, so I use them indistinctly.

a) ag ActiveRecord

This is to search the Rails codebase for the ActiveRecord constant usage:

-> ag ActiveRecord

activerecord/test/models/movie.rb
1:class Movie < ActiveRecord::Base

activerecord/test/models/order.rb
1:class Order < ActiveRecord::Base

activerecord/test/models/organization.rb
1:class Organization < ActiveRecord::Base

Ever wondered how many times is that constant used? Here’s how to do it:

-> ag ActiveRecord|wc -l
    3677

b) ag 'def save'

If you want to search the Rails codebase for a #save method:

->  ag 'def save'
actionpack/test/lib/controller/fake_models.rb
92:  def save; @id = 1; @post_id = 1 end
113:  def save; @id = 1; @post_id = 1 end
133:  def save; @id = 1; @comment_id = 1 end
147:  def save; @id = 1 end
163:  def save; @id = 1; @tag_id = 1 end

actionview/test/lib/controller/fake_models.rb
65:  def save; @id = 1; @post_id = 1 end
86:  def save; @id = 1; @post_id = 1 end
106:  def save; @id = 1; @comment_id = 1 end
120:  def save; @id = 1 end
135:  def save; @id = 1; @tag_id = 1 end

c) ag -l 'def save'

To list the files in the Rails codebase containing a #save method.

->  ag -l 'def save'
actionpack/test/lib/controller/fake_models.rb
actionview/test/lib/controller/fake_models.rb
activemodel/lib/active_model/dirty.rb
activemodel/test/cases/dirty_test.rb
activerecord/lib/active_record/attribute_methods/dirty.rb
activerecord/lib/active_record/attribute_methods.rb
activerecord/lib/active_record/autosave_association.rb

2. Replacing files contents with sed

I don't actually know that much about sed, but I do understand the basics, and I’ve found that that is more than enough to make pretty strong refactorings.

Let's work a bit more over the previous example cases.

a) ag -l ActiveRecord | xargs sed -i '' 's/ActiveRecord/ActiveDoom/'

In the Rails codebase rename ActiveRecord modules to ActiveDoom.

I always refactor control versioned code because it helps me avoid using the -i "extension" sed parameter (more on this in the next paragraphs).

->  ag -l ActiveRecord | xargs sed -i '' 's/ActiveRecord/ActiveDoom/'

This a two-step process:

First, list all file names containing the ActiveRecord string:

-> ag -l ActiveRecord

Second, use each file name to construct an argument list with xargs and invoke the sed utility.

sed is using two arguments, the first one is -i "", which basically creates backup files with the extension in quotations, none in this case, hence the empty "". In the second argument we pass the command 's/ActiveRecord/ActiveDoom/'.

The sed arguments’ syntax may vary depending on the version you are using, so make sure you check the man sed for the version you are using.

3. Moving files with mmv

For basic file-moving needs you have mv, but sometimes you want to mass rename. So enter mmv for that effect.

Now, let's work with active_merchant. More specifically; its gateways. Here I see a class name ActiveMerchant::Billing::ModernPaymentsGateway, so I would expect its file name to be activemerchant/billing/modern\payments_gateway.rb.

What I want is to insert the _gateway word in all gateway classes.

-> mmv "lib/active_merchant/billing/gateways/*.rb" "lib/active_merchant/billing/gateways/#1_gateway.rb"

Let's see the first five files:

->  tree lib/active_merchant/billing/gateways |head -n 5
lib/active_merchant/billing/gateways
├── authorize_net_cim_gateway.rb
├── authorize_net_gateway.rb
├── balanced_gateway.rb
├── banwire_gateway.rb
├── barclays_epdq_extra_plus_gateway.rb
├── barclays_epdq_gateway.rb

Look at the files’ new prefix, it worked.

Conclusion

I barely scratched the surface of what you can accomplish using these tools, you can add regular expressions, back references and sed commands besides the "s" in 's///', among other useful features.

Finally, another good reason to use these tools is the fact that they do not depend on an IDE or language.

Have fun!

Craftsmanship
Can’t keep up? A simple guide to give feedback.
AEM
How to use internal redirects in AEM?
Development
What is VTEX platform and why should you migrate your ecommerce to it?