Reputation: 519
I'm working on a application in which there is table named companies
and a company can have multiple users
and users can have multiple books
and magazines
belonging to company. Currently the associations is as follows:
Company.rb
has_many :users
has_many :books
has_many :magazines
User.rb
belongs_to :company
has_many :user_books, dependent: :destroy
has_many :books, through: :user_books
has_many :user_magazines, dependent: :destroy
has_many :magazines, through: :user_magazines
Book.rb
belongs_to :company
has_many :user_books, dependent: :destroy
has_many :users, through: :user_books
Same association exists for magazines as well. Not using has_and_belongs_to_many
association as we are capturing additional data in association table.
Now we need to introduce a new entity called audio_books
similar to books
and magazines
but instead of creating one more association table called user_audio_books
we are planning to create a polymorphic association table called user_items
which would record the association between user and the company items(books, magazines & audio_books).
New association: User.rb
has_many :user_items, dependent: destroy
has_many :books, through: :user_items, as: :source, source: source, source_type: 'Book'
has_many :magazines, through: :user_items, as: :source, source: source, source_type: 'Magazine'
However this new association will be enabled only for few companies for time being, until then we need to support all the 16 methods added by has_many association on both the models on conditional basis.
I'm looking for something like in user.rb:
has_many :user_books, dependent: :destroy
has_many :user_items, dependent: :destroy
<if company.audio_books_enabled? >
has_many :books, through: :user_items, as: :source, source: source, source_type: 'Book'
<else>
has_many :books, through: :user_books
<end>
audio_books_enabled?
is not an sql query but a redis based check.
Is it possible to achieve this?
Upvotes: 3
Views: 667
Reputation: 29751
You can't have two associations with the same name. But a method that would return different associations could work:
class User < ApplicationRecord
belongs_to :company
has_many :user_books, dependent: :destroy
has_many :user_items, dependent: :destroy
# NOTE: rename the old `books` association
has_many :legacy_books, through: :user_books, source: :book, class_name: "Book"
# NOTE: add another `books` association
has_many :new_books, through: :user_items, as: :source, source: :source, source_type: "Book"
def books
if company.audio_books_enabled?
new_books
else
legacy_books
end
end
# maybe something like this for associating books with user
def books= params
if company&.audio_books_enabled?
self.new_books = params
else
self.legacy_books = params
end
end
end
You could have a books
association and books
method if you have to:
class User < ApplicationRecord
belongs_to :company
has_many :user_books, dependent: :destroy
has_many :user_items, dependent: :destroy
# NOTE: rename the old `books` association
has_many :legacy_books, through: :user_books, source: :book, class_name: "Book"
# NOTE: add another `books` association
has_many :books, through: :user_items, as: :source, source: :source, source_type: "Book"
def books
if company.audio_books_enabled?
super
else
legacy_books
end
end
end
Upvotes: 1
Reputation: 1104
You write that "this new association will be enabled only for few companies for time being" by which I assume you mean "I would only like to allow some companies to create these objects/associations".
You can do that with a validation:
# user_item.rb
validate do |user_item|
errors.add(:company, :audio_books_not_enabled, message: "does not have audio books enabled") unless user_item.company.audio_books_enabled?
end
Upvotes: 0