TDD WordPress Theme

Tanto al comenzar a desarrollar un tema desde cero como cuando ya adquirió mayor complejidad tenemos que hacer una serie de chequeos de forma manual para asegurarnos que no nos olvidamos de incluir ninguna funcionalidad o no quedó código muerto.

Obviamente una buena checklist y una organización bien planificada de nuestro código simplifica mucho el tema pero siempre es menester automatizar este tipo de tareas.

WordPress provee una Test Suit pero está orientada principalmente al testeo del funcionamiento de WordPress más que a las cuestiones atinentes al desarrollo de themes.

Si bien puede ser utilizada como base para testear nuestros temas, a mi entender, agrega demasiado overhead para lo que nos aporta. Partiendo de este presupuesto decidí comenzar a desarrollar algunos tests propios con PHPUnit.

Primeramente, basado en el checklist que antes mencioné, definí una serie de chequeos que me interesa automatizar:

  • Existe los archivos functions.php, style.css y el screenshot
  • No hay archivos sin usar
  • Existe el template correspondiente a los template tags usados. Por ej.: get_header y header.php
  • Todas las uris locales son relativas
  • Todos los scripts/styles registrados son usados y existen
  • Hay sidebars y todas son usadas en algún lado
  • Todos los espacios de menu son usados en algún momento
  • Las páginas/artículos y el sitio tienen metadatos
  • El favicon está definido y la ruta existe
  • El theme no tiene que tomar funciones de plugin:
    • Dashboard widgets
    • Custom Post Types
    • Custom Taxonomies
    • Shortcodes
    • Metaboxes
    • Social integrations
    • Bloques de Gutenberg
  • La paginación respeta la configuración de Ajustes > Lectura
  • Customizer sanitization
  • Scripts/styles externos cargados de forma agnóstica
  • No se usa @import en los stylesheet
  • Soporte para title tag
  • Uso de $content_width
  • CSS básicos definidos:
    • alignleft, aligncenter, alignright,
    • wp-caption,
    • size-full, size-large, size-medium, size-thumbnail
  • Uso de WP_Filesystem en vez de las PHP File Functions: mkdir, fopen, fread, fwrite, fputs
  • Sin estilos o scripts hardcodeados
  • Scripts cargados únicamente en footer
  • i18n

A primera vista, algunos son sencillos como la verificación de la existencia de archivos, otros van a requerir análisis del código y algunos que podrían ser verificados utilizando la API que trae WordPress contra un servidor de desarrollo o staging.

Por ahora voy a ir haciendo el desarrollo y pruebas contra la copia de desarrollo del theme de un sitio que está en producción para tener un objetivo más real aunque la idea sería, a largo plazo, constituir un repositorio agnóstico que pueda ser rápidamente clonado en nuestro proyecto.

La primera aproximación quedaría con la siguiente estructura dentro de mi workflow habitual:

  • tests/bootstrap.php
  • tests/FaviconTest.php
  • tests/MissingFilesTest.php
  • tests/StylesheetTest.php
  • composer.json
  • phpunit.xml

Y el código propiamente de cada archivo sería:

Próximamente estaré publicando más novedades.

WordPress Customizer JS API

Esta entrada es la parte 1 de 1 en la serie Wordpress Customizer JS API

A partir de la versión 4.1 de WordPress implementó una API en Javascript que permite la creación y el control de todos los elementos del Customizer además del renderizado de controles a partir de plantillas de Underscore.

Estas dos novedades permiten el manejo de los paneles, las secciones y los controles únicamente desde Javascript, abriendo un mundo de posibilidades.

La organización de la API viene dada por el uso de colecciones para agrupar las instancias de los elementos que ya existen y modelos para la creación de nuevos. Las colecciones son:

  • wp.customize.control
  • wp.customize.panel
  • wp.customize.section

En tanto, los modelos son:

  • wp.customize.Control
  • wp.customize.Panel
  • wp.customize.Section

En el caso de los controles existen modelos específicos que extienden a Control y agregan características específicas para cada tipo:

  • BackgroundControl
  • BackgroundPositionControl
  • CodeEditorControl
  • CroppedImageControl
  • DateTimeControl
  • HeaderControl
  • ImageControl
  • MediaControl
  • SiteIconControl
  • UploadControl

