Project

General

Profile

Actions

Feature #19884

open

Make Safe Navigation Operator work on classes

Added by p8 (Petrik de Heus) 8 months ago. Updated 7 months ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:114774]

Description

If a constant isn't defined it will raise a NameError:

DoesNotExist.some_method # raises: uninitialized constant DoesNotExist (NameError)

In libraries that have optional dependencies, we can check if the constant is defined before calling a method on it:

defined?(OptionalDependency) && OptionalDependency.some_method

Currently in Ruby, the Safe Navigation Operator is used to avoid NoMethodError exceptions when calling methods on objects that may be nil.
It would be nice if we could use the Safe Navigation Operator to avoid NameError on undefined constants as well.

ClassThatMightNotExist&.some_method

Updated by sawa (Tsuyoshi Sawada) 8 months ago

Currently, you can do ActiveRecord::Base.some_method rescue nil

Using &. for rescuing an error in addition to the current behavior would make it confusing.

Furthermore, the specification you are asking for is not clear. What Exception classes do you want &. to rescue?

You stated that you want &. to work on classes, but that does not make sense. If ActiveRecord::Base is not defined, it is not a class. How would Ruby know that it is (supposed to be) a class?

Updated by rubyFeedback (robert heiler) 8 months ago

I am not sure that pattern is that common to warrant using &., aside from the syntax not being very elegant, but that may be my own bias against it (syntax-wise that is). I don't seem to need to do "defined?(Foo::Bar) && Foo::Bar.some_method".

Is it that common to use "defined?()"? For behaviour change I tend to use .respond_to? or even .is_a? more often.

Updated by AMomchilov (Alexander Momchilov) 7 months ago

@rubyFeedback (robert heiler)

It's common in library code, that want's to be flexible to different configurations of the target application. E.g. https://github.com/rubocop/rubocop/blob/a455e9d55771f1e3dfea0cc4183e66f9632b431c/lib/rubocop/lockfile.rb#L31

Updated by p8 (Petrik de Heus) 7 months ago

There's multiple examples in the Rails code base:

https://github.com/rails/rails/blob/7f7f9df8641e35a076fe26bd097f6a1b22cb4e2d/railties/lib/rails/generators/named_base.rb#L139
https://github.com/rails/rails/blob/7f7f9df8641e35a076fe26bd097f6a1b22cb4e2d/railties/lib/rails/generators/generated_attribute.rb#L67C1-L76
https://github.com/rails/rails/blob/7f7f9df8641e35a076fe26bd097f6a1b22cb4e2d/guides/rails_guides/markdown.rb#L184-L190

Considering the following:

def valid_type?(type)
  DEFAULT_TYPES.include?(type.to_s) ||
    !defined?(ActiveRecord::Base) ||
    ActiveRecord::Base.connection.valid_type?(type)
end

It would be nice to rewrite this as:

def valid_type?(type)
  DEFAULT_TYPES.include?(type.to_s) ||
    ActiveRecord&::Base&.connection.valid_type?(type)
end

Updated by p8 (Petrik de Heus) 7 months ago

sawa (Tsuyoshi Sawada) wrote in #note-1:

Currently, you can do ActiveRecord::Base.some_method rescue nil

That would also rescue UndefinedMethodError, ArgumentError, and any error occuring in some_method.

Furthermore, the specification you are asking for is not clear. What Exception classes do you want &. to rescue?

NameError

You stated that you want &. to work on classes, but that does not make sense. If ActiveRecord::Base is not defined, it is not a class. How would Ruby know that it is (supposed to be) a class?

I have no idea how &. is currently implemented, but defined? also handles undefined classes.

Updated by sawa (Tsuyoshi Sawada) 7 months ago

p8 (Petrik de Heus) wrote in #note-5:

Furthermore, the specification you are asking for is not clear. What Exception classes do you want &. to rescue?

NameError

Then you should edit and write that in the description.

defined? also handles undefined classes.

Can you show an example of that?

Actions #7

Updated by p8 (Petrik de Heus) 7 months ago

  • Description updated (diff)

Updated by p8 (Petrik de Heus) 7 months ago

sawa (Tsuyoshi Sawada) wrote in #note-6:

Then you should edit and write that in the description.

I've updated the description. Hopefully it's more clear now.

defined? also handles undefined classes.

Can you show an example of that?

defined?(UndefinedConstant) # returns nil instead of raising an error

Updated by sawa (Tsuyoshi Sawada) 7 months ago

sawa (Tsuyoshi Sawada) wrote:

You stated that you want &. to work on classes, but that does not make sense. If ActiveRecord::Base is not defined, it is not a class. How would Ruby know that it is (supposed to be) a class?

p8 (Petrik de Heus) wrote:

defined? also handles undefined classes.

sawa (Tsuyoshi Sawada) wrote:

Can you show an example of that?

p8 (Petrik de Heus) wrote:

defined?(UndefinedConstant) # returns nil instead of raising an error

Where does it say that UndefinedConstant is an "undefined class " rather than an undefined constant?

Updated by nobu (Nobuyoshi Nakada) 7 months ago

What you want doesn't seem the extension of the safe navigation operator.

p8 (Petrik de Heus) wrote:

If a constant isn't defined it will raise a NameError:

DoesNotExist.some_method # raises: uninitialized constant DoesNotExist (NameError)

In libraries that have optional dependencies, we can check if the constant is defined before calling a method on it:

defined?(OptionalDependency) && OptionalDependency.some_method

Rather the extension of defined? operator which returns the value (instead of the expression type string) or nil.

Let's call it defined⁉️ for now.
Isn't this what you want?

defined⁉️(OptionalDependency)&.some_method

Currently in Ruby, the Safe Navigation Operator is used to avoid NoMethodError exceptions when calling methods on objects that may be nil.
It would be nice if we could use the Safe Navigation Operator to avoid NameError on undefined constants as well.

ClassThatMightNotExist&.some_method

This can't work because &. will works on the result of the LHS, which might have raised already.

Updated by p8 (Petrik de Heus) 7 months ago

nobu (Nobuyoshi Nakada) wrote in #note-10:

Rather the extension of defined? operator which returns the value (instead of the expression type string) or nil.

Let's call it defined⁉️ for now.
Isn't this what you want?

defined⁉️(OptionalDependency)&.some_method

Yes, that would be nice.

Actions #12

Updated by Anonymous 7 months ago


Updated by kddnewton (Kevin Newton) 7 months ago

@nobu (Nobuyoshi Nakada) has said this already, but just to reiterate, if we changed Foo&.bar to do nothing in the case that it was undefined, it would break things like zeitwerk and other libraries that handle const_missing.

Actions

Also available in: Atom PDF

Like0
Like2Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like1