SlideShare ist ein Scribd-Unternehmen logo
1 von 23
Downloaden Sie, um offline zu lesen
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

Weitere ähnliche Inhalte

Andere mochten auch

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
 

Andere mochten auch (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
 

Ähnlich wie 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
 
От Rails-way к модульной архитектуре
От Rails-way к модульной архитектуреОт Rails-way к модульной архитектуре
От Rails-way к модульной архитектуреIvan Nemytchenko
 
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
 

Ähnlich wie Presenters (18)

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
 
От Rails-way к модульной архитектуре
От Rails-way к модульной архитектуреОт Rails-way к модульной архитектуре
От Rails-way к модульной архитектуре
 
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
 

Kürzlich hochgeladen

Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Zilliz
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...apidays
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024The Digital Insurer
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businesspanagenda
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...apidays
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusZilliz
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Jeffrey Haguewood
 

Kürzlich hochgeladen (20)

Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 

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