Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
REFACTORING WORKSHOP 
Bruce Li @ Rails Pacific 2014 
Please clone this repo 
https://github.com/ascendbruce/refactoring-wo...
ABOUT ME 
• Li, Po-Chun a.k.a. Bruce Li 
• Work at Techbang 
• http://ascendbruce.logdown.com/ 
• @BruceToyRoom, @techbang...
REFACTORING 
is important 
• Reduce maintenance cost 
• Technical debt is not about technology. It’s about 
people 
• prog...
TEST COVERAGE 
is important 
• Confident in changes 
• Code School, TeaLeaf are good start 
• SimpleCov and CodeClimate ca...
PRY 
is useful 
• gem "pry-rails" 
gem "pry-byebug" # or pry-debugger for ruby 1.9 
• binding.pry # to entering debug mode...
COMMENTS IN CODE 
is bad (in some cases) 
• Ideally, code should describe itself. 
• Out-dated comments are worser than no...
1. INTENTION REVEALING 
METHOD 
• Add comments if code need it. 
• Transform comments into methods. 
• Comments are now co...
user_is_admitted?
user_is_admitted?
user_is_admitted? 
set_marketing_flash
user_is_admitted? 
set_marketing_flash
2. MOVE MODEL LOGIC INTO 
THE MODEL 
• Move the logic, which belongs to model, into the 
model 
• instance variable should...
class ProjectsController 
def new 
# @#$^$%&#%&*#$%& 
@project = Project.new 
! 
! 
@project.do_ooxx 
@project.xxoo = "xxo...
class ProjectsController 
def new 
# @#$^$%&#%&*#$%& 
@project = Project.new 
@project.do_something 
! 
! 
# &*%^&@#$! 
en...
class ProjectsController 
def new 
# @#$^$%&#%&*#$%& 
@project = Project.new 
@project.do_something 
! 
! 
# &*%^&@#$! 
en...
class ProjectsController 
def new 
# @#$^$%&#%&*#$%& 
@project = Project.new 
@project.do_something 
do_ooxx 
self.xxoo = ...
3. REPLACE METHOD WITH 
METHOD OBJECT 
• Create a class with same initialization arguments as 
BIG method 
• Copy & Paste ...
class OriginMethods 
def a_fat_method 
! 
! 
! 
! 
end 
end 
# $%@ + ($@# / ^%) 
# && ^ %& * #%%^ 
# wtf 
# and 100 lines....
class NewMethods 
def perform 
end 
end 
class OriginMethods 
def a_fat_method 
! 
! 
! 
! 
end 
end 
# $%@ + ($@# / ^%) 
...
class NewMethods 
def perform 
! 
! 
! 
! 
end 
end 
# $%@ + ($@# / ^%) 
# && ^ %& * #%%^ 
# wtf 
# and 100 lines... 
clas...
class NewMethods 
def perform 
! 
! 
! 
! 
end 
end 
# $%@ + ($@# / ^%) 
# && ^ %& * #%%^ 
# wtf 
# and 100 lines... 
clas...
class NewMethods 
! 
! 
! 
! 
def perform 
File.open(??????????) 
# ... 
end 
end 
! 
class OriginMethods 
def a_fat_metho...
class NewMethods 
! 
! 
! 
! 
def perform 
File.open( ) 
# ... 
end 
end 
! 
class OriginMethods 
def a_fat_method(file_na...
class NewMethods 
! 
! 
! 
! 
def perform 
File.open( ) 
# ... 
end 
end 
! 
class OriginMethods 
def a_fat_method(file_na...
class NewMethods 
! 
! 
! 
! 
def perform 
def initialize(file_name) 
@file_name = file_name 
end 
File.open( ) 
# ... 
en...
class NewMethods 
! 
! 
! 
! 
def perform 
def initialize(file_name) 
@file_name = file_name 
end 
@file_name 
File.open( ...
4. SERVICE OBJECT 
• If we add new functionality to an object and: 
• It couples to a new dependency 
• It loses cohesion ...
4. SERVICE OBJECT 
• If it's a domain concept, it's an Object. If it's only 
an algorithm (no state) we call it a Service....
5. FORM OBJECT 
• accepts_nested_attributes_for is hard to track 
• Making very different forms for the same object is 
a ...
5. FORM OBJECT 
• include ActiveModel::Model 
• Set attr_reader for related models 
• Set attr_accessor for accepted field...
REFERENCE 
• Crisp's Blog - Good and Bad Technical Debt 
• RailsCasts - Form Objects 
• 7 Patterns to Refactor Fat ActiveR...
Refactoring Workshop (Rails Pacific 2014)
Refactoring Workshop (Rails Pacific 2014)
Refactoring Workshop (Rails Pacific 2014)
Upcoming SlideShare
Loading in …5
×

Refactoring Workshop (Rails Pacific 2014)

2,723 views

Published on

Refactoring Workshop (Rails Pacific 2014)

Published in: Software
  • Be the first to comment

Refactoring Workshop (Rails Pacific 2014)

  1. 1. REFACTORING WORKSHOP Bruce Li @ Rails Pacific 2014 Please clone this repo https://github.com/ascendbruce/refactoring-workshop run bundle install and Check your ruby version
  2. 2. ABOUT ME • Li, Po-Chun a.k.a. Bruce Li • Work at Techbang • http://ascendbruce.logdown.com/ • @BruceToyRoom, @techbangtech
  3. 3. REFACTORING is important • Reduce maintenance cost • Technical debt is not about technology. It’s about people • programmer happiness is very important
  4. 4. TEST COVERAGE is important • Confident in changes • Code School, TeaLeaf are good start • SimpleCov and CodeClimate can make you happier while repairing test cases
  5. 5. PRY is useful • gem "pry-rails" gem "pry-byebug" # or pry-debugger for ruby 1.9 • binding.pry # to entering debug mode • Commands: next, step, break, continue, exit • It’s all you need at first
  6. 6. COMMENTS IN CODE is bad (in some cases) • Ideally, code should describe itself. • Out-dated comments are worser than none. It’s actively misleading. • Comments is helpful for providing additional information or link to issue tracking history.
  7. 7. 1. INTENTION REVEALING METHOD • Add comments if code need it. • Transform comments into methods. • Comments are now code. Code describes itself.
  8. 8. user_is_admitted?
  9. 9. user_is_admitted?
  10. 10. user_is_admitted? set_marketing_flash
  11. 11. user_is_admitted? set_marketing_flash
  12. 12. 2. MOVE MODEL LOGIC INTO THE MODEL • Move the logic, which belongs to model, into the model • instance variable should change to self in model
  13. 13. class ProjectsController def new # @#$^$%&#%&*#$%& @project = Project.new ! ! @project.do_ooxx @project.xxoo = "xxoo" # &*%^&@#$! end end class Project ! ! ! ! end def do_something ! ! end
  14. 14. class ProjectsController def new # @#$^$%&#%&*#$%& @project = Project.new @project.do_something ! ! # &*%^&@#$! end end class Project ! ! ! ! end def do_something ! ! end @project.do_ooxx @project.xxoo = "xxoo"
  15. 15. class ProjectsController def new # @#$^$%&#%&*#$%& @project = Project.new @project.do_something ! ! # &*%^&@#$! end end class Project ! ! ! ! end def do_something ! ! end @project.do_ooxx @project.xxoo = "xxoo"
  16. 16. class ProjectsController def new # @#$^$%&#%&*#$%& @project = Project.new @project.do_something do_ooxx self.xxoo = "xxoo" ! ! # &*%^&@#$! end end class Project ! ! ! ! end def do_something ! ! end
  17. 17. 3. REPLACE METHOD WITH METHOD OBJECT • Create a class with same initialization arguments as BIG method • Copy & Paste the method's body in the new class, with no arguments • Replace original method with a call to the new class • Apply "Intention Revealing Method" to the class
  18. 18. class OriginMethods def a_fat_method ! ! ! ! end end # $%@ + ($@# / ^%) # && ^ %& * #%%^ # wtf # and 100 lines...
  19. 19. class NewMethods def perform end end class OriginMethods def a_fat_method ! ! ! ! end end # $%@ + ($@# / ^%) # && ^ %& * #%%^ # wtf # and 100 lines...
  20. 20. class NewMethods def perform ! ! ! ! end end # $%@ + ($@# / ^%) # && ^ %& * #%%^ # wtf # and 100 lines... class OriginMethods def a_fat_method ! ! ! ! end end
  21. 21. class NewMethods def perform ! ! ! ! end end # $%@ + ($@# / ^%) # && ^ %& * #%%^ # wtf # and 100 lines... class OriginMethods def a_fat_method ! ! ! ! end end NewMethods.new.perform
  22. 22. class NewMethods ! ! ! ! def perform File.open(??????????) # ... end end ! class OriginMethods def a_fat_method(file_name) NewMethods.new.perform end end ?
  23. 23. class NewMethods ! ! ! ! def perform File.open( ) # ... end end ! class OriginMethods def a_fat_method(file_name) NewMethods.new end end
  24. 24. class NewMethods ! ! ! ! def perform File.open( ) # ... end end ! class OriginMethods def a_fat_method(file_name) NewMethods.new end end (file_name).perform
  25. 25. class NewMethods ! ! ! ! def perform def initialize(file_name) @file_name = file_name end File.open( ) # ... end end ! class OriginMethods def a_fat_method(file_name) NewMethods.new end end (file_name).perform
  26. 26. class NewMethods ! ! ! ! def perform def initialize(file_name) @file_name = file_name end @file_name File.open( ) # ... end end ! class OriginMethods def a_fat_method(file_name) NewMethods.new end end (file_name).perform
  27. 27. 4. SERVICE OBJECT • If we add new functionality to an object and: • It couples to a new dependency • It loses cohesion • Testing gets harder and slower
  28. 28. 4. SERVICE OBJECT • If it's a domain concept, it's an Object. If it's only an algorithm (no state) we call it a Service. • Delegate to the Service Object
  29. 29. 5. FORM OBJECT • accepts_nested_attributes_for is hard to track • Making very different forms for the same object is a pain. The model is messed up
  30. 30. 5. FORM OBJECT • include ActiveModel::Model • Set attr_reader for related models • Set attr_accessor for accepted fields • Add validators (ActiveModel::Model will take care of validation and errors) • Define persisted? method (false for create, true for update form) • Add initialize, save, update etc… if needed
  31. 31. REFERENCE • Crisp's Blog - Good and Bad Technical Debt • RailsCasts - Form Objects • 7 Patterns to Refactor Fat ActiveRecord Models

×