Question

I would like to know the common practice to inject code from a module/class into another module/class. I have:

module MyModule
  extend ActiveSupport::Concern

  included do
    # The has_many association for Article / Post.
    has_many :comments

    # Here, from MyModule, I would like to "inject" the related belongs_to 
    # association into Comment where the belongs_to association should be
    # 'belongs_to :article' for Article and 'belongs_to :post' for Post.
  end

  module ClassMethods
    # ...
  end
end

class Article < ActiveRecord::Base
  include MyModule
end

class Post < ActiveRecord::Base
  include MyModule
end

class Comment < ActiveRecord::Base
end

To inject the belongs_to association into Comment, I can use the instance_exec method, but maybe, there is a more smart approach. What should I do in order to avoid repetition in code? Should I include other modules in Comment, or keep all the related code in MyModule, and inject it from there? How to proceed in these cases?

Was it helpful?

Solution

When a model belongs to more than one other model you can use polymorphic associations, and you can use class_eval to include a module:

module MyModule  
  extend ActiveSupport::Concern  

  included do  
    has_many :comments, as: postable  

    Comment.class_eval do
      include Commentable
    end
  end 

  Module Commentable
    extend ActiveSupport::Concern

    included do
      belongs_to :postable, polymorphic: true
    end

    def ping
      p 'pong'
    end
  end 
end

class Article < ActiveRecord::Base
  include MyModule
end


class Post < ActiveRecord::Base
  include MyModule
end

class Comment < ActiveRecord::Base
end

In a non-polymorphic way you can define included like this:

def self.included(base)
  Comment.class_eval do
    belongs_to base.name.underscore # Will result in belongs_to :article and belongs_to :post in this case
  end
end

OTHER TIPS

I use the code like the following to do something similar in one of my projects:

module MyModule
  extend ActiveSupport::Concern
  included do
    ...
  end

  def self.included(other)
    Comment.belongs_to(other.to_s.underscore.to_sym, {:options => 'whatever'})
  end

end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top