Rails 5: загрузка файлов lib в продакшене

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

Теперь я всегда получаю сообщение об ошибке на своем производственном сервере, поскольку загружаю все файлы lib с автозагрузкой в ​​файл application.rb.

module MyApp
    class Application < Rails::Application
        config.autoload_paths += %W( lib/ )
    end
end

На данный момент я установил config.enable_dependency_loading на true, но мне интересно, есть ли лучшее решение для этого. Должна быть причина, по которой автозагрузка отключена в производственной среде по умолчанию.


person Tobias    schedule 05.07.2016    source источник
comment
сумасшедшая вещь, и документы по-прежнему говорят вам делать auto_load. Я был очень смущен тем, что происходит не так в производственной среде для нового приложения. И с тех пор, как я начал изучать Rails 5, я не читал руководства по миграции. Я отправил документ о проблеме, чтобы, надеюсь, решить эту проблему: github.com/rails/rails/issues/27268   -  person akostadinov    schedule 05.12.2016
comment
на удивление, у меня есть два файла в lib dir, один файл легко доступен во время выполнения, а другой нужно запрашивать вручную: D   -  person illusionist    schedule 01.01.2017
comment
@Tobias Какое решение вы в итоге выбрали?   -  person geoboy    schedule 26.07.2018
comment
@geoboy Я группирую код (например, Validators) в папках прямо в каталоге app /, так как там код загружается автоматически.   -  person Tobias    schedule 27.07.2018
comment
это касается правильного пути к файлу и определения класса. Вот что мне подходит в Rails 5.2: Путь к файлу: app/services/paylinx/paylinx_service.rb Определение класса: module Paylinx class PaylinxService end end. Я пробовал эти autoload_paths штуки. у меня не работает.   -  person NamNamNam    schedule 27.01.2019


Ответы (11)


Мой список изменений после перехода на Rails 5:

  1. Поместите lib dir в app, потому что весь код внутри приложения автоматически загружается в dev и загружается в prod и, что наиболее важно, автоматически перезагружается в разработке, поэтому вы не должны Не нужно перезапускать сервер каждый раз, когда вы вносите изменения.
  2. Удалите все require операторы, указывающие на ваши собственные классы внутри lib, потому что все они автоматически загружаются, если их имена файлов / каталогов верны, и если вы оставите require операторы, это может прервать автоматическую перезагрузку. Дополнительная информация здесь
  3. Установите config.eager_load = true во всех средах, чтобы быстро увидеть проблемы с загрузкой кода в dev.
  4. Используйте Rails.application.eager_load! перед игрой с потоками, чтобы избежать ошибок "циклической зависимости".
  5. Если у вас есть какие-либо расширения ruby ​​/ rails, оставьте этот код в старом каталоге lib и загрузите их вручную из инициализатора. Это обеспечит загрузку расширений до того, как ваша дальнейшая логика может от них зависеть:

    # config/initializers/extensions.rb
    Dir["#{Rails.root}/lib/ruby_ext/*.rb"].each { |file| require file }
    Dir["#{Rails.root}/lib/rails_ext/*.rb"].each { |file| require file }
    
person Lev Lukomsky    schedule 13.10.2016
comment
Так как же теперь использовать папку lib? Я имею в виду, что перемещение lib dir в app dir похоже на обходной путь. - person Martin Svoboda; 21.05.2017
comment
/app/lib/ поместил файл / класс, и это НЕ автозагрузка. протестирован в рельсах 5.1, новый проект - person Tim Kretschmer; 21.06.2017
comment
Стоит отметить, что пружину нужно останавливать. Я переместил все в app / lib /, а затем потратил немного времени на размышления, почему я все еще не могу использовать свои классы из консоли. пружинная остановка ftw :) - person jacklin; 14.08.2017
comment
Куда пойдет следующая строка Rails.application.eager_load! - person Steven Aguilar; 11.07.2018
comment
@Lev: Когда я это делаю, я вижу следующую ошибку в dev: NameError - uninitialized constant ApplicationController::JsonWebToken: Вы знаете, что может происходить? - person geoboy; 26.07.2018
comment
@geoboy вот когда файл не загружается. похоже, что файл сначала ищется в корневом контексте, как в ::ModuleOrClass, а затем в текущем контексте, скажем, ApplicationController::ModuleOrClass, в котором он вызван. Ваша ошибка показывает второй результат. - person Hiro; 26.11.2018
comment
Это может сработать, но это не лучшее решение. Структура папок тоже семантическая. Вещи в lib воспринимают близость к проекту иначе, чем вещи в каталоге app. Некоторые другие ответы лучше этого. - person CWitty; 19.06.2019
comment
Хорошее предложение для config.eager_load = true, но плохое предложение по перемещению библиотеки в приложение. - person Damien Roche; 21.08.2019
comment
@CWitty lib является семантическим для гемов, но для приложения Rails довольно неожиданно иметь какой-то рубиновый код в lib и весь другой код в app, например _4 _ / _ 5 _ / _ 6 _ / _ 7 _ / _ 8 _ / _ 9 _ / _ 10 _ / _ 11 _ / ... - person Lev Lukomsky; 21.05.2021

