Fluent Refactoring (Cascadia Ruby Conf 2013)

Sam Livingston-Gray
Sam Livingston-GraySoftware Developer
Fluent Refactoring
Sam Livingston-Gray

THERE
WILL BE
CODE!
It may be
this small

puts (1..100).map { |i|
s = ''
fizz = (i % 3).zero?
buzz = (i % 5).zero?
s << 'Fizz' if fizz
s << 'Buzz' if buzz
s = i if s.empty?
s
}

1
Tuesday, October 22, 13
Math
m/

2
Tuesday, October 22, 13
3
http://2012books.lardbucket.org/books/elementary-algebra/section_06/5d10b670d78abac93a4572dc0c2afb0f.jpg
Tuesday, October 22, 13
4
Tuesday, October 22, 13

http://www.wikihow.com/Image:Solve-for-X-Step-12.jpg
5
Tuesday, October 22, 13

http://math.about.com/od/algebra/ss/birthday.htm
6
http://www.smosh.com/smosh-pit/photos/16-wonderfully-stupid-test-answers
Tuesday, October 22, 13
Algebra

7
Tuesday, October 22, 13
Algebra Isn’t Math

8
Tuesday, October 22, 13
Algebra Isn’t all of Math

9
Tuesday, October 22, 13
Algebra ⊂ Math
Algebra

Math
10
Tuesday, October 22, 13
11

http://upload.wikimedia.org/wikipedia/commons/thumb/0/08/NautilusCutawayLogarithmicSpiral.jpg/793px-NautilusCutawayLogarithmicSpiral.jpg
Tuesday, October 22, 13
12
http://upload.wikimedia.org/wikipedia/commons/a/a4/Mandelbrot_sequence_new.gif
Tuesday, October 22, 13
13
http://mathequalslove.blogspot.com/2012/12/hexaflexagon-love.html
Tuesday, October 22, 13
14
http://think-like-a-git.net/sections/graph-theory/seven-bridges-of-konigsberg.html
Tuesday, October 22, 13
Math is a Language

15
Tuesday, October 22, 13
Math is a Language
Algebra is
[one of]
its Grammar[s]

15
Tuesday, October 22, 13
Fluent Refactoring

16
Tuesday, October 22, 13
17
http://www.kickasslabs.com/2012/04/28/rails-conf-2012-images/100_0304/
Tuesday, October 22, 13
Can I get a
definition?

17
http://www.kickasslabs.com/2012/04/28/rails-conf-2012-images/100_0304/
Tuesday, October 22, 13
Re·fac·tor·ing (noun)

18
Tuesday, October 22, 13
Re·fac·tor·ing (noun)
"...a disciplined technique for
restructuring an existing body of code,
altering its internal structure without
changing its external behavior."
-refactoring.com

18
Tuesday, October 22, 13
TL;DR

19
Tuesday, October 22, 13
TL;DR
"...a disciplined technique for
restructuring an existing body of
code, altering its internal structure
without changing its external
behavior."

19
Tuesday, October 22, 13
Re·fac·tor·ing (noun)
A technique
for restructuring code
without changing behavior

20
Tuesday, October 22, 13
Tell a clearer story
with fewer details

21
Tuesday, October 22, 13
Jargon

22
Tuesday, October 22, 13

http://www.tagxedo.com/app.html
Jargon

22
Tuesday, October 22, 13

http://www.tagxedo.com/app.html
Re·fac·tor·ing (noun)

23
Tuesday, October 22, 13
Re·fac·tor·ing (noun)
A language that
describes ways to
make your code
suck less.
23
Tuesday, October 22, 13
Flu·en·cy (noun)

24
Tuesday, October 22, 13
Flu·en·cy (noun)
What you can say when you’re
not thinking about how to say it

24
Tuesday, October 22, 13
Flu·en·cy (noun)
What you can say when you’re
woken up in the middle of the night
with a flashlight in your face

25
Tuesday, October 22, 13
Stress

26
http://dailyawesimity.files.wordpress.com/2013/01/cat-stress-relief-4.jpg
Tuesday, October 22, 13
Language Hunters

27
Tuesday, October 22, 13
Levels of Proficiency
Level 1
Level 2
Level 3
Level 4

28
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Levels of Proficiency
Level 1

Tarzan at
a party

“Beer!”
“Good party.”

Level 2
Level 3
Level 4

29
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Levels of Proficiency
Level 1

Tarzan at
a party

“Beer!”
“Good party.”

Level 2

Going to
the party

"Where is the party?"
"How do I get to the party?"

Level 3
Level 4

30
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Levels of Proficiency
Level 1

Tarzan at
a party

“Beer!”
“Good party.”

Level 2

Going to
the party

"Where is the party?"
"How do I get to the party?"

Level 3

Discussing
the party

"What happened at the party
last night?"

Level 4

31
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Levels of Proficiency
Level 1

Tarzan at
a party

“Beer!”
“Good party.”

Level 2

Going to
the party

"Where is the party?"
"How do I get to the party?"

Level 3

Discussing
the party

"What happened at the party
last night?"

Level 4

Charlie Rose

"Should parties be illegal?"

32
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Levels of Proficiency
Level 1

Tarzan at
a party

“Beer!”
“Good party.”

Level 2

Going to
the party

"Where is the party?"
"How do I get to the party?"

Level 3

Discussing
the party

"What happened at the party
last night?"

Level 4

Charlie Rose

"Should parties be illegal?"

33
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Levels of Proficiency
Level 1

Tarzan at
a party

“Beer!”
“Good party.”

Level 2

Going to
the party

"Where is the party?"
"How do I get to the party?"

34
Tuesday, October 22, 13

http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
Story Time

35
Tuesday, October 22, 13
Production Rails Code

36
Tuesday, October 22, 13
Production Rails Code
Used with:

• Permission

36
Tuesday, October 22, 13
Production Rails Code
Used with:

• Permission
• Obfuscation

36
Tuesday, October 22, 13
Production Rails Code
Used with:

• Permission
• Obfuscation
• Respect

36
Tuesday, October 22, 13
Respect

37
Tuesday, October 22, 13
class InstallationsController < ActionController::Base
# lots more stuff...
def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end
# lots more stuff...
end

Tuesday, October 22, 13

38
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end
# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

Longest line: 177 chars

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

Longest line: 177 chars
Indentation: 4-16 spaces

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

Longest line: 177 chars
Indentation: 4-16 spaces
Nested control
structures:

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

Longest line: 177 chars
Indentation: 4-16 spaces
Nested control
structures:
audit_trail_for

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

Longest line: 177 chars
Indentation: 4-16 spaces
Nested control
structures:
audit_trail_for

begin/rescue/end

# lots more stuff...
end

Tuesday, October 22, 13

39
Observations

class InstallationsController < ActionController::Base
# lots more stuff...

def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end

~800 lines in file

~50 lines in method

Longest line: 177 chars
Indentation: 4-16 spaces
Nested control
structures:
audit_trail_for

