yep - the magazine should have the total_cost field (slip when I typed in
the example).  Also you're last suggestion is good, but I was trying to
construct an easy example where it highlighted the multiple-model-spanning
validation brick wall I'm at.  So with this in mind, and assuming I use the
"validate" approach (c.f. after_create hook), I still have the same question
really?  So an example of the issue is:

i) assuming there is a validation routine in both Magazine and Article to
check business rules, that is:
   (a) for Magazine: Has to have an associated Article before successful
validation &
   (b) for Article: Has to have an associated Magazine before succesful
validation
ii) issue is that if I create a Magazine, the validation is then hit and
fails because I haven't yet created the Article (i.e. so they don't tie
together)
iii) if I create Article first to cover this then it fails because there
isn't a Magazine yet
iv) I could put the creation in a method like "create_magazine_article_pair"
and remove these above-mentioned validation checks, however this wouldn't
then protect against use of base methods (e.g. create/update/delete would
still be available as part of ActiveRecord)

Is there anyway out of this?  i.e. to end up with a very solid data-access
layer that doesn't allow for the business rules to be broken?

thanks

On Tue, Jan 13, 2009 at 1:11 PM, Andrew Timberlake <
[email protected]> wrote:

> On Tue, Jan 13, 2009 at 1:28 AM, Greg Hauptmann <
> [email protected]> wrote:
>
>> Hi,
>>
>> QUESTION: How can establishing validation that spans multiple models be
>> achieved in Rails?  That is in such a fashion that it is not possible for a
>> developer to break the validation via using any of the public model methods
>> (e.g. update_attribute, save, create etc).
>
>
> A developer can always break validation with something like
> save_with_validation(false)
>
>
>>
>>
>> EXAMPLE:
>> * Concept: MAGAZINE can contain multiple ARTICLES, and an ARTICLE can be
>> associated with multiple MAGAZINE (i.e. many to many).  Cost of Magazine =
>> Sum(Cost of Articles)
>> * Tables:
>>    (a) magazines (has "cost" field)
>>    (b) articles_magazines (to map the many-to-many)
>>    (c) articles (has "total_cost" field)
>
>
> Shouldn't the magazine have the total_cost field, and the article have a
> cost field?
>
>
>>
>>
>> BUSINESS RULE to be implemented:  Not possible for a database update that
>> would end up with a Magazine's "total_cost" not being equal to the
>> Sum(associated articles "cost"s)
>>
>> ISSUES / QUESTIONS:
>> (1) Assume would not try to implement rules at database constraint
>> level???
>> (2) Use of Model "before_create" - but I'm assuming here if the Article is
>> generated (Article.new), and then validation occurs, the code has NOT yet
>> got to the bit where it updates the Magazine?
>> (3) Use of "after_create" - Add a check for both Magazine and Article
>> perhaps here, noting the database record has been created by transaction NOT
>> finalised yet.   So would the following be the best way:
>>
>> -----example-------
>> class Magazine < ActiveRecord::Base
>>   after_save :business_rule_validation
>>   def business_rule_validation
>>     sum_of_articles = << INSERT code that calculates SUM of Articles costs
>> for all articles that are associated with the Magazine >>
>>     errors.add_to_base("business rules fail") if self.total_cost !=
>> sum_of_articles
>>   end
>> end
>
>
> The after_save callback is not for validation - see
> http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
> If you must validate use the validate method or and do your calculations
> and validation in there.
>
>
>>
>> class Article < ActiveRecord::Base
>>   << Add Same Concept as per Magazine >>
>> end
>> -----example-------
>>
>> BUT wouldn't this fail, as it assumes the Article create/update/delete and
>> the Magazine create/update/delete is in the SAME transaction no???   Does
>> this mean you really have to create an overarching facade that handles
>> creates/updates/deletes for Article/Magazines and somehow hide the normal
>> per model save/update/delete???
>>
>> --
>> Greg
>> http://blog.gregnet.org/
>>
>>
>>
>>
>>
> Instead of storing the total cost and doing all this validation, I'd
> calculate the magazine total_cost as needed
>     class Magazine < ActiveRecord::Base
>       def total_cost
>         articles.to_a.sum(&:cost)
>       end
>     end
>
> --
> Andrew Timberlake
> http://ramblingsonrails.com
> http://www.linkedin.com/in/andrewtimberlake
>
> "I have never let my schooling interfere with my education" - Mark Twain
>
> >
>


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

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Talk" 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-talk?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to