Rails двигатели расширяют функциональность
У меня есть двигатель, который определяет некоторые модели и контроллеры. Я хочу иметь возможность расширять функциональность некоторых моделей / контроллеров в моем приложении (например, добавление методов) без потери исходной функциональности модели / контроллера из движка. Везде, где я читал, вам просто нужно определить контроллер с тем же именем в вашем приложении, и Rails автоматически объединит их, однако это не работает для меня, и контроллер в движке просто игнорируется (я не думаю, что он даже загружен).
6 ответов
Просто если кто-нибудь столкнется с той же проблемой когда-нибудь в будущем, это код, который я написал, который решил мою проблему:
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
Для этого автоматически потребуются файлы из движка, а затем из приложения, если путь к файлу начинается с "app".
require MyEngine::Engine.root.join('app', 'models', 'my_engine', 'my_model')
до определения класса модели в вашем приложении.
Вы можете добавить эти строки в файл модуля вашего движка в корневой директории lib:
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
Затем у вас есть возможность в главном приложении (приложение, использующее движок) запрашивать необходимые файлы из движка. Это хорошо, потому что вы поддерживаете функциональность Rails Engines по умолчанию, а также имеете простой инструмент для использования обычного наследования ruby без необходимости исправлений.
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"
Вы можете изменить порядок загрузки двигателя, чтобы избежать необходимости на каждой из ваших моделей.
В config/application.rb добавьте эту строку:
module MyApp
class Application
config.railties_order = [MyEngine::Engine, :main_app, :all]
end
end
Это обеспечит загрузку моделей из MyEngine до MyApp.
Это правда. Контроллер, который найден первым, будет использован.
Таким образом, чтобы это работало, у вас может быть два варианта:
- создайте локальную копию контроллера и измените нужный метод
- Если у вас есть контроль над плагином, вы можете создать Модуль, содержащий код и включить код в оба контроллера, переопределяя только метод в вашем локальном контроллере. По моему мнению, поскольку нет множественного наследования, это единственный путь.
Надеюсь это поможет.
Я никогда раньше не использовал Двигатели, но вы не можете определить новый контроллер, который наследуется от контроллера, предоставленного движком