Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy doesn't validation in a model referenced by a callback cause my original transaction to fail?
    primarykey
    data
    text
    <p>I have a model with an after_create callback. This callback causes a new record to be created in another model. However if a validation fails in the child record creation, the original transaction is still being saved.</p> <p>This doesn't seem right. According to Rails docs the whole thing is wrapped in a transaction. Am I doing something wrong?</p> <pre><code>class ServiceProvision &lt; ActiveRecord::Base has_one :cash_receipt after_create :receive_payment_for_service_provision, :if =&gt; Proc.new { |sp| sp.immediate_settlement == true } private def receive_payment_for_service_provision cash_account = CashAccount.find_by_currency_id_and_institution_id( self.currency_id, self.institution_id ) CashReceipt.create( :account_id =&gt; account.id, :service_provision_id =&gt; self.id, :amount =&gt; self.amount, :currency_id =&gt; self.currency.id, :cash_account_id =&gt; ( cash_account ? cash_account.id : nil ) ) end end class CashReceipt &lt; ActiveRecord::Base belongs_to :service_provision validates_presence_of :cash_account_id end </code></pre> <p>The CashReceipt does fail and returns an error when its passed nil for the cash_account_id, however my new ServiceProvision object is still being saved.</p> <pre><code>it "should fail if a cash account doesn't exist for the currency and institution" do currency = Factory.create( :currency ) institution = Factory.create( :institution ) service_provision = Factory.build( :service_provision, :currency_id =&gt; currency.id, :institution_id =&gt; institution.id, :immediate_settlement =&gt; true ) service_provision.save.should == false service_provision.should have( 1 ).error end 'ServiceProvision service provision creation should raise an error if a cash account doesn't exist for the currency and institution' FAILED expected: false, got: true (using ==) </code></pre> <p>This seems to contradict this from the docs</p> <blockquote> <p>Both Base#save and Base#destroy come wrapped in a transaction that ensures that whatever you do in validations or callbacks will happen under the protected cover of a transaction. So you can use validations to check for values that the transaction depends on or you can raise exceptions in the callbacks to rollback, including after_* callbacks.</p> </blockquote> <p>And if I manually try to cancel the transaction in the callback like so:</p> <pre><code>cr = CashReceipt.create( :account_id =&gt; account.id, :service_provision_id =&gt; self.id, :amount =&gt; self.amount, :currency_id =&gt; self.currency.id, :cash_account_id =&gt; ( cash_account ? cash_account.id : nil ) ) unless cr.errors.empty? errors.add_to_base("Error while creating CashReciept [#{cr.errors}].") return false end </code></pre> <p>then the new ServiceProvision object is still saved.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload