Carga automática de archivos lib en Rails 4

229

Utilizo la siguiente línea en un inicializador para cargar automáticamente el código en mi /libdirectorio durante el desarrollo:

config / initializers / custom.rb:

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

(de Rails 3 Quicktip: recarga automática de carpetas lib en modo de desarrollo )

Funciona muy bien, pero es demasiado ineficiente para usar en producción: en lugar de cargar libs en cada solicitud, solo quiero cargarlas al inicio. El mismo blog tiene otro artículo que describe cómo hacer esto:

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/**/"]

Sin embargo, cuando cambio a eso, incluso en el desarrollo, obtengo NoMethodErrors cuando intento usar las funciones lib.

Ejemplo de uno de mis archivos 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

Llamar Time.milli_stamparrojará NoMethodError

Me doy cuenta de que otros han respondido preguntas similares sobre SO, pero todos parecen tratar con convenciones de nomenclatura y otros problemas de los que no tenía que preocuparme antes: mis clases de lib ya funcionaban para cargar por solicitud, solo quiero cambiarlo a la carga por inicio . ¿Cuál es la forma correcta de hacer esto?

Yarin
fuente
¿La carpeta config / initializers se carga automáticamente cuando se inicia una aplicación Rails?
Jwan622

Respuestas:

548

Creo que esto puede resolver tu problema:

  1. en config / application.rb :

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

    y mantenga la convención de nomenclatura correcta en lib .

    en lib / foo.rb :

    class Foo
    end

    en lib / foo / bar.rb :

    class Foo::Bar
    end
  2. si realmente quieres hacer algunos parches de mono en un archivo como lib / extensions.rb , puedes requerirlo manualmente:

    en config / initializers / require.rb :

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

PD

ifyouseewendy
fuente
1
@ ifyouseewendy- Tienes toda la razón- debido a que extensiones.rb no seguía las convenciones de nomenclatura de Rails, Rails no lo incluiría en el proceso de carga. Lo conseguí trabajando requiriéndolo manualmente.
Yarin
@ifyouseewendy ¿cómo puedo incluir archivos antes de cargar los modelos? agregar la ruta para cargar automáticamente es genial, pero ¿cómo controlar el orden de inclusión? thx
Matrix
@Matrix "incluye archivos antes de cargar los modelos", puede requerir manualmente su archivo sin usar la función de carga automática.
ifyouseewendy
@ifyouseewendy si lo requiero en el inicializador pero el archivo está en autoload_path, ¿se volverá a cargar (cargar 2 veces) o no? theire es un "require_once" como en php?
Matrix
55
Esto no parece funcionar en Rails 5 API en producción (pero sí en desarrollo). Creo que necesitas usar config.eager_load_paths << Rails.root.join('lib'). Sin embargo, eso tiene un gran inconveniente en que también eager_load_pathscarga todo en las tareas. Creo que la solución de lulalala es mejor. Aquí hay una publicación de blog con más detalles: blog.arkency.com/2014/11/…
hirowatari
33

Aunque esto no responde directamente a la pregunta, creo que es una buena alternativa para evitar la pregunta por completo.

Para evitar todo el autoload_pathso eager_load_pathsmolestia, crear un "lib" o un directorio "misceláneos" en el directorio "aplicación". Coloque los códigos como lo haría normalmente allí, y Rails cargará archivos tal como cargará (y volverá a cargar) archivos de modelo.

lulalala
fuente
3
Estoy en Rails 4.2. y no carga automáticamente archivos debajo app, necesito hacerlo manualmente ... o necesito ponerlo en la ruta de carga automática ...
Arup Rakshit
66
Estás equivocado, Arup, cualquier subdirectorio del directorio de aplicaciones está automáticamente en la matriz autoload_paths en Rails 4.2 .
Dr.Strangelove
Exceptuando el app/viewsdirectorio que no se agrega; o más bien se elimina explícitamente.
James B. Byrne
1
Gran respuesta. Lo único que funcionó para mí en rieles 5 / api.
jstafford
66
Solo recuerde que libestá destinado a código que se puede aplicar a múltiples proyectos y posiblemente se pueda extraer a una gema. Si no es así, cree una carpeta más apropiada en la búsqueda de aplicaciones como services/o presenters/e incluso elimine estas.
PhilT
6

Esto podría ayudar a alguien como yo que encuentre esta respuesta al buscar soluciones a cómo Rails maneja la carga de la clase ... Descubrí que tenía que definir un module nombre cuyo nombre coincidiera con mi nombre de archivo de manera adecuada, en lugar de solo definir una clase:

En el archivo lib / development_mail_interceptor.rb (Sí, estoy usando el código de un 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, pero no se carga si no hubiera puesto la clase dentro de un módulo.

Sameers
fuente
1
En ruby ​​"hacer coincidir apropiadamente" significa que el archivo está ubicado en el sistema de archivos en LOAD_PATH/module/class.rb(subrayado) donde LOAD_PATHestá en las rutas de carga utilizadas por la aplicación Ruby (autoload_paths en el caso de Rails). libha fluctuado de ser cargado automáticamente por Rails a no cargarse automáticamente, y en versiones recientes (> = Rails 3.x) no se carga automáticamente. No se recomienda la magia que haga que esto funcione para usted. Tal vez es un viejo Railscast?
Peter H. Boling
0

Use config.to_prepare para cargar sus parches / extensiones de mono para cada solicitud en modo de desarrollo.

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"

final

Madx
fuente