我正在寻找一种更好的方法来处理错误,我们通常编写接受块的代码,以便在成功的结果中完成。我颠倒了那个想法,并把它写下来:
class SomeVeryBigReportObjectOnWhatWentWrong
# contains attributes to help format a nice report for humans
# comes up with a nice backtrace
# writes to rails log
# writes to error aggregator (External site)
# maybe writes to slack depending on error
# Records the error in another log for developers to view
# dances and twirls too.
end
class MyTest
def can?(test, &block)
return true if test
if block_given?
error_report = SomeVeryBigReportObjectOnWhatWentWrong.new("error message for starters")
yield error_report
end
return false
end
end
class MyController < ApplicationController
def action
page = Page.find(params[:id])
obj = MyTest.new
# obj.can?(:read, page)
return unless obj.can?(false) { | big_error_report |
big_error_report.context = .... # about 15 lines of this....
flash[:error] = big_error_report.message
redirect_to big_error_report.default_error_page
}
# back to our regular successful outcomes...
end
end
此代码的优点是错误处理是缩进的,允许人们轻松查看错误报告的内容,它可以从周围的代码中提取大量上下文,并且可以很好地自定义错误报告。
让下一位程序员的生活更轻松:http://blog.codinghorror.com/the-noble-art-of-maintenance-programming/
代码的缺点是,当我看到它时,我的第一个“思考”是
“返回”,除非“这是假的”?呃?阻止是什么? - 困惑 -
这段代码有异味吗?
答案 0 :(得分:0)
我的感觉是:
can?
执行两项操作:检查条件运行测试与否,然后运行一个块。但实际上,您可能只需要其中一个。此外,当你返回false
时,你想为这个值做些什么,控制逻辑外部?&block
已作为明确参数发送,但我们尚未使用如果我重构:(先说清楚)
class MyTest
def run_report
if block_given?
yield SomeVeryBigReportObjectOnWhatWentWrong.new("error message for starters")
else
# Handle by default
end
end
def can_report?(test)
# FIXME: I think you're gonna do something here to proceed test
test
end
end
class MyController < ApplicationController
def action
obj = MyTest.new
if obj.can_report?(false)
obj.run_report { | big_error_report |
big_error_report.context = .... # about 15 lines of this....
flash[:error] = big_error_report.message
redirect_to big_error_report.default_error_page
}
return
end
# back to our regular successful outcomes...
end
end