thanks stephen - I think (haven't tested it) the problem here would still be
called a line 1 (see below) of my use case.  So whilst it would trigger
validation for chapter in a non-looping sense, the problem is that until
Chapter & the Allocation are saved to DB the validation will fail.  Make
sense?  Hence why I was interested in an a "before_final_commit" type hook.
---------------------------
1  @b.save!  # SEEMS TO TRIGGER BOOK AFTER_SAVE HERE RATHER THAN HOLDING OFF
2  @c.save!
3  @a1 = Allocation.create!(:book_id => @b.id, :chapter_id => @c.id, :amount
=> 100)
---------------------------





On Mon, Jan 26, 2009 at 7:14 AM, stephen paul suarez <[email protected]>wrote:

> hmmm, i have an idea for you but i think it's a bit hacky.. i've put the
> check on validation since i think it mostly relates to validating your
> models..# ----------- Allocation ---------------
> class Allocation < ActiveRecord::Base
>    belongs_to :book
>    belongs_to :chapter
>
>    validate :check
>
>    def check
>      #this will always be true.. this just ensures that
>      return @check if @check
>  @check = true
>      self.book.valid?
>    end
> end
>
> # ----------- BOOK ---------------
> class Book < ActiveRecord::Base
>   has_many :allocations
>   has_many :chapters, :through => :allocations
>
>   validates_associated :allocations
>   validate :check
>
>   def check
>     puts "BOOK: after_save"
> #reload associated allocations always
>     sum = self.allocations(true).map(&:amount).sum
>  errors.add("amount","do NOT match") unless self.amount == sum
>   end
> end
>
>
> this is a bit tricky, since Allocation#save will try to call on
> Book#valid?, the validates_associated will in turn try to call
> Allocation#valid? once again, but the second time the code executes on
> Allocation#check, the validation will just return true.. hth
>
> --stephen
>
>
> On Sun, Jan 25, 2009 at 1:35 PM, Greg Hauptmann <
> [email protected]> wrote:
>
>> 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/
>>
>>
>>
>>
>>
>
> >
>


-- 
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