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 <
[email protected]> 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 <
> [email protected]> wrote:
>
>> Greg
>>
>> On Tue, Jan 13, 2009 at 2:24 PM, Greg Hauptmann <
>> [email protected]> 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 [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
-~----------~----~----~----~------~----~------~--~---