stack level too deep error crashed my app. I have a deposit model with two rows: income and cashout. This two rows modified by after_create callbacks in income and cashout models. In cashout model I want to use a before_create callback for validate a balance of deposit (deposit balance must be greater or equal to 0 after creating cashout) and receiving an error: SystemStackError in CashoutsController#create.
ps: Income callback work fine.
Codes:
deposit.rb
class Deposit < ActiveRecord::Base
belongs_to :user
has_many :incomes
has_many :cashouts
end
schema.rb
create_table "deposits", force: true do |t|
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.decimal "income", precision: 12, scale: 2
t.decimal "cashout", precision: 12, scale: 2, default: 0.0
end
cashout.rb
class Cashout < ActiveRecord::Base
belongs_to :deposit
validates :deposit_id, :order_sum, presence: true
validates :order_sum, numericality: true
validates :order_sum, numericality: { greater_than_or_equal_to: 0}
before_create :validate_order #before_save crashed too
after_create :update_deposit_cashout # working fine
private
def validate_order
@deposit = self.deposit
@income = @deposit.income
@cashout = self.order_sum
if @income - @cashout >= 0
self.save
else
!self.save
end
end
def update_deposit_cashout
@deposit = self.deposit
@deposit.update_attributes(:cashout => @cashout + self.order_sum)
end
end
cashouts_controller.rb
class CashoutsController < ApplicationController
before_action :signed_in_user, only: [:create]
def new
@cashout = @deposit.cashouts.build
end
def create
@deposit = current_user.deposit
@cashout = @deposit.cashouts.build(cashout_params)
@cashout.save
if @cashout.save
flash[:success] = "Your order request has been sent!"
redirect_to '/deposit'
else
flash[:error] = "Your order request hasn't been sent!"
redirect_to '/deposit'
end
end
def show
@deposit = Deposit.find(params[:id])
@deposit.cashout
end
private
def cashout_params
params.require(:cashout).permit(:order_sum)
end
end
income.rb
class Income < ActiveRecord::Base
belongs_to :deposit
validates :deposit_id, :order_sum, presence: true
validates :order_sum, numericality: true
validates :order_sum, numericality: { greater_than_or_equal_to: 0}
after_create :update_deposit_income
private
def update_deposit_income
@deposit = self.deposit
if @deposit.income == nil
@income = 0
else
@income = @deposit.income
end
@deposit.update_attributes(:income => @income + self.order_sum)
end
end
incomes_controller.rb
class IncomesController < ApplicationController
before_action :signed_in_user, only: [:create]
def new
@income = @deposit.incomes.build
end
def create
@user = current_user
@deposit = @user.deposit
@income = @deposit.incomes.build(income_params)
@income.save
if @income.save
flash[:success] = "Your order request has been sent!"
redirect_to '/deposit'
else
flash[:error] = "Your order request hasn't been sent!"
redirect_to '/deposit' if current_user.present?
end
end
def show
@deposit = Deposit.find(params[:id])
@deposit.income
end
private
def income_params
params.require(:income).permit(:order_sum)
end
end
Where is my mistake?
The stack level too deep error usually happens because your code is stuck in a loop: either a method calling itself, or two methods calling each other repeatedly.
In your case i think it's caused by the validate_order method. You are calling this in a before_save callback, and the method calls self.save, which will trigger the callback, which will call the method again, which will save again, which will trigger the callback etc etc.
Think about this for a moment - it doesn't make sense to save an object as part of it's "before_save" functionality does it?
What i suspect you want to do is to just validate the object, and if validation fails that will automatically block the save anyway. Something like this
validate :validate_order
def validate_order
@deposit = self.deposit
@income = @deposit.income
@cashout = self.order_sum
if @income - @cashout <= 0
self.errors.add(:deposit, "is too small")
end
end
This is just an example as i don't know what the actual logic of what you are testing is supposed to be.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With