begin/rescue/end
if/else/end

# lots more stuff...
end

Tuesday, October 22, 13

39
Complexity

40
http://scientopia.org/blogs/whitecoatunderground/2009/06/10/my-head-just-asploded-twice/
Tuesday, October 22, 13
Ship it!

41
Tuesday, October 22, 13

http://shipitsquirrel.github.io/
~800 lines

42
Tuesday, October 22, 13
~800 lines

42
http://hyperboleandahalf.blogspot.com/2010/06/this-is-why-ill-never-be-adult.html
Tuesday, October 22, 13
~800 lines

42
http://hyperboleandahalf.blogspot.com/2010/06/this-is-why-ill-never-be-adult.html
Tuesday, October 22, 13
~800 lines

43
http://hyperboleandahalf.blogspot.com/2010/06/this-is-why-ill-never-be-adult.html
Tuesday, October 22, 13
Make the Job Smaller

44
Tuesday, October 22, 13
Replace Method with
Method Object
See also:
Katrina Owen,
“Therapeutic Refactoring”

45
Tuesday, October 22, 13
class InstallationsController <
ActionController::Base
def schedule
# LOTS OF CODE
end
end

46
Tuesday, October 22, 13
class InstallationsController <
ActionController::Base
def schedule
# LOTS OF CODE
end
end
class ScheduleInstallation
def call
end
end

47
Tuesday, October 22, 13
class InstallationsController <
ActionController::Base
def schedule
end
end
class ScheduleInstallation
def call
# LOTS OF CODE
end
end

47
Tuesday, October 22, 13
class InstallationsController <
ActionController::Base
def schedule
ScheduleInstallation.new.call
end
end
class ScheduleInstallation
def call
# LOTS OF CODE
end
end

47
Tuesday, October 22, 13
NoMethodError
LOL WUT?

48
Tuesday, October 22, 13
class
def
#
#
#
#
end
end

ScheduleInstallation
call
LOTS OF CODE
... params ...
... render ...
... redirect_to ...

49
Tuesday, October 22, 13
class ScheduleInstallation
def initialize(controller)
@controller = controller
end
def
#
#
#
#
end
end

call
LOTS OF CODE
... params ...
... render ...
... redirect_to ...

50
Tuesday, October 22, 13
class ScheduleInstallation
def initialize(controller)
@controller = controller
end
def
#
#
#
#
end
end

call
LOTS OF CODE
... @controller.params ...
... @controller.render ...
... @controller.redirect_to ...

51
Tuesday, October 22, 13
class ScheduleInstallation
def initialize(controller)
@controller = controller
end
extend Forwardable
def_delegators :@controller,
:params, :render, :redirect_to
def
#
#
#
#
end
end

call
LOTS OF CODE
... params ...
... render ...
... redirect_to ...

52
Tuesday, October 22, 13
class ScheduleInstallation
def initialize(controller)
@controller = controller
end
def call
# LOTS OF CODE
end
def method_missing(m, *a, &b)
@controller.send(m, *a, &b)
end
end

53
Tuesday, October 22, 13
Code Archaeology

54
http://anthro.ucsc.edu/undergraduate/sub-fields/anthro-archeaology.html
Tuesday, October 22, 13
if request.xhr?
# ...20 lines...
else
# ...22 lines...
end

55
Tuesday, October 22, 13
if request.xml_http_request?
# ...20 lines...
else
# ...22 lines...
end

56
Tuesday, October 22, 13
if request.xml_http_request?
begin
#...
end
else
# ...22 lines...
end

57
Tuesday, October 22, 13
if request.xml_http_request?
begin
if @installation.pending_credit_check?
render :json => #...
return
end
#...
end
else
# ...22 lines...
end

58
Tuesday, October 22, 13
if request.xml_http_request?
begin
if @installation.pending_credit_check?
render :json => #...
return
end
#...
end
else
# ...22 lines...
end

58
Tuesday, October 22, 13
if request.xml_http_request?
begin
if @installation.pending_credit_check?
render :json => #...
return
end
#...
Guard Clause
end
-Smalltalk Best Practice Patterns
else
by Kent Beck
# ...22 lines...
end

58
Tuesday, October 22, 13
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => #...
return
end
#...
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to installations_path(:city_id =>
end
begin
#...
end
end
59
Tuesday, October 22, 13
tion.city_id, :view => "calendar") and return

