简体   繁体   中英

Delegate class in main app to class in rails engine

I have an isolated Rails Engine: Admin .

In that Engine I create Sites through a GUI.

in Engine:

module Admin
  class Site < ActiveRecord::Base
  end
end

In main app I inherit from the engine's Site in order to use it as a constant at the root level:

class Site < Admin::Site
end

I do this because it feels wrong to couple the Admin::Site in models, controllers and tests of the main app. But I guess there's some downsides to this approach and I also guess one could argue that the coupling is the same.

How can I delegate this in a better way than inheritance?

OR

Should I restructure my code and maybe put the Site class in a gem that both the main app and the Engine can use? What I would really like is an interface to the engine's class to reduce the entry points, and thus the coupling.

Sidenote, I have a total of 3-4 of these classes that resides in the engine but is used in the main app.

EDIT:

Maybe I should just wrap it like this:

class Site 
  def initialize(args = {})
    @klass = Admin::Site.new(args)    
  end

  def method_missing(method_name, *args, &block)
    @klass.send(method_name, *args, &block)
  end
end

Of course, then I could also narrow the interface of Site to only include what I need from Admin::Site in the main app.

Two possible solutions come to mind,
1) ActiveSupport::Conern
2) Open Classing

Given that you have isolated these 3-4 Models/Controllers in an Engine, one approach is to leverage the ActiveSupport::Concern to explicitly include your Engine's functionality in the MainApp.

More info here, Rails Docs on ActiveSupport::Concern

ActiveSupport::Concern example:

# Rails Engine
module SomeEngine
  module SomeController

    extend ActiveSupprt::Concern

    included do
      before_filter :some_before_filter
    end

    # regular instance methods
    def index 
      ...
    end

    protected
    def some_before_filter
      ....
    end

  end
end    

# MainApp
class SomeController < BaseController
  include SomeEngine::SomeController

  # rest of MainApp logic
end

Another common approach is to rewrite a Ruby class methods at run time ("open classing") . Spree does this by implementing the "decorator" pattern on for Engine model/contrller classes in the MainApp.

More info here, Spree's implementation of Decorators

"#class_eval do" example,

# in MainApp
SomeEngine::SomeController.class_eval do
  # logic added in the MainApp
end

I would also check out this RailsConf talk aout RailsEngnes,
http://confreaks.com/videos/863-railsconf2012-rails-engines-patterns

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM