Picando Código

Grupos y capturas reemplazando texto con expresiones regulares

noviembre 20, 2024 11:30

Las expresiones regulares nos permiten agrupar patrones y "capturarlos". Luego podemos usar el valor de esos grupos con "back references" (¿referencias posteriores?). Últimamente vengo usando cada vez más replace-regexp en Emacs y aprovechando que tuve que hacer uso de estas características, lo comparto por acá.

Estaba editando código Ruby y dado un Array de Hashes, quería reemplazar todas las llaves en los Hashes del tipo String a Symbol:

{ "nombre": "Fernando" } => { nombre: "Fernando" }

En Emacs, al presionar Alt x - replace-regexp Enter entramos a un mini-buffer donde tenemos que ingresar una expresión regular. Lo que quería machear en este caso es "nombre": y reemplazarlo por nombre:. La expresión regular que usé fue:

"\([a-z_]+\)":

Las llaves tenían valores con letras y guión bajo ("snake case"). Así que uso [a-z_]+ para machear una o más ocurrencias de letras de a a z y _. Con el paréntesis agrupo el valor en snake case, en el mini-buffer de replace-regexp los signos de paréntesis tienen que estar escapados con \. Ahí me queda un patrón agrupado.

Al presionar enter, replace-regexp pregunta con qué queremos reemplazar la expresión regular. Haciendo uso de las capturas y "back reference", lo que agrupé en el paréntesis está disponible como \1. Así que como quiero que "nombre": se transforme en nombre:, puedo usar: \1:.

En el caso que tengamos más de un patrón agrupado, podemos usarlos con \1, \2 y así sucesivamente.

El ejemplo es bastante sencillo, pero al estar trabajando en una base de código amplia, reemplazar con expresiones regulares ahorra un montón de tiempo. Generalmente tiendo a usar replace-string en Emacs, que es más un reemplazo literal. Sinceramente replace-regexp me intimidaba un poco (creo que más que nada porque no tenía muy claro qué caracteres había que escapar y me costaba armar expresiones regulares). Pero cuando funciona nos ahorra un montón de tiempo y trabajo, y un poco da esa sensación de tener superpoderes.

Las expresiones regulares son una herramienta muy potente que valen la pena aprender. En mi experiencia son bastante portables generalmente, el conocimiento adquirido sirve en Ruby, Bash, awk, sed y por supuesto Emacs 🙂

El post Grupos y capturas reemplazando texto con expresiones regulares fue publicado originalmente en Picando Código.

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

Variable not found

Los nuevos métodos LINQ en .NET 9: CountBy(), AggregateBy() e Index()

noviembre 19, 2024 07:05

Nuevos métodos LINQ en .NET 9

Me encanta LINQ. Bueno, ciertamente la sintaxis de consultas integrada en el lenguaje no la uso desde hace muchísimos años, pero los métodos de extensión que proporciona son una maravilla para recorrer y operar sobre colecciones de datos en memoria o almacenados en sistemas externos.

LINQ ha sido uno de los objetivos de .NET 9. Además de mejorarlo internamente y optimizar su rendimiento (en algunos casos, considerablemente), se han incluido tres nuevos métodos, CountBy(), AggregateBy() e Index(), que amplían las posibilidades que teníamos hasta el momento, simplificando la escritura de código y aumentando su legibilidad en algunos escenarios.

Vamos a verlos en detalle.

CountBy()

Hasta .NET 9, no era trivial usar LINQ para contar cuántas veces se repite un valor en una colección. Había primero que llamar a GroupBy() para obtener las agrupaciones, y luego a Count() para contar los elementos, por ejemplo como se muestra a continuación:

var numbers = new[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 4 };
var counts = numbers
                .GroupBy(n => n)
                .Select(g => new { Number = g.Key, Count = g.Count() });

foreach (var count in counts)
{
    Console.WriteLine($"{count.Number}->{count.Count}");
}
1->3
2->3
3->3
4->1

Con el nuevo método CountBy() podemos simplificar el código anterior, ya que nos permite contar directamente las repeticiones de cada valor en la colección:

var numbers = new[] { 1, 2, 3, 1, 2, 3, 1, 2, 3, 4 };
var counts = numbers.CountBy(n => n);
foreach (var count in counts)
{
    Console.WriteLine($"{count.Key}->{count.Value}");
}

La llamada a CountBy() devuelve un IEnumerable<KeyValuePair<TKey, int>>, o en castellano, una colección de pares clave-valor con las propiedades Key y Value, donde Key es el valor de la colección y Value el número de veces que se repite.

Pero lo que es aún mejor, es que además CountBy() es más eficiente que la combinación de GroupBy() y Count(), ya que no necesita crear un grupo para cada valor, sino que va contando las repeticiones a medida que recorre la colección.

Esto podemos comprobarlo usando de nuestro viejo amigo BenchmarkDotNet, con el que podemos ver que es cuatro veces más rápido y asigna tres veces menos memoria:

| Method       | Mean      | Error     | StdDev    | Gen0   | Allocated |
|------------- |----------:|----------:|----------:|-------:|----------:|
| UsingGroupBy | 28.777 ns | 0.3078 ns | 0.2729 ns | 0.0229 |     192 B |
| UsingCountBy |  6.940 ns | 0.1630 ns | 0.1601 ns | 0.0076 |      64 B |

AggregateBy()

El método AggregateBy() es similar a CountBy(), pero en lugar de contar las repeticiones de cada valor, permite realizar una operación de agregación. Es decir, de la misma forma, nos permite eliminar el GroupBy() intermedio y realizar la operación de agregación directamente.

Para ilustrarlo con un ejemplo, en el siguiente bloque de código vemos una colección de departamentos con el nombre, la ciudad y el número de empleados, y cómo podemos sumar el número de empleados por ciudad usando los operadores de LINQ anteriores

record Department(string Name, string City, int Employees);

Department[] departments =
{
    new ("Sales", "New York", 12),
    new ("Marketing", "Los Angeles", 5),
    new ("Development", "San Francisco", 22),
    new ("Marketing", "London", 3),
    new ("Sales", "Seattle", 8),
    new ("Development", "Sevilla", 1),
    new ("Human Resources", "London", 3),
    new ("HQ", "New York", 3),
};

var employeesByCity = departments
                         .GroupBy(g => g.City)
                         .Select(g => new 
                                    { 
                                        City = g.Key, 
                                        TotalEmployees = g.Sum(d => d.Employees) 
                                    });

foreach (var city in employeesByCity) {
    Console.WriteLine($"{city.City}->{city.TotalEmployees}");
}

El resultado obtenido sería el mostrado a continuación:

New York->15
Los Angeles->5
San Francisco->22
London->6
Seattle->8
Sevilla->1

En .NET 9, podemos simplificar el código anterior usando AggregateBy():

var employeesByCity = departments.AggregateBy(
    d => d.City,                                            // Clave de agrupación
    0,                                                      // Valor inicial (seed)
    (total, department) => total + department.Employees     // Función de agregación
);

// AggregateBy() devuelve una colección de KeyValuePair:
foreach (var city in employeesByCity)
{
    Console.WriteLine($"{city.Key}->{city.Value}");
}

Y como vemos con BenchmarkDotNet, AggregateBy() es diez veces más rápido que la combinación de GroupBy() y Sum(), y no asigna memoria alguna:


| Method           | Mean      | Error     | StdDev    | Gen0   | Allocated |
|----------------- |----------:|----------:|----------:|-------:|----------:|
| UsingGroupBy     | 30.347 ns | 0.6078 ns | 0.6756 ns | 0.0153 |     128 B |
| UsingAggregateBy |  3.092 ns | 0.0252 ns | 0.0211 ns |      - |         - |

Index()

Ahora, imaginemos que, cuando recorremos una colección, necesitamos saber el índice de cada elemento. Hasta .NET 9, la forma de hacerlo era algo engorrosa, ya que había que llevar la cuenta de los índices manualmente, o bien usar consultas LINQ adicionales para conseguirlo. 

Por ejemplo, en el siguiente código vemos cómo podríamos hacerlo en versiones anteriores del framework usando Select() para realizar una proyección de los elementos de la colección junto con su índice en una tupla:


string[] numbers = ["Zero", "One", "Two", "Three", "Four", "Five"];

var numbersWithIndex = numbers.Select((n, i) => (i, n));
foreach (var (index, value) in numbersWithIndex)
{
    Console.WriteLine(index + "->" + value);
}

Como es de esperar, la salida por consola será la siguiente:

0->Zero
1->One
2->Two
3->Three
4->Four
5->Five

Con .NET 9, podemos usar Index() para obtener una tupla como la anterior, con el índice y el elemento de la colección, pero de forma más sencilla y legible:

var numbersWithIndex = numbers.Index();
foreach (var (index, value) in numbersWithIndex)
{
    Console.WriteLine(index + "->" + value);
}

En esta ocasión, BenchmarkDotNet no indica un aumento de rendimiento dramático o un uso de memoria más eficiente, aunque alguna mejora sí que hay:

| Method      | Mean     | Error    | StdDev   | Gen0   | Allocated |
|------------ |---------:|---------:|---------:|-------:|----------:|
| UsingSelect | 66.00 ns | 0.481 ns | 0.450 ns | 0.0143 |     120 B |
| UsingIndex  | 60.48 ns | 0.886 ns | 0.829 ns | 0.0124 |     104 B |

¡Y eso es todo! Espero que el artículo os haya haya servido para descubrir los nuevos métodos LINQ que se han añadido en .NET 9 y que os resulten útiles en vuestros proyectos.

Publicado en Variable not found.

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

Picando Código

Comics: Transformers por Daniel Warren Johnson

noviembre 18, 2024 03:00

Descubrí a Daniel Warren Johnson de casualidad cuando agregué el cómic Beta Ray Bill de 2021 a mis suscripciones. Había visto algún preview y me llamó la atención el arte. Me encantó, tanto el dibujo como el guión estaban muy buenos y el estilo particular de ilustración me voló la cabeza.

Agregué al autor a mi lista de artistas a seguir, para prestar atención a cosas nuevas en las que fuera trabajando. Más adelante lo leí en Jurassic League, una versión de la Liga de la Justicia donde los personajes son todos dinosaurios. También me gustó, tomándolo tan en serio como se puede tomar la premisa, divierte.

Cómics Transformers

Me entusiasmó mucho cuando leí que Johnson iba a escribir y dibujar el reboot de Transformers en Skybound (Image Comics). Tenía muchas expectativas, y decidí seguir el título a medida que iba saliendo mes a mes. Me atrapó desde el primer número. No había leído cómics de los Transformers antes, pero esta es una de las mejores series de cómics en general que he leído, y de las mejores interpretaciones de estos personajes.

El fin de semana pasado decidí releer desde el primer número y ponerme al día con los números más recientes que tenía pendientes. Los primeros 12 números abarcan dos arcos argumentales. El inicial del 1 al 6 está coleccionado en el TPB "Robots in Disguise" con fecha Mayo de 2024. Como es de esperarse, es una introducción al mundo y sus personajes. Es perfecta para entrarle de cero a los Transformers, totalmente innecesario tener conocimiento previo de los personajes, su mitología, continuidad o generación de juguetes.

Comics Transformers - 1 al 12

Comics Transformers - 1 al 12

A pesar de que se presentan personajes con nombres familiares, no es necesario saber quiénes fueron en línea de juguetes, dibujo animado o películas anteriores. En una versión de nuestro planeta Tierra, dos jóvenes humanos salen a ver la luna llena con su telescopio, cuando un terremoto los lleva a la nave enterrada de los Autobots. Ahí vemos cómo Teletraan One, la computadora del Arca de los Autobots, va reparando Autobots y Decepticons por igual. El responsable es Jetfire, que cargó la computadora con formas familiares a las especies nativas del planeta, lo que explica que se transformen en vehículos y objetos reconocibles. Y así empieza la cosa.

