Project

General

Profile

Actions

Bug #11465

closed

`Array#flatten` calls `to_ary` on objects with `method_missing` even if `respond_to_missing?(:to_ary)` returns `false`.

Added by senny (Yves Senn) over 8 years ago. Updated over 8 years ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
[ruby-core:<unknown>]

Description

I noticed this behavior while working on an issue reported on the Rails tracker: https://github.com/rails/rails/issues/21296
I put up a reproduction along with some details about the issue in a Gist: https://gist.github.com/senny/02ee933cc75e7ac8093f

The problem is that Array#flatten calls to_ary on objects with method_missing even if respond_to_missing(:to_ary) returns false.

class RespondToMissing
  def respond_to_missing?(method, stuff)
    return false if method == :to_ary
    super
  end

  def method_missing(*args)
    super
  end
end

trace = TracePoint.new(:raise) do |tp|
  p tp.raised_exception
  puts tp.raised_exception.backtrace.join("\n")
  puts
end
trace.enable

RespondToMissing.new.respond_to? :to_ary # => false
[RespondToMissing.new].flatten

# OUTPUT:
# false
# #<NoMethodError: undefined method `to_ary' for #<RespondToMissing:0x007fd57322ce78>>
# script.rb:10:in `method_missing'
# script.rb:38:in `flatten'
# script.rb:38:in `<main>'

If the objects with method_missing also define their own respond_to? it works as expected:

class RespondTo
  def respond_to?(method)
    super
  end

  def method_missing(*args)
    super
  end
end

trace = TracePoint.new(:raise) do |tp|
  p tp.raised_exception
  puts tp.raised_exception.backtrace.join("\n")
  puts
end
trace.enable

p RespondTo.new.respond_to? :to_ary # => false
[RespondTo.new].flatten # no internal exceptions are raised

# OUTPUT:
# false

Expected Behavior

I expect respond_to_missing? to be considered (same as respond_to?) before Array#flatten calls #to_ary.

Workaround

As described above it's possible to work around this issue by also defining a respond_to? method. Another workaround is to define to_ary and return nil.

Actions #1

Updated by nobu (Nobuyoshi Nakada) over 8 years ago

  • Status changed from Open to Closed

Applied in changeset r51646.


vm_eval.c: share with rb_obj_respond_to

  • vm_eval.c (check_funcall_respond_to): share the behavior with
    rb_obj_respond_to. [ruby-core:70460] [Bug #11465]
  • vm_method.c (vm_respond_to): extract from rb_obj_respond_to and
    merge r39881.
Actions

Also available in: Atom PDF

Like0
Like0