views:

32

answers:

2

I have a has_one association that is reflective on the objects of another association. I have Project, which has many ProjectUsers, which tie Projects and Users. One of those ProjectUsers is authoritative. The issue is that both the has_one and the has_many use the same foreign key on project_users. Here's the base idea of the models.

class Project < ActiveRecord::Base
  has_many :project_users, :class_name => 'ProjectUser', :foreign_key => 'project_id'
  has_one :authoritative_user, :class_name => 'ProjectUser', :foreign_key => 'project_id', :conditions => {:authoritative => true}
end

class ProjectUser < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
  # has a boolean column 'authoritative'
end

What I would like to be able to do is call something like.

project = Project.new
project_user = ProjectUser.new
project.project_users << project_user
project.authoritative_user = project_user
other_project_user = ProjectUser.new
project.project_users << other_project_user
project.authoriative_user = other_project_user

Where authoritative_user= would update the project user to have authoritative be set to true, and make the previous authoritative user have authoritative set to false. Another issue I am having is that second time I set authoritative_user on the project, project_id on the previous ProjectUser gets set to nil, and so it is no longer associated though the Project's project_users.

I'm not sure if I'm just doing this completely wrong, or if I'm just missing something.

+1  A: 

Personally, I'd probably look to simplify/separate concerns. Here is an example (note: untested):

class Project < ActiveRecord::Base
  has_many :project_users
  has_many :users, :through => :project_users
  has_one :authoritative_user
end

class  ProjectUser < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
end

class AuthoritativeUser < ActiveRecord::Base
  belongs_to :project
  belongs_to :user

  validates_uniqueness_of :user_id, :scope => :project_id
end

Essentially, I break out the authoritative_user attribute of your ProjectUser model into it's own. Very simple, clean and not very exciting.

You could probably build a few convenience methods like has_authoritative_user? and update_authoritative_user in your Project model.

I'm sure you'll get a few better suggestions.

Hope this helps!

Brian
I like this solution if the authoritative_user didn't have to be mutable. I could even see that working if there wasn't other columns on ProjectUser to describe the Project <=> User relationship, and so just the user on AuthoritativeUser could change to reflect who was the authoritative user, but unfortunately, there IS that extra data. I could potentially abstract out all that additional meta data into a separate model, and just associate that with the ProjectUser/AuthoritativeUser. I say potentially, since the actual case for this would require a massive scary refactor of 2 year old code.
lambdabutz
Unless I'm missing something, authoritative_user can certainly change (but, there can be only one). In addition, there is no reason why the `ProjectUser` model can't have additional attributes. After all, that's essentially why the `has_many :through` relationship exists.
Brian
+1  A: 
class Project < ActiveRecord::Base
  has_many :project_users
  has_many :users, :through => :project_users

  belongs_to :authoritative_project_user, :class_name => 'ProjectUser'
  has_one    :authoritative_user, :through :authoritative_project_user
end

class  ProjectUser < ActiveRecord::Base
  belongs_to :project
  belongs_to :user

  has_one :project_as_authorized_user
end

then just let the has_one project_as_authorized_user relationship nil out your belongs_to authorized_project_user

Ransom Briggs