Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not use NotImplementedError #776

Merged
merged 1 commit into from
Oct 12, 2023

Conversation

hallelujah
Copy link
Contributor

@hallelujah hallelujah commented May 26, 2023

Instead use NoMethodError as to indicate that the method should be defined in the class

Closes #775

To do

  • I have read the contributing guidelines.
  • I have added relevant tests.
  • I have adjusted relevant documentation.
  • I have made sure the individual commits are meaningful.
  • I have added relevant lines to the CHANGELOG.

PS: Thank you for contributing to Pundit ❤️

@Burgestrand
Copy link
Member

Thank you for the PR! Please update the changelog.

@denisdefreyne
Copy link

I worry that NoMethodError will cause confusion, because the method does exist; it just isn’t properly implemented in subclasses.

In my own code, I’ve used my own SubclassResponsibilityError, like this:

class SubclassResponsibilityError < StandardError
  def initialize(klass, method_name)
    super("Subclasses of #{klass} must implement the ##{method_name} method.")
  end
end

Would this make sense? It would mean adding a custom exception to the generator, which might not be what we want.

In any case, I do think NoMethodError is an improvement over NotImplementedError, so this PR gets a thumbs up from me!

@Burgestrand
Copy link
Member

@denisdefreyne I understand your worry, and it's a valid point. 😊

Taking that into consideration, and weighing that against adding a custom exception to the generator, together with the fact that the error message is descriptive, I think it's worth seeing how this flies in the wild.

It's generated code, so if this turns out to be confusing for people then 1) we can adjust it and 2) nobody needs to wait for a new release.

@Linuus
Copy link
Collaborator

Linuus commented Oct 11, 2023

What is a bit odd about this to me is that respond_to?(:resolve) #=> true but then raise NoMethodError when called.

https://ruby-doc.org/core-3.0.0/NoMethodError.html

Raised when a method is called on a receiver which doesn't have it defined and also fails to respond with method_missing.

I'm not saying NotImplementedError is perfect:
https://ruby-doc.org/core-3.0.0/NotImplementedError.html

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.

But maybe it maps closer to how it's used in this context?

Copy link
Member

@Burgestrand Burgestrand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

My thinking is that this error is for developers and not something you would expect to see runtime. We could have our own error (MethodNotImplemented = Class.new(Pundit::Error)) and raise that, but I don't think increasing our API surface area (another public name) is worth it for this.

Main benefit here is no longer inheriting from ScriptError, which an unimplemented resolve definitely is not.

Instead use NoMethodError as to indicate that the method should be
defined in the class

Closes varvet#775
@Burgestrand
Copy link
Member

FYI @hallelujah I updated this branch with a rebase, because main ran into a build issue on jruby due to no fault of this PR.

@Burgestrand Burgestrand merged commit 4d8cdf1 into varvet:main Oct 12, 2023
13 checks passed
@hallelujah hallelujah deleted the 775-use-NomethodError branch October 21, 2023 10:55
@franzliedke
Copy link

franzliedke commented May 27, 2024

Strictly speaking, this is a BR break. NotImplementedError is not a subclass of StandardError, whereas NoMethodError is. That means the exception will now be handled by a rescue statement (without specific exception class).

See the classic https://www.honeybadger.io/blog/understanding-the-ruby-exception-hierarchy/.

@Burgestrand
Copy link
Member

Burgestrand commented May 28, 2024

This change affects a generator for the ApplicationPolicy, which is typically run once in the lifetime of an application. If you've set up Pundit in your app already, this won't affect you.

If you're setting up Pundit fresh in a new application, then yes this might raise a different error than what you're used to. If you make a habit of rescue StandardError, then yes you might catch this error too.

Regardless, you should be able to bundle update pundit just fine, and all your pre-existing code should still work.

@franzliedke
Copy link

Of course, totally missed that it's in a generator. 🤦🏼

G-Rath added a commit to ackama/rails-template that referenced this pull request May 31, 2024
[Pundit have recently
changed](varvet/pundit#776) to using
`NoMethodError`, which breaks our spec - the main benefit of this change
is that the new error extends from `StandardError` so in theory is
easier to catch; however, it seems the community is divided as aside
from the different superclass the existing error is more accurate since
`NoMethodError` is meant to be thrown when the class does not
`respond_to?` the method in question and that is not true.

I personally would like to keep using `NotImplementedError` though I'm
happy to change if someone does prove a real-world situation where it's
more burdson to catch (or that our error monitoring won't catch it), but
I feel that should be a separate conversation - so this PR is changing
it back to maintain our current status quo until such time that we do
decide to change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do not use NotImplementedError
5 participants