Project

General

Profile

Actions

Bug #19067

closed

Module methods not usable at toplevel under wrapped script

Bug #19067: Module methods not usable at toplevel under wrapped script

Added by shioyama (Chris Salzberg) about 3 years ago. Updated about 3 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:110378]

Description

#foo.rb  module Foo end private_constant :Foo 
module MyModule; end load "./foo.rb", MyModule # undefined method `private_constant' for main:Object (NoMethodError) #  # private_constant :Foo  # ^^^^^^^^^^^^^^^^  # Did you mean? private_methods 

However, this works:

module MyModule module Foo end private_constant :Foo end 

load loads the code under the wrap module, so this seems like a bug to me.

This applies to all methods on Module, which are usable inside a normal module definition but not usable when the module is the wrap argument to load.

I think these should all be usable in this context. If they cannot, then clarity is needed about what "being executed under" means in the documentation for load.

See: https://bugs.ruby-lang.org/issues/19024#note-23

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #1

  • Subject changed from `private_constant` does not work at toplevel in wrapped module context to private_constant does not work at toplevel in wrapped module context

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #2

  • Description updated (diff)

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #3

  • Subject changed from private_constant does not work at toplevel in wrapped module context to private_constant raises NoMethodError when called at toplevel under wrapped script

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #4

  • Description updated (diff)

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #5

  • Subject changed from private_constant raises NoMethodError when called at toplevel under wrapped script to private_constant and other Module methods not usable at toplevel under wrapped script

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #6

  • Subject changed from private_constant and other Module methods not usable at toplevel under wrapped script to Module class methods not usable at toplevel under wrapped script

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #7

  • Description updated (diff)

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #8

  • Description updated (diff)

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #9

  • Subject changed from Module class methods not usable at toplevel under wrapped script to Module methods not usable at toplevel under wrapped script

Updated by fxn (Xavier Noria) about 3 years ago Actions #10 [ruby-core:110386]

I have always been skeptical about the ability to pass a module to Kernel#load.

As an author of the target file I no longer control the nesting, which is fundamental to know the constant references I am writing are the ones I actually mean. Now, the nesting is always in the hands of the caller, and that to me is a red flag.

In this case, private_constant does not work at the top-level, why should I write that code in the first place? You can't either define methods for that matter. This feature is not like writing module WrappingModule at the top of the file.

The documentation is unclear about what does "being executed under" mean. Without that definition, we cannot know if this is a bug.

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #11 [ruby-core:110387]

The documentation is unclear about what does "being executed under" mean. Without that definition, we cannot know if this is a bug.

This is true. I am labelling it as a "bug" but it should be understood as "inconsistency".

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #12 [ruby-core:110390]

As an author of the target file I no longer control the nesting, which is fundamental to know the constant references I am writing are the ones I actually mean.

You cannot be sure the constant you are referencing is the one you mean, ever. I can define "my" Foo, then load your file which does something with "your" Foo, and your assumptions about Foo may be incorrect because of what my Foo has in it.

If you want to absolutely be sure what a constant is referencing, you would need to disallow load altogether.

Updated by mame (Yusuke Endoh) about 3 years ago Actions #13 [ruby-core:110392]

I think we can say the following by the same reasoning:

# foo.rb p self 
module MyModule; end load "./foo.rb", MyModule #=> current: self, expected?: MyModule 

because:

module MyModule p self #=> MyModule end 

This is a bit incompatible, but I wonder if it is better than dealing with Module#using, Module#private_method, etc. individually.

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #14 [ruby-core:110395]

This is a bit incompatible, but I wonder if it is better than dealing with Module#using, Module#private_method, etc. individually.

Yes that's a fair point, and to be honest, I didn't realize how much failed to work as "expected" until after I had created this issue.

Still, I think this is worth thinking about. Previously, it was not possible to control the module to load, so this was not really an issue. But now it is.

And I do think it's slightly ambiguous what is supposed to happen here.

e.g.

# foo.rb require "my_dependency" # ... code that depends on my_dependency 
module MyModule def require(filename) puts "required #{filename}!" end end load "./foo.rb", MyModule #=> required my_dependency! 

In this case, because top_self extends the top_wrapper module, require is (re-)defined and foo.rb does not actually require its dependency.

This is interesting, but I don't think it was the original intention of extending top_self with top_wrapper.

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #15

  • Description updated (diff)

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #16

  • Description updated (diff)

Updated by shioyama (Chris Salzberg) about 3 years ago Actions #17 [ruby-core:110404]

This is actually even confusing prior to the recent change:

If the optional wrap parameter is true, the loaded script will be executed under an anonymous module, protecting the calling program's global namespace.

# foo.rb def foo end foo 
load "foo.rb", true 

This "works", but it does not do this, which is what I think would be expected:

Module.new do def foo end foo end # undefined local variable or method `foo' for #<Module:0x00007f0823a7efb8> (NameError) 

I think what load actually does is powerful and I've stated so elsewhere, but it is not simply "executing it under an anonymous namespace".

Updated by matz (Yukihiro Matsumoto) about 3 years ago Actions #18 [ruby-core:110436]

load "foo.rb" should work (similar enough) with or without wrapping module. In that sense, replacing self is unacceptable.
Probably you want to get the "target class/module" which is the target of constant/method definitions. I have no idea of notation right now.

Matz.

Updated by matz (Yukihiro Matsumoto) about 3 years ago Actions #19

  • Status changed from Open to Rejected
Actions

Also available in: PDF Atom