SlideShare una empresa de Scribd logo
1 de 23
Descargar para leer sin conexión
Skinny Controllers
FAT MODELS
Thursday, April 18, 13
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
Blog post from 2006
1 <!-- 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
Simplify the view
1 <!-- 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
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
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
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
Seems good?
• right?
• right?
• view is cleaner
• controller is clean
• but can it be cleaner???
Thursday, April 18, 13
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
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
Many responsibilies!
• formatting
• building id
• database queries
• calulating
Thursday, April 18, 13
When I first saw this post
I thought it was super awesome
until it got so huge and cluttered
and did too many things
Thursday, April 18, 13
Single Responsibility
Principle
A class should do one thing
ONLY
Thursday, April 18, 13
# 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
View changes slightly
1 <!-- 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
But I propose a better way
Thursday, April 18, 13
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
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
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
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
Still a bit of an
experiment for me
• coworkers like it
• controllers are small
• models are for persistence only
Thursday, April 18, 13
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
questions?
• twitter: @rubygeekdotcom
• app.net: @rubygeek
• github: rubygeek
• web: blog.rubygeek.com
• email: nola@rubygeek.com
Thursday, April 18, 13

Más contenido relacionado

Destacado

Ruby Data Types and Data Structures
Ruby Data Types and Data StructuresRuby Data Types and Data Structures
Ruby Data Types and Data StructuresNola Stowe
 
Women Who Code Functional Programming - 9/26/2016
Women Who Code   Functional Programming - 9/26/2016Women Who Code   Functional Programming - 9/26/2016
Women Who Code Functional Programming - 9/26/2016Nola Stowe
 
All girlhacknight intro to rails
All girlhacknight intro to railsAll girlhacknight intro to rails
All girlhacknight intro to railsNola Stowe
 
Getting better through Katas
Getting better through KatasGetting better through Katas
Getting better through KatasNola Stowe
 
Intro to Clojure lightningtalk
Intro to Clojure lightningtalkIntro to Clojure lightningtalk
Intro to Clojure lightningtalkNola Stowe
 
EKONOMI & MANAJEMEN AGRIBISNIS
EKONOMI & MANAJEMEN AGRIBISNISEKONOMI & MANAJEMEN AGRIBISNIS
EKONOMI & MANAJEMEN AGRIBISNISHidayat Ramadhan
 
How to Run a ClojureBridge Workshop
How to Run a ClojureBridge WorkshopHow to Run a ClojureBridge Workshop
How to Run a ClojureBridge WorkshopNola Stowe
 
Euptoieta claudia hortensia
Euptoieta claudia hortensiaEuptoieta claudia hortensia
Euptoieta claudia hortensiaMárcio Martins
 

Destacado (9)

Ruby Data Types and Data Structures
Ruby Data Types and Data StructuresRuby Data Types and Data Structures
Ruby Data Types and Data Structures
 
Women Who Code Functional Programming - 9/26/2016
Women Who Code   Functional Programming - 9/26/2016Women Who Code   Functional Programming - 9/26/2016
Women Who Code Functional Programming - 9/26/2016
 
All girlhacknight intro to rails
All girlhacknight intro to railsAll girlhacknight intro to rails
All girlhacknight intro to rails
 
Getting better through Katas
Getting better through KatasGetting better through Katas
Getting better through Katas
 
Intro to Clojure lightningtalk
Intro to Clojure lightningtalkIntro to Clojure lightningtalk
Intro to Clojure lightningtalk
 
EKONOMI & MANAJEMEN AGRIBISNIS
EKONOMI & MANAJEMEN AGRIBISNISEKONOMI & MANAJEMEN AGRIBISNIS
EKONOMI & MANAJEMEN AGRIBISNIS
 
How to Run a ClojureBridge Workshop
How to Run a ClojureBridge WorkshopHow to Run a ClojureBridge Workshop
How to Run a ClojureBridge Workshop
 
Pseudoscada erruca
Pseudoscada errucaPseudoscada erruca
Pseudoscada erruca
 
Euptoieta claudia hortensia
Euptoieta claudia hortensiaEuptoieta claudia hortensia
Euptoieta claudia hortensia
 

