In your previous examples, the validation was getting called twice  
because you *defined* it twice;
once in Allocation and once in Book. The preferred idiom in Rails is  
to define it once, and be careful
how you use the objects. Since your rule won't allow Allocations or  
Books to be updated independently,
it seems logical that there will be (roughly) one place in the code  
that does the update.

AR doesn't automatically save associated records, but it's easy enough  
to do it yourself.

Your models would end up looking like this:

# ------------ ALLOCATION -------------
class Allocation < ActiveRecord::Base
   belongs_to :book
   belongs_to :chapter
   # note no validation here
end

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

   before_save :check_totals
   def check_totals
     sum = self.allocations.sum(:amount)
     if !(self.amount == sum)
       self.errors.add :amount, 'amounts do not match'
       false
     end
   end

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

With a controller action like this:

def update_stuff
   @book = ... # get book instance
   # do stuff to @book.allocations - don't use update_attributes, as  
it will save the allocations
   if @book.save
     # success
   else
     # something went wrong
   end
end

The false return value from check_totals will rollback the whole  
implicit transaction that book.save is enclosed in
if the totals don't match up.

I'm not sure what the concern about ActiveScaffold is about - I  
haven't looked at it in a lot of depth, but I doubt that
it supports the kind of multi-model form you'll need to update the  
records the way you plan to. As it stands, it wouldn't
be possible to update either a Book or an Allocation independently.

The concept of "trusted" access methods is somewhat useless; if you  
don't trust the code in your controllers, you have
a whole other problem. Even if ironclad validations could be set up,  
all it takes is a call to save(false) to get past them...

--Matt


On Jan 25, 2009, at 4:56 PM, Greg Hauptmann wrote:

> BTW - I've had an indication after posting on a mysql forum site  
> that MySql does not provide a "deferred constraints" feature, whilst  
> apparently Oracle and Postgres do.   From what I gather this would  
> have been useful as I could have put the business logic check in the  
> database I think, to be only run after all statements had occurred,  
> but before the final commit.    Implication is that MySql won't  
> provide the solution so it would have to be in Rails if at all.
>
> At this stage I'd be happy to assume I can not get a fully robust  
> solution to cross-model validation checks in Rails.  What at least  
> would be good is to be able to get to the concept that:
>       • Data Access Layer - That is model classes and base ActiveRecord  
> methods for models:  These do not provide any protection  
> themselves.  If a developer uses the normal ActiveRecord calls (e.g.  
> save, update etc) then it's up to them to get it right, however they  
> would be encouraged not to use this layer directly but use the  
> "Service Layer"
>       • Service Layer - Provides methods to use the models/tables that  
> have cross-model business rules.  Basically these are the "trusted"  
> methods that will respect the business rules (noting it's not  
> possible it seems to have Rails provide the robust solution).  For  
> example:
>               • update_chapters - would update all chapters first (no model  
> validation calls would be firing), then at the end perform the  
> business logic check
>       • Controller Layer - calls the service layer
> Implication here is that one may not be able to use the normal tools  
> like ActiveScaffold which automatically gives you maintenance pages  
> for all the models, as it would be hooking into the Data Access  
> Layer directly and therefore wouldn't adhere to Business Logic check..
>
> How does this sound?  Probably the best I can do?
>

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