Project

General

Profile

Actions

Bug #10564

closed

DelegateClass, method_missing, and instance_eval -- different behavior in ruby 2.1

Added by jrochkind (jonathan rochkind) over 9 years ago. Updated over 9 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
2.1.3p242
[ruby-core:66661]

Description

This is an odd one, I don't completely understand. But there is different behavior in ruby 2.0 vs 2.1, and the 2.0 behavior is what I'd expect. Not sure if it's a bug, expected difference in behavior, or something else.

Here is a minimized reproduction:

require 'delegate'

class DelegateWithMethodMissing < DelegateClass(Object)    

  def method_missing sym, *args, &block
    if sym == :lambda
      Kernel.raise Exception.new("Why is it catching lambda?")
    end
          
    Kernel.puts "Caught method missing for `#{sym}` with args: #{args}"
  end
  
end

o = DelegateWithMethodMissing.new( Object.new )

o.instance_eval do 
  missing_method "value"
  missing_method_with_lambda_value lambda {|a| a}
end  

On 2.0.0p576, this produces:

Caught method missing for missing_method with args: ["value"]
Caught method missing for missing_method_with_lambda_value with args: [#<Proc:0x007fa8532b3330@./spec/confstruct/test.rb:19 (lambda)>]

On 2.1.3p242, this instead raises the exception:

Caught method missing for missing_method with args: ["value"]
.test.rb:7:in `method_missing': Why is it catching lambda? (Exception)

Somehow the lambda inside the instance_eval is being caught by method_missing, instead of accessing the ordinary lambda keyword. If I remove the method_missing definition, then lambda can work as expected again, which is even more confusing.

Updated by jrochkind (jonathan rochkind) over 9 years ago

Oh, and you can note Kernel.raise and Kernel.puts in the reproduction -- this was neccesary to actually access raise and puts, otherwise just raw raise or puts also got unexpectedly caught by method_missing (causing an infinite recursion running out of stack in this case)

Updated by matz (Yukihiro Matsumoto) over 9 years ago

  • Status changed from Open to Closed

It's not a bug.

Delegator which is superclass of DelegateClass, is a subclass of BasicObject, not Object, so that those delegation objects does not respond to methods defined in Kernel module, that means lambda etc. are not available in the instance_eval block (where the receiver is Delegator).

Matz.

Updated by jrochkind (jonathan rochkind) over 9 years ago

Okay, thanks.

What I don't understand is, if method_missing is not defined in the < DelegateClass(Object) class, then I can still do:

my_lambda = o.instance_eval do 
   lambda {|a| a}
end
my_lambda #=> Proc object

And it works, and no method_missing on a superclass is called.

But if method_missing is defined on the subclass, then it gets called. Why is method_missing even being called, for a line that would work fine without calling superclass method_missing?

If not a bug, I still predict it's definitely going to be unexpected behavior for other developers that use DelegateClass, instance_eval, and method_missing together.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0