2

I have a Rails application that was working fine with Rails 3.2.14. I have just started to work on upgrading it to Rails 4, essentially following along with Railscast episode 415.

I got as far as the part where I try to run my rspec tests to find many now fail with the following error:

 Failure/Error: Unable to find matching line from backtrace
 SystemStackError:
   stack level too deep

I also checked my Cucumber tests and find similar failures.

stack level too deep (SystemStackError)

As everything was fine before I started upgrading, I have been searching for other experiences that are similar but haven't found any.

I have tried both Ruby 1.9 and 2.0. I have not modified any code

Any guidance appreciated.

Update:

I have tracked the problem down to a model thatat defines method_missing as part of its implementation. I have described what I have found in a separate answer so that it can be commented on separately.

My application contained a model that implemented its own method_missing which could cause a recursive loop that ultimatly concluded in the "stack level to deep" error.

Community
  • 1
  • 1
starfry
  • 9,273
  • 7
  • 66
  • 96
  • Not an answer per se, but see http://stackoverflow.com/questions/9247071/rspec-stack-level-too-deep for how to approach. – Peter Alfvin Aug 17 '13 at 12:49
  • The problem also happens on a `rake db:seed`. I believe it is due to my app recursively including model subdirectories (`config.autoload_paths += Dir[Rails.root.join('app', 'models', 'content_items')]`). I have replicated the behaviour in a new test app and am now investigating that... – starfry Aug 19 '13 at 08:25
  • Please see my update to the original question. The stack overflow is merely a symptom of a model that defines a method `method_missing`. There is a definate behavioural difference in Rails 4 when `method_missing` is defined in a model. – starfry Aug 21 '13 at 17:20
  • Nice work! I just searched and while I found an apparently related https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CC8QFjAA&url=http%3A%2F%2Fstackoverflow.com%2Fquestions%2F16088392%2Fdefining-method-missing-on-active-record-in-rails-4-throws-systemstackerror-sta&ei=ePgUUuDZFu_wyAGcoIHADA&usg=AFQjCNFVivCMCoIOv_Kyc05ak4Sh8-upyQ&sig2=e3G6G539pel_LgPDSzNvhA&bvm=bv.50952593,d.aWc, I didn't see this called out in any release notes. You might want to factor our your explanation in a separate answer where folks can clearly see it (and upvote it!). – Peter Alfvin Aug 21 '13 at 17:31
  • And unless you think the recursion is entirely unique to your situation, you might want to even restore the title in order to help others with this symptom find it. – Peter Alfvin Aug 21 '13 at 17:50
  • thanks. I have separated it out as per your suggestion. I think my recursion problem was due to how the model was coded. It just wasn't expecting `method_missing` to be called for every use of an accessor!!! All I need to do now is find out how this scenario should be handled in Rails 4. – starfry Aug 21 '13 at 22:10

2 Answers2

3

Per suggestion, there is sufficient detail to warrant separating this as an answer.

I have tracked the problem down to a model that defines method_missing as part of its implementation. I have found that, if there is a method_missing definition in the model, then it is called instead of any accessors. This causes any model set-up to fail.

(in my specific case, method_missing was defined and this is what caused the stack overflow that I mentioned originally).

I can reproduce the problem succinctly by defining a new rails app in Rails 3.2.14 and then creating a new model:

class Item < ActiveRecord::Base
  attr_accessible :name, :content
  store :content

  def method_missing(id, *args) 
    puts "method missing: #{id}"
  end
end

and the associated migration

class CreateItems < ActiveRecord::Migration
  def change
    create_table :items do |t|
      t.string :name
      t.text :content

      t.timestamps
    end
  end
end

If I run a rails console I can exercise the model:

$ rails console
Loading development environment (Rails 3.2.14)
2.0.0p247 :001 > x = Item.new(name: 'foo')
 => #<Item id: nil, name: "foo", content: {}, created_at: nil, updated_at: nil> 
2.0.0p247 :002 >

If I build exactly the same thing in Rails 4.0.0 I get different output:

$ rails console
Loading development environment (Rails 4.0.0)
2.0.0p247 :001 > x = Item.new(name: 'foo')
method missing: name=
 => #<Item id: nil, name: nil, content: {}, created_at: nil, updated_at: nil> 
2.0.0p247 :002 > 

You'll notice that, in Rails 3.2.14, the name attribute is set to foo as is the intention. In Rails 4.0.0, however, see that method_missing is called and the attribute is not set.

I am reading up on the changes to ActiveRecord but I haven't been able to find anything that suggests models using method_missing which were ok prior to Rails 4 would no longer be ok in Rails 4.

Any pointers to getting the code example able to work in Rails 4 would help me solve the problems that I have with my model.

UPDATE

By manually invoking the upward method_missing chain, I can get the above example to work:

  def method_missing(id, *args) 
    super
    if respond_to? id
      send(id,*args)
    else
      puts "method missing: #{id}"
    end 
  end 

Needing to do this feels wrong to me because it isn't clear why this behaviour changed between Rails 3 and 4. I feel I am missing something else...

starfry
  • 9,273
  • 7
  • 66
  • 96
1

I don't know what the change was either, but you can avoid calling super for non-accessor methods by checking respond_to_without_attributes?. This is used in the source

  def method_missing(method, *args) 
    super if respond_to_without_attributes?(method, true) # returns true when attr accessors

    if respond_to? method
      send(method,*args)
    else
      puts "method missing: #{method}"
    end 
  end 
rigyt
  • 2,219
  • 3
  • 28
  • 40