ทับทิมบนราง. ข้อผิดพลาดระดับสแต็กลึกเกินไป

ข้อผิดพลาดระดับสแต็กลึกเกินไปทำให้แอปของฉันขัดข้อง ฉันมีรูปแบบการฝากเงินสองแถว: รายได้และการรับเงิน สองแถวนี้แก้ไขโดย after_create การโทรกลับในรูปแบบรายได้และการจ่ายเงิน ในรูปแบบการจ่ายเงิน ฉันต้องการใช้การเรียกกลับ before_create เพื่อตรวจสอบยอดเงินฝาก (ยอดเงินฝากจะต้องมากกว่าหรือเท่ากับ 0 หลังจากสร้างการจ่ายเงิน) และได้รับข้อผิดพลาด: SystemStackError ใน CashoutsController#create

PS: การโทรกลับรายได้ทำงานได้ดี

รหัส:

ฝาก.rb

class Deposit < ActiveRecord::Base
    belongs_to :user
    has_many :incomes
    has_many :cashouts
end

สคีมา.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

รายได้.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

ความผิดพลาดของฉันอยู่ที่ไหน?


person anndrew78    schedule 17.08.2015    source แหล่งที่มา


คำตอบ (1)


ข้อผิดพลาด stack level too deep มักจะเกิดขึ้นเนื่องจากโค้ดของคุณติดอยู่ในลูป: อาจเป็นวิธีการที่เรียกตัวเอง หรือสองวิธีที่เรียกกันและกันซ้ำๆ

ในกรณีของคุณ ฉันคิดว่ามันเกิดจากวิธี validate_order คุณกำลังเรียกสิ่งนี้ด้วยการเรียกกลับ before_save และเมธอดเรียก self.save ซึ่งจะทริกเกอร์การเรียกกลับ ซึ่งจะเรียกเมธอดอีกครั้ง ซึ่งจะบันทึกอีกครั้ง ซึ่งจะทริกเกอร์การเรียกกลับ ฯลฯ

ลองคิดดูสักครู่ - มันไม่สมเหตุสมผลเลยที่จะบันทึกวัตถุให้เป็นส่วนหนึ่งของฟังก์ชัน "before_save" ใช่ไหม

สิ่งที่ฉันสงสัยว่าคุณต้องการทำคือเพียงตรวจสอบความถูกต้องของวัตถุ และหากการตรวจสอบล้มเหลว ระบบจะบล็อกการบันทึกโดยอัตโนมัติต่อไป บางอย่างเช่นนี้

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

นี่เป็นเพียงตัวอย่างเนื่องจากฉันไม่รู้ว่าตรรกะที่แท้จริงของสิ่งที่คุณกำลังทดสอบควรจะเป็นอย่างไร

person Max Williams    schedule 17.08.2015