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 10 years ago. Updated over 10 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 10 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 10 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 10 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