Hi, (no luck on the user forum so I'm hoping I can ask here)

I'm trying to get a simple cross-model business rule working.  In this case
the rule is (see below for models overview):
  * Rule = Sum(allocations amount, for a book) = Book Amount

ISSUE: The issue is in using after_create is that either the book or
allocation is saved before the other.  The only way I can see to make this
work is to have a check just prior to COMMIT, where all records are visible
within the DB and your final checks can be run (and rolled back if there is
problem).  Hence my question:

QUESTION: Is there an "before_commit" hook somewhere in Rails?  (or how else
would I satisfy my requirement)


----------------------------------------------------------------------------------
Macintosh-2:after_create_test greg$ spec spec/model/all_in_one_test_spec.rb
============= NEW TEST ======================
BOOK: after_save
F============= NEW TEST ======================
CHAPTER: after_save
.============= NEW TEST ======================
BOOK: after_save
.

1)
RuntimeError in 'Book should save if allocation amount == book amount'
amounts do NOT match
./spec/model/all_in_one_test_spec.rb:26:in `after_save_check'
./spec/model/all_in_one_test_spec.rb:51:

Finished in 0.061092 seconds

3 examples, 1 failure
----------------------------------------------------------------------------------

Macintosh-2:after_create_test greg$ cat -n
spec/model/all_in_one_test_spec.rb
     1  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
     2
     3  # ------------ ALLOCATION -------------
     4  class Allocation < ActiveRecord::Base
     5    belongs_to :book
     6    belongs_to :chapter
     7
     8    after_save :after_save_check
     9    def after_save_check
    10      puts "ALLOCATION: after_save"
    11      b = self.book
    12      sum = b.allocations.collect{|i| i.amount}.inject(0){|sum, n| sum
+ n }
    13      raise("amounts do NOT match") if !(b.amount == sum)
    14    end
    15  end
    16
    17  # ----------- BOOK ---------------
    18  class Book < ActiveRecord::Base
    19    has_many :allocations
    20    has_many :chapters, :through => :allocations
    21
    22    after_save :after_save_check
    23    def after_save_check
    24      puts "BOOK: after_save"
    25      sum = self.allocations.collect{|i| i.amount}.inject(0){|sum, n|
sum + n }
    26      raise "amounts do NOT match" if !(self.amount == sum)
    27    end
    28
    29  end
    30  # ----------- CHAPTER ---------------
    31  class Chapter < ActiveRecord::Base
    32    has_many :allocations
    33    has_many :books, :through => :allocations
    34
    35    after_save :after_save_check
    36    def after_save_check
    37      puts "CHAPTER: after_save"
    38    end
    39
    40  end
    41
    42  # --------- RSPEC (BOOK) ------------
    43  describe Book do
    44    before(:each) do
    45      puts "============= NEW TEST ======================"
    46      @b = Book.new(:amount => 100)
    47      @c = Chapter.new()
    48    end
    49
    50    it "should save if allocation amount == book amount" do
    51      @b.save!
    52      @c.save!
    53      Allocation.create!(:book_id => @b.id, :chapter_id => @c.id,
:amount => 100)
    54    end
    55
    56    it "should raise database exception if try to save allocation
prior to book" do
    57      lambda {
    58        @c.save!
    59        Allocation.create!(:book_id => @b.id, :chapter_id => @c.id,
:amount => 100)
    60        @b.save!
    61      }.should raise_error
    62    end
    63
    64    it "should raise error if allocation amount != book amount" do
    65      lambda {
    66        @b.save!
    67        @c.save!
    68        Allocation.create!(:book_id => @b.id, :chapter_id => @c.id,
:amount => 90)
    69      }.should raise_error
    70    end
    71
    72
    73  end
    74
----------------------------------------------------------------------------------
ActiveRecord::Schema.define(:version => 20090123000614) do

  create_table "allocations", :force => true do |t|
    t.integer  "book_id",    :null => false
    t.integer  "chapter_id", :null => false
    t.integer  "amount",     :null => false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "books", :force => true do |t|
    t.integer  "amount",     :null => false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "chapters", :force => true do |t|
    t.datetime "created_at"
    t.datetime "updated_at"
  end

end
----------------------------------------------------------------------------------












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