snowangel
snowangel

Reputation: 3462

Stack level too deep when trying to update_attributes which haven't changed

This rather convoluted form works OK when there are params with "books" changes, but throws an impossible-to-debug stack level too deep error when params are submitted with no changes in the data. Any thoughts? I've commented out every validation and callback in all the models to no avail.

View:

  = semantic_form_for(@work, :method => :put, :html => {:class => "form-horizontal"}) do |f|
       # some code
            = f.semantic_fields_for(:workcontacts, wc) do |ff|           
            #...
    = f.semantic_fields_for(:books, @book) do |gg|              
      = gg.input :contributor_statement, :input_html => {:class => 'wysihtml5'}

Controller:

  def update
    @work     = Work.find(params[:id])
      respond_to do |format|        
        begin
          if @work.update_attributes(params[:work])
          flash[:success] = "#{@work.title} #{t(:global_save)}"
          format.html { redirect_to :back, :work => @work.id }
          format.json { respond_with_bip(@work) }
        else
          format.html { 
            redirect_to :back, :work => @work.id
            flash[:failure] = "#{@work.errors.full_messages.join(', ')}"
          }
          format.json { respond_with_bip(@work) }
        end
        rescue SystemStackError
          puts $!
          puts caller[0..100]
        end
      end
  end

Upvotes: 0

Views: 1001

Answers (2)

snowangel
snowangel

Reputation: 3462

Sorry about this, because it won't help a soul, but posting the solution for completeness.

A Work

has_many :books              , :inverse_of => :work

A Book

  belongs_to :work         , :inverse_of => :books, :touch => :child_updated_at, counter_cache: true

In the Book model I had

  accepts_nested_attributes_for :work

Which is the culprit.

Upvotes: 1

Richard Peck
Richard Peck

Reputation: 76784

Stack Level Too Deep basically means you have an infinite (recursive) loop somewhere in your application

--

Fix

Your code seems somewhat unconventional, so I've refactorered it for you:

  def update
    @work = Work.find(params[:id])
      respond_to do |format|        
          if @work.update(work_params)
              format.html {
                  flash[:success] = "#{@work.title} #{t(:global_save)}"
                  redirect_to :back, :work => @work.id
              }
              format.json { respond_with_bip(@work) }
          else
              format.html { 
                 redirect_to :back, :work => @work.id
                 flash[:failure] = "#{@work.errors.full_messages.join(', ')}"
               }
               format.json { respond_with_bip(@work) }
           end
      end
  end

  private

  def work_params
      params.require(:work).permit(:x, :y, :z)
  end

It looks to me like this code should work for you. However, I would suggest you post your model code for the Work model - as you'd typically have stack level too deep if you're using an after_create callback which saves again (hence going round in circles)

Upvotes: 1

Related Questions