5t111111
5t111111

Reputation: 710

How to set has_many association between parent app's model and mounted engine's model?

Following to RailsGuides instruction, I have created an engine for blogging system in my app. This blog engine is mounted as /blog.

RailsGuides shows how to add belongs_to association to the mounted engine's Article model. However, the parent app's User model still requires has_many association to the engine's Article model which is in different namespace.

How to set has_many association between parent app's model and mounted engine's model?

Thanks in advance.

Upvotes: 2

Views: 1034

Answers (2)

The accepted answer requires manual modification of main_app's parent model in order to set the has_many relationships to the engine's child model. So each time you add the engine to one your main_apps you would have to go into the main_apps models and set up all required relationships by hand.

A more robust, although more complicated, solution would be to use the decorator pattern in the engine so that the engine will auto-configure main_app's parent model with the relationships it needs.

By using this method you just need to add a setting to the engine initializer in your main_app and the engine will handle the rest.

In engine:

blog.gemspec.rb

s.add_dependency 'decorators' #this will install the decorators gem for use in engine

lib/blog/blog.rb

module Blog
  class Engine < ::Rails::Engine
    isolate_namespace Blog
    engine_name 'blog'

    #to set up main_app objects via decorators in engine
    config.to_prepare do
      Decorators.register! Engine.root, Rails.root
    end

  end
end

lib/blog.rb

require 'decorators'

module Blog

  mattr_accessor :user_class #Can now reference this setting as Blog.user_class

class << self

    #the following lets us add functionality to main_app user model

    def decorate_user_class!
      Blog.user_class.class_eval do
        has_many :articles, :class_name => "Blog::Article", :foreign_key => "user_id"
      end
    end

  end
end

app/decorators/lib/blog/user_class_decorator.rb

if Blog.user_class
  Blog.decorate_user_class!
else
  raise "Blog.user_class must be set in main_app blog.rb initializer"
end

In main app:

app/initializers/blog.rb

Blog.user_class = User

If you run rails console from main app, you will see relationships will have been set properly. The decorator pattern in the engine can also be used to extend the main_app's models and controllers in different ways, not just Activerecord relationships. Almost complete decoupling achieved!

Upvotes: 0

Jaffa
Jaffa

Reputation: 12719

In the rails application, you know what module you include, so you can simply specify the relation with the class name ;)

has_many :articles, class_name: 'Blog::Article'

check if this is the right syntax for your database adapter, e.g. I'm using this for Mongoid, but it should be the same with ActiveRecord AFAIK

Upvotes: 4

Related Questions