Ever get the urge to update a ton of files? I know I do. For example, I recently changed multiple hundred coffeescript files from the syntax of
MyGreatClass = Backbone.Model.extend( defaults: awesome: true )
class @MyGreatClass extends Backbone.Model defaults: awesome: true
How long did it take me? A couple of minutes. Here’s how.
I like ack. To find all the files I need to edit, I’d write something like this:
This finds everything that doesn’t start with a space, has an equals sign, and
.extend( at the end of the line.
After looking over the results of ack and ensuring that everything that matched is what I want to edit, I’ll open those files in vim.
If you’ve been using vim and haven’t taken advantage of macros (especially if
you’re editing a lot of files in a similar fashion), you’re missing out. Open
up vim and type
:help q to get the nitty-gritty; I’ll summarize here.
To start recording a macro, press (in normal mode) q and then a letter or number. This will record a macro to whatever register you chose (via the letter or number).
Once you’re recording a macro, anything you type will be recorded to that macro so that it can be replayed. What I would type to change these files to the new format would be:
Whoa, brain overload. Let’s break it down:
qq # records the macro to the q buffer gg # first line in file 0 # first character in line iclass @<esc> # inserts class @ at the cursor and returns to normal mode f= # finds the first equal after the cursor cwextends<esc> # changes the word (=) and moves to insert mode, adds extends, and returns to normal mode 2f. # finds the second period after the cursor D # deletes the remainder of the line G # moves to the end of the file dd # deletes the line :wn # writes the file and moves to the next file in the list q # stops recording
This should be fairly straightforward; the only thing I really want to point
out is the
n in that command moves to the next file in the list
of files you opened with vim. This is one half of what makes editing all these
files really fast.
Now that you have your macro, it’s time to replay it. To replay a macro, press
(in normal mode)
@q (assuming you stored your macro into the
q register). If
you were to run that macro, it’ll run it against the current file, write the file,
and move to the next. Since vim supports prefixing many commands with a number
(for the number of times to repeat the command), running
100@q will run that
macro on the first one hundred open files that I’ve opened with vim.
Typically, this should be all you need to batch-edit, but if there are more
files, just run that command again (or start with a higher number). If there
are no more files to edit, vim will let you know.
Ben also mentioned recursive macros (my mind was blown) by adding
before the last q (which will run the
q macro before stopping recording).
Just make sure your q register is empty! This would allow you to run your macro
once, without specifying the number of times to run it, because vim will run
the macro until it’s out of files. Fancy!
Want to kick ass at vim? Pick up a copy of Vim for Rails Developers and become blazing-fast! If you want to hang out with fellow vim users to swap awesome tips like this, be sure to head to the Boston Vim Meetup!
Are your test suites slow? Do you continually refactor your test suites so can get your TATFT on, but still have slow, slow tests? Do you isolate your tests from the database where possible to reduce disk I/O but find yourself mocked by the long runtime of CPU-bound suites?
Maybe it’s time for you to install… PARALLEL SPECS! Originally written by Michael Grosser to speed up RSpec suites, which is awesome, parallel_specs now sports support for running Test::Unit suites. (Yes, that means Shoulda too!)
Remember all that excitement from years ago about multi-core processors? “You can burn a CD while you check your email!” “You can email pictures TWICE AS FAST!!” Well, hold onto your butts, because – yep, that’s right – you can use them to run your tests faster! See for yourself!
This is what your two cores looks like when they’re BOTH RUNNING TESTS AT THE SAME TIME!
Now how about some numbers!
That’s a 30% speedup – REAL SPEEDUP! 30% to 40% was typical for our projects. Think your tests are largely I/O bound due to extreme database interaction and that using two cores won’t speed it up? THINK AGAIN! Our tests were running so fast that the database was like “SLOWWWW DOWWWWWN!”
Do you use RSpec? Do you use Test::Unit? Then you can get in on the action! FAST TESTING ACTION! Fast tests need a fast installation. Get parallel_specs up and running in no time flat – here’s how I did it for our existing Test::Unit applications:
Let’s see how slow our tests are when they are run one! at! a! time!
$ time rake test:units test:functionals Started .......... (abbreviated... FOR BLOG READING SPEED!!) 415 tests, 898 assertions, 0 failures, 0 errors 29.14s user 3.74s system 87% cpu 37.614 total
Are you ready? Let’s install a plugin! IN OUR APP!
$ script/plugin install git://github.com/jasonm/parallel_specs.git
More databases… it’s what concurrent environments crave!
$ rake db:structure:dump $ echo "create database myapp_test2;" | mysql -uroot $ mysql -uroot -Dmyapp_test2 < db/development_structure.sql $ vim config/database.yml # add some ERB to config/database.yml test: adapter: mysql encoding: utf8 database: myapp_test<%= ENV['TEST_ENV_NUMBER'] %> username: root
$ time rake test:parallel (in /Users/jasonmorrison/dev/myapp/trunk) 2 processes for 49 tests, ~ 24 tests per process Loaded suite -e Started .................................. (abbreviated) 29.32s user 3.31s system 143% cpu 22.669 total
BLAZOW! That’s like 15 more seconds I can spend working out. OR REFACTORING!
USING ALL AVAILABLE HARDWARE, I CAN DELIVER BUSINESS VALUE FASTER!! USE PARALLEL_SPECS!!
parallel_specs does not run cucumber features in parallel. You might look at testjour which is aimed at running cucumber features, although it is intended to run them in a distributed fashion across multiple machines, rather than on one multicore machine.
If you have multiple disks, spreading databases out across them will likely yield even more performance.
The solution isn’t originally mine – the original idea made its way to me from Pivotal Labs via Michael Grosser. Currently, I’ve forked Michael’s code to add Test::Unit support, and plan to send a pull request after I clean up some of the duplication I added.
When bears say “deliver business value,” they mean eating your whole campsite.
Do not drink parallel_specs.