Greg Hauptmann
2009-Jan-12 23:28 UTC
Validation spanning multiple models(tables) - how can this be achieved in Rails?
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). 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) 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 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/ --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Andrew Timberlake
2009-Jan-13 03:11 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
On Tue, Jan 13, 2009 at 1:28 AM, Greg Hauptmann < greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 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 > endThe 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 --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Greg Hauptmann
2009-Jan-13 06:15 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
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 < andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote:> On Tue, Jan 13, 2009 at 1:28 AM, Greg Hauptmann < > greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Andrew Timberlake
2009-Jan-13 09:12 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
On Tue, Jan 13, 2009 at 8:15 AM, Greg Hauptmann < greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> 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 < > andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote: > >> On Tue, Jan 13, 2009 at 1:28 AM, Greg Hauptmann < >> greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 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/ > > > > > >Nice challenge, I''ve never had to do this (and am still not convinced of why you would need to - I would usually be happy with something like a magazine being created with no articles and then have the articles added later) but anyway, here''s a solution: You (can''t) do it with the standard association helpers (that I can work out) but I solved it by creating an add_article method that stores to-be-saved articles in an array and then checks both that array and the association on validation. Once the magazine is saved, it takes care of saving the articles which will validate because they have a magazine. class Magazine < ActiveRecord::Base has_and_belongs_to_many :articles after_save :save_articles def initialize(*args) super(*args) @article_array = [] end def total_cost articles.to_a.sum(&:cost) end def add_article(article) @article_array << article end private def validate errors.add(:title, "can''t have no articles") if articles.size == 0 && @article_array.size == 0 end def save_articles @article_array.each do |article| article.magazines << self article.save! end end end class Article < ActiveRecord::Base has_and_belongs_to_many :magazines def validate errors.add(:title, "can''t have no magazines") if magazines.size == 0 end end class MagazineTest < ActiveSupport::TestCase test "magazine can''t be saved with no articles" do assert_raise ActiveRecord::RecordInvalid do Magazine.create!(:title => ''Test Magazine'') end end test "magazine can be save with articles" do magazine = Magazine.new(:title => ''Test Magazine'') article = Article.new(:title => ''Test Article'', :cost => 20) magazine.add_article article assert_nothing_thrown do magazine.save! end assert !magazine.new_record? assert !article.new_record? end end class ArticleTest < ActiveSupport::TestCase test "article can''t be saved with no magazines" do assert_raise ActiveRecord::RecordInvalid do Article.create!(:title => ''Test Article'', :cost => 20) end 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 --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Greg Hauptmann
2009-Jan-13 12:24 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
thanks for pondering this one with me Andrew - I''ll need to think about this tomorrow :) , couple of off-the-cuff comments: * very neat * just wondering if this will work for multiple magazines linked to one article (i.e. many-to-many) * do you think there''s no way to solve this without creating a new method in fact (like your "add_article")? * one thing that this has made me realize is that I was also thinking of/assuming that my validation checks would be database based (e.g. search database to see result), but by working in the object world this helps remove the inherit database transaction/commits only approach where I was getting stuck seeing how to do it regards Greg On Tue, Jan 13, 2009 at 7:12 PM, Andrew Timberlake < andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote:> On Tue, Jan 13, 2009 at 8:15 AM, Greg Hauptmann < > greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > >> 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 < >> andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote: >> >>> On Tue, Jan 13, 2009 at 1:28 AM, Greg Hauptmann < >>> greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 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/ >> >> >> >> >> > Nice challenge, I''ve never had to do this (and am still not convinced of > why you would need to - I would usually be happy with something like a > magazine being created with no articles and then have the articles added > later) but anyway, here''s a solution: > > You (can''t) do it with the standard association helpers (that I can work > out) but I solved it by creating an add_article method that stores > to-be-saved articles in an array and then checks both that array and the > association on validation. Once the magazine is saved, it takes care of > saving the articles which will validate because they have a magazine. > > class Magazine < ActiveRecord::Base > has_and_belongs_to_many :articles > after_save :save_articles > > def initialize(*args) > super(*args) > @article_array = [] > end > > def total_cost > articles.to_a.sum(&:cost) > end > > def add_article(article) > @article_array << article > end > > private > def validate > errors.add(:title, "can''t have no articles") if articles.size == 0 && > @article_array.size == 0 > end > > def save_articles > @article_array.each do |article| > article.magazines << self > article.save! > end > end > end > > class Article < ActiveRecord::Base > has_and_belongs_to_many :magazines > > def validate > errors.add(:title, "can''t have no magazines") if magazines.size == 0 > end > end > > class MagazineTest < ActiveSupport::TestCase > test "magazine can''t be saved with no articles" do > assert_raise ActiveRecord::RecordInvalid do > Magazine.create!(:title => ''Test Magazine'') > end > end > > test "magazine can be save with articles" do > magazine = Magazine.new(:title => ''Test Magazine'') > article = Article.new(:title => ''Test Article'', :cost => 20) > magazine.add_article article > > assert_nothing_thrown do > magazine.save! > end > > assert !magazine.new_record? > assert !article.new_record? > end > end > > class ArticleTest < ActiveSupport::TestCase > test "article can''t be saved with no magazines" do > assert_raise ActiveRecord::RecordInvalid do > Article.create!(:title => ''Test Article'', :cost => 20) > end > > 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Andrew Timberlake
2009-Jan-13 13:11 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
Greg On Tue, Jan 13, 2009 at 2:24 PM, Greg Hauptmann < greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> thanks for pondering this one with me Andrew - I''ll need to think about > this tomorrow :) , couple of off-the-cuff comments: > * very neat >Thank you> > * just wondering if this will work for multiple magazines linked to one > article (i.e. many-to-many) >It does but I only focussed on the magazine side as with the magazine/article relationship it''s more likely that you''ll create a magazine and add articles than create an article and add magazines. You can do it though, you''ll just need to implement an add_magazine method to the article model The validations definitely work both ways, check the tests.> > * do you think there''s no way to solve this without creating a new method > in fact (like your "add_article")? >I played around with the various collection methods but each of them tries to save a model at some point where the other isn''t saved and then the validations will fail. Also most of the association methods rely on there being an id in the association model (i.e. it must be saved). My solution doesn''t require either model to be saved and handles the saving of children when needed. My solution will also continue to work after creation if you''re updating deleting children etc. E.g. You don''t have to use the add_article method once you have at least one article saved with a link to the magazine. You could do: magazine.add_article article magazine.save! magazine.articles << Article.new(...)> * one thing that this has made me realize is that I was also thinking > of/assuming that my validation checks would be database based (e.g. search > database to see result), but by working in the object world this helps > remove the inherit database transaction/commits only approach where I was > getting stuck seeing how to do it >You could deal with it more in the database but then you would lose the abstraction of the models and you would need to couple more tightly to the database.> regards > Greg >-- Andrew Timberlake http://ramblingsonrails.com http://www.linkedin.com/in/andrewtimberlake "I have never let my schooling interfere with my education" - Mark Twain --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Greg Hauptmann
2009-Jan-15 00:21 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
I was thinking that one generic approach to handle cross model validations could be: [1] VALIDATE AT OBJECT LEVEL: Validate your specific cross model rules at the Rails object level (i.e. before a "save" using the Rails validate) - have a "model objects array" to add each associated model object that was part of the validation [2] ENSURE ALL MODELS THAT WERE PART OF VALIDATION ARE SAVED: In each models "after_save" then check it''s list of "model objects array" to ensure each is actually saved, then if not either save all, or if there is an issue then issue manual Rollback so that all items are rolled back. Probem: I''m noting that re [1] and validating at the object level, if I allocate a book to a chapter, whilst the "chapter.book" works, the call "book_object.chapters" does not work??? Any way around this or is this a rails thing? i.e. my concept was in the object world the links should have been in place. Example: b = Book.new c = Chapter.new c.book = b c.book ==> works and gives b object b.chapters ==> DOES NOT WORK - gives [] tks On Tue, Jan 13, 2009 at 11:11 PM, Andrew Timberlake < andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote:> Greg > > On Tue, Jan 13, 2009 at 2:24 PM, Greg Hauptmann < > greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > >> thanks for pondering this one with me Andrew - I''ll need to think about >> this tomorrow :) , couple of off-the-cuff comments: >> * very neat >> > > Thank you > > >> >> * just wondering if this will work for multiple magazines linked to one >> article (i.e. many-to-many) >> > > It does but I only focussed on the magazine side as with the > magazine/article relationship it''s more likely that you''ll create a magazine > and add articles than create an article and add magazines. > You can do it though, you''ll just need to implement an add_magazine method > to the article model > The validations definitely work both ways, check the tests. > > >> >> * do you think there''s no way to solve this without creating a new method >> in fact (like your "add_article")? >> > > I played around with the various collection methods but each of them tries > to save a model at some point where the other isn''t saved and then the > validations will fail. Also most of the association methods rely on there > being an id in the association model (i.e. it must be saved). > My solution doesn''t require either model to be saved and handles the saving > of children when needed. > My solution will also continue to work after creation if you''re updating > deleting children etc. E.g. You don''t have to use the add_article method > once you have at least one article saved with a link to the magazine. > You could do: > magazine.add_article article > magazine.save! > magazine.articles << Article.new(...) > > >> * one thing that this has made me realize is that I was also thinking >> of/assuming that my validation checks would be database based (e.g. search >> database to see result), but by working in the object world this helps >> remove the inherit database transaction/commits only approach where I was >> getting stuck seeing how to do it >> > > You could deal with it more in the database but then you would lose the > abstraction of the models and you would need to couple more tightly to the > database. > > >> regards >> Greg >> > > > -- > 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Greg Hauptmann
2009-Jan-15 05:46 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
PS. Here''s an update where I''m at if anyone whats to comment. Not quite finished however I''m wondering now if I ensure solid validation level checks in model validations (e.g. both ends of an association are working, means have to set both ends manually) that I could then rely on Rails to actually save both ends of an association even if you only save one. (e.g. seems when I save book, chapter also gets saved, and vice-versa). Here''s where I''m at: ---------------------------------------------------------- require File.expand_path(File.dirname(__FILE__) + ''/../spec_helper'') # ----------- BOOK --------------- class Book < ActiveRecord::Base has_many :chapters def validate if self.chapters.length == 0 errors.add_to_base("Book does not have an associated Chapter") end end end # ----------- CHAPTER --------------- class Chapter < ActiveRecord::Base belongs_to :book def validate if !self.book errors.add_to_base("Chapter does not have an associated Book") end end end # --------- RSPEC (BOOK) ------------ describe Book do before(:each) do @valid_attributes = {:amount => 100} @b = Book.new(:amount => 100) @c = Chapter.new(:amount => 100) end it "should save without error when association is in place" do @c.book = @b @b.chapters = [@c] @b.save! @c.save! end it "should delete without error when both ends of association are deleted" do @c.book = @b @b.chapters = [@c] @b.save! @c.save! @c.destroy @b.destroy end it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do lambda{ @b.save! }.should raise_error end it "should fail for SAVE! if there is no association (DATABASE LEVEL)" do @c.book = @b @b.chapters = [@c] lambda{ @b.save! }.should_not raise_error #on basis that Rails will save Chapter automatically end it "should fail for DELETE if one side left open" do @c.book = @b @b.chapters = [@c] @b.save! @c.save! lambda{ @b.destroy }.should raise_error end end # ----------RSPEC (CHAPTER) -------------------- describe Chapter do before(:each) do @valid_attributes = {:amount => 100} @b = Book.new(:amount => 100) @c = Chapter.new(:amount => 100) end it "should save without error when association is in place" do @c.book = @b @b.chapters = [@c] @b.save! @c.save! end it "should delete without error when both ends of association are deleted" do @c.book = @b @b.chapters = [@c] @b.save! @c.save! @c.destroy @b.destroy end it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do lambda{ @c.save! }.should raise_error end it "should fail for SAVE! if there is no association (DATABASE LEVEL)" do @c.book = @b @b.chapters = [@c] lambda{ @c.save! }.should_not raise_error # on basis that Rails will automatically save Book end it "should fail for DELETE if one side left open" do @c.book = @b @b.chapters = [@c] @b.save! @c.save! lambda{ @c.destroy }.should raise_error end end ---------------------------------------------------------- $ ./script/autospec loading autotest/rails_rspec /opt/local/bin/ruby /opt/local/lib/ruby/gems/1.8/gems/rspec-1.1.12/bin/spec spec/models/all_in_one_test_spec.rb -O spec/spec.opts F......... 1) ''Chapter should fail for DELETE if one side left open'' FAILED expected Exception but nothing was raised ./spec/models/all_in_one_test_spec.rb:114: Finished in 0.280484 seconds 10 examples, 1 failure ---------------------------------------------------------- On Thu, Jan 15, 2009 at 10:21 AM, Greg Hauptmann < greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> I was thinking that one generic approach to handle cross model validations > could be: > > [1] VALIDATE AT OBJECT LEVEL: Validate your specific cross model rules at > the Rails object level (i.e. before a "save" using the Rails validate) > - have a "model objects array" to add each associated model object that > was part of the validation > > [2] ENSURE ALL MODELS THAT WERE PART OF VALIDATION ARE SAVED: In each > models "after_save" then check it''s list of "model objects array" to ensure > each is actually saved, then if not either save all, or if there is an issue > then issue manual Rollback so that all items are rolled back. > > Probem: I''m noting that re [1] and validating at the object level, if I > allocate a book to a chapter, whilst the "chapter.book" works, the call > "book_object.chapters" does not work??? Any way around this or is this a > rails thing? i.e. my concept was in the object world the links should have > been in place. Example: > b = Book.new > c = Chapter.new > c.book = b > c.book ==> works and gives b object > b.chapters ==> DOES NOT WORK - gives [] > > tks > > > On Tue, Jan 13, 2009 at 11:11 PM, Andrew Timberlake < > andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote: > >> Greg >> >> On Tue, Jan 13, 2009 at 2:24 PM, Greg Hauptmann < >> greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: >> >>> thanks for pondering this one with me Andrew - I''ll need to think about >>> this tomorrow :) , couple of off-the-cuff comments: >>> * very neat >>> >> >> Thank you >> >> >>> >>> * just wondering if this will work for multiple magazines linked to one >>> article (i.e. many-to-many) >>> >> >> It does but I only focussed on the magazine side as with the >> magazine/article relationship it''s more likely that you''ll create a magazine >> and add articles than create an article and add magazines. >> You can do it though, you''ll just need to implement an add_magazine method >> to the article model >> The validations definitely work both ways, check the tests. >> >> >>> >>> * do you think there''s no way to solve this without creating a new method >>> in fact (like your "add_article")? >>> >> >> I played around with the various collection methods but each of them tries >> to save a model at some point where the other isn''t saved and then the >> validations will fail. Also most of the association methods rely on there >> being an id in the association model (i.e. it must be saved). >> My solution doesn''t require either model to be saved and handles the >> saving of children when needed. >> My solution will also continue to work after creation if you''re updating >> deleting children etc. E.g. You don''t have to use the add_article method >> once you have at least one article saved with a link to the magazine. >> You could do: >> magazine.add_article article >> magazine.save! >> magazine.articles << Article.new(...) >> >> >>> * one thing that this has made me realize is that I was also thinking >>> of/assuming that my validation checks would be database based (e.g. search >>> database to see result), but by working in the object world this helps >>> remove the inherit database transaction/commits only approach where I was >>> getting stuck seeing how to do it >>> >> >> You could deal with it more in the database but then you would lose the >> abstraction of the models and you would need to couple more tightly to the >> database. >> >> >>> regards >>> Greg >>> >> >> >> -- >> 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/ > > >-- 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Andrew Timberlake
2009-Jan-15 06:20 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
On Thu, Jan 15, 2009 at 7:46 AM, Greg Hauptmann < greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> PS. Here''s an update where I''m at if anyone whats to comment. Not quite > finished however I''m wondering now if I ensure solid validation level checks > in model validations (e.g. both ends of an association are working, means > have to set both ends manually) that I could then rely on Rails to actually > save both ends of an association even if you only save one. (e.g. seems when > I save book, chapter also gets saved, and vice-versa). Here''s where I''m at: > > ---------------------------------------------------------- > require File.expand_path(File.dirname(__FILE__) + ''/../spec_helper'') > > # ----------- BOOK --------------- > class Book < ActiveRecord::Base > has_many :chapters > > def validate > if self.chapters.length == 0 > errors.add_to_base("Book does not have an associated Chapter") > end > end > > end > > # ----------- CHAPTER --------------- > class Chapter < ActiveRecord::Base > belongs_to :book > > def validate > if !self.book > errors.add_to_base("Chapter does not have an associated Book") > end > end > > end > > # --------- RSPEC (BOOK) ------------ > describe Book do > before(:each) do > @valid_attributes = {:amount => 100} > @b = Book.new(:amount => 100) > @c = Chapter.new(:amount => 100) > end > > it "should save without error when association is in place" do > @c.book = @b > @b.chapters = [@c] > @b.save! > @c.save! > end > > it "should delete without error when both ends of association are > deleted" do > @c.book = @b > @b.chapters = [@c] > @b.save! > @c.save! > > @c.destroy > @b.destroy > end > > it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do > lambda{ @b.save! }.should raise_error > end > > it "should fail for SAVE! if there is no association (DATABASE LEVEL)" do > @c.book = @b > @b.chapters = [@c] > lambda{ @b.save! }.should_not raise_error #on basis that Rails will > save Chapter automatically > end > > it "should fail for DELETE if one side left open" do > @c.book = @b > @b.chapters = [@c] > @b.save! > @c.save! > > lambda{ @b.destroy }.should raise_error > end > > end > > # ----------RSPEC (CHAPTER) -------------------- > describe Chapter do > before(:each) do > @valid_attributes = {:amount => 100} > @b = Book.new(:amount => 100) > @c = Chapter.new(:amount => 100) > end > > it "should save without error when association is in place" do > @c.book = @b > @b.chapters = [@c] > @b.save! > @c.save! > end > > it "should delete without error when both ends of association are > deleted" do > @c.book = @b > @b.chapters = [@c] > @b.save! > @c.save! > > @c.destroy > @b.destroy > end > > it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do > lambda{ @c.save! }.should raise_error > end > > it "should fail for SAVE! if there is no association (DATABASE LEVEL)" do > @c.book = @b > @b.chapters = [@c] > lambda{ @c.save! }.should_not raise_error # on basis that Rails will > automatically save Book > end > > it "should fail for DELETE if one side left open" do > @c.book = @b > @b.chapters = [@c] > @b.save! > @c.save! > > lambda{ @c.destroy }.should raise_error > end > > end > ---------------------------------------------------------- > > $ ./script/autospec > loading autotest/rails_rspec > /opt/local/bin/ruby /opt/local/lib/ruby/gems/1.8/gems/rspec-1.1.12/bin/spec > spec/models/all_in_one_test_spec.rb -O spec/spec.opts > F......... > > 1) > ''Chapter should fail for DELETE if one side left open'' FAILED > expected Exception but nothing was raised > ./spec/models/all_in_one_test_spec.rb:114: > > Finished in 0.280484 seconds > > 10 examples, 1 failure > > ---------------------------------------------------------- > >Greg, put in a before_delete callback to see if the deletion would leave the one side open and then return false if it would. -- Andrew Timberlake http://ramblingsonrails.com http://www.linkedin.com/in/andrewtimberlake "I have never let my schooling interfere with my education" - Mark Twain --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Andrew Timberlake
2009-Jan-15 06:21 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
On Thu, Jan 15, 2009 at 8:20 AM, Andrew Timberlake < andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote:> On Thu, Jan 15, 2009 at 7:46 AM, Greg Hauptmann < > greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > >> PS. Here''s an update where I''m at if anyone whats to comment. Not quite >> finished however I''m wondering now if I ensure solid validation level checks >> in model validations (e.g. both ends of an association are working, means >> have to set both ends manually) that I could then rely on Rails to actually >> save both ends of an association even if you only save one. (e.g. seems when >> I save book, chapter also gets saved, and vice-versa). Here''s where I''m at: >> >> ---------------------------------------------------------- >> require File.expand_path(File.dirname(__FILE__) + ''/../spec_helper'') >> >> # ----------- BOOK --------------- >> class Book < ActiveRecord::Base >> has_many :chapters >> >> def validate >> if self.chapters.length == 0 >> errors.add_to_base("Book does not have an associated Chapter") >> end >> end >> >> end >> >> # ----------- CHAPTER --------------- >> class Chapter < ActiveRecord::Base >> belongs_to :book >> >> def validate >> if !self.book >> errors.add_to_base("Chapter does not have an associated Book") >> end >> end >> >> end >> >> # --------- RSPEC (BOOK) ------------ >> describe Book do >> before(:each) do >> @valid_attributes = {:amount => 100} >> @b = Book.new(:amount => 100) >> @c = Chapter.new(:amount => 100) >> end >> >> it "should save without error when association is in place" do >> @c.book = @b >> @b.chapters = [@c] >> @b.save! >> @c.save! >> end >> >> it "should delete without error when both ends of association are >> deleted" do >> @c.book = @b >> @b.chapters = [@c] >> @b.save! >> @c.save! >> >> @c.destroy >> @b.destroy >> end >> >> it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do >> lambda{ @b.save! }.should raise_error >> end >> >> it "should fail for SAVE! if there is no association (DATABASE LEVEL)" >> do >> @c.book = @b >> @b.chapters = [@c] >> lambda{ @b.save! }.should_not raise_error #on basis that Rails will >> save Chapter automatically >> end >> >> it "should fail for DELETE if one side left open" do >> @c.book = @b >> @b.chapters = [@c] >> @b.save! >> @c.save! >> >> lambda{ @b.destroy }.should raise_error >> end >> >> end >> >> # ----------RSPEC (CHAPTER) -------------------- >> describe Chapter do >> before(:each) do >> @valid_attributes = {:amount => 100} >> @b = Book.new(:amount => 100) >> @c = Chapter.new(:amount => 100) >> end >> >> it "should save without error when association is in place" do >> @c.book = @b >> @b.chapters = [@c] >> @b.save! >> @c.save! >> end >> >> it "should delete without error when both ends of association are >> deleted" do >> @c.book = @b >> @b.chapters = [@c] >> @b.save! >> @c.save! >> >> @c.destroy >> @b.destroy >> end >> >> it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do >> lambda{ @c.save! }.should raise_error >> end >> >> it "should fail for SAVE! if there is no association (DATABASE LEVEL)" >> do >> @c.book = @b >> @b.chapters = [@c] >> lambda{ @c.save! }.should_not raise_error # on basis that Rails will >> automatically save Book >> end >> >> it "should fail for DELETE if one side left open" do >> @c.book = @b >> @b.chapters = [@c] >> @b.save! >> @c.save! >> >> lambda{ @c.destroy }.should raise_error >> end >> >> end >> ---------------------------------------------------------- >> >> $ ./script/autospec >> loading autotest/rails_rspec >> /opt/local/bin/ruby >> /opt/local/lib/ruby/gems/1.8/gems/rspec-1.1.12/bin/spec >> spec/models/all_in_one_test_spec.rb -O spec/spec.opts >> F......... >> >> 1) >> ''Chapter should fail for DELETE if one side left open'' FAILED >> expected Exception but nothing was raised >> ./spec/models/all_in_one_test_spec.rb:114: >> >> Finished in 0.280484 seconds >> >> 10 examples, 1 failure >> >> ---------------------------------------------------------- >> >> > Greg, put in a before_delete callback to see if the deletion would leave > the one side open and then return false if it would. > >Sorry, it''s before_destroy -- Andrew Timberlake http://ramblingsonrails.com http://www.linkedin.com/in/andrewtimberlake "I have never let my schooling interfere with my education" - Mark Twain --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Greg Hauptmann
2009-Jan-15 06:51 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
just wondering what I would put in: (a) before_destroy - then proactively destroy the other associated object, BUT if those objects had a before_destory this could become circular no? (b) after_destroy - test to see if both ends were destroy, but if one end tests first prior to the other end being destroyed this would be an issue no? On Thu, Jan 15, 2009 at 4:21 PM, Andrew Timberlake < andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote:> On Thu, Jan 15, 2009 at 8:20 AM, Andrew Timberlake < > andrew-642hCh26+Dt3UeSHeRwt+FaTQe2KTcn/@public.gmane.org> wrote: > >> On Thu, Jan 15, 2009 at 7:46 AM, Greg Hauptmann < >> greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: >> >>> PS. Here''s an update where I''m at if anyone whats to comment. Not quite >>> finished however I''m wondering now if I ensure solid validation level checks >>> in model validations (e.g. both ends of an association are working, means >>> have to set both ends manually) that I could then rely on Rails to actually >>> save both ends of an association even if you only save one. (e.g. seems when >>> I save book, chapter also gets saved, and vice-versa). Here''s where I''m at: >>> >>> ---------------------------------------------------------- >>> require File.expand_path(File.dirname(__FILE__) + ''/../spec_helper'') >>> >>> # ----------- BOOK --------------- >>> class Book < ActiveRecord::Base >>> has_many :chapters >>> >>> def validate >>> if self.chapters.length == 0 >>> errors.add_to_base("Book does not have an associated Chapter") >>> end >>> end >>> >>> end >>> >>> # ----------- CHAPTER --------------- >>> class Chapter < ActiveRecord::Base >>> belongs_to :book >>> >>> def validate >>> if !self.book >>> errors.add_to_base("Chapter does not have an associated Book") >>> end >>> end >>> >>> end >>> >>> # --------- RSPEC (BOOK) ------------ >>> describe Book do >>> before(:each) do >>> @valid_attributes = {:amount => 100} >>> @b = Book.new(:amount => 100) >>> @c = Chapter.new(:amount => 100) >>> end >>> >>> it "should save without error when association is in place" do >>> @c.book = @b >>> @b.chapters = [@c] >>> @b.save! >>> @c.save! >>> end >>> >>> it "should delete without error when both ends of association are >>> deleted" do >>> @c.book = @b >>> @b.chapters = [@c] >>> @b.save! >>> @c.save! >>> >>> @c.destroy >>> @b.destroy >>> end >>> >>> it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do >>> lambda{ @b.save! }.should raise_error >>> end >>> >>> it "should fail for SAVE! if there is no association (DATABASE LEVEL)" >>> do >>> @c.book = @b >>> @b.chapters = [@c] >>> lambda{ @b.save! }.should_not raise_error #on basis that Rails will >>> save Chapter automatically >>> end >>> >>> it "should fail for DELETE if one side left open" do >>> @c.book = @b >>> @b.chapters = [@c] >>> @b.save! >>> @c.save! >>> >>> lambda{ @b.destroy }.should raise_error >>> end >>> >>> end >>> >>> # ----------RSPEC (CHAPTER) -------------------- >>> describe Chapter do >>> before(:each) do >>> @valid_attributes = {:amount => 100} >>> @b = Book.new(:amount => 100) >>> @c = Chapter.new(:amount => 100) >>> end >>> >>> it "should save without error when association is in place" do >>> @c.book = @b >>> @b.chapters = [@c] >>> @b.save! >>> @c.save! >>> end >>> >>> it "should delete without error when both ends of association are >>> deleted" do >>> @c.book = @b >>> @b.chapters = [@c] >>> @b.save! >>> @c.save! >>> >>> @c.destroy >>> @b.destroy >>> end >>> >>> it "should fail for SAVE! if there is no association (OBJECT LEVEL)" do >>> lambda{ @c.save! }.should raise_error >>> end >>> >>> it "should fail for SAVE! if there is no association (DATABASE LEVEL)" >>> do >>> @c.book = @b >>> @b.chapters = [@c] >>> lambda{ @c.save! }.should_not raise_error # on basis that Rails will >>> automatically save Book >>> end >>> >>> it "should fail for DELETE if one side left open" do >>> @c.book = @b >>> @b.chapters = [@c] >>> @b.save! >>> @c.save! >>> >>> lambda{ @c.destroy }.should raise_error >>> end >>> >>> end >>> ---------------------------------------------------------- >>> >>> $ ./script/autospec >>> loading autotest/rails_rspec >>> /opt/local/bin/ruby >>> /opt/local/lib/ruby/gems/1.8/gems/rspec-1.1.12/bin/spec >>> spec/models/all_in_one_test_spec.rb -O spec/spec.opts >>> F......... >>> >>> 1) >>> ''Chapter should fail for DELETE if one side left open'' FAILED >>> expected Exception but nothing was raised >>> ./spec/models/all_in_one_test_spec.rb:114: >>> >>> Finished in 0.280484 seconds >>> >>> 10 examples, 1 failure >>> >>> ---------------------------------------------------------- >>> >>> >> Greg, put in a before_delete callback to see if the deletion would leave >> the one side open and then return false if it would. >> >> > Sorry, it''s before_destroy > > > -- > 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Andrew Timberlake
2009-Jan-15 08:18 UTC
Re: Validation spanning multiple models(tables) - how can this be achieved in Rails?
On Thu, Jan 15, 2009 at 8:51 AM, Greg Hauptmann < greg.hauptmann.ruby-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> just wondering what I would put in: > (a) before_destroy - then proactively destroy the other associated object, > BUT if those objects had a before_destory this could become circular no? > (b) after_destroy - test to see if both ends were destroy, but if one end > tests first prior to the other end being destroyed this would be an issue > no? > > >>> Greg, put in a before_delete callback to see if the deletion would leave >>> the one side open and then return false if it would. >>> >>> >> Sorry, it''s before_destroy > >I wouldn''t try do a dependant destroy, I''d return false (to stop the destroy) or raise an error so that the destroy never happens. If you want to destroy with the dependant you are going to have to put a special case destroy on one side that will automatically destroy it''s association. So the one will make sure it can''t be destroyed if it will break referential integrity, the other side will destroy it''s dependent along with itself. -- Andrew Timberlake http://ramblingsonrails.com http://www.linkedin.com/in/andrewtimberlake "I have never let my schooling interfere with my education" - Mark Twain --~--~---------~--~----~------------~-------~--~----~ 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 rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---