La utilización de las colecciones y los métodos es bastante intuitiva ya que sigue esquemas conocidos para los que tienen una base de Javascript y similares a los que se utiliza mediante la API PHP de WordPress.Por ejemplo, 

Pero, antes de meternos de lleno, necesitamos que nuestro código se cargue cuando abramos el Customizer, para ello vamos a usar el hook customize_controls_enqueue_scripts:

function wp_enqueue_customize_script() {
    wp_enqueue_script( 'wp-customizer', get_template_directory_uri() . '/scripts/customizer/main.js', [], null, true );
}

add_action( 'customize_controls_enqueue_scripts', 'wp_enqueue_customize_script' );

Paneles

Para agregar paneles invocamos el método add de la colección panel que recibe como parámetro un objeto creado a partir del modelo Panel que, a su vez, recibe los mismos parámetros que $wp_customize->add_panel():

wp.customize.panel.add(
    new api.Panel( 'wp_nuevo_panel', {
        title: 'Nuevo panel',
        priority: 25
    })
);

Lo que agregaría un panel con id wp_nuevo_panel, título Nuevo panel y prioridad 25.

A partir de que lo agreguemos podremos acceder al mismo mediante la colección de la siguiente manera:

wp.customize.panel('wp_nuevo_panel').sections();

Lo que devuelve todas las secciones anidadas en este panel.

wp.customize.panel('wp_nuevo_panel').params.title;

Que nos permite obtener el título del panel en cuestión.

Secciones

Las secciones siguen el mismo formato que los paneles:

wp.customize.section.add(
    new api.Section( 'wp_nueva_seccion', {
        title: 'Nueva seccion',
        panel: 'wp_nuevo_panel',
        priority: 25,
        customizeAction: 'Personalización'
    })
);

Lo único particular que vemos es la opción customizeAction que representa el texto que aparece en la parte de arriba del título de la sección y que es necesario definirlo, por ahora, porque no tiene valor predeterminado.

Al igual que con los paneles, una vez agregada la sección, podemos acceder a la misma a través de la correspondiente colección:

wp.customize.sections('wp_nueva_seccion').priority(30);

Lo que cambiaría la prioridad asignada al panel cambiando la posición del mismo. O:

wp.customize.sections('wp_nueva_seccion').controls();

Para obtener la colección de controles que hay en la sección.

Controles

Llegamos a la parte más interesante del Customize, los controles. Por ahora vamos a ver la interacción desde la API y más adelante, en otro artículo, las potencialidades de las templates de Underscore.

En este caso nos encontraremos con más particularidades que con los otros dos apartados porque los controles, para que sean finalmente almacenados en nuestra BBDD, tienen estar relacionados con una setting. Si bien se pueden agregar settings de forma dinámica, por ahora, sigue siendo más sencillo agregarlas desde PHP y utilizarlas desde Javascript.

wp.customize.control.add(
    new api.Control( 'wp_nueva_control', {
        label: 'Texto',
        section: 'wp_nueva_seccion',
        setting: wp.customize( 'mi_setting_de_texto' )
    })
);

Como vemos, un sencillo control de texto, no nos presenta ninguna dificultad. Lo único nuevo es la forma de pasar la setting y se debe a que wp.customize es, a su vez, la colección donde se almacenan las settings.

Pero, por ejemplo, cuando queremos agregar un MediaControl necesitamos especificar además el texto de las etiquetas:

