The Hash#fetch is such a Ruby example:
%w(Ruby Java).fetch(0) => "Ruby"and it raises the KeyError exception, when something went unexpected:
%w(Ruby Java).fetch(2) => IndexError: index 2 outside of array bounds: -2...2but the API provides a user friendly way to deal with unexpected behaviour. It just delegates the decision to the consumer. There are 3 options. First one is capturing the exception like:
begin %w(Ruby Java).fetch(2) rescue IndexError puts "Python is missing." endor a more convenient way:
%w(Ruby Java).fetch(2, "Add a language.")by simply assigning the fallback value as the second parameter. The third way to deal with the unexpected behaviour is passing a block:
%w(Ruby Java).fetch(2) { |index| "Add a language at position #{index}." }That consumer friendly approach not only applies to Hash#fetch but also to Hash#delete and many others. And it should also be adopted to the own API if required.
For example:
class Person def from_csv file File.open file end endshould be refactored to:
class Person def from_csv file, &fallback File.open file rescue Errno::ENOENT => error if block_given? yield(error, self) else raise end self end endNow it is up to the consumer how to deal with the exceptional behaviour:
Person.new.from_csv('person_1.csv') => Errno::ENOENT: No such file or directory @ rb_sysopen - person_1.csv Person.new.from_csv('person_1.csv') { |e| raise IOError.new e.message } => IOError: No such file or directory @ rb_sysopen - person_1.csv person = Person.new.from_csv('person_1.csv') { |e, person| person.name = 'unknown' } => #<Person:0x00000005303f90 @name="unknown">but the method itself can be refactored even a little more to:
class Person def from_csv file, &fallback fallback ||= ->(error) { raise } File.open file rescue Errno::ENOENT => error fallback.call(error) self end endIt works the same but removes the awful if/else condition by setting up a default lambda. Further articles of interest:
Supported by Ruby 2.1.1
Keine Kommentare:
Kommentar veröffentlichen