ActiveRecord::Base.transaction - SystemStackError - stack level too deep:

Im having an issue that seems to only happen when trying to use a transaction. Ive used transactions many times in the past and Im at a loss as to why im getting the stack level too deep problem.

SystemStackError - stack level too deep:

actionpack (3.2.13) lib/action_dispatch/middleware/reloader.rb:70:in `’

I have a StackOveflow with more detail - http://stackoverflow.com/q/16930511/1408461

As stated in the SO. If I try to save the record to the database outside the transaction it saves just fine. I only get the error in the transaction.

Params Object

params => {
  "resource"=> {
    "resource_type"=>"document",
    "resource_name"=>"My Rails Doc",
    "source_id"=>"Dropbox"
  },
  "resource_document"=> {
    "resource_document"=> #<ActionDispatch::Http::UploadedFile:0x007f8e919d06f8
      @content_type="text/plain",
      @headers=         "Content-Disposition: form-data; name=\"resource_document[resource_document]\";
filename=\"rails_local_env_setup.txt\"\r\n
Content-Type: text/plain\r\n",
      @original_filename="rails_local_env_setup.txt",
      @tempfile= #<File:/var/folders/t4/lfmj7mhj52b2krryzh7dj4hh0000gn/T/RackMultipart20130604-29589-2c0seo>>},

  "action"=>"create",
  "controller"=>"resources"
 }
**Controller**

def create if current_teacher @resource = ResourceObject.create_teacher_resource(params, current_teacher) end

if current_student @resource = ResourceObject.create_student_resource(params, current_student) end

if current_admin @resource = Resource.new(params[:resource]) end

respond_to do |format| if @resource. success format.html { redirect_to @resource, notice: ‘Resource was successfully created.’ } format.json { render json: @resource, status: :created, location: @resource } else format.html { render action: “new” } format.json { render json: @resource.errors, status: :unprocessable_entity } end end end

Transaction

class ResourceObject

def self.create_teacher_resource(params, teacher) begin ActiveRecord::Base.transaction do # Create Resource @resource = Resource. new @resource.resource_name = params[:resource][:resource_name] @resource.resource_type = params[:resource][:resource_type] @resource.source_id = params[:resource][:source_id] @resource.teacher_id = teacher. id @resource. save

Create Resource Document

    @resource_document = ResourceDocument.        new

@resource_document.resource_document = params[:resource_document][:resource_document] @resource_document.resource_id = @resource. id @resource_document. save

TODO Add Commom Core Joins

    # TODO Video Uploader
    # TODO Image Uploader

    return @resource.success = "ok"
  end
    rescue Exception => e

TODO Need to figure out how to pass exception message back to controller

      return @resource.        errors

end end end

end

Resource Model

	class Resource < ActiveRecord::Base
include TransactionAttributes
attr_accessible :resource_name, :resource_type, :source_id, :teacher_id, :student_id, :success, :errors
belongs_to :teacher, :class_name => "Teacher", :foreign_key => "teacher_id"
belongs_to :student, :class_name => "Student", :foreign_key => "student_id"
has_many :resource_documents
# has_many :resource_images
# has_many :resource_videos
# has_many :question_resources
# has_many :assignment_resources
end

Im having an issue that seems to only happen when trying to use a transaction. Ive used transactions many times in the past and Im at a loss as to why im getting the stack level too deep problem.

SystemStackError - stack level too deep:

actionpack (3.2.13) lib/action_dispatch/middleware/reloader.rb:70:in `’

I have a StackOveflow with more detail - http://stackoverflow.com/q/16930511/1408461

As stated in the SO. If I try to save the record to the database outside the transaction it saves just fine. I only get the error in the transaction.

Params Object

