Back in January, Sandi Metz introduced her rules for developers in a Ruby Rogues podcast episode episode. Around the time Sandi’s rules were published, the team I am on was starting a new project. This post details the experience of that team applying Sandi’s rules to the new application.
Here are the rules:
@object.collaborator.value is not allowed).Paraphrasing Sandi, “You should break these rules only if you have a good reason or your pair lets you.” Your pair or the person reviewing your code are the people who you should ask.
Think of this as rule zero. It is immutable.
Despite the large number of private methods we wrote, keeping classes short proved easy. It forced us consider what the single responsibility of our class was, and what should be extracted.
This applied to specs as well. In one case, we found a spec file ran over the limit which helped us realize we were testing too many features. We split the file into a few, more focused, feature specs.
That made us realize that git diffs wouldn’t necessarily show us when we exceed 100 lines.
Limiting methods to five lines per method is the most interesting rule.
We agreed if, else, and end are all lines. In an if block with two branches, each branch could only be one line.
For example:
def validate_actor
if actor_type == 'Group'
user_must_belong_to_group
elsif actor_type == 'User'
user_must_be_the_same_as_actor
end
end
Five lines ensured that we never use else with elsif.
Having only one line per branch urged us to use well-named private methods to get work done. Private methods are great documentation. They need very clear names, which forced us to think about the content of the code we were extracting.
The four method arguments rule was particularly challenging in Rails, and particularly in the views.
View helpers such as link_to or form_for can end up requiring many parameters to work correctly. While we put some effort into not passing too many arguments, we fell back to Rule 0 and left the parameters if we couldn’t find a better way to do it.
This rule raised the most eyebrows before we started the experiment. Often, we needed more than one type of thing on a page. For example, a homepage needed both an activity feed and a notification counter.
We solved this using the Facade Pattern. It looked like this:
app/facades/dashboard.rb:
class Dashboard
def initialize(user)
@user = user
end
def new_status
@new_status ||= Status.new
end
def statuses
Status.for(user)
end
def notifications
@notifications ||= user.notifications
end
private
attr_reader :user
end
app/controllers/dashboards_controller.rb:
class DashboardsController < ApplicationController
before_filter :authorize
def show
@dashboard = Dashboard.new(current_user)
end
end
app/views/dashboards/show.html.erb:
<%= render 'profile' %>
<%= render 'groups', groups: @dashboard.group %>
<%= render 'statuses/form', status: @dashboard.new_status %>
<%= render 'statuses', statuses: @dashboard.statuses %>
The Dashboard class provided a common interface for locating the user’s collaborator objects and we passed the dashboard’s state to view partials.
We didn’t count instance variables in controller memoizations toward the limit. We used a convention of prefixing unused variables with an underscore to make it clear what is meant to be used in a view:
def calculate
@_result_of_expensive_calculation ||= SuperCalculator.get_started(thing)
end
We recently concluded our experiment as a success, published results in our research newsletter, and have incorporated the rules into our best practices guide.
Written by Caleb Thompson.
Besides moving attribute whitelisting to the controller rather than the model,
Rails 4’s move to
Strong Parameters
over attr_accessible provides great documentation about the data with which
records are being created.
Here is an example of a controller many of us have written, using
strong_parameters:
class CommentsController < ApplicationController
respond_to :html
def create
@comment = Comment.create(comment_params)
respond_with @comment
end
private
def comment_params
params.
require(:comment).
permit(:body).
merge(user: current_user, commentable: commentable)
end
def commentable
# find and return a commentable record
end
end
Notice how the comment_params method tells you at a glance what object’s
parameters this controller/action cares about (comment), the specific data
being used (body), and the extra information being added. After glancing at
the method, you hardly have to concern yourself with the rest of the class:
everything just makes sense.
strong_parameters will be standard in Rails 4.0, but they can be used now in
Rails 3.*.
Written by Caleb Thompson.
We just pushed the latest update to Ruby Science, including three new chapters. Previous purchasers and Prime subscribers can grab the update on Learn.
New chapters this week discuss:
The book is a work in progress, and currently contains around 158 pages of content. Your purchase gets you access to the current release of the book, all future updates, and the companion example application. In addition, purchasers can send thoughtbot their toughest Ruby, Rails, and refactoring questions.
Get your copy of Ruby Science today.
Earlier this week, we announced the launch of our subscription service: Learn Prime.
For just $99/month, you get ongoing access to everything we teach, including books like Ruby Science. You’ll even get access to all our in-person and online workshops. Get access to exclusive subscriber content, as well as access to our private Campfire room, where you can get live help from thoughtbot designers and developers.
We pushed another update to Ruby Science today which includes five brand new chapters. Current readers can grab the update on Learn.
This week’s updates dive into three important topics. New chapters:
The first two chapters discuss the importance of naming methods and classes. We discuss how to use class and method names to improve readability and discover classes and methods that need refactoring.
Another chapter demonstrates how to extract concerns in a repeatable, combinable way using the Decorator pattern.
The last two chapters provide tips for identifying and fixing mixin abuse.
The book is a work in progress, and currently contains around 145 pages of content. A $49 purchase gets you access to the current release of the book, all future updates, and the companion example application. In addition, purchasers can send thoughtbot their toughest Ruby, Rails, and refactoring questions.
Today’s release of Ruby Science includes three new chapters. If you’re already reading Ruby Science, make sure to grab the latest version.
This week’s updates include tips on safely extracting classes, as well as a specific example of extracting Value Objects. In addition, we discuss how you can use Ruby’s classes-as-objects attitude to eliminate the need for most abstract factory classes.
The book is a work in progress, and currently contains around 123 pages of content. A $49 purchase gets you access to the current release of the book, all future updates, and the companion example application. In addition, purchasers have the ability to send thoughtbot their toughest Ruby, Rails, and refactoring questions.