views:

363

answers:

2

I am building a simple book check out application. One of the things that I need to do is determine if a book is checked out. I have my associations between my people and book classes setup through a book_check_out class. My goal is to use the checked_out property of book to determine if a book is presently checked out. However, in my present implementation when a book is not checked out and I reference book.checked_out.XXX I receieve the error "You have a nil object when you didn't expect it!" My goal is to use book.checked_out for two purposes in some views show that yes, that book is checked out and in other views show who it is presently checked out to.

class Person < ActiveRecord::Base
  has_many :book_check_outs
  has_many :books, :through => :book_check_outs
end


class Book < ActiveRecord::Base

  has_many :book_check_outs
  has_many :people, :through => :book_check_outs

  def checked_out
    book_check_outs || false
  end
end


class BookCheckOut < ActiveRecord::Base
  belongs_to :book
  belongs_to :person
end
+1  A: 

When you've got a boolean outcome, something that can either be true or false (for instance, a book can either be checked out or not), you need to assume a default for the outcome. In this case, we can assume it's false (the book is not checked out by default).

In the book model, remove your *has_many :checked_out* line and create a method with the same name:

def checked_out
  book_check_outs || false
end

This should return either the BookCheckOuts, or if there are none associated with the instance, FALSE. It gives you a quick, easy, foolproof method of checking an association and removing the nasty nil object error.

Edit You could also just return TRUE or FALSE, and not return the most recent checkouts by checking that book_check_outs is nil or not. Rails provides a method called blank? which calls both nil? and empty?

def checked_out
  !book_check_outs.blank?
end

I love Ruby :)

Jamie Rumbelow
This unfortunately, returns an error "uninitialized constant Book::BookCheckOuts". I like the idea though my only concern is that I am not attempting to get at the BookCheckOuts collection if it's been checked_out just the most recent check out and if its not checked out returning false is perfect.
ahsteele
I did receive the "uninitialized constant Book::BookCheckOuts" until I changed both to book_check_outs. I think it has to do w/ the database naming convention.
ahsteele
Sorry about that, late night responding and all that, brain got foggy and forgot that you needed to access the generated accessor not the class.
Jamie Rumbelow
A: 

I forget if it actually matters or not, but for clarity's sake you might want to put the has_many relation above the has_many :through relation.

I am also not sure you should be declaring in the Book class that it has_many :BookCheckOuts AND it has_one :checked_out, since the checked_out actually is a BookCheckOut which you declared it having many of above.

Shane Liebling
I agree w/ the has_many and has_one it feels a bit awkward. I am looking for the "right" way of going about this. I've switched the has_many and has_many through. I too am not sure if it matters but I agree your suggestion makes things more clear.
ahsteele
Well, I guess the question I have is: do you have multiple copies of the same book in your library, and thus could loan out different ones? If _not_ then you only really need a has_one relationship. If you _do_ have multiple copies then you might want to have something like a PhysicalBook model which is a representation of each physical book you have...
Shane Liebling