Ruby on Rails provides a large API for web development, including adding new methods to Ruby’s base classes. One method I don’t see used that often is Hash#except
. From the Rails API documentation:
Return a hash that includes everything but the given keys. This is useful for limiting a set of parameters to everything but a few known toggles:
@person.update_attributes(params[:person].except(:admin))
A great use I found for it is to help test validation methods on Model objects. By defining a method that will return all the valid attributes on a model, you can easily exclude the ones you want to test with Hash#except
.
I’m using RSpec in this example but the same idea works with Test::Unit
or any other testing framework. I created a method valid_attributes
that will return the attributes my Model needs to be valid. In this case my SystemNotification
object needs a body, subject, and some users.
1 2 3 4 5 6 7 8 9 10 11 |
module SystemNotificationSpecHelper def valid_attributes user1 = mock_model(User, :mail => 'user1@example.com') user2 = mock_model(User, :mail => 'user2@example.com') return { :body => 'a body', :subject => 'a subject line', :users => [user1, user2] } end end |
I used mocks for the users because I don’t care about them, only that they are present. So to test if my object is valid, I can easily just pass in valid_attributes
to my new method:
1 2 3 4 5 6 7 8 |
describe SystemNotification, "valid?" do include SystemNotificationSpecHelper it 'should be valid with the body, subject, and users' do system_notification = SystemNotification.new(valid_attributes) system_notification.valid?.should be_true end end |
Now I want to write some more specs to make sure if a SystemNotification
is invalid if it’s missing any of the required attribute. Since valid_attributes
is a Hash, I can use Hash#except
to remove each attribute individually and make sure the object is invalid for each case.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
describe SystemNotification, "valid?" do include SystemNotificationSpecHelper it 'should be valid with the body, subject, and users' do system_notification = SystemNotification.new(valid_attributes) system_notification.valid?.should be_true end it 'should be invalid without a subject' do system_notification = SystemNotification.new(valid_attributes.except(:subject)) system_notification.valid?.should be_false end it 'should be invalid without a body' do system_notification = SystemNotification.new(valid_attributes.except(:body)) system_notification.valid?.should be_false end it 'should be invalid without any users' do system_notification = SystemNotification.new(valid_attributes.except(:users)) system_notification.valid?.should be_false end end |
This is just a simple example but you can use Hash#except
in many other places:
- updating attributes like the Rail documentation sample
- copying values from one object to another
- extracting options from a argument
If you’d like some extra practice, refactor my specs to make sure SystemNotification#errors
is populated based on what attribute failed. You can find the full code on GitHub, just send me a pull request when you’re done.
Eric