Self reference in Mongoid is not working in both directions - ruby-on-rails

I have a class like:
class User
include Mongoid::Document
has_and_belongs_to_many :following, class_name: "User", inverse_of: :followers
has_and_belongs_to_many :followers, class_name: "User", inverse_of: :following
When I add a user as following another user like this:
def follow id
self.following.push User.find(id)
end
following gets updated, but the "followers" reference does not for the User that is now being followed. I'm assuming I don't have to manually push to create the inverse relationship, so what am I doing wrong?

Are you using devise by any chance? If so, you may have problems with saving the inverse user because it is missing password combination. The other user, that is, the user adding the following user is probably already authenticated and is saving fine. I discovered this problem in a similar situation myself.
I've asked another question on how to disable validation on inverse objects:
Mongoid: disabling validation on inverse objects when saving parent for HABTM relationship Options

Related

rails join table issue with different roles (owner, non-owner)

In my app users can create products so at the moment User has_many :products and Product belongs_to :user. Now I want the product creator product.user to be able to invite other users to join the product, but I wanna keep the creator the only one who can edit the product.
One of the setups I've got in my mind is this, but I guess it wouldn't work, since I don't know how to distinguish between created and "joined-by-invitation" products when calling user.products.
User
has_many :products, through: :product_membership
has_many :product_memberships
has_many :products # this is the line I currently have but think it wouldn't
# work with the new setup
Product
has_many :users, through: :product_membership
has_many :product_memberships
belongs_to :user # I also have this currently but I'd keep the user_id on the product
# table so I could call product.user and get the creator.
ProductUsers
belongs_to :user
belongs_to :product
Invitation
belongs_to :product
belongs_to :sender, class: "User"
belongs_to :recipient, class: "User"
To work around this issue I can think of 2 solutions:
Getting rid of the User has_many :products line that I currently have and simply adding an instance method to the user model:
def owned_products
Product.where("user_id = ?", self.id)
end
My problem with this that I guess it doesn't follow the convention.
Getting rid of the User has_many :products line that I currently have and adding a boolean column to the 'ProductUsers' called is_owner?. I haven't tried this before so I'm not sure how this would work out.
What is the best solution to solve this issue? If none of these then pls let me know what you recommend. I don't wanna run into some issues later on because of my db schema is screwed up.
You could add an admin or creator attribute to the ProductUsers table, and set it to false by default, and set it to true for the creator.
EDIT: this is what you called is_owner?
This seems to be a fairly good solution to me, and would easily allow you to find the creator.
product.product_memberships.where(is_owner?: true)
should give you the creator

Rails 4 return tasks associated with more than one user

Question: How can I return Assignments associates with one of many users in the users array?
I researched the Rails guides and some only posts but I can't figure this out yet.
https://codereview.stackexchange.com/questions/46319/is-there-a-better-approach-to-searching-has-and-belongs-to-many-relations
http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
http://guides.rubyonrails.org/active_record_querying.html#retrieving-multiple-objects-in-batches
I am associating users to assignments two different ways.
1- user "user_id" is the one who creates the assignment
2- The assignment is given to multiple users. Users are associated to assignments using has_and_belongs_to_many :users
Basically each assignment is associated to the user who owns the task.
The assignment is also given to multiple users who will work on it.
I can successfully return all tasks associated with user but not with users.
I'm trying to display only the tasks associated with the current_user (devise)
This works for user:
assignment.rb
class Assignment < ActiveRecord::Base
belongs_to :deliverable
belongs_to :user
has_and_belongs_to_many :users
end
user.rb
has_and_belongs_to_many :assignments
The association are working fine. In the console I can get all the users associated via HABTM to the assignment.
I have a designer dashboard where i only want to display assignments given to the current user.
designer_dashboard controller:
#if I do this I'll get all the assignments:
#assignments = Assignment.all
# but I want to be able to do something like this to get only the assingments associated with the current user via HABTM
#assignments = Assignment.includes(:users).where(["user_ids =?", current_user])
The data isn't modelled to reflect the 2 different types of relationship that exist between users and tasks - task owners and task designers. You've only set up one of them.
You will need to remodel the data and give the relationships more meaningful names.
One way would be to use has_many_through for the association that a Task has with who its assigned to. A Task has_many Designers through AssignedTasks. The association between Task and User can be named as TaskOwner. If you set up both these associations you will be able to get current_user.owned tasks and current_user.assigned tasks which is more clear than referring to user and users.
class Task
belongs_to :task_owner, class_name: "User"
has_many :assigned_tasks
has_many :designers, through: assigned_tasks, class_name: "User"
end
class User
has_many :owned_tasks, class_name: "Task"
has_many :assigned_tasks, foreign_key: designer_id
has_many :tasks, through: :assigned_tasks,
end
class AssignedTask
belongs_to :designer
belongs_to :task
end
You will need to generate some migrations to add the requisite ids.
Also, I seem to remember reading somewhere that Task is a reserved word. You may want to rename task to something else.
Margo answer is correct.
This works in controller:
#assignments = current_user.assignments

