Лучший способ загрузить модуль / класс из папки lib в Rails 3?

273

Поскольку последний релиз Rails 3 больше не является автозагрузкой модулей и классов из lib, каков будет лучший способ их загрузки?

Из GitHub:

A few changes were done in this commit:

Do not autoload code in *lib* for applications (now you need to explicitly 
require them). This makes an application behave closer to an engine 
(code in lib is still autoloaded for plugins);
Винсент
источник

Ответы:

251

Начиная с Rails 2.3.9 , есть настройка, в config/application.rbкоторой вы можете указать каталоги, содержащие файлы, которые вы хотите загрузить автоматически.

Из application.rb:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
Слободан Ковачевич
источник
7
Обратите внимание на @ благодарный ответ также, если вы хотите автоматически загрузить все поддерево app/lib.
Том Харрисон
199
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Источник: Rails 3 Quicktip: Автозагрузка каталога lib, включая все подкаталоги, избегайте отложенной загрузки

Обратите внимание, что файлы, содержащиеся в папке lib, загружаются только при запуске сервера. Если вы хотите, чтобы эти файлы автоматически загружались , прочтите: Rails 3 Quicktip: Автоматическая перезагрузка папок lib в режиме разработки . Помните, что это не относится к производственной среде, поскольку постоянная перезагрузка замедляет работу машины.

благодарный
источник
Ссылки мертвы
Беси
84

Магия автозагрузки

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

Поэтому, когда дело доходит до загрузки материалов из подкаталогов, есть недочёт или соглашение, о котором вы должны знать. Иногда магия Ruby / Rails (на этот раз в основном Rails) может затруднить понимание, почему что-то происходит. Любой модуль, объявленный в путях автозагрузки, будет загружен, только если имя модуля соответствует имени родительского каталога. Так что, если вы попытаетесь вставить lib/my_stuff/bar.rbчто-то вроде:

module Foo
  class Bar
  end
end

Он не будет загружен автоматически. Потом еще раз , если вы переименовать родительские директории для fooтаким образом , хостинга для вашего модуля в пути: lib/foo/bar.rb. Это будет там для вас. Другой вариант - назвать файл, который вы хотите загрузить автоматически, по имени модуля. Очевидно, что тогда может быть только один файл с таким именем. В случае, если вам нужно разделить ваши вещи на множество файлов, вы, конечно, можете использовать этот файл, чтобы требовать другие файлы, но я не рекомендую этого, потому что тогда, когда вы находитесь в режиме разработки, и вы изменяете эти другие файлы, Rails не может автоматически перезагрузите их для вас. Но если вы действительно хотите, вы можете иметь один файл с именем модуля, который затем указывает фактические файлы, необходимые для использования модуля. Таким образом , вы можете иметь два файла: lib/my_stuff/bar.rbиlib/my_stuff/foo.rb первый из них будет таким же, как указано выше, а второй будет содержать одну строку:require "bar"и это будет работать точно так же.

PS Я чувствую себя обязанным добавить еще одну важную вещь. В последнее время, когда я хочу, чтобы в каталоге lib было что-то, что нужно было загрузить автоматически, я начинаю думать, что если это то, что я на самом деле разрабатываю специально для этого проекта (что обычно бывает, это может когда-нибудь случиться) превратиться в «статический» фрагмент кода, используемого во многих проектах или в подмодуле git и т. д., в этом случае он обязательно должен быть в папке lib), тогда, возможно, его место вообще не будет в папке lib. Возможно, это должно быть в подпапке в папке приложения. У меня такое ощущение, что это новый способ работы. Очевидно, одна и та же магия работает в тех местах, где вы кладете свои вещи на пути автозагрузки, так что это хорошо для этих вещей. Во всяком случае, это только мои мысли на эту тему. Вы можете не согласиться. :)


ОБНОВЛЕНИЕ: О типе магии ..

Как отметил Северин в своем комментарии, ядро ​​«автозагрузки модульного механизма», безусловно, является частью Ruby, а пути автозагрузки - нет. Вам не нужны Rails, чтобы сделатьautoload :Foo, File.join(Rails.root, "lib", "my_stuff", "bar"), И когда вы попытаетесь сослаться на модуль Foo в первый раз, он будет загружен для вас. Однако, что делает Rails, так это то, что он дает нам возможность автоматически пытаться загружать вещи из зарегистрированных папок, и это было реализовано таким образом, что нужно что-то предполагать в соглашениях об именах. Если бы он не был реализован подобным образом, то каждый раз, когда вы ссылаетесь на что-то, что в данный момент не загружено, ему придется просматривать все файлы во всех папках автозагрузки и проверять, содержит ли какой-либо из них то, на что вы пытались сослаться. Это в свою очередь победило бы идею автозагрузки и автозагрузки. Тем не менее, с этими соглашениями он может вычесть из модуля / класса ваши попытки загрузить, где это может быть определено, и просто загрузить это.

Timo
источник
1
Почему это магия Рубина? Ruby просто предоставляет функцию автозагрузки Module #, которую вы можете использовать для команды загрузки файла при доступе к (неопределенной) константе (см. Ruby-doc.org/core-1.9.3/Module.html#method-i-autoload ). Сопоставление имен модулей / классов с каталогами / файлами, по моему мнению, выполняется в Rails / ActiveSupport (например, здесь: github.com/rails/rails/blob/… ). Я ошибся?
Северин
Да, я верю, что вы правы. Я был слишком поспешен, чтобы «исправить» свой первоначальный ответ, когда Забба указал на его «недостаток». Позвольте мне обновить мой ответ немного больше, чтобы прояснить этот вопрос.
Тимо
1
Я потратил около получаса. Мне нужно (хотел) для автозагрузки Sprockets :: JSRender :: Процессор. Путь для этого можно найти, зайдя в консоль rails и выполнив «Sprockets :: JSRender :: Processor» .underscore и убедившись, что это «sprockets / js_render / processor» (с добавленным .rb) HTH.
Pedz
Вы только что сохранили мое здравомыслие. ~ глубокий вздох облегчения ~ большое спасибо за то, что поделились :)
Brenden
Спасибо за этот самый полезный комментарий. Я не понимаю, почему некоторые модули ведут себя так, как они, пока я не прочитал ваш комментарий. Благословения на вас!
mjnissim
41

