how to implement relationships between models with mongoid - ruby-on-rails

1º user John has many gifts
user.rb
class User
include Mongoid::Document
has_many :gifts, dependent: :destroy, :autosave => true
has_many :orders, dependent: :destroy, :autosave => true
end
gift.rb
class Gift
include Mongoid::Document
belongs_to :user
has_many :orders ,dependent: :destroy, :autosave => true
end
2º user Anthony buy a gift to John and make a new order
class Order
include Mongoid::Document
belongs_to :gift
belongs_to :user
end
Now the user Anthony wants to access all his sales made.
The challenge here is that a user may have two roles, buyer or a seller.
How should I develop relationships between models that Antonio can access his sales made?

Models structure
class User
include Mongoid::Document
has_many :gifts, dependent: :destroy, :autosave => true
has_many :orders, dependent: :destroy, :autosave => true
end
class Gift
include Mongoid::Document
belongs_to :user
belongs_to :gifted_to, :class_name => 'User'
has_one :order ,dependent: :destroy, :autosave => true
end
class Order
include Mongoid::Document
belongs_to :gift
# below associtation is just for quicker ref
# otherwise you can have access to it via gift also
belongs_to :user
end

Related

Multiple polymorphic relations of same type in one model - MongoID

I couldn't figure out how to work the title correctly, so let me explain.
I have my class Like:
class Like
include Mongoid::Document
belongs_to :likable, polymorphic: true
belongs_to :user
end
class Submission
include Mongoid::Document
belongs_to :creator, class_name: 'User', inverse_of: :submissions
has_many :likes, :as => :likable, :dependent => :destroy
end
Then the class that causes the problem:
class User
include Mongoid::Document
has_many :submissions, :dependent => :destroy
has_many :liked_submissions, :as => :likable, :dependent => :destroy, class_name: 'Like'
has_many :liked_comments, :as => :likable, :dependent => :destroy, class_name: 'Like'
end
Now, the problem is that I can't figure out how to properly have both 'liked_comments' and 'liked_comments' in the user class. When I have it as I do above I get the error: Ambiguous relations :liked_submissions, :liked_comments defined on User. when trying to create a like for a submission.
I looked around, and I found a Github issue that said that this may not be possible, but I can't find this issue again, and I wasn't even sure that it was related to this issue.
Is this possible, and if not, any suggestions to getting around it? I would like to use likes in a BlogPost model as well, but if this doesn't work, I'll have to find another way.
Thanks in advance.
I tried this and it works using mongoid version 4.0.2:
class Like
include Mongoid::Document
belongs_to :likable, polymorphic: true
belongs_to :user, inverse_of: :user
end
class Submission
include Mongoid::Document
belongs_to :creator, class_name: 'User', inverse_of: :submissions
has_many :likes, as: :likable, dependent: :destroy
end
class User
include Mongoid::Document
has_many :submissions, dependent: :destroy
has_many :likes, as: :user
has_many :liked_submissions, as: :likable, dependent: :destroy, class_name: 'Like'
has_many :liked_comments, as: :likable, dependent: :destroy, class_name: 'Like'
end
class BlogPost
include Mongoid::Document
has_many :likes, as: :likable, dependent: :destroy
end
Also user.liked_submissions and user.liked_comments are the same relation so it is equivalent to declaring an alias like this:
class User
include Mongoid::Document
has_many :submissions, dependent: :destroy
has_many :likes, as: :user
has_many :liked_submissions, as: :likable, dependent: :destroy, class_name: 'Like'
alias :liked_comments :liked_submissions
end
Had tough times after discovering this issue but got cool solution that works
Add to your Gemfile
gem 'mongoid-multiple-polymorphic'
And this works like a charm:
class Resource
has_one :icon, as: :assetable, class_name: 'Asset', dependent: :destroy, autosave: true
has_one :preview, as: :assetable, class_name: 'Asset', dependent: :destroy, autosave: true
end

In Rails, how would you merge these two different has_manys into one?

Let's say we have a User.
A user has_many documents through account like so…
class User < ActiveRecord::Base
belongs_to :account
has_many :documents, :through => :account, :order => "created_at DESC"
end
class Account < ActiveRecord::Base
has_one :owner, :class_name => "User", :dependent => :destroy
has_many :documents, :dependent => :destroy
end
class Document < ActiveRecord::Base
belongs_to :account
end
Nice and simple, this is where it gets tricky…
A user can also collaborate on documents, this via the collaborators join table…
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :documnet
end
class Document < ActiveRecord::Base
has_many :collaborators, :dependent => :destroy
has_many :users, :through => :collaborators
accepts_nested_attributes_for :collaborators, :allow_destroy => true
end
The final user bit of this is what i'm not sure about. I want to add another has many documents, and when you call user.documents it blends both documents via their account and the ones they're collaborating on…
class User < ActiveRecord::Base
belongs_to :account
has_many :documents, :through => :account, :order => "created_at DESC"
#documents need to do both has manys…
has_many :collaborators, :dependent => :destroy
has_many :documents, :through => :collaborators
end
Thanks, it's a bit long but I can think of a neat solution. Any help would be much appreciated.
You can create a method that will request on the tables documents, accounts and collaborators to find the documents related to the user:
class User < ActiveRecord::Base
#...
def documents
Document.includes(:account, :collaborators).where('collaborators.user_id = ? OR documents.account_id = ?', self.id, self.account.id)
end
end
I've not tested this request, but I hope you get the idea. Please correct it if it's erroneous.
For the 2 has_many documents, :through..., you can remove them if you don't need them anymore; Otherwise, you have to give them different names (and different from the method above).