Я просто использовал config.eager_load_paths вместо config.autoload_paths, например, упомянул акостадинов в комментарии на github: https://github.com/rails/rails/issues/13142#issuecomment-275492070.

# config/application.rb
...
# config.autoload_paths << Rails.root.join('lib')
config.eager_load_paths << Rails.root.join('lib')

Он работает в среде разработки и производства.

Благодарим Йохана за предложение заменить #{Rails.root}/lib на Rails.root.join('lib')!

person Michał Zalewski    schedule 08.07.2017
comment
Работает как шарм. Мне не понравился синтаксис, поэтому я изменил его на config.eager_load_paths << Rails.root.join('lib'). - person 3limin4t0r; 28.12.2017
comment
Для меня это был лучший ответ. Мой проект на Rails 5.2 начался с нуля, а папка / lib все еще создавалась вне папки / app. Я не видел веской причины перемещать его. - person Samir Haddad; 31.08.2018
comment
Ага, это работает! Кажется, разработчикам Rails очень нравится вызывать проблемы с загрузкой библиотеки: D До следующего раза! - person Damien Roche; 21.08.2019
comment
В Rails 5.2 вместо этого используется config.eager_load_paths += [Rails.root.join('lib')], потому что config.eager_load_paths - это замороженный массив - person William Wong Garay; 11.02.2020
comment
@WilliamWongGaray config.eager_load_paths доступен только для чтения, когда вы пытаетесь изменить его в инициализаторе. Когда вы добавляете пути в application.rb, он будет работать с использованием обоих методов. - person Michał Zalewski; 23.02.2020

Автозагрузка отключена в производственной среде из-за безопасности потоков. Спасибо @ Зелёный за ссылку.

Я решил эту проблему, сохранив файлы lib в папке lib в моем app каталоге, как рекомендовано на Github . Каждая папка в папке app загружается Rails автоматически.

