Another newbie question by me... I have this: class User < ActiveRecord::Base ... has_one :book, :through => :library ... end class Library < ActiveRecord::Base has_one :book, :dependent => :destroy end class Library < ActiveRecord::Base belongs_to :library validates_existence_of :library end I just don''t know what my route will look like. Any help or suggestions? Are my models correct in how I used the has_one :through Mike Thanks
mlittle wrote:> Another newbie question by me...First, do yourself a favor and not start off by advertising your "newbie" status. Trust that experienced programmers will know right away where you are in your learning by reading your question. Before attempting to answer this question I would like some confirmations. Read on...> I have this: > > class User < ActiveRecord::Base > ... > has_one :book, :through => :library > ... > endAre you wanting to say here that a user can only check out one book at a time from the library?> class Library < ActiveRecord::Base > has_one :book, :dependent => :destroy > endAnd a library can only ever contain one book?> class Library < ActiveRecord::Base > belongs_to :library > validates_existence_of :library > endDid you mean to define Book here instead of reopening the Library class and adding stuff to it? Assuming you meant Book it seems fine for a book to belong to a library. There is no validates_existence_of. Assuming you meant validates_presence_of :library: I''m even a little bit torn on this one, but I validate the presence of the foreign key (:library_id in this case) rather than validate the association. From my experience so far it seems to make for cleaner test scripts and also does not directly depend on the actual association object making model tests easier to manage.> I just don''t know what my route will look like.Once you get your model actually right your routes will make sense.> Any help or suggestions? Are my models correct in how I used the has_one :throughKeep this in mind as a general rule of thumb: The reason has_one exists is for creating one-to-one associations. Thing A can have only one B and thing B can only ever have one A. So why would you ever need one-to-one associations? Can''t you just add all the needed fields to one table? Technically the answer is "Yes you can." However, there are some very useful and practical uses for one-to-one associations. - Sometimes groups of fields, which actually are functionally depend on the primary key of a row, are related in other ways and are good candidate to be broken out into a separate table. - Often a table may contain a set of "optional" fields that don''t need to have values for every row in a table. These can be broken out to a separate table for effeciency''s sake and to clean up all those NULLs you would see if there was only one table. - There are also cases where a table may contain a large character or binary blob. Most of the time it''s convenient to just use the wildcard when selecting data: (select * from my_table;). If that table contained a column of type (TEXT or BLOB). Then you''re going to suffer a significant performance hit. That could be fixed by always specifying the list of columns with something like: (select Col1, Col2, Coln from my_table;), but you could also use a one-to-many and put the TEXT or BLOB in a separate table. Then (select * from my_table;) would only fetch the foreign key (i.e. my_blob_id), which is a simple integer and you won''t suffer the performance hit. When you actually want to get the large TEXT or BLOB you can either grab them individually thought the association, or include them if you need to grab a list of objects: @posts = Post.find(:all, :include => :post_narrative). So why has_one and not belongs_to? Well to answer this you need to consider how a one-to-on association is built in Rails. It is really just like a one-to-many association with a validation that only allows one object to be associated on the "many" side. So think of has_one as a "special" has_many than includes the proper validation (to ensure one-to-one) and also provides the one object instead of an array containing the one object. So which side gets the has_one? Well that''s easy to answer as well: use belongs_to on the model that contains the foreign key and use has_one on the model that doesn''t. You get to decide which model should get the foreign key. Usually objects have a natural association that makes that fairly easy. Say that a Person can only have one Residence (within this design). You wouldn''t think about a Person belonging to the Residence. You would rather say the Residence belongs to the Person. So Residence would get the foreign key, an hence the belongs_to :person. So then Person gets the has_one: residence. I apologize for the long reply. Maybe this belongs_to :blog. -- Posted via http://www.ruby-forum.com/.
Robert, thanks for the reply. validates_existence_of is a plugin -> http://github.com/bogdans83/validates_existence_of/. I''ll try to make this as simple as possible. Here is what i''m trying to do: A''s has many B''s and B''s have 1 C. A = Users B = Books C = Libraries Hope this clarifies everything for you. Sorry about the mistakes in my first post. Thanks again for your help On Oct 1, 5:19 pm, Robert Walker <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> mlittle wrote: > > Another newbie question by me... > > First, do yourself a favor and not start off by advertising your > "newbie" status. Trust that experienced programmers will know right away > where you are in your learning by reading your question. > > Before attempting to answer this question I would like some > confirmations. Read on... > > > I have this: > > > class User < ActiveRecord::Base > > ... > > has_one :book, :through => :library > > ... > > end > > Are you wanting to say here that a user can only check out one book at a > time from the library? > > > class Library < ActiveRecord::Base > > has_one :book, :dependent => :destroy > > end > > And a library can only ever contain one book? > > > class Library < ActiveRecord::Base > > belongs_to :library > > validates_existence_of :library > > end > > Did you mean to define Book here instead of reopening the Library class > and adding stuff to it? > > Assuming you meant Book it seems fine for a book to belong to a library. > > There is no validates_existence_of. Assuming you meant > validates_presence_of :library: > I''m even a little bit torn on this one, but I validate the presence of > the foreign key (:library_id in this case) rather than validate the > association. From my experience so far it seems to make for cleaner test > scripts and also does not directly depend on the actual association > object making model tests easier to manage. > > > I just don''t know what my route will look like. > > Once you get your model actually right your routes will make sense. > > > Any help or suggestions? Are my models correct in how I used the has_one :through > > Keep this in mind as a general rule of thumb: The reason has_one exists > is for creating one-to-one associations. Thing A can have only one B and > thing B can only ever have one A. > > So why would you ever need one-to-one associations? Can''t you just add > all the needed fields to one table? Technically the answer is "Yes you > can." However, there are some very useful and practical uses for > one-to-one associations. > > - Sometimes groups of fields, which actually are functionally depend on > the primary key of a row, are related in other ways and are good > candidate to be broken out into a separate table. > > - Often a table may contain a set of "optional" fields that don''t need > to have values for every row in a table. These can be broken out to a > separate table for effeciency''s sake and to clean up all those NULLs you > would see if there was only one table. > > - There are also cases where a table may contain a large character or > binary blob. Most of the time it''s convenient to just use the wildcard > when selecting data: (select * from my_table;). If that table contained > a column of type (TEXT or BLOB). Then you''re going to suffer a > significant performance hit. That could be fixed by always specifying > the list of columns with something like: (select Col1, Col2, Coln from > my_table;), but you could also use a one-to-many and put the TEXT or > BLOB in a separate table. Then (select * from my_table;) would only > fetch the foreign key (i.e. my_blob_id), which is a simple integer and > you won''t suffer the performance hit. When you actually want to get the > large TEXT or BLOB you can either grab them individually thought the > association, or include them if you need to grab a list of objects: > @posts = Post.find(:all, :include => :post_narrative). > > So why has_one and not belongs_to? Well to answer this you need to > consider how a one-to-on association is built in Rails. It is really > just like a one-to-many association with a validation that only allows > one object to be associated on the "many" side. So think of has_one as a > "special" has_many than includes the proper validation (to ensure > one-to-one) and also provides the one object instead of an array > containing the one object. > > So which side gets the has_one? Well that''s easy to answer as well: use > belongs_to on the model that contains the foreign key and use has_one on > the model that doesn''t. You get to decide which model should get the > foreign key. Usually objects have a natural association that makes that > fairly easy. Say that a Person can only have one Residence (within this > design). You wouldn''t think about a Person belonging to the Residence. > You would rather say the Residence belongs to the Person. So Residence > would get the foreign key, an hence the belongs_to :person. So then > Person gets the has_one: residence. > > I apologize for the long reply. Maybe this belongs_to :blog. > > -- > Posted viahttp://www.ruby-forum.com/.
mlittle wrote:> Robert, thanks for the reply. validates_existence_of is a plugin -> > http://github.com/bogdans83/validates_existence_of/. I''ll try to make > this as simple as possible. Here is what i''m trying to do: > > A''s has many B''s and B''s have 1 C. > > A = Users > B = Books > C = LibrariesThen your associations will be as follows: class User < AR::B has_many :books end class Book < AR::B belongs_to :user belongs_to :library end class Library < AR::B has_many :books end> > Hope this clarifies everything for you. Sorry about the mistakes in my > first post. Thanks again for your help > > On Oct 1, 5:19�pm, Robert Walker <rails-mailing-l...-ARtvInVfO7m5VldFQK4jKA@public.gmane.orgt>Best, -- Marnen Laibow-Koser http://www.marnen.org marnen-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
mlittle wrote:> Robert, thanks for the reply. validates_existence_of is a plugin -> > http://github.com/bogdans83/validates_existence_of/. I''ll try to make > this as simple as possible. Here is what i''m trying to do:Got it. I though maybe that was the case, and might explain some of my previous frustrations with testing these associations.> A''s has many B''s and B''s have 1 C. > > A = Users > B = Books > C = LibrariesOkay, That makes more sense. Let me first write this as a user story: Users may check out many books from a library. Users may borrow from many libraries. Only one library may own a book (in this design let''s consider two books with the same title separate books). Books loaned to users must be returned to the owning library. So I see four models here not three: User Library Book Loan Feature: Book check out In order to learn cool stuff, or be entertained As a user I want to be able to checkout many books from many libraries Scenario: Check from 2 libraries Given I select ''Programming Ruby'' from the Public Library And I select ''The Rails Way'' from the University Library When I check out from the Public Library And I check out from the University Library Then I should have 2 books Feature: Book check in In order to prevent late fees As a user I want to be able to check in books Scenario: I must return a book to the correct library Given I have ''Programming Ruby'' When I check in to the University Library Then the check in should be denied And I should have 2 books There may be other scenarios to consider, but I wanted to focus on these two to help describe the model: It looks to me like you need a standard many-to-many association with some additional validation to make sure the books get returned back to the library that owns them. class User < ActiveRecord::Base has_many :loans has_many :books, :through => :loans end class Loan < ActiveRecord::Base belongs_to :user belongs_to :book end class Book < ActiveRecord::Base has_many :loans has_many :users, :through => :loans belongs_to :library end class Library < ActiveRecord::Base has_many :books end loans_controller.rb # DELETE /loans/1 # DELETE /loans/1.xml def destroy @loan = Loan.find(params[:id]) @loan.destroy if @loan.book.library == current_library respond_to do |format| flash[:error] = "This book was format.html { redirect_to(loans_url) } format.xml { head :ok } end end I''ll leave it as an exercise for you to figure out how to determine the "current library" that the user is attempting to return the book to. Notice that in this design the act of checking in a book is to destroy the loan of the book. So loan is a resource in it''s own right. Of course checking out a book would be to create a new loan. Note: this code is incomplete and untested with no warrantee of any kind. :) -- Posted via http://www.ruby-forum.com/.
Robert, Thanks so much for the help. Question, though, could you help with with the routing that would be involved? On Oct 1, 7:20 pm, Robert Walker <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> mlittle wrote: > > Robert, thanks for the reply. validates_existence_of is a plugin -> > >http://github.com/bogdans83/validates_existence_of/. I''ll try to make > > this as simple as possible. Here is what i''m trying to do: > > Got it. I though maybe that was the case, and might explain some of my > previous frustrations with testing these associations. > > > A''s has many B''s and B''s have 1 C. > > > A = Users > > B = Books > > C = Libraries > > Okay, That makes more sense. Let me first write this as a user story: > > Users may check out many books from a library. Users may borrow from > many libraries. Only one library may own a book (in this design let''s > consider two books with the same title separate books). Books loaned to > users must be returned to the owning library. > > So I see four models here not three: > > User > Library > Book > Loan > > Feature: Book check out > In order to learn cool stuff, or be entertained > As a user > I want to be able to checkout many books from many libraries > > Scenario: Check from 2 libraries > Given I select ''Programming Ruby'' from the Public Library > And I select ''The Rails Way'' from the University Library > When I check out from the Public Library > And I check out from the University Library > Then I should have 2 books > > Feature: Book check in > In order to prevent late fees > As a user > I want to be able to check in books > > Scenario: I must return a book to the correct library > Given I have ''Programming Ruby'' > When I check in to the University Library > Then the check in should be denied > And I should have 2 books > > There may be other scenarios to consider, but I wanted to focus on these > two to help describe the model: > > It looks to me like you need a standard many-to-many association with > some additional validation to make sure the books get returned back to > the library that owns them. > > class User < ActiveRecord::Base > has_many :loans > has_many :books, :through => :loans > end > > class Loan < ActiveRecord::Base > belongs_to :user > belongs_to :book > end > > class Book < ActiveRecord::Base > has_many :loans > has_many :users, :through => :loans > belongs_to :library > end > > class Library < ActiveRecord::Base > has_many :books > end > > loans_controller.rb > > # DELETE /loans/1 > # DELETE /loans/1.xml > def destroy > @loan = Loan.find(params[:id]) > @loan.destroy if @loan.book.library == current_library > > respond_to do |format| > flash[:error] = "This book was > format.html { redirect_to(loans_url) } > format.xml { head :ok } > end > end > > I''ll leave it as an exercise for you to figure out how to determine the > "current library" that the user is attempting to return the book to. > > Notice that in this design the act of checking in a book is to destroy > the loan of the book. So loan is a resource in it''s own right. Of course > checking out a book would be to create a new loan. > > Note: this code is incomplete and untested with no warrantee of any > kind. :) > -- > Posted viahttp://www.ruby-forum.com/.
mlittle wrote:> Robert, > > Thanks so much for the help. Question, though, could you help with > with the routing that would be involved? > > On Oct 1, 7:20�pm, Robert Walker <rails-mailing-l...-ARtvInVfO7m5VldFQK4jKA@public.gmane.orgt>I can''t think of any "special" routing that would be required. This design uses all standard REST actions so you should need only the standard resource routes: map.resources :users, :loans, :books, :libraries I suppose you could use some nested routes, but that would not be required for this to work. You would need some way to let the loans_controller know what library the book is attempting to be returned to, but I don''t think that should affect routing in any way. -- Posted via http://www.ruby-forum.com/.