* [ruby-core:124726] [Ruby Feature#21871] Add Module#undef_const
@ 2026-02-09 6:31 jeremyevans0 (Jeremy Evans) via ruby-core
2026-02-09 9:51 ` [ruby-core:124732] " Eregon (Benoit Daloze) via ruby-core
2026-02-12 8:18 ` [ruby-core:124786] " matz (Yukihiro Matsumoto) via ruby-core
0 siblings, 2 replies; 3+ messages in thread
From: jeremyevans0 (Jeremy Evans) via ruby-core @ 2026-02-09 6:31 UTC (permalink / raw)
To: ruby-core; +Cc: jeremyevans0 (Jeremy Evans)
Issue #21871 has been reported by jeremyevans0 (Jeremy Evans).
----------------------------------------
Feature #21871: Add Module#undef_const
https://bugs.ruby-lang.org/issues/21871
* Author: jeremyevans0 (Jeremy Evans)
* Status: Open
----------------------------------------
I propose to add `Module#undef_const`, which would operate for constants similarly to how `undef_method` (and the `undef` keyword) works for methods. When an undefed constant is found during constant lookup, lookup stops, and results in `const_missing` being called (similar to how `undef_method` stops method lookup and results in `method_missing` being called).
The expected use for this is for stopping direct access to global constants inside namespaces. For example:
```ruby
class DangerousClass; end
class SafeClass
undef_const :DangerousClass
DangerousClass # NameError (const_missing behavior)
end
DangerousClass # no error
```
You cannot implement this behavior without introducing the equivalent of `Module#undef_const`. The best you can do is define a constant in the namespace that raises later when there is some access to the returned object. However, that approach has significant limitations, since you cannot raise on the lookup, only later when something operates on the returned object:
```ruby
class DangerousClass; end
class SafeClass
DangerousClass = BasicObject.new
DangerousClass.foo # NoMethodError
DangerousClass::Constant # TypeError
def m
# doesn't raise, NoMethodError/TypeError occurs potentially much later in unrelated code
[DangerousClass]
end
end
```
As to why it is good to stop direct access to a particular constants inside a namespace, it helps avoid IDOR (insecure direct object reference) security issues, since it pushes you to use a style like:
```ruby
# Find a bar related to this foo
bar = foo.bars.find { it.id == some_id }
```
As opposed to:
```ruby
bar = Bar.find { it.id == some_id && it.foo.id == foo.id }
```
Assuming these are equivalent, the first approach is still better, because it avoids the possibility that a developer can write the following, missing the necessary check that the bar being accessed is related to the foo:
```ruby
bar = Bar.find { it.id == some_id }
```
By using `undef_const :Bar` in the namespace, the `Bar.find` approach will not work, and developers will be pushed to use the `foo.bars.find` approach, which enforces safer behavior.
I've submitted a pull request that implements this proposal: https://github.com/ruby/ruby/pull/16116
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/
^ permalink raw reply [flat|nested] 3+ messages in thread
* [ruby-core:124732] [Ruby Feature#21871] Add Module#undef_const
2026-02-09 6:31 [ruby-core:124726] [Ruby Feature#21871] Add Module#undef_const jeremyevans0 (Jeremy Evans) via ruby-core
@ 2026-02-09 9:51 ` Eregon (Benoit Daloze) via ruby-core
2026-02-12 8:18 ` [ruby-core:124786] " matz (Yukihiro Matsumoto) via ruby-core
1 sibling, 0 replies; 3+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2026-02-09 9:51 UTC (permalink / raw)
To: ruby-core; +Cc: Eregon (Benoit Daloze)
Issue #21871 has been updated by Eregon (Benoit Daloze).
"undefined constants" is what I would call the old autoload behavior which left autoload constants in some limbo state when the loading failed.
I'm not keen on basically re-adding that or something similar to constants, the semantic model is much simpler without them.
It also means an extra check whenever looking up a constant, which is not great.
The sentinel value could leak and that would cause chaos (including potentially non-undefined constants being partially treated as undefined if that sentinel is assigned to another constant).
Most importantly, I think it would add a lot of confusion for users for cases like:
```ruby
A = 42
p A # OK
module Foo
# ...
p A # fails
end
```
We would be breaking a pretty fundamental properly of constant scopes here, which seems clearly not worth it.
I think the `DangerousClass = BasicObject.new` approach is good enough, I doubt many codebases would want to use a style like `bar = foo.bars.find { it.id == some_id }`.
Preventing access to `Bar` in a whole namespace seems too restrictive, the code could also be `bar = foo.find_bar(some_id)`, and there can be legitimate accesses to `Bar` under the namespace.
People might also just workaround with `bar = ::Bar.find { it.id == some_id }` or `bar = SomeHigherNamespace::Bar.find { it.id == some_id }` and then undefining the constant doesn't achieve much besides confusion.
TLDR: I'm against this, there are many downsides and use case seems pretty niche and not that convincing (doesn't actually help IDOR much).
----------------------------------------
Feature #21871: Add Module#undef_const
https://bugs.ruby-lang.org/issues/21871#change-116333
* Author: jeremyevans0 (Jeremy Evans)
* Status: Open
----------------------------------------
I propose to add `Module#undef_const`, which would operate for constants similarly to how `undef_method` (and the `undef` keyword) works for methods. When an undefed constant is found during constant lookup, lookup stops, and results in `const_missing` being called (similar to how `undef_method` stops method lookup and results in `method_missing` being called).
The expected use for this is for stopping direct access to global constants inside namespaces. For example:
```ruby
class DangerousClass; end
class SafeClass
undef_const :DangerousClass
DangerousClass # NameError (const_missing behavior)
end
DangerousClass # no error
```
You cannot implement this behavior without introducing the equivalent of `Module#undef_const`. The best you can do is define a constant in the namespace that raises later when there is some access to the returned object. However, that approach has significant limitations, since you cannot raise on the lookup, only later when something operates on the returned object:
```ruby
class DangerousClass; end
class SafeClass
DangerousClass = BasicObject.new
DangerousClass.foo # NoMethodError
DangerousClass::Constant # TypeError
def m
# doesn't raise, NoMethodError/TypeError occurs potentially much later in unrelated code
[DangerousClass]
end
end
```
As to why it is good to stop direct access to a particular constants inside a namespace, it helps avoid IDOR (insecure direct object reference) security issues, since it pushes you to use a style like:
```ruby
# Find a bar related to this foo
bar = foo.bars.find { it.id == some_id }
```
As opposed to:
```ruby
bar = Bar.find { it.id == some_id && it.foo.id == foo.id }
```
Assuming these are equivalent, the first approach is still better, because it avoids the possibility that a developer can write the following, missing the necessary check that the bar being accessed is related to the foo:
```ruby
bar = Bar.find { it.id == some_id }
```
By using `undef_const :Bar` in the namespace, the `Bar.find` approach will not work, and developers will be pushed to use the `foo.bars.find` approach, which enforces safer behavior.
I've submitted a pull request that implements this proposal: https://github.com/ruby/ruby/pull/16116
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/
^ permalink raw reply [flat|nested] 3+ messages in thread
* [ruby-core:124786] [Ruby Feature#21871] Add Module#undef_const
2026-02-09 6:31 [ruby-core:124726] [Ruby Feature#21871] Add Module#undef_const jeremyevans0 (Jeremy Evans) via ruby-core
2026-02-09 9:51 ` [ruby-core:124732] " Eregon (Benoit Daloze) via ruby-core
@ 2026-02-12 8:18 ` matz (Yukihiro Matsumoto) via ruby-core
1 sibling, 0 replies; 3+ messages in thread
From: matz (Yukihiro Matsumoto) via ruby-core @ 2026-02-12 8:18 UTC (permalink / raw)
To: ruby-core; +Cc: matz (Yukihiro Matsumoto)
Issue #21871 has been updated by matz (Yukihiro Matsumoto).
First, it doesn't seem directly related to the example and the functionality being proposed. Second, it doesn't directly explain the pros and cons of introducing `undef`ining constants. At this point, I don't see the need for it.
Matz.
----------------------------------------
Feature #21871: Add Module#undef_const
https://bugs.ruby-lang.org/issues/21871#change-116398
* Author: jeremyevans0 (Jeremy Evans)
* Status: Open
----------------------------------------
I propose to add `Module#undef_const`, which would operate for constants similarly to how `undef_method` (and the `undef` keyword) works for methods. When an undefed constant is found during constant lookup, lookup stops, and results in `const_missing` being called (similar to how `undef_method` stops method lookup and results in `method_missing` being called).
The expected use for this is for stopping direct access to global constants inside namespaces. For example:
```ruby
class DangerousClass; end
class SafeClass
undef_const :DangerousClass
DangerousClass # NameError (const_missing behavior)
end
DangerousClass # no error
```
You cannot implement this behavior without introducing the equivalent of `Module#undef_const`. The best you can do is define a constant in the namespace that raises later when there is some access to the returned object. However, that approach has significant limitations, since you cannot raise on the lookup, only later when something operates on the returned object:
```ruby
class DangerousClass; end
class SafeClass
DangerousClass = BasicObject.new
DangerousClass.foo # NoMethodError
DangerousClass::Constant # TypeError
def m
# doesn't raise, NoMethodError/TypeError occurs potentially much later in unrelated code
[DangerousClass]
end
end
```
As to why it is good to stop direct access to a particular constants inside a namespace, it helps avoid IDOR (insecure direct object reference) security issues, since it pushes you to use a style like:
```ruby
# Find a bar related to this foo
bar = foo.bars.find { it.id == some_id }
```
As opposed to:
```ruby
bar = Bar.find { it.id == some_id && it.foo.id == foo.id }
```
Assuming these are equivalent, the first approach is still better, because it avoids the possibility that a developer can write the following, missing the necessary check that the bar being accessed is related to the foo:
```ruby
bar = Bar.find { it.id == some_id }
```
By using `undef_const :Bar` in the namespace, the `Bar.find` approach will not work, and developers will be pushed to use the `foo.bars.find` approach, which enforces safer behavior.
I've submitted a pull request that implements this proposal: https://github.com/ruby/ruby/pull/16116
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-02-12 8:18 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-09 6:31 [ruby-core:124726] [Ruby Feature#21871] Add Module#undef_const jeremyevans0 (Jeremy Evans) via ruby-core
2026-02-09 9:51 ` [ruby-core:124732] " Eregon (Benoit Daloze) via ruby-core
2026-02-12 8:18 ` [ruby-core:124786] " matz (Yukihiro Matsumoto) via ruby-core
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).