Hi Matt/all,
Actually from what I can see Rails does not hold off on trigger it's
"after_save" callbacks until just before commit in the case they are wrapped
in a specific transaction (unfortunately).   So there's still no
"before_commit" equivalent yet anyone has identified.  Let me know if I'm
wrong however here's the test I've run.


------------------- test output
-------------------------------------------------
Macintosh-2:after_create_test greg$ spec
spec/model/with_transaction_block.rb
BOOK: before_save
F

1)
RuntimeError in 'Book should allow creation of book-allocation-chapter if
costs match'
amounts do NOT match
./spec/model/with_transaction_block.rb:27:in `after_save_check'
./spec/model/with_transaction_block.rb:57:
./spec/model/with_transaction_block.rb:56:

Finished in 0.048817 seconds

1 example, 1 failure
Macintosh-2:after_create_test greg$

--------------- spec
------------------------------------------------------------------
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

# ------------ ALLOCATION -------------
class Allocation < ActiveRecord::Base
  belongs_to :book
  belongs_to :chapter

  before_save :after_save_check
  def after_save_check
    puts "ALLOCATION: before_save"
    b = self.book
    sum = b.allocations.sum(:amount)
    raise("amounts do NOT match") if !(b.amount == sum)
  end
end

# ----------- BOOK ---------------
class Book < ActiveRecord::Base
  has_many :allocations
  has_many :chapters, :through => :allocations

  before_save :after_save_check
  def after_save_check
    puts "BOOK: before_save"
    sum = self.allocations.sum(:amount)
    raise "amounts do NOT match" if !(self.amount == sum)
  end

end
# ----------- CHAPTER ---------------
class Chapter < ActiveRecord::Base
  has_many :allocations
  has_many :books, :through => :allocations

  before_save :after_save_check
  def after_save_check
    puts "CHAPTER: before_save"
  end

end

# --------- RSPEC (BOOK) ------------
describe Book do
  before(:each) do
    @b = Book.new(:amount => 100)
    @c = Chapter.new()
  end

  it "should allow creation of book-allocation-chapter if costs match" do
    Book.transaction do
      @b.save!  # SEEMS TO TRIGGER BOOK AFTER_SAVE HERE RATHER THAN HOLDING
OFF
      @c.save!
      @a1 = Allocation.create!(:book_id => @b.id, :chapter_id => @c.id,
:amount => 100)
    end
  end

end

-------mysql log
-----------------------------------------------------------------
090125 15:31:46    1534 Connect     r...@localhost on after_create_test_test
                   1534 Query       SET SQL_AUTO_IS_NULL=0
                   1534 Statistics
                   1534 Query       SHOW FIELDS FROM `books`
                   1534 Query       SHOW FIELDS FROM `chapters`
                   1534 Query       BEGIN
                   1534 Query       SHOW FIELDS FROM `allocations`
                   1534 Query       SELECT sum(`allocations`.amount) AS
sum_amount FROM `allocations` WHERE (`allocations`.book_id = NULL)
                   1534 Query       ROLLBACK
                   1534 Quit



Is my analysis correct?

Cheers
Greg

On Sun, Jan 25, 2009 at 10:08 AM, Matt Jones <[email protected]> wrote:

>
> There's no explicit hook, but you can pretty much do what you've
> described using transactions.
> If you're updating the chapters in a single controller action, you can
> use a transaction block
> (see
> http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
> )
>  to wrap all
> the changes. Then, either use an after_save on Book, or just call a
> method directly to validate the
> combination.
>
> You'll want to use save! and friends within the block, and catch
> exceptions (ActiveRecord::RecordInvalid and
> ActiveRecord::RecordNotSaved) to display errors.
>
> --Matt Jones
>
>
> On Jan 24, 2009, at 6:44 PM, Greg Hauptmann wrote:
>
> > Hi Mike, all
> >
> > Understood.  To help align my fictitious example to the cross-model
> > validation question I've asked consider that:
> > (a) the book value is fixed [e.g. perhaps think of this as a bank
> > account transaction amount, being allocated out to different tax
> > categories & then the user wants to adjust the tax categories]
> > (b) the user manually adjusts the chapter value (i.e. there is no
> > programmatic approach to calculating the distribution)
> >
> > So this brings it back to my scenario I'm not sure how to solve in
> > Rails whereby the sequence of events here would be:
> >   - change chapter 1 value
> >   - change chapter 2 value
> >   - change chapter 3 value
> >   - <only at this point should the cross model business rule be
> > checked, i.e. Book.amount.should == Sum(chapter values)>
> >
> > My assumption here (correct me if I'm wrong) is that any Rails
> > validation/after_save/observer kicks in at such of the sequence
> > points, whereas what is actually required here is a cross_model
> > business logic check at the end.
> >
> > Does this make sense?   Is there a ways in Rails to get access to a
> > "before_commit" type hook that would align with the point I want the
> > business logic check to kick in?
> >
> > Thanks
> >
>
>
> >
>


-- 
Greg
http://blog.gregnet.org/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Core" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/rubyonrails-core?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to