:has_many :through Question

I have 3 tables that are forming a has_many through relationship:

create_table “sites”, :force => true do |t|
t.column “account_id”, :integer, :default => 0, :null => false
t.column “created_on”, :timestamp
t.column “updated_on”, :timestamp
t.column “lock_version”, :integer, :default => 0, :null => false
t.column “deleted”, :string, :limit => 1, :default => “N”, :null =>
false
end

create_table “sites_users”, :force => true do |t|
t.column “created_on”, :timestamp
t.column “updated_on”, :timestamp
t.column “lock_version”, :integer, :default => 0, :null => false
t.column “deleted”, :string, :limit => 1, :default => “N”, :null =>
false
t.column “site_id”, :integer, :limit => 10, :default => 0, :null =>
false
t.column “user_id”, :integer, :limit => 10, :default => 0, :null =>
false
t.column “activated”, :string, :limit => 1, :default => “N”, :null
=> false
t.column “role”, :integer, :limit => 10, :default => 1, :null =>
false
end

create_table “users”, :force => true do |t|
t.column “created_on”, :timestamp
t.column “updated_on”, :timestamp
t.column “lock_version”, :integer, :default => 0, :null => false
t.column “deleted”, :string, :limit => 1, :default => “N”, :null =>
false
t.column “name”, :string, :limit => 100, :default => “”, :null =>
false
t.column “login”, :string, :limit => 40, :default => “”, :null =>
false
t.column “email”, :string, :limit => 100, :default => “”, :null =>
false
t.column “hashed_password”, :string, :limit => 40, :default => “”,
:null => false
t.column “salt”, :string, :limit => 40, :default => “”, :null =>
false
end

My user model looks like this:
require ‘digest/sha1’

class User < ActiveRecord::Base
has_many :sites, :through => :sites_users
has_many :sites_users

validates_length_of :login, :within => 6…40
validates_length_of :password, :within => 6…40
validates_presence_of :login, :email, :password,
:password_confirmation, :salt
validates_uniqueness_of :login, :email
validates_format_of :email,
:with =>
/^([^@\s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i,
:message => “Invalid email”
attr_protected :id, :salt

attr_accessor :password, :password_confirmation

def self.authenticate(login, pass)
u=find(:first, :conditions=>[“login = ?”, login])
return nil if u.nil?

su = su = u.sites_users.detect{ |su| su.site = session[:site].id

and su.active = ‘Y’ }
return nil if su.nil?

return u if User.encrypt(pass, u.salt)==u.hashed_password
nil

end

end

The system is setup to have many users assigned to different sites.
Sites_users contains their role (dependent on site) and whether they
are active on that site. When logging in the user needs to be
authorized against the users table and the sites_users.

The problem I’m getting is that the table name sites_users is giving
rails a fit. When I run the authenticate method I get the following
error:
…lib/active_support/dependencies.rb:100:in `const_missing’:
uninitialized constant SitesUser

It’s looking for the wrong class, notice that “Sites” is still
pluralized. Since the class name is SiteUser this is not working.

I’m assuming I’ve broken the naming convention someplace, but I’m not
sure where. Do I rename my table? My class?

Any help would be… helpful!

Thanks!
Joe

Hey Chris, perfect explanation yah, thanks for the help. I think I had
habtm
and has_many through twisted together in my brain. So much for late
nights,
not enough caffeine, and staring at a problem too long.

In order to keep this from being completely confusing I ended up making
the
table name “Assignments” and renaming everything.

short answer: your class should be named SitesUser and not SiteUser,
by default, this is what Rails expects.

the reason for this is because sites_users is not an invisible join
table like what you would use for a habtm relationship between sites
and users. It’s actually a full on table that goes hand in hand with
the SitesUser model. Rails doesn’t know to remove the pluralization
of sites in the table name because of this.

you have 3 options

  1. change the model name to SitesUser to correspond to your table name

  2. change the table name to site_users (and all your associations as
    well) to correspond to your model name

  3. set the table_name in the SiteUsers model to ‘sites_users’

i recommend option 2.

did i explain that well enough?

Hi, I think I like your “user_roles” name a lot better than the
“Assignments” name I switched it to. I’ll end up going with that I
think -
thanks again!

as an afterthought…

rename the sites_users table (and therefore the model and
associations) to something more descriptive, perhaps something like
‘site_roles’. remember you’re not locked into the habtm table naming
requirements.

class SiteRole < ActiveRecord::Base
belongs_to :site
belongs_to :user
end

class Site < ActiveRecord::Base
has_many :roles
has_many :users, :through => :site_roles
end

class User < ActiveRecord::Base
has_many :site_roles
has_many :sites, :through => :site_roles
end