Ruby on Rails — Stack level too deep


Ruby on Rails — Stack level too deep



model:


after_save :set_correct_post_type

def set_correct_post_type
if self.document.present?
if (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.presentationml.presentation") || (find_mime_type(self.document.original_filename) == "application/vnd.ms-powerpoint")
self.update_attributes(:post_type => 3)
elsif (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) || (find_mime_type(self.document.original_filename) == "application/msword") || (find_mime_type(self.document.original_filename) == "application/pdf")
self.update_attributes(:post_type => 2)
elsif (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/png") || (MIME::Types.type_for(self.document.original_filename).first.content_type =="image/jpeg") || (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/jpg")
self.update_attributes(:post_type => 5)
end
end #line17
end



Logs:


(0.3ms) rollback transaction
Completed 500 Internal Server Error in 1939ms (ActiveRecord: 37.5ms)

SystemStackError (stack level too deep):
app/models/teacher_post.rb:17:in `set_correct_post_type'
app/models/teacher_post.rb:17:in `set_correct_post_type'
app/models/teacher_post.rb:17:in `set_correct_post_type'
app/models/teacher_post.rb:17:in `set_correct_post_type'
app/models/teacher_post.rb:17:in `set_correct_post_type'
app/models/teacher_post.rb:17:in `set_correct_post_type'
app/models/teacher_post.rb:17:in `set_correct_post_type'



And then it repeats the last line again and again until I stop it manually. Can anyone tell me what I'm doing wrong?





Which line is at app/models/teacher_post.rb:17?
– Jagdeep Singh
Jul 2 at 10:19


app/models/teacher_post.rb:17





@JagdeepSingh I've commented line 17 in the model code
– R.lOOp
Jul 2 at 10:23




3 Answers
3



The reason for "stack level too deep error" is because you are calling update_attributes inside after_save callback, which will invoke the callback after_save again, calling update_attributes again, and so on...


update_attributes


after_save


after_save


update_attributes



Change it to following:


before_save :set_correct_post_type

def set_correct_post_type
if self.document.present?
if (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.presentationml.presentation") || (find_mime_type(self.document.original_filename) == "application/vnd.ms-powerpoint")
self.post_type = 3
elsif (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) || (find_mime_type(self.document.original_filename) == "application/msword") || (find_mime_type(self.document.original_filename) == "application/pdf")
self.post_type = 2
elsif (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/png") || (MIME::Types.type_for(self.document.original_filename).first.content_type =="image/jpeg") || (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/jpg")
self.post_type = 5
end
end
end



When you call update_attributes, the callbacks are called, as your method set_correct_post_type is set as "after_save", you end with an loop.



You call save on you method that triggers the set_correct_post_type method that call update_attributes that triggers the set_correct_post_type method that call update_attributes...



If you don't want to trigger the callbacks in your method, look at update_columns instead of update_attributes.
Of consider setting attribute with a before_save instead of a after save.



If you have your custom code, without any gems etc, avoid using rails callbacks. I'd recommend use Serice Object ex. CreateTeacherPost which will create a post, do magic with params, types etc all in one Transaction. This way you will avoid problems like below and you will always know whats going on without callbacks magic.


CreateTeacherPost



But if you really want to use this pattern it's going into an infinite loop because each update_attributes is calling after_save! callback method. You could use update_column method or before_save callback and set attribute directly using self.post_type=number.
But first will call SQL update the second time, there is no reason to do this.


update_column


before_save


self.post_type=number



One more :) if you have to/want to use after callback better use after_commit callback. It's much safer.






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Rothschild family

Cinema of Italy