wp.customize.control.add(
    new api.MediaControl( 'anred_placemark_image_control', {
        section: 'wp_nueva_seccion',
        label: 'Logo',
        setting: api( 'wp_logo_image' ),
        button_labels: {
            change: 'Cambiar logo',
            default: 'Sin logo',
            frame_button: 'Elegir logo',
            frame_title: 'Elegir logo',
            placeholder: 'Logo no elegido',
            remove: 'Quitar logo',
            select: 'Elegir logo'
        }
    }
)

Hasta el momento no encontré ninguna referencia sobre qué parámetros tienen valor por defecto y cuales no para cada control. La fuente última para saberlo es el código de la API que se encuentra en wp-admin/js/customize-controls.js o el código PHP de los controles en la carpeta wp-includes/customize/.

Arquitectura de un sitio web

La arquitectura de un sitio web o aplicación web es la descripción de la configuración, función y relación de los distintos componentes que la integran. En este artículo me voy a centrar en dar un vistazo general de la arquitectura del hardware subyacente sobre el que corre un sitio web.

En el principio…

Cuando comenzamos con nuestro sitio lo más probable que nuestra arquitectura se limite a un único servidor donde están los tres componentes más comunes: el servidor web, la base de datos y el almacenamiento. Además de esto nos apoyaremos en un servidor DNS, generalmente manejado por nuestro proveedor de hosting, y en un CDN para servir las librerías y/o el framework que utilicemos.

Servidor web

Es el encargado de recibir y responder las peticiones HTTP del navegador de los usuarios. En lo básico su funcionamiento se limita a recibir un pedido de una url, buscar el archivo correspondiente en el sistema de archivos y enviarlo. Este funcionamiento se puede ampliar mediante módulos o extensiones como PHP que le permite “ejecutar” los archivos escritos en ese lenguaje antes de enviar la respuesta al cliente. Apache httpd, NGINX y Microsoft IIS son los tres más usados.

Base de datos

Toda aquella información que no guardamos en archivos y no tomamos de otros servicios normalmente termina almacenado de una forma sistematizada en la base de datos. Las ventajas derivadas de tener la información en una BBDD viene dada por la especialización de las mismas que permiten la realización de consultas mucho más complejas y más rápido que con otros sistemas de almacenamiento. Oracle Database, MySQL y Microsoft SQL Server son las tres más usadas.

Almacenamiento

Imágenes, audio, código fuente, étc, todo tiene que estar guardado en algún lugar accesible para el servidor web. A los efectos prácticos es donde va a parar todo aquello que por su tamaño u otras razones no podemos almacenar en nuestra base de datos. Dependiendo de nuestras necesidades puede ser el mismo sistema de archivos del sistema operativo o algún sistema por bloques u objetos más especializado.

CDN

Aunque está representado únicamente por un servidor es, en realidad, una red de servidores que proveen almacenamiento distribuido en muchos puntos del planeta. La idea detrás de este servicio es reducir la distancia entre el usuario y los archivos proveyendo una copia de los mismos en un punto de la red más cercano físicamente. Esto permite liberar parte de la carga de nuestro servidor y mejorar los tiempos de acceso a la información. Algunos de los más conocidos son Akamai, MaxCDN, Amazon AWS y Cloudflare.

Más potencia…

A medida que la cantidad de usuarios que acceden a nuestro sitio web comienza a aumentar los recursos de nuestra aplicación se van a ver excedidos. Esta falta de recursos suele identificarse en alguno de estos cuatro puntos:

  • CPU
  • Memoria
  • E/S de red
  • E/S de disco

En líneas generales, dejando de lado los problemas de código, nuestros problemas se van a centrar en CPU y memoria. Tanto nuestro servidor web como nuestra base de datos compiten por estos recursos y cuando se agotan es cuando suelen comenzar a aparecer las desconexiones intermitentes, falsos 404 y caídas.

La solución obvia pasa por el escalamiento vertical, es decir, mover el sitio a un servidor más potente. Durante un tiempo esto nos va a permitir seguir trabajando pero en algún momento vamos a encontrarnos con un techo y vamos a necesitar una nueva solución.

Esta solución consisten en utilizar servidores específicos para cada componente del sitio web. Ahora ya no compiten entre si sino que tienen recursos dedicados.

Reverse Proxy

Un servidor proxy funciona como intermediario entre los dos extremos de una conexión permitiendo hacer cambios en el contenido como el destino de la comunicación de forma transparente para ambos terminales. En lo que a nuestro sitio respecta, su funcionalidad pasa por recibir todos los pedidos de los clientes y redirigirlos al servidor apropiado. En el gráfico se utiliza para separar el contenido estático de la aplicación propiamente dicha.

CDN

Si tenemos una gran cantidad de archivos estáticos o son de un gran tamaño también puede ser momento de empezar a utilizar la CDN también para nuestros contenidos. Cada servicio tiene sus particularidades pero en general esta distribución de contenido se dará mediante push o pull. El primer caso es la carga activa de nuestros contenidos en la CDN, utilizamos la red para aquellos contenidos que específicamente  subamos. El otro método implica que la CDN funcione como intermediaria con nuestro sitio web y la primera vez que un usuario solicite un contenido lo cargue de forma automática.

No hay dos sin tres…

Al igual que la primera solución, la anterior, también puede verse superada y, nuevamente, nos encontraremos en la necesidad de agregar más servidores pero ¿dónde? Cada parte de nuestro sitio está en su propio servidor y no podemos subdividirlo en componentes más pequeños por lo que ahora tendremos que empezar a duplicarlo. Si tenemos dos servidores web en vez de uno deberíamos duplicar nuestra capacidad, ídem con la BBDD. 

La duplicación de los distintos módulos trae, igualmente, nuevos problemas asociados: como distribuir los pedidos de los usuarios entre uno y otro y cómo mantenerlos sincronizados para que den la misma respuesta.

Load Balancer

Para solucionar el primer problema entra en el juego los balanceadores de carga cuya función específica es decidir a que nodo enviar la petición recibida. Dependiendo de la implementación que utilicemos además tendremos la posibilidad de desviar a el contenido a otro servidor si uno se cae, persistir las sesiones mandando todas las peticiones de un usuario al mismo servidor, étc.

Replicación de la BBDD

Mantener sincronizadas distintas base de datos es un problema bastante complejo pero, por suerte, en lo que respecta a nuestro sitio web, los distintos DBMS traen sistemas que se encargan de hacerlo con unas pocas líneas de configuración de nuestra parte.

En líneas generales la idea detrás de la replicación es la existencia una base de datos original que, cada vez que se modifica, envía a una BBDD copia o replica la información necesaria para reproducir el cambio. De esta forma cuando hace falta una consulta se puede realizar tanto a la BBDD original como a las replicas con la seguridad de obtener la misma información. 

Obviamente, esta solución va a requerir algunos cambios en nuestra aplicación porque únicamente se puede escribir en una sola de las BBDD y, en caso de que nuestra aplicación haga más escrituras que lecturas, puede que la performance empeore en vez de mejorar por la carga de mantener la sincronización.

En conclusión…

Las arquitecturas presentadas son aproximaciones incompletas y genéricas. Existen muchos otros sistemas que podemos incluir en nuestro sitio para mejorar el desempeño como la cache tanto para el servidor web como para la base de datos.

Además, dependiendo de nuestras necesidades puede ser que tengamos múltiples servidores web y una única base de datos o únicamente un servidor web y muchos servidores de almacenamiento. También el balanceo de carga puede hacerse después del proxy y no antes.

En resumen, cada caso necesita un análisis específico y los ejemplos dados no tienen porque ser aplicables a todos.

Notificaciones de Windows en WSL

Con todos los avances que ha tenido el Windows Subsystem fro Linux, una de las funcionalidades que todavía sigue pendiente de implementar es el soporte para notificaciones. Esta característica que podría haberse considerado meramente estética desde la aparición de los background jobs pasó a ser una necesidad.

A menos que nos podamos permitir tener varias terminales abiertas en pantalla para monitorear cualquier proceso ejecutado en segundo plano o de forma automática, es necesario que utilicemos un sistema de información centralizado y Windows ya nos provee sus notificaciones.

El principal problema con esta idea es que no existe ningún punto de contacto entre Windows y el D-Bus.  Dada esta limitación es necesario buscar otro enfoque. La mejor opción hasta ahora viene siendo aprovechar la interoperabilidad1 entre Windows y nuestro sabor de Linux.

Para esto, necesitamos instalar BurnToast, un módulo de PowerShell que nos permite lanzar desde la línea de comandos notificaciones de Windows.

PS > Install-Module -Name BurntToast

Una vez instalado, desde probamos el funcionamiento:

PS > New-BurntToastNotification -Text "Hi There!"

En caso de que nos de error “porque la ejecución de scripts está deshabilitada en este sistema”. Es necesario que habilitemos la ejecución, por lo menos, para módulos firmados:

PS > Set-ExecutionPolicy RemoteSigned

Ahora que estamos seguros que todo está listo para emitir notificaciones desde PowerShell, es hora de que hagamos pruebas desde WSL.

WSL > powershell.exe -command "New-BurntToastNotification -Text 'Hi There!'"

Finalmente, ya estamos listos para enviar notificaciones a Windows desde WSL. Además de poder elegir el texto podemos personalizar otros aspectos de las notificaciones, toda la documentación para esto está en la página del módulo.

Recursos útiles para el desarrollo web

Serie de herramientas y bibliotecas online que son útiles durante el proceso de desarrollo web.

Google Analytics

Podemos usar otro pero definitivamente este es el gestor de estadísticas gratuito más completo. Información en tiempo real e histórica sobre los horarios de acceso, distribución geográfica, origen, étc. Y, más interesante al momento de pensar la, datos sobre el tipo de dispositivos usados para conectarse (mobile, tablet o desktop), el navegador o la resolución de pantalla. Por último, si tenés instalado el plugin (ahora sin soporte pero todavía funcional) podés incluso analizar el click heatmap de tu sitio.

Can I Use

Una vez que ya sabemos todo sobre los dispositivos que los usuarios van a usar para conectarse al sitio, es momento de empezar a delimitar las tecnologías que vamos a tener disponibles. En 10 años Can I Use se ha vuelto el standard de facto para obtener este tipo de información. Alcanza con ingresar en el buscador el nombre de la tecnología, un comando o etiqueta relacionado y tendremos información sobre el soporte por versión en los distintos navegadores, el uso relativo de los mismos y notas al pie sobre cosas a tener en cuenta.

MDN Web Docs

Definidas las tecnologías empieza el momento de picar código y comienzan, también las dudas que si un checkbox lanza un evento click o un change, que cual era el comando para agregar un elemento al principio de un Array, étc. Mientras más tecnologías están involucradas en el desarrollo web más difícil es acordarse al dedillo de todo y ahí entra a jugar MDN Web Docs una de las referencias más completas en el tema.

CSS-Tricks

Si bien MDN Web Docs tiene un apartado para CSS, la autoridad en el tema es de CSS-Tricks. Desde snippets a, como su nombre lo indica, trucos y consejos relacionados con el uso de los estilos y una galería de ejemplos de los más interesantes.

CSS Triggers

Una vez que ya empieza a tomar forma el sitio, empezamos a agregar más interactividad al mismo mediante la captura de eventos y manipulación del DOM. Estas modificaciones del modelos de objetos repercuten directamente en el CSSOM y pueden implicar un reflow, un repaint o un relayout cada uno con sus respectivas demoras. Cuando necesitamos saber si es mejor usar transform o modificar directamente el width de un objeto ahí entra CSS Triggers para no equivocarse.

Regular Expressions 101

Hablando de tópicos donde hace falta un ayuda memoria, llegamos a las expresiones regulares. Karma de muchos programadores, Regular Expressions 101 trae tanto una referencia como un entorno de pruebas para poder generar ese pattern perfecto.

Debuggex

Ya ni nos acordamos que matchea ese pattern que tenemos delante o estamos leyendo código de otra persona y vamos medio perdidos nada mejor que Debuggex, un tester visual que nos permite ver de qué va la cosa.

CSS Stats

Recién habíamos terminado de armar todo cuando el cliente nos pidió “unos pocos cambios, algo acá y allá” o llevamos tiempo en producción y hasta el hijo de la vecina, “que sabe un poco de computadoras”, metió mano. Por lo que sea terminamos con 402 colores únicos o 12k de reglas y no lo sabemos. Llevar un buen control sobre la situación de nuestros archivos de estilo es una tarea que se ve tremendamente simplicada con CSS Stats.

Pingdom

Siguiendo en la línea de controlar la performance del sitio, entramos en el área de las herramientas más generales como Pingdom que nos provee rápidamente con un resumen de la situación general. Vistoso por demás, también resulta útil para “robar” algunos gráficos para presentar al cliente.

WebPagetest

Lo que Pingdom tiene de bonito, WebPagetest lo tiene de detallado. Nunca lo vamos a usar como imagen en un Power Point pero si necesitamos estadísticas precisas para ver dónde está el cuello de botella en la carga o qué script está consumiendo demasiado tiempo este sitio nos va a permitir encontrar la información que buscamos.

WordPress y PHPUnit

PHPUnit es uno de los frameworks para test unitarios automáticos para PHP más usados y el elegido por los desarrolladores de WordPress junto con QUnit para probar la plataforma. Para facilitar las pruebas, los desarrolladores han creado ya todo un conjunto de test y clases auxiliares que nos resultarán muy útiles en nuestro workflow.

Instalación

Primero, necesitamos instalar PHPUnit en su versión 6 ya que la 7 no es soportada por WordPress. Hay distintas formas documentadas en el sitio del framework y podemos elegir la que más se adapte a nuestro entorno.

Segundo, creamos una nueva base de datos separada para los tests porque que la suite va a borrar todos los datos de las tablas de la instalación donde se ejecute.

Tercero, no es necesario pero definitivamente es la mejor forma de manejar el entorno de pruebas vamos a instalar wp-cli. Nuevamente, hay varías formas de llevar adelante la tarea y va a depender de nuestro entorno el método que elijamos.

Por mi parte, me ha dado mejores resultados la instalación recomendada: descargar el .phar, darle permisos de ejecución y ponerlo en una carpeta que esté en PATH.

Cuarto, nos situamos en la carpeta de la instalación de wordpress para utilizar las opciones del paquete scaffold de wp-cli para generar los archivos necesarios para instalar los tests según sea un plugin o un theme.

wp scaffold plugin-tests <nombre plugin>
wp scaffold theme-tests <nombre theme>

Quinto, ejecutamos el archivo generado dentro de la carpeta del plugin/theme en el paso anterior para instalar el entorno de pruebas. Vamos a necesitar las credenciales y datos de acceso a la base de datos que creamos antes.

bin/install-wp-tests.sh <nombre bbdd> <usuario> <contraseña> <host>

Con esto queda todo listo para que empecemos a desarrollar nuestros tests y ponerlos en la carpeta homónima para que PHPUnit los encuentre.

Para más información sobre los unit test en WordPress estos artículos van a dar una idea de las posibilidades:

Patrón de Inyección de Dependencias

La idea detrás del patrón de inyección de dependencias es lograr una mayor separación de las responsabilidades de nuestro código cambiando la forma en que se manejan las dependencias entre objetos.

Un forma común de establecer esta dependencia es codificar dentro de una clase, normalmente en el constructor, la creación de la instancia de un objeto que es necesario para su funcionamiento.

 
public class Dependencia { ... }

public class Clase {
	private Dependencia depend;
	
	public Clase() {
		depend = new Dependencia();
	}
	
	public void funcion() {
		depend.doSomething();
	}
}

class Program {
	public static void main(String [ ] args) {
		Clase miclase = new Clase();
		miclase.funcion();
	}
}

Este tipo de dependencia suele ser denominado fuerte porque una clase depende directamente de la otra para funcionar. Cualquier cambio en la clase de la que se depende obliga a la realización de cambios en la clase dependiente generando, en la práctica, una subordinación entre una y otra.

Como podemos suponer, al ser dos clases diferentes, las responsabilidades de cada una son distintas y no deberían estar tan ligadas porque dificulta el mantenimiento, la extensión del código y la escalabilidad del software. Aquí es donde entra la inyección de dependencias.

Para lograr una mayor separación de responsabilidades, este patrón recomienda la utilización de interfaces o clases abstractas para establecer las dependencias y, por lo tanto, el uso de objetos ya creados en vez de instanciarlos internamente.

En la práctica, cada clase debe indicar el “tipo” de objeto que necesita para trabajar y es otra parte del código la que se debe encargar de implementar la clase en función de la interfaz, crearlo y pasarlo al objeto.

 
public interface iDependencia { ... }

public class MiDependencia : iDependencia { ... }

public class Clase {
	private iDependencia Depend;
	
	public Clase(iDependencia d) {
		Depend = d;
	}
	
	public void funcion() {
		Depend.doSomething();
	}
}

class Program {
	static void main() {
		MiDependencia dependencia1 = new MiDependencia();
		Clase miclase = new Clase(dependencia1);
		miclase.funcion();
	}
}

Como se puede observar ya no existe una relación directa entre ambas clases. Nuestra clase indica que necesita un objeto creado a partir de una clase que implemente la interfaz pero no específica qué clase. Este tipo de separación permite cambiar rápidamente la clase que pasamos como dependencia e incluso trabajar con varias clases.

Un ejemplo muy extendido de este tipo de diseño es el que utilizan los frameworks que se comunican con bases de datos. Estos establecen una interfaz con este fin y una o dos clases que la implementen para distintas BBDD dando la libertad al programador de crear una nueva si necesita trabajar con otra o instanciarlas varias veces si necesita más de una conexión.

Como toda solución tiene sus pros y sus cons. Como otra ventaja aparte de las mencionadas, al estar más modularizado el código, es más fácil realizar test unitarios.

Como contra la necesidad de que el lenguaje soporte interfaces o clases abstractas y la necesidad de escribir más código para implementar el diseño. También, a consecuencia de agregar un grado de separación entre la dependencia y la clase, se puede dificultar la depuración en caso de surgir errores de integración.

Patrón de Carga Diferida: Value Holder

Esta entrada es la parte 4 de 4 en la serie Patrón de Carga Diferida

El patrón de carga diferida es una técnica para mejorar la performance utilizada en casos en que nos encontramos con que no todas las características de un programa son usadas durante su ejecución o para distribuir los tiempos de carga, cuando estos son muy grandes, durante la ejecución para poder mejorar los tiempos de respuesta al iniciar.

Hay cuatro formas comunes de implementar la carga diferida: inicialización diferida, proxy virtual, fantasma y value holder.

Value Holder

Esta técnica implica la utilización de un objeto cuyo tiempo de carga es menor y que reemplaza al objeto original hasta que sea necesario su carga.

Por ejemplo, la utilización de una imagen con pocos colores y de muy  pequeño tamaño (un logo en escala de grises) que se utiliza en los sitios webs con muchas fotos en vez de la foto original hasta que esta es cargada.

Es necesario que el value holder esté optimizado al máximo, si termina siendo menos o igual de eficiente que cargar el objeto original el método pierde sentido.

<img class="lazy" src="placeholder-image.jpg" data-src="image-to-lazy-load-1x.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" alt="I'm an image!">
document.addEventListener("DOMContentLoaded", function() {
  var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

  if ("IntersectionObserver" in window) {
    let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          let lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.srcset = lazyImage.dataset.srcset;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });

    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  }
});