59
Tuesday, October 22, 13
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => #...
return
end
#...
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to installations_path(:city_id =>
return
end
begin
#...
end
60
Tuesday, October 22, 13
if request.xhr?
if request.xhr?
begin
if @installation.pend
if @installation.pending_credit_check? #..
render :json =>
render :json => #... return
return
end
end
begin
#...
#...
end
end
else
else
if @installation.pending_credit_check?
if @installation.pend
flash[:error] = #...
flash[:error] = #..
redirect_to installations_path(:city_id =>
redirect_to install
return
return
end
end
begin
begin
#...
#...
end
end
60
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
begin
#...
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to installations_path(:city_id =>
return
end
begin
#...
end
61
Tuesday, October 22, 13
if request.xhr?
if request.xhr?
if @installation.pending_credit_check?
if @installation.pend
render :json => #...
render :json => #..
return
return
end
end
begin
else
#...
if @installation.pend
end
flash[:error] = #..
else
redirect_to #...
if @installation.pending_credit_check?
return
flash[:error] = #... end
redirect_to installations_path(:city_id =>
end
return
end
if request.xhr?
begin
begin
#...
#...
end
end
61
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to #...
return
end
end

62
Tuesday, October 22, 13
emph·AS·is

63
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to #...
return
end
end

64
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to #...
return
end
end

64
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to #...
return
end
end

64
Tuesday, October 22, 13
Flatten Nested
Conditionals
source:
Michael Feathers
in Dr. Dobbs

65
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to #...
return
end
end

66
Tuesday, October 22, 13
if ajax
if nope
render :json => #...
return
end
else
if nope
flash[:error] = #...
redirect_to #...
return
end
end

67
Tuesday, October 22, 13
if ajax
if nope
render :json => #...
return
end
else
if nope
flash[:error] = #...
redirect_to #...
return
end
end

67
Tuesday, October 22, 13

ajax

nope

TRUE

TRUE

ajax

nope

FALSE

TRUE
if ajax
if nope
render :json => #...
return
end
else
if nope
flash[:error] = #...
redirect_to #...
return
end
end

68
Tuesday, October 22, 13
if ajax
if ajax
if nope
if nope
render :json => #...
render :json => #..
return
return
end
end
else
end
if nope
if not ajax
flash[:error] = #... if nope
redirect_to #...
flash[:error] = #..
return
redirect_to #...
end
return
end
end
end

68
Tuesday, October 22, 13
if ajax
if nope
render :json => #...
return
end
end
if not ajax
if nope
flash[:error] = #...
redirect_to #...
return
end
end

69
Tuesday, October 22, 13
if ajax
if ajax && nope
if nope
render :json => #...
render :json => #... return
return
end
end
if (not ajax) && nope
end
flash[:error] = #...
if not ajax
redirect_to #...
if nope
return
flash[:error] = #...end
redirect_to #...
return
end
end

69
Tuesday, October 22, 13
if ajax && nope
render :json => #...
return
end
if (not ajax) && nope
flash[:error] = #...
redirect_to #...
return
end

70
Tuesday, October 22, 13
if ajax && nope
render :json => #...
return
end
if (not ajax) && nope
flash[:error] = #...
redirect_to #...
return
end

70
Tuesday, October 22, 13
if ajax && nope
render :json => #...
return
end
if (not ajax) && nope
flash[:error] = #...
redirect_to #...
return
end

if nope
if ajax
render :json => #..
return
end
if not ajax
flash[:error] = #..
redirect_to #...
return
end
end

70
Tuesday, October 22, 13
if nope
if ajax
render :json => #...
return
end
if not ajax
flash[:error] = #...
redirect_to #...
return
end
end

71
Tuesday, October 22, 13
if nope
if nope
if ajax
if ajax
render :json => #...
render :json => #..
return
return
end
else
if not ajax
flash[:error] = #..
flash[:error] = #...
redirect_to #...
redirect_to #...
return
return
end
end
end
end

71
Tuesday, October 22, 13
if nope
if ajax
render :json => #...
return
else
flash[:error] = #...
redirect_to #...
return
end
end

72
Tuesday, October 22, 13
if nope
if nope
if ajax
if ajax
render :json => #...
render :json => #..
return
else
else
flash[:error] = #..
flash[:error] = #...
redirect_to #...
redirect_to #...
end
return
return
end
end
end

72
Tuesday, October 22, 13
if request.xhr?
if @installation.pending_credit_check?
render :json => #...
return
end
else
if @installation.pending_credit_check?
flash[:error] = #...
redirect_to #...
return
end
end

73
Tuesday, October 22, 13
if request.xhr?
if @installation.pendin
if @installation.pending_credit_check?
if request.xhr?
render :json => #...
render :json => #..
return
else
end
flash[:error] = #..
else
redirect_to #...
if @installation.pending_credit_check?
end
flash[:error] = #... return
redirect_to #...
end
return
end
end

73
Tuesday, October 22, 13
if @installation.pending_credit_check?
if request.xhr?
render :json => #...
else
flash[:error] = #...
redirect_to #...
end
return
end

74
Tuesday, October 22, 13
if @installation.pendin
if @installation.pending_credit_check?
cant_schedule_while_c
if request.xhr?
render :json => #... return
end
else
flash[:error] = #...
redirect_to #...
end
return
end

74
Tuesday, October 22, 13
if @installation.pending_credit_check?
cant_schedule_while_credit_check_pending
return
end
if request.xhr?
begin
#...
end
else
begin
#...
end
end

75
Tuesday, October 22, 13
“Hard Work” Montage

76
http://spectrumculture.com/2012/09/re-makere-model-the-karate-kid-1984-vs-the-karate-kid-2010.html/
Tuesday, October 22, 13
class InstallationsController < ActionController::Base
def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end
end

77
Tuesday, October 22, 13
class InstallationsController < ActionController::Base
def schedule
desired_date = params[:desired_date]
if request.xhr?
begin
if @installation.pending_credit_check?
render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400
return
end
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date
render :json => {:errors => nil, :html => schedule_response(@installation, date)}
end
else
render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] }
end
end
rescue ActiveRecord::RecordInvalid => e
render :json => {:errors => [e.message] }
rescue ArgumentError => e
render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]}
end
else
if @installation.pending_credit_check?
flash[:error] = "Cannot schedule installation while credit check is pending"
redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return
end
begin
audit_trail_for(current_user) do
if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city)
if @installation.scheduled_date
if @installation.customer_provided_equipment?
flash[:success] = %Q{Installation scheduled}
else
flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.}
end
end
else
flash[:error] = %Q{Could not schedule installation, check the phase of the moon}
end
end
rescue => e
flash[:error] = e.message
end
redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path
: installations_path(:city_id => @installation.city_id, :view => "calendar"))
end
end
end

class ScheduleInstallation
def call
if @installation.pending_c
cant_schedule_while_cred
return
end

77
Tuesday, October 22, 13

