Модули Rails / lib и

83

Я пишу кастомную оболочку для open_flash_chartплагина. Он помещен в/lib и загружается как модуль в ApplicationController.

Однако у меня есть проблема с иерархией классов или что-то в этом роде.

С любого контроллера я могу получить доступ к таким open_flash_chartфункциям, как OpenFlashChart,Line и т.д.

Однако в классе в /lib модуля это не работает!

Есть идеи?

Мантас
источник
надеюсь, это поможет вам stackoverflow.com/questions/17304110/…
С.Ядав

Ответы:

147

Есть два способа загрузки файлов в Rails:

  • Он регистрируется в процессе автозагрузки, и вы ссылаетесь на константу, соответствующую имени файла. Например, если у вас есть app/controllers/pages_controller.rbPagesController и вы ссылаетесь на него, app/controllers/pages_controller.rbон будет загружен автоматически. Это происходит для предустановленного списка каталогов в пути загрузки. Это особенность Rails, которая не является частью обычного процесса загрузки Ruby.
  • Файлы явно required. Если это файл required, Ruby просматривает весь список путей в ваших путях загрузки и находит первый случай, когда файл, который вы required, находится в пути загрузки. Вы можете увидеть весь путь загрузки, проверив $ LOAD_PATH (псевдоним для $ :).

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

Я также заметил, что вас может смутить еще одна вещь. ApplicationController не является корневым объектом в системе. Обратите внимание:

module MyModule
  def im_awesome
    puts "#{self} is so awesome"
  end
end

class ApplicationController < ActionController::Base
  include MyModule
end

class AnotherClass
end

AnotherClass.new.im_awesome
# NoMethodError: undefined method `im_awesome' for #<AnotherClass:0x101208ad0>

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

class AnotherClass
  include MyModule
end

AnotherClass.new.im_awesome
# AnotherClass is so awesome

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

Иегуда Кац
источник
2
Просто хотел добавить: если один из ваших модулей в / lib (или в одном из каталогов автозагрузки) уже определен; Например, вы перегружаете ActiveRecord или String, вам придется явно требовать его, иначе он не будет загружен
Майк,
1
как ни странно, я получаю: неинициализированную константу GaClient (NameError), если я не требую заранее 'ga_client' (класс определен в lib / ga_client.rb). Есть ли документация по схеме именования автозагрузки?
mkirk
87

В Rails 3 / lib модули не загружаются автоматически.

Это потому, что строка:

# config.autoload_paths += %W(#{config.root}/extras)

внутри config / application.rb есть комментарии.

Вы можете попробовать раскомментировать эту строку или, (у меня это сработало даже лучше), оставить комментарий (для справки в будущем) и добавить эти две строки:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Диегопау
источник
Это дублирует ../libпуть в ApplicationName::Application.config.autoload_pathsмассиве.
jibiel
1
@jibiel так какое здесь разрешение?
ylluminate
4
почему последний вариант вам больше подошел? Кроме того, почему было изменено значение по умолчанию ... должна быть причина, несмотря на то, что многие люди находят обходные пути.
ckarbass
@ylluminate Так что сингла config.autoload_paths += Dir["#{config.root}/lib/**/"]должно хватить. @ckarbass Вторая строка дает вам свободу организовать ваши утилиты в подпапки и, таким образом, улучшить ваши модули с помощью пространств имен . Это почти все. И вот почему значение по умолчанию было изменено. Лучше поздно, чем никогда :)
jibiel
Мне это кажется немного запутанным. Разве это не излишне автозагрузка граблей?
Деннис
22

Что сработало для меня, помимо раскомментирования config.autoload_paths (я использую Rails 3.1.3), так это создание инициализатора, подобного этому:

#config/initializers/myapp_init.rb
require 'my_module'    
include MyModule

Таким образом, я могу вызывать методы mymodule из любого места и как методы классаModel.mymodule_method или как методы экземпляраmymodel.mymodule_method

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

Изменить: после этого, я думаю, лучше было бы:

создайте инициализатор вроде этого:

#config/initializers/myapp_init.rb
require ‘my_module’

При необходимости включите модуль, например:

1) если вы хотите использовать его как «Методы класса», используйте «extension»:

class Myclass < ActiveRecord::Base
   extend MyModule
   def self.method1
      Myclass.my_module_method
   end
end

2) если вы хотите использовать его как «Методы экземпляра», включите его в определение класса:

class Myclass < ActiveRecord::Base
include MyModule
   def method1
      self.my_module_method 
   end
end

3) помните, что это include MyModuleотносится к файлу my_module.rbв вашем пути загрузки, который должен быть обязательным в первую очередь

Фернандо Фабрети
источник
3
Я создал свой модуль в libпапке, поэтому добавил config.autoload_paths += %W(#{config.root}/lib)в config/application.rbфайл. После этого я последовал вашему предложению добавить config/initializers/myapp_init.rbфайл и его содержимое. Все в порядке. Большое спасибо :)
SMMousavi
Несмотря на то, что requireу меня работает, а автоматическая загрузка - нет (метод undefined module), в этом комментарии говорится, что вы не должны использоватьrequire .
Деннис
3

Чтобы использовать модуль lib/my_module.rbв ваших моделях и контроллерах:

В config/application.rb:

config.watchable_dirs['lib'] = [:rb]

В вашей модели (аналогичная идея для вашего контроллера):

require_dependency 'my_module'

class MyModel < ActiveRecord::Base
  include MyModule

  MyModule.some_method
end

Более подробно этот метод описан на http://hakunin.com/rails3-load-paths

Деннис
источник
0

Возможно, вы захотите явно загрузить файл (ы) в каталог lib во время инициализации приложения.
В моем config / application.rb у меня есть запись как,
config.autoload_paths += %W(#{config.root}/lib)

Также это может быть тот случай, когда имя / иерархия модуля не такие же, как в файле, или расположение / имя файла не такое же, как эта иерархия, поэтому автоматическая загрузка этого файла также невозможно. Поэтому, когда я добавил запись в конец config / application.rb as,
require "./lib/file_name_without_extention
она работала нормально.

Свапнил Чинчолкар
источник