Weblogs Código

Variable not found

¿Qué es Blazor, eso de lo que todo el mundo habla?

enero 21, 2020 04:36

Blazor Recuerdo que la primera vez que, en una de mis visitas a Redmond, vi a Steve Sanderson haciendo una demo de Blazor "en vivo" me pareció casi magia: una aplicación web cuya lógica de presentación y UI se implementaba exclusivamente en C#, eliminando por completo de la ecuación a Javascript. ¡Uau!

En aquellos tiempos era aún poco más que un prototipo y no se podía saber si aquella línea de investigación llevaría a alguna parte, pero era tan prometedor que, algún tiempo después, a primeros de 2018, Blazor pasó a ser un proyecto experimental del equipo ASP.NET de Microsoft.

A partir de ese momento se fueron despejando las incógnitas que quedaban en cuanto a la viabilidad real de convertir aquello en un producto y se continuaron implementando funcionalidades, hasta que, en abril de 2019, Daniel Roth anunció que daban por concluida la fase experimental y Blazor entraba oficialmente a formar parte del stack de tecnologías para la web.

A día de hoy, Blazor es un framework completo para el desarrollo de aplicaciones web SPA, como lo pueden ser Angular, React o Vue. Dispone de sistema de bindings, routing, componentes, ciclo de vida, validaciones, plantillas, gestión de errores, inyección de dependencias, etc. Todo lo que podemos necesitar para crear aplicaciones web profesionales de calidad.

La primera versión de Blazor se lanzó acompañando a .NET Core 3.0 en Septiembre de 2019, aunque sólo en uno de sus modelos de hospedado: el denominado server-side o Blazor Server.

Pero no era ese el único plan...

Los "sabores" de Blazor: client-side, server-side... y otros

Inicialmente, la idea de Blazor era conseguir ejecutar el código C# en el browser llevando a éste una versión deducida del runtime de .NET. Este enfoque suele conocerse como Blazor WebAssembly o client-side, pues se basa en llevar físicamente al browser la ejecución de la parte cliente de la aplicación web.

WebAssemblyLa clave del funcionamiento de Blazor client-side es un runtime de .NET que corre sobre WebAssembly (WASM), una especificación abierta que define un "lenguaje ensamblador" común a todas las máquinas virtuales presentes en los navegadores modernos (y pollyfillable para los anteriores). Sobre ese runtime, ya en el lado cliente, es donde corren nuestras aplicaciones Blazor cuando elegimos este modelo de hospedaje.

Así, la primera vez que un usuario accede a una aplicación Blazor WebAssembly, su navegador descarga tanto el runtime de .NET como los ensamblados propios de la aplicación (compilados desde C# en tiempo de desarrollo) y sus dependencias. Ya tras esta descarga inicial, que gracias al caché de los navegadores se realizaría sólo una vez, los ensamblados de la aplicación serían ejecutados por el runtime directamente desde el navegador.

Un detalle importante a tener en cuenta es que el hecho de que el código cliente de nuestras aplicaciones lo desarrollemos usando .NET no significa que estemos llevando al cliente todo el potencial de esta plataforma. Dado que este código se ejecuta en cliente, al igual que cuando programamos con Javascript, estamos limitados por el sandbox proporcionado por el navegador para la ejecución segura de aplicaciones. Es decir, no podremos acceder a dispositivos o recursos locales más allá de los permitidos por las APIs estándar de los navegadores.

Por otra parte, como la ejecución se realiza puramente en cliente, no sería estrictamente necesario disponer de un servidor ASP.NET, IIS ni nada parecido. Tanto el runtime como los ensamblados resultantes de la compilación podrían ser distribuidos en un CDN, y es lo único que se necesitaría para poner en marcha la aplicación (bueno, a no ser que ésta necesite de un backend, claro.)

¿Y qué ventajas tiene esto? Pues bueno, ya el simple hecho de sustituir Javascript por C# para implementar el lado cliente puede aportarnos ventajas interesantes. Aparte de que C# es un lenguaje muy superior, también será posible utilizar o reutilizar bibliotecas .NET existentes, compartir código entre cliente y servidor, o utilizar entornos tan potentes como Visual Studio durante la fase de desarrollo.

Pero como siempre ocurre, no es la panacea; también provocará que la carga inicial de la aplicación sea más lenta (aunque, como decíamos, la caché podrá ayudarnos con esto), o requerirá que los clientes sean compatibles con WASM (aunque esto ya no es un problema en la mayoría de los casos).
Y bueno, de alguna forma nos alejaremos del ecosistema Javascript, que ya sabemos que es enorme, aunque también es cierto que desde Blazor es posible interoperar con bibliotecas JS existentes.
En la actualidad, Blazor WebAssembly sólo está disponible en preview, y está previsto lanzar la primera versión oficial en mayo de 2020 (aunque ésta aún no será LTS).
Como comentaba algo más arriba, el enfoque client-side de Blazor fue el origen del proyecto y sigue siendo el objetivo principal del mismo. Sin embargo, durante la fase de desarrollo aparecieron otros escenarios de uso de Blazor que podrían resultar interesantes y entraron a formar parte del Roadmap del producto, modificando los planes iniciales:

Blazor Roadmap

En la ilustración anterior podemos observar que el Roadmap posicionó Blazor Server como el primer hito del conjunto de tecnologías Blazor. De hecho, es el único framework del que tenemos a día de hoy una versión estable, production ready y con soporte a largo plazo.

Blazor Server parte del mismo objetivo que Blazor WebAssembly respecto a utilizar C# para la implementación de la lógica de presentación, aunque el enfoque que sigue es bastante diferente. En este caso no se trata de llevar todo al cliente sino al contrario: hacer que todo se ejecute en el servidor, comunicándose con el browser mediante una conexión persistente de SignalR:

Blazor WebAssembly vs Blazor Server

Para conseguirlo, el servidor mantiene en memoria una representación del estado de la página, que es modificada por mensajes SignalR que son enviados cuando se producen cambios o interacciones en la interfaz de usuario.

Por ejemplo, si un usuario pulsa un botón en la página, el evento "click" no es procesado en cliente sino enviado a través de la conexión SignalR al servidor, que ejecutará el handler oportuno (escrito en C#). Los cambios realizados en el UI desde dicho handler son enviados de vuelta al cliente, que actualizará el DOM de forma automática.
Pues sí, en cierto modo es muy similar a lo que utilizábamos en Web Forms años atrás, pero eliminando la pesadez del postback, la carga de página completa y el mantenimiento del estado mediante el célebre viewstate. De hecho, Blazor Server es la tecnología propuesta por Microsoft para sustituir a Web Forms, al promover un tipo de desarrollo conceptualmente muy parecido.
Como siempre ocurre, la idea tiene sus ventajas e inconvenientes. Como parte positiva respecto al modelo client-side, en este caso la carga inicial sería más rápida porque no hay nada que descargar desde el cliente. También, dado que el código se ejecutaría en el servidor, podríamos utilizar todo tipo de bibliotecas y componentes para .NET, así como las herramientas habituales de desarrollo y depuración. Y para casos de necesidad, este enfoque conseguiría casi el soporte universal por parte de los browsers, pues no requiere ninguna característica especial en el lado cliente.

Pero ah, amigos, todo esto no es gratis. El hecho de que cada interacción o evento en el UI deba ser enviado y procesado en el servidor añade una latencia que no todas las aplicaciones podrán soportar sin destrozar su usabilidad. Además, las conexiones concurrentes y el mantenimiento del estado en memoria podrían limitar las opciones de escalabilidad de los sistemas.

La buena noticia es que el modelo de componentes, y por tanto la forma de desarrollar, es la misma. De esta forma, podríamos iniciar un proyecto utilizando Blazor Server y migrar a Blazor WebAssembly más adelante con relativamente poco esfuerzo. Bueno, o al menos en teoría ;)

Pero aparte de Blazor Server y Blazor WebAssemby, y como veíamos en el Roadmap algo más arriba, el resto de tecnologías Blazor van acercándose consecutivamente a la ejecución nativa de aplicaciones, tanto desde dispositivos móviles como de escritorio:
  • Blazor PWA, para la construcción de Progressive Web Applications (PWA), webs que proporcionan una experiencia más cercana a las aplicaciones nativas al ser capaces de trabajar offline, recibir notificaciones push, o ser instaladas en los equipos de los usuarios. Sin embargo, la ejecución se realizará siempre sobre el sandbox proporcionado por el browser, por lo que tendremos las mismas limitaciones que si utilizáramos JS.
     
  • Blazor Hybrid, aplicaciones que correrán sobre Electron y renderizarán su UI utilizando tecnologías web sobre WebViews o motores de renderizado de webs. Dado que serán aplicaciones .NET puras, en este caso sí se podrá acceder a todos los recursos del equipo donde se instalen.
     
  • Blazor Native, que permitirá en el futuro utilizar el mismo modelo de programación para crear aplicaciones nativas puras, capaces de renderizarse utilizando componentes nativos de la plataforma de ejecución, fuera del mundo web. En la práctica, quiere decir que podríamos utilizar esta tecnología en lugar de Xamarin o React Native para implementar nuestras aplicaciones nativas.
Aunque muy sujeto a cambios, pero está previsto lanzar previews de PWA e Hybrid a finales de 2020, acompañando a .NET 5. La última referencia que he encontrado de Blazor Native es que está en fase experimental y, por tanto, no existen aún previsiones de disponibilidad.

Solución usando Mobile Blazor BindingsAparte, hace apenas un par de días han anunciado otro giro de tuerca: Mobile Blazor Binding. Este proyecto, aún en fase experimental, permite utilizar el modelo de programación de Blazor y la sintaxis Razor para la construcción de aplicaciones nativas Android e iOS, en la práctica algo así como un híbrido entre Blazor y Xamarin. El objetivo es acercar al mundo de las apps nativas a esa gran masa de desarrolladores web acostumbrados a Razor, y la verdad es que tiene bastante buena pinta.

Esto promete, ¿eh? 😉

¿Cómo se desarrolla con Blazor?

Por si no os lo habéis preguntado, el nombre Blazor procede de la unión de "Browser" y "Razor" (la "L" no sé de dónde sale, supongo que dará más musicalidad al resultado :-P), así que ya podéis intuir por dónde van los tiros: Razor que se ejecuta en el navegador.

De hecho, los componentes y páginas Blazor se implementan utilizando sintaxis Razor, que muchos ya conocéis de otras películas como ASP.NET Core MVC o Razor Pages, aunque con algunos cambios. Por ejemplo, en Blazor las páginas o componentes se implementan en archivos .razor, a diferencia de los clásicos .cshtml de MVC o Razor Pages.

Pero como las cosas se suelen entender mejor con código, mejor que echéis primero un vistazo al siguiente archivo Sum.razor:
@page "/sum"

<h1>Calculator</h1>
<div>
<input type="number" @bind-value="@A" />
+
<input type="number" @bind-value="@B" />
<button @onclick="Calculate">Calculate</button>
</div>
@if (Result != null)
{
<div>
The sum of @A and @B is @Result
</div>
}

@code {
public int? A { get; set; }
public int? B { get; set; }
public int? Result { get; set; }

void Calculate()
{
Result = A + B;
}
}
Calculadora BlazorEchando un vistazo puede entenderse que se trata de una simple calculadora para realizar sumas que incluye un par de tags <input> para solicitar los sumandos y un botón para realizar el cálculo. Al pulsarlo, se muestra el resultado de la suma en un <div> que inicialmente no está visible.

Este bloque de código es suficiente para entender la magia de Blazor:
  • En el bloque @code, implementado por completo en C#, definimos todas las propiedades y métodos que necesitemos para implementar la lógica de nuestra página. En este caso, tenemos un par de propiedades para los sumandos, otra para el resultado, y un método que realiza el cálculo.
    @code {
    public int? A { get; set; }
    public int? B { get; set; }
    public int? Result { get; set; }

    void Calculate()
    {
    Result = A + B;
    }
    }
  • Utilizamos bindings sobre los controles HTML, muy al estilo de otros frameworks basados en MVVM, como Angular o React. En el ejemplo podemos ver que bindeamos el valor de los <input> a las propiedades definidas en el bloque @code, de la misma forma que establecemos el manejador del evento click al método Calculate().
    <input type="number" @bind-value="@A" />
    +
    <input type="number" @bind-value="@B" />
    <button @onclick="Calculate">Calculate</button>
  • Dado que usamos sintaxis Razor, es posible utilizar los habituales bloques de control de flujo como @if, @foreach, etc. En el caso anterior, usamos un condicional para añadir a la página el <div> con el resultado, pero sólo cuando hemos calculado la suma.
    @if (Result != null)
    {
    <div>
    The sum of @A and @B is @Result
    </div>
    }
Fijaos que hemos eliminado de un plumazo a Javascript, y la web funcionará sin cambiar de página, muy al estilo SPA:
  • Si hemos optado por utilizar el host Blazor Server, todos los eventos e interacciones sobre el UI (cambios de valores en controles bindeados, clicks, etc.) serán enviados al servidor a través de la conexión SignalR establecida automáticamente. El servidor ejecutará el código y enviará de vuelta al cliente, usando la misma conexión, las modificaciones a realizar en el DOM para actualizar la interfaz de usuario.

  • En cambio, si hemos optado por utilizar Blazor WebAssembly, la página será compilada y el ensamblado resultante será enviado al cliente, quien lo ejecutará sobre el runtime basado en WebAssembly. Por tanto, la ejecución de la página se realizará totalmente en cliente, como si la hubiéramos implementado totalmente Javascript, sin necesidad de interactuar con el servidor en ningún momento.
Y lo interesante es que en ambos casos nuestro código será el mismo. Es decir, la página Sum.razor será básicamente la misma para cualquiera de los modelos de ejecución de Blazor, por lo que podríamos crear nuestras aplicaciones e ir evolucionando posteriormente de un modelo a otro. Por ejemplo, a día de hoy sólo podríamos poner aplicaciones Blazor en producción usando Blazor Server, pero en unos meses podríamos pasar al modelo WebAssembly cuando esté disponible, realizando pocos cambios.

Pero no sólo eso, la idea también aplica para el resto de "sabores" de Blazor, como PWA, Hybrid o Native: lo único que cambiará es el "empaquetado" y, en algunos casos, el proceso interno de renderizado, pero nuestro código seguirá siendo principalmente el mismo... o al menos, estas son las intenciones iniciales; conforme avancen estos proyectos iremos viendo hasta qué punto es así.

En definitiva, Blazor es un framework del que seguro vamos a oír hablar bastante durante los próximos meses, y no sin razón. Aunque aún está empezando a rodar y quedan bastantes cosas por pulir, es conveniente no perderlo de vista porque realmente las expectativas están muy altas :)

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 387

enero 20, 2020 07:05

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin / Mobile

Mobile Blazor Bindings architecture

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Navegapolis

La empresa consciente

enero 19, 2020 06:07

empresa pinAlgunas perlas del libro La empresa consciente de Fredy Kofman.

 

"Yo utilizo siete cualidades para distinguir a los empleados conscientes de los inconscientes.

Las primeras tres son atributos de la personalidad: responsabilidad incondicional, integridad esencial y humildad ontológica. Las tres siguientes son habilidades interpersonales: comunicación auténtica, negociación constructiva y coordinación impecable. La séptima cualidad es la condición que hace posibles a las seis anteriores: maestría emocional.

Comprender en qué consisten estas cualidades es algo sencillo, pero es difícil ponerlas en práctica. Parecen naturales, pero desafían supuestos profundamente arraigados acerca de nosotros mismos, de otras personas y del mundo. Por este motivo, aun cuando generalmente las conocemos, no sabemos cómo implementarlas. Son cuestión de sentido común, pero no de práctica común.

Los empleados conscientes asumen responsablemente su vida. No comprometen valores humanos para lograr el éxito material. Dicen su verdad y escuchan las verdades de los demás con honestidad y respeto. Buscan soluciones creativas a los desacuerdos y honran de manera impecable sus responsabilidades. Se conectan con sus emociones y las expresan productivamente.

Los empleados inconscientes hacen lo opuesto. Culpan a otros por sus problemas, buscan gratificación inmediata olvidando la ética, y proclaman tener siempre la razón."

 

» Leer más, comentarios, etc...

Blog Bitix

Los permisos del sistema de archivos de GNU/Linux

enero 17, 2020 06:15

El sistema de permisos de GNU/Linux por defecto es menos capaz que el de Windows basado en listas de control o ACLs pero es más sencillo y suficiente para muchos casos y usuarios. Cada archivo o directorio tiene unos bits de control que determinan los permisos de lectura, escritura y ejecución para el propietario, grupo y el resto de usuarios. Los comandos ls, chmod y chown permiten listar los permisos de los archivos y cambiarlos.

GNU

Linux

En un sistema multiprogramado como son cualquiera de los actuales modernos que son utilizados por varios usuarios y programas es necesario que tengan algún mecanismo de seguridad y permisos para proteger los recursos. En el caso del sistema de archivos GNU/Linux usa un sistema simple pero suficiente para una buena parte de casos de uso.

En GNU/Linux los permisos de cada archivo se dividen en lectura, escritura y ejecución, identificados por las letras rwx respectivamente, para el propietario del archivo, el grupo de usuarios al que pertenece y para el resto de usuarios, identificados estos grupos con las letras ugo respectivamente. Para saber si se posee el permiso de lectura, escritura y ejecución se utiliza un bit para cada uno de ellos. Para el conjunto de usuario, grupo y resto de usuarios en total se utilizan 9 bits. Estos 9 bits forman la cadena rwxrwxrwx que para escribirlos de forma numérica y más simple se pueden utilizar base octal, por ejemplo 744 se traduce a binario en 111100100 significando que el usuario tiene permisos para leer, escribir y ejecutar el archivo, dados los tres primeros bits, y el grupo de usuario y resto de usuarios solo tienen el permiso de lectura.

Con el comando para listar el contenido de un directorio ls -lha se muestra el listado en formato largo con la opción -l que incluye los permisos, las unidades de tamaño en formato legible para humanos con la opción -h, con la opción -a incluye los archivos ocultos que son los que empiezan por . y con la opción --group-directories-first los directorios primero.

