Carregamento automático de arquivos lib no Rails 4

229

Eu uso a seguinte linha em um inicializador para carregar automaticamente o código no meu /libdiretório durante o desenvolvimento:

config / inicializadores / custom.rb:

RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?

(da dica rápida do Rails 3: recarregar automaticamente as pastas lib no modo de desenvolvimento )

Funciona muito bem, mas é muito ineficiente para usar na produção. Em vez de carregar bibliotecas em cada solicitação, eu só quero carregá-las na inicialização. O mesmo blog possui outro artigo que descreve como fazer isso:

config / application.rb:

# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

No entanto, quando mudo para isso, mesmo em desenvolvimento, recebo NoMethodErrors ao tentar usar as funções lib.

Exemplo de um dos meus arquivos lib:

lib / extensions.rb:

Time.class_eval do
  def self.milli_stamp
    Time.now.strftime('%Y%m%d%H%M%S%L').to_i
  end
end

A chamada Time.milli_stamplançará NoMethodError

Sei que outras pessoas responderam perguntas semelhantes no SO, mas todas parecem lidar com convenções de nomenclatura e outros problemas com os quais eu não precisava me preocupar antes. Minhas aulas de lib já funcionavam para carregamento por solicitação, só quero mudar para carregar por inicialização . Qual é o caminho certo para fazer isso?

Yarin
fonte
A pasta config / initializers é carregada automaticamente quando um aplicativo Rails é iniciado?
Jwan622

Respostas:

548

Eu acho que isso pode resolver seu problema:

  1. em config / application.rb :

    config.autoload_paths << Rails.root.join('lib')

    e mantenha a convenção de nomenclatura correta na lib .

    em lib / foo.rb :

    class Foo
    end

    em lib / foo / bar.rb :

    class Foo::Bar
    end
  2. se você realmente quiser fazer alguns patches de macaco em arquivos como lib / extensions.rb , poderá solicitá-lo manualmente:

    em config / initializers / require.rb :

    require "#{Rails.root}/lib/extensions" 

PS

ifyouseewendy
fonte
1
@ ifyouseewendy- Você está exatamente certo- porque o extensions.rb não estava seguindo as convenções de nomenclatura do Rails, o Rails não a incluiria no processo de carregamento. Eu consegui fazê-lo manualmente, exigindo-o.
Yarin 31/10
@ifyouseewendy como posso incluir arquivos antes do carregamento dos modelos? adicionar o caminho para o carregamento automático é legal, mas como controlar a ordem de inclusão? thx
Matrix
@Matrix "incluir arquivos antes do carregamento dos modelos", você pode solicitar manualmente seu arquivo sem usar o recurso de carregamento automático.
ifyouseewendy
@ifyouseewendy se eu precisar no inicializador, mas o arquivo estiver em autoload_path, ele será recarregado (carregado duas vezes) ou não? theire é um "require_once" como no php?
Matrix
5
Isso não parece funcionar na API do Rails 5 na produção (mas funciona no desenvolvimento). Eu acredito que você precisa usar config.eager_load_paths << Rails.root.join('lib'). No entanto, isso tem uma grande desvantagem em eager_load_pathscarregar tudo nas tarefas também. Eu acho que a solução da lulalala é melhor. Aqui está um post do blog com mais detalhes: blog.arkency.com/2014/11/…
hirowatari
33

Embora isso não responda diretamente à pergunta, mas acho que é uma boa alternativa evitar a questão completamente.

Para evitar todo o autoload_pathsou eager_load_pathsaborrecimento, criar uma "lib" ou um diretório "MISC" no diretório "app". Coloque os códigos como faria normalmente lá, e o Rails carregará arquivos da mesma forma que carregará (e recarregará) arquivos de modelo.

lulalala
fonte
3
Estou no Rails 4.2. E não auto arquivos de carga sob app, eu preciso fazê-lo manualmente ...... ou necessidade de colocá-lo no caminho autload ..
Arup Rakshit
6
Você está errado, Arup, todos os subdiretórios do diretório de aplicativos estão automaticamente na matriz autoload_paths no Rails 4.2.Veja edgeguides.rubyonrails.org/…
Dr.Strangelove
Exceto o app/viewsdiretório que não é adicionado; ou melhor, é explicitamente removido.
James B. Byrne
1
Ótima resposta. A única coisa que funcionou para mim nos trilhos 5 / api.
Jstafford
6
Lembre-se de que libse destina a um código que pode ser aplicado a vários projetos e pode ser extraído para uma gema. Caso contrário, crie uma pasta mais apropriada na pesquisa de aplicativos como services/ou presenters/e até os subdivide.
PhilT
6

Isso pode ajudar alguém como eu que encontra essa resposta ao procurar soluções sobre como o Rails lida com o carregamento de classes ... Eu descobri que precisava definir um modulecujo nome correspondesse ao meu nome de arquivo adequadamente, em vez de apenas definir uma classe:

No arquivo lib / development_mail_interceptor.rb (Sim, estou usando o código de um Railscast :))

module DevelopmentMailInterceptor
  class DevelopmentMailInterceptor
    def self.delivering_email(message)
      message.subject = "intercepted for: #{message.to} #{message.subject}"
      message.to = "[email protected]"
    end
  end
end

funciona, mas não carrega se eu não tivesse colocado a classe dentro de um módulo.

sameers
fonte
1
Em ruby, "correspondência adequada" significa que o arquivo está localizado no sistema de arquivos em LOAD_PATH/module/class.rb(sublinhado), onde LOAD_PATHestá nos caminhos de carregamento usados ​​pelo aplicativo Ruby (autoload_paths no caso do Rails). libflutuou de ser carregado automaticamente pelo Rails para não ser carregado automaticamente, e nas versões recentes (> = Rails 3.x) ele não é carregado automaticamente. Qualquer mágica que esteja fazendo esse trabalho para você não é recomendada. Talvez seja um Railscast antigo?
Peter H. Boling
0

Use o config.to_prepare para carregar patches / extensões de macaco para cada solicitação no modo de desenvolvimento.

config.to_prepare do |action_dispatcher|
 # More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
 Rails.logger.info "\n--- Loading extensions for #{self.class} "
 Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
   Rails.logger.info "Loading extension(s): #{entry}"
   require_dependency "#{entry}"
 end
 Rails.logger.info "--- Loaded extensions for #{self.class}\n"

fim

Madx
fonte