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
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.
You can use Ruby's send()
method to inject your code into the controller at the time the engine is created...
# lib/cool_engine/engine.rb
module CoolEngine
class Engine < ::Rails::Engine
isolate_namespace CoolEngine
initializer "cool_engine.load_helpers" do |app|
# You can inject magic into all your controllers...
ActionController::Base.send :include, CoolEngine::ActionControllerExtensions
ActionController::Base.send :include, CoolEngine::FooBar
# ...or add special sauce to models...
ActiveRecord::Base.send :include, CoolEngine::ActiveRecordExtensions
ActiveRecord::Base.send :include, CoolEngine::MoreStuff
# ...even provide a base set of helpers
ApplicationHelper.send :include, CoolEngine::Helpers
end
end
end
This method spares you from having to redefine the controller inheritance within your main app.
@cowboycoded method 2 in conjunction with require_dependency
and config.reload_plugins
worked for me on Rails 3.2.2 / Ruby 1.9.
Here is the code: https://stackoverflow.com/a/9790497/22237
Here is what I put in my Rails 3 app in application.rb
after require 'rails/all'
(let me know if it is a bad place to put it)
require 'active_support/dependencies'
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.to_s + '/app')
relative_name = file_name.gsub(Rails.root.to_s, '')
#@engine_paths ||= Rails::Application.railties.engines.collect{|engine| engine.config.root.to_s }
#EDIT: above line gives deprecation notice in Rails 3 (although it works in Rails 2), causing error in test env. Change to:
@engine_paths ||= YourAppName::Application.railties.engines.collect{|engine| engine.config.root.to_s }
@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
For a while this didn't work raising
TypeError in PostsController#index
superclass mismatch for class PostsController
but that was due to a mistyped class definition class PostsController < ActionController::Base
which should be class PostsController < ApplicationController
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
require PostsEngine::Engine.config.root + 'app' + 'controllers' + 'posts_controller'
class PostsController < ApplicationController
# extended methods
end
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.
Why not just inherit from the Engine's controller class in your application (and point your routes at the new child controllers)? Sounds conceptually similar to the way that you extend built-in Devise controllers.