En los listados del comando ls el primer caracter indica si el archivo es un archivo regular o un directorio que se identifican por la letra d como el directorio Descargas, si fuese una l indicaría que es un enlace como el enlace .steampath. El propietario de los archivos es mi usuario picodotdev, salvo en el caso del directorio superior referenciado con .. que es el superusuario root.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ ls -lha --group-directories-first
total 376K
drwx------ 39 picodotdev picodotdev 4,0K ene 15 22:27 .
drwxr-xr-x  3 root       root       4,0K nov 28  2018 ..
drwx------ 48 picodotdev picodotdev 4,0K ene 13 19:22 .cache
drwx------ 45 picodotdev picodotdev 4,0K ene 13 23:49 .config
drwxr-xr-x  2 picodotdev picodotdev 4,0K ene  6 01:57 Descargas
drwxr-xr-x  3 picodotdev picodotdev 4,0K sep 25 23:00 Documentos
drwxr-xr-x  2 picodotdev picodotdev 4,0K dic 19  2018 Escritorio
drwxr-xr-x  2 picodotdev picodotdev 4,0K mar 18  2019 Grabaciones
drwxr-xr-x  8 picodotdev picodotdev 4,0K abr 18  2019 .gradle
drwxr-xr-x  2 picodotdev picodotdev 4,0K dic 29  2018 Imágenes
drwxr-xr-x  4 picodotdev picodotdev 4,0K nov 29  2018 .java
drwx------  3 picodotdev picodotdev 4,0K nov 28  2018 .local
drwxr-xr-x  4 picodotdev picodotdev 4,0K feb 22  2019 .m2
drwx------  5 picodotdev picodotdev 4,0K nov 28  2018 .mozilla
drwxr-xr-x  2 picodotdev picodotdev 4,0K dic 20  2018 Música
drwxr-xr-x  2 picodotdev picodotdev 4,0K nov 28  2018 Plantillas
drwxr-xr-x  2 picodotdev picodotdev 4,0K nov 28  2018 Público
drwxr-xr-x  5 picodotdev picodotdev 4,0K ene 11 00:58 Software
drwx------  2 picodotdev picodotdev 4,0K may  3  2019 .ssh
drwxr-xr-x  2 picodotdev picodotdev 4,0K sep 24 23:20 .steam
drwxr-xr-x  2 picodotdev picodotdev 4,0K nov 30  2018 Vídeos
drwxr-xr-x  2 picodotdev picodotdev 4,0K feb  1  2019 .vim
-rw-------  1 picodotdev picodotdev 8,9K ene 15 22:27 .bash_history
-rw-r--r--  1 picodotdev picodotdev   21 jun  4  2018 .bash_logout
-rw-r--r--  1 picodotdev picodotdev   57 jun  4  2018 .bash_profile
-rw-r--r--  1 picodotdev picodotdev  541 nov 28 22:14 .bashrc
-rw-r--r--  1 picodotdev picodotdev   73 dic  1  2018 .gitconfig
-rw-r--r--  1 picodotdev picodotdev  16K dic  1  2018 .git-prompt.sh
lrwxrwxrwx  1 picodotdev picodotdev   35 sep 24 23:19 .steampath -> /home/picodotdev/.steam/sdk32/steam
lrwxrwxrwx  1 picodotdev picodotdev   33 sep 24 23:19 .steampid -> /home/picodotdev/.steam/steam.pid
ls.sh

Para cambiar los permisos de un archivo se utiliza el comando chmod pudiendo utilizarse de varias formas. El primer caso establece todos los bits dando permisos de lectura, escritura y ejecución al usuario propietario de archivo, y de lectura para los usuarios del grupo y para el resto de usuarios. El segundo caso da permisos de lectura al usuario en el archivo indicado. El tercer caso quita los permisos de escritura al resto de usuarios. El cuarto caso da permisos de ejecución a todos, al propietario del archivo, a los usuarios de su grupo y al resto de usuarios.

1
2
3
4
$ chmod 644 README.md
$ chmod r+u README.md
$ chmod r-o README.md
$ chmod x-a README.md
chmod.sh

El comando chown permite cambiar el propietario del archivo. En este caso se establece como propietario del archivo el usuario root.

1
2
$ chmod root README.md
$ chmod root:root README.md
chown.sh

En el caso de los directorios tener el permiso de lectura significa poder listar el contenido del directorio (ls), el permiso de escritura poder crear archivos, renombrar y eliminar archivos (por ejemplo con touch, mkdir) y el de ejecución en un directorio otorga acceso al contenido de los archivos (se concede dependiendo de los propios permisos del archivo).

En el permiso de ejecución en vez de una x puede aparecer una s en en los permisos de grupos de usuario y grupo y significa que al ejecutar el archivo en vez de tomar como usuario de ejecución el del usuario o grupo del usuario que lo ejecuta se toma el usuario y grupo del archivo, estos son los setuid y setgid. Si en en el bit de ejecución del grupo otros aparece una t indica que el directorio es sticky y solo el dueño del directorio o el usuario root pueden renombrar o eliminar archivos.

  • r: lectura.
  • w: escritura.
  • x: ejecución.
  • u: usuario.
  • g: grupo.
  • o: otros.
  • a: todos (usuario, grupo y otros).
  • +: añadir permisos.
  • -: quitar permisos.
  • d: directorio.
  • l: enlace simbólico.
  • s: setuid y setgid.
  • t: sticky.

Estos son algunos ejemplos de permisos en directorios.

El usuario archie tiene acceso al directorio Documents. Puede listar, crear archivos, renombrar y eliminar cualquier archivo en Documents independientemente de los permisos de esos archivos. Su posibilidad de acceder al contenido de los archivos dependen de los permisos del archivo.

1
drwx------ 6 archie users  4096 Jul  5 17:37 Documents
permissions-1.sh

En este caso sin el permiso de lectura en el directorio archie tiene acceso completo pero no puede crear, renombrar ni eliminar archivos. Puede listar los archivos y acceder al contenido de los archivos si los permisos de ese archivo lo permiten.

1
dr-x------ 6 archie users  4096 Jul  5 17:37 Documents
permissions-2.sh

archie no puede listar los archivos del directorio pero si conocer el nombre de un archivo existente puede listarlo, renombrarlo, eliminarlo y acceder a su contenido (si los permisos del archivo lo permiten). También puede creer nuevos archivos.

1
d-wx------ 6 archie users  4096 Jul  5 17:37 Documents
permissions-3.sh

archie solo es capaz de acceder a los archivos que conoce (si los permisos de ese archivo lo permiten). No puede listar archivos existentes ni crear, renombrar o eliminar ninguno de ellos.

1
d--x------ 6 archie users  4096 Jul  5 17:37 Documents
permissions-4.sh

Debe tenerse en cuenta que los anteriores son permisos sobre directorios y son independientes de los permisos de los archivos individuales. Cuando se crea un nuevo archivo es el directorio el que cambia, este es el por qué se necesitan permisos en el directorio.

Otro ejemplo, en este caso para un archivo, no un directorio. La primera letra no es una d sino un -, con lo que foobar es un archivo, no un directorio. Los permisos del propietario son rw- de modo que el propietario (archie) tiene la capacidad de leer y escribir pero no de ejecutarlo. El permiso de ejecución no es necesario si los archivos son de texto o datos. Los permisos de grupo son r--, de modo que el grupo tiene la habilidad de leer el archivo pero no de escribir en él de ninguna forma, esencialmente es un archivo de solo lectura para el grupo. Los permisos para el grupo de otros es el mismo.

1
-rw-r--r-- 1 archie users  5120 Jun 27 08:28 foobar
permissions-5.sh

Otro esquema más flexible pero más complejo son las listas de control de acceso o ACL. Las ACL permiten dar permisos a cualquier usuario o grupo para cualquier archivo.

En la wiki de Arch Linux sobre permisos de archivo y atributos se explica detalladamente y con ejemplos el sistema de permisos en GNU/Linux.

» Leer más, comentarios, etc...

Coding Potions

Vue JS ≫ Cómo crear y emitir eventos entre componentes

enero 17, 2020 12:00

Introducción

Anteriormente vimos qué son los props en Vue y para qué sirven. Vimos que los props sirven para pasar información desde el componente padre al componente hijo. Si no recuerdas o quieres aprender el funcionamiento de los props te dejo el enlace al artículo: Cómo crear y usar props en Vue

En el artículo de hoy vamos a ver cómo crear eventos y para qué sirven ya que son esenciales para la creación de componentes web reutilizables.

Qué son los eventos en Vue

Los eventos o events son la contraparte de los props, es decir, sirven para pasar información desde el componete hijo al padre. El ejemplo típico de uso es de cuando queremos notificar de un suceso en el componente hijo.

Por ejemplo podemos crear un componente que simplemente renderice un botón. Si queremos que este botón sea reutilizable en toda la página web lo que necesitamos es notificar al componente que usa el botón si el usuario hace clic en el enlace.

Lo bueno de los eventos es que a parte de servir para notificar, se pueden pasar datos e información desde el hijo al padre en caso de que la necesitemos.

Los props se pasan desde el componente padre al componente hijo y los eventos del hijo al padre

Cómo crear y emitir eventos

Aunque no lo creas, si has usado Vue seguramente ya hayas usado sin saberlo los eventos ya que el @submit y el @click no son más que eventos que ya vienen por defecto en Vue.

Como supondrás, la forma de recibir eventos es poniendo el @ antes del nombre del evento, pero vamos a ver cómo crear tus propios eventos desde el componente hijo.

Volvamos al ejemplo del Botón ya que es un ejemplo claro para usar eventos. La estrategia a seguir es reaccionar con el evento click de Vue para ejecutar una función que sera la encargada de enviar el evento al componente padre.

<template>
  <button @click="handleClick">{{text}}</button>
</template>
<script>
export default {
  props: {
    text: String
  },
  methods: {
    handleClick() {
      this.$emit("click");
    }
  }
}
</script>

Con la función this.$emit() emites el evento. Dentro de la función tienes que poner el nombre que le quieres dar al evento, es decir, si por ejemplo pones this.$emit("hover"); luego para usarlo tienes que llamar al evento @hover. En este ejmplo le he puesto el nombre de click para que podamos estar a la escucha del click en el botón como si se tratase del evento click que viene en Vue por defecto.

Hay otra forma de crear eventos, se trata de crearlos directamente desde la vista, sin tener que poner el this:

<template>
  <button @click="$emit('click')">{{text}}</button>
</template>

De esta forma ya no necesitas crear un método desde el que enviar el evento. Con ambas formas obtienes el mismo resultado.

Cómo estar a la escucha de eventos

Para usar el botón que acabamos de crear:

<my-button text="Botón de ejemplo" @click="handleClick"></my-button>

Recuerda importarlo dentro del componente para poder usarlo. Si hubieras llamado al evento hover por ejemplo, entonces sería:

<my-button text="Botón de ejemplo" @hover="handleClick"></my-button>

Cómo enviar datos en los eventos

Una cosa muy interesante de Vue es que permite enviar información dentro de los eventos. Puedes enviar cualquier cosa: variables, computadas, resultados de métodos, etc.

Para enviar datos tan solo tienes que añadir un parámetro más al evento:

<template>
  <button @click="handleClick">{{text}}</button>
</template>

<script>
export default {
  props: {
    text: String
  },
  data: () => ({
    example: "Hello world"
  }),
  methods: {
    handleClick() {
      this.$emit("click", this.example);
    }
  }
}
</script>

Para recoger en el componente padre la información que viene del hijo lo tienes que hacer en el método que declaras cuando declaras el evento que estás escuchando:

<template>
  <my-button text="Botón de ejemplo" @click="handleClick"></my-button>
</template>

<script>
import MyButton from "@/components/MyButton.vue";

export default {
  components: {
    MyButton
  },
  methods: {
    handleClick(info) {
      console.log("Click event on the button of the children with: " + info)
    }
  }
}
</script>

Si te fijas, en la vista HTML solo necesitas llamar al nombre del método, en otras palabras NO tienes que hacer esto:

<my-button text="Botón de ejemplo" @click="handleClick(info)"></my-button>

Conclusiones

Este artículo es muy corto porque los eventos no dan para más. Es algo muy simple de implementar y encima muy útil para la creación de los componentes si lo combinas con los props.

La única desventaja de los props y los eventos es que si por ejemplo tienes herencias más completas (hijos con más componentes hijos), tienes que pasar los props y los eventos hacia arriba y hacia abajo en la herencia, aunque ya veremos como solucionar esto con el store de Vue.

» Leer más, comentarios, etc...

Picando Código

Nuevo diseño en Montevideo Bicis 🚲

enero 15, 2020 11:15

Lo que empezó con una corrección mínima de un enlace en Montevideo Bicis me llevó a hacer algunos cambios bastante grandes:

  • Migré de Bootstrap a Bulma.io, mi nuevo framework CSS preferido
    Lo que también elimina la dependencia del JavaScript completo de Bootsrap. Cambió bastante el diseño, dándole una apariencia un poco más moderna (en mi muy humilde opinión). También me restula más fácil de mantener. Como vengo usando Bulma en varios proyectos, lo tengo más fresco en la memoria para cambiar cosas.
  • Eliminé la dependencia jQuery 🙌
    En su momento fu un sitio hecho lo más rápido posible para publicarse enseguida. Y la dependencia con jQuery vino con la facilidad de desarrollo. Pero en verdad hay tan poco JavaScript en el sitio que no valía la pena incluirlo. Y hoy en día no es muy necesario, menos para sitios tan pequeños como éste.
  • Quité la imagen de fondo
    No quedaba demasiado bien de todas formas, y es un request menos en el navegador.

Todavía puede quedar alguna cosa por corregir, pero en principio creo que se ve un poco mejor, y sin duda quedó más liviano al no cargar tanto JavaScript y CSS innecesarios.

Cómo se veía antes:

Montevideo Bicis Antes

Cómo se ve ahora: MontevideoBicis.com
Si llegan a encontrar algún error o detalle, pueden dar de alta un issue en GitHub o contactarme por acá o por Twitter.

» Leer más, comentarios, etc...

Variable not found

Incluir recursos estáticos en una Biblioteca de Clases Razor (RCL)

enero 14, 2020 11:43

ASP.NET CoreLas Razor Class Libraries (RCL) constituyen una interesante fórmula para crear componentes redistribuibles de interfaz de usuario para aplicaciones basadas en ASP.NET Core MVC o Razor Pages. En las bibliotecas de clases de este tipo podemos incluir controladores, view components, tag helpers o vistas y páginas Razor, elementos que estarán disponibles en las aplicaciones que las referencien, bien directamente o bien a través del paquete NuGet en el que las distribuyamos.

Sin embargo, es menos conocido el hecho de que estas bibliotecas pueden incluir también recursos estáticos como imágenes, hojas de estilo o scripts, lo que resulta bastante interesante a la hora de crear componentes totalmente autosuficientes y muy reutilizables.

En este post vamos a ver cómo crear una RCL redistribuible que definirá el tag helper <mario>, cuya inclusión en una página hará que ésta muestre el conocido personaje correteando por la pantalla, como se muestra en la siguiente captura:

Mario corriendo por la pantalla

1. Lo básico: creación y consumo de la Razor Class Library

Como sabemos, podemos crear RCLs utilizando Visual Studio o bien desde la línea de comandos:
dotnet new razorclasslib --support-pages-and-views
Importante: en ambos casos es fundamental añadir el soporte para páginas y vistas a la hora de crear el proyecto.
También podemos crear una biblioteca de clases tradicional, y modificar su archivo .csproj para dejarlo como el mostrado a continuación:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>
Una vez creado el proyecto, ya podemos introducir en él los componentes que queramos distribuir en la biblioteca. En nuestro caso, iremos preparando el esqueleto del tag helper <mario>, que podría ser algo así:
public class MarioTagHelper: TagHelper
{
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.SuppressOutput();
output.Content.SetHtmlContent("<p align=center>Mario will be here!</p>");
return base.ProcessAsync(context, output);
}
}
Para utilizar este componente desde una aplicación ASP.NET Core MVC, sólo tenemos que referenciar el proyecto RCL (o el paquete NuGet, si hubiéramos decidido distribuir así nuestra biblioteca) y, dado que se trata de un tag helper, registrarlo en el archivo _ViewImports.cshtml. Suponiendo que la biblioteca Razor se denomina MarioRcl, sería algo como:
...
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, MarioRcl
De esta forma, ya podríamos utilizar en nuestras páginas la etiqueta que hemos definido, por ejemplo en la vista Index.cshtml creada por defecto en proyectos MVC:
...
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web
apps with ASP.NET Core</a>.</p>
</div>
<mario></mario>
El resultado en ejecución sería el siguiente:

Tag Helper mario -solo texto- en ejecución

2. Incluir recursos estáticos

Por simplificar, en el código anterior solo hemos mostrado un texto en el lugar donde se introdujo la etiqueta <mario>, aunque en realidad lo que nos interesará es insertar un código scripts que vaya superponiendo distintas imágenes para crear la ilusión de animación.


Mario corriendo, frame 1    Mario corriendo, frame 2    Mario corriendo, frame 3

Dado que vamos a utilizar estas tres imágenes, debemos añadirlas a la carpeta wwwroot del proyecto RCL, que sabemos que es donde se almacenan por convención los recursos estáticos; un buen lugar para hacerlo, sin duda, sería en una subcarpeta llamada images en su interior.

Carpeta wwwroot del proyecto RCLPor defecto, todos los recursos presentes en la carpeta wwwroot de la RCL serán distribuidos junto con la biblioteca de clases Razor. De hecho, si probáis a publicar el proyecto ASP.NET Core MVC que referencia a la RCL, podréis observar que los recursos estáticos se encuentran en la carpeta de salida, en un directorio llamado por convención wwwroot\_content\[Ensamblado RCL].

Por ejemplo, si nuestro proyecto RCL se denomina MarioRcl, los recursos estáticos introducidos en la carpeta wwwroot de la Razor Class Library se introducirán automáticamente en la carpeta de salida wwwroot\_content\MarioRcl del proyecto web que lo utilice, como se muestra en la siguiente captura de pantalla del explorador de archivos de Windows:

