- Randomly mixes input gathering, error handling, and business logic
- imposes cognitive load on the reader
- no digressions
- consistent narrative structure
http://github.com/avdi/cowsay
- Gather input
- Perform work
- Deliver results
- Handle Failure
- 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)
- Use
- Reject
- Use an assertion method instead of checking a bunch of conditions inline
- Third arg to Exception defines the stacktrace
- http://github.com/avdi/failfast
- 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"
- guard clause, ie
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
- 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
- Checked Method
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