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
end
should 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
end
Now 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
end
It 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