I want to override the #build method for an ActiveRecord::Associations::CollectionProxy. I'm trying to write this custom #build method from within the Note class so that when note.topics.build is called it will run custom code. My overarching goals is that when I execute Note.new(topics_attributes), the custom build method is called. From my understanding, I need this to ensure that (a) associated objects(topics) with the same name don't get created and (b)if a topic does have the same name as another existing topic, the existing topic is retrieved. I can get (a) with the validates :name, uniqueness: true
macro, but I don't think I can get (a) and (b) with existing macros (please correct me if that is wrong).
Here is my best attempt so far:
class Note < ApplicationRecord
has_and_belongs_to_many :topics do
def build(attributes = {}, &block)
super
attributes.values.each do |topic_name|
if topic_name.present?
new_topic = Topic.find_by(name: topic_name) || Topic.new(name: topic_name)
if self.persisted?
new_topic.notes << self unless self.topics.pluck(:id).include?(new_topic.id)
else
self.topics << new_topic
end
end
end
end
end
This is more or less the logic I want. The problem is that self is referring to the Topic::ActiveRecord::Associations::CollectionProxy object, but I want it to refer to the instance of Note that .topics.build will be called on.
Any thoughts on how to get some variable to reference the Note instance? Or perhaps, there's a better strategy to get this to work?
The other strategy I tried was to write the custom method as a instance method for the Note class. Trying write a method def topics.build
through an error along the line "topics method note defined for Note class".