Recursos estáticos de la RCL
Si hay recursos estáticos que no queremos incluir al empaquetar la RCL, podemos excluirlos explícitamente utilizando el elemento <DefaultItemExcludes> del archivo .csproj.
Y con esto, podríamos decir que la magia está hecha. El único requisito adicional a tener en cuenta es que el proyecto web deberá incluir el middleware Static Files en el pipeline, pues es la forma de asegurar el proceso de peticiones a archivos estáticos.

En este momento, si modificamos el tag helper de la siguiente forma, ya deberíamos poder ver a Mario, aún quietecito, en las páginas que utilicen el tag <mario>.
public class MarioTagHelper : TagHelper
{
private static readonly string Root =
$"/_content/{typeof(MarioTagHelper).Assembly.GetName().Name}";

public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.SuppressOutput();
output.Content.SetHtmlContent(GetHtml());
return base.ProcessAsync(context, output);
}

private string GetHtml() =>
$"<p align=center><img src='{Root}/images/1.png'>";
}
Tag Helper Mario en ejecución mostrando una imagen

Sólo un detalle adicional: esto os funcionará sin problema cuando publiquéis el proyecto o si lo ejecutáis desde la carpeta de salida o el IDE en el entorno Development, pero no si modificáis el entorno a Production. En este caso tendréis que añadir una configuración extra al Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStaticWebAssets(); <-- ¡Añadir esta línea!
webBuilder.UseStartup<Startup>();
});

Punto extra: démosle vida al muñeco

Ya sabemos todo lo necesario para incluir recursos estáticos en una RCL y distribuirlos con ella, así que podríamos considerar que este último punto sobra en el post. Pero como habíamos establecido al principio, el objetivo era ver corretear a Mario por pantalla, así que démosle un último empujón.

Para conseguirlo, sólo tendremos que introducir algo de Javascript en el tag helper para que mueva el sprite y vaya alternando sus frames al mismo tiempo. Una posible implementación podría ser la siguiente:
public class MarioTagHelper : TagHelper
{
private static readonly string Root =
$"/_content/{typeof(MarioTagHelper).Assembly.GetName().Name}";

public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.SuppressOutput();
var id = context.UniqueId; // ID único del tag <mario> en proceso,
// generado por Razor

output.Content.SetHtmlContent(GetHtml(id));
return base.ProcessAsync(context, output);
}

private string GetHtml(string id) =>
$@"<div style='width=100%;' id='parent-{id}'>
<img id='mario-{id}' style='position: absolute; left: 0;'>
</div>
<script>
(function () {{
let frame = 0;
let parent = document.getElementById('parent-{id}');
let element = document.getElementById('mario-{id}');
let left = parseInt(element.style.left);
setInterval(function() {{
element.src = '{Root}/images/' + frame + '.png';
element.style.left = left+'px';
left=(left+10)%(parent.offsetWidth-element.offsetWidth);
frame = (frame+1) % 3;
}}, 80);
}})();
</script>";

}

¡Y eso es todo! Con esto ya tendremos a Mario correteando por la pantalla. Pero lo importante no es eso, sino que por el camino hemos aprendido a crear una RCL reutilizable con contenidos estáticos :)

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 386

enero 14, 2020 11:42

Enlaces interesantes Ahí van un buen puñado de enlaces recopilados durante las semanas anteriores. Como siempre, espero que os resulten interesantes. :-)

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Picando Código

[Libro] The Psycopath Test por Jon Ronson

enero 14, 2020 09:00

Jon Ronson - The Psychopath Test

Jon Ronson – The Psychopath Test

Éste es un libro que tenía en la mira desde que leí Them: Adventures with Extremists por Jon Ronson el año pasado. El autor tiene una forma de escribir bastante entretenida, investigación periodística mezclada con ficción y humor. Además quería ver si leyendo este libro confirmaba mi teoría de que la mayoría de CEO’s y políticos del mundo son psicópatas…

Them era más “documental” y cómico, éste es más un libro de psicología donde explora ese mundo mediante la narrativa, pasando por el psicoanálisis, la industria farmacéutica y la psicopatía. Pero no deja de tener un componente periodístico. La descripción del libro hace un buen trabajo en resumirlo así que para qué reinventar la rueda:

¿Y si la sociedad no fuera fundamentalmente racional, pero estuviera motivada por la locura? Este pensamiento lleva a Jon Ronson a una aventura completamente convincente al mundo de la locura.

En el camino, Jon conoce psicópatas, aquellos cuyas vidas han sido tocadas por la locura y aquellos cuyo trabajo es diagnosticarla, incluyendo al influencial psicólogo que desarrolló el Test de la psicopatía, de quien Jon aprende el arte de la observación de psicópatas. Una habilidad que aparentemente revela que la locura podría de hecho estar en el corazón de todo…

Combinando el humor marca registrada de Jon, encanto incisión de investigación, The Psycopath Test es entretenido y honesto, desenterrando verdades peligrosas y haciendo preguntas serias sobre cómo definimos la normalidad en un mundo donde somos cada vez más juzgados por nuestros bordes más locos.

Es bastante entretenido pero obviamente no ahonda tanto en el tema desde un punto de vista científico, sino más bien opiniones y experiencias, manteniendo su estilo para entretener al lector y no ser un libro para estudiantes de psicología. Me resultó muy fácil de leer, y lo terminé en dos sesiones de lectura.

Es de esos libros que siento -al igual que me pasó con Them- me dan algunas herramientas más para entender un poco más el mundo en el que vivo. El test del que habla no es la verdad absoluta, pero es lo que se usa para diagnosticar el desorden. Entonces al aprenderlo uno se siente un poco como el autor en que puede empezar a detectar potenciales psicópatas donde antes no los veía. Y también está interesante el ejercicio de pensar en cuánto de lo que pasa en el mundo es por culpa de (o gracias a) algún psicópata.

La sociedad actual está mayormente controlada por CEO’s y políticos. ¿Todos los CEO’s son psicópatas? No, pero una buena cantidad seguro que sí. ¿Los políticos también? ¡Seguro! Por lo menos cuentan con los 3 puntos de “grandioso sentido de autoestima”, “ser mentirosos patológicos”, “ser manipuladores”, “falta de empatía”, “estilo de vida parasitario” y lo dejo ahí porque ya son 15 puntos y falta nada para calificar como psicópatas… Esto explicaría mucho, pero como podrán concluir si leen el libro, no es tan blanco y negro.

Me resultó un buen catalizador para despertar el interés en el tema y buscar más información. Particularmente me interesó y agregué a mi lista de libros por conseguir Snakes in Suits, que ahonda más en el tema pero está escrito por verdaderos psicólogos. De todas formas recomiendo The Psycopath Test por Jon Ronson, al igual que Them. Son muy entretenidos y te hacen pensar y evaluar distintas ideas, los mejores tipos de libros.

Llevo una buena racha de lectura en los últimos meses. Las vacaciones de fin de año me permitieron leer 3 libros, y desde entonces vengo tratando de mantener la costumbre. Así que con suerte en breve termino lo que estoy leyendo ahora y publico otra reseña, así mantengo en ejercicio la escritura.

» Leer más, comentarios, etc...

Fixed Buffer

Azure App Configuration: Configuración centralizada de aplicaciones

enero 14, 2020 09:00

Tiempo de lectura: 8 minutos
La imagen muestra el icono de Azure App Configuration

Vuelta al trabajo y vuelta al blog tras un merecido descanso navideño. Después de unas semanas de parón hoy vengo a hablaros de un servicio muy interesante para centralizar la configuración de aplicaciones. Este servicio es Azure App Configuration.

Es hora ya de dejar .Net Core 3, sus interfaces, expresiones switch y demás novedades y volver a meternos de lleno con diferentes herramientas que utilizar en nuestro día a día.

Situación

La idea de escribir sobre este servicio viene de que no hace mucho en el trabajo estuvimos comentando cual podría ser una buena manera de mantener de manera centralizada las diferentes configuraciones que tienes nuestros contenedores en kubernetes, ya que el hecho de utilizar variables de entorno obliga a reiniciar el contenedor para actualizar las variables.

Existen múltiples opciones para resolver ese problema utilizando volúmenes de persistencia para almacenar esas configuraciones, pero eso requiere de tener un volumen, lo que no siempre es posible o aceptable…

Hace cosa de un año Microsoft puso en preview el servicio Azure App Configuration que promete resolver todos los problemas de centralizar la configuración de un plumazo. La parte mala es que después de un año, sigue en preview… Por esta razón no lo estamos implementando aún. Pero no quita que sea un servicio muy interesante y por eso vamos a verlo en profundidad en esta entrada.

¿Qué es Azure App Configuration?

En primer lugar, conviene tener claro de que estamos hablando. Azure App Configuration es un servicio que nos permite tener de manera unificada y centralizada las diferentes configuraciones que tienen una o varias aplicaciones. Este servicio funciona no solo con C# y .Net, sino también con Java, Python y JavaScript.

Con esto vamos a poder resolver de manera muy sencilla situaciones donde, por ejemplo, estamos haciendo una escalabilidad horizontal. La escalabilidad horizontal consiste en tener varias instancias de nuestro servicio corriendo a la vez de modo que aumentemos la capacidad de respuesta que tiene. Un ejemplo de esto puede ser una web corriendo en varias máquinas a la vez de modo que se pueda responder a muchas más peticiones que si solo estuviese en una.

Este es un escenario tipo donde compartir la configuración es vital, ya que si tenemos el mismo servicio en paralelo, pero hay que configurarlo en cada instancia que está corriendo, podemos tener problemas porque una instancia no esté configurada igual que las demás.

Aquí es donde entra en juego Azure App Configuration. Gracias a este servicio vamos a tener concentradas en un almacén las configuraciones de modo que, al cambiarlas en ese punto, todas las instancias se enteren y actualicen sus datos. Un punto interesante es que mientras siga en preview es gratuito, por lo que te recomiendo que lo pruebes ya que es gratis y puede ser una solución a algún problema.

Crear el almacén

Para poder utilizar un almacén de Azure App Configuration vamos a tener que crear el servicio en el portal de Azure. Para esto basta con que pulsemos crear un recurso y escribamos ‘App Configuration’:

La imagen muestra el portal de Azure creando un Azure App Configuration

Tras esto le damos a crear y solo es necesario rellenar 4 campos para crear el almacén:

La imagen muestra los 4 datos que solicita para crear el almacen. Nombre del recurso, Suscripción, Grupo de recursos y localización

Con esto se inicia el proceso de creación del almacén que estará disponible en unos segundos. Una vez que esté disponible, ya podemos empezar a usarlo como almacén de configuraciones.

Lo primero será registrar las diferentes configuraciones que van a estar disponibles para las aplicaciones. Para eso dentro del servicio vamos a ir a ‘Explorador de configuración’ y después a ‘Crear’:

La imagen muestra el panel principal de Azure App Services indicados los botones `Explorador de configuración` y `Crear`

Seleccionamos clave valor (algunas traducciones de Azure son…) y ya podemos registrar la clave:

La imagen muestra la ventana de creación de claves

De este modo tan sencillo vamos a poder ir creando diferentes claves en Azure App Configuration con sus valores que van a poder ser consumidos por las diferentes aplicaciones. Para esta entrada yo he creado dos claves que luego se consumirán desde una aplicación ASP NET Core:

La imagen muestra dos claves: asp:welcomesettings:author=FixedBuffer y asp:welcomesettings:message=Hola a todos!

En caso de que tengas ficheros de configuración ya creados, es posible importarlos desde varios formatos gracias a la opción Importar/Exportar. De igual modo es posible exportarlos a varios formatos.

Obteniendo acceso de aplicaciones para Azure App Configuration

Ahora que ya está creado el almacén y tiene algunas claves creadas, es necesario tener unos datos de acceso de modo que las aplicaciones se puedan conectar con el este para leer la configuración.

Esto lo vamos a conseguir desde el menú ‘Claves de Acceso’ donde vamos a poder elegir si queremos de lectura y escritura o solo de lectura:

La imagen señala los botones 'Claves de acceso', 'Claves de lectura' y 'Copiar cadena de conexión'

Elijamos de lectura y escritura o solo lectura, lo que nos interesa es la cadena de conexión ya que es lo que vamos a necesitar en las aplicaciones para poder acceder al almacén.

Utilizando Azure App Configuration en una aplicación .Net Core

Para poder utilizar el almacén de Azure App Configuration dentro de una aplicación .Net, es preciso añadir el paquete NuGet ‘Microsoft.Extensions.Configuration.AzureAppConfiguration‘. Además, para poder buscarlo dentro de Visual Studio es necesario tener activado el check de mostrar paquetes en preview.

Una vez que esta el paquete instalado simplemente es necesario obtener el ‘IConfiguration’ de donde vamos a recuperar las configuraciones:

private static IConfiguration _configuration = null;
//...
var builder = new ConfigurationBuilder();
builder.AddAzureAppConfiguration(options =>
{
    options.Connect(connectionString)
           .ConfigureRefresh(refresh => //registramos el refresco
           {
               refresh.Register("asp:welcomesettings:author")
                      .Register("asp:welcomesettings:message")
                      .SetCacheExpiration(TimeSpan.FromSeconds(10));
           });
});
_configuration = builder.Build();
//Escribimos un mensaje accediendo a la configuración
Console.WriteLine(_configuration["asp:welcomesettings:message"]);

Con el código anterior estamos recuperando las configuraciones que haya registradas en Azure App Configuration y además le estamos indicando que queremos que ‘asp:welcomesetting:author’ y ‘asp:welcomesetting:message’ se actualicen. También estamos invalidando el tiempo de actualización por defecto que es 30 segundos para hacer que se actualice cada 10.

Con esto, simplemente sería necesario añadir el IConfiguration al inyector de dependencias si lo hubiese o mantenerlo con un acceso global para poder acceder a la configuración siempre que nos haga falta. Además, el único dato de configuración que necesita la propia aplicación es la cadena de conexión al almacén.

Utilizando Azure App Configuration en una aplicación web ASP NET Core

Aunque con lo que acabamos de ver para aplicaciones puede ser suficiente, hay otro paquete que nos permite directamente utilizar este almacén como uno de los propios de la web de modo que automáticamente podamos inyectar las configuraciones como una dependencia. Este paquete es ‘Microsoft.Azure.AppConfiguration.AspNetCore‘.

Para el ejemplo se está utilizando una aplicación ASP NET Core 3.1 MVC

Una vez instalado vamos a añadir la configuración directamente sobre el HostBuilder (en Program.cs):

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
      .ConfigureAppConfiguration((hostingContext, config) =>
      {
          config.AddAzureAppConfiguration(options =>
          {
              options.Connect(connectionString)
                   .ConfigureRefresh(refresh =>
                   {
                       refresh.Register("asp:welcomesettings:message")
                            .Register("asp:welcomesettings:author")
                            .SetCacheExpiration(TimeSpan.FromSeconds(10));
                   });
           });
       })
       .ConfigureWebHostDefaults(webBuilder =>
       {
           webBuilder.UseStartup<Startup>();
       });

Como puedes comprobar, hasta aquí es exactamente igual que hacíamos en una aplicación. La diferencia radica en que ahora vamos a crear una clase para esos settings:

public class WelcomeSettings
{
    public string Message { get; set; }
    public string Author { get; set; }
}

Y en ‘ConfigureServices’ de la clase ‘Startup’ vamos a añadir la línea para convertir la configuración en la clase:

services.Configure<WelcomeSettings>(Configuration.GetSection("asp:welcomesettings"));

Por último, dentro del método ‘Configure’ de esta misma clase vamos a añadir el middleware de Azure App Copnfiguration:

app.UseAzureAppConfiguration();

Ahora todo está listo y configurado para poder utilizar Azure App Configuration como un almacén de configuración dentro de nuestra aplicación web. Vamos a probarlo inyectando la configuración como un snapshot a HomeController y pasándoselo a una vista donde lo visualicemos:

public class HomeController : Controller
{
    private readonly WelcomeSettings _welcomeOptions;

    public HomeController( IOptionsSnapshot<WelcomeSettings> welcomeOptions)
    {
        _welcomeOptions = welcomeOptions.Value;
    }

    public IActionResult Index()
    {
        return View(_welcomeOptions);
    }
}
@model WelcomeSettings

<div class="text-center">
    <h1 class="display-4">@Model.Message</h1>
    <h2 class="display-4">By: @Model.Author</h2>
</div>'

Una vez que esta esto listo, solo nos queda probarlo. Si arrancamos la aplicación nos encontramos con algo como esto:

La imagen muestra la web con los mensajes obtenidos desde Azure App Configuration

Sin detener la aplicación vamos a editar el valor de la configuración ‘asp:welcomesettings:author’ desde el servicio haciendo click en los 3 puntos y pulsando editar:

La imagen muestra con flechas como se llega a los 3 puntos de la derecha y al botón editar

Una vez que hemos editado su valor, simplemente vamos a recargar la página de nuestra web y vamos a comprobar cómo ha cambiado el valor.

Recuerda que le hemos dado 10 segundos de tiempo de vida a la cache de la configuración, por lo que puede tardar hasta 10 segundos en actualizarse el valor que vemos.

La imagen muestra la misma web ASP NET Core que hemos creado con los valores del Azure App Configuration actualizados

Conclusión

Aunque ahora mismo el servicio de Azure App Configuration está en preview y lleva así desde hace mucho tiempo, es un servicio que funciona bien. Personalmente me gusta y lo utilizo en mis proyectos personales ya que me permite centralizar la configuración de todos mis proyectos en un único sitio de manera muy cómoda.

De igual manera me permite que cuando los contenedores que tengo desplegados sobre un AKS escalan (AKS es el servicio de Kubernetes de Azure, del que hablaremos en algún momento 🙂 ), automáticamente adopten la configuración y apliquen los cambios. Sí, sé que esto lo puedo conseguir perfectamente con volúmenes donde se persistan las configuraciones, pero gracias Azure App Configuration tengo las que pertenecen a contenedores y las que no en el mismo sitio.