person Tobias    schedule 18.08.2016
comment
Я использовал config.eager_load_paths << "#{Rails.root}/lib", это лучше IMO, чтобы следовать рекомендуемой структуре приложения rails. - person akostadinov; 05.12.2016
comment
Помещение lib в app/lib рекомендуется участниками rails github.com/rails/rails/issues / 13142 # issuecomment-275549669 - person eXa; 31.01.2017
comment
Это полностью разрушает цель lib. Я бы подождал, пока зазвонит нежная любовь или DHH. А пока я (лично) рекомендую придерживаться ответа @Lev Lukomsky. - person Josh Brody; 06.09.2017
comment
@JoshBrody Сейчас я считаю, что вам вообще не нужен каталог /lib. Сторонние библиотеки в большинстве случаев представляют собой драгоценные камни, а в противном случае должен быть создан гем. Для других файлов я создаю определенные папки в каталоге /app. Например validators. - person Tobias; 06.09.2017
comment
Я согласен! Я думаю, что вся бизнес-логика должна принадлежать /app; Я предполагаю, что я имею в виду, если вы пишете расширение Rescue или имеете некоторые объекты, не относящиеся к домену, которые расширяют ActiveModel, где это должно быть? Мой первый инстинкт подсказывает мне lib - person Josh Brody; 06.09.2017
comment
Я думаю (после того, как ошибся во многих проектах), что /lib предназначен для функций, которые могут быть отдельными драгоценными камнями (не требуют ничего в приложении, среде или базе данных), но не (пока) - своего рода промежуточная точка . Однако я нахожу, что всегда есть служебные функции, связанные с приложением, которые не совсем подходят ни к одной из существующих /app/* структур - это хорошо подходит для /app/lib, и тогда они являются хорошими кандидатами для рефакторинга (абстрагирование в приложение или выделение чего-то другого. ) - person Tim Diggins; 22.01.2018

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

Вот долгая дискуссия по этому поводу. https://github.com/rails/rails/issues/13142

person Зелёный    schedule 05.07.2016
comment
Это обсуждение является лучшим, хотя и долгим, источником информации по теме, с которой мне приходилось сталкиваться. - person Jason; 21.06.2020

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

P.S. Я изменил свой ответ, теперь он добавляет к обоим путям автозагрузки, независимо от среды, что позволяет работать и в настраиваемых средах (например, на сцене)

# config/initializers/load_lib.rb
...
config.eager_load_paths << Rails.root.join('lib')
config.autoload_paths << Rails.root.join('lib')
...
person srghma    schedule 29.01.2018
comment
Не могли бы вы подробнее объяснить, почему это решает проблему? - person Stuart.Sklinar; 29.01.2018
comment
@ Stuart.Sklinar, это позволяет автоматически перезагружать библиотеку, а также работает в производственной среде. P.S. Я изменил свой ответ, теперь он добавляет к обоим путям автозагрузки, независимо от среды, что позволяет работать и в пользовательских средах (например, на сцене) - person srghma; 29.01.2018
comment
Не могли бы вы расширить (в своем ответе)? Ответ только на код на самом деле никому не помогает понять, почему это должно быть сделано таким образом - я должен добавить, что я не разработчик Ruby, просто помогая прояснить SO. Добавление комментария к ответу, содержащему только код, придаст ему некоторый реальный контекст. - person Stuart.Sklinar; 29.01.2018
comment
@ Stuart.Sklinar уверен - person srghma; 29.01.2018

Просто измените config.autoload_paths на config.eager_load_paths в файле config / application.rb. Потому что в rails 5 автозагрузка по умолчанию отключена для производственной среды. Для получения дополнительных сведений следуйте ссылка.

 #config.autoload_paths << "#{Rails.root}/lib"
  config.eager_load_paths << Rails.root.join('lib')

Он работает как для разработки среды, так и для производства.

person Jitendra Rathor    schedule 27.11.2019

В некотором смысле, здесь представлен единый подход в Rails 5 для централизации конфигурации готовности и автозагрузки, в то же время он добавляет требуемый путь автозагрузки всякий раз, когда настроена активная загрузка, иначе она не сможет работать правильно:

# config/application.rb
...
config.paths.add Rails.root.join('lib').to_s, eager_load: true

# as an example of autoload only config
config.paths.add Rails.root.join('domainpack').to_s, autoload: true
...
person pocheptsov    schedule 29.05.2018

Для тех, кто боролся с этим, как я, недостаточно просто поместить каталог в app/. Да, вы получите автозагрузку, но не обязательно, , которая требует соблюдения соглашений об именах выполнено.

Кроме того, использование инициализатора для загрузки старого lib корневого уровня предотвратит перезагрузку во время разработки.

person Abdullah Barrak    schedule 10.05.2017

Перемещение папки lib в приложение помогло решить проблему, мой API Twitter не работал в производственной среде. У меня была «неинициализированная константа TwitterApi», и мой Twitter API находился в моей папке lib. У меня был config.autoload_paths += Dir["#{Rails.root}/app/lib"] в моем application.rb, но он не работал до перемещения папки.

Это сработало

person Laurie    schedule 15.11.2017

Я согласен с тем, что некоторые зависимости принадлежат lib, а некоторые могут принадлежать app/lib.

Я предпочитаю загружать все файлы, которые я выбрал для размещения lib для всех сред, поэтому я делаю это в config/application.rb сразу после запроса пакета, но перед открытием модуля MyApplicationName.

# load all ruby files in lib
Dir[File.expand_path('../../lib/**/*.rb', __FILE__)].each { |file| require file }

Это не зависит от Rails.root (который еще не определен) и не зависит от активной загрузки (которая может быть отключена для среды).

person eprothro    schedule 20.01.2021

резюмируя ответ Льва: mv lib app было достаточно, чтобы весь мой lib код был автоматически загружен / перезагружен.

(rails 6.0.0beta3, но должен работать и на rails 5.x)

person localhostdotdev    schedule 22.04.2019