¿Qué es un plug-in?

Publicado por

El concepto de plug-in es muy familiar para la mayoría de los usuarios de computadoras y dispositivos móviles. Aplicamos esta idea todos los días ¿quién no se ha molestado con su navegador porque quiere actualizar todo el tiempo el plug-in de Flash? Pero ¿realmente te has preguntado qué es un plug-in? Si sigues leyendo, lo sabrás.

En computación, un plug-in es un componente de software construido para conectarse a una aplicación preexistente y proporcionarle un conjunto de funcionalidades extra. Los programas capaces de gestionar y utilizar plug-ins son muy flexibles, porque optan por reducir su tamaño al evitar implementar características no esenciales, tarea que delegan a los plug-ins. Esto le permite al usuario adaptar el software para sus propios fines, escribiendo sus propios componentes o utilizando plug-ins desarrollados por un tercero.

¿Cómo funcionan?

Desde el punto de vista del usuario, es muy sencillo, por lo general lo único que se necesita para conectar un plug-in es instalarlo en el directorio adecuado y echar a andar la aplicación. Pero ¿cómo puede la aplicación saber que tiene disponible el nuevo plug-in y cómo comunicarse con este? Ese trabajo lo hace un subcomponente usualmente llamado "manejador de plug-ins", el cual se encarga de descubrir qué piezas de software nuevas están almacenadas en el directorio de plug-ins. Cuando encuentra al menos un archivo en esa ubicación, verifica que efectivamente se trate de un plug-in, y en ese caso, lo conecta al núcleo de la aplicación. A partir de ese momento, el plug-in está listo para utilizarse.

Un pequeño ejemplo

Para que quede más clara la idea de cómo se ve por dentro un programa cuando puede conectarse con plug-ins te presento el siguiente ejemplo, que consiste en una pequeña aplicación escrita en Python. Si quieres examinar a detalle el código fuente de este ejemplo, puedes descargar el repositorio desde aquí.

El archivo app es nuestro programa principal, los plug-ins para este programa son módulos normales de Python. Si ejecutas app desde la línea de comandos:

➜ app

Verás una ventana como esta:

Aplicación extensible a través de plug-ins

Inútil al principio, pero esto se debe a que los plug-ins no están instalados. Para poder utilizar un plug-in, es necesario copiarlo en la carpeta plugins (nota que inicialmente están guardados en la carpeta lib).

➜ tree
.
├── app
├── lib
│   ├── calcula.py
│   └── man.py
├── plugins
└── ui.py
2 directorios, 6 archivos

Además de instalarlos, es necesario que tanto los plug-ins como el núcleo del programa compartan una interfaz. En nuestro ejemplo, la interfaz está definida por un conjunto de funciones en el caso de los plug-ins y métodos públicos en el caso del núcleo.

Del lado del núcleo se requieren al menos dos métodos, los cuales le permitirán al plug-in intercambiar información con este:

  • leer_entrada(): para entregar los datos presentes en la entrada del programa
  • escribir_salida(): para que el núcleo reciba la salida producida por el plug-in

A su vez, cada plug-in válido necesita implementar su propia interfaz, que le permitirá registrarse dentro del programa y responder a las solicitudes que reciba desde el núcleo:

  • activo(): cuando el núcleo descubra los plug-ins instalados, va a invocar esta función para saber si puede o no utilizar el plug-in
  • iniciar(): una vez que el núcleo sabe que está frente a un plug-in válido, lo conectará a través de esta función.
  • hacer() : es la función principal del plug-in, cada vez que el núcleo quiera utilizarlo, va a invocar esta función.

El programa consiste en dos entidades independientes: una es el núcleo, y la otra es una interfaz gráfica. Dentro del núcleo existe otro componente llamado manejador, que es el encargado de administrar los plug-ins. La estructura de nuestro ejemplo se ve más o menos así:

Modelo UML

El código

La parte importante del programa está en el manejador de plug-ins, el cual está implementado en la clase app.Manejador, teniendo como disparador el método app.Nucleo.carga_plugins().