audit_trail_for(current_us
if schedule!
if @installation.sched
scheduling_succeeded
end
do_post_success_cleanu
else
scheduling_failed
end
end
rescue => e
handle_exception e
end
end
class ScheduleInstallation
def call
if @installation.pending_credit_check?
cant_schedule_while_credit_check_pending
return
end
audit_trail_for(current_user) do
if schedule!
if @installation.scheduled_date
scheduling_succeeded
end
do_post_success_cleanup
else
scheduling_failed
end
end
rescue => e
handle_exception e
end
end
78

Tuesday, October 22, 13
class ScheduleInstallation
def call
if @installation.pending_credit_check?
cant_schedule_while_credit_check_pending
return
end
audit_trail_for(current_user) do
if schedule!
if @installation.scheduled_date
scheduling_succeeded
end
do_post_success_cleanup
else
scheduling_failed
end
end
rescue => e
handle_exception e
end
end

request.xhr?

79

Tuesday, October 22, 13
class ScheduleInstallation
def call
if @installation.pending_credit_check?
cant_schedule_while_credit_check_pending
return
end
audit_trail_for(current_user) do
if schedule!
if @installation.scheduled_date
scheduling_succeeded
end
do_post_success_cleanup
else
scheduling_failed
end
end
rescue => e
handle_exception e
end
end

request.xhr?

79

Tuesday, October 22, 13
Under The Rug

80
Tuesday, October 22, 13
def cannot_schedule_while_#...
if request.xhr?
# ...1 line...
else
# ...2 lines...
end
end

def scheduling_failed
if request.xhr?
# ...1 line...
else
# ...2 lines...
end
end

def handle_exception(e)
if request.xhr?
# ...7 lines...
else
# ...2 lines...
end
end

def do_post_success_cleanup
if request.xhr?
# DO NOTHING
else
# ...1 line...
end
end

def scheduling_succeeded
if request.xhr?
# ...2 lines...
else
# ...5 lines...
end
end
81
Tuesday, October 22, 13
def cannot_schedule_while_#...
if request.xhr?
# ...1 line...
else
# ...2 lines...
end
end

def scheduling_failed
if request.xhr?
# ...1 line...
else
# ...2 lines...
end
end

def handle_exception(e)
if request.xhr?
# ...7 lines...
else
# ...2 lines...
end
end

def do_post_success_cleanup
if request.xhr?
# DO NOTHING
else
# ...1 line...
end
end

def scheduling_succeeded
if request.xhr?
# ...2 lines...
else
# ...5 lines...
end
end
81
Tuesday, October 22, 13
class ScheduleInstallation
def scheduling_failed
if request.xhr?
render :json => {:errors#...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
class ScheduleInstallation
def scheduling_failed
if request.xhr?
render :json => {:errors#...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
class ScheduleInstallation
def scheduling_failed
if request.xhr?
render :json => {:errors#...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
class ScheduleInstallation
def scheduling_failed
if request.xhr?
render :json => {:errors#...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
class ScheduleInstallation
def scheduling_failed
if request.xhr?
render :json => {:errors#...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
class ScheduleInstallation
def scheduling_failed
if request.xhr?
render :json => {:errors#...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
Single Responsibility
Principle
(SRP for short)

83
Tuesday, October 22, 13
ScheduleInstallation

84
Tuesday, October 22, 13
ScheduleInstallation
ScheduleInstallationAnd
DoOneThingForAJAXRequestsAnd
DoSomethingElseForHTMLRequests

84
Tuesday, October 22, 13
ScheduleInstallation
ScheduleInstallation And

DoOneThingForAJAXRequests And
DoSomethingElseForHTMLRequests

85
Tuesday, October 22, 13
ScheduleInstallation
ScheduleInstallation And
DoOneThingForAJAXRequests And
DoSomethingElseForHTMLRequests

86
Tuesday, October 22, 13
Recap

87
Tuesday, October 22, 13
InstallationsController

88
Tuesday, October 22, 13
InstallationsController

ScheduleInstallation

88
Tuesday, October 22, 13
InstallationsController
???
ScheduleInstallation

88
Tuesday, October 22, 13
InstallationsController

Responder
???
ScheduleInstallation

89
Tuesday, October 22, 13
InstallationsController
???
Responder
???
ScheduleInstallation

89
Tuesday, October 22, 13
class ScheduleInstallation
def call
private
def
def
def
def
def
end

cannot_schedule_while_credit_check_pendin
handle_exception(e)
scheduling_failed
scheduling_succeeded
do_post_success_cleanup

class Responder
end

90
Tuesday, October 22, 13
class ScheduleInstallation
class ScheduleInstallat
def call
def call
end
private
class Responder
def cannot_schedule_while_credit_check_pendin
def cannot_schedule_w
def handle_exception(e) def handle_exception(
def scheduling_failed
def scheduling_failed
def scheduling_succeededdef scheduling_succee
def do_post_success_cleanup do_post_success_c
def
end
end
class Responder
end

90
Tuesday, October 22, 13
Responder

91
Tuesday, October 22, 13
if request.xhr?
# do this
else
# do that
end
Tuesday, October 22, 13
Replace Conditional
With Polymorphism

93
Tuesday, October 22, 13
class
def
def
def
def
def
end

Tuesday, October 22, 13

Responder
cannot_schedule_while_credit_check_pending
handle_exception(e)
scheduling_failed
scheduling_succeeded
do_post_success_cleanup
class
def
def
def
def
def
end

Responder
class AJAXResponder
cannot_schedule_while_credit_check_pending
def
handle_exception(e) cannot_schedule_while_cre
scheduling_failed
def handle_exception(e)
scheduling_succeeded def scheduling_failed
do_post_success_cleanup scheduling_succeede
def
def do_post_success_cle
end

class HTMLResponder
def
cannot_schedule_while_cre
def handle_exception(e)
def scheduling_failed
def scheduling_succeede
def do_post_success_cle
end
Tuesday, October 22, 13
class InstallationsController
< ActionController::Base
def schedule
responder = request.xhr?
?
AJAXResponder.new(self) :
HTMLResponder.new(self)
ScheduleInstallation.new(responder).call
end
end

Tuesday, October 22, 13
class InstallationsController
< ActionController::Base
def schedule
responder = request.xhr?
?
AJAXResponder.new(self) :
HTMLResponder.new(self)
ScheduleInstallation.new(responder).call
end
end

Tuesday, October 22, 13
class InstallationsController
< ActionController::Base
def schedule
responder = request.xhr?
?
AJAXResponder.new(self) :
HTMLResponder.new(self)
ScheduleInstallation.new(responder).call
end
end

Tuesday, October 22, 13
class AJAXResponder
def scheduling_failed
if request.xhr?
render :json => #...
else
flash[:error] = #...
redirect_to #...
end
end
end
class HTMLResponder
def scheduling_failed
if request.xhr?
render :json => #...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13
class AJAXResponder
def scheduling_failed
if request.xhr?
render :json => #...
else
flash[:error] = #...
redirect_to #...
end
end
end
class HTMLResponder
def scheduling_failed
if request.xhr?
render :json => #...
else
flash[:error] = #...
redirect_to #...
end
end
end

Tuesday, October 22, 13

class AJAXResponder
def scheduling_failed
render :json => #...
end
end
class HTMLResponder
def scheduling_failed
flash[:error] = #...
redirect_to #...
end
end
class InstallationsController
< ActionController::Base
def schedule
responder = request.xhr?
?
AJAXResponder.new(self) :
HTMLResponder.new(self)
ScheduleInstallation.new(responder).call
end
end

Tuesday, October 22, 13
Where Do We Go
From Here?

98
http://gomakemeasandwich.wordpress.com/2011/10/26/a-yeaf-of-gmmas-where-do-we-go-from-here/
Tuesday, October 22, 13
99
Tuesday, October 22, 13

http://www.poodr.info/
100
Tuesday, October 22, 13
101
Tuesday, October 22, 13
102
Tuesday, October 22, 13
103
Tuesday, October 22, 13
Commit Early,
Commit Often

104
Tuesday, October 22, 13
Throw Your Work Away

105
Tuesday, October 22, 13
Speed Up Your Tests!

106
Tuesday, October 22, 13
Speed Up Your Tests!
See also:
Katrina Owen,
“Therapeutic Refactoring”
(srsly)

106
Tuesday, October 22, 13
I work at LivingSocial.
We’re hiring.

107
Tuesday, October 22, 13
I work at LivingSocial.
We’re hiring.
(Who isn’t?)

107
Tuesday, October 22, 13
Thanks to:
Jim Shore and Diana Larsen
for “Agile Fluency”
Kirsten Comandich
Sandi Metz, Katrina Owen,
Sonia Connolly
PDX.rb
108
Tuesday, October 22, 13
github.com/geeksam
/fluent-refactoring

109
Tuesday, October 22, 13
github.com/geeksam
/fluent-refactoring
Sam Livingston-Gray
geeksam@gmail.com
Twitter, Github: @geeksam

110
Tuesday, October 22, 13
1 of 167

More Related Content

What's hot(20)

Ruby RobotsRuby Robots
Ruby Robots
Daniel Cukier2K views
Scalable vector emberScalable vector ember
Scalable vector ember
Matthew Beale5.2K views
To Err Is HumanTo Err Is Human
To Err Is Human
Alex Liu4K views
Unbreakable: The Craft of CodeUnbreakable: The Craft of Code
Unbreakable: The Craft of Code
Joe Morgan1.2K views
Taking the pain out of signing users inTaking the pain out of signing users in
Taking the pain out of signing users in
Francois Marier1.3K views
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wild
Joe Morgan1.5K views
TicketBEKA? Ticket bookingTicketBEKA? Ticket booking
TicketBEKA? Ticket booking
Likhith Pujari60 views
JavaScript & HTML5 - Brave New WorldJavaScript & HTML5 - Brave New World
JavaScript & HTML5 - Brave New World
Robert Nyman17K views
FuncUnitFuncUnit
FuncUnit
Brian Moschel2.5K views
Vidéo approche en immobilierVidéo approche en immobilier
Vidéo approche en immobilier
hervepouliot1K views
JQuery In RailsJQuery In Rails
JQuery In Rails
Louie Zhao3.1K views
Social media mashup website'sSocial media mashup website's
Social media mashup website's
Mathias Vissers578 views
jQuery quick tutsjQuery quick tuts
jQuery quick tuts
Nasa Vietnam1.4K views
PhpPhp
Php
Linh Tran537 views
JQuery in SeasideJQuery in Seaside
JQuery in Seaside
ESUG3.8K views

Viewers also liked(19)

Fluent Refactoring (Lone Star Ruby Conf 2013)Fluent Refactoring (Lone Star Ruby Conf 2013)
Fluent Refactoring (Lone Star Ruby Conf 2013)
Sam Livingston-Gray1.9K views
Cucumbers Have Layers - RubyConf 2015Cucumbers Have Layers - RubyConf 2015
Cucumbers Have Layers - RubyConf 2015
Sam Livingston-Gray2.2K views
Tratado de NeuillyTratado de Neuilly
Tratado de Neuilly
ElenaCanizares318 views
HRM Management InstituteHRM Management Institute
HRM Management Institute
Stefany Ting, CHRP276 views
JOMC slide deckJOMC slide deck
JOMC slide deck
Brenna McFadden202 views
Intake 37 ef1Intake 37 ef1
Intake 37 ef1
Mahmoud Ouf166 views
COMO PINTO ACRILICOCOMO PINTO ACRILICO
COMO PINTO ACRILICO
veris1963167 views
Information Mapping (1)Information Mapping (1)
Information Mapping (1)
Sujatha Narayanan284 views
DilasaDilasa
Dilasa
Lalit Agrawal693 views
DEFINICIÓN SOBRE EL AMORDEFINICIÓN SOBRE EL AMOR
DEFINICIÓN SOBRE EL AMOR
Alquimista Aula371 views
Affiches de la familleAffiches de la famille
Affiches de la famille
Alquimista Aula1.8K views
1.  prepare usb installer1.  prepare usb installer
1. prepare usb installer
kris harden189 views
Bmw museumBmw museum
Bmw museum
Marta Martín207 views
Bahasa Mandarin DasarBahasa Mandarin Dasar
Bahasa Mandarin Dasar
Devy Riani113 views

Similar to Fluent Refactoring (Cascadia Ruby Conf 2013)

Ruby is AwesomeRuby is Awesome
Ruby is AwesomeAstrails
2.4K views182 slides

Similar to Fluent Refactoring (Cascadia Ruby Conf 2013)(20)

Slides changes symfony23Slides changes symfony23
Slides changes symfony23
Javier López727 views
GCRC 2014 - The Dark Side of RubyGCRC 2014 - The Dark Side of Ruby
GCRC 2014 - The Dark Side of Ruby
Gautam Rege2.3K views
Ten Groovy Little JavaScript TipsTen Groovy Little JavaScript Tips
Ten Groovy Little JavaScript Tips
Troy Miles9.1K views
Ruby is AwesomeRuby is Awesome
Ruby is Awesome
Astrails2.4K views
Drools and BRMS 6.0 (Dublin Aug 2013)Drools and BRMS 6.0 (Dublin Aug 2013)
Drools and BRMS 6.0 (Dublin Aug 2013)
Mark Proctor2.3K views
CzzawkCzzawk
Czzawk
宗志 陈974 views
Dtrace и немного магииDtrace и немного магии
Dtrace и немного магии
Dan Kruchinin735 views
Teaching Programming OnlineTeaching Programming Online
Teaching Programming Online
Pamela Fox1.4K views
Beware: Sharp ToolsBeware: Sharp Tools
Beware: Sharp Tools
chrismdp415 views
Grow your own tools - VilniusRBGrow your own tools - VilniusRB
Grow your own tools - VilniusRB
Remigijus Jodelis307 views
CouchdbCouchdb
Couchdb
Brian Smith509 views
Intro to Clojure's core.asyncIntro to Clojure's core.async
Intro to Clojure's core.async
Leonardo Borges13.8K views
D3.js capita selectaD3.js capita selecta
D3.js capita selecta
Joris Klerkx1K views
An Introduction To jQueryAn Introduction To jQuery
An Introduction To jQuery
Andy Gibson517 views

Recently uploaded(20)

Fluent Refactoring (Cascadia Ruby Conf 2013)

  • 1. Fluent Refactoring Sam Livingston-Gray THERE WILL BE CODE! It may be this small puts (1..100).map { |i| s = '' fizz = (i % 3).zero? buzz = (i % 5).zero? s << 'Fizz' if fizz s << 'Buzz' if buzz s = i if s.empty? s } 1 Tuesday, October 22, 13
  • 4. 4 Tuesday, October 22, 13 http://www.wikihow.com/Image:Solve-for-X-Step-12.jpg
  • 5. 5 Tuesday, October 22, 13 http://math.about.com/od/algebra/ss/birthday.htm
  • 9. Algebra Isn’t all of Math 9 Tuesday, October 22, 13
  • 15. Math is a Language 15 Tuesday, October 22, 13
  • 16. Math is a Language Algebra is [one of] its Grammar[s] 15 Tuesday, October 22, 13
  • 19. Can I get a definition? 17 http://www.kickasslabs.com/2012/04/28/rails-conf-2012-images/100_0304/ Tuesday, October 22, 13
  • 21. Re·fac·tor·ing (noun) "...a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior." -refactoring.com 18 Tuesday, October 22, 13
  • 23. TL;DR "...a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior." 19 Tuesday, October 22, 13
  • 24. Re·fac·tor·ing (noun) A technique for restructuring code without changing behavior 20 Tuesday, October 22, 13
  • 25. Tell a clearer story with fewer details 21 Tuesday, October 22, 13
  • 26. Jargon 22 Tuesday, October 22, 13 http://www.tagxedo.com/app.html
  • 27. Jargon 22 Tuesday, October 22, 13 http://www.tagxedo.com/app.html
  • 29. Re·fac·tor·ing (noun) A language that describes ways to make your code suck less. 23 Tuesday, October 22, 13
  • 31. Flu·en·cy (noun) What you can say when you’re not thinking about how to say it 24 Tuesday, October 22, 13
  • 32. Flu·en·cy (noun) What you can say when you’re woken up in the middle of the night with a flashlight in your face 25 Tuesday, October 22, 13
  • 35. Levels of Proficiency Level 1 Level 2 Level 3 Level 4 28 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 36. Levels of Proficiency Level 1 Tarzan at a party “Beer!” “Good party.” Level 2 Level 3 Level 4 29 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 37. Levels of Proficiency Level 1 Tarzan at a party “Beer!” “Good party.” Level 2 Going to the party "Where is the party?" "How do I get to the party?" Level 3 Level 4 30 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 38. Levels of Proficiency Level 1 Tarzan at a party “Beer!” “Good party.” Level 2 Going to the party "Where is the party?" "How do I get to the party?" Level 3 Discussing the party "What happened at the party last night?" Level 4 31 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 39. Levels of Proficiency Level 1 Tarzan at a party “Beer!” “Good party.” Level 2 Going to the party "Where is the party?" "How do I get to the party?" Level 3 Discussing the party "What happened at the party last night?" Level 4 Charlie Rose "Should parties be illegal?" 32 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 40. Levels of Proficiency Level 1 Tarzan at a party “Beer!” “Good party.” Level 2 Going to the party "Where is the party?" "How do I get to the party?" Level 3 Discussing the party "What happened at the party last night?" Level 4 Charlie Rose "Should parties be illegal?" 33 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 41. Levels of Proficiency Level 1 Tarzan at a party “Beer!” “Good party.” Level 2 Going to the party "Where is the party?" "How do I get to the party?" 34 Tuesday, October 22, 13 http://www.jamesshore.com/Blog/Proficiencies-of-Planning.html
  • 44. Production Rails Code Used with: • Permission 36 Tuesday, October 22, 13
  • 45. Production Rails Code Used with: • Permission • Obfuscation 36 Tuesday, October 22, 13
  • 46. Production Rails Code Used with: • Permission • Obfuscation • Respect 36 Tuesday, October 22, 13
  • 48. class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end # lots more stuff... end Tuesday, October 22, 13 38
  • 49. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end # lots more stuff... end Tuesday, October 22, 13 39
  • 50. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file # lots more stuff... end Tuesday, October 22, 13 39
  • 51. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method # lots more stuff... end Tuesday, October 22, 13 39
  • 52. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method Longest line: 177 chars # lots more stuff... end Tuesday, October 22, 13 39
  • 53. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method Longest line: 177 chars Indentation: 4-16 spaces # lots more stuff... end Tuesday, October 22, 13 39
  • 54. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method Longest line: 177 chars Indentation: 4-16 spaces Nested control structures: # lots more stuff... end Tuesday, October 22, 13 39
  • 55. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method Longest line: 177 chars Indentation: 4-16 spaces Nested control structures: audit_trail_for # lots more stuff... end Tuesday, October 22, 13 39
  • 56. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method Longest line: 177 chars Indentation: 4-16 spaces Nested control structures: audit_trail_for begin/rescue/end # lots more stuff... end Tuesday, October 22, 13 39
  • 57. Observations class InstallationsController < ActionController::Base # lots more stuff... def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end ~800 lines in file ~50 lines in method Longest line: 177 chars Indentation: 4-16 spaces Nested control structures: audit_trail_for begin/rescue/end if/else/end # lots more stuff... end Tuesday, October 22, 13 39
  • 59. Ship it! 41 Tuesday, October 22, 13 http://shipitsquirrel.github.io/
  • 64. Make the Job Smaller 44 Tuesday, October 22, 13
  • 65. Replace Method with Method Object See also: Katrina Owen, “Therapeutic Refactoring” 45 Tuesday, October 22, 13
  • 66. class InstallationsController < ActionController::Base def schedule # LOTS OF CODE end end 46 Tuesday, October 22, 13
  • 67. class InstallationsController < ActionController::Base def schedule # LOTS OF CODE end end class ScheduleInstallation def call end end 47 Tuesday, October 22, 13
  • 68. class InstallationsController < ActionController::Base def schedule end end class ScheduleInstallation def call # LOTS OF CODE end end 47 Tuesday, October 22, 13
  • 69. class InstallationsController < ActionController::Base def schedule ScheduleInstallation.new.call end end class ScheduleInstallation def call # LOTS OF CODE end end 47 Tuesday, October 22, 13
  • 71. class def # # # # end end ScheduleInstallation call LOTS OF CODE ... params ... ... render ... ... redirect_to ... 49 Tuesday, October 22, 13
  • 72. class ScheduleInstallation def initialize(controller) @controller = controller end def # # # # end end call LOTS OF CODE ... params ... ... render ... ... redirect_to ... 50 Tuesday, October 22, 13
  • 73. class ScheduleInstallation def initialize(controller) @controller = controller end def # # # # end end call LOTS OF CODE ... @controller.params ... ... @controller.render ... ... @controller.redirect_to ... 51 Tuesday, October 22, 13
  • 74. class ScheduleInstallation def initialize(controller) @controller = controller end extend Forwardable def_delegators :@controller, :params, :render, :redirect_to def # # # # end end call LOTS OF CODE ... params ... ... render ... ... redirect_to ... 52 Tuesday, October 22, 13
  • 75. class ScheduleInstallation def initialize(controller) @controller = controller end def call # LOTS OF CODE end def method_missing(m, *a, &b) @controller.send(m, *a, &b) end end 53 Tuesday, October 22, 13
  • 77. if request.xhr? # ...20 lines... else # ...22 lines... end 55 Tuesday, October 22, 13
  • 78. if request.xml_http_request? # ...20 lines... else # ...22 lines... end 56 Tuesday, October 22, 13
  • 79. if request.xml_http_request? begin #... end else # ...22 lines... end 57 Tuesday, October 22, 13
  • 80. if request.xml_http_request? begin if @installation.pending_credit_check? render :json => #... return end #... end else # ...22 lines... end 58 Tuesday, October 22, 13
  • 81. if request.xml_http_request? begin if @installation.pending_credit_check? render :json => #... return end #... end else # ...22 lines... end 58 Tuesday, October 22, 13
  • 82. if request.xml_http_request? begin if @installation.pending_credit_check? render :json => #... return end #... Guard Clause end -Smalltalk Best Practice Patterns else by Kent Beck # ...22 lines... end 58 Tuesday, October 22, 13
  • 83. if request.xhr? begin if @installation.pending_credit_check? render :json => #... return end #... end else if @installation.pending_credit_check? flash[:error] = #... redirect_to installations_path(:city_id => end begin #... end end 59 Tuesday, October 22, 13
  • 84. tion.city_id, :view => "calendar") and return 59 Tuesday, October 22, 13
  • 85. if request.xhr? begin if @installation.pending_credit_check? render :json => #... return end #... end else if @installation.pending_credit_check? flash[:error] = #... redirect_to installations_path(:city_id => return end begin #... end 60 Tuesday, October 22, 13
  • 86. if request.xhr? if request.xhr? begin if @installation.pend if @installation.pending_credit_check? #.. render :json => render :json => #... return return end end begin #... #... end end else else if @installation.pending_credit_check? if @installation.pend flash[:error] = #... flash[:error] = #.. redirect_to installations_path(:city_id => redirect_to install return return end end begin begin #... #... end end 60 Tuesday, October 22, 13
  • 87. if request.xhr? if @installation.pending_credit_check? render :json => #... return end begin #... end else if @installation.pending_credit_check? flash[:error] = #... redirect_to installations_path(:city_id => return end begin #... end 61 Tuesday, October 22, 13
  • 88. if request.xhr? if request.xhr? if @installation.pending_credit_check? if @installation.pend render :json => #... render :json => #.. return return end end begin else #... if @installation.pend end flash[:error] = #.. else redirect_to #... if @installation.pending_credit_check? return flash[:error] = #... end redirect_to installations_path(:city_id => end return end if request.xhr? begin begin #... #... end end 61 Tuesday, October 22, 13
  • 89. if request.xhr? if @installation.pending_credit_check? render :json => #... return end else if @installation.pending_credit_check? flash[:error] = #... redirect_to #... return end end 62 Tuesday, October 22, 13
  • 91. if request.xhr? if @installation.pending_credit_check? render :json => #... return end else if @installation.pending_credit_check? flash[:error] = #... redirect_to #... return end end 64 Tuesday, October 22, 13
  • 92. if request.xhr? if @installation.pending_credit_check? render :json => #... return end else if @installation.pending_credit_check? flash[:error] = #... redirect_to #... return end end 64 Tuesday, October 22, 13
  • 93. if request.xhr? if @installation.pending_credit_check? render :json => #... return end else if @installation.pending_credit_check? flash[:error] = #... redirect_to #... return end end 64 Tuesday, October 22, 13
  • 94. Flatten Nested Conditionals source: Michael Feathers in Dr. Dobbs 65 Tuesday, October 22, 13
  • 95. if request.xhr? if @installation.pending_credit_check? render :json => #... return end else if @installation.pending_credit_check? flash[:error] = #... redirect_to #... return end end 66 Tuesday, October 22, 13
  • 96. if ajax if nope render :json => #... return end else if nope flash[:error] = #... redirect_to #... return end end 67 Tuesday, October 22, 13
  • 97. if ajax if nope render :json => #... return end else if nope flash[:error] = #... redirect_to #... return end end 67 Tuesday, October 22, 13 ajax nope TRUE TRUE ajax nope FALSE TRUE
  • 98. if ajax if nope render :json => #... return end else if nope flash[:error] = #... redirect_to #... return end end 68 Tuesday, October 22, 13
  • 99. if ajax if ajax if nope if nope render :json => #... render :json => #.. return return end end else end if nope if not ajax flash[:error] = #... if nope redirect_to #... flash[:error] = #.. return redirect_to #... end return end end end 68 Tuesday, October 22, 13
  • 100. if ajax if nope render :json => #... return end end if not ajax if nope flash[:error] = #... redirect_to #... return end end 69 Tuesday, October 22, 13
  • 101. if ajax if ajax && nope if nope render :json => #... render :json => #... return return end end if (not ajax) && nope end flash[:error] = #... if not ajax redirect_to #... if nope return flash[:error] = #...end redirect_to #... return end end 69 Tuesday, October 22, 13
  • 102. if ajax && nope render :json => #... return end if (not ajax) && nope flash[:error] = #... redirect_to #... return end 70 Tuesday, October 22, 13
  • 103. if ajax && nope render :json => #... return end if (not ajax) && nope flash[:error] = #... redirect_to #... return end 70 Tuesday, October 22, 13
  • 104. if ajax && nope render :json => #... return end if (not ajax) && nope flash[:error] = #... redirect_to #... return end if nope if ajax render :json => #.. return end if not ajax flash[:error] = #.. redirect_to #... return end end 70 Tuesday, October 22, 13
  • 105. if nope if ajax render :json => #... return end if not ajax flash[:error] = #... redirect_to #... return end end 71 Tuesday, October 22, 13
  • 106. if nope if nope if ajax if ajax render :json => #... render :json => #.. return return end else if not ajax flash[:error] = #.. flash[:error] = #... redirect_to #... redirect_to #... return return end end end end 71 Tuesday, October 22, 13
  • 107. if nope if ajax render :json => #... return else flash[:error] = #... redirect_to #... return end end 72 Tuesday, October 22, 13
  • 108. if nope if nope if ajax if ajax render :json => #... render :json => #.. return else else flash[:error] = #.. flash[:error] = #... redirect_to #... redirect_to #... end return return end end end 72 Tuesday, October 22, 13
  • 109. if request.xhr? if @installation.pending_credit_check? render :json => #... return end else if @installation.pending_credit_check? flash[:error] = #... redirect_to #... return end end 73 Tuesday, October 22, 13
  • 110. if request.xhr? if @installation.pendin if @installation.pending_credit_check? if request.xhr? render :json => #... render :json => #.. return else end flash[:error] = #.. else redirect_to #... if @installation.pending_credit_check? end flash[:error] = #... return redirect_to #... end return end end 73 Tuesday, October 22, 13
  • 111. if @installation.pending_credit_check? if request.xhr? render :json => #... else flash[:error] = #... redirect_to #... end return end 74 Tuesday, October 22, 13
  • 112. if @installation.pendin if @installation.pending_credit_check? cant_schedule_while_c if request.xhr? render :json => #... return end else flash[:error] = #... redirect_to #... end return end 74 Tuesday, October 22, 13
  • 115. class InstallationsController < ActionController::Base def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end end 77 Tuesday, October 22, 13
  • 116. class InstallationsController < ActionController::Base def schedule desired_date = params[:desired_date] if request.xhr? begin if @installation.pending_credit_check? render :json => {:errors => ["Cannot schedule installation while credit check is pending"]}, :status => 400 return end audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date date = @installation.scheduled_date.in_time_zone(@installation.city.timezone).to_date render :json => {:errors => nil, :html => schedule_response(@installation, date)} end else render :json => {:errors => [%Q{Could not update installation. #{@installation.errors.full_messages.join(' ')}}] } end end rescue ActiveRecord::RecordInvalid => e render :json => {:errors => [e.message] } rescue ArgumentError => e render :json => {:errors => ["Could not schedule installation. Start by making sure the desired date is on a business day."]} end else if @installation.pending_credit_check? flash[:error] = "Cannot schedule installation while credit check is pending" redirect_to installations_path(:city_id => @installation.city_id, :view => "calendar") and return end begin audit_trail_for(current_user) do if @installation.schedule!(desired_date, :installation_type => params[:installation_type], :city => @city) if @installation.scheduled_date if @installation.customer_provided_equipment? flash[:success] = %Q{Installation scheduled} else flash[:success] = %Q{Installation scheduled! Don't forget to order the equipment also.} end end else flash[:error] = %Q{Could not schedule installation, check the phase of the moon} end end rescue => e flash[:error] = e.message end redirect_to(@installation.customer_provided_equipment? ? customer_provided_installations_path : installations_path(:city_id => @installation.city_id, :view => "calendar")) end end end class ScheduleInstallation def call if @installation.pending_c cant_schedule_while_cred return end 77 Tuesday, October 22, 13 audit_trail_for(current_us if schedule! if @installation.sched scheduling_succeeded end do_post_success_cleanu else scheduling_failed end end rescue => e handle_exception e end end
  • 117. class ScheduleInstallation def call if @installation.pending_credit_check? cant_schedule_while_credit_check_pending return end audit_trail_for(current_user) do if schedule! if @installation.scheduled_date scheduling_succeeded end do_post_success_cleanup else scheduling_failed end end rescue => e handle_exception e end end 78 Tuesday, October 22, 13
  • 118. class ScheduleInstallation def call if @installation.pending_credit_check? cant_schedule_while_credit_check_pending return end audit_trail_for(current_user) do if schedule! if @installation.scheduled_date scheduling_succeeded end do_post_success_cleanup else scheduling_failed end end rescue => e handle_exception e end end request.xhr? 79 Tuesday, October 22, 13
  • 119. class ScheduleInstallation def call if @installation.pending_credit_check? cant_schedule_while_credit_check_pending return end audit_trail_for(current_user) do if schedule! if @installation.scheduled_date scheduling_succeeded end do_post_success_cleanup else scheduling_failed end end rescue => e handle_exception e end end request.xhr? 79 Tuesday, October 22, 13
  • 120. Under The Rug 80 Tuesday, October 22, 13
  • 121. def cannot_schedule_while_#... if request.xhr? # ...1 line... else # ...2 lines... end end def scheduling_failed if request.xhr? # ...1 line... else # ...2 lines... end end def handle_exception(e) if request.xhr? # ...7 lines... else # ...2 lines... end end def do_post_success_cleanup if request.xhr? # DO NOTHING else # ...1 line... end end def scheduling_succeeded if request.xhr? # ...2 lines... else # ...5 lines... end end 81 Tuesday, October 22, 13
  • 122. def cannot_schedule_while_#... if request.xhr? # ...1 line... else # ...2 lines... end end def scheduling_failed if request.xhr? # ...1 line... else # ...2 lines... end end def handle_exception(e) if request.xhr? # ...7 lines... else # ...2 lines... end end def do_post_success_cleanup if request.xhr? # DO NOTHING else # ...1 line... end end def scheduling_succeeded if request.xhr? # ...2 lines... else # ...5 lines... end end 81 Tuesday, October 22, 13
  • 123. class ScheduleInstallation def scheduling_failed if request.xhr? render :json => {:errors#... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 124. class ScheduleInstallation def scheduling_failed if request.xhr? render :json => {:errors#... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 125. class ScheduleInstallation def scheduling_failed if request.xhr? render :json => {:errors#... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 126. class ScheduleInstallation def scheduling_failed if request.xhr? render :json => {:errors#... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 127. class ScheduleInstallation def scheduling_failed if request.xhr? render :json => {:errors#... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 128. class ScheduleInstallation def scheduling_failed if request.xhr? render :json => {:errors#... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 129. Single Responsibility Principle (SRP for short) 83 Tuesday, October 22, 13
  • 141. class ScheduleInstallation class ScheduleInstallat def call def call end private class Responder def cannot_schedule_while_credit_check_pendin def cannot_schedule_w def handle_exception(e) def handle_exception( def scheduling_failed def scheduling_failed def scheduling_succeededdef scheduling_succee def do_post_success_cleanup do_post_success_c def end end class Responder end 90 Tuesday, October 22, 13
  • 143. if request.xhr? # do this else # do that end Tuesday, October 22, 13
  • 145. class def def def def def end Tuesday, October 22, 13 Responder cannot_schedule_while_credit_check_pending handle_exception(e) scheduling_failed scheduling_succeeded do_post_success_cleanup
  • 146. class def def def def def end Responder class AJAXResponder cannot_schedule_while_credit_check_pending def handle_exception(e) cannot_schedule_while_cre scheduling_failed def handle_exception(e) scheduling_succeeded def scheduling_failed do_post_success_cleanup scheduling_succeede def def do_post_success_cle end class HTMLResponder def cannot_schedule_while_cre def handle_exception(e) def scheduling_failed def scheduling_succeede def do_post_success_cle end Tuesday, October 22, 13
  • 147. class InstallationsController < ActionController::Base def schedule responder = request.xhr? ? AJAXResponder.new(self) : HTMLResponder.new(self) ScheduleInstallation.new(responder).call end end Tuesday, October 22, 13
  • 148. class InstallationsController < ActionController::Base def schedule responder = request.xhr? ? AJAXResponder.new(self) : HTMLResponder.new(self) ScheduleInstallation.new(responder).call end end Tuesday, October 22, 13
  • 149. class InstallationsController < ActionController::Base def schedule responder = request.xhr? ? AJAXResponder.new(self) : HTMLResponder.new(self) ScheduleInstallation.new(responder).call end end Tuesday, October 22, 13
  • 150. class AJAXResponder def scheduling_failed if request.xhr? render :json => #... else flash[:error] = #... redirect_to #... end end end class HTMLResponder def scheduling_failed if request.xhr? render :json => #... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13
  • 151. class AJAXResponder def scheduling_failed if request.xhr? render :json => #... else flash[:error] = #... redirect_to #... end end end class HTMLResponder def scheduling_failed if request.xhr? render :json => #... else flash[:error] = #... redirect_to #... end end end Tuesday, October 22, 13 class AJAXResponder def scheduling_failed render :json => #... end end class HTMLResponder def scheduling_failed flash[:error] = #... redirect_to #... end end
  • 152. class InstallationsController < ActionController::Base def schedule responder = request.xhr? ? AJAXResponder.new(self) : HTMLResponder.new(self) ScheduleInstallation.new(responder).call end end Tuesday, October 22, 13
  • 153. Where Do We Go From Here? 98 http://gomakemeasandwich.wordpress.com/2011/10/26/a-yeaf-of-gmmas-where-do-we-go-from-here/ Tuesday, October 22, 13
  • 154. 99 Tuesday, October 22, 13 http://www.poodr.info/
  • 160. Throw Your Work Away 105 Tuesday, October 22, 13
  • 161. Speed Up Your Tests! 106 Tuesday, October 22, 13
  • 162. Speed Up Your Tests! See also: Katrina Owen, “Therapeutic Refactoring” (srsly) 106 Tuesday, October 22, 13
  • 163. I work at LivingSocial. We’re hiring. 107 Tuesday, October 22, 13
  • 164. I work at LivingSocial. We’re hiring. (Who isn’t?) 107 Tuesday, October 22, 13
  • 165. Thanks to: Jim Shore and Diana Larsen for “Agile Fluency” Kirsten Comandich Sandi Metz, Katrina Owen, Sonia Connolly PDX.rb 108 Tuesday, October 22, 13