Reputation: 2818
I have an engine which defines some models and controllers. I want to be able to extend functionality of some models/controllers in my application (eg. adding methods) without loosing the original model/controller functionality from engine. Everywhere I read that you simply need to define controller with the same name in your application and Rails will automatically merge them, however it doesn't work for me and controller in engine is simply ignored (I don't think it's even loaded).
Upvotes: 16
Views: 5890
Reputation: 2809
require MyEngine::Engine.root.join('app', 'models', 'my_engine', 'my_model')
before the model class definition in your application.
Upvotes: 20
Reputation: 941
You can change the load order of the engine to avoid the require on each of your models.
In config/application.rb add this line:
module MyApp
class Application
config.railties_order = [MyEngine::Engine, :main_app, :all]
end
end
This will ensure that the models from MyEngine are loaded before MyApp
Upvotes: 1
Reputation: 201
You can add these lines to you engine's module file in the lib root directory:
def self.root
File.expand_path(File.dirname(File.dirname(__FILE__)))
end
def self.models_dir
"#{root}/app/models"
end
def self.controllers_dir
"#{root}/app/controllers"
end
Then you have the ability in the main application (the app making use of the engine) to require the necessary files from the engine. This is nice because you maintain Rails Engines default functionality and also have an easy tool for making use of normal ruby inheritance, without the need for patching.
EX:
#ENGINE Model -
class User < ActiveRecord::Base
def testing_engine
puts "Engine Method"
end
end
#MAIN APP Model -
require "#{MyEngine.models_dir}/user"
class User
def testing_main_app
puts "Main App Method"
end
end
#From the Main apps console
user = User.new
puts user.testing_engine #=> "Engine Method"
puts user.tesing_main_app #=> "Main App Method"
Upvotes: 8
Reputation: 2818
Just if anyone else runs into same issue some time in the future, this is the code I wrote that fixed my problem:
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
This will automatically require files from engine before requiring from application if file path starts with 'app'.
Upvotes: 2
Reputation: 28392
I've never used Engines before but can't you define a new controller that inherits from the controller provided by the engine
Upvotes: 0
Reputation: 50057
That is true. The controller that is found first will be used.
So to make it work you might have two options:
Hope this helps.
Upvotes: 1