Mongoid has_many relation returning no values

Suppose an app where users can give gifts to other users. There are two relations between the gift and the user (one as sender and the other one as receiver).
The receiver part seems to not be working according to the following code, where a gift is created but not retrieved when calling the association:
require 'rubygems'
require 'mongoid'
Mongoid.load!("./config/mongoid.yml")
class User
include Mongoid::Document
has_many :gifts
has_many :gifts_sent, class_name: "Gift", as: :sender
end
class Gift
include Mongoid::Document
belongs_to :user, inverse_of: :gifts
belongs_to :sender, inverse_of: :gifts_sent, class_name: "User"
end
alice = User.create!
bob = User.create!
gift = Gift.create! sender: alice, user: bob
puts Gift.where(sender_id: alice.id).count # => 1 (nice)
puts alice.gifts_sent.count # => 0 (not so nice)
How should the association be defined to make the last line output 1?
You have to give a inverse_of to has_many gifts relation.
And to work the second relation named gifts_sent, You have to mention the foreign_key while defining relation.
class User
include Mongoid::Document
has_many :gifts, inverse_of: :user
has_many :gifts_sent, :foreign_key => :assign_whatever_field, class_name: "Gift", inverse_of: :sender,
end
And the gift Model will be
class Gift
include Mongoid::Document
belongs_to :user, inverse_of: :gifts
belongs_to :sender,:foreign_key => :assign_whatever_field, inverse_of: :gifts_sent, class_name: "User"
end
It is necessary to include the inverse relation in the User model as well:
class User
include Mongoid::Document
has_many :gifts, inverse_of: :user
has_many :gifts_sent, inverse_of: :sender, class_name: "Gift"
end
class Gift
include Mongoid::Document
belongs_to :user, inverse_of: :gifts
belongs_to :sender, inverse_of: :gifts_sent, class_name: "User"
end

How to define CanCan abilities with model associations

I'm using CanCan to define abilities in my app.
Question is how to define ability of a model that belongs to the User through an association.
For example this works because Campaign has an affiliate_id field
can :manage, Campaign, :affiliate_id => affiliate.id
However, Affiliates have Subscriptions and Messages through Keywords. Keywords belong to a Campaign.
All associations are working fine. However I'm not sure how to limit only Subscriptions and Messages to the current_user.
These are my current associations
# affiliate.rb
has_many :keywords, :through => :campaigns
has_many :campaigns
# campaign.rb
belongs_to :affiliate
has_many :keywords
has_many :subscriptions, :through => :keywords
has_many :messages, :through => :subscriptions
# keyword.rb
has_many :subscriptions
has_many :messages
belongs_to :campaign
# message.rb
belongs_to :user
belongs_to :campaign
belongs_to :subscription
belongs_to :keyword
# subscription.rb
belongs_to :campaign
belongs_to :affiliate
belongs_to :keyword
has_many :messages
Also, (bonus question), how can I show all objects if the user is admin? I have something like this but It's not retrieving all objects.
if affiliate.status == "admin"
can :manage, :all
end

How do I model these relationships?

I have a contact model, this includes name, address, phone number, etc.
I have a user model which should have_one contact.
I have a Customer model which has_many contacts.
I have a Producer model which has many contacts.
A contact can be only a user, a user and a customer, a user and a producer, or any combination of these three. I also need to be sure that the same contact record is linked when a contact is linked to multiple models for data integrity.
how should I create the associations?
This looks like a good application for a polymorphic association:
class User < ActiveRecord::Base
has_one :contact, :as => :contactable
end
class Customer < ActiveRecord::Base
has_many :contacts, :as => :contactable
end
class Producer < ActiveRecord::Base
has_many :contacts, :as => :contactable
end
class Contact < ActiveRecord::Base
belongs_to :contactable, :polymorphic => true
end
EDIT
It seems I didn't read the specs all the way through :) To associate the same contact with multiple Users, Customers, etc, you could use a has_many :through:
class User < ActiveRecord::Base
has_one :user_contact, :dependent => :destroy
has_one :contact, :through => :user_contact
end
class Customer < ActiveRecord::Base
has_many :customer_contacts, :dependent => :destroy
has_many :contacts, :through => :customer_contacts
end
class Producer < ActiveRecord::Base
has_many :producer_contacts, :dependent => :destroy
has_many :contacts, :through => :producer_contacts
end
class UserContact
belongs_to :user
belongs_to :contact
end
class CustomerContact
belongs_to :customer
belongs_to :contact
end
class ProducerContact
belongs_to :producer
belongs_to :contact
end
class Contact < ActiveRecord::Base
has_many :user_contacts, :dependent => :destroy # might use 'has_one' here depending on your requirements
has_many :users, :through => :user_contacts
has_many :customer_contacts, :dependent => :destroy
has_many :customers, :through => :customer_contacts
has_many :producer_contacts, :dependent => :destroy
has_many :producers, :through => :producer_contacts
end
That gives you one join table for each of the three associations. Each Contact can belong to none, one, or many of the three other models by adding rows to the join tables.

Resources