Question

I'm building a Rails 3 gem that essentially modifies the records returned from an ActiveRecord query. One of the things I'm doing is overriding the method_missing and respond_to? methods, but it seems that my respond_to? definition is resulting in an infinite loop that is throwing a "SystemStackError: stack level too deep" error.

Here are my original definitions of these methods:

def respond_to?(name, *args)
  super(name, *args) || parent_association.respond_to?(name)
end

def method_missing(name, *args, &block)
  if parent_association.respond_to?(name)
    parent_association.send(name, *args, &block)
  else
    super(name, *args, &block)
  end
end

def parent_association
  send(parent_association_name)  # Essentially yields another ActiveRecord
                                 # instance (e.g.: instance of User), but
                                 # never returns itself.
end

In trying to learn why this infinite loop was occurring, I restructured respond_to? with some "before" and "after" output to see where it's getting stuck.

def respond_to?(name, *args)
  return true if super(name, *args)
  puts "before (#{name})"
  result = parent_association.respond_to?(name)
  puts "after"
  result
end

When running, it appears that various callbacks and attribute methods run as expected, with a single before and after call for each:

before (_run__374051839217347232__initialize__1707831318230746190__callbacks)
after
before (_run__374051839217347232__validation__1707831318230746190__callbacks)
after
before (_run__374051839217347232__validate__1707831318230746190__callbacks)
after
before (_run__374051839217347232__save__1707831318230746190__callbacks)
after
before (_run__374051839217347232__create__1707831318230746190__callbacks)
after
before (created_at)
after
before (created_on)
after
...

However, any time I see a find callback, that appears to be caught in an infinite loop:

before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
...
SystemStackError: stack level too deep

If I hack my respond_to?, then everything appears to run smoothly:

def respond_to?(name, *args)
  return true if super(name, *args)
  return false if name =~ /^_run_.*_find_.*_callbacks$/
  parent_association.respond_to?(name)
end

What am I doing wrong that I seem to need this hack? And how can I avoid it?

Was it helpful?

Solution 2

The issue ended up being this function:

def parent_association
  send(parent_association_name)  # Essentially yields another ActiveRecord
                                 # instance (e.g.: instance of User), but
                                 # never returns itself.
end

The variable parent_association_name is something like employee, which is defined via something like:

belongs_to :employee

Because employee is not defined on the model instance until AFTER the find callback executes, and because I was first calling respond_to? in a spot BEFORE the find callback is being called (in code no included in my original question), the call to send(parent_assocation_name) was causing a recursive call to respond_to?.

OTHER TIPS

If parent_association returns a new AR object, it will also inherits your hack, so calling respond_to? on it will call your respond_to?, which will create a AR new object, etc...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top