2. Refactor of a blog post
•Skinny Controller, Fat
Model
• Popular post from 6 years ago and I
followed it with all my code since
• http://weblog.jamisbuck.org/
2006/10/18/skinny-controller-fat-
model
Thursday, April 18, 13
5. move @people collection
to controller
1
2 # app/controllers/people_controller.rb
3 class PeopleController < ActionController::Base
4 def index
5 @people = Person.find(
6 :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],
7 :order => "last_name, first_name")
8 @people = @people.reject { |p| p.address.nil? }
9 end
10 end
Thursday, April 18, 13
6. Create methods in Model
1 # app/models/person.rb
2 class Person < ActiveRecord::Base
3 # ...
4
5 def name
6 "#{last_name}, #{first_name}"
7 end
8
9 def age
10 (Date.today - person.birthdate) / 365
11 end
12
13 def pseudo_id
14 new_record? ? "new" : id
15 end
16 end
Thursday, April 18, 13
7. Making view cleaner
1 <!-- app/views/people/index.rhtml -->
2 <% @people.each do |person| %>
3 <div id="person-<%= person.pseudo_id %>">
4 <span class="name"><%= person.name %></span>
5 <span class="age"><%= person.age %></span>
6 </div>
7 <% end %>
Thursday, April 18, 13
8. Seems good?
• right?
• right?
• view is cleaner
• controller is clean
• but can it be cleaner???
Thursday, April 18, 13
9. Yes it can
1 # app/models/person.rb
2 class Person < ActiveRecord::Base
3 def self.find_recent
4 people = find(
5 :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],
6 :order => "last_name, first_name")
7 people.reject { |p| p.address.nil? }
8 end
9
10 # ...
11 end
12
13 # app/controllers/people_controller.rb
14 class PeopleController < ActionController::Base
15 def index
16 @people = Person.find_recent
17 end
18 end
Thursday, April 18, 13
10. Model is now huge
• it does a query to get recent persons
• it builds a full name with first+last
• it calculates the age of a person
• it creates a psuedo_id
Thursday, April 18, 13
14. # app/helpers/person_helper.rb
module PersonHelper
def name(person)
"#{person.last_name}, #{person.first_name}"
end
def age(person)
(Date.today - person.birthdate) / 365
end
def pseudo_id(person)
person.new_record? ? "new" : id
end
end
Move the formatting to a
helper
Thursday, April 18, 13
17. Presenter
1 class PersonPresenter
2
3 def initialize(person)
4 @person = person
5 end
6
7 def name
8 "#{@person.last_name}, #{@person.first_name}"
9 end
10
11 def age
12 (Date.today - @person.birthdate) / 365
13 end
14
15 def pseudo_id
16 @person.new_record? ? "new" : id
17 end
18
19 end
Thursday, April 18, 13
18. Controller
Collection Presenter
1 # app/controllers/people_controller.rb
2 class PeopleController < ActionController::Base
3 def index
4 people = Person.find_recent
5 @presenters = PeoplePresenter.new(people)
6 end
7 end
8
9 # app/presenters/people_presenter.rb
10 class PeoplePresenter
11 attr_reader :people
12
13 def initialize(persons)
14 @people = persons.each { |person| PersonPresenter.new(person) }
15 end
16 end
Thursday, April 18, 13
19. View with presenter
1 <!-- app/views/people/index.rhtml -->
2 <% @presenters.each do |present| %>
3 <div id="person-<%= present.pseudo_id %>">
4 <span class="name"><%= present.name %></span>
5 <span class="age"><%= present.age %></span>
6 </div>
7 <% end %>
8
Thursday, April 18, 13
20. Tests are smaller
1
2
3 describe PersonPresenter do
4
5 let(:person) { FactoryGirl.create(:person,
first_name: "Bob",
last_name => "Green" )}
6
7 it "formats name" do
8 presenter = PersonPresenter.new(person)
9 presenter.name.should == "Green, Bob"
10 end
11
12 end
May not even need to fire up database if
your inputs to presenter are simple
results in faster tests
Thursday, April 18, 13
21. Still a bit of an
experiment for me
• coworkers like it
• controllers are small
• models are for persistence only
Thursday, April 18, 13
22. Other techniques
• Service Objects - http://railscasts.com/episodes/398-service-
objects
• RubyPair - domain models https://github.com/rubypair/
rubypair
• ActiveSupport::Concerns - http://
programmingtour.blogspot.com/2012/12/why-i-dont-use-
activesupportconcern.html
• Draper, a gem for presenters - https://github.com/
drapergem/draper
• Learned some of what I did with presenters from CodeSchool
“Rails Best Practices” - http://www.codeschool.com/courses/
rails-best-practices
Thursday, April 18, 13