params => {
  "resource"=> {
    "resource_type"=>"document",
    "resource_name"=>"My Rails Doc",
    "source_id"=>"Dropbox"
  },
  "resource_document"=> {
    "resource_document"=> #<ActionDispatch::Http::UploadedFile:0x007f8e919d06f8
      @content_type="text/plain",
      @headers=         "Content-Disposition: form-data; name=\"resource_document[        resource_document]\";
filename=\"rails_local_env_        setup.txt\"\r\n
Content-Type: text/plain\r\n",
      @original_filename="rails_local_env_setup.txt",
      @tempfile= #<File:/var/folders/t4/lfmj7mhj52b2krryzh7dj4hh0000gn/T/RackMultipart20130604-29589-2c0seo>>},

  "action"=>"create",
  "controller"=>"resources"
 }
**Controller**

def create if current_teacher @resource = ResourceObject.create_teacher_resource(params, current_teacher) end

if current_student @resource = ResourceObject.create_student_resource(params, current_student) end

if current_admin @resource = Resource.new(params[:resource]) end

respond_to do |format| if @resource. success format.html { redirect_to @resource, notice: ‘Resource was successfully created.’ } format.json { render json: @resource, status: :created, location: @resource } else format.html { render action: “new” } format.json { render json: @resource.errors, status: :unprocessable_entity } end end end

Transaction

class ResourceObject

def self.create_teacher_resource(params, teacher) begin ActiveRecord::Base.transaction do # Create Resource @resource = Resource. new @resource.resource_name = params[:resource][:resource_name] @resource.resource_type = params[:resource][:resource_type] @resource.source_id = params[:resource][:source_id] @resource.teacher_id = teacher. id @resource. save

Create Resource Document

    @resource_document = ResourceDocument.        new

@resource_document.resource_document = params[:resource_document][:resource_document] @resource_document.resource_id = @resource. id @resource_document. save

TODO Add Commom Core Joins

    # TODO Video Uploader
    # TODO Image Uploader

    return @resource.success = "ok"
  end
    rescue Exception => e

A style note here: rescuing Exception is not typically what you want. See this for more details:

  # TODO Need to figure out how to pass exception message back to controller
      return @resource.        errors

end end end

end

Resource Model

class Resource < ActiveRecord::Base include TransactionAttributes

I’d be curious as to what’s in this module - it could be relevant…


  attr_accessible :resource_name, :resource_type, :source_id, :teacher_id, :student_id, :success, :errors

:errors here is not useful - attr_accessible controls mass-assignment of attributes (as when you do Resource.new(:param1 => ‘foo’, :param2 => ‘bar’). You almost certainly do not want to mass-assign an internal part of ActiveRecord. :slight_smile:

  belongs_to :teacher, :class_name => "Teacher", :foreign_key => "teacher_id"
belongs_to :student, :class_name => "Student", :foreign_key => "student_id"

Another minor (not terribly relevant) style note: :class_name and :foreign_key are both redundant here, as the values specified are the same as the ActiveRecord defaults.

Next steps for debugging this:

  • it would be useful to see the code for the TransactionAttributes module you’re including. Perhaps something is hitting an internal method and making a mess.

  • showing the DB schema would help as well; again, having fields with names that clash with ActiveRecord’s can cause peculiar behavior.

  • a full stack trace might shed some light; especially in the case of StackLevelTooDeep, the final line isn’t always helpful.

–Matt Jones

This is the TransactionAttributes

module TransactionAttributes

def success

success

end

def success=(success)

self.success = success

end

def errors

errors

end

def errors=(errors)

self.errors = errors

end

end

The reason for these was i was trying to set a property on a returned object or the errors. I yet to figure out a way i can return a more descriptive error message from the transaction back to the controller.

I’ll fix the style note. That just happens to populate when using a tab trigger in Sublime.

This is the table schema

== Schema Information

That’s the problem right there - this method is calling itself. If you want to define a getter / setter, you should either do this:

def success

@success

end

def success=(value)

@success = value

end

Thanks Matt. That was the problem. At one point I had pulled those out, or at least I thought I did. I guess thats why I was confused. Anyway. Thanks For the help. I do have Michaels stuff. Its very good. My setters were a rookie mistake.

One thing that I wish i could find more stuff about is transactions. Most examples seem to handle the transactions in the controller which I dont like. So I have troubles getting the errors back to the controller for better user feedback. Hence the reason I was trying to use the TransactionAttributes module. Backfire!

Appreciate your help.

Bob