Similar a Presenters

Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2RORLAB
 
Machine Learning, Key to Your Classification Challenges
Machine Learning, Key to Your Classification ChallengesMachine Learning, Key to Your Classification Challenges
Machine Learning, Key to Your Classification ChallengesMarc Borowczak
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)Joao Lucas Santana
 
Django tips & tricks
Django tips & tricksDjango tips & tricks
Django tips & tricksRenyi Khor
 
Liking Relevance - PHP North East 2014
Liking Relevance - PHP North East 2014Liking Relevance - PHP North East 2014
Liking Relevance - PHP North East 2014Jeroen van Dijk
 
Exploring the Sweet Spot: Geolocation, Health, and Gov-data
Exploring the Sweet Spot: Geolocation, Health, and Gov-data Exploring the Sweet Spot: Geolocation, Health, and Gov-data
Exploring the Sweet Spot: Geolocation, Health, and Gov-data Lance Roggendorff
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosEdgar Suarez
 
OpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con PythonOpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con PythonPyCon Italia
 
Deprecating ActiveRecord Attributes without making Zombies
Deprecating ActiveRecord Attributes without making ZombiesDeprecating ActiveRecord Attributes without making Zombies
Deprecating ActiveRecord Attributes without making Zombiesyann ARMAND
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksThemePartner
 
Snp tables documentation
Snp tables documentationSnp tables documentation
Snp tables documentationMahesh Birajdar
 

Similar a Presenters (17)

Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2Action View Form Helpers - 1, Season 2
Action View Form Helpers - 1, Season 2
 
Java 8 revealed
Java 8 revealedJava 8 revealed
Java 8 revealed
 
Machine Learning, Key to Your Classification Challenges
Machine Learning, Key to Your Classification ChallengesMachine Learning, Key to Your Classification Challenges
Machine Learning, Key to Your Classification Challenges
 
Ruby on Rails
Ruby on RailsRuby on Rails
Ruby on Rails
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)Desenvolvimento web com Ruby on Rails (parte 2)
Desenvolvimento web com Ruby on Rails (parte 2)
 
Django tips & tricks
Django tips & tricksDjango tips & tricks
Django tips & tricks
 
Liking Relevance - PHP North East 2014
Liking Relevance - PHP North East 2014Liking Relevance - PHP North East 2014
Liking Relevance - PHP North East 2014
 
Eu odeio OpenSocial
Eu odeio OpenSocialEu odeio OpenSocial
Eu odeio OpenSocial
 
Exploring the Sweet Spot: Geolocation, Health, and Gov-data
Exploring the Sweet Spot: Geolocation, Health, and Gov-data Exploring the Sweet Spot: Geolocation, Health, and Gov-data
Exploring the Sweet Spot: Geolocation, Health, and Gov-data
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutos
 
OpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con PythonOpenERP e l'arte della gestione aziendale con Python
OpenERP e l'arte della gestione aziendale con Python
 
Deprecating ActiveRecord Attributes without making Zombies
Deprecating ActiveRecord Attributes without making ZombiesDeprecating ActiveRecord Attributes without making Zombies
Deprecating ActiveRecord Attributes without making Zombies
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Migrating legacy data
Migrating legacy dataMigrating legacy data
Migrating legacy data
 
Slimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en TruuksSlimme Joomla! Templating Tips en Truuks
Slimme Joomla! Templating Tips en Truuks
 
Snp tables documentation
Snp tables documentationSnp tables documentation
Snp tables documentation
 

Último

Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 

Último (20)

Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 

Presenters

  • 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
  • 3. Blog post from 2006 1 <!-- 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 view 1 <!-- 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 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
  • 11. Many responsibilies! • formatting • building id • database queries • calulating Thursday, April 18, 13
  • 12. When I first saw this post I thought it was super awesome until it got so huge and cluttered and did too many things Thursday, April 18, 13
  • 13. Single Responsibility Principle A class should do one thing ONLY 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
  • 15. View changes slightly 1 <!-- 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 way 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
  • 23. questions? • twitter: @rubygeekdotcom • app.net: @rubygeek • github: rubygeek • web: blog.rubygeek.com • email: nola@rubygeek.com Thursday, April 18, 13