Has_and_belong_to_many relation with the same model

I am trying to implement a has_and_belong_to_many relation with the same model, but I don't know how.
For Example, a user should be able to follow other users.
Also, I have multiple fields of the same model in that model; how would I give it another name?
There are two scenarios and two different implementations:
The 'Friends' model
Let's say one User can have many :friends where each friend is also an object of the User model. You can do it this way:
has_and_belongs_to_many :friends, class_name: 'User'
This tells rails that an object of User class can have a many-to-many relation with itself as friends. So you can call something like this:
#user_a.friends
#=> [#user_x, #user_y, #user_z] # Array of User Objects
#user_x.friends
#=> [#user_a, #user_b, #user_c] # Array of User Objects
The 'Followers/Following' model
Let's say one User can follow other users as well have other users follow him. This is how you'll implement it:
has_many :followers, class_name: 'User', inverse_of: :following
belongs_to :following, class_name: 'User', inverse_of: :followers
This tells rails that each user can have many followers which is an Array of other User objects, and that user is accessible to others as an object in the following array. For example, if #user2 follows #user1, it would look like this:
#user1.followers
#=> [#user2, #user3]
#user2.following
#=> [#user1]

Delete Rails model and its associations in one action? NEWBIE question

I have a really simple association with the Devise user object where each user has one Profile (with more application specific stuff...) I am having no issues creating the User object and accessing the user and its profile object. i.e.,
#user.profile
However, I'm having an issues when I try to delete the profile object - I'd assume that when I delete the User object, it would also delete each associated object. The association in my User object is like so
accepts_nested_attributes_for :profile, :allow_destroy => true
The has_one and belongs_to associations are set on both the User and Profile objects. Maybe the issues is in Devise code - I'm stumped. An idea what I'm missing here.
You need to specify :dependent on the association:
has_one :profile, :dependent => :destroy
Look Association for more information.

How to associate descendant models to a parent

I have the following models:
Account
has_many :libraries
Library
has_many :topics
belongs_to :account
Topic
has_many :functions
belongs_to :library
Function
has_one :example
belongs_to :topic
Example
belongs_to :function
I would like to be able to able to do things such as:
some_account.libraries
some_account.topics
some_account.functions
some_account.examples
In addition, I would like to be able to assign an account to a descendant, i.e
some_example.account = some_account
some_function.account = some_account
some_topic.account = some_account
some_library.account = some_account
To give some context:
I am letting a user (Account) create each Library, Topic, Function, Example. record separately. Then a user is free to change how the records are associated: Change the topic of a Function, move a Topic to a different Library, add an example to a function, and so on.
To my understanding no matter what record is created, I would need to assign it to a user (account) so that I can have a list of each Model records that a user has created, as well as prevent other users from seeing stuff that doesn't belong to them
Although I might be overcomplicating, I really don't know :(
Thanks in advance.
Just put
belongs_to :account
on each entity a user can make... and add a foreign key, and
Account
has_many :libraries
has_many :topics
has_many :functions
has_many :examples
(Note: I use the hobo_fields gem to make migrations easier)
That way.. if they change which functions are in which topics etc.. you can't loose who created it.
If you want to make sure users cannot add their topics to someone else's library just put validation on the record to prevent it.

Resources