Randomly mixes input gathering, error handling, and business logic
imposes cognitive load on the reader
no digressions
consistent narrative structure
Example code with this talk
http://github.com/avdi/cowsay
Gather input
Perform work
Deliver results
Handle Failure
Dealing with uncertain input
Coerce
Use #to_s, #to_i, and #to_a
liberally
Array(something)
is better than #to_a
in Ruby 1.9
Use a decorator (presenter)
Reject
Ignore
guard clause, ie return "" if message.nil?
Special case you can't ignore? Special Case pattern
represent special case as an object
Use a "black hole Null Object"
class NullObject
def initialize
@origin = caller . first
end
def __null_origin__
@origin
end
def method_missing ( *args , &block )
self
end
def nil?
true
end
end
def Maybe ( value )
value . nil? ? NullObject . new : value
end
nil
is overused in Ruby
it can mean a lot of things
use Hash#fetch when appropriate
collection.fetch(key) { fallback_actions }
Use a NullObject as default
NullObject can log its origin
Conditionals for Business Logic
Reserve conditionals for business logic
Don't use for nil catching, error handling
Chaining -- better with a smart Null Object and Maybe method
Iteration
Single object operations are implicitly one-or-error
Iteration is implicitly 0-or-more, not 1-or-fail
Chains of enumerable operations are self-nullifying
Return a Special Case or Null Object
Put the happy path first
Put error handling at the end
Or in other methods
Put a rescue in a method rather than wrapping method calls in begin . . . rescue . . . end block
Extract error handling methods
Extract error handling methods
def checked_popen ( command , mode , fail_action )
check_child_exit_status do
@io_class . popen ( command , "w+" ) do |process |
yield ( process )
end
end
rescue Errno ::EPIPE
fail_action . call
end