问题
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.
回答1:
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...
回答2:
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
来源:https://stackoverflow.com/questions/18287707/model-with-method-missing-behaves-differently-in-rails-4-than-rails-3-can-cau