Independent Study :: Refactoring Redmine, Part 3
This week (or, rather, last week) I finished Refactoring: Ruby Edition by Jay Fields and performed some more refactorings on Redmine. I discovered that the second half of the book had more value than the first and that even good code can be improved significantly.
Refactoring: Ruby Edition
After reading the first half of the book, I stated that the book didn't provide too much value beyond Fowler's original. After reading the second half, I would disagree with that assessment. The catalog continually introduced several alternative ways to do something in Ruby that you wouldn't be able to do in Java (the language of the original book).
These alternative methods are actually very powerful when put into practice. After playing with the refactorings, I believe that they give a Ruby developer better tools to refactor and could, possibly, give the Ruby developer the power to create better abstractions that allow for better code.
Some of my favorite refactorings given in the catalog include those that use the Ruby block syntax. For example, the following code
def total
result = 0
items.each do |item|
result += item.value
end
result
end
can be refactored into
def total
items.inject(0) { |sum, item| sum + item.value }
end
and I absolutely love that. As long as you know what inject means (and, while that's a bad name, there doesn't seem to be a better one), this code makes perfect sense, is shorter and very DRY. I like all three of those. In Java, to do this you would need to create a convoluted anonymous class that wouldn't necessarily improve the code.
I would recommend this book to any developer that is writing serious Ruby code, perhaps even experienced ruby developers. While experienced developers may be familiar with the Rubyisms, this book describes a methodical, structured way to introduce those Rubyisms during refactoring. For people new to Ruby, this is a must; and, for people that practice TDD, this book should be attached to your hip.
Refactoring Redmine, Part 3
This was my last encounter with Redmine and I came away happy with my work. While Redmine has some very good, solid code throughout, there were always areas that could be refactored. This particular area was a very small class file that likely was never revisited after it was written.
Step 1
Extract Methods
http://github.com/hammerdr/redmine/commit/08fb53b9ade8f021b24b1d5fa1f70627048c0970 http://github.com/hammerdr/redmine/commit/fb0cca25319efac87d3408bd45565a1eb2e674b5 http://github.com/hammerdr/redmine/commit/ef921d57bd4228313eeacb284f16978247eea95e http://github.com/hammerdr/redmine/commit/94a8bef751e41bc8e624a0f18cfd6f4d28b5635f http://github.com/hammerdr/redmine/commit/1cea1d0f50ffb65b07628198f7c38e114588630b
Step 2
Introduce Guard
http://github.com/hammerdr/redmine/commit/aebd019fa59d9abd23f6b3b964e45822b9ba2d70
Step 3
Extract Methods
http://github.com/hammerdr/redmine/commit/160d0a0392be269f7a2e043f9aea04470e09e68c http://github.com/hammerdr/redmine/commit/9c6a2c7c449695b9dcc64a0a6d2874c8ebc614a6
Step 4
Introduce Guard
http://github.com/hammerdr/redmine/commit/f5e8311724fa6e708622dfbf1a6250c5582f315a
Step 5
Rename methods
http://github.com/hammerdr/redmine/commit/51805b7d4356ad3ba150d5c3ca2240d4893f5fb1 http://github.com/hammerdr/redmine/commit/84d5285348343425910d9a67e3b8330207c8ae48
Step 6
Extract Methods
http://github.com/hammerdr/redmine/commit/e04e5d5eaeeb4020f02d0a3e7be844218eec7b3c http://github.com/hammerdr/redmine/commit/e04e5d5eaeeb4020f02d0a3e7be844218eec7b3c http://github.com/hammerdr/redmine/commit/7c4c6aa5487c406589675c7d6c64b5c6b4518771 http://github.com/hammerdr/redmine/commit/cbc0603eb08578093c6b941446d2a195ef8bd333
Step 7
Introduce Default Parameter
http://github.com/hammerdr/redmine/commit/31a6ab321640386d15c8c86b8ab6da7a0223a764
Step 8
http://github.com/hammerdr/redmine/commit/62ac466835c3f5b6820e7c52a7805a54717a294d http://github.com/hammerdr/redmine/commit/526d5fed1aa5b2f5319ee43d72d5054ff21d65e3 http://github.com/hammerdr/redmine/commit/b8ccc9807b9f5a9a6f17afc3351558566a6af275
Step 9
Rename Method
http://github.com/hammerdr/redmine/commit/6fe721d81fc0c1f1a07bde2bf56c8abb96d818ec
Step 10
Decomposing if block to guard clauses. I think that this is a great refactor for the arrow head anti-pattern. Quick and easy to get rid of.
http://github.com/hammerdr/redmine/commit/11b7b259c5039ee12a93dbad96d5f1b175efc19b
Step 11
Extract Conditional To Method
http://github.com/hammerdr/redmine/commit/563025817503459e98032596a9d4dcdad0d0963e
Step 12
Consolidating Conditional. I think that this is a really cool, effective refactor. It is obviously a mix of Extract(/Inline) Method and Consolidate(/Break Up) Conditional, but its effectiveness is more than the sum.
http://github.com/hammerdr/redmine/commit/5d755cee63561836a0e711b5d48540532daaa306
Step 13
Rename Method
http://github.com/hammerdr/redmine/commit/fe29e2c88b13125ccad69b968a38ce7c962d1550
Analysis of Redmine Refactor
I felt that this was by far my best refactor to date. The code that I refactored was not terrible. I could read the code without much struggle. However, because I applied the refactoring tool, I think that the code turned out orders of magnitude better. I could now quickly glance over the code and understand what was going on.
About Derek Hammer
Derek Hammer writes code for a living. He loves to travel, share a good beer and try new things. He currently lives in Chicago, Illinois and works for ThoughtWorks.