schneikai
8/8/2014 - 9:18 AM

How to add or overwrite methods from engine controllers or models in the app that uses the engine.

How to add or overwrite methods from engine controllers or models in the app that uses the engine.

# This simple solution open-classes the controller/model in the main app
# to add or overwrite methods.
# A drawback of this solution is that you won't be able to access the original
# method by using +super+.

# engine: app/controllers/myengine/my_controller.rb
# This is the controller in our engine with a index method defined.
module MyEngine
  class MyController < ApplicationController
    def index
    end
  end
end


# app: app/controllers/myengine/my_controller.rb
# Now we open-class the engines controller in our app that uses the engine
# and overwrite the +index+ method.
# We need to require the engines controller first because Rails would only load
# the first controller it finds and that is our overwritten controller here.
require_dependency MyEngine::Engine.config.root.join('app', 'controllers', 'myengine', 'my_controller.rb').to_s

class MyEngine::MyController
  def index
  end
end

# Source
# http://stackoverflow.com/questions/7719633/how-to-extend-rails-engines-controllers-properly
# http://edgeguides.rubyonrails.org/engines.html#overriding-models-and-controllers

# A modification of this solution is using the decorator pattern. It also modifies
# the engine controller/model by open-classing but the extensions/overwrites
# are defined in another directory for example the lib directory.
# I don't like that pattern because I think it hides to modifications too much.

# Also the solution on edgeguides loads the decorators in the main app
# via initializer code in the engine. That feels weired for example
# ActiveAdmin places code in +app/decorators+ so now we would load that code
# twice...
# This solution creates a blank model/controller in the engine and adds all
# functionality via a concern. To overwrite or add methods in the main app
# you simply create the same blank model/controller, include the same module
# and define your overwrites.
# You can even use the original methods by calling +super+.

# engine: app/controllers/myengine/my_controller.rb
module MyEngine
  class MyController < ApplicationController
    include MyEngine::Concerns::Controllers::MyController
  end
end

# engine: app/concerns/myengine/controllers/my_controller.rb
module MyEngine::Concerns::Controllers::MyController
  extend ActiveSupport::Concern
  
  def index
    
  end
end

# app: app/controllers/myengine/my_controller.rb
module MyEngine
  class MyController < ApplicationController
    include MyEngine::Concerns::Controllers::MyController
    
    def index
      
    end
  end
end

# Source
# http://edgeguides.rubyonrails.org/engines.html#overriding-models-and-controllers