Fuente:  Lazy Loading Images and Video

Patrón de Carga Diferida: Fantasma

Esta entrada es la parte 3 de 4 en la serie Patrón de Carga Diferida

El patrón de carga diferida es una técnica para mejorar la performance utilizada en casos en que nos encontramos con que no todas las características de un programa son usadas durante su ejecución o para distribuir los tiempos de carga, cuando estos son muy grandes, durante la ejecución para poder mejorar los tiempos de respuesta al iniciar.

Hay cuatro formas comunes de implementar la carga diferida: inicialización diferida, proxy virtual, fantasma y value holder.

Ghost

Esta técnica implica la creación del recurso pero con una cantidad mínima de información y diferir el resto de la carga hasta que sea necesario.

Por ejemplo, al presentar un listado inicializamos el objeto con la información mínima para mostrar (por ejemplo un título o una descripción) y al hacer click en un ítem recién cargamos el resto del objeto para poder acceder a la información completa.

Algunas consideraciones que es necesario tener en cuenta al utilizar este patrón son:

  • Como la carga se realiza en dos tramos, puede que cuando hagamos la carga inicial el objeto esté disponible y cuando querramos obtener el resto de la información no.
  • Para que tenga sentido usar este patrón la carga mínima tiene que ser considerablemente más chica que la carga total del recurso sino el overhead por realizar dos accesos puede terminar haciendo todo el proceso menos eficiente.
function Documento(id) {
  var self = this;

  $.get('/get_preview/' + id, function (response) {
    self.title = response.title;
    self.path = response.path;
  });

  function mostrar_documento() {
    $.get('/get/' + id, function (response) {
      self.data = response.data;
      console.log(self.data);
    });
  }
}

var documentos = [
  new Documento(1),
  new Documento(3),
  new Documento(4),
];

documentos.forEach( function(e) {
  $('body').append( function(html, index) {
    var $item = $('<p><strong>' + e.id + ': ' + e.title + '</strong>');
    $item.click( this.mostrar_documento() );
  });
});