Mix Polymorphic associations with STI or MTI in Ruby on Rails - ruby

I have a problem or dilemma after implementing polymorphic association following this: All the code is located here,
I have implemented this model:
Let's suppose that I also need the subscription to magazines. It will also be something similar to the other two
class Magazines < ApplicationRecord
has_many :subscriptions, as: :subscribable
end
class User < ApplicationRecord
has_many :subscriptions
has_many :podcasts, through: :subscriptions, source: :subscribable, source_type: 'Podcast'
has_many :newspapers, through: :subscriptions, source: :subscribable, source_type: 'Newspaper'
has_many :magazines, through: :subscriptions, source: :subscribable, source_type: 'Newspaper'
end
class Subscription < ApplicationRecord
belongs_to :subscribable, polymorphic: true
belongs_to :user
end
It works well, the problem now is ok I can handle like three types of subscription
MagazineSubscription, PodcastSubscription and NewspaperSubscription. The three have the same attributes and same behaviour, but belongs to different model. What happens If after doing that I need some kind of MTI or STI with the subscription classes. i.e. the MagazineSubscription have different behaviour and maybe other attributes. There is an easy way on this to accomplish this new requirement like creating a Subscription class that handles all that the polymorphic association and the other models:
class Subscription < ActiveRecord::Base
self.inheritance_column = :sti_subscription
belongs_to :subscribable, polymorphic: true
belongs_to :user
def _type=(sType)
sti_subscrition = sType.to_s.classify.constantize.base_class.to_s + "Subscription"
super(sType.to_s.classify.constantize.base_class.to_s)
end
end
class MagazineSubscription < Subscription
# new behaviour here
end
or I must follow something similar to this with the Subscription class handling two polymorphic relations, with the subscribable and his descendants
So my question is when I have used polymorphic association is an easy way to use that to set STI or MTI, or I need to make a new approach

Related

ruby remove common element collections

I have three classes: User, Subscription and Plan. I want to load all of the Plans that the User doesn't have. What's the best way to do it in Rails?
I have two collections: current_user.subscriptions and Plan.where(active: true)
And i am using mongoid
def dashboard
#plans = Plan.where(active: true)#.each { #plans.delete_if has_current_user_plan subscription.title }
end
def has_current_user_plan(name)
current_user.subscriptions.where(title: name, active: true).exists?
end
class User
has_many :subscriptions
class Subscription
belongs_to :plan
belongs_to :user
class Plan
has_many :subscriptions
AR:
class User < ActiveRecord::Base
has_many :subscriptions
has_many :plans, through: :subscriptions # !!!
end
Plan.where(active: true).where.not(id: current_user.plans)
I'm not really sure what's the best approach for Mongoid because I've never used it. From what I've gather from the documentation, something like the following might work although I'm not running the code.
Plan.where(active: true).not_in(_id: Subscription.where(user_id: current_user.id).pluck(:plan_id))

Activerecord checking if association exist

I have two ActiveRecord models:
class Class < ActiveRecord::Base
(...)
has_many :class_to_teacher, dependent: :destroy
end
class Teacher < ActiveRecord::Base
has_many :classes,
through: :class_to_teacher
end
class ClassToTeacher < ActiveRecord::Base
belongs_to :klass
belongs_to :teacher
end
When I remove Class I don't remove it completly only I remove record from ClassToTeacher record. I need to keep this data:
def leave(class, teacher)
teacher.klasses.delete(class)
end
Now I have to get all associated classes (classes which contains record in ClassToTeacher). How to do this the best? Thanks for all answers.
You should try something like that:
Class.includes(:class_to_teacher).where('class_to_teacher.id is not null').references(:class_to_teacher).all
Assuming that you have an 'id' field in your database for the ClassToTeacher
I guess there's a lot of ways to get there but the simplest one is probably this:
Klass.where(id: KlassToTeacher.select(:klass_id))
This will result in a single query with a sub-query. Note that I changed the names of the models because Class is already defined in Ruby and you're just asking for trouble.

How to get the association name in rails?

I have the following association code in my user.rb model file
class User < ActiveRecord::Base
has_many :sent_messages, class_name: 'ChatMessage', foreign_key: 'sender_id'
has_many :received_messages, class_name: 'ChatMessage', foreign_key: 'receiver_id'
end
I want a method in the ChatMessage model which should be triggered by the following
current_user.sent_messages
current_user.received_messages
The method should return the name of the association that was called.
Eg:
class ChatMessage < ActiveRecord::Base
after_find :get_association_name
def get_association_name
self.association_name // this should return sent_message or received_message depending on whether current_user.sent_messages or current_user.received_messages was called
end
end
Is there a way to get this association name in rails?
Any help is much appreciated. Thanks
I am not sure, exactly what you are looking for, but
CurrentUser.reflect_on_all_associations(:has_many)
will give an array of all has_many associations.
I haven't used AR associations extensions for cases like this, but you should be able to do:
has_many :sent_messages, class_name: 'ChatMessage', foreign_key: 'sender_id' do
def get_association_name; 'sent_messages'; end
# or, to make this more generic,
# def get_association_name; proxy_association.reflection.name.to_s; end
end
And the method should be accessible from your relation. If you were using Rails 4, you could extract the generic version out into a separate module to extend your associations more succinctly. See http://guides.rubyonrails.org/association_basics.html#association-extensions.
EDIT
Try:
has_many :sent_messages, class_name: 'ChatMessage', foreign_key: 'sender_id' do
def and_set_type
proxy_association.target.each do |msg|
msg.update_attribute(:type, 'sent')
end
scoped
end
end
And then access your sent_messages with current_user.sent_messages.and_set_type.

Active Record has_many through more than one model

Is it possible to access objects more than one model away?
For example let's say I have
class Contact <ActiveRecord:Base
has_many :interactions
end
class Interaction <ActiveRecord:Base
belongs_to :contact
belongs_to :course_presentation
end
class CoursePresentation <ActiveRecord:Base
has_many: interactions
belongs_to :course
end
class Course <ActiveRecord:Base
has_many :course_presentations
end
Right now I know I could write a through relationship via contacts to course presentations and then get all the course related to all the course presentations or I could do
contact.interactions.map{ |i| i.course_presentation.course }
I would like to be able to pull courses related to a contact directly so ... e.g.
contact.courses
Is this possible?
Yes, I believe so. Just add the following:
class Contact < ActiveRecord::Base
has_many :interactions
has_many :course_presentations, through: :interactions
has_many :courses, through: :course_presentations
end

Setting ids in has_many relationship between three models

A user can create a post. Posts have comments. A comment must belong to both a user and a post, however not necessarily the user who created the post. Is the following the best way to model this:
class User < ActiveRecord::Base
has_many :posts
has_many :comments
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
If so, what is the best practice for ensuring:
user.comments.new cannot be called. I want all new posts to be created using the post they are associated with.
The id of the user creating the comment is properly set when calling post.comments.new
Otherwise, what is a better way to model this relationship?
Your models are fine.
To set the author of the comment, you'd have to do that in your controller:
#comment = post.comments.new do |c|
c.user = current_user
end
Just don't use user.comments.new. Why do you want to make it impossible to use it?

Resources