Bug #9005
closedobject.send(:define_method, ...){...} creates private method
Description
I assume this is caused by r40022, which made define_method consider visibility. However, visibility should only be considered if define_method is called normally, not via send. When called via send, it should define a public method. Here's example code showing the error:
$ ruby21 -ve "Object.send(:define_method, :foo){|*a| 1}.foo"
-e:1:in <main>': private method foo' called for :foo:Symbol (NoMethodError)
I apologize in advance if this has already been fixed (I tested 2.1.0-preview1, not trunk), but from the commit logs it doesn't appear to have been.
        
           Updated by elight (Evan Light) almost 12 years ago
          Updated by elight (Evan Light) almost 12 years ago
          
          
        
        
      
      Ran against trunk:
-e:1:in `<main>': private method `foo' called for :foo:Symbol (NoMethodError)
        
           Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          
          
        
        
      
      - Status changed from Open to Assigned
- Assignee set to nobu (Nobuyoshi Nakada)
        
           Updated by jeremyevans0 (Jeremy Evans) almost 12 years ago
          Updated by jeremyevans0 (Jeremy Evans) almost 12 years ago
          
          
        
        
      
      There's only two days until the release of 2.1.0, and this still hasn't been fixed. This is a serious regression that breaks existing code (among other things, it causes Sequel's tests to freeze). If this can't be fixed before 2.1.0, I think r40022 should be reverted on the 2.1 branch, and the feature should be moved to 2.2.0. Assuming this is fixed on trunk before 2.1.0, it should be immediately backported to the 2.1 branch.
        
           Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          
          
        
        
      
      send is irrelevant here.
If you make define_method public, it isn't needed.
c = Class.new {
  class << self
    public :define_method
  end
}
c.define_method(:foo) {}
c.new.foo #=> NoMethodError
Your code just reflects the default visibility at the top level.
        
           Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          
          
        
        
      
      - Status changed from Assigned to Closed
- % Done changed from 0 to 100
This issue was solved with changeset r44380.
Jeremy, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
proc.c: make method by define_method public
- proc.c (rb_mod_define_method): consider visibility only if self
 in the caller is same as the receiver, otherwise make public as
 well as old behavior. [ruby-core:57747] [Bug #9005]
 [ruby-core:58497] [Bug #9141]
- vm.c (rb_vm_cref_in_context): return ruby level cref if self is
 same.
        
           Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          Updated by nobu (Nobuyoshi Nakada) almost 12 years ago
          
          
        
        
      
      - Backport set to 1.9.3: DONTNEED, 2.0.0: DONTNEED, 2.1.0: REQUIRED
        
           Updated by scotttam (Scott Tamosunas) about 11 years ago
          Updated by scotttam (Scott Tamosunas) about 11 years ago
          
          
        
        
      
      This isn't fixed on 2.1.3. Has that changeset not made it into a release?
2.1.3 :001 > RUBY_VERSION
 => "2.1.3" 
2.1.3 :002 > class Foo
2.1.3 :003?>   
2.1.3 :004 >       private
2.1.3 :005?>     
2.1.3 :006 >       define_method("bar") { puts "BAR" }
2.1.3 :007?>   end
 => :bar 
2.1.3 :008 > 
2.1.3 :009 >   Foo.new.bar
NoMethodError: private method `bar' called for #<Foo:0x007ff90b8e3198>
	from (irb):9
	from /Users/me/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'`
Where this works on 2.0
2.0.0-p451 :001 > RUBY_VERSION
 => "2.0.0" 
2.0.0-p451 :002 > class Foo
2.0.0-p451 :003?>   
2.0.0-p451 :004 >       private
2.0.0-p451 :005?>     
2.0.0-p451 :006 >       define_method("bar") { puts "BAR" }
2.0.0-p451 :007?>   end
 => #<Proc:0x0000010103cd00@(irb):6 (lambda)> 
2.0.0-p451 :008 > 
2.0.0-p451 :009 >   Foo.new.bar
BAR
        
           Updated by jeremyevans0 (Jeremy Evans) about 11 years ago
          Updated by jeremyevans0 (Jeremy Evans) about 11 years ago
          
          
        
        
      
      Scott Tamosunas wrote:
This isn't fixed on 2.1.3. Has that changeset not made it into a release?
2.1.3 :001 > RUBY_VERSION => "2.1.3" 2.1.3 :002 > class Foo 2.1.3 :003?> 2.1.3 :004 > private 2.1.3 :005?> 2.1.3 :006 > define_method("bar") { puts "BAR" } 2.1.3 :007?> end => :bar 2.1.3 :008 > 2.1.3 :009 > Foo.new.bar NoMethodError: private method `bar' called for #<Foo:0x007ff90b8e3198> from (irb):9 from /Users/me/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>'`Where this works on 2.0
2.0.0-p451 :001 > RUBY_VERSION => "2.0.0" 2.0.0-p451 :002 > class Foo 2.0.0-p451 :003?> 2.0.0-p451 :004 > private 2.0.0-p451 :005?> 2.0.0-p451 :006 > define_method("bar") { puts "BAR" } 2.0.0-p451 :007?> end => #<Proc:0x0000010103cd00@(irb):6 (lambda)> 2.0.0-p451 :008 > 2.0.0-p451 :009 > Foo.new.bar BAR
The behavior change here is deliberate, since you are calling define_method inside the class definition after calling private.  This bug was that define_method when called outside the class definition was generating private methods, which was fixed before the release of 2.1.0.