2016-03-22 2 views
0

Моя User модель has_oneAccount, которая имеет столбец balance.Обеспечить минимальный баланс 0 в десятичной колонке

Наблюдайте:

> alice = User.first 
=> #<User id: 1, charge_fee: nil, created_at: "2016-03-19 20:15:14", updated_at: "2016-03-19 20:15:14"> 
> bob = User.second 
=> #<User id: 2, charge_fee: nil, created_at: "2016-03-19 20:15:18", updated_at: "2016-03-19 20:15:18"> 
> alice.account.balance.to_s 
=> "50.0" 
> bob.account.balance.to_s 
=> "50.0" 

Когда один передает пользовательские деньги на другой, баланс отправителя уменьшается, и увеличивается на ресивере:

> alice.account.transfer(to: bob.account, amount:25) 
=> nil 
> alice.account.balance.to_s 
=> "25.0" 
> bob.account.balance.to_s 
=> "75.0" 

Система идеально подходит, за исключением того, что пользователи могут передавать больше денег чем у них, и их баланс переходит в отрицательный:

> alice.account.transfer(to:bob.account, amount:50) 
=> nil 
> alice.account.balance.to_s 
=> "-25.0" 
> bob.account.balance.to_s 
=> "125.0" 

Как сделать принудительно выполнить его так, чтобы команда Account.decrement!('balance', amount) не удалась, если account.balance опустился ниже 0?

Полный источник этой заявки можно найти here. Фактическая increment! и decrement! происходит here, вот выдержка:

Account.transaction do 
    Transaction.create(transaction) 
    transaction[:to].increment!('balance', transaction[:amount]) 
    transaction[:from].decrement!('balance', transaction[:amount]) 

    Transaction.create(fee_transaction) if transaction[:transfer] 
end 

ответ

0

неудачу 'Баланс не может быть отрицательным', если сделка [: от] .balance < BigDecimal ('0') & & сделки [: от] .мета? == false

+0

Wow, ummm .. ваше форматирование отстой, но это точно строка кода, которую мне нужно было вставить в транзакцию! Спасибо. Я объясню другим людям: Я делаю транзакцию [здесь] (https://github.com/amingilani/rails-bank-app/blob/master/app/models/account.rb#L55). Этот код не выполняет транзакцию, если это не мета-учет (например, депозит, нормальный баланс которого отрицательный). @waleedvic, вы действительно прочитали всю кодовую базу, чтобы выяснить мета-флаг или что-то еще? – amingilani

2

Так что проблема в том, что decrement! skips validations. Чтобы получить нужное поведение, вам нужно будет написать что-то вроде этого. Один простой способ - подтвердить, что balance не менее 0 и использовать update_attributes для установки новой суммы. Вы можете использовать ActiveRecord::Base.transaction и reload для обеспечения атомарности.

class Account < ActiveRecord::Base 
    validates :balance, numericality: { greater_than_or_equal_to: 0 } 

    def withdraw!(amount) 
    transaction do 
     reload 
     update_attributes!(balance: balance - amount) 
    end 
    end 
end 

Вы также можете сделать проверку непосредственно в методе withdraw!, если у вас есть какая-то причина, чтобы не использовать валидации Rails' (может быть, иногда люди могут преувеличивать их расчетный счет). Затем вы можете использовать decrement!, если хотите.

class Account < ActiveRecord::Base 
    def withdraw!(amount) 
    transaction do 
     reload 
     raise 'Insufficient funds' unless balance >= amount 
     decrement!('balance', amount) 
    end 
    end 
end 

В зависимости, что еще происходит, может быть, вы можете пропустить reload, чтобы избежать дополнительных запросов к базе данных, до тех пор, пока вы уверены, что вы имеете последнюю дату записи.

+0

Благодарю вас, однако, я также обновлю вопрос, фактические «приращения» и «декремент» были сделаны в одной транзакции [здесь] (https://github.com/amingilani/rails-bank- app/blob/master/app/models/account.rb # L55), и, хотя ваш код работает, отмеченный ответ на самом деле обеспечивает способ отказа транзакции, если средства слишком низки. Это ACID, потому что он по-прежнему использует функцию «декремент!» И проверяет, является ли учетная запись мета-учетной записью (например, депозиты, которые будут иметь нормальный баланс в отрицательном выражении), другой ответ обращается к обоим из них с кодовой базой. – amingilani

Смежные вопросы