Muchos plugins incluyen muchas definiciones de clases dentro de plugin.rb, o usan require_relative para cargar archivos ruby. Eso funciona, pero conlleva algunas desventajas:
- No hay recarga automática de cambios en desarrollo. Cualquier cambio requiere un reinicio completo del servidor
- Poner las llamadas a
requireen el orden correcto puede ser complicado - Si se cargan (
require’d) fuera del bloqueafter_initialize, otras clases/módulos cargados automáticamente pueden no estar disponibles
¡Hay una solución! Los plugins pueden apoyarse en el sistema de carga automática estándar de Rails. Para plugins nuevos, todo lo que necesitas está definido en el plugin-skeleton. Este tema describe cómo adaptar un plugin existente y extender la configuración.
1. Definir un módulo y un Rails::Engine para tu plugin
En plugin.rb, define un módulo para tu plugin con un PLUGIN_NAME único, y añade una línea require_relative para cargar el archivo del motor que estamos a punto de crear.
# name: my-plugin-name
# ...
module ::MyPluginModule
PLUGIN_NAME = "my-plugin-name"
end
require_relative "lib/my_plugin_module/engine"
Ahora crea {plugin}/lib/my_plugin_module/engine.rb:
module ::MyPluginModule
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace MyPluginModule
end
end
Cosas importantes a notar:
-
En
plugin.rb, debes incluir::antes del nombre de tu módulo para definirlo en el namespace raíz (de lo contrario, se definiría bajoPlugin::Instance) -
require_relative "lib/.../engine"debe estar en la raíz del archivoplugin.rb, no dentro de un bloqueafter_initialize -
Poner el motor en su propio archivo bajo
lib/es importante. Definirlo directamente en el archivoplugin.rbno funcionará. (Rails usa la presencia de un directoriolib/para determinar la raíz del motor) -
La ruta del archivo debe incluir el nombre del módulo, de acuerdo con las reglas de Zeitwerk
-
El
engine_namese utiliza como prefijo para las tareas rake y cualquier ruta definida por el motor (
documentación de rails) -
isolate_namespaceayuda a prevenir que cosas se filtren entre el núcleo y el plugin (
documentación de Rails)
2. Definir archivos ruby en la estructura de directorios correcta
El motor ahora cargará automáticamente todos los archivos en {plugin}/app/{type}/*. Por ejemplo, podemos definir un controlador
{plugin}/app/controllers/my_plugin_module/examples_controller.rb
module ::MyPluginModule
class ExamplesController < ::ApplicationController
requires_plugin PLUGIN_NAME
def index
render json: { hello: "world" }
end
end
end
Esto se cargará automáticamente cada vez que algo en Rails intente acceder a ::MyPluginModule::MyController. Para probar cosas, intenta acceder a esa clase desde la consola de rails.
Para que la carga automática funcione correctamente, las rutas de los archivos deben coincidir con la jerarquía completa del módulo/clase de acuerdo con las reglas definidas por Zeitwerk.
3. Definir rutas en el motor del plugin
Crea un archivo {plugin}/config/routes.rb
MyPluginModule::Engine.routes.draw do
get "/examples" => "examples#index"
# define routes here
end
Discourse::Application.routes.draw do
mount ::MyPluginModule::Engine, at: "my-plugin"
end
Este archivo se cargará automáticamente por el motor, y los cambios tendrán efecto sin reiniciar el servidor. En este caso, la acción del controlador estaría disponible en /my-plugin/examples.json.
4. Añadir más rutas cargadas automáticamente
A veces te puede gustar introducir directorios adicionales de archivos Ruby cargables automáticamente. El ejemplo más común es el directorio lib/ en un plugin.
Modifica la definición de tu motor para añadir lib/ a las rutas de carga automática del motor:
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace MyPluginModule
config.autoload_paths << File.join(config.root, "lib")
end
Ahora puedes definir un módulo lib como
{plugin}/lib/my_plugin_module/some_lib_module.rb
module ::MyPluginModule::SomeLibModule
end
Y ahora cualquier referencia a ::MyPluginModule::SomeLibModule cargará automáticamente el módulo desde este archivo.
5. ¡Beneficio!
Todos estos archivos ahora se cargarán automáticamente sin ninguna llamada deliberada a require. Los cambios serán capturados automáticamente por rails y recargados en su lugar sin reiniciar el servidor.
Este documento está controlado por versiones: sugiere cambios en github.