Recent articles from Progress Updates:
- The only daily stand up meeting template you need
- The only two questions you need to ask in your daily standup scrum
- The only daily standup meeting agenda you will ever need
- How to Create a Weekly Report With Templates and Examples
- How to automate your end of week status reports
One of the benefits of programming in ruby is that it is easy to write short methods. This is helpful as it makes it easier to test different methods in isolation. Also, most importantly it's easier to read the class when the class is constructed of small methods. Often, when I get back to a program after a while, I may not have enough context, and writing short, focussed methods helps with this.
For example, in one of my Shopify apps, I let users add multiple filters to a workflow.
For example, when an order is created, I need to check the order with workflows conditions, and then add tags accordingly, if the check passes. For each of the conditions, I also need to check if the user added AND (all conditions must match), or OR (any condition must match), and then make a call to the Shopify API to get the data.
I like using service objects for handling the business logic for the app. The service object below is the one that checks if the workflow conditions passes or not.
class CheckWorkflowConditions
attr_accessor :shop
def initialize(shop:, workflow:, order_id:)
@shop = shop
@workflow = workflow
@order_id = order_id
@filter_check_results = []
end
def passes?
return false if no_filters?
return atleast_one_filter_passes? if any_conditions_filter?
return all_filters_passes? if all_conditions_filter?
end
private
def no_filters?
@workflow.filters.empty?
end
def any_conditions_filter?
!all_conditions_filter?
end
def all_conditions_filter?
@workflow.all_conditions
end
def atleast_one_filter_passes?
@workflow.filters.each do |filter|
perform_filter_check(filter)
break if includes_passed_filter?
end
includes_passed_filter?
end
def all_filters_passes?
@workflow.filters.each do |filter|
perform_filter_check(filter)
break if includes_failed_filter?
end
!includes_failed_filter?
end
def perform_filter_check(filter)
@filter_check_results << CheckOrderWithFilter.new(shop: shop, order_id: @order_id, filter: filter).get_result
end
def includes_passed_filter?
@filter_check_results.include?(true)
end
def includes_failed_filter?
@filter_check_results.include?(false)
end
end
The CheckWorkflowConditions service object has followed the composed method technique from Eloquent Ruby. That is:
- Each method does one single thing. The
no_filters?
method only checks if filters are present? Similarly all the other methods in this class focusses only on doing one thing each. - Each method operates at single conceptual level. The high level logic is not mixed with the details in any method.
- Each method has a name that specifies what it does. The method name kind of tells us what the method is trying to do.
The above service object also does not follow the advice that each method should only have one way out, so that all the logic results in a return at the bottom of the method. Although the passes?
method has multiple returns, because we have followed the composite technique, it is easy to read and understand.
Also, the logic related to making the Shopify API calls has been delegated to a different service object: CheckOrderWithFilter.new(shop: shop, order_id: @order_id, filter: filter).get_result
. This makes the whole class more focussed on just checking if the workflow conditions pass or fail, making it easier to test.
Writing (and reading) ruby code becomes more enjoyable when the class is made of methods that are short, focussed.
This post originally appeared on Weightless.
Top comments (0)