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
@cowboycoded method 2 in conjunction with
require_dependency
andconfig.reload_plugins
worked for me on Rails 3.2.2 / Ruby 1.9.Here is the code: https://stackoverflow.com/a/9790497/22237
I've created a gem based on the code from Andrius and Andrei above. Instead of copying around that code, just require the mixable_engines gem. Only works with rails 3 right now.
https://github.com/asee/mixable_engines
https://rubygems.org/gems/mixable_engines
@Andrei and @Artrius: I've credited you in the license file, let me know if you want your real name or some other credit.
Method 1
Here is what I put in my Rails 3 app in
application.rb
afterrequire 'rails/all'
(let me know if it is a bad place to put it)For a while this didn't work raising
but that was due to a mistyped class definition
class PostsController < ActionController::Base
which should beclass PostsController < ApplicationController
Method 2
If you do not want to do this for all engine controllers etc., you can load the engine's controller before the definition in the main app
You can use Ruby's
send()
method to inject your code into the controller at the time the engine is created...This method spares you from having to redefine the controller inheritance within your main app.
If you do not want patch active support to change the load order as suggested in Rails engines extending functionality, you can make use of a rack middleware for authentication. If authentication is done as part of every controller action, this approach might save you lot of code and time.
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.
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