Readablity is an important aspect of creating code that you or others will need to understand later. Because I often need to communicate purpose to other developers, I opt to avoid using
if/else blocks of code, particularly in view templates.
For example, some users of a system might have certain details available to them based upon their role or some particular bit of data. Here's how I simplified my views using presenters.
Where you might have something like this in your views
<%= if profile.has_experience? && profile.experience_public? %>
Experience: <%= user_profile.experience %><% end %>
There is a better way...
Tell objects to execute blocks, let them decide if they really should
I want my views to handle conditional options, but I want the conditions to be clearer than querying for values.
<%= user_profile.full_name %><%= user_profile.bio_html %> <% user_profile.with_experience do %>
Experience: <%= user_profile.experience %><% end %> <% user_profile.with_hobbies do %>
Hobbies: <%= user_profile.hobbies %><% end %>
Here I specify what I want to display when the user profile has details for experience or details for hobbies. The implementation is simple:
class ProfilePresenter < ::Presenter def with_experience(&block) if profile.has_experience? && profile.experience_public? block.call(view) end end end
The sample code is contrived, but what this allows me to do is move the details of what having experience means. Presently the code checks if the profile
This could easily be moved into a separate method for use in the view:
class ProfilePresenter < ::Presenter def has_public_experience? profile.has_experience? && profile.experience_public? end end
The problem with a method like this is that 1) its name is dependent upon the implementation and 2) its intent is different from the intent used in the view.
Make your code imply responsibilites
First, the name merely combines two values. What will happen when requirements change and we need to ensure that the provided experience includes at least 3 years of activity? The meaning of
has_public_experience? will change along with its behavior and lead to surprises for developers unfamiliar with these particular details. It will no longer merely be existince of experience allowed for the pubilc.
This leads us to the second problem: the intent of the method is to display features, not query for values. Were we to stick with a query method like
has_public_experience? we would end up considering the content inside the view along with the meaning of the method every time we read this code.
By creating a method which accepts a block, our view template implies that the responsibility for determining display is elsewhere: in the presenter. The view will display what is configured, but determining that is upto the object we're showing. Leaving query methods lying around in your views is just asking for the next developer to change the view code to
profile.has_public_experience? && profile.has_3_years_experience?, and then the value of your presenter is lost.
with_experience helped us force developers to consider where changes should be made. How do you prepare your code for change? How does your team ensure that resposibilities are well-managed?
Readers of my Clean Ruby newsletter get regular tips like this. Sign-up below.