Rails: обратный вызов self.inherited и загрузка классов (не очень ...) - PullRequest
1 голос
/ 25 июля 2011

Настройка

В моем драгоценном камне есть базовый класс Компонентов:

module Component
  class Base
    def self.inherited(component_class)
      Component.class_eval do
        define_method(component_class.to_s.underscore) do |*args, &block|
          component_class.new(*args, &block)
        end
      end
    end
  end
end

Для каждого класса, унаследованного от этого базового класса (например, FancyComponent < Component::Base), в модуле Component должен быть определен помощник (например, fancy_component).

Это работает для любых компонентов, поставляемых с моим драгоценным камнем, т.е. Component.instance_methods.include? :fancy_component #=> true

Теперь Rails вступает в игру

Я хочу, чтобы пользователи моего драгоценного камня могли добавлять компоненты. Они хранятся в app/components.

Эта папка включена во все следующие элементы:

  • MyApp :: Application.config.load_paths
  • MyApp :: Application.config.autoload_paths
  • MyApp :: Application.config.eager_load_paths

Новый компонент UserComponent < Component::Base хранится в app/components/user_component.rb.

Проблема

Если я запускаю консоль рельсов, ситуация выглядит следующим образом:

Loading development environment (Rails 3.0.4, ruby-1.9.2-p0)  
Component.instance_methods.include? :fancy_component   #=> true 
Component.instance_methods.include? :user_component    #=> false
UserComponent                                          #=> UserComponent
Component.instance_methods.include? :user_component    #=> true

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

Итак, как принудительно выполнить загрузку этого класса для выполнения наследуемого?

Ответы [ 2 ]

0 голосов
/ 26 ноября 2013

Это ошибка в рельсах (но wontfix), смотрите также этот билет здесь:

https://github.com/rails/rails/issues/3364

0 голосов
/ 25 июля 2011

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

Вот базовый пример того, как вы можете реализовать эту функцию в своем «корневом» файле gem (если ваш gem называется «my_component», файл «lib / my_component.rb») выполняет что-то вроде это:

require 'component/base/component'
# require here all other classes necessary for your gem

rb_files = File.join( Rails.root, 'app', 'components', '**', '*.rb' )
Dir.glob( rb_files ).each do |file|
  require file.gsub('.rb')
end

При этом вы будете загружать все файлы в «app / components», но тогда Rails не будет перезагружать эти объекты, поскольку они требуются не Rails, а вашим собственным гемом. Если вы не возражаете против невозможности изменить эти файлы, это может быть нормально, но тогда у вас могут возникнуть проблемы в среде разработки (или в любой среде, в которой "cache_classes" имеет значение false).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...