Any opinions on whether methods that need to return either “success” or a detailed error message should use an Exception to contain the error message or should return nil for success, and a String with the error message in the case of failure.
I’ve used both. Some folks like to only use exceptions for “exceptional” cases. However, raising an exception can be simpler than conditionals.
I wouldn’t use an exception unless it’s truly an exceptional case. Paraphrasing Avdi Grimm, when you are thinking about raising an exception, ask yourself, “Am I prepared to end the program because of this?”
Would it be possible for your method to return a status object that indicates success or failure along with a message?
Off the top of my head, here’s an example of a status object.
class CommandStatus
# Some handy constants to give meaning to some booleans we'll be using
SUCCESS = true
FAILURE = false
attr_reader :message
# A factory method to easily create a status that represents success
def self.success
new(SUCCESS)
end
# A factory method to easily create a status that represents failure
def self.failure(message)
new(FAILURE, message)
end
def initialize(success, message=nil)
@success = success
@message = message
end
The method users of this class would call to check if a status represents success
def success?
@success == SUCCESS
end
If the method you were talking about, rather than returning nil for success and a string with the failure message for failures, return either CommandStatus.success or CommandStatus.failure(your_failure_message) on failures.
The code that uses receives this status object returned to it, would do something like:
if status.success?
# handle success
else
puts status.message
end
I’ve done something very similar to CommandStatus, except that my class MethodResponse is geared toward refactoring controller logic into a service object. It seems to work very nicely. The apply and set_flash methods are helpers to avoid duplicating the same code in multiple controller methods. Here it is. Any suggestions? Opinions?
class MethodResponse
attr_reader :flash_type
attr_reader :flash_message
attr_reader :http_status_code
attr_reader :data
attr_reader :redirect_path
attr_reader :session_data
# http_status can be either symbol or number
def initialize(flash_type: :notice,
flash_now: false,
flash_message: "",
http_status: :ok,
data: {},
redirect_path: nil,
session_data: {})
@flash_type = flash_type
@flash_now = flash_now
@flash_message = flash_message
@http_status_code = Rack::Utils::status_code(http_status)
@data = data
@redirect_path = redirect_path
@session_data = session_data
end
def self.symbol_to_status_code(symbol)
Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
end
# Currently only supports redirects
def apply(controller)
@session_data.each { |k, v| controller.session[k] = v }
if @flash_message.present?
if @flash_now
controller.flash.now[@flash_type] = @flash_message
else
controller.flash[@flash_type] = @flash_message
end
end
controller.redirect_to @redirect_path if @redirect_path.present?
end
def set_flash(flash)
flash[flash_type] = flash_message if flash_message.present?
end
end