Предупреждение: если вы хотите загрузить 'monkey patch' или 'open class' из папки 'lib', не используйте подход 'автозагрузка' !!!

  • Подход " config.autoload_paths ": работает, только если вы загружаете класс, который определен только в ОДНОМ месте. Если какой-то класс уже был определен где-то еще, таким способом вы не сможете загрузить его снова.

  • Подход " config / initializer / load_rb_file.rb ": всегда работает! независимо от того, является ли целевой класс новым классом, «открытым классом» или «обезьяньим патчем» для существующего класса, он всегда работает!

Для получения дополнительной информации см .: https://stackoverflow.com/a/6797707/445908.

Сивей Шен 申思维
источник
6
Это критическое различие, чтобы понять. Спасибо за это.
Тайлер Кольер
28

Очень похоже, но я думаю, что это немного более элегантно:

config.autoload_paths += Dir["#{config.root}/lib", "#{config.root}/lib/**/"]
Брайан Армстронг
источник
18

В моем случае я пытался просто загрузить файл прямо в директории lib.

Внутри application.rb ...

require '/lib/this_file.rb' 

не работал, даже в консоли, а потом, когда я пытался

require './lib/this_file.rb' 

и рельсы загружает файл отлично.

Я все еще довольно нуб, и я не уверен, почему это работает, но это работает. Если кто-то хотел бы объяснить это мне, я был бы признателен: Я надеюсь, что это поможет кому-то в любом случае.

Ник Рес
источник
2
Это потому, что ./lib/this_file.rb ищет в текущем каталоге (в консоли Rails это будет корень вашего Rails), а /lib/this_file.rb ищет его как абсолютный путь. Пример: ./lib/this_file.rb = /var/www/myrailsapp/lib/this_file.rb, /lib/this_file.rb = /lib/this_file.rb
Джейсон
7

У меня такая же проблема. Вот как я это решил. Решение загружает каталог lib и все его подкаталоги (не только прямые). Конечно, вы можете использовать это для всех каталогов.

# application.rb
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
hjuskewycz
источник
5
Это имеет неприятный побочный эффект - полностью нарушает соглашения о пространствах имен Rails. Если lib / bar / foo.rb, определяющий Bar :: Foo, появляется раньше, чем lib / foo.rb, определяющий Foo в поиске автозагрузки, то вы получите ошибочные ошибки, например, Expected lib/bar/foo.rb to define constant Fooесли попытаетесь загрузить lib / foo.rb, ссылаясь на Foo постоянная.
Джейкоб
5

config.autoload_paths не работает для меня. Я решаю это по-другому

Ruby on rails 3 не выполняет автоматическую перезагрузку (автозагрузку) кода из папки / lib. Я решаю это, положив внутрьApplicationController

Dir["lib/**/*.rb"].each do |path|
  require_dependency path
end 
msa.im
источник
4

Если только определенные файлы нуждаются в доступе к модулям в lib, просто добавьте оператор require к файлам, которые в нем нуждаются. Например, если одной модели требуется доступ к одному модулю, добавьте:

require 'mymodule'

в верхней части файла model.rb.

Майк Фишер
источник
50
Вы не должны использовать его requireв приложении rails, потому что оно препятствует ActiveSupport::Dependenciesзагрузке этого кода должным образом. Вместо этого вы должны использовать config.autoload_pathsкак ответ выше, а затем включить / расширить, как требуется.
ben_h
13
Спасибо @Mike, я собирался сделать то, что ты сделал, было приятно увидеть объяснение, почему это плохо, спасибо, что не удалил ответ.
Пупено
как насчет включения 'mymodule', если вы просто хотите загрузить один модуль?
Майк
1
@ben_h Если вы не requireоткуда-нибудь в приложении Rails? В задании на грабли я в настоящее время - requireи include- модуль, который живет в lib/. Разве я не должен этим заниматься?
Деннис
@ben_h Мой поиск показывает, что он является общим для requireвашего lib/кода (например, этот пост в блоге , этот SO-ответ ). Я все еще не уверен в этом. Можете ли вы дать больше доказательств в пользу отказа от использования require?
Деннис
2

Назовите имя файла правильно.

Шутки в сторону. Я боролся с классом в течение часа, потому что класс был Governance :: ArchitectureBoard, а файл находился в lib / management / Architecture_baord.rb (транспонировал O и A в "board")

Кажется очевидным в ретроспективе, но это был дьявол, выслеживающий это. Если класс не определен в файле, в котором Rails ожидает, что он будет основан на обработке имени класса, он просто не найдет его.

Дэвид Хэмпи
источник
2

Начиная с этого Rails 5, рекомендуется поместить папку lib в каталог приложения или вместо этого создать другие значимые пространства имен для папки as services, presentersи featuresт. Д. И поместить ее в каталог приложения для автоматической загрузки с помощью rails.

Пожалуйста, проверьте также эту ссылку для обсуждения на GitHub .

Ашик Салман
источник