Your SlideShare is downloading. ×
0
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Presenters
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Presenters

238

Published on

Skinny Controllers, Fat Models and ways to slim your models and make it more testable

Skinny Controllers, Fat Models and ways to slim your models and make it more testable

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
238
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  1. Skinny ControllersFAT MODELSThursday, April 18, 13
  2. Refactor of a blog post•Skinny Controller, FatModel• Popular post from 6 years ago and Ifollowed it with all my code since• http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-modelThursday, April 18, 13
  3. Blog post from 20061 <!-- app/views/people/index.rhtml -->2 <% people = Person.find(3 :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],4 :order => "last_name, first_name") %>5 <% people.reject { |p| p.address.nil? }.each do |person| %>6 <div id="person-<%= person.new_record? ? "new" : person.id %>">7 <span class="name">8 <%= person.last_name %>, <%= person.first_name %>9 </span>10 <span class="age">11 <%= (Date.today - person.birthdate) / 365 %>12 </span>13 </div>14 <% end %>Thursday, April 18, 13
  4. Simplify the view1 <!-- app/views/people/index.rhtml -->2 <% @people.each do |person| %>3 <div id="person-<%= person.new_record? ? "new" : person.id %>">4 <span class="name">5 <%= person.last_name %>, <%= person.first_name %>6 </span>7 <span class="age">8 <%= (Date.today - person.birthdate) / 365 %>9 </span>10 </div>11 <% end %>Thursday, April 18, 13
  5. move @people collectionto controller12 # app/controllers/people_controller.rb3 class PeopleController < ActionController::Base4 def index5 @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 end10 endThursday, April 18, 13
  6. Create methods in Model1 # app/models/person.rb2 class Person < ActiveRecord::Base3 # ...45 def name6 "#{last_name}, #{first_name}"7 end89 def age10 (Date.today - person.birthdate) / 36511 end1213 def pseudo_id14 new_record? ? "new" : id15 end16 endThursday, April 18, 13
  7. Making view cleaner1 <!-- 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 can1 # app/models/person.rb2 class Person < ActiveRecord::Base3 def self.find_recent4 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 end910 # ...11 end1213 # app/controllers/people_controller.rb14 class PeopleController < ActionController::Base15 def index16 @people = Person.find_recent17 end18 endThursday, 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_idThursday, April 18, 13
  11. Many responsibilies!• formatting• building id• database queries• calulatingThursday, April 18, 13
  12. When I first saw this postI thought it was super awesomeuntil it got so huge and clutteredand did too many thingsThursday, April 18, 13
  13. Single ResponsibilityPrincipleA class should do one thingONLYThursday, April 18, 13
  14. # app/helpers/person_helper.rbmodule PersonHelperdef name(person)"#{person.last_name}, #{person.first_name}"enddef age(person)(Date.today - person.birthdate) / 365enddef pseudo_id(person)person.new_record? ? "new" : idendendMove the formatting to ahelperThursday, April 18, 13
  15. View changes slightly1 <!-- app/views/people/index.rhtml -->2 <% @people.each do |person| %>3 <div id="person-<%= pseudo_id(person) %>">4 <span class="name"><%= name(person) %></span>5 <span class="age"><%= age(person) %></span>6 </div>7 <% end %>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
  16. But I propose a better wayThursday, April 18, 13
  17. Presenter1 class PersonPresenter23 def initialize(person)4 @person = person5 end67 def name8 "#{@person.last_name}, #{@person.first_name}"9 end1011 def age12 (Date.today - @person.birthdate) / 36513 end1415 def pseudo_id16 @person.new_record? ? "new" : id17 end1819 endThursday, April 18, 13
  18. ControllerCollection Presenter1 # app/controllers/people_controller.rb2 class PeopleController < ActionController::Base3 def index4 people = Person.find_recent5 @presenters = PeoplePresenter.new(people)6 end7 end89 # app/presenters/people_presenter.rb10 class PeoplePresenter11 attr_reader :people1213 def initialize(persons)14 @people = persons.each { |person| PersonPresenter.new(person) }15 end16 endThursday, April 18, 13
  19. View with presenter1 <!-- 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 %>8Thursday, April 18, 13
  20. Tests are smaller123 describe PersonPresenter do45 let(:person) { FactoryGirl.create(:person,first_name: "Bob",last_name => "Green" )}67 it "formats name" do8 presenter = PersonPresenter.new(person)9 presenter.name.should == "Green, Bob"10 end1112 endMay not even need to fire up database ifyour inputs to presenter are simpleresults in faster testsThursday, April 18, 13
  21. Still a bit of anexperiment for me• coworkers like it• controllers are small• models are for persistence onlyThursday, 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-practicesThursday, April 18, 13
  23. questions?• twitter: @rubygeekdotcom• app.net: @rubygeek• github: rubygeek• web: blog.rubygeek.com• email: nola@rubygeek.comThursday, April 18, 13

×