Rails 3 - Model Association Problems - ruby-on-rails-3

I have a project where an User can own a Project and make part of a Project as a Team.
My models are like that:
class User
has_many :projects, :foreign_key => "owner_id"
has_many :project_memberships, :foreign_key => "member_id"
has_many :shared_projects, :class_name => "Project", :through => :project_memberships, :foreign_key => "member_id"
end
class Project
belongs_to :owner, :class_name => "User"
has_many :project_memberships
has_many :members, :class_name => "User", :through => "project_memberships", :foreign_key => "member_id"
end
My question is: How can I create/delete etc a new Project so an User can own it since I'm not using nested resources?
Here is my Project Controller:
def new
#project = Project.new
end
def create
#owner = User.find(params[:user_id])
#project= #owner.projects.build(params[:project])
...
end
Thanks in advance.

If I understand your question correctly, you need to store current signed in user ID in session or use some authentication gem (like devise) which will do it for you.
Devise provides helper method current_user which returns an instance of User model. So you could do like so:
def create
#project= current_user.projects.build(params[:project])
...
end
Update
If you pass user_id through form, you allow anyone to create project with another user's id. Actions that create something, that belong to current user should be constrained to current user on the serverside

Related

How to get single attribute from array in a has_many :through association (User/Friend model)

In my User model I have the following:
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, :class_name => "User", :foreign_key => "friend_id"
has_many :pending_friends,
:through => :friendships,
:conditions => "status = 'pending'",
:foreign_key => "user_id",
:source => :friend
has_many :requested_friends,
:through => :friendships,
:source => :friend,
:conditions => "status = 'requested'"
def friends
direct_friends | inverse_friends
end
In my Friendship model I have the following:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
In my view I have created an array of each of the users' friends, shown below. This code works and populates the array with all the data from the friends' "User" database model attributes.
User.first.friends
However, I want to be able to call the users' friend's name's. So, for example, shouldn't I be able to do something like this?
User.first.friends.map(&:name)
How do I get an array containing just the friend's name's, instead of all the friend's user attributes? I would also appreciate if anyone could tell me why .first is used (I got that from here: Rails calling User record from Friends model), as it doesn't just get the first instance of the User's friends (it gets all the instances). And why does just doing:
User.friends
return an empty array?
Try method pluck:
User.first.friends.pluck(:name)
You should use first method to retrieve one object from table. The User is table with a lot of users.

Rails 3 - Nested Form and has_many through association

Hi people, long time not been here. But I'm back because I need your help again please. I have a rails 3.0.9 app, and I'm working with nested forms and has_many through association. When i create an instance, it works great. The problems come when I try to edit. Here is an example for a better explanation. (table names and attributes are just for explaining)
Table Client
id
company_name
address
Table Worker
id
first_name
last name
Table Contact
id
client_id
worker_id
my models looks like these
class Worker < ActiveRecord::Base
has_many :contacts, :dependent => :destroy
has_many :clients, :through => :contacts, :foreign_key => 'client_id'
end
class Client < ActiveRecord::Base
has_many :contacts, :foreign_key => "client_id",:dependent => :destroy
has_many :workers, :through => :contacts, :foreign_key => 'worker_id'
accepts_nested_attributes_for :workers, :allow_destroy => false
end
class Contact < ActiveRecord::Base
belongs_to :worker, :foreign_key => "worker_id"
belongs_to :client, :foreign_key => "client_id"
end
Then in my form for create a client, I can create many workers, and rails make the association and creates the instances for the contacts table (by using nested forms).
The thing is, if I want to edit a client by removing a contact, the contact is not removed. As you can see I put this line in the clients model
accepts_nested_attributes_for :workers, :allow_destroy => false
I set the allow_destroy to false, because I don't want to delete the worker itself, I just want to remove the contact tuple.
Does anybody know how can I solve this?? Hope you can help me... Thanks

Rails 3 has_many :through + join table conditions / scoping

