rfish26535
rfish26535

Reputation: 459

Add associations from mixin without overwriting existing associations

I have a number of classes like the below:

class Event < ActiveRecord::Base
  include PreventUpdate

  has_many :participants, conditions: ...
  has_many :venues, conditions: ...
  has_many :companies, conditions: ...
end

I have a module that handles prevent_update logic (obj is in past, obj is banned) for the classes, and as part of that it queries the including class for its has_many associations in order to write before_add hooks to those associations.

module PreventUpdate
  extend ::ActiveSupport::Concern

  included do 
    self.reflect_on_all_associations(:has_many).each do |assoc|
      has_many assoc.name, before_add: :prevent_update
    end
  end

  def prevent_update
    #core prevent update logic
  end

end

The only problem is that the dynamically added and original has_many statements overwrite each other. Which overwrites which depends on where in the including class the module is included.

Is there any way that the dynamically added and original declarations can "accumulate", i.e. the module's declaration can simply add on to the original without overwriting?

Upvotes: 2

Views: 404

Answers (1)

Jacob Brown
Jacob Brown

Reputation: 7561

This is untested, but you should be able to just do:

included do 
  self.reflect_on_all_associations(:has_many).each do |assoc|
    assoc.options.merge!(:before_add => :prevent_update)
  end
end

This would require that the concern include come after the has_many declarations. If you want to do the include before them, you could add:

module ClassMethods
  def modify_associations
    self.reflect_on_all_associations(:has_many).each do |assoc|
      assoc.options.merge!(:before_add => :prevent_update)
    end
  end
end

and then:

# ...    
has_many :companies, conditions: ...
modify_associations

EDIT

This should work:

included do 
  self.reflect_on_all_associations(:has_many).each do |assoc|
    has_many assoc.name, assoc.options.merge(:before_add => :prevent_update)
  end
end

Upvotes: 2

Related Questions