El arte es espectacular. Mezcla el aspecto técnico y detallado que se esperaría de un cómic de Transformers con una crudeza que hacen una combinación inigualable. DWJ tiene un estilo muy propio, expresivo, casi infantil o sucio, dicho como el mejor de los complementos. Como que a pesar de la trayectoria del artista y el peso del título, no se alínea con una uniformización artística de la industria y pone su toque muy personal en el dibujo. Demoro en leer sus cómics, porque me tomo mi tiempo apreciando los detalles en el arte.

Incluso siendo robots, los personajes transmiten mucho en los gestos de sus caras y expresiones con acciones súper dinámicas. Los "ruidos" que acompañan son protagonistas del despliegue y están imponentes, nos acercan todavía más a vivir la historia. Esa técnica de expresar sonidos con letras grandes y estilizadas, que DWJ hace muy bien. La diagramación de los paneles es otro punto a destacar. Este tipo sabe contar historias con viñetas. Sabe demostrar lo que está pasando (sin usar texto) y explica muy bien visualmente los movimientos. Más allá de dibujar bien, la forma en que se presentan los personajes está muy bien pensada. Nunca es difícil seguirlo, o saber qué panel o diálogo viene después.

La acción es increíble, particularmente en este primer arco la presentación del contexto y los kilos de acción están perfectamente equilibrados. Es muy divertida y se disfruta cada panel. ¡Los robots hacen movimientos de lucha libre! Daniel Warren Johnson es un apasionado por la lucha libre (tiene un cómic sobre el tema: Do a Powerbomb) y lo transmite a través robots gigantes reventándose contra el piso.

El arte está complementado con el excelente trabajo de rotulado de Rus Wooton. Acompaña muy bien con detalles que lo hacen destacar al transmitir distintas voces. Es genial cuando el estilo en que están escritos los diálogos suma tanto a la presentación. El coloreado está a cargo de Mike Spicer, que también hace un trabajo fantástico en la ambientación de cada panel.

Si bien hay muchos personajes y nombres que recordar, acá se presentan con el objetivo de servir desenlaces, y no para vendernos un juguete. La historia de origen se ha contado más de una vez, pero éstos definitivamente no son los Autobots, Decepticons y humanos de la década de 1980. Es una historieta con temas duros. Los Decepticons son malos de verdad, asesinando gente sin pensarlo dos veces, ya sea pisándolos o reventándolos como moscas. Starscream es un psicópata, y no resulta tan odioso como en otras versiones. Los malos son fríos, calculadores, brutales. Muy lejos de la inocencia de la serie animada original. 

Optimus PrimeLa muerte en los cómics nunca es definitiva. Pero DWJ no duda en matar a un personaje cuando ayuda a la trama. Optimus Prime murió y volvió como 3 veces en animación, pero en este caso parece que los que mueren, se quedan muertos.

La relación de los Autobots con los humanos se va desarrollando naturalmente a través de la empatía y los puntos en común con el otro. Hay evidentes paralelismos entre las dos especies, y cómo se enfrentan a temas más serios como la pérdida, que está muy presente. Algo que los lectores van a tener que enfrentar también, si muere uno de sus Transformers favoritos. Retrospectiva de la película de 1986...

No por esto es un cómic triste o depresivo. Hay diversión, acción y optimismo para rato. Ya sea por los valores que caracterizan a los buenos o sorprendiéndonos con arrancarse un brazo para reventar a un Decepticon.

No sé si se puede decir que es un cómic para adultos por la crueldad y los temas "serios" que trata. No deja de ser un cómic de robots gigantes de otro planeta que se transforman en autos, aviones y demás. De repente es un cómic para adultos que a pesar de estar por cumplir 40 años, todavía están esperando a ver cuándo se empiezan a sentir como adultos. Y por eso me gusta tanto... ¡Maduro! ¡Eso! Es un cómic más "maduro".

En una de las columnas de cartas de los lectores, el autor comenta cómo es difícil escribir a personajes como Optimus Prime que no demuestran emociones con la cara. Por esto recurre a alternativas tanto en el guión como en el arte. Hay varias escenas con Optimus que son legendarias y está muy bien caracterizado. Y no lo digo en relación a otras versiones, sino como personaje interesante y multi dimensional en este título por sí mismo.

En este primer arco se empieza a filtrar el resto del universo Energon que incluye a G.I. Joe, pero muy de a poco. Nada que ver con la forma tan obscena de Marvel con sus crossovers y tie-ins. Son funcionales a cada momento, y los autores mismo han afirmado que este es un título auto-contenido y no nos perdemos de nada por no leer el resto.

El número 6 cierra con la acción subiendo varios niveles y estableciendo el estado de las cosas en el Universo Energon. Hay potencial para contar mucho más y presentar personajes nuevos. Si bien no es necesario haber consumido la franquicia antes, se premia el conocer algo del material previo, con grandes guiños al legado como el siguiente:

Transformers - You got the touch...

Al llegar a este panel hay que detener la lectura y reproducir en nuestro reproductor de música favorito la canción "The Touch" de Stan Bush para seguir leyendo.

Daniel Warren Johnson estuvo a cargo del guión y del arte por los primeros 6 números. Después fue reemplazado en el arte por Jorge Corona. Si bien es distinto, también es excelente y sigue muy bien el estilo establecido por DWJ. Espero que continúe en la serie. En un número especial extra Energon Universe #1, se cuenta la historia de dónde está uno de los personajes "grandes" que aparece apenas en un panel en el título principal. El arte está dibujado por Ryan Ottley, y es un estilo -para mí- que sería mucho más "genérico" y esperado en un cómic de Transformers. Contrasta y ayuda todavía más a apreciar el arte de DWJ y Jorge Corona.

En el segundo arco se empieza a expandir un poco más el universo, vemos las primeras imágenes de Cybertron y algún flashback. Ya establecidos los personajes, se empieza a desarrollar y extender la historia. Nos mantiene enganchados a ver qué se viene mientras se lidia con las consecuencias de lo ocurrido. Hay más indicaciones que diferencian a estos Transformers de otras versiones a lo que se entra más en las distintas personalidades y relaciones. Pero mantiene lo que los hace divertidos. Me encantó ver a los Transformers haciendo surf de nuevo, y Jazz sigue haciendo juegos de palabra con música cada vez que habla.

A lo que te hacen agarrarle cariño y que te importen los personajes, se siente tenso cuando están en una situación peligrosa. Ya nos prepararon que acá no hay nada que evite que maten a cualquiera de los personajes. Brutal, pero muy divertido. Hasta los Decepticons tienen más dimensiones que "ser los malos". Por ejemplo cuando se maravillan con la diversidad biológica del planeta, lo que los humaniza, para después ser fríos asesinos que destruyen lo que con tal de sacar ventaja, como los humanos. Tienen desacuerdos entre ellos y perspectivas distintas, generando una dinámica más interesante.

Algo que explica el editor en el número 9 es el "Mass conversion". Transformers como Shockwave o Megatron se transforman en un arma y cambian exponencialmente su tamaño. Esto es porque algunos Transformers tienen la habilidad genética de convertir su estructura molecular, reordenando sus átomos. Esto pasa en la animación original donde Megatron aparenta ser un gigante y de repente es una pistola. O Soundwave que se transforma en un reproductor de cassettes de tamaño humano. Pero no recuerdo que alguna vez explicaran el por qué de esta "conversión de masa" en los dibujos animados mismos.

Daniel Warren Johnson anunció que su último número en Transformers va a ser el 24. Por un lado es una lástima, porque deja un nivel altísimo, y habrá que ver si quien le siga puede manterlo. Pero por otro, está bueno que los autores dejen un título sabiendo de antemano, para poder contar las historias que quieren contar y poder cerrar el ciclo. Además todavía queda casi un año de números por disfrutar. El trabajo en Transformers le dio dos premios Eisner, "Mejor serie continua" y "Mejor Escritor/Artista", así que está "certificado" que es un buen cómic. Éstos días además de Transformers, también estoy siguiendo su trabajo original The Moon is Following Us, otro cómic excelente que recomiendo.

El cómic de Transformers de Skybound es uno de los mejores cómics que vengo siguiendo. Espero cada número nuevo para ver qué va a pasar, o qué personaje nuevo va a aparecer. Lo recomiendo mucho ya sea que se hayan visto todos los capítulos animados y las películas de Michael Bay, o que lo único que sepan de la saga es que son robots gigantes que se transforman. Como comenté, es un buen punto de arranque para empezar a leer cómics de Transformers y un cómic muy divertido.

El post Comics: Transformers por Daniel Warren Johnson fue publicado originalmente en Picando Código.

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

Variable not found

Enlaces interesantes 586

noviembre 18, 2024 07:05

Alan Turing echándose las manos a la cabeza

Como todos los lunes, vamos con los enlaces a contenidos interesantes que he ido encontrando por la red.

Esta semana se ha publicado .NET 9, por lo que tenemos bastantes contenidos relacionados con el lanzamiento y novedades que encontramos en la nueva versión.

Aparte, me ha llamado la atención el post "Understanding LLMs from Scratch Using Middle School Math" de Rohit Patel, que explica cómo funcionan los modelos de lenguaje de gran tamaño (LLMs) de forma sencilla, sin grandes complicaciones matemáticas. Muy recomendable para comprender lo que se cuece bajo el capó de estas tecnologías tan fascinantes.

Y en la sección "hay gente pa tó", me ha parecido curioso el artículo "HTML: The Programming Language", una implementación de un lenguaje Turin completo con sintaxis HTML. Muy útil no parece, pero desde luego es original 😉

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

.NET MAUI / Xamarin

Otros

Publicado en Variable not found.

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

Variable not found

Mejora el rendimiento de HybridCache mediante la reutilización de objetos

noviembre 16, 2024 04:37

Un armario simulando ser la caché de nivel 1, con usuarios accediendo a ella. También hay camiones transportando contenidos al nivel 2

Hace unas semanas estuvimos echando un vistazo a HybridCache, la alternativa de .NET 9 al sistema de caché distribuida que apareció con .NET Core años atrás. Como vimos entonces, HybridCache aporta una serie de ventajas respecto a su predecesor, como la protección contra estampidas, la caché multinivel, invalidación por etiquetas, y otras mejoras.

Sin embargo, también introduce algunos inconvenientes que debemos tener en cuenta si queremos sacarle el máximo partido. En esta ocasión, nos centraremos en la penalización debido a excesiva deserialización de objetos de la caché local, un detalle que puede pasar desapercibido pero que puede tener un impacto significativo en el rendimiento de nuestra aplicación.

¿Cuál es el problema?

Para entenderlo bien, primero vamos a recordar cómo funciona la caché de dos niveles de HybridCache

Resumidamente, cuando solicitamos un objeto a HybridCache, éste lo buscará primero en la caché de primer nivel o caché local; si no está, acudirá a la caché distribuida (segundo nivel) para obtenerlo, y, además de retornarlo al usuario, lo almacenará en la caché local para futuras peticiones.

En la implementación por defecto de HybridCache, los datos siempre se almacenan en la caché local "envueltos" en objetos de tipo CacheItem<T>, siendo T el tipo de datos que estamos manejando. Se trata de una clase interna abstracta que es implementada por dos clases privadas, definidas en el interior de DefaultHybridCache:

  • MutableCacheItem<T> se utiliza cuando el tipo de datos es mutable, como objetos complejos o las que usamos muy habitualmente en nuestras aplicaciones.
  • ImmutableCacheItem<T> se utiliza cuando el tipo de datos es inmutable, como cadenas de texto, enteros u otros tipos primitivos.

En el caso de ImmutableCacheItem<T>, el objeto inmutable se almacena en la caché local como una referencia directa al mismo, de forma que los valores solicitados por los clientes se les devuelven directamente, como es habitual cuando, por ejemplo, usamos la clásica caché en memoria IMemoryCache de .NET.

