Rails 3, how to make sure Group has_one user.role = groupleader - ruby-on-rails

My current Group model:
class Group < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :users, :through => :memberships
end
My Current User Model
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
#some more stuff
end
Membership Model
class Membership < ActiveRecord::Base
attr_accessible :user_id, :group_id
belongs_to :user
belongs_to :group
end
Role Model
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
I have a Ability class and CanCan installed to handle roles. I have a role type groupleader and need to make sure a Group has only one groupleader...
I think its something like: Group has_one User.role :groupleader... but I know thats not it.

It doesn't make sense to me to have the role on the users table if you want it to determine what the user can do within the context of a group.
Where it would make sense is to have it on the memberships table for groups and users. Records in this table would then have three columns: user_id, group_id and role.
Then to retrieve the leader for the group you would execute a query like this:
group.users.where("memberships.role = 'leader'").first
Where group is a Group object, i.e. Group.first or Group.find(13).
This then leaves open the possibility that you can have more than one leader for a group further down the track if required.
If your roles are in a separate table, then you can do this:
group.users.where("memberships.role_id = ?", Role.find_by_name("leader").id).first

Related

How to model schema with Ruby on Rails?

I have two models, Group and User. A user can create many groups and is then the owner of those groups. Other users can join those groups and therefore become members of those groups.
My question now is how to model this with Ruby on Rails.
I have created a join table between Group and Users and I added a reference from Group to the owner (foreign id to user).
Note: not sure if I did all this correctly.
Here are the attributes I currently have in Group and User (I don't know how to access the join table, so I can't show it to you).
Group: id, topic, home_town, created_at, updated_at, user_id
user_id was added with the following migration and I want it to reference the owner:
class AddOwnerReferenceToGroup < ActiveRecord::Migration
def change
add_reference :groups, :user, index: true
end
end
User: id, name, email, password_digest, remember_token, created_at, updated_at
As for the relationships, here's what each class contains:
User -> has_many :groups
Group -> has_many :users
Is it possible (and do I have) to add a belongs_to relationship in the group class to reference to the owner?
I would set it up like so:
class User < ActiveRecord::Base
has_many :group_users, :dependent => :destroy
has_many :groups, :through => :group_users
has_many :owned_groups, :through => :group_users, :class_name => "Group", :conditions => ["group_users.owner = ?", true]
...
end
class Group < ActiveRecord::Base
has_many :group_users, :dependent => :destroy
has_many :users, :through => :group_users
def owner
self.users.find(:first, :conditions => ["group_users.owner = ?", true])
end
def owner=(user)
if gu = self.group_users.find_by_user_id(user.id)
gu.update_attributes(:owner => true)
else
self.group_users.create(:user_id => user, :owner => true)
end
end
...
end
#group_users table has fields user_id, group_id, owner(bool)
class GroupUser < ActiveRecord::Base
belongs_to :group
belongs_to :user
after_save :enforce_single_owner
def enforce_single_owner
if self.changes["owner"] && self.owner
GroupUser.find(:all, :conditions => ["group_id = ? and id <> ? and owner = ?", self.group_id, self.id, true]).each{|gu| gu.update_attributes(:owner => false)
end
end
...
end
In this schema, the join table model has responsibility for tracking which of the members of the group is the owner of the group. A group has many users, and one of those wil be the owner.
Yes, it is possible and you should.
By adding to your Group
belongs_to :owner, class_name: 'User'
you can model your requirement that a group is created by one of the users.
In my snippet I used an arbitrary name in belongs_to just to make the code more readable. You don't have to but it definitely clears up things. The downside is that ActiveRecord cannot guess to which class you refer when using custom association names, thus, you need to manually specify it.
In a similar fashion you could change has_many :users to
has_many :members, class_name: 'User'
Whatever feels more natural when you read your code.

Rails 3 / Active Record - Association problems with additional model in assignment table

I currently have a setup that links the models User, Dealer and Role together. User and Dealer is many to many, and is working as expected with a Dealer_user assignment table.
The problem is that I want to have roles assigned to the user that are specific to the dealer also (i.e. a user could be a Sales Manager and a Parts Manager in one dealership, while being a Sales Manager and a Director in another).
In order to do this, I have a Role model (which belongs to a Role_type). Role should belong to Dealer_user, and Dealer_user has many Roles.
The intention is that I will be able to do dealer.users.where(:id => user.id).first.roles and it will return only the roles specific to that dealership.
The problem I have is that when I run the following test code: dealer.users.where(:id => user.id).first.roles.create(:role_type_id => 1 + Random.rand(4))
I get an error: Cannot modify association 'User#roles' because the source reflection class 'Role' is associated to 'DealerUser' via :has_many.
Can anyone suggest what I am doing wrong with my models (which are below)?
NOTE: The belongs_to relationship that Role has with Dealer_user is polymorphic because it could also belong to Sale_user or other association tables, which require the same functionality as Dealer.
class Dealer < ActiveRecord::Base
attr_accessible :name, :address_id
has_many :dealer_users
has_many :users, :through => :dealer_users
has_many :roles, :through => :dealer_users
end
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name
has_many :dealer_users
has_many :dealers, :through => :dealer_users
has_many :roles, :through => :dealer_users
end
class DealerUser < ActiveRecord::Base
attr_accessible :dealer_id, :user_id
belongs_to :dealer
belongs_to :user
has_many :roles, :as => :role_originator
end
class Role < ActiveRecord::Base
attr_accessible :role_type_id
belongs_to :role_type
belongs_to :role_originator, :polymorphic => true
end
Edit: No luck so far - can anyone help?
I would use has_many through association with the roles table. Get rid of the dealer_user table, and add columns to the roles table dealer_id and user_id
Then your models would look something like this:
class Dealer < ActiveRecord::Base
has_many :users, :through => :roles
has_many :roles
end
class User < ActiveRecord::Base
has_many :dealers, :through => :roles
has_many :roles
end
class Role < ActiveRecord::Base
belongs_to :dealer
belongs_to :user
end
That should make it easier to do the types of queries you're trying. The rails guide has a really good overview here

Modelling multiple many-many relationships in Rails

After reading RailsGuides, some answers here and other docs I am still confused about how to model this relationship.
Thinking of a model where there are objects User and Group, where users can be ordinary members of a group or have management privilege (create group, invite user, remove user, remove group) I want both objects to have two many-many relationships with each other.
user.memberOfGroups = a list of groups the user belongs to
group.userMembers = a list of all users who are members of the group
user.managedGroups = a list of groups the user has management
privileges for
group.managingUsers = a list of users with management privilege for
the group
Is this going to work?
class User < ActiveRecord::Base
has_many :managedGroups, :class_name => "Group", :foreign_key => "managingUsers"
has_many :memberOfGroups, :class_name => "Group", :foreign_key => "userMembers"
end
class Group < ActiveRecord::Base
has_many :managingUsers, :class_name => "User", :foreign_key => "managedGroups"
has_many :userMembers, :class_name => "User", :foreign_key => "memberOfGroups"
end
I have found in a guide how to use scaffold to generate the model objects which is fine with me, I just want to create a basic web service for POST and GET. If the above is correct it should be as simple as creating the model with scaffold and then editing to include the lines shown?
Some issues:
Membership and access control should be modeled by different tables.
Does management of a group imply membership?
The foreign key design as described would only allow for each user to manage one group/one group to have one manager. In all many:many relationships you'll need a third join model.
This would be a good starting point:
class User < ActiveRecord::Base
has_many :users_groups
has_many :groups, :through => :users_groups
end
class Group < ActiveRecord::Base
has_many :users_groups
has_many :users, :through => :users_groups
end
class UsersGroup < ActiveRecord::Base # will be the users_groups table
belongs_to :user
belongs_to :group
end
class Permission < ActiveRecord::Base
belongs_to :user
belongs_to :group
validate_presence_of :name # The name of the permission e.g. "manage"
end

has_many :through default values

I have a need to design a system to track users memberships to groups with varying roles (currently three).
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
end
class Role < ActiveRecord::Base
has_many :memberships
has_many :users, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :role
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
end
Ideally what I want is to simply set
#group.users << #user
and have the membership have the correct role. I can use :conditions to select data that has been manually inserted as such :
:conditions => ["memberships.role_id= ? ", Grouprole.find_by_name('user')]
But when creating the membership to the group the role_id is not being set.
Is there a way to do this as at present I have a somewhat repetitive piece of code for each user role in my Group model.
UPDATED
It should be noted what id ideally like to achieved is something similar to
#group.admins << #user
#group.moderators << #user
This would create the membership to the group and set the membership role (role_id ) appropriately.
You can always add triggers in your Membership model to handle assignments like this as they are created. For instance:
class Membership < ActiveRecord::Base
before_save :assign_default_role
protected
def assign_default_role
self.role = Role.find_by_name('user')
end
end
This is just an adaptation of your example.

Triple join in Ruby on Rails

I have question regarding associations in Ruby on Rails. In the application there are projects, users, roles and groups. The project belongs to a group with users, a user can belong to many different groups but can only have one specific role within that group. For example:
In one group the user is the project owner, but in another group he is a writer.
What is the best way to implement this using the built in functions in Rails?
Thanks
Here is a very quick set of models that should fulfill your requirements:
class User < ActiveRecord::Base
has_many :group_memberships
has_many :groups, :through => :group_memberships
end
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :role
belongs_to :group
end
class Role < ActiveRecord::Base
has_many :group_memberships
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, :through > :group_memberships
end
Basically there is a join table that has a user, group and role id in it. I'll leave the migrations as an exercise for the questioner

Resources