Ruby Tips from Ben Orenstein
Several weeks ago, I posted a blog post about Sandi Metz's Rules and shout out for writing short code.
I didn't provide great examples for that article but I found this perfect example from Ben Orenstein's new screencast: A Grab-Bag of Ruby/Rails Tips.
In this screencast, he steps through a refactoring process that makes a code shorter (or more abstracted).
The original code
class Post def display_on_featured_page! if created_at < 10.days.ago && user.has_confirmed_email? update(featured: true) end end end
- It's not clear enough at the first glance what are
created_at < 10.days.ago
anduser.has_confirmed_email?
doing - So Ben extracted them into private methods and give them better names.
- It's not clear enough at the first glance what are
Extract conditional statement to private methods
class Post def display_on_featured_page! if good_candidate_for_featuring? update(featured: true) end end def good_candidate_for_featuring? not_too_old? && low_spam_risk? end private def not_too_old? created_at < 10.days.ago end def low_spam_risk? user.has_confirmed_email? end end
- After this refactoring, this code's abstraction level is higher and the code itself becomes more clear
- But Ben didn't stop at here, he continued discussing about what if we need to add another condition into this code: we need to add a Policy object handle this
Policy Object
class Post def display_on_featured_page! if good_candidate_for_featuring? update(featured: true) end end def good_candidate_for_featuring? FeatureablePostPolicy.new(self).good_candidate? end end class FeatureablePostPolicy def initiatives(post) @post = post end def good_candidate? not_too_old? && low_spam_risk? # && ... end private def not_too_old? @post.created_at < 10.days.ago end def low_spam_risk? @post.user.has_confirmed_email? end # ... end
- When the logic becomes more and more complicated, it's definitely
necessary to move them into a separated object (Like
FeatureablePostPolicy
)
- When the logic becomes more and more complicated, it's definitely
necessary to move them into a separated object (Like
This example shows a perfect path for our code logic to grow:
- At first, we write logic directly into our (public) method to pass the tests
- Then, we extract some (private) methods as helpers to support the original method (with the help of existing tests)
- At last, we extract these (private) methods into a separated new object
In this screencast, he not only talked about this example, but also many other pretty useful tips for Ruby/Rails development.
And this screencast is actually a part of his new course Refactoring Rails. In this course, he will explain more great ways to refactor your Rails application.
So if you are interested, definitely check them out.