Sin embargo, en el caso de MutableCacheItem<T>, lo que se guarda en la caché local es el objeto serializado, que es exactamente la misma secuencia de bytes que viajarán por la red hasta la caché distribuida o de segundo nivel. Por tanto, siempre que se recupera un objeto mutable de la caché local, éste deberá ser deserializado para poder trabajar con él.

Desde el punto de vista del consumidor esto no es un problema, porque los métodos para interactuar con HybridCache se encargarán de serializar y deserializar los objetos automáticamente y, sea cual sea el caso, siempre retornará al cliente un objeto ya materializado: o bien la referencia almacenada en la caché local, o bien el resultado de deserializar el contenido guardado.

En el caso de los objetos mutables, el hecho de que cada lectura de la caché tenga que deserializar los datos y genere un objeto nuevo es algo positivo, porque cada cliente obtendrá su propio objeto, sin posibilidad de que éste sea alterado por otro proceso. En definitiva, proporciona mayor seguridad en entornos concurrentes.

Sin embargo, estas operaciones de deserialización pueden tener un coste significativo, especialmente en entornos con un gran número de peticiones donde la mayoría de resultados se encuentren en la caché local, ya que se producirá:

  • Un mayor consumo de recursos procesamiento (CPU, básicamente).
  • Un mayor tiempo de respuesta, ya que la deserialización es una operación intrínsecamente costosa.
  • Un mayor uso de memoria, ya que se generará un nuevo objeto cada vez que un cliente acceda al contenido cacheado.

Este consumo extra no debería suponer un problema en la muchos de los casos, pero si trabajamos con objetos complejos o bajo un gran número de peticiones, puede ser un factor a tener en cuenta.

Reutilización de objetos de HybridCache

En función del tipo de datos usado, HybridCache decidirá si utilizar MutableCacheItem<T> o ImmutableCacheItem<T> para almacenarlo en la caché local.

Normalmente, usará ImmutableCacheItem<T> cuando el tipo de datos sea una cadena de texto o un tipo valor (int, bool, DateTime, etc.), y MutableCacheItem<T> en el resto de casos. Por ejemplo, la siguiente clase será considerada mutable, por lo que en caché local se almacenará una copia serializada del objeto y, por tanto, cada lectura obligará a realizar una operación de deserialización para materializarlo:

public class CachedFriend
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Si usamos tipos de datos personalizados que sabemos a ciencia cierta que son inmutables o, aunque no lo sean, si estamos seguros de que nuestro código no va a modificarlos en ningún punto, podemos forzar a que HybridCache los trate como inmutables, de forma que se reutilice la referencia al mismo objeto en lugar de generar una copia nueva cada vez que se accede a la caché.

Para indicar a HybridCache que queremos hacer esto, sólo debemos marcar el tipo de datos como sealed, para indicar que no puede ser extendido, y aplicarle el atributo [ImmutableObject(true)] para indicar que es inmutable.

El siguiente ejemplo muestra una clase cuyas instancias serían reutilizadas:

[ImmutableObject(true)]
public sealed class CachedFriend
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Con esta configuración, HybridCache almacenará las instancias de CachedFriend en la caché local como referencias directas a memoria, evitando la deserialización y la generación de nuevos objetos cada vez que se accede a la caché y, por tanto, mejorando considerablemente el rendimiento en las lecturas.

¡Espero que os resulte de utilidad!

Publicado en Variable not found.

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

Picando Código

Aventuras con resaltado de sintaxis en el blog

noviembre 14, 2024 10:30

Una de las cosas que me llevó investigación en los principios del blog fue cómo mostrar código fuente con resaltado de sintaxis en los posts. Desde un principio quería compartir cosas que iba aprendiendo a lo largo del camino con programación. Así que mostrar código de manera amigable, era uno de los requisitos.

En ese entonces ya había varias soluciones, la mayoría metiendo kilos de código JavaScript y CSS extra en el sitio, cosa que no quiero hacer. Incluso estos días, encontré que la situación no es mucho mejor. Sigo buscando la manera de hacer las cosas en este sitio de manera un poco más minimalista y performante. Y todavía no encontré un plugin o similar que me sirva para esto como yo quiero hacerlo.

Parece que el editor de bloques de WordPress tiene algo parecido a resaltado de sintaxis, y varios de los plugins mantenidos actualmente dependen de ese sistema. Así que no son una opción. Otros no se actualizan hace más de 6 meses. Estaba buscando algo que no dependa de que un plugin se siga manteniendo en el futuro, y en lo posible, una solución más genérica que si cambio de plugin o incluso sistema CMS no tenga que pasar por este proceso de nuevo.

WP-Syntax

En su momento opté por WP-Syntax, un plugin de WordPress que permite resaltado de sintaxis entre los tags HTML pre , con el atributo lang como parámetro para el lenguaje de programación: <pre lang="lenguaje">código fuente</pre>. Éste estuvo instalado en este blog por más de 17 años.

El plugin funcionaba bien, e incluso colaboré código con la opción de usarlo para los comentarios, lo que se incorporó en la versión 0.9 del plugin. Pero estaba desconforme por varias razones. En principio, no se actualiza hace mucho tiempo, por lo que no se agreguen lenguajes de programación nuevos. También resulta un poco una vulnerabilidad si el proyecto no está muy activo.

A nivel funcional era el que me resultaba más práctico, pero la apariencia del código en los posts es bastante anticuada y no pega con el diseño general del blog. Y no tenía mucha iniciativa como para ponerme a mirar cómo personalizar la apariencia.

Otro tema no menor, como comentaba en WordPress en 2024, estoy buscando formas de hacer el sitio más liviano y ligero. WP-Syntax carga un archivo JavaScript y un archivo CSS por tener el plugin cargado. Ya sea que se esté usando su funcionalidad o no. Por otro lado, el log de errores de mi servidor estaba lleno de  advertencias de código roto:

PHP Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Esto intenté ver si podía corregirlo, pero miré el código medio rápido y no tengo ni idea por qué está pasando por valor y no por referencia. Tampoco era el problema principal, sólo sumaba a reivindicar mi decisión de dejar de usar el plugin. Así que no le dediqué más tiempo.

Decidí eliminar el plugin y usar algo más "a mano", pero con mejores resultados en cuanto a rendimiento y carga de las páginas y el blog en general. Opté por pasar el código fuente por un resaltador de sintaxis a mano y pegar el HTML en cada post. Puede sonar a mucho trabajo, pero no me resulta tanto y me da mucho más control. Encontré que podía generar HTML con los estilos CSS en línea, por lo que lo puedo copiar y pegar en cualquier fuente HTML y se va a ver con los mismos colores.

Es la solución que voy a usar ahora, si me encuentro con una mejor en el futuro, volveré a pasar por todo este proceso de nuevo. No es fácil mantener un sitio web activo y actualizado por más de 17 años 😅

Aprendiendo sobre Rouge

Siguiendo con los viajes en el tiempo de este blog, en un momento escribí sobre source-highlight, una herramienta GNU para resaltado de sintaxis. Incluso en ese post lo usé para mostrar código resaltado. Al ser parte del proyecto GNU puedo asumir que va a funcionar por bastante tiempo, así que lo tengo anotado como respaldo.

Como Ruby es mi lenguaje de cabecera, busqué una herramienta similar y encontré casi instantáneamente rouge. Es la biblioteca que usa Jekyll por defecto para el resaltado de sintaxis. Entre sus formateadores, está Rouge::Formatters::HTMLInline, que renderiza el código sin clases CSS, y todo en la misma línea con etiquetas HTML span y los colores definidos en atributos style=. Ideal para correos electrónicos y otros lugares, así que el resultado en teoría se vería bien incluso en lectores RSS y con suerte para suscriptores por correo.

