Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
151 views
in Technique[技术] by (71.8m points)

Extending controllers of a Rails 3 Engine in the main app

I am using a Rails engine as a gem in my app. The engine has PostsController with a number of methods and I would like to extend the controller logic in my main app, e.g. to add some methods. If I just create PostsController in the main app, then the engine's controller is not loaded.

There is a solution proposed in question Rails engines extending functionality based on altering ActiveSupport::Dependencies#require_or_load

Is it the only/correct way to do this? If yes, where do I put that piece of code?

EDIT1:

This is the code suggested by Andrius for Rails 2.x

module ActiveSupport::Dependencies
  alias_method :require_or_load_without_multiple, :require_or_load
  def require_or_load(file_name, const_path = nil)
    if file_name.starts_with?(RAILS_ROOT + '/app')
      relative_name = file_name.gsub(RAILS_ROOT, '')
      @engine_paths ||= Rails::Initializer.new(Rails.configuration).plugin_loader.engines.collect {|plugin| plugin.directory }
      @engine_paths.each do |path|
        engine_file = File.join(path, relative_name)
        require_or_load_without_multiple(engine_file, const_path) if File.file?(engine_file)
      end
    end
    require_or_load_without_multiple(file_name, const_path)
  end
end
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

By design, classes in a Rails::Engine are supposed to be scoped to the engine. That way they don't introduce strange bugs by accidentally stomping all over code loaded in the main app or by other engines. Monkeypatching ActiveSupport::Dependencies to mix engines across-the-board is a really bad workaround.

Just use a Rails::Railtie, instead. They have all the same functionality, but aren't scoped the same way as an engine. You have access to the entire rails app stack (including engines). It's a more surgical approach.

module MyModule

  module SomeModelExtensions
    # Called when this module is included on the given class.
    def self.included(base)
      base.send(:include, InstanceMethods)
      base.extend(ClassMethods)
    end

    module ClassMethods
      def some_new_class_method
        # do stuff...
      end
    end

    module InstanceMethods
      def some_new_instance_method
        # do stuff...
      end
    end

  end

  module SomeControllerExtensions
    def self.included(base)
      base.send(:include, InstanceMethods)
      base.alias_method_chain :new, :my_module
    end

    module InstanceMethods
      # override the 'new' method
      def new_with_my_module
        # do stuff
      end
    end
  end

  class Railtie < ::Rails::Railtie

    # The block you pass to this method will run for every request in
    # development mode, but only once in production.
    config.to_prepare do
      SomeModel.send(:include, MyModule::SomeModelExtensions)
      SomeController.send(:include, MyModule::SomeControllerExtensions)
    end

  end

end

As far as file layout, railties look exactly like engines.

Further reading: Extending Rails 3 with Railties

And if you're still confused, take a look at this git project which has a full implementation: https://github.com/jamezilla/bcms_pubcookie


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...