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
 
От 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
 

Similar a 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
 

Último

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 

Último (20)

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 

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