I'm working on an app that has the models User and Project, and User can be assigned to multiple Projects, via ProjectUser, with a role (e.g. Developer, Designer).
Project
has_many :project_users
has_many :users, :through => :project_users
User
has_many :project_users
has_many :projects, :through => :project_users
ProjectUser (user_id, project_id, role)
belongs_to :user
belongs_to :project
I can call #project.users and #user.projects, but since there are varying roles, I'd like to be a bit more specific with the relations. Ideally, I want to be able to do the following:
#project.developers
# returns #project.users, but only where ProjectUser.role = 'Developer'
#project.designers << #user
# creates a ProjectUser for #project, #user with role 'Designer'
#user.development_projects
# returns projects where #user is assigned as a 'Developer'
#user.design_projects << #project
# creates a ProjectUser for #project, #user with role 'Designer'
I currently have the following code:
has_many :developers, :through => :project_users, :source => :user,
:class_name => "User",
:conditions => ['project_users.role = ?','Developer']
But this only really does the fetching one-way, and doesn't give me much else - I can't build or assign or anything.
I'm attempting some more complex logic which I think might work, but would appreciate some pointers:
has_many :developer_assignments, :source => :project_user,
:conditions => { :role => 'Developer' }
has_many :developers, :through => :developer_assignments # class_name?
Any suggestions? Thanks!
has_many accepts a block that can define/override methods for the association. This will allow you to create a custom method for <<. I've created a small example for you, you could create build in a similar fashion.
# Project.rb
has_many :developers, :through => :project_users, :source => :user,
:conditions => "project_users.role = 'developer'" do
def <<(developer)
proxy_owner.project_users.create(:role => 'developer', :user => developer)
end
end
Now you can add a new developer to your your project with: #project.developers << #user as requested. #project.developers gives you all the developers.
If you have a lot of roles, it might be useful to create these has_many statements dynamically.
# Project.rb
ROLES = ['developer','contractor']
ROLES.each do |role|
self.class_eval <<-eos
has_many :#{role.downcase}s, :through => :project_users, :source => :user,
:conditions => "project_users.role = '#{role}'" do
def <<(user)
proxy_owner.project_users.create(:role => '#{role}', :user => user)
end
end
eos
end
Looking back at everything above it doesn't seem like the rails way of doing things. Scoping this should make it possible to get the build and create commands working without redefining everything.
Hope this helps!
It sounds like what you're looking for is a combination of RoR's single table inheritance and named scopes.
Take a look at the following article for a nice example about polymorphic associations. This should help you with achieving the following:
#project.developers
# returns #project.users, but only where ProjectUser.role = 'Developer'
#project.designers << #user
# creates a ProjectUser for #project, #user with role 'Designer'
Scopes will give you a clean way to implement #user.development_projects but there may be more trickery required to get the << operator.
Did you try using scopes yet? It doesn't let you do <<. But it simplifies querying.
Try:
Project
scope :developers, lambda {
includes(:project_users).where("project_users.role = ?", "developer")
}
You will be able to get all developers using: #project.developers

rails complex many-to-many query

I've got 3 models: User, Team, and Membership -
class Team < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
end
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :teams, :through => :memberships
def team_mates
teams = Team.where(:members => id)
team_mates = teams.members
end
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
validates :user_id, :presence => true
validates :team_id, :presence => true
end
And, I can't quite figure out how to write the team_mates method in the User model. It should return an array of the other users that are in a team with the current_user. My thought is that I should be useing a scope to limit Team to only include teams where the current user is a member but I can't quite figure out the syntax. Any help on this would be greatly appreciated.
Thanks!
Use the membership table to find users who share any team with the user you are calling the method on. Then to filter out duplicate users who share more than 1 team with current user, use distinct.
I haven't tested the below code, but hopefully it will get you on the right path:
def team_mates
m = Membership.scoped.table
Users.join(m).where(m[:team_id].in(team_ids)).project('distinct users.*')
end
UPDATE
Looks like some of the Arel methods in that answer don't have an easy mapping back to ActiveRecord land. (If someone knows how, I'd love to know!) And also it returns the 'current user'. Try this instead:
def team_mates
User.joins(:memberships).where('memberships.team_id' => team_ids).where(['users.id != ?', self.id]).select('distinct users.*')
end
How about?
User.joins(:memberships).joins(:teams).where("teams.id" => id).uniq
Maybe something like this
scope team_mates, lambda {|user_id, teams| joins(:memberships).where(:team_id => teams, :user_id => user_id)}
def team_mates
User.team_mates(self.id, self.teams.collect {|t| t.id})
end
Here is more info:
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html

How do i create an object if it has more than one belongs_to?

I have the following:
class Org < ActiveRecord::Base
has_many :users
has_many :entries
end
class Entry < ActiveRecord::Base
belongs_to :org
belongs_to :user
validates_presence_of :entry_text
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end
I can Create Orgs and Users... How do i create an entry if there are two belongs_to? and what is this pattern called?
Double nested resources are tricky. The trick with users usually is to keep it out of your desired entry path.
Your question is kind of broad, but if you specify more information, people would be able to help you better. Also, I would recommend using the gem Devise for your user management system. Since you're using 'users' I would assume you want users from orgs to create entries. The entry created would be a part of org and the user would be the session's current user. Sorry if I am wrong to assume this.
Your routes.rb file can look something like this (assuming rails 3):
resources :orgs do
resources :entries
end
Then the create of your entry controller would look like:
#entry = #org.entries.new(params[:topic])
#entry.user = current_user #or however you are managing the current user's session.
And you'd want to set the org for the entire class by making a method that loads your current org and do a before_filter :loadOrg
def loadOrg
#org = Org.find(params[:id])
end
This is of course assuming your path is something like: /org/(id)/entry/(entry_id)
and not
/org/(id)/user/(user_id)/entry/(entry_id)
which in my opinion is unnecessary and can lead to more problems. You can always create a userpage model that calls all entries by users, but the default route doesn't necessarily have to include users in the path.
I don't see any problem.
#entry = Entry.create(:entry_text => "Hello World!")
Now questions to clarify what do you need:
Can #entry belongs both org and user at the same time? Or it can belongs to only one of them?
Should #entry belongs to at least one of them?
If #entry supposed to belong only one of them, so you should use Polymorphism
http://railscasts.com/episodes/154-polymorphic-association
class Entry < ActiveRecord::Base
belongs_to :textable, :polymorphic => true
validates_presence_of :entry_text
end
class Org < ActiveRecord::Base
has_many :users
has_many :entries, :as => :textable
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries, :as => :textable
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end