A rollback is not issued when dependent object before_destroy callback returns false

Hi,

I am wondering whether this is the designed behavior of ActiveRecord,
when,
to me, it is not the expected one.

I have a simple model design with an Order having many Order Items and
an
Order Item belonging to an Order. The has_many association on Order has
a
:dependent => :destroy option.
Also, the Order Item has a before_destroy function that would check the
price of the Order Item. If the price is above 100, it returns false and
does not allow the Order Item to be destroyed.

The classes codes has as follows:

class Order < ActiveRecord::Base
attr_accessible :order_code
has_many :order_items, :inverse_of => :order, :dependent => :destroy
end

class OrderItem < ActiveRecord::Base
attr_accessible :price, :order_id

belongs_to :order, :inverse_of => :order_items

before_destroy :low_price

protected

def low_price
if price <= 100
puts “I have low price…I can be deleted”
true
else
puts “I have high price…I cannot be deleted”
false
end
end
end

The problem is that when I am issuing a destroy on an instance of an
order
that has an order item that has price > 100, then the order item is not
deleted, but the order does. Here are the console commands and output:

rails c
Loading development environment (Rails 3.2.8)
1.9.3-p194 :001 > o = Order.new :order_code => ‘first order’
=> #<Order id: nil, order_code: “first order”, created_at: nil,
updated_at: nil>
1.9.3-p194 :002 > o.save!
(0.1ms) BEGIN
SQL (0.2ms) INSERT INTO orders (created_at, order_code,
updated_at) VALUES (‘2012-08-20 08:14:20’, ‘first order’, ‘2012-08-20
08:14:20’)
(2.0ms) COMMIT
=> true
1.9.3-p194 :003 > o.order_items << OrderItem.new(:price => 110)
(0.1ms) BEGIN
SQL (0.3ms) INSERT INTO order_items (created_at, order_id,
price, updated_at) VALUES (‘2012-08-20 08:14:31’, 5, 110,
‘2012-08-20
08:14:31’)
(0.5ms) COMMIT
OrderItem Load (0.3ms) SELECT order_items.* FROM order_items
WHERE
order_items.order_id = 5
=> [#<OrderItem id: 4, order_id: 5, price: 110, created_at: “2012-08-20
08:14:31”, updated_at: “2012-08-20 08:14:31”>]
1.9.3-p194 :004 > o.destroy
(0.2ms) BEGIN
I have high price…I cannot be deleted
SQL (1.0ms) DELETE FROM orders WHERE orders.id = 5
(1.8ms) COMMIT
=> #<Order id: 5, order_code: “first order”, created_at: “2012-08-20
08:14:20”, updated_at: “2012-08-20 08:14:20”>
1.9.3-p194 :005 >

Can somebody explain to me, why the failure of the dependent object
destroy
method does not trigger a rollback?

Thanks in advance
Panayotis

Why would you want to return false from a callback and still destroy it?
I
think you might try to squeeze two different concepts into a single
method
definition: if you want to check whether or not price is lower than
something and actually USE that information somewhere else, then just
create another method “low_price?”, and you can use it in “low_price”
callback:

def low_price
if low_price?
# do something before the record is destroyed
end
true
end

def low_price?
price <= 100
end

2012/8/20 Panayotis M. [email protected]

Sorry should read.

2012/8/21 Gintautas Šimkus [email protected]

Thanks for your answer. See my comments below

On Tuesday, August 21, 2012 7:34:34 AM UTC+3, Dihital wrote:

Why would you want to return false from a callback and still destroy it?

No. I do not want. I think you missed the point of my post. In the
example
that I have written the Order Item IS NOT CORRECTLY deleted, because it
has
high price. The PROBLEM is that the OWNING Order is deleted, when it
shouldn’t.

Please, consider this as an example of an owner (Order in the example)
being deleted when it shouldn’t (because of the fact that the owned item
(Order Item in the example) is not deleted). Do not stick too much on
the
business value of the example.
There may not be much, if any.

Panayotis