No es recomendable utilizar elementos en preview si vamos a llevar algo a producción y no seré yo el que recomiende hacerlo, si alguien quiere hacerlo es bajo su cuenta y riesgo. No obstante, durante la fase de desarrollo donde las configuraciones cambian frecuentemente, sobre todo en entornos de desarrollo, es una opción a tener muy encuenta.

¿Qué opinas tú? ¿Conocías Azure App Configuration como almacén de configuraciones?

Para que puedas probar tú mismo Azure App Configuration en ambos escenarios, he subido el código a GitHub en dos ramas, application para la aplicación de consola y web para la aplicación MVC. En ambos casos tendrás que añadir las configuraciones en el servicio y reemplazar la cadena de conexión.

**La entrada Azure App Configuration: Configuración centralizada de aplicaciones se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Una sinfonía en C#

¿Qué es un Webhook? o ¿Cómo lograr ser informado de eventos sin hacer pooling?

enero 14, 2020 12:01

En ocasiones necesitamos enterarnos de cambios en otros sistema, por ejemplo, tenemos un repositorio Git en Github y queremos saber cuándo se ha hecho un cambio para hacer algo en consecuencia (como iniciar un proceso de compilación).

Haciendo pooling

La primera forma de hacerlo sería de algún modo verificar si hay cambios ( cada 1 minuto digamos), es decir, enviar una petición cada 1 minuto.

image

Esta técnica se suela llamar pooling y de hecho se utiliza en casos que aplica, pero tiene ciertos problemas:

  • App1 (y todas las aplicaciones que desean saber si hay cambios) tiene que implementar el pooling, un timer, el request, etc.)
  • Cada App lo hará a su manera, y el intervalo de verificación puede ser diferente.
  • Cada App debería implementar un modo de saber que efectivamente hay cambios (imaginemos que en lugar de Git es una API que nos dice si hay un producto disponible por ejemplo) y esto podría tener un costo alto.
  • Todas las App cliente hacen pooling al mismo server y podría generar un costo a éste e incluso “tirarlo” si se hacen muchos request por segundo.

Webhooks al rescate

Hay dos formas prinicipales para solucionar problemas en software: la primera es agregra una capa más y la segunda es invertir el flujo y éste es el caso de los Webhooks.

Un Webhook es un endpoint que ofrece el cliente (por ejemplo App1) es decir una URL que acepta peticiones, y que es llamada por la API que queremos consumir, en este caso Github

image

 

Entonces en un primer momento App1 le dice a GitHub cuál es la URL de su Webhook (y eventualmente de qué eventos quiere ser informado) y luego se queda esperando.

Github (o la API que fuera) guarda la URL del Webhook de App1 (y a la todos los clientes que quieran ser notificados) y cuando detecta cambios le informa haciendo un POST a cada URL de los clientes, adicionalmente envía información sobre el evento ocurrido.

A partir de esto App1 hace un pull de los cambios (o lo que sea).

Es decir, en lugar de nosotros como cliente que consume una API preguntar cada x tiempo si hay cambios, le decimos a la API que nos avise cuando hay cambios a través de una URL dada, cuando esto ocurre hacemos algo en consecuencia.

Probando  un Webhook con Github.

Una cosa que no está definida en un Webhook son los datos que la API (Github en este caso) nos va a enviar en el payload del POST a nuestro Webhook (la información sobre los cambios, el evento, etc.) esto es libre, cada API/Aplicación/etc. incluye la información que cree necesaria, cosa que parece lógica para dar flexibilidad y porque cada aplicación puede querer informar cosas diferentes.

Entonces, ¿cómo sabemos qué datos nos llegan a nuestro Webhook?

Evidentemante nuestro Webhook ( el endpoint que ofrecemos para ser informados de eventos en Github en este ejemplo) tiene que ser accesible desde internet (para que Github puede invocarlo, claro) y estoy puede ser un problema a la hora de desarrollarlo.

Hay herramientas que voy a recomendar para verificar esto, es un sitio web que nos permite generar una URL aleatoria (nuestro Webhook temporal) y ver todas los requests que recibe y su respectivo payload. Entonces vamos a https://webhook.site

Webhook Site

Este sitio apenas ingresamos nos genera una URL aleatoria que será nuestro Webhook, es decir, la URL por la cual, mediante un POST las API (o apps que querramos que nos informen) nos informarán.

image

Para probar cómo funciona vamos a utilizar Postman y hace un simple request.

 

image

 

Simplemente un POST a la URL que generó webhook.site y ponemos algo en el body, apretamos Send

 

image

 

Y magia! vemos nuestro request, bien, funciona,ahora vamos a hacerlo con Github, crear un Webhook sobre un repositorio.

Configurar GitHub

Para esto vamos a un repositorio, a la pestaña de setting y ahí a Webhooks (se pueden configurar a nivel usuario también)

image

image

En este caso se seleccionó la opción “send me everything” para recibir todos los eventos (es una prueba, claro)

Lo primero que va a ocurrir es que Github envían un ping a nuestro Webhook para verificar que funciona, y podemos verlo en webhook.site

 

image

Esto es buena señal (quiere decir que la configuración es correcta), entonces ahora hacemos un cambio en cualquier archivo en nuestro repositorio y recibimos el evento con la información

image

Y recibimos un nuevo POST con la información que envía Github, como dije, esta información varía con cada evento y en cada sitio, de modo que tenemos que leer la documentación o probar qué nos envía para poder leer esto dentro de nuestro Webhook.

Conclusiones:

Los Webhook son una forma excelente de recibir notificaciones de sitios web (o APIs, Repositrios, etc.) que queremos monitorizar para realizar tareas en consecuencia, además hoy por hoy casi todo el mundo los soporta.

Falta ver el soporte de autenticación y algunos detalles más pero la idea básica es ésta, espero sera de utilidad, nos leemos.

» Leer más, comentarios, etc...

Blog Bitix

Tomar capturas de pantalla de páginas web desde la línea de comandos o desde la interfaz gráfica con Firefox

enero 11, 2020 07:30

El navegador web Firefox tiene una opción con la que poder tomar una captura de pantalla de una página web desde la línea de comandos, que es útil como parte de un proceso automatizado. Firefox también permite tomar capturas de pantalla de una página web desde su interfaz gráfica o desde la consola web.

Firefox

Como norma general las pruebas de una aplicación deben estar automatizadas con una herramienta de testing. Para el código Java una herramienta de pruebas es JUnit y para una aplicación web es Geb. Sin embargo, algunas pruebas que no son de funcionalidad de la aplicación sino en una aplicación web por ejemplo de estilos y de visualización en la pantalla, que la página se muestre correctamente. Para estos casos no queda más que revisar, visualizar y comprobar la corrección visual de la misma, una posibilidad es entrar en la página manualmente mediante su dirección y realizar la comprobación.

Sin embargo, las acciones manuales hay que evitarlas en la medida de lo posible y que sean las menos posibles realizando su automatización ya que consumen muhco tiempo dedicable a tareas de valor. En este caso realizando la automatización se evita abrir el navegador, introducir la URL de la página o navegar hasta ella por cada URL a probar.

Aunque la revisión visual no esté automatizada y dependa de intervención humana al menos evitando las acciones manuales de introducir la URL o navegar hasta ella supone un ahorro de tiempo importante. La correcta visualización de una página puede consistir en revisar una captura de pantalla de la página y la automatización consiste en obtener esa captura de pantalla.

Esta automatización es interesante también ya que guardando los archivos de captura permiten tener un registro del estado de la visualización de una página a lo largo del tiempo y ver los cambios que se han ido realizando.

Captura de pantalla desde la linea de comandos con Firefox

Para obtener la captura de pantalla el navegador Firefox permite desde la línea de comandos cargar una página a partir de su URL y obtener su captura de pantalla guardándola en un archivo jpg o png. Las capturas de pantalla también se pueden tomar en un servidor GNU/Linux sin interfaz gráfica con un comando habiendo instalado Firefox previamente para este propósito.

1
$ firefox -P headless -headless --screenshot duckduckgo.png https://duckduckgo.com/
firefox-screenshot.sh

El parámetro -P indica el perfil a usar y hay que crear uno para tomar capturas de pantalla al mismo tiempo que está abierto el perfil personal en una ventana gráfica del navegador. Hay varias formas de crear un nuevo perfil en Firefox. Desde la página de configuración about:profiles, con el gestor de perfiles o desde la línea de comandos.

Perfiles de usuario en Firefox Gestor de perfiles de Firefox

Perfiles de usuario y gestor de perfiles de Firefox
1
$ firefox -p
firefox-gestor-perfiles.sh
1
$ firefox -CreateProfile headless
firefox-createprofile.sh

Si hay necesidad de tomar varias capturas de pantalla de diferentes URLs de la aplicación o diferentes páginas web, con un script del intérprete de comandos Bash y dada una lista de URLs una por línea en un archivo se realiza el bucle. El parámetro screenshot contiene el nombre de la imagen en el que guardar la captura y a continuación la URL de la que tomar la captura, el parámetro --window-size especifica el ancho del navegador al tomar la captura, cambiar el ancho permite observar como se visualiza la página en diferentes resoluciones de ancho (1366, 1600, 1920, 2560, …).

1
2
3
4
5
google,https://www.google.es
duckduckgo,https://duckduckgo.com
ubuntu,https://www.ubuntu.com
redhat,https://www.redhat.com
archlinux,https://www.archlinux.org
links.txt
1
2
3
4
5
6
7
#!/usr/bin/env bash
cat links.txt | while read -r LINE
do
  NAME=`echo $LINE | cut -d \, -f 1`
  URL=`echo $LINE | cut -d \, -f 2`
  firefox -P headless -headless --screenshot "$NAME.png" $URL --window-size=1920
done
firefox-screenshot-links.sh

El resultado son las siguientes capturas de pantalla.

Google DuckDuckGo Ubuntu

RedHat Arch Linux

Capturas de pantalla realizadas con Firefox en modo headless

Con un gran número de páginas validar visualmente cada una de las páginas mediante una imagen aun con la automatización de tomar la captura consume una buena cantidad de tiempo, para reducir el número de capturas a validar el archivo puede limitarse a unas representativas de la aplicación o unas aleatorias de todo el conjunto, con los comandos sort -R y head -3 se toman las 3 primeras líneas aleatorias del archivo, el comando sort las ordena de forma aleatoria y el comando head toma el número de líneas indicado.

1
2
3
4
5
6
7
#!/usr/bin/env bash
cat links.txt | sort -R | head -3 | while read -r LINE
do
  NAME=`echo $LINE | cut -d \, -f 1`
  URL=`echo $LINE | cut -d \, -f 2`
  firefox -P headless -headless --screenshot "$NAME.png" $URL --window-size=1920
done
firefox-screenshot-links-random-firts.sh

Captura de pantalla desde Firefox

Firefox también permite tomar capturas de pantalla desde la interfaz gráfica, con el botón derecho y la opción del menú emergente Hacer una captura de pantalla de la pantalla completa o de la parte visible en ese momento o también desde el Inspector que se muestra con el botón derecho y la opción Inspeccionar elemento y habilitando la opción Hacer una captura de pantalla de la página completa y si se desea Captura de pantalla al portapapeles con las opciones de los ajustes.

Ajustes para mostrar botón de captura en las herramientas del inspector

Ajustes para mostrar botón de captura en las herramientas del inspector

La consola web de Firefox también permite tomar capturas de pantalla escribiendo el comando :screenshot.

» Leer más, comentarios, etc...

Picando Código

[Libro] Radicalized por Cory Doctorow

enero 10, 2020 12:00

Cory Doctorow - RadicalizedEl primer libro que terminé de leer en 2020 fue Radicalized por uno de mis autores preferidos: Cory Doctorow. Este libro reafirma su lugar entre mis favoritos, ¡lo amé!. Es un poco diferente a las novelas que he leído: cuatro novelas cortas, distintas e independientes. Pero el formato no afecta para nada, lo recomiendo ampliamente.

Había leído muy poco al respecto para no enterarme mucho de la trama. Así cada cuento se me iba presentando medio sorpresa, aunque la tapa da algunas pistas de por dónde va la historia. “Cuatro cuentos de nuestro momento actual”, son tan humanos y reales que reflejan totalmente el estado de la sociedad en este momento en el tiempo. Por algo la contratapa del libro nos dice “Distopia is now”.

El primero es típico de Doctorow, una historia que no sabría si incluir el término “ciencia” en el género “ficción”. Al ser tan actual, pierde un poco lo especulativo característico de la ciencia ficción. El relato distópico que nos presentan podría haber pasado perfectamente en 2019. Esta tendencia se mantiene hasta el final, pero no se termina de perder del todo ese no sé qué de la ciencia ficción.

El autor tiene la costumbre y facilidad de practicar activismo mientras nos divierte con la historia. Explica problemas como el DRM (Gestión Digital de Restricciones), cómo funciona y por qué es una mala idea que exista. Cuenta con la capacidad de explicar temas técnicos de manera bastante sencilla. Temas familiares para quienes tenemos conocimientos informáticos, por lo que apela a nuestro nerdismo. Pero alienta también a aprender e interesarse más, y con suerte logra convertir a quien lee en activista a la vez.

El hilo conductor de la historia es una tostadora que no permite tostar cierto tipo de pan, y de ahí se desenlaza toda una trama que gira entorno a eso. Algo sumamente real y que lamentablemente puede pasar con cualquier dispositivo actual, sobretodo los que se conectan de manera estúpidamente innecesaria a Internet…

Después de esa historia tan característica, una sobre el racismo y la brutalidad policial en el contexto de un mundo con superhéroes. El personaje principal va a resultarle familiar hasta a la persona más alejada de los cómics y cine superheróico. Si bien tiene un nombre distinto, no es más que el universo alternativo de uno de los héroes de capa más populares. Me encantan los cómics que mezclan esto de la política con super poderes, ¿hasta dónde deberían llegar y hasta dónde deberían dejar a los humanos “sin poderes” decidir por sí mismos? Algunas buenas historias de este estilo leí en The Authority: Revolution por Ed Brubaker, Avengers de Geoff Johns, y un poco también en Silver Surfer: Parable por Stan Lee y Moebius que es un librazo.

El cuento está genial, y al terminarlo me quedé con ganas de leer algún cómic de superhéroes escrito por Cory Doctorow (In Real Life está bastante bueno) y algo más de este estilo de narrativa donde se cuestiona por qué los super héroes no se encargan de todos los problemas de la sociedad…

La siguiente es una de esas historias que da un poco de miedo leer por la paranoia de entrar en una lista de algún gobierno. Algo similar a lo que me pasó con Little Brother, se trata de la radicalización de ciertas personas en situación desesperada. Sin contar mucho de qué va la trama, es una buena crítica a ciertos sistemas impuestos por el capitalismo que… bueno, hacen lo que hace el capitalismo, lucrar con el sufrimiento humano. Creo que es el que te deja más “al borde de la silla” por saber qué va a pasar después.

Para cerrar el libro, el cuento de un señor llamado Martin que pasa años construyendo su búnker para sobrevivir al colapso de la sociedad. Un aspecto interesante que describe es cómo saber cuándo entrar al búnker. Con las noticias actuales y la forma en que manipulan a la sociedad, cualquier evento puede ser “El Evento” que da la señal de que el mundo se terminó de ir a la mierda y es hora de ir a encerrarse en el búnker. Martin selecciona a un grupo de personas para acompañarlo y volver a salir una vez que el caos haya terminado y se tenga que salir a rearmar una sociedad. En un escenario apocalíptico cuyo alcance desconocemos (como debería ser en el caso que realmente ocurriera), y que imaginarán que puede tener desenlaces variados y dispares. Pero el final no decepciona.

El mensaje que nos deja el libro y que pienso empezar a citar seguido:

– “Éste no es el tipo de pelea que ganamos, es el tipo de pelea que peleamos”
Cory Doctorow

Libro sumamente recomendable. En mi pila de libros para leer se encuentra su novela Walkaway. Probablemente sea mi próximo libro al terminar el que estoy leyendo en éstos momentos. En Twitter ha comentado de dos trabajos a publicar en el futuro: “Stalkerware”, una historia en el universo de Little Brother (fuente) y “The Lost Cause” una novela sobre verdad, reconciliación y el cambio climático (fuente).  Ansío leer ambos trabajos una vez publicados. Los títulos posiblemente cambien, la novela Walkaway tenía el título “Utopia” originalmente. Si quieren saber más, pueden seguir al autor en Twitter donde comparte el progreso de su escritura bajo el hashtag #dailywords.

» Leer más, comentarios, etc...

Variable not found

Top ten 2019 en Variable not found

enero 07, 2020 07:43

Lo más visto en 2019
Estrenamos este nuevo año y década (aunque esto último es bastante más discutible) siguiendo una de las grandes tradiciones de este blog: dedicar el primer post a repasar cuál fue el contenido publicado durante los doce meses anteriores que más os llamó la atención.

Pero antes, abusando de este pequeño slice de atención que me estáis prestando en este momento, quería aprovechar para desearos un 2020 repleto de salud y alegría, tanto en el plano personal como en el profesional.

Y ahora, al turrón...

Top ten 2019 en Variable not found

Comenzando por el final, y entrando en la lista del top ten casi igualado con otros artículos, encontramos el post donde presentaba EF6.TagWith. Se trata de un paquete NuGet inspirado en la característica de EF Core con el mismo nombre, que permite añadir etiquetas personalizadas a consultas realizadas desde Entity Framework. Una herramienta que espero que no os haga falta nunca ;)

Ya en novena posición empezamos a encontrar pistas de lo que ha sido la tendencia en 2019: el triunfo de los artículos destinados a explicar novedades del lenguaje C#, probablemente debido a las grandes novedades que se han presentado en su última versión. En este caso comenzamos con el nuevo operador de C#8 Null coalescing assigment "??=", una construcción imprescindible para simplificar la sintaxis de asignaciones a variables nulas.

