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

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
 
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
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native ApplicationsWSO2
 
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
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...apidays
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...apidays
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...apidays
 
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
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Victor Rentea
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKJago de Vreede
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusZilliz
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
 

Kürzlich hochgeladen (20)

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
 
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
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
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...
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 

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