The purpose of NotImplementedError is defined in the Ruby docs:
NotImplementedError is raised when a feature is not implemented on the current platform. For example, methods depending on the fsync or fork system calls may raise this exception if the underlying operating system or Ruby runtime does not support them.
So whenever I see code like this:
1 2 3 4 5 6 7 8 9 |
class Vehicle def manufacture(n) n.times { create } end def create raise NotImplementedError, "subclass did not define #create" end end |
I cringe. Raising NotImplementedError simply to signal that a subclass didn’t abide by the contract of its superclass is an abuse of the error. The error is intended to be raised when a method cannot be implemented due to a limitation of the underlying platform (OS or Ruby runtime).
Forget NotImplementedError
So what should you do when you write something in Ruby that relies on an abstract method? The “most Ruby” thing to do would be to not implement the method at all. Instead, document the expectation and if a subclass fails to meet it a NameError , or often its subclass NoMethodError , will be raised automatically.
1 2 3 4 5 6 7 8 9 |
class Vehicle def manufacture(n) n.times { create } end # @abstract Subclass is expected to implement #create # @!method create # Make a particular model of world class vehicle end |
If a subclass of Vehicle does not quack like a Vehicle then it’s not really a Vehicle, and the Ruby interpreter will let us know with a loud NameError. Keep your ducks in a row and you’ll find little need for NotImplementedError.
Get comfortable with ducks
I admit, I am guilty of abusing NotImplementedError. I did it a few times early in my Ruby career, after years of being a Java programmer. In Java, and many other languages, you must declare any abstraction that your code relies upon. If you don’t the compiler will yell at you. When I first started with Ruby it was uncomfortable to not declare my abstractions. I found comfort in empty body methods and NotImplementedError. After awhile, however, not declaring the abstraction at all started to make the most sense. Ruby relies on duck typing, and its only expectation is that the receiver of a message respond. If the receiver cannot respond then it’s the job of the interpreter to let you know. This gives us a lot of power. It allows the implementation of an abstract method to come from anywhere. In the example above it’s expected to be defined by a subclass. It could just as easily come from a mixin, a class redefinition, or runtime metaprogramming.
Conclusion
Next time you want to raise NotImplementedError please stop. 95% of the time it is not what you’re looking for. Instead consider documentation. You’ll get the comfort of declaring the abstraction and you’ll keep it real with Ruby.
Got questions or feedback? I want it. Drop your thoughts in the comments below or hit me @ccstump. You can also follow me on Twitter to be aware of future articles.
Thanks for reading!
I believe there is tremendous benefit to the NotImplementedError that is not realized when you simply add comments to your base class.
The problem with not using NotImplementedError is that your non-implemented method could be called from literally anywhere in your code base. It might be a third party library, it might be the base class, it might be a class elsewhere in the library, etc. And when a NoMethodError is raised, it will be from the place that calls the missing method.
This will simply leave the implementer of your base class stumped and confused. It will not necessarily direct them to the base class where they can read the documentation you’ve left in the form of comments. Additionally, if your code is in the form of a gem, it may be difficult, especially for a new developer, just to *find* the code for the base class.
Much better to throw in a NotImplementedError, along with some documentation, because that is where the exception will be raised. In the base class, right next to the documentation about how a user is supposed to implement that method.
And if your code is wrapped up in a gem, the developer will still be able to see the filename/line-number, so they can look it up, even it is deeply hidden somewhere in their system files.
Valid points. If you’re more comfortable raising an exception then I’d suggest a custom exception instead of the standard library’s NotImplementedError which, as mentioned, is documented as existing for a different purpose.
hi there, thank you for sharing your views i appreciate it:
Sandi Metz seems to suggest that raising some type of error – anything at all – is better than the no method error situation which arises if the super class relies on the sub-class to implementing the method exclusively. In other words, better to be explicitly clear – so that things are obvious at a glance.
she suggests that things might be obvious for the immediate developer, but a future one who doesn’t undersetand the code base, may be confused if such an exception is not raised. i share this view, even though tehcnically, the exception may not be the correct one.