Continuando en esta línea, en el puesto número ocho se encuentra el post Switch expressions en C# 8, describiendo también una de las nuevas sintaxis que vinieron de la mano de la última versión del lenguaje. En este caso, una fórmula para que determinados bloques switch sean más claros y concisos.

En el artículo Registro y obtención de múltiples implementaciones de servicios en ASP.NET Core, y un caso práctico que encontramos seguidamente vemos cómo aplicar principios SOLID y un patrón como Strategy para mejorar un escenario que podemos encontrar con cierta frecuencia.

En la sexta posición saltamos brevemente a Entity Framework Core. En el post Evaluación en cliente de EF Core: sus peligros y cómo evitarla veíamos cómo este framework gestionaba expresiones que no podían ser evaluadas en el servidor. Afortunadamente, con EF Core 3.0 han dado marcha atrás y ya no hay que tener en cuenta estas precauciones :)

Volvemos a C#, para estudiar una de las novedades principales de la versión 8, que llega con la promesa de evitar los fatídicos y frecuentes Null Reference Exceptions: Tipos referencia anulables en C# 8. Por fin, un freno para el "error del billón de dólares", aunque tendremos que adaptar las aplicaciones para que esto sea una realidad.

En cuarta posición de los posts más visitados encontramos un primer vistazo a otra de las perlas introducidas en C# 8 y, sin duda, la más controvertida: la implementación por defecto en interfaces. He de reconocer que a primera vista no me convenció mucho, pero conforme pasa el tiempo estoy empezando a verle la utilidad y casi les perdono que tengamos que reaprender eso de que "las interfaces son contratos" ;)

Ya en tercera posición, me he alegrado de encontrar una colaboración del amigo Jorge Turrado: Cómo crear un paquete NuGet y publicarlo en Nuget.org, un gran post que, sin duda, merecía estar en las primeras posiciones del ranking. Os recomiendo que no os perdáis su blog Fixed Buffer, porque encontraréis más, y de calidad.

And the winner is...El segundo puesto lo ocupa un artículo sobre una de las novedades más espectaculares que acompañaron a ASP.NET Core 2.2, a primeros del año pasado: El hosting in-process. Este modo de hospedado, que en las versiones más recientes es ya la opción por defecto, permitía cuadruplicar el rendimiento de nuestras aplicaciones for free, sin hacer apenas ningún cambio. Y doy fe de que el cambio es brutal en entornos que requieren gran velocidad de respuesta.

Y el premio para el artículo de 2019 más visitado recae en el magnífico post Antipatrones de asincronía en C#, un compendio de malas prácticas habituales a la hora de utilizar asincronía en nuestras aplicaciones. Hey, y puedo decir lo de "magnífico" sin rubor porque el mérito no es mío, pues solo hice de traductor autorizado del original C# Async Antipatterns de Mark Heath ;)

Por último, me gustaría añadir una mención especial a dos posts que publiqué hace sólo unos días, y por tanto no han tenido tiempo de recibir tantas visitas como otros menos recientes, pero aún así muestran posiciones destacadas en el top ten. Además, ambos parecen inocentadas, aunque sólo uno de ellos lo es:
Gorro de Santa ClausEl "SantaGate" de Visual Studio Code (y ¡felices fiestas!), una felicitación navideña acompañada de una disparatada curiosa historia que se ha vivido hace pocas semanas en el repositorio de Visual Studio Code en GitHub a cuenta del gorrito de Santa Claus. Sin duda, una prueba irrefutable de que hay gente pa tó 😉
Como de costumbre, aprovecho el 28 de diciembre para dar rienda suelta a la imaginación, y este año me he inventado un patrón de diseño: el Ostrich Design Pattern; si no lo conocéis, no dudéis en echarle un vistazo porque seguro que os será de mucha utilidad en vuestro día a día 😁
Y visto todo esto, ahora sí, ¡vamos a por 2020!

Publicado en Variable not found.

» Leer más, comentarios, etc...

Blog Bitix

Mejorar el tiempo de carga de una página web usando lazy load

enero 04, 2020 01:30

Las páginas grandes y con muchos recursos como imágenes e iframes implementar la carga vaga o lazy load obtienen un gran beneficio, necesitando realizar menos peticiones en la carga inicial, con menos tamaño y cargándose en menos tiempo. Los navegadores han añadido soporte para desde JavaScript proporciona este soporte.

Firefox

Chromium

El tiempo de carga de una página web es una métrica importante para una buena experiencia de usuario, es una métrica evaluada por PageSpeed y también de cara al posicionamiento en los buscadores o SEO ya que influye en la fórmula que usa Google para clasificar y posicionar las páginas en la página de resultados de búsqueda, un buen posicionamiento en los buscadores es importante porque significa más visitas a una web que en muchos casos es la mayor fuente de usuarios. Un tiempo de carga alto hace que los usuarios abandonen la página antes de que esté cargada completamente, influyendo en el porcentaje de rebote y el tiempo de visita que se puede medir con Google Analytics.

Una de las variables que influyen en el tiempo de carga es el número de peticiones y el tamaño de los recursos de esas peticiones que se hacen al servidor para descargar los elementos de la página completa, estos son imágenes, hojas de estilo, archivos de JavaScript, iframes, … Sin embargo, los navegadores cargan todos los elementos de una página incluso aquellos que están en la parte baja e inicialmente no se ven hasta que el usuario se desplaza hasta visualizarlos. Cargar elementos que no se visualizan es innecesario e ineficiente en el navegador pero también para el servidor que ha de atender a más peticiones.

Los artículos de mi blog como Desempaquetado de PlayStation 4 Slim de 1 TB o Desempaquetado Intel NUC8i5BEK (Bean Canyon), HyperX Impact (RAM) y Samsung 970 EVO NVMe (SSD), incluyen numerosas imágenes e iframes de vídeos de Youtube y publicidad de Amazon, en todas se carga los comentarios de Disqus y para compartir los artículos con ShareThis que están al final de la página y hasta que el usuario no ha leido el artículo son innecesarios. Disqus además en concreto para cargarse realiza numerosas peticiones adicionales. Estás páginas son de las más complejas por número de recursos y extensión del artículo que tengo en el blog por lo que he analizado una de ellas antes y después aplicando lazy loading para cargar inicialmente solo los elementos que se visualizan que son aquellos que están en la parte superior de la página.

Página PlayStation Página PlayStation

Página Intel NUC Página Intel NUC

Páginas representativas de Blog Bitix

Analizando el número de peticiones, tamaño y tiempo de carga en la página de la PlayStation realizaban 343 peticiones inicialmente, con un tamaño de descarga de 5 MiB en un tiempo de carga según Firefox de 15 segundos. Evaluando esta página con PageSpeed un aspecto importante que indica a mejora reducir el número de elementos descargados, además de reducir peticiones y tamaño de la página se descarguen inicialmente los elementos importantes y omitiendo recursos de JavaScript hace que el desempeño sea mejor. El resultado es un menor tiempo de carga.

Métricas de carga en PageSpeed página PlayStation antes Métricas de carga de PageSpeed página Intel NUC antes

Métricas de carga en PageSpeed antes

Métricas de carga de Firefox página PlayStation antes

Métricas de carga en Firefox

La solución es cargar los elementos imágenes, iframes, vídeos y comentarios de Disqus cuando se vayan a visualizar al desplazarse el usuario hasta ellos. Esto reduce notablemente el número de peticiones realizadas inicialmente, el tamaño de descarga y el tiempo de carga. Una librería de JavaScript que permite realizar esta funcionalidad es Lozad, no tiene dependencias, es muy pequeña, es soprendentemente fácil de utilizar en relación con el beneficio que aporta. Aprovecha el soporte de la interfaz IntersectionObserver precisamente proporcionada por los navegadores para realizar la carga vaga o lazy load de forma eficiente.

Para las imágenes e iframes hay que añadir una clase a los elementos de HTML y cambiar el atributo src a data-src. Los comentarios de Disqus se cargan con un archivo de JavaScript por lo que hay que retrasar la carga hasta que el elemento HTML que los contiene se visualice y con una función de callback se inicia la inserción de su recurso de JavaScript utilizando jQuery para cargar insertar en la página dinámicamente recursos JavaScript.

Este es una plantilla que utilizo para generar de forma estática el contenido del blog con Hugo en el que las imágenes ilustrativas de los artículos usan el atributo data-src e incluyen la clase de CSS lozad, en el código fuente o utilizando la función Inspeccionar se comprueba el HTML resultado. El otro archivo parte del JavaScript de este blog que implementa la carga vaga de las imágenes, iframes y scritps de Disqus y ShareThis. Para el caso de insertar JavaScript dinámicamente se observa un elemento con una expresión de jQuery y mediante una función callback que Lozad invoca cuando su elemento se visualiza momento en que se realiza la acción de insertar el recurso de JavaScript.

1
2
3
4
5
6
7
8
<div class="media">
    <figure>
    <a href="{ $imagescaled1.RelPermalink }" title="{ .Params.title1 }" data-gallery="data-gallery"><img data-src="{ $imagescaledthumb1.RelPermalink }" width="{ $imagescaledthumb1.Width }" height="{ $imagescaledthumb1.Height }" layout="responsive" class="lozad"></a>
    <a href="{ $imagescaled2.RelPermalink }" title="{ .Params.title2 }" data-gallery="data-gallery"><img data-src="{ $imagescaledthumb2.RelPermalink }" width="{ $imagescaledthumb2.Width }" height="{ $imagescaledthumb2.Height }" layout="responsive" class="lozad"></a>
    <a href="{ $imagescaled3.RelPermalink }" title="{ .Params.title3 }" data-gallery="data-gallery"><img data-src="{ $imagescaledthumb3.RelPermalink }" width="{ $imagescaledthumb3.Width }" height="{ $imagescaledthumb3.Height }" layout="responsive" class="lozad"></a>
    <figcaption>{ .Params.caption }{ if .Params.source }<br/><small>{ i18n "source" }: { .Params.source }</small>{ end }</figcaption>
    </figure>
</div>
figureproc.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
require(['jquery', 'lozad', 'jquery-blueimp-gallery'], function ($, lozad, blueimp) {
    ...

    function initLazyload() {
        var observer = lozad('.lozad', {
            rootMargin: '50px 0px'
        });

        var disqusObserver = lozad('#disqus_thread', {
            rootMargin: '50px 0px',
            load: function(el) {
                $.ajax({
                    url: '//' + disqus_shortname + '.disqus.com/' + disqus_script,
                    async: true,
                    cache: true,
                    dataType: 'script',
                    success: function() {}
                });
            }
        });

        var shareThisObserver = lozad('div.sharethis-inline-share-buttons', {
            rootMargin: '50px 0px',
            load: function(el) {
                $.ajax({
                    url: '//platform-api.sharethis.com/js/sharethis.js#property=5920c4ce1bd0670011e06acd&product=inline-share-buttons',
                    async: true,
                    cache: true,
                    dataType: 'script',
                    success: function() {}
                });
            }
        });

        observer.observe();
        disqusObserver.observe();
        shareThisObserver.observe();
    }
    
    ...
    initLazyload();
});

main.js

Con simplemente esta mejora, que no es complicada de realizar, añadiendo el código anterior y realizando los sencillos cambios en los atributos de imágenes e iframes las métricas en la comparación con página anterior mejoran notablemente pasando apróximadamente de 15 segungos a menos de 4 en un tiempo de carga hasta que el navegador dejan de hacer peticiones.

Página Prueba Antes Después
PlayStation Firefox 343 peticiones, 5 MiB, 15s 104 peticiones, 1.5 MiB, 3.5s
PlayStation PageSpeed 23 44
Intel NUC PageSpeed 9 38

La métrica de PageSpeed mejoran notablemente, es una cifra sobre 100 que aún dista de ser alta debido a que Analytics y AdSense imponen una fuerte penalización por utilizarlos e incluirlos en las páginas. La página WebPageTest proporciona algunos datos adicionales y complementarios a los proporcinados por PageSpeed, también es recomendable usarla para medir la variación en los resultados con los cambios realizados con el objetivo de mejorar una página.

Métricas de carga en PageSpeed página PlayStation después Métricas de carga de PageSpeed página Intel NUC después

Métricas de carga en PageSpeed después

Métricas de carga de Firefox página PlayStation después

Métricas de carga en Firefox

Los navegadores van a añadir el soporte de carga vaga directamente en las imágenes e iframes con un el atributo loading mediante el cual el JavaScript anterior será innecesario para estos elementos.

En definitiva es un pequeño cambio sencillo de realizar y que mejora notablemente la experiencia de usuario, la carga del servidor, es recomendable para el SEO incluso desde el punto de vista de la privacidad de los usuarios.

Otro uso distinto para la carga vaga es lanzar eventos de Analytics, esto lo he empleado para saber si los usuarios llegan al final de los artículos. Con esto es posible obtener datos interesantes sobre cuales son las mejoras páginas o artículos, por ejemplo, para diferencia entre página muy visitada por que esté bien posicionada pero ni interesante para los uauarios porque no llegan al final del artículo o por el contrario páginas con pocas visitas pero que los usuarios las leen hasta el final.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
require(['jquery', 'lozad'], function($, lozad) {
    function initAnalytics() {
        ...

        var pageBottomObserver = lozad('#pageBottom', {
            rootMargin: '50px 0px',
            load: function(el) {
                ga('send', 'event', 'page', 'show', 'bottom', {'nonInteraction': 1});
            }
        });
        pageBottomObserver.observe();
    }

    ...

    initAnalytics();
    ...
});
app.js

» Leer más, comentarios, etc...

Blog Bitix

Mejorar el tiempo de carga de una página web usando lazy load

enero 04, 2020 01:30

Las páginas grandes y con muchos recursos como imágenes e iframes implementar la carga vaga o lazy load obtienen un gran beneficio, necesitando realizar menos peticiones en la carga inicial, con menos tamaño y cargándose en menos tiempo. Los navegadores han añadido soporte para desde JavaScript proporciona este soporte.

Firefox
Chromium

El tiempo de carga de una página web es una métrica importante para una buena experiencia de usuario, es una métrica evaluada por PageSpeed y también de cara al posicionamiento en los buscadores o SEO ya que influye en la fórmula que usa Google para clasificar y posicionar las páginas en la página de resultados de búsqueda, un buen posicionamiento en los buscadores es importante porque significa más visitas a una web que en muchos casos es la mayor fuente de usuarios. Un tiempo de carga alto hace que los usuarios abandonen la página antes de que esté cargada completamente, influyendo en el porcentaje de rebote y el tiempo de visita que se puede medir con Google Analytics.

Una de las variables que influyen en el tiempo de carga es el número de peticiones y el tamaño de los recursos de esas peticiones que se hacen al servidor para descargar los elementos de la página completa, estos son imágenes, hojas de estilo, archivos de JavaScript, iframes, … Sin embargo, los navegadores cargan todos los elementos de una página incluso aquellos que están en la parte baja e inicialmente no se ven hasta que el usuario se desplaza hasta visualizarlos. Cargar elementos que no se visualizan es innecesario e ineficiente en el navegador pero también para el servidor que ha de atender a más peticiones.

Los artículos de mi blog como Desempaquetado de PlayStation 4 Slim de 1 TB o Desempaquetado Intel NUC8i5BEK (Bean Canyon), HyperX Impact (RAM) y Samsung 970 EVO NVMe (SSD), incluyen numerosas imágenes e iframes de vídeos de Youtube y publicidad de Amazon, en todas se carga los comentarios de Disqus y para compartir los artículos con ShareThis que están al final de la página y hasta que el usuario no ha leido el artículo son innecesarios. Disqus además en concreto para cargarse realiza numerosas peticiones adicionales. Estás páginas son de las más complejas por número de recursos y extensión del artículo que tengo en el blog por lo que he analizado una de ellas antes y después aplicando lazy loading para cargar inicialmente solo los elementos que se visualizan que son aquellos que están en la parte superior de la página.

Página PlayStation Página PlayStation
Página Intel NUC Página Intel NUC Páginas representativas de Blog Bitix

Analizando el número de peticiones, tamaño y tiempo de carga en la página de la PlayStation realizaban 343 peticiones inicialmente, con un tamaño de descarga de 5 MiB en un tiempo de carga según Firefox de 15 segundos. Evaluando esta página con PageSpeed un aspecto importante que indica a mejora reducir el número de elementos descargados, además de reducir peticiones y tamaño de la página se descarguen inicialmente los elementos importantes y omitiendo recursos de JavaScript hace que el desempeño sea mejor. El resultado es un menor tiempo de carga.

Métricas de carga en PageSpeed página PlayStation antes Métricas de carga de PageSpeed página Intel NUC antes Métricas de carga en PageSpeed antes
Métricas de carga de Firefox página PlayStation antes Métricas de carga en Firefox

La solución es cargar los elementos imágenes, iframes, vídeos y comentarios de Disqus cuando se vayan a visualizar al desplazarse el usuario hasta ellos. Esto reduce notablemente el número de peticiones realizadas inicialmente, el tamaño de descarga y el tiempo de carga. Una librería de JavaScript que permite realizar esta funcionalidad es Lozad, no tiene dependencias, es muy pequeña, es soprendentemente fácil de utilizar en relación con el beneficio que aporta. Aprovecha el soporte de la interfaz IntersectionObserver precisamente proporcionada por los navegadores para realizar la carga vaga o lazy load de forma eficiente.

Para las imágenes e iframes hay que añadir una clase a los elementos de HTML y cambiar el atributo src a data-src. Los comentarios de Disqus se cargan con un archivo de JavaScript por lo que hay que retrasar la carga hasta que el elemento HTML que los contiene se visualice y con una función de callback se inicia la inserción de su recurso de JavaScript utilizando jQuery para cargar insertar en la página dinámicamente recursos JavaScript.

Este es una plantilla que utilizo para generar de forma estática el contenido del blog con Hugo en el que las imágenes ilustrativas de los artículos usan el atributo data-src e incluyen la clase de CSS lozad, en el código fuente o utilizando la función Inspeccionar se comprueba el HTML resultado. El otro archivo parte del JavaScript de este blog que implementa la carga vaga de las imágenes, iframes y scritps de Disqus y ShareThis. Para el caso de insertar JavaScript dinámicamente se observa un elemento con una expresión de jQuery y mediante una función callback que Lozad invoca cuando su elemento se visualiza momento en que se realiza la acción de insertar el recurso de JavaScript.

