Расширение функциональности Rails

У меня есть двигатель, который определяет некоторые модели и контроллеры. Я хочу иметь возможность расширять функциональность некоторых моделей/контроллеров в моем приложении (например, методы добавления), не теряя исходных функций модели/контроллера с движка. Всюду я читал, что вам просто нужно определить контроллер с тем же именем в вашем приложении, и Rails автоматически объединит их, однако он не работает для меня, а контроллер в движке просто игнорируется (я не думаю, что он даже загружен).

Ответы

Ответ 1

Просто, если кто-то еще столкнется с тем же вопросом в будущем, это код, который я написал, который исправил мою проблему:

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

Это автоматически потребует файлы от двигателя, прежде чем требовать от приложения, если путь к файлу начинается с "приложения".

Ответ 2

require MyEngine::Engine.root.join('app', 'models', 'my_engine', 'my_model')

до определения класса модели в вашем приложении.

Ответ 3

Вы можете добавить эти строки к вашему файлу модуля двигателя в корневой каталог 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 ​​без необходимости исправления.

Пример:

#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"

Ответ 4

Это верно. Контроллер, который будет найден первым, будет использоваться.

Итак, чтобы заставить его работать, у вас может быть два варианта:

  • создать локальную копию контроллера и изменить метод, который вам нужен.
  • Если у вас есть контроль над плагином, вы можете создать модуль, содержащий код, и include код в обоих контроллерах, только переопределяя метод в локальном контроллере. По мне, поскольку нет множественного наследования, это единственный способ.

Надеюсь, что это поможет.

Ответ 5

Вы можете изменить порядок загрузки двигателя, чтобы избежать необходимости на каждой из ваших моделей.

В config/application.rb добавьте эту строку:

module MyApp
  class Application
    config.railties_order = [MyEngine::Engine, :main_app, :all]
  end
end

Это обеспечит загрузку моделей из MyEngine до MyApp

Ответ 6

Я никогда раньше не использовал Двигатели, но не могу определить новый контроллер, который наследуется от контроллера, предоставленного двигателем