Как я могу автоматически перезагружать код gem при каждом запросе в режиме разработки в Rails?

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

gem 'mygem', path: File.expath_path('../../mygem', __FILE__)

Поскольку большая часть кода в этих драгоценных камнях на самом деле является частью приложения, он все еще часто меняется. Я хотел бы иметь возможность использовать функцию Rails, при которой код перезагружается при каждом запросе в процессе разработки (т. е. когда config.cache_classes имеет значение false), но по умолчанию это делается только в рамках обычной структуры приложения.

Как я могу настроить Rails для перезагрузки кода gem при каждом запросе, как и в случае с кодом приложения?


person aceofspades    schedule 17.03.2014    source источник


Ответы (2)


Путем проб и ошибок я обнаружил, что требуется несколько шагов с помощью ActiveSupport.

  • Добавьте activesupport в качестве зависимости в файлы .gemspec

    spec.add_dependency 'activesupport'
    
  • Включите ActiveSupport::Dependencies в модуль верхнего уровня вашего гема (это было самым неуловимым требованием)

    require 'bundler'; Bundler.setup
    require 'active_support/dependencies'
    
    module MyGem
      unloadable
      include ActiveSupport::Dependencies
    end
    
    require 'my_gem/version.rb'
    # etc...
    
  • Настройте свой драгоценный камень для использования автозагрузки. Вы либо вручную используете объявления ruby ​​autoload для сопоставления символов в имена файлов или используйте правила иерархии папок и модулей в стиле Rails (см. ActiveSupport #constantize)

  • В каждом модуле и классе вашего гема добавьте unloadable.

    module MyModule
      unloadable
    end
    
  • В каждом файле, который зависит от модуля или класса из гема, в том числе и в самом геме, объявите их вверху каждого файла, используя require_dependency. При необходимости найдите путь к драгоценному камню, чтобы правильно разрешить пути.

    require_dependency "#{Gem.loaded_specs['my_gem'].full_gem_path}/lib/my_gem/myclass"
    

Если вы получаете исключения после изменения файла и выполнения запроса, убедитесь, что вы не пропустили зависимость.

Некоторые интересные подробности см. в этой исчерпывающей публикации о Rails (и рубин) автозагрузка.

person aceofspades    schedule 17.03.2014

Решение, которое я использовал для Rails 6, с выделенным загрузчиком классов Zeitwerk и средством проверки файлов:

  • Добавьте гем в проект Rails, используя параметр path: в Gemfile.

    gem 'mygem', path: 'TODO'  # The root directory of the local gem
    
  • В development.rb настройте загрузчик классов и наблюдатель за файлами.

    gem_path = 'TODO'  # The root directory of the local gem, the same used in Gemfile
    
    # Create a Zeitwerk class loader for each gem
    gem_lib_path = gem_path.join('lib').join(gem_path.basename)
    gem_loader = Zeitwerk::Registry.loader_for_gem(gem_lib_path)
    gem_loader.enable_reloading
    gem_loader.setup
    
    # Create a file watcher that will reload the gem classes when a file changes
    file_watcher = ActiveSupport::FileUpdateChecker.new(gem_path.glob('**/*')) do
      gem_loader.reload
    end
    
    # Plug it to Rails to be executed on each request
    Rails.application.reloaders << Class.new do 
      def initialize(file_watcher)
        @file_watcher = file_watcher
      end
    
      def updated?
        @file_watcher.execute_if_updated
      end
    end.new(file_watcher)
    

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

Подробное руководство см. в разделе Вставьте гем в проект Rails и включите автоперезагрузку.

person Baldrick    schedule 21.03.2021