Project

General

Profile

Bug #16982

Updated by sawa (Tsuyoshi Sawada) almost 4 years ago


 I found two problems with `DelegateClass`. 
 The patch is available on GitHub. https://github.com/ruby/ruby/pull/3221 

 


 # `instance_methods` 

 First, `instance_methods` method's result does not contain the names of methods a method name that are added after the class is defined. 
 For example 

 


 ```ruby 
 require 'delegate' 

 class Parent 
   def parent_public; end 

   protected 

   def parent_protected; end 
 end 

 class Child < DelegateClass(Parent) 
 end 

 class Parent 
   def parent_public_added; end 

   protected 

   def parent_protected_added; end 
 end 

 # They're ok. 
 p Child.instance_methods.include? :parent_public      # => true 
 p Child.instance_methods.include? :parent_protected # => true 

 # They're wrong. :parent_public_added and :parent_protected_added should be included, but they are not. 
 p Child.instance_methods.include? :parent_public_added      # => false 
 p Child.instance_methods.include? :parent_protected_added # => false 
 ``` 

 


 So I think `DelegateClass` should define `instance_methods` method explicitly, like `public_instance_methods` and `protected_instance_methods`. 

 I've added `instance_methods` in this commit https://github.com/ruby/ruby/pull/3221/commits/3f49e3ff094288e41b5629fd07aa8fa858abb196 

 



 # `instance_method` and `public_instance_method` 

 


 Second, `instance_method` and `public_instance_method` cannot get a method object for methods a method that are added after the class is defined. 
 And they it cannot get a method object for `:to_s` and so on because of https://github.com/ruby/ruby/blob/fbb32b1f483925987d225b4dc08efd197775bcae/lib/delegate.rb#L390. 

 



 For example: 

 


 ```ruby 
 require 'delegate' 

 class Parent 
   def parent() end 
 end 

 class Child < DelegateClass(Parent) 
 end 

 class Parent 
   def parent_added() end 
 end 

 p Child.instance_method(:parent) # returns an return a UnboundMethod 
 p Child.instance_method(:parent_added) # undefined method `parent_added' for class `Child' (NameError) 
 p Child.instance_method(:to_s) # undefined method `to_s' for class `Child' (NameError) 
 ``` 

 But actually a `Child` instance has these all these methods. 
 So I think `instance_method` method should return a method object. 

 



 I actually got the error when I tried try to use `rbs prototype runtime`. 
 https://github.com/ruby/rbs/blob/150bc6e65838eab2be088a5cc456b6f4b04bc81c/lib/rbs/prototype/runtime.rb#L172 
 This code raises an error when it analyses a class that inherits a class generated by `DelegateClass` method. 
 For example 

 


 ```ruby 
 # test.rb 
 require 'delegate' 

 class Parent 
 end 
 class Child < DelegateClass(Parent) 
 end 
 class Parent 
   def foo() end 
 end 
 ``` 

 


 ```bash 
 $ rbs prototype runtime --require-relative test.rb    Child  
 W, [2020-06-24T21:17:45.628527 #890363]    WARN -- rbs: Skipping anonymous superclass #<Class:0x000056098f280750> of Child 
 /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:172:in `instance_method': undefined method `foo' for class `Child' (NameError) 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:172:in `target_method?' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:210:in `block in generate_methods' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:210:in `select' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:210:in `generate_methods' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:351:in `generate_class' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:43:in `block in decls' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:40:in `each' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/prototype/runtime.rb:40:in `decls' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/cli.rb:455:in `run_prototype' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/lib/rbs/cli.rb:85:in `run' 
	 from /home/pocke/.rbenv/versions/trunk/lib/ruby/gems/2.8.0/gems/rbs-0.4.0/exe/rbs:7:in `<top (required)>' 
	 from /home/pocke/.rbenv/versions/trunk/bin/rbs:23:in `load' 
	 from /home/pocke/.rbenv/versions/trunk/bin/rbs:23:in `<main>' 
 ``` 

 


 And I also got the same error when I applied the first patch to patch, for `instance_methods` method. The first patch broke `ruby/test/ruby/test_method.rb`. 
 https://github.com/ruby/ruby/blob/d1fb446b62b1e114252606dcf040dd9392f25170/test/ruby/test_method.rb#L1300 

 


 I fixed the problem with this commit https://github.com/ruby/ruby/pull/3221/commits/d1fb446b62b1e114252606dcf040dd9392f25170. 



Back