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
)
to
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:
ack '^[^\s].*\=.*\.extend\($' app/assets/javascripts -l
This finds everything that doesn’t start with a space, has an equals sign, and
has .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.
vim $(ack '^[^\s].*\=.*\.extend\($' app/assets/javascripts -l)
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:
qqgg0iclass @<esc>f=cwextends<esc>2f.DGdd:wnq
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 :wn. 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 @q right
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!

Shoulda’s users have long enjoyed the brevity and simple, declaritive nature of macros. These test-generating methods cut the most common ActiveRecord and ActionPack tests down to one beautiful, readable line. However, it doesn’t seem fair in this time of unity to leave our RSpec brothers and sisters out in the cold. If you like RSpec and Shoulda, but would just as soon never use the word “Test::Unit::TestCase” again, you have some good news in store.
Shoulda’s ActiveRecord macros have been rewritten to use RSpec-like matcher classes under the hood. That means that, providing you’re using at least version 1.1.12 of RSpec, you can write model specs like this:
describe User do
it { should belong_to(:account) }
it { should have_many(:posts) }
it { should validate_presence_of(:email) }
it { should allow_value("test@example.com").for(:email) }
it { should_not allow_value("test").for(:email) }
end
You can see more examples in the Shoulda rdoc. We’ve also added a dash of detection so that RSpec users can just require ‘shoulda’ and be on their way – the ActiveRecord matchers will be mixed into your model examples automatically.
The ActionPack macros haven’t been converted yet, but that process is already under way, so stay tuned for those. By Shoulda 3.0, we plan to have every macro in Shoulda to RSpec users.
For all your loyal Test::Unit users out there, you can keep using the same syntax you’ve always used:
class UserTest < ActiveRecord::TestCase
should_belong_to :account
should_have_many :posts
should_validate_presence_of :email
should_allow_values_for :email, "test@example.com"
should_not_allow_values_for :email, "test"
end
Whether you like to TDD, BDD, test, or spec, Shoulda macros are available for a framework near you!
Thanks go out to David Chelimsky for helping to make integration with RSpec a possibility.
Update: if you’re having issues with undefined methods when using Shoulda with RSpec via config.gem, try updating to Shoulda 2.9.1.
Those of you familiar with Shoulda may have noticed the new names in the examples above. If you’ve ever had to dive into the Shoulda documentation to figure out if the method you were looking for was “should_require_attributes” or “should_require_presence_of” or “should_need_to_have_a,” the latest version will bring some peace to your mind. Shoulda macros that directly corresponded to an ActiveRecord validation macro have been renamed to follow the fine example set by Rails:
# Before:
should_require_attributes :name
should_require_unique_attributes :email
should_only_allow_numeric_values_for :age
should_require_acceptance_of :terms_of_service
# After:
should_validate_presence_of :name
should_validate_uniqueness_of :email
should_validate_numericality_of :age
should_validate_acceptance_of :terms_of_service
We’ve also renamed should_protect_attributes and added a new macro for consistency:
# Before:
should_protect_attributes :password
# After:
should_allow_mass_assignment_of :name, :email
should_not_allow_mass_assignment_of :password
The old names still work for now, but you’ll see deprecation warnings in your tests.