Fluent Refactoring (Cascadia Ruby Conf 2013)

931 views

Published on

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
931
On SlideShare
0
From Embeds
0
Number of Embeds
22
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Fluent Refactoring (Cascadia Ruby Conf 2013)

  1. 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
  2. 2. Math m/ 2 Tuesday, October 22, 13
  3. 3. 3 http://2012books.lardbucket.org/books/elementary-algebra/section_06/5d10b670d78abac93a4572dc0c2afb0f.jpg Tuesday, October 22, 13
  4. 4. 4 Tuesday, October 22, 13 http://www.wikihow.com/Image:Solve-for-X-Step-12.jpg
  5. 5. 5 Tuesday, October 22, 13 http://math.about.com/od/algebra/ss/birthday.htm
  6. 6. 6 http://www.smosh.com/smosh-pit/photos/16-wonderfully-stupid-test-answers Tuesday, October 22, 13
  7. 7. Algebra 7 Tuesday, October 22, 13
  8. 8. Algebra Isn’t Math 8 Tuesday, October 22, 13
  9. 9. Algebra Isn’t all of Math 9 Tuesday, October 22, 13
  10. 10. Algebra ⊂ Math Algebra Math 10 Tuesday, October 22, 13
  11. 11. 11 http://upload.wikimedia.org/wikipedia/commons/thumb/0/08/NautilusCutawayLogarithmicSpiral.jpg/793px-NautilusCutawayLogarithmicSpiral.jpg Tuesday, October 22, 13
  12. 12. 12 http://upload.wikimedia.org/wikipedia/commons/a/a4/Mandelbrot_sequence_new.gif Tuesday, October 22, 13
  13. 13. 13 http://mathequalslove.blogspot.com/2012/12/hexaflexagon-love.html Tuesday, October 22, 13
  14. 14. 14 http://think-like-a-git.net/sections/graph-theory/seven-bridges-of-konigsberg.html Tuesday, October 22, 13
  15. 15. Math is a Language 15 Tuesday, October 22, 13
  16. 16. Math is a Language Algebra is [one of] its Grammar[s] 15 Tuesday, October 22, 13
  17. 17. Fluent Refactoring 16 Tuesday, October 22, 13
  18. 18. 17 http://www.kickasslabs.com/2012/04/28/rails-conf-2012-images/100_0304/ Tuesday, October 22, 13
  19. 19. Can I get a definition? 17 http://www.kickasslabs.com/2012/04/28/rails-conf-2012-images/100_0304/ Tuesday, October 22, 13
  20. 20. Re·fac·tor·ing (noun) 18 Tuesday, October 22, 13
  21. 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
  22. 22. TL;DR 19 Tuesday, October 22, 13
  23. 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. 24. Re·fac·tor·ing (noun) A technique for restructuring code without changing behavior 20 Tuesday, October 22, 13
  25. 25. Tell a clearer story with fewer details 21 Tuesday, October 22, 13
  26. 26. Jargon 22 Tuesday, October 22, 13 http://www.tagxedo.com/app.html
  27. 27. Jargon 22 Tuesday, October 22, 13 http://www.tagxedo.com/app.html
  28. 28. Re·fac·tor·ing (noun) 23 Tuesday, October 22, 13
  29. 29. Re·fac·tor·ing (noun) A language that describes ways to make your code suck less. 23 Tuesday, October 22, 13
  30. 30. Flu·en·cy (noun) 24 Tuesday, October 22, 13
  31. 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. 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
  33. 33. Stress 26 http://dailyawesimity.files.wordpress.com/2013/01/cat-stress-relief-4.jpg Tuesday, October 22, 13
  34. 34. Language Hunters 27 Tuesday, October 22, 13
  35. 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. 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. 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. 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. 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. 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. 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
  42. 42. Story Time 35 Tuesday, October 22, 13
  43. 43. Production Rails Code 36 Tuesday, October 22, 13
  44. 44. Production Rails Code Used with: • Permission 36 Tuesday, October 22, 13
  45. 45. Production Rails Code Used with: • Permission • Obfuscation 36 Tuesday, October 22, 13
  46. 46. Production Rails Code Used with: • Permission • Obfuscation • Respect 36 Tuesday, October 22, 13
  47. 47. Respect 37 Tuesday, October 22, 13
  48. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
  58. 58. Complexity 40 http://scientopia.org/blogs/whitecoatunderground/2009/06/10/my-head-just-asploded-twice/ Tuesday, October 22, 13
  59. 59. Ship it! 41 Tuesday, October 22, 13 http://shipitsquirrel.github.io/
  60. 60. ~800 lines 42 Tuesday, October 22, 13
  61. 61. ~800 lines 42 http://hyperboleandahalf.blogspot.com/2010/06/this-is-why-ill-never-be-adult.html Tuesday, October 22, 13
  62. 62. ~800 lines 42 http://hyperboleandahalf.blogspot.com/2010/06/this-is-why-ill-never-be-adult.html Tuesday, October 22, 13
  63. 63. ~800 lines 43 http://hyperboleandahalf.blogspot.com/2010/06/this-is-why-ill-never-be-adult.html Tuesday, October 22, 13
  64. 64. Make the Job Smaller 44 Tuesday, October 22, 13
  65. 65. Replace Method with Method Object See also: Katrina Owen, “Therapeutic Refactoring” 45 Tuesday, October 22, 13
  66. 66. class InstallationsController < ActionController::Base def schedule # LOTS OF CODE end end 46 Tuesday, October 22, 13
  67. 67. class InstallationsController < ActionController::Base def schedule # LOTS OF CODE end end class ScheduleInstallation def call end end 47 Tuesday, October 22, 13
  68. 68. class InstallationsController < ActionController::Base def schedule end end class ScheduleInstallation def call # LOTS OF CODE end end 47 Tuesday, October 22, 13
  69. 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
  70. 70. NoMethodError LOL WUT? 48 Tuesday, October 22, 13
  71. 71. class def # # # # end end ScheduleInstallation call LOTS OF CODE ... params ... ... render ... ... redirect_to ... 49 Tuesday, October 22, 13
  72. 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. 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. 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. 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
  76. 76. Code Archaeology 54 http://anthro.ucsc.edu/undergraduate/sub-fields/anthro-archeaology.html Tuesday, October 22, 13
  77. 77. if request.xhr? # ...20 lines... else # ...22 lines... end 55 Tuesday, October 22, 13
  78. 78. if request.xml_http_request? # ...20 lines... else # ...22 lines... end 56 Tuesday, October 22, 13
  79. 79. if request.xml_http_request? begin #... end else # ...22 lines... end 57 Tuesday, October 22, 13
  80. 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. 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. 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. 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. 84. tion.city_id, :view => "calendar") and return 59 Tuesday, October 22, 13
  85. 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. 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. 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. 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. 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
  90. 90. emph·AS·is 63 Tuesday, October 22, 13
  91. 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. 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. 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. 94. Flatten Nested Conditionals source: Michael Feathers in Dr. Dobbs 65 Tuesday, October 22, 13
  95. 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. 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. 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. 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. 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. 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. 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. 102. if ajax && nope render :json => #... return end if (not ajax) && nope flash[:error] = #... redirect_to #... return end 70 Tuesday, October 22, 13
  103. 103. if ajax && nope render :json => #... return end if (not ajax) && nope flash[:error] = #... redirect_to #... return end 70 Tuesday, October 22, 13
  104. 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. 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. 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. 107. if nope if ajax render :json => #... return else flash[:error] = #... redirect_to #... return end end 72 Tuesday, October 22, 13
  108. 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. 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. 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. 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. 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
  113. 113. 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
  114. 114. “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
  115. 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. 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. 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. 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. 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. 120. Under The Rug 80 Tuesday, October 22, 13
  121. 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. 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. 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. 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. 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. 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. 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. 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. 129. Single Responsibility Principle (SRP for short) 83 Tuesday, October 22, 13
  130. 130. ScheduleInstallation 84 Tuesday, October 22, 13
  131. 131. ScheduleInstallation ScheduleInstallationAnd DoOneThingForAJAXRequestsAnd DoSomethingElseForHTMLRequests 84 Tuesday, October 22, 13
  132. 132. ScheduleInstallation ScheduleInstallation And DoOneThingForAJAXRequests And DoSomethingElseForHTMLRequests 85 Tuesday, October 22, 13
  133. 133. ScheduleInstallation ScheduleInstallation And DoOneThingForAJAXRequests And DoSomethingElseForHTMLRequests 86 Tuesday, October 22, 13
  134. 134. Recap 87 Tuesday, October 22, 13
  135. 135. InstallationsController 88 Tuesday, October 22, 13
  136. 136. InstallationsController ScheduleInstallation 88 Tuesday, October 22, 13
  137. 137. InstallationsController ??? ScheduleInstallation 88 Tuesday, October 22, 13
  138. 138. InstallationsController Responder ??? ScheduleInstallation 89 Tuesday, October 22, 13
  139. 139. InstallationsController ??? Responder ??? ScheduleInstallation 89 Tuesday, October 22, 13
  140. 140. 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
  141. 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
  142. 142. Responder 91 Tuesday, October 22, 13
  143. 143. if request.xhr? # do this else # do that end Tuesday, October 22, 13
  144. 144. Replace Conditional With Polymorphism 93 Tuesday, October 22, 13
  145. 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. 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. 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. 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. 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. 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. 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. 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. 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. 154. 99 Tuesday, October 22, 13 http://www.poodr.info/
  155. 155. 100 Tuesday, October 22, 13
  156. 156. 101 Tuesday, October 22, 13
  157. 157. 102 Tuesday, October 22, 13
  158. 158. 103 Tuesday, October 22, 13
  159. 159. Commit Early, Commit Often 104 Tuesday, October 22, 13
  160. 160. Throw Your Work Away 105 Tuesday, October 22, 13
  161. 161. Speed Up Your Tests! 106 Tuesday, October 22, 13
  162. 162. Speed Up Your Tests! See also: Katrina Owen, “Therapeutic Refactoring” (srsly) 106 Tuesday, October 22, 13
  163. 163. I work at LivingSocial. We’re hiring. 107 Tuesday, October 22, 13
  164. 164. I work at LivingSocial. We’re hiring. (Who isn’t?) 107 Tuesday, October 22, 13
  165. 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
  166. 166. github.com/geeksam /fluent-refactoring 109 Tuesday, October 22, 13
  167. 167. github.com/geeksam /fluent-refactoring Sam Livingston-Gray geeksam@gmail.com Twitter, Github: @geeksam 110 Tuesday, October 22, 13

×