1
2
3
4
5
6
7
8
<div class="media">
    <figure>
    <a href="{ $imagescaled1.RelPermalink }" title="{ .Params.title1 }" data-gallery="data-gallery"><img data-src="{ $imagescaledthumb1.RelPermalink }" width="{ $imagescaledthumb1.Width }" height="{ $imagescaledthumb1.Height }" layout="responsive" class="lozad"></a>
    <a href="{ $imagescaled2.RelPermalink }" title="{ .Params.title2 }" data-gallery="data-gallery"><img data-src="{ $imagescaledthumb2.RelPermalink }" width="{ $imagescaledthumb2.Width }" height="{ $imagescaledthumb2.Height }" layout="responsive" class="lozad"></a>
    <a href="{ $imagescaled3.RelPermalink }" title="{ .Params.title3 }" data-gallery="data-gallery"><img data-src="{ $imagescaledthumb3.RelPermalink }" width="{ $imagescaledthumb3.Width }" height="{ $imagescaledthumb3.Height }" layout="responsive" class="lozad"></a>
    <figcaption>{ .Params.caption }{ if .Params.source }<br/><small>{ i18n "source" }: { .Params.source }</small>{ end }</figcaption>
    </figure>
</div>
figureproc.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
require(['jquery', 'lozad', 'jquery-blueimp-gallery'], function ($, lozad, blueimp) {
    ...

    function initLazyload() {
        var observer = lozad('.lozad', {
            rootMargin: '50px 0px'
        });

        var disqusObserver = lozad('#disqus_thread', {
            rootMargin: '50px 0px',
            load: function(el) {
                $.ajax({
                    url: '//' + disqus_shortname + '.disqus.com/' + disqus_script,
                    async: true,
                    cache: true,
                    dataType: 'script',
                    success: function() {}
                });
            }
        });

        var shareThisObserver = lozad('div.sharethis-inline-share-buttons', {
            rootMargin: '50px 0px',
            load: function(el) {
                $.ajax({
                    url: '//platform-api.sharethis.com/js/sharethis.js#property=5920c4ce1bd0670011e06acd&product=inline-share-buttons',
                    async: true,
                    cache: true,
                    dataType: 'script',
                    success: function() {}
                });
            }
        });

        observer.observe();
        disqusObserver.observe();
        shareThisObserver.observe();
    }
    
    ...
    initLazyload();
});

main.js

Con simplemente esta mejora, que no es complicada de realizar, añadiendo el código anterior y realizando los sencillos cambios en los atributos de imágenes e iframes las métricas en la comparación con página anterior mejoran notablemente pasando apróximadamente de 15 segungos a menos de 4 en un tiempo de carga hasta que el navegador dejan de hacer peticiones.

Página Prueba Antes Después
PlayStation Firefox 343 peticiones, 5 MiB, 15s 104 peticiones, 1.5 MiB, 3.5s
PlayStation PageSpeed 23 44
Intel NUC PageSpeed 9 38

La métrica de PageSpeed mejoran notablemente, es una cifra sobre 100 que aún dista de ser alta debido a que Analytics y AdSense imponen una fuerte penalización por utilizarlos e incluirlos en las páginas. La página WebPageTest proporciona algunos datos adicionales y complementarios a los proporcinados por PageSpeed, también es recomendable usarla para medir la variación en los resultados con los cambios realizados con el objetivo de mejorar una página.

Métricas de carga en PageSpeed página PlayStation después Métricas de carga de PageSpeed página Intel NUC después Métricas de carga en PageSpeed después
Métricas de carga de Firefox página PlayStation después Métricas de carga en Firefox

Los navegadores van a añadir el soporte de carga vaga directamente en las imágenes e iframes con un el atributo loading mediante el cual el JavaScript anterior será innecesario para estos elementos.

En definitiva es un pequeño cambio sencillo de realizar y que mejora notablemente la experiencia de usuario, la carga del servidor, es recomendable para el SEO incluso desde el punto de vista de la privacidad de los usuarios.

Otro uso distinto para la carga vaga es lanzar eventos de Analytics, esto lo he empleado para saber si los usuarios llegan al final de los artículos. Con esto es posible obtener datos interesantes sobre cuales son las mejoras páginas o artículos, por ejemplo, para diferencia entre página muy visitada por que esté bien posicionada pero ni interesante para los uauarios porque no llegan al final del artículo o por el contrario páginas con pocas visitas pero que los usuarios las leen hasta el final.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
require(['jquery', 'lozad'], function($, lozad) {
    function initAnalytics() {
        ...

        var pageBottomObserver = lozad('#pageBottom', {
            rootMargin: '50px 0px',
            load: function(el) {
                ga('send', 'event', 'page', 'show', 'bottom', {'nonInteraction': 1});
            }
        });
        pageBottomObserver.observe();
    }

    ...

    initAnalytics();
    ...
});
app.js

» Leer más, comentarios, etc...

Navegapolis

Estimaciones ágiles, métricas de productividad, #NoEstimates y otros animales.

enero 02, 2020 05:20

metricas pinLos equipos ágiles no quieren saber cuánto trabajo llevan invertido en una tarea, sino cuánto falta para terminarla, porque no que les interesa contabilizar horas, sino mantener un ritmo de avance hacia la meta.

Los equipos que no tienen dificultades para mantener el avance o que se apoyan en otros criterios para lograrlo —como el ritmo de entrega— y que tampoco necesitan sincronizar su producción con la de otros equipos, consideran que las estimaciones son una práctica innecesaria.

Por las diferencias entre la agilidad y la gestión tradicional, estas son tres actitudes cuestionables en relación con las métricas ágiles:

 

» Leer más, comentarios, etc...

Israel Perales

Feliz año nuevo 2020

diciembre 31, 2019 07:42

Feliz año nuevo 2020

2019 Fue un gran año para mi, hice casi todas mis metas profesionales o personales, metí muchos cambios en mi vida y aunque por N motivos no pude disfrutarlas como quisiera, allí están.

En el blog no he publicado lo suficiente, los borradores existen, solo siento que puedo mejorar la redacción y tengo pensado hacer videos en lugar de solo escribir, tratar con una forma diferente de hacer las cosas para salir de mi zona de confort en esta parte.

También intenté de dedicarle tiempo a otras cosas, comencé a ir al gimnasio, igual fui 1 semana al crossfit y al final corriendo por las noches, lo máximo que pude correr fueron 10k, creo que es importante el ejericio, me ayudo mucho a resolver problemas y suavizar otros, la excusa perfecta para hacerme con un Smart Watch.

Para no hacer mas largo el post espero que tengan un prospero año nuevo.

» Leer más, comentarios, etc...

Blog Bitix

Hemeroteca #16

diciembre 31, 2019 06:30

Hugo

Se acaba otro año y en este caso además la década, en el siguiente 2020 harán 10 años ya desde que empecé a escribir artículos técnicos principalmente sobre GNU/Linux y Java, algunos artículos de opinión y desempaquetados los productos que compro relacionados con la tecnología. Una década escribiendo uno o dos artículos semanales, no es fácil aguantar durante todo este tiempo y conservar la motivación y tener el tiempo que requieren cada uno de los artículos. Muchos surgen de ideas del trabajo que en muchos casos no puedo aplicar laboralmente, salvo alguna pequeña excepción, por implementarlos en Java por mi preferencia en el caso de los de programación y usar otras tecnologías laboralmente.

Este segundo semestre he escrito algunos artículos menos, 34, frente a los 48 del primer semestre y es que con la compra de una PlayStation 4 estoy dedicando parte del tiempo que antes dedicaba al blog e investigar tecnología a jugar a algunos videojuegos, aún así esa cantidad de artículos sigue siendo al menos uno por semana que es mi cadencia mínima de publicación que deseo. En total en el 2019 he publicado 82 artículos para llegar a la cifra de 453 artículos en el tiempo de vida de Blog Bitix desde finales del 2013, son muchos más teniendo en cuenta para ser algo que dedico en mi tiempo libre y cada caracter de los artículos requiere teclearlo, corregir las faltas de ortografía, hacer que las frases estén bien construidas sintácticamente y sean fáciles de comprender, cada idea e imagen a recortar y capturar está colocada de forma artesanal y manual, lo único automatizado es generar la versión estática de la web que gracias a Hugo junto con GitHub Pages y algunos comandos de GNU/Linux me permite centrarme más en solo escribir el contenido.

Pero los artículos no es lo único que he hecho este 2019, he recibido algunas pull request e issues para Script de instalación de Arch Linux desatendido, automatizado y personalizable y el repositorio en GitHub tiene una buena cantidad de stars y forks, he continuado manteniendo la traducción al español de VLC, he enviado una actualización con las nuevas cadenas y corregidos algunos errores de la traducción al español para la reciente versión 6.1 de VirtualBox y tratado de mejorar la de KeePassXC, algunas mejoras internas para generar el blog, incluido rediseñarlo para que el contenido esté centrado, más grande horizontalmente y publicidad lateral sticky.

Listando los artículos que he escrito durante el año 2019 y este semestre, en la categoría de artículos de desempaquetado he escrito sobre la PlayStation 4.

Dos de opinión.

Una de las temáticas principales de este blog es GNU/Linux, dentro de ella de diversa temática desde programación hasta el entorno de escritorio.

La otra temática principal de mi blog es la programación con el lenguaje Java donde están la mayoría de los artículos que he escrito.

Pasado el año está bien hacer un pequeño balance de como ha sido para evaluar si hay algo que se puede hacer para mejorar, en cuanto a las cifras del blog en número de visitas se ha mantenido prácticamente igual al 2018 con ~500K páginas vistas, a pesar de la cifra anualizada no son más de 1.5K al día que no es mucho, hay mucho margen de mejora. Aunque siempre es un punto de motivación normalmente no pienso en escribir artículos que atraigan a más lectores sino antes en lo que me apetece escribir y publicar. Aún escribiendo artículos más avanzados uno de los artículos más visitados es a pesar de su sencillez y simplicidad 4 formas de hacer un bucle for en Java, he escrito artículos mucho más complejos que mostrar varias formas de hacer un bucle for pero parece que los artículos sencillos tienen un público más amplio que los artículos más complejos.

He escrito lo que en cada momento lo que me ha apetecido, lógicamente no desdeñanado conseguir más visitas pero sin ser esta la principal motivación de escribir algo. Como cada año, para observar las métricas pasados varios, hago públicos los datos de visitas en Analytics, ingresos de AdSense y de afiliación de Amazon. Por si a alguien sin blog, con uno o por comparar le resultan interesantes y quiere hacerse una idea.

Analytics 2019 de Blog Bitix AdSense 2019 de Blog Bitix

Evolución visitas e ingresos en 2019

En cuanto a ingresos algunos menos 410€ frente a 480€ del año anterior, aún así todos los meses de forma consistente los ingresos están alrededor de entre 20 y 30 euros que no está mal.

Ingresos 2018 de Blog Bitix Páginas vistas 2018 de Blog Bitix Clics 2018 de Blog Bitix

CRT 2019 de Blog Bitix CPC 2019 de Blog Bitix RPM 2019 de Blog Bitix

Tipos de puja 2019 de Blog Bitix Plataformas 2019 de Blog Bitix

Métricas de AdSense

En los ingresos por enlaces de afiliado de Amazon ha habido algunos meses especialmente notables y me sorprende que un solo blog como este le proporcione a Amazon facturar esa cantidad de dinero.

Aparte de la publicidad de AdSense la monetización por afiliación es una gran estrategia para conseguir ventas en un negocio beneficioso para las tres partes el editor que obtiene una recompensa por la venta motivándole a seguir escribiendo artículos que los usuarios consideren buenos como para finalizar en una compra, el usuario que dependiendo del artículo obtiene información objetiva que le disipa las dudas como para realizar con mayores garantías de hacer una buena compra y el vendedor que aumenta su facturación y en caso de que no se produzca la venta coloca enlaces dando su página de comercio electrónico a conocer con los enlaces disponibles para una futura ocasión u otros usuarios.

Facturación e ingresos Amazon

Esto es todo, ¡buen 2020!

Christmas Tux 2019

¡Buen 2020! Fuente: klowner.com

» Leer más, comentarios, etc...

Blog Bitix

Comando para convertir imágenes a WebP con menor tamaño desde JPEG y PNG

diciembre 30, 2019 09:00

Las velocidades de conexión actuales de 100 Mbps y de 600 Mbps en los hogares permiten no darle tanta importancia al tamaño de una imagen y aunque los dispositivos móviles también tienen unas velocidades de conexión rápidas reducir el tamaño de las imágenes puede significar que una página web cargue algo más rápido. El ahorro está en un 30% y 60% lo que en colecciones grandes de imágenes el ahorro es considerable. WebP proporciona un ahorro de tamaño en las imágenes con una calidad similar que jpg y png.

GNU

Linux

Para que las imágenes ocupen menos espacio se utilizan formatos como jpg para fotos y png utilizado en imágenes como capturas de pantalla de ordenador. En las imágenes fotográficas los píxeles adyacentes suelen tener el mismo color y formatos como jpg se basan en esto para dividir la imagen en pequeñas baldosas en las que pueda almacenar la información de menos píxeles, jpg es un formato con pérdida de calidad que sin ser excesiva es aceptable y se ve compensada con un ahorro importante en el tamaño de la imagen. El formato png es un formato sin pérdida de calidad pero con igualmente con reducido tamaño en imágenes de captura de pantalla de ordenador.

Para comprimir aún más el tamaño de las imágenes con la misma calidad apreciable Google ha desarrollado el formato de imágenes WebP. Webp soporta imágenes con pérdida de calidad para ser una alternativa a jpg, sin pérdida de calidad y imágenes con animaciones como alternativa a imágenes png con animación. La reducción en tamaño de WebP sobre jpg y png está en un 30% o 60% dependiendo de la imagen un ahorro de tamaño significativo que es útil para que por ejemplo las páginas web tengan un menor tamaño de descarga y con ello se carguen más rápido si tiene numerosas fotos e imágenes.

Los navegadores modernos como Google Chrome, Firefox, Microsoft Edge y versiones para Android ya soportan WebP en los formatos con pérdida, sin pérdida y con animaciones.

Una imagen que usé para crear una imagen reducida o vista previa tiene un tamaño de 1600 píxeles de ancho y 1067 de alto ocupando 1018 KiB, la imagen en formato png tiene un tamaño de 1426 píxeles por 947 ocupando 78 KiB.

Fotografía en formato jpg Captura de pantalla en formato _png_

Imágenes en formato JPEG y PNG

Las mismas imágenes comprimidas con WebP ocupan 826 KiB, un 18% menos, para la original en formato jpg y 26 KiB, un 66% menos, para la original en formato png. Las reducciones de tamaño son importantes y teniendo en cuenta que es sin pérdida de calidad apreciable sobre las originales utilizar WebP como formato de imagen permite ahorrar un tamaño importante de descarga en una página web o en espacio de almacenamiento en colecciones grandes de fotografías e imágenes.

Fotografía en formato WebP Captura de pantalla en formato WebP

Mismas imágenes en formato WebP

ImageMagick permite convertir las imágenes entre estos formatos. Con este comando se convierten todas las imágenes jpg y png de un directorio a formato WebP.

1
2
$ for f in *.png; do convert -define webp:lossless=true "$f" "${f%.*}.webp"; done;
$ for f in *.jpg; do convert -define webp:lossless=false "$f" "${f%.*}.webp"; done;
convert-to.webp.sh

» Leer más, comentarios, etc...

Variable not found

Ostrich Design Pattern, y su soporte en C# 9

diciembre 27, 2019 11:01

Un reciente estudio de la consultora Garner indica que durante el desarrollo de una aplicación dedicamos más del 80% de nuestro tiempo a implementar controles de posibles fallos.

Además, este otro informe de StackOverflow obtenido tras analizar el código fuente de miles de proyectos open source, el control y tratamiento de excepciones y problemas supone más del 60% de nuestra base de código y, por tanto, aporta gran parte de la complejidad interna de las aplicaciones.

Pero, adicionalmente, estos estudios ponen al descubierto otros tres aspectos bastante interesantes:
  • Primero, que la mayoría de errores que intentamos controlar no se van a producir nunca. Son posibles a nivel de flujo de código, pero en la operativa de la aplicación no ocurrirán, por lo que podríamos decir que son problemas creados artificialmente durante el proceso de desarrollo.
     
  • Segundo, las líneas de control de errores no están exentas de problemas, por lo que muy a menudo encontraremos en ellas nuevo código de control (¿quién no ha visto try/catch anidados a varios niveles?), por lo que la bola de nieve no para nunca de crecer: código de tratamiento de errores que a su vez contiene código de tratamiento de errores, y así hasta el infinito.
     
  • Y por último, también nos encontramos con que en muchas ocasiones el código de control no hace nada. Por ejemplo, se cuentan por millones las líneas de código detectadas en Github cuyo tratamiento de excepciones consiste simplemente en la aplicación a rajatabla del Swallow Design Pattern, por ejemplo, implementando bloques catch() vacíos.
Y conociendo estos datos, ¿por qué dedicamos tanto tiempo a la gestión de errores en nuestro código? Pues básicamente porque hemos sido educados para eso. Exceptuando cuando se nos cala el coche, no hay nada que suponga un golpe al ego tan importante como cuando una de nuestras aplicaciones falla, y por eso no escatimamos recursos a la hora de evitarlo.

¿No estaría bien poder ignorar esos problemas y centrar nuestro código en aportar valor a nuestros clientes?

Introducing GoF's Ostrich Design Pattern (ODP)

Design Patterns BookSi, como el que os habla, sois unos seguidores incondicionales del Gang of Four y sus 23 patrones de diseño sabréis que el número 18 es el denominado Ostrich Pattern. Se trata de un patrón de diseño que describe una fórmula para el tratamiento de errores bastante más productiva y eficiente que la que solemos utilizar.