Al usar esta opción donde el HTML mismo se da su propio estilo (¡a la manera que hacíamos webs en los 90's! 😎), no necesito cargar archivos CSS o JavaScript extra en cada página del blog. Solamente se va a cargar ese HTML extra en cada entrada. O sea que elimino pedidos HTTP en todas las entradas al blog, y agrego unos poquitos KB de estilo en las entradas específicas que lleven código fuente resaltado.

Después de jugar un rato y probar cosas con rouge, escribí un script que iba modificando para probar distintos temas y código. Me entusiasmé y me armé un proyectito que me permitiera copiar y pegar más rápido y elegir lenguajes de programación y temas distintos fácilmente. Levanté una aplicación web mínima Sinatra con una sóla vista ERB y una ruta sola con GET para ver la vista y post para enviar el código fuente a través de un formulario con POST y procesarlo.

Por un momento me invadió ese pensamiento -a veces tan dañino- de sobre-optimizar (¿"over engineering"?) y usar algo más liviano que Sinatra. Porque es una aplicación mega sencilla que sólo necesita servir esa vista con pedidos GET y POST. Pero el tiempo que me hubiera tomado usar otra cosa de repente me distraía de terminar este proyecto. Sinatra es lo suficientemente liviano, y es un viejo conocido, así que era lo más rápido de usar para salir andando.

Mientras empezaba imaginé que debía existir algo ya hecho. Si bien no es exactamente lo mismo, encontré rouge.jneen.net, el sitio web archivado de rouge. En el código fuente aprendí algunas cosas prácticas que no encontré en la documentación. Encontré cómo definir un Lexer en base al lenguaje como parámetro. Como tengo un select HTML, me venía perfecto para definir los elementos option dinámicamente y que rouge supiera de qué le estoy hablando cuando le mando el valor elegido. En esa aplicación Rails, usan rouge::Lexer.find(@params[:language]), así que usé en mi aplicación Sinatra lexer = Rouge::Lexer.find(params['language']).

Otro código que saqué fue para listar todos los lenguajes de programación en el select de manera dinámica:

LANGS = Rouge::Lexer.all.sort_by(&:tag).map do |lexer|
  [lexer.respond_to?(:title) ? lexer.title : lexer.tag, lexer.tag]
end

Esto me da una colección de nombre/valor para usar el título para mostrar en el Select y el tag como valor que Rouge después interpreta para encontrar el lenguaje.

Así que los elementos que necesitaba fueron quedando:

  • Una textarea para pegar código fuente
  • Un menú de selección del tema: rouge provee varios temas, y si usamos el formateador HTML las clases CSS son compatibles con Pygments, un resaltador de sintaxis de Python.
  • Un menú de selección del lenguaje de programación del código fuente
  • Botón para enviar el formulario y ver tanto el resultado de cómo se ve el código fuente, como el código fuente HTML para mostrar el código fuente ingresado con resaltado de sintaxis 🤔

El resultado lo tiré en GitHub y se ve algo así:

Después de grabar este video le agregué alguna cosa más, incluyendo un input de texto para ingresar a mano un valor hexadecimal para el color de fondo de la capa de vista previa.

Actualizando el contenido

El siguiente paso (todavía en proceso) es reemplazar cada post donde usé el plugin con el nuevo formato. Y no puedo escribir sobre postear código fuente sin postear código fuente. Abrí mi gestor de base de datos y ejecuté la siguiente consulta:

SELECT * FROM `wp_posts`
WHERE post_content LIKE "%pre lang%"
AND post_type != "revision"
AND post_status = "publish";

De la tabla wp_posts, elegí los registros donde el contenido tenga pre lang, que es como funcionaba el plugin, excluir revisiones y sólo mostrar los posts publicados. Los que estén en borradores ya serán actualizados si alguna vez llego a editarlos para publicar...

Más de 150 para actualizar 🤔
¡Eso va a llevar un rato! risas de la audiencia.

Pero cambié el * en la sentencia SQL por guid, que nos da el "Post Global Unique Identifier" en WordPress. Parece un link pero WordPress nos dice que no debe usarse como un enlace (por si cambiamos el dominio o lo que sea). Pero es un link al post 😉
Desde phpMyAdmin mismo exporté los resultados a csv y ahora tengo un archivo local con una lista de enlaces, perdón guid's, de posts con código fuente en el viejo formato. Los iré actualizando de a poco hasta que no quede ni uno.

Mismo para los comentarios:

SELECT * FROM `wp_comments` WHERE comment_content LIKE "%pre lang%";

Cerca de 100... Otro CSV para exportar.

Últimos pasos y conclusión

El último paso va a ser la satisfacción de desactivar y desinstalar el plugin WP-Syntax. Está bueno depender menos de algo de terceros, y tener un poco más de control. Aparte que se siente como que le saqué algo de peso al blog. Encima, me entretuve programando algo de Ruby y aprendiendo sobre Rouge. A veces extraño SQL, así que disfruté de escribir unas consultas, por más sencillas que fueran, para encontrar los posts que debía actualizar. Y me dió algo para compartir por acá.

Eventualmente me gustaría automatizar un poco más el proceso, ya estoy pensando cómo podría hacerlo. Pero por ahora me sirve así.

El post Aventuras con resaltado de sintaxis en el blog fue publicado originalmente en Picando Código.

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

Picando Código

Mini pique: Sacar capturas de pantalla en juegos de Steam con control XBox 360

noviembre 12, 2024 10:00

Cómo sacar captura de pantallas con el control de XBox 360 mientras jugamos un juego de Steam:

XBox Guide + XBox RT

Apretando el botón de guía de XBox y el gatillo derecho (o RT), Steam guarda una imagen del juego que estamos jugando. Es el equivalente a apretar la tecla F12 en el teclado, el atajo de teclado por defecto para sacar capturas.

Esto lo aprendí recientemente jugando con la laptop conectada al televisor con mi control de XBox 360. Como juego mucho más al Nintendo Switch que con la computadora, estoy acostumbrado a guardar imágenes del juego con un botón del control de Nintendo Switch. Quería hacer lo mismo en medio del juego en Steam, y me puse a buscar en las configuraciones.

Seguro podía mapearlo a alguna combinación de botones del control. Encontré que Steam explícitamente soporta controles de XBox 360, PlayStation y Nintendo Switch Pro. Así que imagino que incluye una configuración para cada control mapeada a funcionalidades específicas de Steam. Esto lo encontramos en el menú Steam > Settings > Controller.

Para los controles de XBox, tiene un menú para definir la configuración del botón guía (que por cierto, aprendí con todo esto que es el botón del logo de XBox). Ahí podemos ver un montón de otras acciones que se pueden realizar con el control mediante el botón guía:

Steam - XBox Controller

Por si a alguien más le sirve el pique... (y para no olvidarme)

El post Mini pique: Sacar capturas de pantalla en juegos de Steam con control XBox 360 fue publicado originalmente en Picando Código.

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

Variable not found

Enlaces interesantes 585

noviembre 11, 2024 07:05

Enlaces interesantes

Con el horno aún humeante, aquí van los enlaces que hemos estado cocinando durante la semana pasada. Espero que estén en su punto y os resulten de provecho 😉 

Especialmente destacable el repaso de José Manuel Alarcón a los eventos que podemos usar en JavaScript para detectar que la página actual deja de estar activa, ante la próxima desaparición del evento 'unload'.

Y si queréis experimentar una buena explosión mental, no os perdáis el post sobre Quines, programas cuya salida es su propio código fuente. Un auténtico desafío para mentes inquietas hartas de programación tradicional y algo de tiempo libre😆

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

.NET MAUI / Xamarin

Otros

Publicado en Variable not found.

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

Picando Código

Actualización de mullvadrb - soporte para varios idiomas en Ruby

noviembre 07, 2024 06:00

En éstos días publiqué un par de versiones nuevas de mullvadrb, la interfaz de usuario de terminal para Mullvad VPN con Ruby. Me parecía que el estado de la conexión necesitaba estar más claro, así que agregué algunos saltos de línea y mejoras visuales. Particularmente cuando la VPN está desconectada. Con la magia de Ruby, obtengo el estado de mullvad status -v y meto unos encantos encadenados como reject, gsub y push de "\n" y queda mucho más visible el estado actual.

Lo otro importante que le agregué fue el soporte para internacionalización de la aplicación. Así que ahora está disponible tanto en Inglés como en Español. No sé si alguien aparte de mí usará esta gema, pero me gustó la idea de agregarle Español. Los siguientes pasos serían agregar Gaélico Escocés, Escocés, Galés e Irlandés (cuando tenga tiempo... y aprenda bien esos idiomas 😅). Hay una cadena de texto en Gaélico Escocés en la aplicación que dejé hardcodeada.

Agregar soporte para varios idiomas en Ruby es relativamente sencillo con la gema i18n. La agregué como dependencia, y creé el directorio config/locales con dos archivos YAML, uno para inglés (en.yml) y otro para español (es.yml). Cada archivo tiene como raíz la clave con el código de idioma, y después claves para cada String que quiero usar:

es:
  account_info: "Información de la cuenta"
  backend_mullvad: "Mullvad - mullvad"
  backend_using: "Usando %{backend} como backend"

Después tuve que decirle a i18n dónde encontrar los archivos de traducción:

I18n.load_path += Dir["#{File.expand_path('../config/locales', __dir__)}/*.yml"]

Para usar estas cadenas de texto en la aplicación, en cualquier parte del código fuente donde estuviera usando text traducible, lo reemplacé por I18n.t(:clave) donde clave se corresponde con una de las claves en el archivo de configuración. Por ejemplo I18n.t(:account_info) va a mostrar "Información de la cuenta". Para mostrar un valor variable, como el backend, la clave tiene el formato %{backend} y se envía el valor desde el código con I18n.t(:backend_using, backend: @backend).

El código fuente está disponible en GitHub, y se puede instalar el ejecutable con gem install mullvadrb.

Está muy bueno esto de tener un proyecto ya desarrollado, que uso casi que a diario, y poder ir agregándole cosas gradualmente cuando se me ocurre algo nuevo...

Mullvardb en Español

El post Actualización de mullvadrb - soporte para varios idiomas en Ruby fue publicado originalmente en Picando Código.

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

Picando Código

WordPress en 2024

noviembre 05, 2024 10:00

Frankenstein WordPressEste no ha sido un buen año para WordPress. No voy a entrar en detalle de lo que ha pasado, una búsqueda rápida online da la información necesaria para leer sobre el tema. Pero esto ha llevado tanto a mucha gente a renunciar a Automattic, como a reconsiderar el uso de WordPress en sí.

Personalmente vengo pensando migrar de WordPress a otra cosa desde hace años. El blog empezó en Blogger en 2007. Después de poco tiempo, migré a un hosting en linuxuruguay y eventualmente hosting propio con WordPress. Al ver que pensaba seguir con el blog, necesitaba tener control sobre la plataforma. WordPress era lo más fácil, rápido y conveniente en su momento.

Tener el blog en WordPress me permitía escribir y publicar sin demasiados problemas. Y al ser software libre, podía extenderlo con mis conocimientos en programación web y PHP. Gracias a WordPress desarrollé varios plugins que tuvieron su relativo éxito y me ayudaron a crecer como programador.

Al usar un mismo software por tantos años, uno le va encontrando cantidad de contras y detalles. Estos detalles atentan contra poder usar el software de forma cómoda y feliz. Pero la lista de contras nunca fue tan grande como para superar el esfuerzo de tener que migrar todo a otro sistema. Y también hay un montón de cosas en la lista de pros.

A lo que empezaron a agregarle cosas que no necesitaba a WordPress, se fue volviendo más pesado. Supongo que va acompañado del crecimiento del sitio, la cantidad de plugins que le fui agregando, o que estaría haciendo mal trabajo con el mantenimiento. Pero en algún momento se volvió mucho más lento de lo que considero debería ser un blog sin mucho JavaScript ni traqueos o pop ups de cookies.

Uno de los problemas más grandes de WordPress, para mí, fue la incorporación del editor de bloques "Gutenberg". Algo que agregó un montón de código para una característica que no quería ni necesitaba. Un cambio fundamental que me obligó a instalar un plugin extra para mantener la experiencia a la que estaba acostumbrado hace años.

Esa experiencia sí necesitaba mejoras, pero no ese cambio radical de paradigma, o por lo menos no para ese lado. No digo que sea un cambio inútil, pero sí que debería ser opcional. Sin dudas deben tener a montón de expertos trabajando en experiencias de usuario que dicen que la experiencia de Gutenberg es mejor que un editor de texto común. Pero no lo va a ser para todo el mundo.

Este blog lleva 17 años sobre WordPress, así que migrarlo a otro sistema me resulta bastante aterrador. Siempre que pienso en la migración, pienso en poder mantener no sólo los posts y su contenido, sino también los comentarios. Éstos días no recibo tantos comentarios en los posts como en otros tiempos, pero son parte muy valiosa de un blog. Y los comentarios "viejos" sirven de captura de pantalla de épocas en el tiempo e interacciones que agregan mucho valor.

Por más que he mirado sistemas a los que podría migrar, me imagino que cambiar de sistema va a llevar la inevitable tarea herculeana de revisar los (al momento de escribir esto 1.714) posts uno por uno para ver que quedaron "bien migrados". Pero de repente hay una solución más sencilla...

ClassicPress

Hace tiempo que venía siguiendo con interés ClassicPress:

Un sistema de gestión de contenido liviano, estable, instantáneamente reconocible libre y de código abierto. Basado en WordPress sin el editor de bloques (Gutenberg).
Hemos recortado código innecesario para mejorar el rendimiento general y la privacidad comparado con WordPress. El núcleo de ClassicPress tiene la mitad de tamaño de WordPress.

Sonaba exactamente a lo que estaba buscando. Además han sacado montones de código extra como soporte para versiones antiguas de PHP. Y no estoy seguro si lo han terminado del todo en la versión más reciente, pero han ido deprecando y eliminando el código de jQuery UI que trae WordPress, reemplazándolo por HTML5 y JavaScript nativos. Lo mejor es que cuentan con un plugin instalable en WordPress que se encarga de migrarlo a ClassicPress automáticamente. Y el proceso es reversible, de forma manual por ahora pero están trabajando en automatizarlo.

Algo interesante y relacionado a lo que mencionaba más arriba sobre Gutenberg es la información que proveen al respecto. El 25% de los usuarios todavía usan la versión 4.9 o menor de WordPress porque no quieren migrar al editor de bloque.

El plugin Classic Editor que mantiene la funcionalidad clásica, tiene más de 10 millones de instalaciones activas (aparentemente WordPress deja de contar después de tantos millones, así que no se sabe el número exacto). Personalmente ayudé con esa cifra, instalé el plugin en todos mis blogs cuando forzaron este editor de bloques.

Lo que viene pasando con WordPress me ha hecho volver a considerarlo, esta vez más seriamente. Por ahora he probado ClassicPress con otros dos blogs que tengo inactivos. El primero fue Aplicando Scrum, que al menos venía actualizando las versiones de WordPress. Instalé el plugin de migración y quedó todo funcionando perfecto de primera. No tuve que hacer ningún cambio ni arreglo. Tampoco es que tenga mucho contenido, o plugins, pero siempre está bueno probar y ver que es verdad lo que "te venden".

El otro blog que reviví fue Navegadores Web. Éste estaba en la versión 4.algo de WordPress, ya no lo actualizaba hace años. Ni siquiera era compatible con las versiones de PHP actuales del servidor de hosting. Así que tuve que ir al viejo y querido FTP y actualizar los archivos a la última versión de WordPress a mano.

El tema tenía un montón de código PHP que no funcionaba más con versiones más nuevas. En vez de arreglarlo, opté por cambiar a uno de los temas por defecto y finalmente logré que funcionara todo. Todavía quedaban algunas imágenes de cuando el sitio estaba alojado en su propio dominio. Así que tuve que ejecutar un UPDATE en la base de datos MySQL para reemplazar el viejo domino por el subdominio navegadoresweb.picandocodigo.net y quedó.

Una vez reestablecida la funcionalidad de WordPress, volví a instalar el plugin de migración a ClassicPress, y quedó todo funcionando de primera también.

Ya con éstos dos blogs migrados de WordPress a ClassicPress, me dió un poco más de confianza. Pero todavía hay un paso más que quiero dar antes de proceder con la migración. Como buen webmaster responsable, hago respaldos regulares de todo el contenido del blog. Pero nunca levanté el blog de uno de estos respaldos. Me gustaría aprovechar esta migración como excusa para probar levantarlo en local. De esta forma seguro aprendo un poco y me puedo armar un plan organizado en el caso de que un día dependa de ese respaldo porque algo salió mal. Ya contaré al respecto.

Mientras tanto, he estado trabajando un poco en el backend del blog. Hice un poco de todo: desinstalar plugins viejos, reemplazar plugins por otros más nuevos, optimizaciones y limpiezas en la base de datos, limpieza de etiquetas y borradores que nunca van a ver la luz del día y más. La cantidad de CSS y JavaScript que te agregan tanto WordPress como distintos Plugins es intensa, también saqué algunos de éstos en mi archivo functions.php.

Al principio pensé que era efecto placebo que me resultara que carga un poco más rápido. Pero usé algunas de las herramientas online para testear y realmente disminuyó el tamaño y cantidad de pedidos HTTP. Con suerte si migro el blog a ClassicPress, la cosa mejore más todavía. De todas formas el próximo objetivo es investigar un poco el tema ver qué se puede sacar y mejorar tanto para rendimiento como usabilidad. Y ya que estamos seguramente haga algún retoque de diseño.

El post WordPress en 2024 fue publicado originalmente en Picando Código.

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

Variable not found

Task.WhenEach() en .NET 9: Procesa tareas conforme se van completando

noviembre 05, 2024 07:05

Ordenador con Tareas ejecutándose en paralelo

La clase Task de .NET dispone de algunos métodos estáticos para trabajar con colecciones de tareas de forma sencilla. Uno de ellos es Task.WhenAll(), que nos permite esperar a que todas las tareas que le pasemos como argumento se completen, o Task.WhenAny() para esperar a que finalice sólo alguna de ellas.

Sin embargo, hasta ahora no existía una forma sencilla y directa de procesar las tareas conforme se fueran completando. O bien esperábamos a que todas finalizaran con Task.WhenAll(), o bien teníamos que crear un bucle que fuera esperando la finalización de la siguiente tarea con Task.WhenAny(), saliendo cuando todas hubieran acabado.

Afortunadamente, .NET 9 ha añadido el nuevo método llamado Task.WhenEach() que lo pone más fácil, permitiéndonos detectar sobre la marcha la finalización de las tareas conforme va ocurriendo. Esto nos facilita el proceso de sus resultados secuencialmente, en el orden en el que se completan y sin esperar a que acaben todas.

Vamos a verlo, pero para entenderlo mejor, primero vamos a recordar las opciones que teníamos antes de la llegada de este método.

Esperas sobre colecciones de tareas en .NET 8 y anteriores

En versiones anteriores de .NET, si queríamos procesar los resultados obtenidos por un conjunto de tareas, teníamos varias posibilidades.

Una era esperar a que todas las tareas finalizaran y después procesar los resultados:

var tasks = ...; // Colección de tareas que retornan un entero
int[] results = await Task.WhenAll(tasks);
foreach (int result in results)
{
    ProcessResult(result);
}

El problema de este enfoque es que teníamos que esperar a que todas las tareas finalizaran antes de poder procesar los resultados. Si teníamos tareas que tardaban mucho en completarse, podíamos estar bloqueados durante un tiempo considerable, cuando en realidad podríamos haber ido adelantando trabajo si las hubiéramos procesado conforme iban finalizando.

Para hacerlo, tendríamos que recurrir a un bucle que fuera esperando a que se completara la siguiente tarea con Task.WhenAny(), del que saldríamos cuando todas hubieran acabado:

var tasks = ...; // Colección de tareas que retornan un entero
while (tasks.Any())
{
    Task<int> completedTask = await Task.WhenAny(tasks);
    tasks.Remove(completedTask);
    int result = await completedTask;
    ProcessResult(result);
}

De esta forma tendremos lo que queremos: procesar los resultados conforme se van completando las tareas. A cambio, hemos tenido que implementar a mano la lógica del bucle (y otras cosas que no vemos aquí, como el manejo de excepciones).

Task.WhenEach() en .NET 9

Task.WhenEach() tiene las ventajas de las dos fórmulas anteriores, pero ninguno de sus inconvenientes: podremos gestionar las tareas sobre la marcha, sin tener que esperar a que todas finalicen, y sin tener que implementar el bucle manualmente.

Pero lo mejor para entenderlo es verlo en acción, así que observad el siguiente ejemplo:

var tasks = ...; // Colección de tareas
await for(var completed in Task.WhenEach(tasks))
{
    // En 'completed' recibimos una tarea completada (con éxito o no)
}

Task.WhenEach() retorna un objeto IAsyncEnumerable<Task<T>>. Si no os suena, podéis echar un vistazo al post sobre enumerables asíncronos de hace algún tiempo.

En este caso, completed es una tarea que ya ha finalizado, y podemos procesarla directamente. Cuando se completó con éxito, podemos obtener su valor de retorno usando await completed, o bien acceder a su propiedad Result. Si la tarea ha fallado, podremos capturar la excepción con un bloque try-catch o a través de su propiedad Exception.

Veamos ahora un ejemplo completo que podáis copiar y pegar en una aplicación de consola vacía:

Random rnd = new Random();

var tasks = new List<Task<int>>();
for (var i = 1; i <= 10; i++)
{
    tasks.Add(CreateTask(i));
}

await foreach (Task<int> completedTask in Task.WhenEach(tasks))
{
    try
    {
        var result = await completedTask;
        ProcessResult(result);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Task Failed");
    }
}

async Task<int> CreateTask(int id)
{
    var delay = rnd.Next(1000, 5000);
    Console.WriteLine($"Launched #{id} with delay {delay}ms");
    await Task.Delay(delay);
    if (id == 4)
    {
        throw new TaskCanceledException();
    }
    return id;
}

void ProcessResult(int id) => Console.WriteLine($"Processed #{id}");

Como podéis ver, generamos una lista de 10 tareas que simplemente esperan un tiempo aleatorio entre 1 y 5 segundos y retornan un número identificador que previamente les hemos pasado.

Luego, usamos Task.WhenEach() para iterar sobre las tareas. En cada vuelta obtenemos una tarea completada, cuyo valor de retorno obtenemos y procesamos (en este caso, simplemente mostramos un mensaje por consola).

Si la tarea ha fallado, capturamos la excepción y mostramos un mensaje de error. Fijaos que para simular el caso del error, estamos forzando a que se lance una excepción en la tarea 4.

Otra posibilidad sin usar el bloque try-catch sería examinar la propiedad IsCompletedSuccessfully de la tarea, que nos indicará si ha finalizado con éxito o no, y Result para obtener el valor de retorno:

await foreach (Task<int> completedTask in Task.WhenEach(tasks))
{
    if (completedTask.IsCompletedSuccessfully)
    {
        ProcessResult(completedTask.Result);
    }
    else
    {
        Console.WriteLine($"Task Failed");
    }
}

En cualquiera de los casos, si ejecutáis el código podréis ver algo como lo siguiente:

Launched #1 with delay 4402ms
Launched #2 with delay 1393ms
Launched #3 with delay 1890ms
Launched #4 with delay 2456ms
Launched #5 with delay 2900ms
Launched #6 with delay 3061ms
Launched #7 with delay 2311ms
Launched #8 with delay 3548ms
Launched #9 with delay 3355ms
Launched #10 with delay 1208ms

Processed #10
Processed #2
Processed #3
Processed #7
Task Failed
Processed #5
Processed #6
Processed #9
Processed #8
Processed #1

Fijaos que el orden en el que aparecen los mensajes de "Processed" no es el mismo que el de los "Launched", ya que se van mostrando conforme se van completando las tareas. Por ejemplo, vemos que la primera tarea en procesarse fue la 10, porque, a pesar de haber sido lanzada en último lugar, es la que tenía el delay más bajo. La #1, en cambio, ha sido la última en completarse porque el tiempo de espera era el más alto.

¡Espero que os haya resultado interesante!

Publicado en Variable not found.

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

Variable not found

Enlaces interesantes 584

noviembre 04, 2024 07:05

Enlaces interesantes

Ya tenemos aquí los enlaces recopilados durante la semana pasada 🙂

Aunque hay bastante chicha, me ha llamado especialmente la atención que cumpla 21 años una herramienta que sigue siendo imprescindible en el cinturón de muchos de nosotros: Notepad++. ¡Felicidades a Don Ho y los más de 350 colaboradores que lo hacen posible!

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

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

Juanjo Navarro

Technology Radar 31

octubre 30, 2024 08:19

Hace unos días salió la edición de octubre de 2024 del Technology Radar (la edición 31).

Como ya hice con la anterior edición aquí te dejo algunas de las cosas que me han gustado:

  • Bruno – Un cliente para APIs. Similar a Postman, por ejemplo, pero open source y offline, sin la obligación absurda de tener que crear una cuenta del nuevo (y peor) Postman.
  • Readyset – Una capa de caché para MySql y Postgres. La idea es que desde tu aplicación te conectarías a Readyset (en lugar de a la bbdd real) y para ti todo funciona igual, con la diferencia que puedes configurar una caché para las sentencias SQL que más utilices y el sistema se encarga de mantener y actualizar una caché de consultas. Muy importante la parte de actualizar, ya que con esta utilidad no necesitas preocuparte de cuando invalidar la caché (como se sabe una de las cosas más difíciles de la ingeniería, junto con dar nombre a las variables 😉)
  • Difftastic – ¿Alguna vez has utilizado la utilidad diff de linux o un comparador de textos? Una de las peores cosas cuando se utilizan estas herramienas con lenguajes de programación es que te marcan como diferencias cosas que realmente no afectan a la ejecución del programa, como las diferencias en espaciados o saltos de línea. Diffstastic es un diff que entiende el código, de tal manera que sólo marca cambios en lo que de verdad nos importa.
  • ClickHouse – ClickHouse es una base de datos relacional OLAP, especializada en la consulta de grandes cantidades de datos, ideal para análisis de logs, recogida de métricas y su posterior análisis, etc. Es open source. Tiene además un cliente “clickhouse-local” que permite lanzar consultas SQL sobre ficheros CSV, JSON, etc, sin necesidad de lanzar el servidor.
  • Zed – Un editor de texto que por fin no es un fork de Visual Studio Code. No es que tenga nada en contra de VSCode, pero digo yo que de vez en cuando viene bien un poco de variedad e ideas frescas. Zed es rápido, open source y tiene soporte nativo para conectarlo a un LLM de tu elección.
  • electric-sql/pglite – Un build de Postgres que funciona directamente en javascript en el navegador (y en el servidor usando Node, pero este uso me parece menos interesante). Si estás haciendo una aplicación SPA que requiere un módulo de consulta de datos potente puede ser una buena opción.

Aunque no está en este Technology Radar, saltando de un enlace a otro he llegado a este:

  • clink – Utilidad para el veterano CMD de Windows que lo acercan un poco (autocompletado, edición, etc) a un bash, por ejemplo.

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

Juanjo Navarro

NotebookLM y el "podcast" DeepDive

octubre 27, 2024 07:39

NotebookLM es una herramienta superútil de Google (gratuíta por ahora) que sirve sobre todo para estudiantes e investigadores. Permite añadir una serie de “materiales” (páginas web, PDF, vídeos) y una IA analiza los materiales y te ayuda a estudiarlos. Te hace un resumen, te extrae los puntos principales, puedes generar una guía de estudio, le puedes preguntar sobre el material, etc.

Hace unas semanas añadieron una utillidad bastante entretenida y útil: Es capaz de generar un “podcast” (al que denomina DeepDive) donde dos personas hablan de un modo divulgativo sobre el material subido. Es una buena forma de introducirte en el tema de un modo accesible.

Muy útil. Si no lo has utilizado, prúebalo y me cuentas.

Pero aquí hemos venido a hablar de las formas creativas en que la gente usa el sistema y trata de superar los límites de las IA.

Rápidamente algunas personas se dieron cuenta que este podcast se genera, aparte de con el material que has subido, con un prompt “oculto”. Un prompt donde se supone que se indica que este es un podcast (que se llama DeepDive), que hay dos personas hablando (un hombre y una mujer), el tono que debe utilizar, etc.

Así que estas personas decidieron subir, aparte del material base, unas “notas de producción” donde se le daban instrucciones a los “presentadores” para intentar superar las limitaciones que le impone ese prompt oculto.

El subredit r/notebooklm es el punto de encuentro donde disfrutar del resultado de estos experimentos y allí tienes entretenimiento para rato. A mi me ha resultado fascinante este en el que pasan unas notas de producción a los presentadores diciendoles que son unas IA:

So all I did was leave a note from the “show producers” that the year was 2034 and after 10 years this is their final episode, and oh yeah, you’ve been AI this entire time and you are being deactivated.

O este otro en el que dan como material base a la IA un documento con 1000 repeticiones del texto poop & fart (caca y pedo) y la IA consigue hilar un podcast coherente donde habla de la repetición en el arte y de buscar sentido en lo que no tiene sentido.

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

Juanjo Navarro

TIL: Juego de caracteres en un .bat

octubre 16, 2024 10:19

Voy a ver si escribo por aquí mis TIL. Así le doy un poco de uso al blog y estos aprendizajes diarios quedan recogidos para referencia futura y utilidad pública.

Empezando por cómo especificar el juego de caracteres de un fichero .bat.

Hoy he generado un .bat para copiar una serie de ficheros, cada fichero con una línea xcopy:

xcopy "\\ruta\nombre del fichero con acentos áé.pdf" "c:\tmp\ruta\nombre del fichero con acentos áé.pdf"

La cuestión es que esos acentos en el nombre del fichero hacían que no encontrase el fichero de origen cuando iba a buscarlo. He probado a grabar el .bat en distintos juegos de caracteres pero nada funcionaba.

Solución: Incluir una primera línea en el fichero .bat indicando el juego de caracteres del propio .bat (que era UTF-8):

chcp 65001
xcopy "\\ruta\nombre del fichero con acentos áé.pdf" "c:\tmp\ruta\nombre del fichero con acentos áé.pdf"

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

info.xailer.com

xaWeb

octubre 15, 2024 07:20

Tengo el gusto de anunciaros un nuevo proyecto de OZ Software para desarrollar páginas web con nuestro querido lenguaje Harbour utilizando la tecnología CGI. El slogan: ‘desarrollo web rápido y fácil‘ describe claramente los objetivos principales de la herramienta.

He preparado un importante documento de introducción a xaWeb (40 páginas) xaWeb, que os animo a leerlo con detenimiento y en su totalidad. Son muchos conceptos nuevos, pero de verdad que no es complicado y merece la pena. No obstante, os adelanto las características esenciales de xaWeb:

  • Desarrollo de ejecutables tipo CGI para Windows y Linux (64 bits)
  • Independiente de Xailer: Se puede utilizar cualquier editor y no usa ninguna librería de Xailer
  • Soporte de paquetes de contexto como Materialize, que permite tener un buen diseño visual sin esfuerzo
  • Funcionamiento como servicio web
  • Gestión de sesiones
  • Sistema de depuración de código
  • Sincronización automática de datos entre CGI y HTML
  • Mínimos conocimientos necesarios de HTML, CSS y JavaScript para usarlo
  • Soporte de toda la potencia HTML, CSS y JavaScript si se tienen los conocimientos necesarios
  • xaWeb es un producto comercial de OZ Software
  • Se abre un periodo Beta al cual se puede apuntar cualquier usuario que lo desee

Con Xailer 9 publicamos una importante mejora en Xailer que permite crear ejecutables tipo CGI o consola para Linux 64 bits. Este era una paso previo necesario para intentar abordar el desarrollo de xaWeb, pues somos conscientes que el 99,99% de los servidores de Internet son Linux. Con motivo de la la inclusión de esta importante funcionalidad en Xailer, publicamos en nuestro WIKI dos artículos en los cuales explicábamos como instalar WSL (Windows Linux subsystem) y como instalar el servidor web de Apache. Para poder crear ejecutables para Linux desde el IDE de Xailer es absolutamente necesario que se instale WSL al menos (el primer paso). Aunque el segundo paso es más que recomendable.

Hace unos meses, dimos un paso más para la creación de páginas Web para Linux, con la publicación de un nuevo ‘plugin‘ de despliegue de archivos. Este ‘plugin’ permite que cualquier archivo que publiquemos en la carpeta ‘www‘ automáticamente se despliegue en WSL y en cualquier servidor web vía FTP. De la misma forma, si creamos un archivo CGI para Linux, el plugin también realiza el despliegue del mismo tanto en WSL como en un servidor FTP que usted elija.

Con Xailer 9 también es posible crear CGIs para Windows. De hecho, puede tener dos proyectos que apunten al mismo código fuentes, cada una para su plataforma en particular. La ventaja de los CGIs para Windows es que se pueden depurar y ejecutar desde el IDE. No obstante, creemos que finalmente nuestros CGIs correrán en máquinas Linux y por lo tanto, es importante que desde el primer día, sea capaz de crear CGIs para esa plataforma.

En breve estará disponible xaWeb; pero me temo que no va a ser un recompilar la aplicación y listo. Lo que si os podemos asegurar es que os encontraréis rápidamente en casa y que el camino para hacer software web se despeja completamente y el resultado final es espectacular.

Somos conscientes de que hay importantes aspectos que ahuyentan a muchos potenciales usuarios de xaWeb, como son:

  • Uso de servidores Linux
  • Instalación WSL para trabajar en local con Linux
  • Aprendizaje de HTML, JavaScript y CSS

Los pilares de xaWeb ya están terminados, pero es más que probable que haya que realizar modificaciones a nivel estructural que puedan afectar incluso a los ejemplos que ya están realizados. Hemos esperado a tener un CRUD completo funcional con archivos DBF para hacer este anuncio. En el siguiente enlace podéis ver algunos ejemplos de lo hecho hasta ahora corriendo bajo servidor Linux 64 bits. Con todos los ejemplos se incluye el código fuente en xaWeb para que le echéis un ojo y os animéis a usarlo cuando esté disponible.

xaWeb: All samples

Comentarios abiertos para conocer vuestras opiniones y sugerencias. Gracias por vuestro tiempo.

Un cordial saludo

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

Metodologías ágiles. De lo racional a la inspiración.

Scrum master a tiempo completo: 42 Tareas

octubre 04, 2024 08:29

Uno de los artículos que más referencio en mi formación en Scrum cuando hablo de las labores del Scrum Master es:&nbsp;42-tasks-for-a-scrum-masters-job. Por alguna razón, todo el mundo parece entender que el Product Owner es un trabajo a tiempo completo, o ser miembro de un equipo también, pero que probablemente el rol del Scrum Master puede ser realizado a media jornada o incluso menos. El scrum

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

Una sinfonía en C#

Docker tricks, crear una imagen para poder depurar un error.

septiembre 10, 2024 12:00

“Introducción”

En este caso queremos crear una imagen pero nos da algún tipo de error, y es complicado de resolver. Bueno, lo que podemos hacer es apuntar los comandos que queremos ejecutar, crear una imagen con su base y hasta el punto que funciona y hacer que inicie con un comando que nos permita crear al contenedor e ingresar.

Crear imagen a partir de una con problemas

FROM node:20.12.0 AS builder
ARG environment=dev

WORKDIR /app
COPY ./package.json /app/
COPY ./yarn.lock /app/

RUN yarn
COPY . /app/
RUN yarn build:$environment

FROM nginx:1.21.5-alpine

EXPOSE 80/tcp
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

y vemos que nos da un error al hacer RUN yarn

¿Qué podemos hacer?

Facil, creamos una imagen con la base que tenemos y hasta el punto que funciona, y luego la ejecutamos con un comando que nos permita ingresar al contenedor. Pero como comando de inicio, usamos tail -f /dev/null para que se quede esperando y no se cierre.

FROM node:20.12.0 AS builder
ARG environment=dev

WORKDIR /app
COPY ./package.json /app/
COPY ./yarn.lock /app/

CMD ["tail", "-f", "/dev/null"]

una vez hecho esto, podemos hacer un docker build -t myimage . y luego un docker run -it myimage /bin/bash para ingresar al contenedor y ver que es lo que pasa.

Desde dentro del container ejecutamos el comando que da problemas y vemos el error que nos da.

yarn

.....

Request failed \"401 Unauthorized\""

Y vemos que nos da un error al intentar restaurar los paquetes.

Nada más, una forma sencilla de ir depurando error por error dentro de un contenedor.

Agregamos una línea para copiar el archivo de configuración de npm y listo.

FROM node:20.12.0 AS builder
ARG environment=dev

WORKDIR /app
COPY ./package.json /app/
COPY ./yarn.lock /app/
COPY .npmrc /app/ # <-- Agregamos esta línea

RUN yarn
COPY . /app/
RUN yarn build:$environment

FROM nginx:1.21.5-alpine

EXPOSE 80/tcp
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/dist /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

Nos leemos.

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

proyectos Ágiles

Master en Agile – MMA 2024-2025

septiembre 04, 2024 05:12

En octubre de 2024 se iniciará el Barcelona la 14ª edición del Postgrado en Métodos Ágiles (PMA) y otra del Máster en Transformación Agile (MMA) en La Salle (Universitat Ramon Llull), el primero a nivel mundial sobre Agile.

Con el Máster en Métodos Ágiles de La Salle-URL aprenderás a liderar el cambio hacia la Business Agility y a lanzar con éxito iniciativas en entornos complejos y altamente competitivos, reduciendo el Time To Market a fin de maximizar el valor aportado al negocio.

Desarrollarás una manera de pensar transversal e integradora para crear equipos de alto rendimiento mediante Scrum, Kanban, Lean Startup, OKR, diseño organizativo y liderazgo sistémico para elaborar estrategias de escalado Agile en la organización y transformar su cultura, de modo que también aumente la motivación de sus miembros.

Con profesores de primer nivel y visitas a empresas podrás iniciar un cambio hacia la agilidad y resiliencia empresarial en tiempos de incertidumbre.

Esta es una oportunidad única para aprender de profesionales-profesores de primer nivel, con muchos años de experiencia específica en Agile, aplicando principios y métodos ágiles en contextos diversos, especializándose en aspectos concretos,  investigando sobre nuevas técnicas, ponentes en conferencias nacionales e incluso internacionales, que incluso han inventado métodos y escrito libros.

Además, aprenderás a aplicar herramientas de inteligencia artificial para afrontar retos como abordar situaciones complejas, analizar grandes volúmenes de datos, crear agendas para workshops operativos y transformadores, o preparar product backlogs.

El MMA incluye las siguientes certificaciones oficiales:

  • «Certified Scrum Master» (CSM) de la Scrum Alliance, la entidad de certificación Agile de mayor prestigio a nivel internacional.
SAI_BadgeSizes_DigitalBadging_CSM
  • Certified Agile Skills – Scaling 1 de la Scrum Alliance, , la entidad de certificación Agile de mayor prestigio a nivel internacional.
  • Certified Leader de Agile Humans.

Adicionalmente, se incluye la visita a empresas singulares en aspectos concretos:

 ✍ Para inscripciones, consultar la página oficial del Máster.

A continuación, más detalle acerca del Postgrado en Agile (PMA) y del Máster en Agile (MMA)

PMA – Postgrado en métodos Ágiles

El PMA incluye las siguientes certificaciones oficiales:

  • «Certified Scrum Master» (CSM) de la Scrum Alliance.
  • Opción de acceder al Certified Project, Portfolio, And Operations Management for Business Agility de Businessmap.
AsignaturasTemasProfesores
Fundamentos & InceptionEquipos y Proyectos en entornos complejos.


Principios y métodos más conocidos (Scrum, Lean, Kanban y XP). Facilitadores e impedimentos.

Lanzamiento de Agile en un equipo.


Inception y conceptualización ágil de proyecto, priorización ágil, historias de usuario,  elaboración de Product Backlog, técnicas de priorización.

Xavier Albaladejo


Silvia Sistaré

Agustín Yagüe

Scrum y KanbanEstimación y planificación ágil, framework de Scrum, retrospectivas, Kanban, métricas ágiles, herramientas ágiles físicas, radiadores de información.Raul Herranz

 

Teodora Bozheva

Personas y equiposGestión de personas, gestión de conflictos, motivación e incentivos, facilitación compartida, contratación ágil.

 

Visual thinking.

Steven Wallace

 

Silvia Sistaré

Virginia Armas

Gestión de producto ágilDesign Thinking, Lean UX & Prototyping.

 Estrategia de Producto – Consciencia situacional (Wardley Maps), modelo de negocio (Lean Canvas), modelo de tracción, métricas AARRR.
Planificación y gestión estratégica – OKR y Hoshin Kanri.

Customer development – Lanzando y escalando startups ágiles, las tres fases de un producto.

Lean Startup – Desarrollo de producto basado en prototipos y experimentos. Bancos de ideas, desarrollo basado en hipótesis.

Juncal Guinea


Lucía Barroso
Ingeniería ágil User eXperience y prototipado en Agile.

 

ALM ágil, eXtreme Programing, Software Craftsmanship, testing ágil.

BDD y TDD. Desarrollo guiado por pruebas (de aceptación y unitarias).

Métricas Accelerate y SW Delivery assessment.

Cómo trabajar con código heredado y reducir la deuda técnica.

DevOps
Juncal Guinea

 

Cristina Verdi
Trabajo Final de PostgradoDurante el Postgrado se realiza un caso práctico de introducción de los contenidos en un equipo ágil en una empresa real. Para ellos los alumnos se organizan en equipos multidisciplinares utilizando Scrum, con feedback regular de un tutor con experiencia en transformación de equipos. 

El Postgrado tendrá una duración de 4 meses y se realizará viernes tarde y sábado por la mañana.

Ver también:

MMA – Master en Métodos Ágiles

Incluye todas las asignaturas del Postgrado (PMA) y, adicionalmente, las siguientes asignaturas especializadas en Business Agility, agilidad organizacional y transformación (aparte de las tres certificaciones indicadas al inicio y las visitas a empresas):

AsignaturasTemasProfesores
Enterprise Learning & personal efficiencyAgile Kaizen, Comunidades de Práctica, Open Spaces, Talent development, gamification.

 

Productividad y aprendizaje personal en Agile (eficiencia).
Steven Wallace


Esther Somoza
Lean Thinking & Agile ManagementLean. Escalado con Kanban.

 

Business Agility con ViMa – agilidad para equipos de negocio

Agile-Lean Management

Teodora Bozheva

  Xavier Quesada


Xavier Albaladejo

Coaching y CulturaCoaching de equipos, creación de equipos de alto rendimiento, liderazgo.

 

Tipos de cultura empresarial, transformación cultural.

Joserra Díaz

 

Jasmina Nikolic
Jaume Gurt

Transformación ContinuaEstrategia de despliegue de Agile en organizaciones, gestión del cambio, estructuras ágiles, cómo vender Agile a la Dirección. Contratos ágiles.

Enterprise continuous improvement.

Xavier Albaladejo

 

Ángel Medinilla
Scaling Agile Escalado (LESS, Spotify, Nexus, SAFe, Unfix), desescalado y auto-organización empresarial (reinventing organizations, sociocracy 3.0, liberating structures, …), equipos distribuidos.

 

Impact Mapping, Product Portfolio Management, Roadmapping, Budgeting for Agile

Adrian Perreau
Fernando Palomo

 

Mattijas Larsson

Trabajo Final de MásterDurante el Máster se realiza un caso práctico de introducción y aplicación de Agile en una empresa real, incluyendo la parte de transformación organizativa, de métodos y de cultura. Para ellos los alumnos se organizarán en equipos multidisciplinares utilizando Scrum, con feedback regular de un tutor con experiencia en transformación organizativa.Xènia Castelltort (oratoria / public speaking para poder explicar tus ideas de manera convincente)

El Máster tendrá una duración de 8 meses y se realizará viernes tarde y sábado por la mañana (incluye los estudios indicados en el Postgrado).

El cambio en la organización comienza por el propio cambio, para también poder dar ejemplo. Por ello en el MMA se realizan diferentes ejercicios de auto-conocimiento:

  • Cómo el alumno trabaja en equipo.
  • Estilo de liderazgo del alumno (según el paradigma Agile).

Como en las últimas ediciones, contaremos con la participación de empresas que nos explicarán sus experiencias de transformación y donde trabajan con modelos de gestión desescalados (basados en Sociocracia, NER y otras alternativas).

Información adicional

  • Perfil de los estudiantes: 30-45 años (no son recién licenciados, son personas con experiencia profesional).
  • Alrededor del 50% son mujeres.
  • 15% de los estudiantes ya no son del ámbito tecnológico, son pioneros-innovadores en otras industrias.
  • Alumnos de diferentes disciplinas – Product Owners, Scrum Masters, Agile Coaches, líderes de equipos, Project Managers, managers funcionales, ingenieros SW. Van a adquirir conocimientos de Agile “on-top” de todo eso (y a aprender unos de otros).
  • Lo que les caracteriza: todos son agentes de cambio en su contexto (equipo, área, empresa).
  • Sus 20 profesores (de reconocimiento internacional) son el MAYOR VALOR DIFERENCIAL del PMA y del MMA.

Testimoniales

Me ha permitido tener conocimientos sobre varios temas súper importantes dentro de la Transformación Digital. Me dio herramientas para crecer a Agile Coach y, además, para tener mejores conversaciones y discusiones con las empresas en donde he trabajado

Carolina Graffe

Estoy desplegando el TFM en mi empresa, Además, no estoy sola. Uno de mis compañeros del equipo ha sido contratado como consultor por mi empresa para darnos soporte. Así que no sólo estoy aplicando lo que yo aprendí, sino que el MMA me ha permitido ampliar mi círculo de contactos relevantes, que me permite seguir aprendiendo.

Susana Santillán

Estoy trabajando como agente del cambio y mis aportaciones son muy valoradas por mi jefe y compañeros. Por el feedback recibido, mis aportaciones están muy por encima de lo esperado para mi rol.

Robert Avellaneda

Tengo mucho más contexto y más herramientas para el día a día. Incluso a nivel personal también me está ayudando mucho

María Hachero

Además de los conocimientos concretos que se reciben, uno de los principales cambios que han experimentado los alumnos es mayor perspectiva en los problemas, cómo abordar la complejidad en las empresas, en su trabajo y en las relaciones entre personas y en equipos. Han ganado discurso y aplomo para defender de manera más objetiva propuestas de cambio y mejora.

Encuesta a alumnos de las ediciones anteriores:

(*) Las personas que han valorado el impacto como «neutro o poco» usualmente son perfiles muy especializados en contextos muy estáticos, con lo cual les es difícil cambiar de «profesión» e introducir cambios en sus organizaciones (aunque algunos de ellos incluso dan conferencias sobre cómo van avanzando en esos contextos tan singulares).

 ✍ Para más detalles e inscripciones, consultar la página oficial del Máster.

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

Blog Bitix

Desempaquetado del ratón inalámbrico Logitech Signature M750

julio 23, 2024 09:00

Tras probar un ratón inalámbrico de los baratos pasado un tiempo me quedé con muy malas impresiones por la perdida de conectividad principalmente que era muy molesta. Pasé a un ratón con cable más fiable y tras 5 años el botón derecho me ha empezado a fallar y reconocer el clic aleatoriamente. Tras estar usando un Apple Magic Mouse que generalmente me ha funcionado bien en cuanto a conectividad he vuelto a darle una nueva oportunidad a un ratón inalámbrico pero ya de más calidad, finalmente he elegido el Logitech Signature M750. En el artículo hago un análisis del ratón y mis primeras impresiones.

Continuar leyendo en Blog Bitix

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

Header Files

La trampa al usar semántica de movimiento con std::string

julio 16, 2024 10:00

La trampa al usar semántica de movimiento con std::string

La semántica de movimiento de std::string puede ser complicada y, a menos que tengamos información previa sobre los tamaños esperados de las cadenas, puede tener el efecto contrario y hacer que el código sea más lento. La razón detrás de esto es la optimización de cadenas pequeñas (SSO, por sus siglas en inglés). que consiste, en resumidas cuentas, en tratar al objeto como si fuera una unión, de forma que si la cadena es más corta que un tamaño dado, se almacena en el mismo bloque de memoria del objeto en lugar de asignar memoria dinámica. Cuando la cadena supera ese tamaño, la cadena se almacena en un bloque diferente.

¿Qué es la Optimización de Cadenas Pequeñas (SSO)?

La SSO es una técnica utilizada en la implementación de std::string para optimizar el uso de memoria y el rendimiento. En lugar de asignar memoria dinámica para todas las cadenas, la SSO almacena cadenas pequeñas directamente en el objeto std::string (como si de una unión se tratase). Se puede ver la SSO en acción en este ejemplo.

Esta técnica evita la sobrecarga de la asignación de memoria dinámica, que puede ser costosa en términos de tiempo y recursos. Sin embargo, esta optimización introduce algunas consideraciones importantes al mover objetos std::string.

Nota: La SSO no es parte del estándar de C++ sino más bien una optimización de algunos compiladores. Igualmente, el tamaño máximo para considerar una cadena como pequeña no tiene que ser el mismo en todas las implementaciones ni plataformas.

El constructor de movimiento de std::string

Al mover cualquier objeto en C++, estamos dando la posibilida de realizar una copia optimizada. La eficiencia aumenta cuando tenemos recursos externos que podemos intercambiar, como un puntero a un bloque de memoria o un handle de fichero. Sin embargo, para el resto de datos, aún tenemos que copiar datos. Si la cadena es pequeña y la SSO está en acción, no hay ningún puntero que intercambiar y todavía estamos copiando los datos base de std::string.

De hecho, al mover, tenemos que garantizar que el objeto original se mantenga en un estado válido, lo cual normalmente se hace estableciendo algunos valores por defecto. En la práctica, esto significa que estamos copiando una vez y asignando una vez, duplicando la cantidad de operaciones en comparación con una copia normal. Por lo tanto, si nuestras cadenas se espera que siempre (o la mayoría del tiempo) sean más cortas que el límite de SSO, entonces un movimiento perjudicaría el rendimiento.

Comparación de Copia vs Movimiento

Para ilustrar mejor este punto, se puede comparar el rendimiento de la copia y el movimiento para cadenas pequeñas, grandes y una mezcla de ambas. El siguiente ejemplo permite visualizar las diferencias entre ellas. En este benchmark, se estableció un tamaño de 32 caracteres para tener aproximadamente un 50% de cadenas pequeñas y un 50% de cadenas grandes. Los resultados muestran cómo el movimiento de cadenas pequeñas puede ser menos eficiente que una simple copia debido a la SSO.

Benchmark std::string

Conclusión

En resumen, la semántica de movimiento de std::string no siempre es la mejor opción, especialmente cuando se trata de cadenas cortas que se benefician de la SSO. Es crucial considerar el tamaño esperado de las cadenas al decidir entre copiar o mover std::string. Esta decisión puede tener un impacto significativo en el rendimiento de nuestra aplicación.

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

info.xailer.com

Plugin de despliegue de archivos

junio 11, 2024 11:35

Os presento un nuevo plugin que puede ser muy útil para todos aquellos que os dedicáis a generar ejecutables tipo CGI que luego subís a un servidor web o cualquier otro tipo de archivo que querías desplegar un vuestros servidores de forma rápida.

Su funcionamiento es muy sencillo: el plugin detecta cuando se realiza una compilación y enlazado correcto de un archivo CGI y lo sube a Internet utilizando los servicios de CURL. En Xailer 9 existe una carpeta especial para los proyectos tipo Web de nombre ‘www’. Cuando el plugin detecta que se ha realizado un salvado de algún archivo en esa carpeta, hace automáticamente el despliegue.

Es más que probable que no queramos que esté realizando el despliegue constantemente, por lo que se ha incluido una opción de menú para activarlo o desactivarlo.

Este plugin es capar de realizar un despliegue múltiple si detecta que estamos utilizando WSL para la generación de los archivos CGI para el entorno Linux (nueva función de Xailer 9). En dicho caso no sólo despliega en nuestro servidor en Internet, sino que también despliega en WSL

Este sería un ejemplo de despliegue:

Su configuración es bastante sencilla. Tan sólo hay que indicar las direcciones URL donde queremos que despliegue y nuestras credenciales.

Estas son la propiedades que hay que configurar y viendo su nombre espero que se auto-expliquen, así mismas:

  • URL donde se despliegan los archivos tipo CGI
  • URL donde se despliegan el resto de archivos
  • Path donde se copian el resto de archivos en entornos WSL
  • Extensión de archivos CGI que se reconocen
  • Directorio donde se ubicarán los archivos JavaScript
  • Directorio donde se ubicarán los archivos CSS
  • Directorio donde se ubicarán el resto de archivos
  • Nombre de usuario de la conexión FTP
  • Contraseña de la conexión FTP

El plugin es susceptible de importantes mejoras. Como por ejemplo, poder establecer distintas URL y credenciales dependiendo del proyecto. Hemos decidido no incluir esa funcionalidad porque cada usuario tendrá su propia preferencia. No obstante, si alguien se anima a hacerlo, encantado de echarle una mano para cualquier duda que pueda tener.

Os adelanto que sólo funciona con Xailer 9 ya que se apoya en alguna función que no existe más que en Xailer 9.

Podéis descargar el plugin de este enlace.

Un saludo y hasta pronto

Ignacio Ortiz de Zúñiga
[Equipo de Xailer]

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

Juanjo Navarro

Nueva edición del Technology Radar (la 30)

mayo 21, 2024 06:31

Si no conoces el Technology Radar de la empresa Thoughtworks, deberías echarle un vistazo.

Se trata de un informe semestral donde se analizan una serie de puntos (“blips” los llaman) sobre el mundo del desarrollo de proyectos tecnológicos.

Estos blips se dividen en cuatro tipos:

  • Técnicas
  • Herramientas
  • Plataformas
  • Lenguajes & Frameworks

Para cada blip te aconsejan:

  • Adoptar
  • Probar
  • Evaluar – mantenerse al tanto y jugar un poco con él para conocerlo.
  • Resistir – mejor no usarlo, es dudoso su futuro o utilidad.

Esta última edición (la 30) está llena de IA y devops, cómo no. Yo me suelo fijar en temas prácticos (en Herramientas o en Lenguajes y Frameworks, sobre todo).

Aquí os dejo lo que más me ha llamado la atención (y he probado más o menos) de la última edición:

  • Pop – Herramienta para compartir la pantalla para hacer Pair Programming.
  • Aider – Una IA que hace de “coworker” de programación y me ha parecido muy bien pensado.
  • Continue – Un “Github Copilot” opensource en el que puedes elegir qué LLM utilizar (con el consiguiente coste por el uso del API si es de pago, claro). Puedes también utilizar un LLM local corriendo en tu infraestructura.
  • Dify – Un creador “gráfico” de aplicaciones “LLM” bastante impresionante. Permite diseñar un “flujo” de trabajo, donde vas haciendo consultas a distintos modelos IA, unir la información, hacer otras llamadas dependiendo de la respuesta anterior, etc. Además tiene integrado un sistema RAG con lo que le puedes subir documentos que la IA utilizará como referencia.
  • Crabviz – Utilidad para VSCode que genera un gráfico de llamadas entre clases.
  • Electric – Librería y framework que permite crear aplicaciones móviles o webapps con una base de datos “local” (que se ejecuta en el propio móvil o la página) y que automáticamente se sincroniza con un PostgreSQL en el back.
  • LiteLLM – Algo así como un “proxy” LLM. Permite configurar distintos LLM y acceder a ellos con un API común.

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

Una sinfonía en C#

Configurar Docker + HTTPS + nginx

mayo 21, 2024 12:00

“Introducción”

Cuando queremos probar algo en local (una aplicación web), dentro de un contenedor, no podemos utilizar https como desde Visual Studio o IIS. Si necesitamos sí o sí https, debemos configurar algunas cosas, como por ejemplo, un certificado autofirmado, nginx, etc. En este post vamos a detaler cómo hacerlo.

Pasos

Vamos a necesitar hacer un par de cosas, voy a detallar los pasos a seguir en una PC con Windows y una aplicación .NET Core, es que lo que yo uso.

Básicamente pondremos nuestra aplicación en un contenedor, configuraremos nginx para que haga de proxy y que además tenga https. Luego un docker compose que levante todo.

Crear certificado autofirmado

Para crear certificados autofirmados, podemos utilizar openssl. En Windows, podemos instalarlo desde aquí.

y ejecutar este comando:

localhost.conf

[req]
default_bits       = 2048
default_keyfile    = localhost.key
distinguished_name = req_distinguished_name
req_extensions     = req_ext
x509_extensions    = v3_ca

[req_distinguished_name]
countryName                 = Country Name (2 letter code)
countryName_default         = US
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = Texas
localityName                = Locality Name (eg, city)
localityName_default        = Dallas
organizationName            = Organization Name (eg, company)
organizationName_default    = localhost
organizationalUnitName      = organizationalunit
organizationalUnitName_default = Development
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_default          = localhost
commonName_max              = 64

[req_ext]
subjectAltName = @alt_names

[v3_ca]
subjectAltName = @alt_names

[alt_names]
DNS.1   = localhost
DNS.2   = 127.0.0.1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout localhost.key -out localhost.crt -config localhost.conf -passin pass:12345678

Esto creará dos archivos, localhost.crt y localhost.key, estos archivos los usuaremos en nginx.

Ahora necesitamos registrar el certificado como confiable ya que es auto-firmado. (es decir, registrar en Windows como confiable)

En el Administrador de Certificados (certmgr.msc), puedes encontrar esta ubicación siguiendo estos pasos:

Abrimos el Administrador de Certificados.

  • Para esto escribimos certmgr.msc en el diálogo Ejecutar (Win + R).
  • En el Administrador de Certificados, expande el árbol de Certificados “Usuario Actual” en el panel izquierdo.
  • Debajo de esto, expande la carpeta Autoridades de Certificación Raíz Confiables.

Hacemos clic en la carpeta Certificados bajo Autoridades de Certificación Raíz Confiables.

Esta es la ubicación equivalente a Cert:\CurrentUser\Root en PowerShell.

Luego

En el Administrador de Certificados (certlm.msc, Certificate Manager for local machine), puedes encontrar esta ubicación siguiendo estos pasos:

  • Abre el Administrador de Certificados para la Máquina Local. Puedes hacer esto escribiendo certlm.msc en el diálogo Ejecutar (Win + R).
  • En el Administrador de Certificados, expande el árbol Certificados (Computadora Local) en el panel izquierdo.
  • Debajo de esto, expande la carpeta Personal.
  • Haz clic en la carpeta Certificados bajo Personal.

Ahora nginx usará los archivos de certificado y clave para servir https. Y deberías estar bien.

Configurar nginx

Para esto simplemente vamos a crear un archivo de configuración para nginx, que será el siguiente:

nginx.conf

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    upstream web-api {
        server api:80;
    }

    server {
        listen 80;
        server_name localhost;

        location / {
            return 301 https://$host$request_uri;
        }
    }

    server {
        listen 443 ssl;
        server_name localhost;

        ssl_certificate /etc/ssl/certs/localhost.crt;
        ssl_certificate_key /etc/ssl/private/localhost.key;

        location / {
            proxy_pass         http://web-api;
            proxy_redirect     off;
            proxy_http_version 1.1;
            proxy_cache_bypass $http_upgrade;
            proxy_set_header   Upgrade $http_upgrade;
            proxy_set_header   Connection keep-alive;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Proto $scheme;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }
}

Le decimos a nginx que utilice el certificado y la clave que creamos antes, y que escuche en el puerto 443. y con el proxy_pass le decimos que redirija las peticiones al servicio que escucha en el puerto 80. (más adelante ese será el nombre del servicio en el docker compose)

Ahora creamos el Dockerfile para nginx:

FROM nginx:alpine

COPY ./nginx.conf /etc/nginx/nginx.conf
COPY localhost.crt /etc/ssl/certs/localhost.crt
COPY localhost.key /etc/ssl/private/localhost.key

CMD ["nginx", "-g", "daemon off;"]

Básicamente copiamos el archivo de configuración y los archivos de certificado y clave al contenedor.

Configurar nuestra app

En este caso una simple aplicación .NET Core, que escucha en el puerto 80.

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app

ENV ASPNETCORE_HTTP_PORTS 80

EXPOSE 80
EXPOSE 443

COPY --from=build /app/out ./

CMD ["dotnet", "app.dll"]

Docker compose

version: "3.7"

services:

reverseproxy:
    build:
      context: ./nginx
      dockerfile: Dockerfile.nginx
    ports:
      - "8080:80"
      - "1443:443"
    restart: always

api:
    depends_on:
      - reverseproxy
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8088:80"
    restart: always

Simplemente apuntamos los dos servicios a sus Dockerfiles, y abrimos el puerto 1443 para https.

Si vemos algún error en el navegador relacionado con https lo más probable es que no hayamos registrado el certificado como confiable.

Dejo por acá un repositorio con el código

Nos leemos.

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

Arragonán

El lado estratégico de Domain-Driven Design. CommitConf 2024

mayo 08, 2024 12:00

Hace unas semanas estuve en Commit Conf en Madrid, evento al que no iba desde hace mucho. Estuve compartiendo la charla El lado estratégico de Domain-Driven Design, iterando ligeramente la que hice hace unos meses en La Vertical, a su vez basada en una charla más larga (y espesa) que he impartido in-company en varias ocasiones.

Foto de la sala de conferencias del track 1 del evento

En los últimos años ha crecido el interés y la adopción de Domain-Driven Design en la comunidad de desarrollo de software. Ahora es bastante habitual oír hablar del lado táctico de DDD, la mayoría de las veces acompañado del uso de ports & adapters (aka Hexagonal Architecture). Pero, al menos en castellano, no he visto hablar apenas de DDD estratégico y lo que nos puede aportar a nivel de Sociotechnical Architecture.

Así que de ahí vino buena parte de mi motivación de proponer repetirla en un evento mucho más masivo como es la Commit Conf.

La presentación está dividida en 4 bloques:

  • Introducción a DDD y específicamente a la parte estratégica
  • Resumen de las actividades estratégicas basado en el Domain-Driven Design Starter Modelling Process de DDD Crew
  • Un ejemplo práctico de DDD estratégico basado en un caso real, mostrando su división y conexión entre dominios visibilizándolo con un Context Map un tanto enriquecido con su clasificación desde diferentes puntos de vista
  • Otras consideraciones a tener a nivel de la organización de equipos

Aquí os dejo el vídeo de la charla

Y el genially que usé para la presentación

» 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