The are many ways in how to return from a Ruby method. A method can use an explicit return
keyword and return a value, or one can rely on Ruby's use of expressions to skip using return
for brevity.
But there are also tactics in what to return as well. Periodically, a return value may need to have metadata about what it represents. Consider the situation of a call to a payment processor like Stripe. A wrapper method might look like this.
def charge_card(card)
charge = PaymentProcessor.charge(card)
charge
end
Seems simple enough. However, the charge may be successful, or the charge may fail. The credit card CVV may be wrong or the payment processor may be entirely offline. The return value will differ in that case.
def charge_card(card)
PaymentProcessor.charge(card)
rescue => e
nil
end
Returning nil from an external error is a fine short term solution, but over time may fail to be clear to other team members, or its meaning may be lost to time. In its current form, the method would need a comment for clarity on what nil
means.
There's a better way. In fact, the web has been using it since the beginning. It's a response object! In Ruby terms, a response class that looks like an HTTP response would have a status
attribute, a body
attribute, and any extra information needed.
class Response
attr_accessor :status, :body
def success?
status == :ok # similar to a 200 status
end
end
def charge_card(card)
charge = PaymentProcessor.charge(card)
Response.new.tap do |r|
r.status = :ok
r.body = { charge: charge }
end
rescue => e
Response.new.tap do |r|
r.status = :error
r.body = { error: e.message }
end
end
This implementation is more verbose. But that's ok since other programmers will have more clarity in what's happened to the charge. Instead of inferring what nil
is or hoping that a charge object is returned, the return value will always be a consistent Response
instance.
response = charge_card(card)
if response.success?
# do successful things
else
# retry, or do something else
end
In general, this form of data parcelling is known as a value object, and helps represent data in more unique and extensible ways. It takes full advantage of Ruby's object-oriented features, and makes code more web-like in the process.
Top comments (0)