problem with has_many :through association

I have the following models

   1. class Caregiver < ActiveRecord::Base
   2. has_many :call_orders
   3. has_many :users, :through => :call_orders
   4. end
   5.
   6. class User < ActiveRecord::Base
   7. has_many :call_orders, :order => :position
   8. has_many :caregivers, :through => :call_orders
   9. end
  10.
  11. class CallOrder < ActiveRecord::Base
  12. belongs_to :user
  13. belongs_to :caregiver
  14. acts_as_list :scope => :user
  15. end

When I run the following commands, I get an error:

   1. @call_list = User.find(params[:id])
   2. @call_list.call_orders.each do |call_order|
   3. puts call_order.caregiver.first_name

This is the error:

   You have a nil object when you didn't expect it!
   The error occurred while evaluating nil.first_name

chirag wrote:

I have the following models

   1. class Caregiver < ActiveRecord::Base
   2. has_many :call_orders
   3. has_many :users, :through => :call_orders
   4. end
   5.
   6. class User < ActiveRecord::Base
   7. has_many :call_orders, :order => :position
   8. has_many :caregivers, :through => :call_orders
   9. end
  10.
  11. class CallOrder < ActiveRecord::Base
  12. belongs_to :user
  13. belongs_to :caregiver
  14. acts_as_list :scope => :user
  15. end

When I run the following commands, I get an error:

   1. @call_list = User.find(params[:id])
   2. @call_list.call_orders.each do |call_order|
   3. puts call_order.caregiver.first_name

This is the error:

   You have a nil object when you didn't expect it!
   The error occurred while evaluating nil.first_name

What do your tables/migrations look like?

class CreateCaregivers < ActiveRecord::Migration
  def self.up
    create_table :caregivers do |t|
      t.column :first_name, :string
      t.column :last_name, :string
      t.column :address, :string
      t.column :city, :string
      t.column :state, :string
      t.column :home_phone, :string
      t.column :work_phone, :string
      t.column :cell_phone, :string
      t.column :relationship, :string
      t.column :email, :string
    end
  end

  def self.down
    drop_table :caregivers
  end
end

class CreateCallOrders < ActiveRecord::Migration
  def self.up
    create_table :call_orders do |t|
      t.column :user_id, :integer
      t.column :caregiver_id, :integer
      t.column :position, :integer
    end
  end

  def self.down
    drop_table :call_orders
  end
end

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table "users", :force => true do |t|
      t.column :login, :string
      t.column :email, :string
      t.column :crypted_password, :string, :limit => 40
      t.column :salt, :string, :limit => 40
      t.column :created_at, :datetime
      t.column :updated_at, :datetime
      t.column :remember_token, :string
      t.column :remember_token_expires_at, :datetime

      t.column :activation_code, :string, :limit => 40
      t.column :activated_at, :datetime
    end
  end

  def self.down
    drop_table "users"
  end
end

Thanks for the help!
Chirag

Migrations look fine and it's working for me:

>> call_list = User.find(1)
=> #<User:0x3447480 @attributes={"salt"=>nil, "activated_at"=>nil, "updated_at"=>"2007-10-03 22:25:01", "crypted_password"=>nil, "activation_code"=>nil, "remember_token_expires_at"=>nil, "id"=>"1", "remember_token"=>nil, "login"=>"test", "created_at"=>"2007-10-03 22:25:01", "email"=>nil}>
>> call_list.call_orders.each do |call_order|
?> puts call_order.caregiver.first_name
>> end
John

>> call_list.call_orders[0].caregiver
=> #<Caregiver:0x3441634 @attributes={"work_phone"=>nil, "city"=>nil, "id"=>"1", "relationship"=>nil, "cell_phone"=>nil, "home_phone"=>nil, "first_name"=>"John", "address"=>nil, "last_name"=>"Smith", "email"=>nil, "state"=>nil}>

My guess is you have some bad data somewhere.

Im agree with Michael.

Btw, you can improve your code with a nested :include option like
this:
@call_list = User.find(params[:id], :include => [ { :call_orders
=> :caregiver } ])

Take a look at this: http://snippets.dzone.com/posts/show/2089 for a
better
explanation.

Regards.
Pablo Castellazzi.

Is that just the way the data is? caregiver is belongs_to of callorder: call_order.caregiver can be nil (unless you've got other contraints that forbid it)

Fred

Is that just the way the data is? caregiver is belongs_to of
callorder: call_order.caregiver can be nil (unless you've got other
contraints that forbid it)

Freederick mades a good point here. If nil values are accepted as your
model definition suggests, then you should use a "compacted" list of
caregivers like:

@call_list.call_orders.collect(&:caregiver).compact.each do |
caregiver>
  puts caregiver.first_name
end

if nil values are not accepted you should add valitadates_presence_of
and validates_associated to force a caregiver to be present:

class CallOrder < ActiveRecord::Base
   belongs_to :user
   belongs_to :caregiver
   acts_as_list :scope => :user

   validates_presence_of :caregiver_id
   validates_associated :caregiver
end

Regards
Pablo Castellazzi.