In this short post I will look at some patterns in Ruby methods and their naming conventions.
The most prominent pattern is delineation of methods that produce a value and no side-effects from methods with side-effects. I achieve this by only using verbs in side-effect-producing method names, whereas methods with only return value get adjectives and nouns.
Return-value-only
Let's have a look:
def get_name
def name
def produce_red_cube
def red_cube
def normalize_params(params)
def normal_params(params) # or normalized_params
def make_request
def response # sidestep the process by which the response is obtained altogether
def render_view
def content # here as well, focus on the result
As you can see, it's irrelevent how the value is obtained, no need to mention it in the method name. Otherwise we'd end up with def obtain_<thing>_by_deep_coding_magic
methods all over the place.
Naming things this way has the functional side-effect of aligning initialization with getter, making memoization straightforward. Everything becomes self-ensuring and safer.
# before
attr_reader :company
def load_company
@company = Company.find(params[:company_id])
authorize @company, :show
end
def do_something_important
load_company # important that loading occurs before first call to #company, brittle
company.something
end
# after
def company
@company ||= Company.find(params[:company_id]).tap { |c| authorize c, :show }
end
def do_something_important
company.something
end
You may have noticed that this also makes before_action filters unnecessary. 😼
Methods with side-effects
Naming is simple - start with a descriptive verb, followed by the signifying noun.
def enqueue_job
def process_upload
def reorder_toys! # throw in a bang to really show things will change
These come in two flavours - where the return value is wholly unimportant, indeed, having one would be confusing, and where there is a return value, usually an "outcome" object.
For methods without interesting return values I recommend explicitly returning nil
to signify this. It's also useful to prevent leakage of unintended return values, consider:
# bad
def close_old_cases
cases.each do |case|
case.close! if case.old?
end
end #=> cases
# good
def close_old_cases(cases)
cases.each do |case|
case.close! if case.old?
end
nil
end #=> nil
If a method has both significant side-effects (writing to DB, filesystem, etc.) and has an interesting return value, I recommend being explicit about this with a YARD-style comment at least:
# @return [ConversionReport] # convert's an accounts currencies and returns a report object with details
def convert_currencies!(from, to)
...
ConversionReport.new(at: Time.current, from : from, to: to)
end
convert_currencies!.success? #=> true
Naming things can be hard, especially for non-native speakers. I usually try to use expressive and precise words - all those process
and calculate
get a bit repetitive :)
Another pattern I like is using Ruby's special call
method to push the naming into the class. There are many approaches to naming when doing this since there can be several considerations (for example, leading with context/concept to get alphabetical grouping etc.), but I prefer noun-ification with an emphasis on professions, for example:
def process_upload
UploadReviewer.call
def manage_record_lock
Receptionist.call
def calculate_pay
PayrollClerk.call
Photo by Ainur Iman on Unsplash