Rails / módulos lib y

83

Estoy escribiendo un contenedor personalizado para el open_flash_chartcomplemento. Se coloca /liby se carga como un módulo en ApplicationController.

Sin embargo, tengo algún problema de jerarquía de clases o algo.

Desde cualquier controlador puedo acceder a open_flash_chartfunciones como OpenFlashChart, Lineetc.

Sin embargo, en una clase en un /libmódulo, ¡no funciona!

¿Algunas ideas?

Mantas
fuente
espero que esto pueda ayudarlo a stackoverflow.com/questions/17304110/…
S.Yadav

Respuestas:

147

Hay dos formas de cargar archivos en Rails:

  • Se registra en el proceso de carga automática y hace referencia a una constante que corresponde al nombre del archivo. Por ejemplo, si tiene una app/controllers/pages_controller.rbreferencia a PagesController, se app/controllers/pages_controller.rbcargará automáticamente. Esto sucede para una lista preestablecida de directorios en la ruta de carga. Esta es una característica de Rails y no es parte del proceso de carga normal de Ruby.
  • Los archivos son explícitamente required. Si un archivo es required, Ruby busca en toda la lista de rutas en sus rutas de carga y encuentra el primer caso donde el archivo que required está en la ruta de carga. Puede ver la ruta de carga completa inspeccionando $ LOAD_PATH (un alias para $ :).

Como libestá en su ruta de carga, tiene dos opciones: nombrar sus archivos con los mismos nombres que las constantes, por lo que Rails los recogerá automáticamente cuando haga referencia a la constante en cuestión, o requiera explícitamente el módulo.

También me doy cuenta de que podría estar confundido por otra cosa. ApplicationController no es el objeto raíz del sistema. Observar:

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>

Deberá incluir el módulo en la clase en la que desee usarlo.

class AnotherClass
  include MyModule
end

AnotherClass.new.im_awesome
# AnotherClass is so awesome

Por supuesto, para poder incluir el módulo en primer lugar, deberá tenerlo disponible (utilizando cualquiera de las técnicas anteriores).

Yehuda Katz
fuente
2
Solo quería agregar: Si uno de sus módulos en / lib (o en uno de los directorios de carga automática) ya está definido; por ejemplo, sobrecarga ActiveRecord o String, tendrá que solicitarlo explícitamente o no se cargará
Mike
1
extrañamente, obtengo: GaClient constante no inicializado (NameError), a menos que requiera 'ga_client' de antemano (la clase está definida en lib / ga_client.rb). ¿Existe documentación para el esquema de nombres de carga automática?
mkirk
87

En Rails 3 / lib, los módulos no se cargan automáticamente.

Esto se debe a que la línea:

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

dentro de config / application.rb está comentado.

Puede intentar descomentar esta línea o, (funcionó aún mejor para mí), dejar esto comentado (para referencia futura) y agregar estas dos líneas:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
diegopau
fuente
Esto duplicará la ../libruta en la ApplicationName::Application.config.autoload_pathsmatriz.
jibiel
1
@jibiel, ¿cuál es la resolución aquí?
ylluminate
4
¿Por qué la última opción funcionó mejor para usted? Además, por qué se cambió el valor predeterminado ... debe haber una razón a pesar de que muchas personas encuentran soluciones alternativas.
ckarbass
@ylluminate Tan solo config.autoload_paths += Dir["#{config.root}/lib/**/"]debería ser suficiente. @ckarbass La segunda línea le da la libertad de organizar sus utilidades en subcarpetas y así mejorar sus módulos con espacios de nombres . Eso es practicamente todo. Y aquí está la razón por la que se cambió el valor predeterminado. Más vale tarde que nunca :)
jibiel
Esto me parece un poco complicado. ¿No cargaría esto automáticamente tareas de rastrillo innecesariamente?
Dennis
22

Lo que funcionó para mí, además de descomentar config.autoload_paths (estoy en Rails 3.1.3), fue crear un inicializador como este:

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

De esta manera puedo llamar a los métodos mymodule desde cualquier lugar y como métodos de clase Model.mymodule_methodo como métodos de instanciamymodel.mymodule_method

Quizás algún experto pueda explicar las implicaciones de esto. Por ahora, utilícelo bajo su propio riesgo.

Editar: Después, creo que una mejor aplicación sería:

crea un inicializador como este:

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

Incluya el módulo donde sea necesario, así:

1) si desea utilizarlo como "Métodos de clase", utilice "extender":

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

2) si desea utilizarlo como "Métodos de instancia", inclúyalo dentro de la definición de clase:

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

3) recuerde que se include MyModulerefiere a un archivo my_module.rben su ruta de carga que debe ser requerido primero

Fernando Fabreti
fuente
3
He creado mi módulo en la libcarpeta, por lo tanto, lo he agregado config.autoload_paths += %W(#{config.root}/lib)en el config/application.rbarchivo. Después de eso, seguí su sugerencia para agregar un config/initializers/myapp_init.rbarchivo y su contenido. Todo es bueno. Muchas gracias :)
SMMousavi
Aunque requirefunciona para mí y la carga automática no (método de módulo indefinido), este comentario dice que no debes usarrequire .
Dennis
3

Para usar el módulo lib/my_module.rben sus modelos y controladores:

En config/application.rb:

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

En su modelo (idea similar para su controlador):

require_dependency 'my_module'

class MyModel < ActiveRecord::Base
  include MyModule

  MyModule.some_method
end

Este método se describe con más detalle en http://hakunin.com/rails3-load-paths

Dennis
fuente
0

Puede darse el caso de que desee cargar explícitamente archivos en el directorio lib en el momento de la inicialización de la aplicación.
En mi config / application.rb, tengo una entrada como,
config.autoload_paths += %W(#{config.root}/lib)

También este podría ser el caso de que el nombre / jerarquía del módulo no sea el mismo que en el archivo o la ubicación / el nombre del archivo no es el mismo que esa jerarquía, por lo que la carga automática de ese archivo tampoco es posible. Entonces, cuando agregué una entrada en la parte inferior de config / application.rb as,
require "./lib/file_name_without_extention
funcionó bien.

Swapnil Chincholkar
fuente