Básicamente, su enunciado es:
Para aumentar la productividad y evitar que los errores provoquen problemas de estabilidad y funcionamiento, lo más recomendable es ignorarlos.
O en otras palabras, ignorar los errores y no hacer nada con ellos es mejor tanto desde el punto de vista de la productividad como de la fiabilidad de las aplicaciones, pues:
  • No tendremos que perder tiempo en introducir código de control de errores.
  • La complejidad extra que debemos añadir a una aplicación para gestionar todos los posibles errores introduce un riesgo de fallo superior al que estamos intentando combatir.
  • Y, además, dado que la mayoría de estos errores no van a producirse nunca, el impacto de no controlarlos será prácticamente cero.
En la práctica, la aplicación de este patrón es bastante sencilla... y de hecho, llevamos décadas haciéndolo.

Los que trabajásteis con aquella maravilla prehistórica llamada QuickBasic, con las primeras versiones de Visual Basic (¡sí, cuando aún no llevaba el apellido .NET!), con su hermano pequeño VBScript, con VBA (Visual Basic for Applications) o incluso con Visual Basic .NET, seguro disfrutasteis de la seguridad y tranquilidad de introducir una sentencia como la siguiente:
On Error Resume Next
' Hacer algo que puede que falle
La sentencia On Error Resume Next es la implementación más conocida del patrón de diseño Ostrich.
On Error Resume Next simplemente indicaba al runtime que si se producía un error o excepción debía ignorarla y seguir ejecutando la siguiente instrucción. El siguiente ejemplo ilustra la diferencia entre usarlo o no hacerlo:
Sub Main(args As String())
Ostrich()
Classic()
End Sub

Sub Ostrich()
On Error Resume Next
DoSomethingWrong()
Console.WriteLine("This message will be shown")
End Sub

Sub Classic()
DoSomethingWrong()
Console.WriteLine("This message will never be shown")
End Sub

Private Sub DoSomethingWrong()
Throw New Exception("Boom!")
End Sub

Soporte ODP en C# 9

Seguro que estáis al tanto de que el equipo de diseño de C# está haciendo grandes esfuerzos por evitar los errores que más habitualmente encontramos en nuestras aplicaciones.

Prueba de ello es la reciente introducción de los tipos referencia anulables, que pueden ayudar a evitar en gran medida los recurrentes null reference exceptions. Sin embargo, ¿qué pasa con el resto de errores? Y aquí es donde entra el nuevo soporte Ostrich nativo en la próxima versión de C#.

Ha habido bastante discusión al respecto en Github, pero existe ya un cierto acuerdo en que la sintaxis estará alineada con tecnologías existentes con objeto de facilitar su adopción. Por tanto, salvo que se introduzca algún cambio de última hora, podremos activar este nuevo modo permisivo ante errores de la siguiente forma:
public static void Main()
{
on error resume next; // Ostrich mode on
DoSomethingWrong();
Console.WriteLine("This line will be shown");
var x = 10 / 0;
Console.WriteLine("This line will be shown")
}

private static DoSomethingWrong()
{
throw new Exception("Yay!");
}
Como ocurría en otros lenguajes, la simple activación de este modo hará que cualquier excepción sea ignorada y la ejecución continúe por la línea siguiente.
La nueva instrucción on error resume next podrá utilizarse en cualquier ámbito, siendo aplicable exclusivamente al mismo. En el ejemplo anterior, el patrón ODP sólo se aplicaría en el interior del método Main(), pero el siguiente código muestra cómo podría ser aplicado a una clase completa:
public class InvoiceServices
{
on error resume next; // Ostrich mode in this class
public void AddInvoice(Invoice invoice);
public void RemoveInvoice(int id);
...
}
También puede ser aplicado a nivel de namespace:
namespace MyApp.Services
{
on error resume next; // Applied to all classes in this namespace
public class InvoiceServices { ... }
}
O a nivel de proyecto, introduciendo la instrucción fuera de los espacios de nombre:
on error resume next; // Applied to the entire application
namespace MyApp.Services
{
...
}

Mecanismos adicionales para gestionar ODP en C#

Lo que hemos visto hasta el momento será lo que habitualmente utilicemos en nuestras aplicaciones. De hecho, se prevé que la mayoría de desarrolladores apliquen on error resume next a nivel global, tal y como ya se está haciendo internamente en el código fuente del propio framework .NET 5.

Sin embargo, aun estando activado el modo de ignoración de errores, puede resultarnos interesante conocer si se ha producido algún problema. Para ello se ha introducido en el runtime la variable global Err, que siempre contendrá el último error producido:
public void MyMethod()
{
on error resume next;
var j = 0;
var i = 10 / j;
if (Err is DivideByZeroException ex)
{
// Hacer algo
}
}
Aunque se estima que habrá pocas razones para hacerlo, si queremos desactivar el modo Ostrich podemos hacerlo mediante la instrucción On Error Goto 0, de la siguiente forma:
public void MyMethod()
{
on error resume next; // ODP on
var j = 0;
var i = 10 / j;
Console.WriteLine("This line will be shown");

on error goto 0; // ODP off
var k = i / j;
Console.WriteLine("This line won't be shown");
}
Seguro que pensaréis que esto del goto 0 es algo extraño, pero está así por retrocompatibilidad y razones históricas, pues era como se hacía tradicionalmente en VB. Personalmente, creo que habría sido más acertado utilizar algo más explícito como on error crash o on error boom, que eran las otras alternativas que fueron consideradas por el equipo de diseño de C#.

En fin, otra de esas pequeñas mejoras que seguirán haciendo de C# el mejor lenguaje de programación, y que podremos comenzar a utilizar a mitad de enero, cuando sean lanzados C# 9, Visual Studio 2020, .NET Core 5, Entity Framework Core 7, LittlePony 2.0 y la primera versión de Web Forms Core.

Publicado en: www.variablenotfound.com.

» Leer más, comentarios, etc...

Fixed Buffer

Feliz Navidad 2019

diciembre 24, 2019 05:42

Tiempo de lectura: < 1 minuto
La imagen muestra unos arboles de navidad con nieve

Llegan las navidades y es momento de estar con los tuyos y descansar. Quizás comer y beber de más, cantar algún que otro villancico, reír, bailar…

Son las segundas navidades de FixedBuffer y no podría estar más contento de que este proyecto salga adelante. Como esto no es una entrada sobre temas técnicos tampoco me quiero enrollar (que ya bastante lo hago durante el año…)

¡¡Felices fiestas y próspero año 2020!! Os deseo la mejor de las suertes y que disfrutéis de unas vacaciones tranquilas. Dentro de poco volveremos a dar guerra con muchos e interesantes temas.

¡¡Muchas gracias a todos por hacer que este proyecto siga adelante!!

P.D: Como el gran José Aguilar de Variable Not Found se me ha adelantado escribiendo sobre ello, os dejo una felicitación navideña con un suceso muy gracioso e interesante (recomendada lectura 100%).

**La entrada Feliz Navidad 2019 se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Coding Potions

Cómo crear y usar los props en Vue JS

diciembre 24, 2019 12:00

Introducción

Una de las ventajas de los web components es que se pueden parametrizar, es decir, puedes pasar cierta información a los componentes para que se puedan adaptar dependiendo de las necesidades.

En el primer artículo de Vue (Tutorial básico componentes) ya vimos que las etiquetas HTML no son más que web components ya que tienen vista, estilos y lógica. Sabes que cuando creas un input, por ejemplo, puedes decidir si lo quieres de tipo text o de tipo password.

<input type="text" /> 
<input type="password" />

Pues a esto Vue lo llama props. En otras palabras, los props sirven para pasar parámetros o información al propio web component para poder personalizarlo y ajustarlo dependiendo de las necesidades.

Imagina que quieres crear un componente para mostrar un calendario. En principio puedes crearlo para que siempre se vea y funcione igual, pero también puedes crearlo para que sean más personalizables. Por ejemplo, puedes crearlo con la idea de poder cambiar el número de filas y columnas que se van a mostrar.

De esta forma podrías hacer algo parecido a esto:

<calendar columns="3" rows="6"></calendar>

Vue va más allá y no solo deja pasar números y strings en los props, también deja pasar objetos, arrays y variables reactivas.

Qué son los props y cómo se crean

Los props dentro del componente en el que se declaran no son más que variables. Como pasa con las variables, los props también son reactivos, es decir, cuando desde fuera el valor del prop cambie, automáticamente se actualizará la vista y las propiedades computadas asociadas a ese prop.

Los props se pasan desde el componente padre al componente hijo

Si no sabes lo que es una propiedad computada, te recomiendo que mires antes de continuar este artículo sobre eso:

Variables computadas en Vue

Los props los tienes que declarar dentro de la sección props del componente. A diferencia de las variables que declaras dentro del data, aquí no tienes que hacer un return ni tienes que usar una sintaxis rara, directamente declaras el objeto y dentro cada uno de los props.

<script>
export default {
  props: {
    title: String
  }
};
</script>

Como ves, se declarara el nombre del prop y seguido se pone su tipo.

Los tipos que puedes declarar son:

  • String: Para cadenas de texto.
  • Number: Para números.
  • Boolean: Para booleanos true/false.
  • Array: Listas ya sea de tipos básicos como de objetos.
  • Object: Objetos de javascript.
  • Date: Tipo fecha de javascript.
  • Function: Funciones.
  • Symbol: Símbolos.

Cuando el valor que viene de fuera no coincida con el tipo declarado saltará un error en Vue.

Lo bueno de los props en Vue es que le puedes colocar un valor por defecto en caso de que desde fuera no se pase, para ello el prop lo tienes que crear en forma de Objeto.

<script>
export default {
  props: {
    columns: {
      type: Number,
      default: 4
    },
    rows: {
      type: Number,
      default: 3
    }
  }
};
</script>

En el ejemplo anterior si no pasas filas y no pasas columnas se pondrán su valor declarado a default, 4 y 3 en este ejemplo.

También puedes declarar que el prop tiene que ser obligatorio, es decir, desde fuera al usar el componente siempre tendrías que pasar un valor o de lo contrario salta error. Para declarar la obligatoriedad tienes que usar la sintaxis de objeto también y usar la propiedad required.

<script>
export default {
  props: {
    columns: {
      type: Number,
      required: true
    },
    rows: {
      type: Number,
      default: 3
    }
  }
};
</script>

El prop para las columnas es obligatorio y por tanto no hace falta poner valor por defecto, porque cuando uses este componente siempre vas a tener que declarar ese valor. El prop para las filas en este ejemplo no es obligatorio (si no pones required es como si estuviera a false) y por tanto no hace falta que lo pases siempre.

Por como está hecho Vue, si quieres poner que el valor por defecto de un prop de tipo array sea array vacío, o quieres que un prop de tipo objeto por defecto sea objeto vacío, tienes que hacer este truquito para la propiedad default.

<script>
export default {
  dates: {
    events: {
      type: Array,
      default: () => []
    },
    config: {
      type: Object,
      default: () => {}
    }
  }
};
</script>

Mi consejo es que siempre uses la sintaxis de objeto en los props para que todo esté mucho más claro y que siempre declares la propiedad default.

<script>
export default {
  props: {
    // Evita hacer esto
    text: String,

    // Trata de hacer esto
    size: {
      type: Number,
      default: 0
    }
  }
};
</script>

Cómo usar los props dentro del componente

Los props los tienes que considerar como una variable más del data, es decir, puedes acceder desde cualquier método o computada a los props con el this.

<script>
export default {
  props: {
    columns: {
      type: Number,
      default: 4
    },
    rows: {
      type: Number,
      default: 3
    }
  },
  computed: {
    totalCells() {
      return this.columns * this.rows;
    }
  }
};
</script>

Como es lógico, desde la vista también puedes acceder a los props, igual que con las variables del data:

<template>
  <div class="content">
    <p>Rows: {{rows}}, columns: {{columns}}</p>
  </div>
</template>
<script>
export default {
  props: {
    columns: {
      type: Number,
      default: 4
    },
    rows: {
      type: Number,
      default: 3
    }
  },
  computed: {
    totalMonths() {
      return this.columns * this.rows;
    }
  }
};
</script>

También puedes usar los props dentro de los métodos, incluso dentro de la propiedad created.

<script>
export default {
  props: {
    columns: {
      type: Number,
      default: 4
    },
    rows: {
      type: Number,
      default: 3
    }
  },
  created(){
    console.log("Columns: " + this.columns + " Rows: " + this.rows);
  }
};
</script>

Cómo pasar los props al componente

Para usar un componente con props tienes que importarlo como ya vimos, y en la etiqueta tienes que pasar los props como un atributo más del html, es decir:

<template>
  <div class="home">
    <calendar :rows="6" :columns="5"  />
  </div>
</template>

<script>
// @ es un alias a /src
import Calendar from "@/components/Calendar.vue";

export default {
  components: {
    Calendar
  }
};
</script>

Como pasaba como las propiedades dinámicas que vimos con las vistas, por defecto pasas el valor como string. Para poder pasar números, booleanos, arrays, variables definidas en el data, etc tienes que poner dos puntos antes de la propiedad:

<calendar columns="3" rows="6"></calendar>
<!-- Dentro del componente se reciben los números como string
Por lo tanto dentro del componente no podrías hacer un v-for porque cuenta
como string -->

<calendar :columns="3" :rows="6"></calendar>
<!-- Dentro del componente se reciben los números como números de javascript -->

<calendar :dates="[1,2,4,6,7,9]" :inline="true"></calendar>
<calendar :dates="[1,2,4,6,7,9]" inline></calendar>
<!-- Si quieres pasar un boolean con valor true puedes quitar los puntos
y lo que hay entre comillas para abreviar. Ambas formas son correctas -->

Ejemplo pasando a los props variables definidas en el data:

<template>
  <div class="home">
    <calendar :rows="rows" :columns="columns"  />
  </div>
</template>

<script>
// @ es un alias a /src
import Calendar from "@/components/Calendar.vue";

export default {
  components: {
    Calendar
  },
  data: () => ({
    rows: 6,
    columns: 3
  })
};
</script>

Ejemplo pasando propiedades computadas a los props:

<template>
  <div class="home">
    <calendar :text="text"  />
  </div>
</template>

<script>
// @ es un alias a /src
import Calendar from "@/components/Calendar.vue";

export default {
  components: {
    Calendar
  },
  data: () => ({
    rows: 6,
    columns: 3
  }),
  computed: {
    text() {
      return "Columns: " + this.columns + " Rows: " + this.rows;
    }
  }
};
</script>

Cómo usar los props en el Vue Router

Otra de las cosas que tienes que saber respecto a los props en Vue es que también puedes pasar props al navegar hacia rutas, es decir, los props no solo sirven para para pasar información desde un componente padre a un componente hijo, también puedes pasar props a una ruta cualquiera siempre y cuando estén activados.

Para activar los props en las rutas tienes que poner a true un parámetro especial que tienen las rutas al en el array de rutas que creas al configurar las rutas. Veamos un ejemplo:

router.js

{
  path: "/users",
  name: "users",
  component: Users
  props: true
}

Con eso ya le decimos al vue router que esa ruta puede recibir props desde fuera. Veamos cómo se pasan las rutas al navegar a ellas con vue router.

Si recuerdas en anteriores capítulos vimos como usar Vue router para navegar a rutas. Para pasar props tan solo tienes que pue pasar un objeto params con los props ya puestos con el valor que quieres por ejemplo.

this.$router.push({ name: 'users', params: {title: 'test title' }})

En este caso dentro del componente Users hay definido un prop llamado title.

IMPORTANTE. Solo se pueden pasar props a rutas navegando mediante el nombre del componente name. Si decides navegar a una ruta usando su URL, no se podrán pasar props de esta forma.

Conclusiones

Con los props vas a poder hacer componentes genéricos como botones, encabezados, etc que te van a permitir personalizarlos y usarlos en las condiciones que necesites, por ese motivo son muy usados en las librerías de componentes.

En posteriores capítulos veremos el caso contrario, es decir, pasar información desde el componente hijo al componente padre (por ejemplo saber cuando se pulsa un botón en el componente hijo desde el componente padre).

» Leer más, comentarios, etc...

Fixed Buffer

Novedades de C#8: Interfaces. ¿Qué podemos esperar?

diciembre 17, 2019 09:00

Tiempo de lectura: 5 minutos
La imagen muestra el logotipo de c# 8.0 para la entrada de interfaces

Llegamos a la última entrada del año y es el momento de acabar con las novedades «grandes» que nos ha introducido C#8 hablando de las interfaces. En entradas anteriores hemos hablado de Índices y Rangos, del nuevo Pattern Matching y de IAsyncEnumerable que aunque no son las únicas novedades que trae C#8, sí son las más grandes (a mi humilde criterio siempre).

Esta quizás sea una entrada controvertida y es que este cambio no ha gustado a todos por igual en la comunidad.

¿Qué es una interfaz?

Hasta ahora cuando hablábamos de una interfaz nos estábamos refiriendo a un «contrato» que incluía una serie de métodos que toda clase que la implementase se comprometía a cumplir (o de lo contrario se genera un error de compilación).

public interface IVehiculo
{
    void Avanzar();
    void Girar(int grados);
}

public class Coche : IVehiculo
{
    public void Avanzar()
    {
        //....
    }

    public void Girar(int grados)
    {
        //....
    }
}

public class Bicicleta : IVehiculo
{
    public void Avanzar()
    {
        //....
    }

    public void Girar(int grados)
    {
        //....
    }
}

Precisamente este «contrato» nos daba la capacidad de trabajar abstrayéndonos completamente de que había por debajo al utilizar la interfaz. Para nosotros trabajando con la interfaz no nos importa si lo que hay detrás es una bicicleta o un coche. Un caso claro es cuando recibimos esa interfaz en un método.

void AvanzarHacia(IVehiculo vehiculo, int grados)
{
    vehiculo.Girar(grados);
    vehiculo.Avanzar();
}

Hasta aquí no te estoy contando nada nuevo, esto son las interfaces de siempre.

¿Por qué han cambiado las interfaces en C#8?