El método carga_plugins() descubre el contenido del directorio plugins buscando archivos con extensión .py y construyendo la lista correspondiente. Estos son los nombres de los plug-ins instalados. Si el plug-in se cargó correctamente, entonces lo agrega a la interfaz gráfica para que esta muestre el botón correspondiente.

class Nucleo:
  """
  app.Nucleo

  Este componente se encarga de conectar la interfaz de usuario con cada uno de
  los plugins disponibles.
  """
  def __init__(self):
    self.__ruta = os.path.join(os.path.realpath('.'), 'plugins')
    self.__interfaz = None
    self.__manejador = Manejador(self)

  def carga_plugins(self):
    """
    app.Nucleo.carga_plugins()
    --------------------------

    Lee el directorio donde se espera estén instalados los plugins y los carga
    en tiempo de ejecución.

    DEVUELVE
      * Nada
    """
    # Revisa si el directorio de plugins existe, y lo crea si es necesario
    if not os.path.exists(self.__ruta):
      os.makedirs(self.__ruta)
    sys.path.append(self.__ruta)

    # Función para quitar la extensión del nombre del plugin
    # Se espera que cada plugin sea un módulo de python: *.py
    filtro  = lambda plug: ".py" == plug[-3:]
    nombre  = lambda plug: plug[:-3]
    plugins = map(nombre, filter(filtro, os.listdir(self.__ruta)))

    for nombre in plugins:
      plugin = self.__manejador.carga(nombre)
      if plugin:
        self.__interfaz.agrega(nombre, plugin.hacer)

El Manejador, importa el módulo. En caso de éxito configura el plug-in y lo registra en una tabla para futuras referencias; en caso contrario, avisa al núcleo devolviendo None

class Manejador:
  """
  app.Manejador
  -------------

  Componente encargado de gestionar los plugins instalados.
  """

  def __init__(self, nucleo):
    self.nucleo = nucleo
    self.__plugins = dict()

  def carga(self, nombre):
    """
    app.Manejador.carga()
    ---------------------

    Busca el plugin especificado, y carga el módulo correspondiente
    """
    archivo, ruta, info = imp.find_module(nombre)
    plugin = imp.load_module(nombre, archivo, ruta, info)

    if plugin.activo():
      plugin.iniciar(self.nucleo)
      self.__plugins[nombre] = plugin
    else:
      plugin = None

    return plugin

Mientras tanto, en un plug-in, además de implementar la interfaz que mencioné al inicio, se requiere mantener una conexión con el núcleo de la aplicación. Como este es un ejemplo sencillo, nuestra conexión simplemente es una variable que hace referencia al núcleo mismo y cuyo ámbito es todo el módulo.

# -*- encoding:utf-8 -*-
"""
man
---

Plugin para consultar el manual en línea.
"""

import subprocess

__nucleo = None
__activo = True

def hacer():
  global __nucleo
  comando = __nucleo.leer_entrada()
  comando_man = ["man", "-P", "cat", comando ]

  manual = subprocess.check_output(comando_man)
  __nucleo.limpiar()
  __nucleo.escribir_salida(manual)

def iniciar(nucleo):
  global __nucleo
  __nucleo = nucleo

def activo():
  global __activo
  return __activo == True

Y eso es todo. El resto del código en el repositorio es infraestructura para tener un programa completo para hacer esta demo:

Demostración

Como te habrás dado cuenta, el concepto de plug-in es muy sencillo pero a la vez poderoso, porque nos permite ver a estas piezas de software como bloques para construir soluciones propias.

3 comments

  1. Hola

    Te respondo en este artículo por que es el último que pusiste, pero me refiero al contenido en general de la web:

    Por favor, cambia el tipo de letra, el color de la misma o el color de fondo. ¡¡ es un horror intentar leer algo !! Los artículos parecen interesantes pero son totalmente ilegibles.

    Lo he probado desde dos ordenadores diferentes y diferentes navegadores, es totalmente ilegible.

    Un saludo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *