Stanislav Pankevich
Stanislav Pankevich

Reputation: 11368

How to extend Rails Engine's controllers properly?

I am developing Rails plugin (it is 3.1 Engine) called Carrier (https://github.com/stanislaw/carrier).

In one of my rails app I want to extend Carrier's controller with some new methods - fx. add new action #comment_form to Carrier::MessagesController (I want this action only exist in my app - I don't want to add it in Engine - because it is very specific).

Two strategies I see here:

1) I copy {Carrier's plugin root}/app/controllers/carrier/messages_controller.rb file to app/controllers/carrier/ folder of my app, and then extend it (all plugin's original actions are copied to rails app controllers folder too!).

2) More accurate way I want - is just to create {My rails app}/app/controllers/carrier/messages_controller.rb and write only #comment_form method I want Carrier to be extended with.

Expecting that two controllers's content (original from plugin's folder + custom in my rails app having only new #comment_form) will superpose, I tried the second way. But Rails then stopped recognizing all original Carrier's actions (#index, #show, etc...) written in messages_controller.rb from the Carrier plugin's folder and began treating rails app's messages_controller.rb version as the only one (all original actions began treated as empty and thus began rendered through rails conventions default flow).

So my question in general is: How to add new actions to Rails Engines controllers without copying them entirely to Rails app/controllers folder?

UPD

For now I see two solutions which allow extension of engine's controllers without serious hacks (like this gem does: https://github.com/asee/mixable_engines from this thread: Extending controllers of a Rails 3 Engine in the main app)

1) load YourEngine::Engine.config.root + 'app' + 'controllers' + 'your_controller' inside your_controller.rb that is in #{main_app}/app/controller/your_engine folder. Notice load instead of require.

2) Devise way (according to some SO topics suggest): In main app create new controller that subclasses engine's one + edit routes to they point to this new controller.

I am still sure some even better solutions exist. Please correct me if they do!

Upvotes: 4

Views: 3841

Answers (2)

Pierre Pretorius
Pierre Pretorius

Reputation: 2909

I know this is a very old question but in case someone else finds this question, here is a gem that does decorators nicely. It hooks into Rails ActiveSupport and adds a convention to doing decorators that is safe from circular dependencies. We have been using it in production on multiple apps for a while.

https://github.com/EPI-USE-Labs/activesupport-decorators

Upvotes: 1

apneadiving
apneadiving

Reputation: 115511

Your option 2) is fine because it will let you upgrade the gem seamlessly.

Your current way simply overrides the existing controller.

Say you want to extend FooController.

  1. Create a file named foo_controller_decorator.rb in your initializer folder

  2. In the file:

FooController.class_eval do
  #your additionnal code here.
end

Upvotes: 5

Related Questions