Hasta ahora una interfaz que ya se había distribuido y se estaba utilizando era inmutable. En el ejemplo del código anterior, si la estamos utilizando y dentro de un tiempo nos damos cuenta de que hay que añadir un nuevo método y lo añadimos, vamos a romper la compilación. Esto es porque precisamente la interfaz nos obliga a implementar todos sus métodos.

¿Imaginas que pasaría si el equipo de desarrollo de Microsoft se diese cuenta de que IDisposable necesita añadirle más métodos?

La respuesta es muy simple: Romperían todo el ecosistema. Todos los proyectos que tengan alguna clase que implementase IDisposable dejarían de compilar al instante hasta que se añadiese ese método extra de la interfaz. Si echamos un ojo al código de .Net o .Net Core podemos comprobar que son miles las clases que implementan IDisposable. Eso solo dentro del propio framework, si hablamos de su uso por parte de los desarrolladores en librerías y productos, el número de clases que se romperían es tan grande que no podemos ni imaginarlo.

Por supuesto un cambio así que es un breaking change en toda la expresión del término no se puede aplicar sin hacer un cambio de versión «major». Hacerlo sin actualizar la versión sería el final del lenguaje, pero aun actualizando la versión… ¿Estarías dispuesto a rehacer todo el código de un proyecto solo para actualizarlo de versión? ¿Y si no ha cambiado solo IDisposable? Desde luego la acogida de esa nueva versión sería mucho más complicada. Aquí es donde entran los cambios que nos trae C#8. Pero…

¿En que han cambiado las interfaces con C#8?

Precisamente la novedad que tenemos con respecto a las interfaces es que ahora si pueden tener una implementación por defecto. Espera…

WHAT???!! ¿Si una interfaz tiene una implementación por defecto no es una clase abstracta?

Sí. Con anterioridad a C#8 si queríamos tener una implantación por defecto elegíamos una clase abstracta y sino una interfaz. La separación estaba clara. Esto ahora ha cambiado y si por ejemplo después del tiempo nos diésemos cuenta de que la interfaz del ejemplo que hemos puesto no es útil porque en vez de especificar los grados queremos solo decirle también que gire a izquierda y derecha podríamos hacer una interfaz así:

public interface IVehiculo
{
    void Avanzar();
    void Girar(int grados);
    void GirarDerecha() => Girar(90); //Implementación por defecto
    void GirarIzquierda() => Girar(-90); //Implementación por defecto
}

El hecho de actualizar esta interfaz no va a provocar un error de compilación ya que si no encuentra una implementación en la clase para ese método va a utilizar la implementación por defecto.

Este cambio en las interfaces de C#8 no viene solo. Hasta ahora todos los métodos de una interfaz pública eran públicos y esto tenía todo el sentido. Si se definían unos métodos que la clase tenía que implementar, lógicamente tenían que ser accesibles. Como ahora ya no es obligatorio que esos métodos estén todos implementados.

Nuevos modificadores de las interfaces en C#8

Precisamente de lo que hemos visto anteriormente el siguiente paso natural es que una interfaz no sea completamente pública, puede que necesitemos cierta lógica no pública para poder manejar esos casos por defecto. Es por eso que ahora los modificadores de disponibles son:

Con ellos vamos a poder aplicar la lógica necesaria para poder generar funcionamiento por defecto para las interfaces. Puedes ver un ejemplo en que ofrece el equipo de dotnet directamente en github.

¿Cómo se comporta este funcionamiento por defecto?

En primer lugar, toda la implementación está en la propia interfaz y no en la clase que la implementa. Estas implementaciones por defecto no son un tipo de herencia múltiple encubierta. Es decir, si la interfaz tiene una implementación por defecto de un método que la clase no tiene, solo será accesible desde la interfaz y no desde la clase. De hecho, no es una multiherencia ni si quiera a nivel de interfaz, si por ejemplo heredamos nuestra interfaz de dos o más que si tienen el mismo método por defecto, vamos a necesitar especificar cual exactamente queremos utilizar o se generará un error de ambigüedad. Por ejemplo:

public interface IVehiculoTerrestre
{
    void Avanzar() => Console.WriteLine("Avanzar por tierra");
}

public interface IVehiculoNaval
{
    void Avanzar() => Console.WriteLine("Avanzar por mar");
}

public interface IVehiculoHibrido : IVehiculoTerrestre, IVehiculoNaval
{
}

public class VehiculoHibrido : IVehiculoHibrido
{
}


IVehiculoHibrido vehiculo = new VehiculoHibrido();

vehiculo.Avanzar(); //Error de ambigüedad. No compila
((IVehiculoTerrestre)vehiculo).Avanzar(); //Avanzar por tierra
((IVehiculoNaval)vehiculo).Avanzar(); //Avanzar por mar

Y… ¿Dónde está el problema entonces?

De lo que hemos visto más arriba podemos sacar en claro que, aunque es una utilidad interesante y que va a permitir actualizar las interfaces sin miedo a romper un montón de código. El problema viene de que la línea que he separado históricamente las interfaces y las clases abstractas se ha difuminado tanto que se ha convertido en buenas prácticas que en una separación real de funciones. Precisamente por eso muchos opinan que es una característica que dificulta el lenguaje y lo hace confuso porque el lenguaje ya tenía una manera de resolver eso antes de las interfaces de C#8, las clases abstractas. Si alguien quería proveer una implementación por defecto era una clase abstracta lo que debía usar.

Otra de las razones de los detractores de este cambio es que al haber una implementación por defecto ya no hay errores de compilación si la interfaz que estas implementando en tu clase tiene métodos no implementados en la propia clase. Esto puede llevar a errores difíciles de detectar cuando trabajas con la abstracción que te da una interfaz… Imagina que tu método recibe una interfaz y cuando llamas a su método ‘x’ actúa una implementación por defecto sin que tú te estés dando cuenta… Dependiendo de la situación esto puede ser un gran problema.

Personalmente pienso que una característica como las interfaces de C#8 no es ni buena ni mala por si sola. Puede ser muy útil en determinados casos y provocar fallos muy graves en otros y es nuestra labor como desarrolladores conseguir que el mundo sea mejor.

La imagen muestra una escena de Spiderman donde el Tio Ben dice su famosa frase: "Un gran poder conlleva una gran responsabilidad"

¿Qué opinas tú de esta nueva característica? ¿Crees que mejora o que empeora el lenguaje? No dudes en dejar un comentario dando tu opinión sobre el tema.

**La entrada Novedades de C#8: Interfaces. ¿Qué podemos esperar? se publicó primero en Fixed Buffer.**

» Leer más, comentarios, etc...

Coding Potions

Cómo usar el Vue router y ciclo de vida de los componentes

diciembre 12, 2019 12:00

Introducción

Vue, como muchos otros frameworks para el desarrollo web (Angular, React) también tiene un sistema para crear rutas. Como hemos explicado en capítulos anteriores, Vue permite crear páginas web SPA, es decir, el usuario tiene la sensación de que está navegando entre las páginas pero lo que de verdad ocurre es que Vue por debajo está cambiando el HTML al vuelo sin tener que recargar la página.

Por ejemplo, si estás en la página principal de una aplicación SPA y navegas a la URL /users, Vue lo que hace es renderizar el componente asociado a esa ruta en la página web. Como ese componente ya lo tiene cargado el cambio entre páginas es inmediato.

Para conseguir esto, el creador del Vue router (que es español por cierto) ha desarrollado un conjunto de utilidades para crear rutas en Vue. En un fichero configuras las rutas que quieres crear y las asocias a componentes que se cargarán cuando el usuario navegue a la ruta.

Cómo funciona el vue-router

Si has instalado Vue con el Vue CLI y has seleccionado el método manual, te habrá salido la opción de instalar el vue router. Si no lo tienes instalado no te preocupes porque vamos a ver cómo se instala.

Antes de nada, para saber si lo tienes instalado mira en el archivo package.json si tienes dentro de la sección dependencias un paquete llamado:

"vue-router": "^3.0.3"

En mi caso tengo la versión 3.0.3 pero tú puedes tener otra versión, no pasa nada.

Si no tienes instalado vue-router lo que tienes que hacer es instalarlo dentro del proyecto, para ello, mediante línea de comandos ejecuta:

npm install vue-router --save

A continuación, lo que tienes que hacer es crear un archivo llamado router.js dentro de la carpeta src, al mismo nivel que el main.js.

Dentro del archivo lo primero que tienes que hacer es importar Vue y vue-router y hacer que vue use el router, así:

import Vue from "vue";
import VueRouter from 'vue-router'
    
Vue.use(VueRouter)

Si el vue-router lo instalaste con vue-cli al crear el proyecto este archivo ya lo tendrás.

Las rutas se crean en un array con esta sintaxis:

const routes = [
  {
    path: "/",
    name: "home",
    component: Home
  }
];

En este caso hay una sola ruta, la ruta raíz, en otras palabras, la que se abre al abrir la página.

Como ves cada ruta se crea en forma de objeto con propiedades. En el path indicas la ruta que quieres que se use (por ejemplo “/users”), en el name puedes darle un nombre a la ruta para identificarla posteriormente, y en el component pones el componente que quieres que se use en esa ruta, así que tendrás que importarlo de esta forma:

import Home from "./views/Home.vue";

Si te fijas las rutas las importo desde una carpeta llamada views dentro del src. Esto se hace para poder identificar bien qué componentes se encargan de cada una de las vistas. Si quieres localizar el componente que se encarga de renderizar la ruta /users lo único que tienes que hacer es ir a carpeta views para buscar un componente llamado Users o parecido.

Debajo de las rutas, en el mismo fichero, tienes que meter esto para que Vue pueda usar las rutas que acabas de crear:

const router = new Router({
  mode: "history",
  routes
});

export default router;

Se hace el export para que en el main.js podamos pasar las rutas a Vue, es decir, dentro del archivo main.js del src tienes que meter esto para que funcione:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

new Vue({
  router,
  render: h => h(App)
}).$mount("#app");

Y listo, con esto ya tendríamos una ruta funcionando en Vue. Si quieres añadir más rutas lo que tienes que hacer simplemente es añadir más objetos al array de rutas del archivo router.js que acabas de crear, así:

import Vue from "vue";
import VueRouter from 'vue-router'

import Home from "./views/Home.vue";
import Users from "./views/Users.vue";

Vue.use(VueRouter)

const routes = [
  {
    path: "/",
    name: "home",
    component: Home
  },
  {
    path: "/users",
    name: "users",
    component: Users
  }
];

const router = new Router({
  mode: "history",
  routes
});

export default router;

Cuando navegues a la ruta http://localhost:8080/users se cargará el componente Users de la carpeta views y por tanto se verá lo que haya en ese componente. Al navegar a http://localhost:8080/ se cargará el componente Home.

Cómo navegar a rutas desde componentes

Otra cosa muy importante de una aplicación web es la posibilidad de navegar desde los componentes a otras rutas, al pulsar un botón por ejemplo, o cuando ocurra una acción. Vue router también permite navegar desde código a las rutas que tienes definidas.

Al instalar vue router, se crea una etiqueta HTML especial para poder navegar a las rutas, llamada router-link. Veamos un ejemplo:

<router-link to="/about">Link a la página de about</router-link>

También puedes navegar a una ruta usando su nombre (el parámetro name que has configurado antes en el array de rutas), esto es interesante porque si decides cambiar la url de la ruta, vas a poder seguir navegando correctamente porque el nombre sigue siendo el mismo.

<router-link :to="{ name: 'user'}">User</router-link>

Fíjate que he puesto los dos puntos antes de el atributo to para poder pasar un objeto de javascript.

Incluso puedes pasar una variable definida en el data o variable computada para decidir a qué ruta ir:

<router-link :to="user">User</router-link>

En este caso se creará un enlace a la ruta definida en la variable user.

Esta etiqueta renderizará una etiqueta <a> con el href ya configurado a la ruta que especifiques dentro del to.

Router push

Esta forma es la más utilizada para navegar hacia rutas desde la lógica de un componente. Lo que hace simplemente es navegar a la ruta que le pasemos al método. Igual que pasaba con el router link, aquí también puedes pasar un objeto para poder usar el nombre de la ruta en lugar de su ruta.

// Con la URL de la ruta
this.$router.push('home')

// Con el nombre de la ruta
this.$router.push({ path: 'home' })

No necesitas importar nada, Vue creará la variable $router dentro del this con la instancia del router para que puedas navegar desde los componentes.

Router replace

Funciona igual que router push pero con una diferencia. Con el push se navega a la ruta y se guarda la ruta actual en la historia del navegador para que el usuario pueda volver a la página anterior pulsando sobre el botón de atrás en el navegador.

Con router replace el usuario al pulsar sobre el enlace no podrá volver a la página anterior. Esto se suele usar por ejemplo cuando haces un logout del usuario o cuando queremos sacarlo de una página y no queremos que pueda echar hacia atrás la historia del navegador.

// Con la URL de la ruta
this.$router.replace('home')

// Con el nombre de la ruta
this.$router.replace({ path: 'home' })

Router go

Pasando un número, sirve para decidir si queremos retroceder o avanzar en la historia del navegador del usuario.

// Esta instrucción lo que hará sera hacer que el usuario vuelva a la página anterior.
this.$router.go(-1)

// Esta instrucción hará que el usuario avance una página en la historia del navegador en caso de que se pueda
this.$router.go(1)

Ciclo de vida de un componente en Vue

Visto cómo crear rutas en Vue, el siguiente paso en tu aprendizaje es comprender lo que ocurre cuando entras en la ruta.

Los componentes, en todos los frameworks, tienen ciclo de vida, en otras palabras, los componentes, se crean, se cargan, se insertan en ls vista y se destruyen, y lo bueno de esto es que puedes ejecutar código en cada uno de estos estados.

Aunque Vue tiene muchos métodos para distintos estados, voy a explicar los mas habituales, los que acabarás usando siempre, ya que el resto se usan mucho menos. En este gráfico puedes ver el ciclo que sigue Vue para los componentes.

Ciclo de vida de los componentes de Vue: Created, mounted, destroyed

Por cierto, una limitación que tienen estos estados es que no puedes usar dentro arrow functions, no puedes hacer esto porque se pierde el contexto del this y ya no puedes acceder al data y a los métodos.

Si quieres hacer algo de este estilo tienes que guardarte el valor del this o hacer un bindeo del mismo.

() => this.fetchTodos()

Created

Uno de los que más se usan. Se ejecuta después de beforeCreated. En ese punto Vue ya ha cargado todas las opciones del componente y por tanto ya existe la sección data y los métodos. Aquí puedes hacer llamadas a variables y puedes ejecutar métodos.

La única pega es que en este punto Vue todavía no ha cargado la vista, no la ha renderizado y no puedes leer o modificar nada que afecte a ls vista porque todavía no existe. No puedes acceder al DOM de ese componente.

Este método se suele usar para realizar las llamadas API REST, por ejemplo si queremos pintar un array con información que provenga del backend lo que se suele hacer es dentro del created hacer la llamada y almacenar el valor de retorno de una de las variables del data. Como Vue es reactivo, cuando la vista esté cargada se pintará el array.

Veamos un ejemplo:

<script>
export default {
  data: () => ({
    info: null,
  }),
  created() {
    this.info = "Componente cargado";
  }
};
</script>

Si te fijas lo que se hace es declarar un método created, usualmente debajo de los métodos al final del componente.

Como vimos en los métodos, para acceder a las variables se utiliza el this, en este caso para poner un valor a una variable. También podemos llamar a métodos de la sección methods con el this e incluso podemos hacer uso de las propiedades *computadas.*

Mounted

A diferencia del created, en el mounted si que tenemos acceso al DOM, es decir, el computed se ejecuta exactamente cuando se termina de pintar la vista en la página web y por tanto desde aquí podemos hacer cambios en la vista.

<template>
  <a href="/link">link</a>
</template>
<script>
export default {
  data: () => ({
    info: null,
  }),
  mounted() {
    console.log(this.$el.querySelectorAll('a'));
  }
};
</script>

Si te fijas, para acceder a un elemento del DOM se puede hacer mediante la variable que crea Vue al renderizar el componente this.$el.

Si te salta un error porque no existe el elemento, una forma de asegurarse de que Vue ya ha renderizado todos los elementos del DOM es usar la función nextTick de Vue.

<template>
  <a href="/link">link</a>
</template>
<script>
export default {
  data: () => ({
    info: null,
  }),
  mounted() {
    this.$nextTick(() => {
      console.log(this.$el.querySelectorAll('a'));
    });
  }
};
</script>

La función nextTick lo que hace es dejar pasar un pequeño intervalo de tiempo, el correspondiente a un tick.

Updated

Con este método hay que tener mucho cuidado. Se ejecuta cada vez que se produce un cambio en el componente y cambia algo de la vista. Se recomienda aquí no hacer muchos cambios en el data o en la vista y utilizar propiedades computadas o watchers que veremos más adelante.

<script>
export default {
  data: () => ({
    info: null,
  }),
  updated() {
    console.log("Componente actualizado");
  }
};
</script>

Hay que tener cuidado porque puede que se ejecute más veces de las que en realidad queremos.

Destroyed

Como puedes suponer, el destroyed se ejecuta cuando eliminamos un componente, cuando ya no esta cargado. Esto puede pasar cuando cambias de vista o cuando o cuando se borra un componente del DOM porque ha sido ocultado con v-if.

<script>
export default {
  data: () => ({
    info: null,
  }),
  destroyed() {
    console.log("Componente eliminado");
  }
};
</script>

Conclusiones

Existen muchos más métodos en el ciclo de un componente de Vue como el beforeCreated o el beforeMounted, pero no he querido explicar más porque no quiero alargar demasiado el artículo. Además los que te he enseñado aquí son los que más va a usar y los otros en raras ocasiones te harán falta.

Hasta ahora ya hemos visto cómo se crean componentes, cómo crear rutas de la web y cómo controlar el ciclo de vida de los componentes, falta ver otra de las partes más importantes del desarrollo de aplicaciones wen con componentes y es la comunicación entre ellos, el paso de información. En posteriores capítulos abordaremos esta cuestión, no te lo pierdas.

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin