Misusing of NotImplementedError in Ruby
Common mistakes and existing alternatives
Have you ever come across code that raises a NotImplementedError
when a method is not implemented in a class? It may seem like a convenient way to indicate that a method should be implemented, but actually it’s not.
Understanding NotImplementedError
According to the Ruby docs, NotImplementedError
should be used when a feature is not implemented on the current platform. It’s typically used in operating-system-dependent methods to indicate that the current runtime platform does not support certain functionality.
For example, if a method depends on the fsync
or fork
system calls, raising a NotImplementedError
would be appropriate if the underlying operating system or Ruby runtime doesn’t support them.
def save_data(data, file_path)
File.open(file_path, 'w') do |file|
file.write(data)
file.fsync
end
rescue NotImplementedError
raise 'The fsync system call is not supported on this OS.'
end
Confusion and Misuse
However, the name of the exception, NotImplementedError
, can be misleading. Developers often misinterpret its purpose and use it in abstract methods or as a replacement for TODO comments. This misunderstanding has spread, leading to developers wrongly suggesting its use in situations where it doesn’t make sense. Also, NotImplementedError
inherited from ScriptError
, so it won’t be rescued:
Alternatives
Given the risks and potential confusion surrounding NotImplementedError
, it is crucial to consider alternative approaches. While there is no universal consensus, here are three options that can be considered:
Use NoMethodError
Out of the existing Ruby exceptions, NoMethodError is the most appropriate for abstract methods. It clearly indicates that the method does not exist in the current context.
Example:
class AbstractClass
def some_undefined_method
raise NoMethodError.new('method not implemented in this class')
end
end
Raise a RuntimeError
If the situations where you encounter this error are expected to be rare, you may choose to raise a RuntimeError with a specific message. This allows you to provide a custom error message while still adhering to Ruby’s exception hierarchy.
Example:
class AbstractClass
def some_undefined_method
raise RuntimeError.new('method not implemented in this class')
end
end
Define a custom exception class
For more complex projects, it may be beneficial to define your own exception class and use it consistently throughout. For example, creating an CustomMethodError
class that inherits from StandardError
can provide clarity and consistency.
Example:
class CustomMethodError < StandardError
end
class AbstractClass
def some_undefined_method
raise CustomMethodError.new('method not implemented in this class')
end
end
Conclusion
The misuse of NotImplementedError
in Ruby can lead to confusion and unexpected behavior. While the name may be appealing, it is essential to use this exception correctly in OS-dependent methods. For abstract methods or as a TODO replacement, alternative approaches like NoMethodError
, RuntimeError
, or a custom exception class are recommended.