How to refactor long if/return statements in Ruby? -
i encounter complex pile of if
statements, ruby way clean up?
(in service object example, foo has many bars. transferring bar different foo.)
class barmanager include fancyerrorlogger def self.transfer(bar, new_foo) # move needed? line superfluous , premature optimisation? return true if bar.foo_id == new_foo.id # checks bar can moved new_foo. many more elsifs in practice, needs refactoring. these examples demonstrate potential complexity of each step, preventing use of overly simplistic solutions such seen here http://codereview.stackexchange.com/questions/14080/avoiding-a-lot-of-ifs-in-ruby if bar.dependency == :do_not_move_me or bar.some_condition == false bar.errors.add( :transfer, "this bar can't moved, on street corners moping , singing") return false elsif new_foo.want_more_bars == false bar.errors.add( :transfer, "\"we don't take kindly type, bar\" - #{new_foo.name}") return false elsif ((bar.baz.optional_external_nightmare_status != :unused) || (new_foo.bars.inject(false){|result, element| result = result || element.baz.optional_external_nightmare_status != :unused})) bar.errors.add( :transfer, "i have baz news you...") return false elsif bar.yet_another_failure_reason_there_are_many bar.errors.add( :transfer, "there many ways fail, if statement short") return false elsif bar.stubborn? bar.lure_with_carrot! if bar.munching? bar.errors.add ( :transfer, "eh, what's doc" ) return false elsif !bar.following? bar.errors.add ( :transfer, "your carrot small , inadequate. no jokes please" ) return false end end # made through gauntlet, transfer cache_old_foo_id = bar.foo_id # might need bar.foo_id = new_foo.id bar.save! # if using rails counter caching: foo.increment_counter(:bars_count, new_foo.id) foo.decrement_counter(:bars_count, cache_old_foo_id) # did need return true rescue exception => e fancy_error_log e false end end
step 1 remove duplication long if
statement. if
returns value (and returns nil if no if
/elsif
matches), can do
error = if bar.dependency == :do_not_move_me or bar.some_condition == false "this bar can't moved, on street corners moping , singing" elsif new_foo.want_more_bars == false "\"we don't take kindly type, bar\" - #{new_foo.name}") elsif ((bar.baz.optional_external_nightmare_status != :unused) || (new_foo.bars.inject(false){|result, element| result = result || element.baz.optional_external_nightmare_status != :unused}) "i have baz news you..." elsif bar.yet_another_failure_reason_there_are_many "there many ways fail, if statement short" elsif bar.stubborn? bar.lure_with_carrot! if bar.munching? "eh, what's doc" elsif !bar.following? "your carrot small , inadequate. no jokes please" end end if error bar.errors.add :transfer, error return false end
step 2 extract conditions methods understandable names, idea in case. move in each condition method, !
operators, , give every method same parameter list. we'll see why in moment.
error = if bar.unmovable? new_foo "this bar can't moved, on street corners moping , singing" elsif bar.unwanted? new_foo "\"we don't take kindly type, bar\" - #{new_foo.name}") elsif bar.has_bad_news? new_foo "i have baz news you..." elsif bar.will_fail_for_yet_another_reason? new_foo "there many ways fail, if statement short" elsif bar.lure_with_carrot? new_foo # if don't side effects, lure before if "eh, what's doc" elsif bar.uninterested_in_carrot? new_foo "your carrot small , inadequate. no jokes please" end # use error above
step 3 remove duplication again:
checks = { unmovable?: "this bar can't moved, on street corners moping , singing", unwanted?: "\"we don't take kindly type, bar\" - #{new_foo.name}"), has_bad_news?: "i have baz news you...", will_fail_for_yet_another_reason?: "there many ways fail, if statement short", lure_with_carrot?: "eh, what's doc", uninterested_in_carrot?: "your carrot small , inadequate. no jokes please" } method_name, error = checks.find { |method_name, _| bar.send :method_name, new_foo } # use error above
Comments
Post a Comment