Weblogs Código

Variable not found

Un vistazo a los patrones relacionales y combinadores, lo nuevo de C# 9 para exprimir el pattern matching

junio 01, 2021 06:05

.NET Core

Desde C# 7 podemos emplear patrones en expresiones is o bloques switch para especificar las condiciones que deseamos comprobar, y cada nueva versión del lenguaje sigue introduciendo novedades al pattern matching, haciéndolo cada vez más sencillo y cómodo de utilizar.

En particular, C# 9 ha añadido un montón de características interesantes destinadas a facilitar el uso de patrones, pero en este post vamos a centrarnos en las dos más importantes: los combinadores de patrones y los patrones relacionales.

Los patrones relacionales permiten especificar restricciones utilizando los operadores relacionales <, <=, >, y >= para comparar el valor de la expresión a comprobar con constantes de los tipos soportados (sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, nint, y nuint).

Podemos ver un ejemplo sencillo de su uso en el siguiente bloque switch, donde, en lugar de introducir valores constantes en los casos, usamos los operadores relacionales para especificar las condiciones a cumplir en cada uno de ellos:

string result;
switch(i)
{
case 1:
result = "One";
break;
case < 3:
result = "Less than 3";
break;
case < 5:
result = "Less than 5";
break;
case >= 5:
result = "Equal or greater than 5";
break;
};

Ciertamente, en este código la forma de expresar las condiciones es concisa y fácilmente legible, aunque el bloque switch tradicional es demasiado verboso. Por ello, sería mejor replantear el código anterior con una expresión switch, lo que reducirá significativamente la cantidad de código a emplear (de 15 a 6 líneas), manteniendo la funcionalidad intacta:

var result = i switch
{
1 => "One",
< 3 => "Less than 3",
< 5 => "Less than 5",
>= 5 => "Equal or greater than 5",
};

Por otra parte, los combinadores de patrones introducen las nuevas las palabras clave and y or, que permiten evaluar condiciones compuestas por más de un patrón, y not, cuyo objetivo es negar la evaluación de un patrón. Combinando estos nuevos operadores podemos expresar de forma sencilla y fácilmente legible restricciones mucho más complejas que en versiones anteriores del lenguaje.

Por ejemplo, observad lo claro que resulta el siguiente bloque de código, imposible de crear antes de C#9:

var result = i switch
{
10 or 11 => "10-11",
>= 0 and <= 3 => "0-3",
<= 4 and <= 6 => "4-6",
>= 7 => "7-",
};

El compilador analizará las restricciones y será capaz de arrojarnos un error cuando existan ambigüedades o errores, como en el siguiente ejemplo:

var result = i switch
{
>= 0 and <= 3 => "0-3",
>= 2 and <= 3 => "2-3", // No compila, está incluido en el anterior
_ => "Otros"
};

Este mismo tipo de condiciones pueden utilizarse en bloques if a través del operador is:

if (i is 9 or 8) { ... }
if (i is >= 3 and <= 8) { ... }

En cuanto al combinador not, probablemente, uno de los ejemplos más claros de su uso lo vimos ya hace unos días, utilizándolo para determinar cuándo un objeto no es nulo:

if(invoice is not null)  { ... }

También podríamos usarlo en construcciones más complejas, donde ganamos bastante en legibilidad si lo comparamos con las versiones anteriores de C#. Veamos varios ejemplos.

En primer lugar, imaginad que necesitamos comprobar que un objeto no sea nulo, ni de un tipo determinado. Así es como se podría implementar en las dos últimas versiones del lenguaje:

// C# 8:
if(!(obj is null) && !(obj is Invoice))
{
// Si obj no es nulo, pero tampoco es un Invoice
}

// C# 9:
if (obj is not null and not Invoice)
{
// Sin comentarios, ¡el código se entiende solo!
}

Veamos ahora cómo determinar si un entero es distinto a un conjunto de valores determinados (en este caso, los impares en el rango 0-10):

// C# 8:
if(num != 1 && num != 3 && num != 5 && num != 7 && num != 9)
{
// Num no es 1, 3, 5, 7, ni 9
}

// C# 9:
if (num is not (1 or 3 or 5 or 7 or 9))
{
// Num no es 1, 3, 5, 7, ni 9
}

Fijamos que en este caso hemos utilizado paréntesis para introducir prioridades en la expresión. Es algo, también introducido en C# 9, que potencia aún más las capacidades de este tipo de construcciones.

El mismo nivel de concisión y elegancia podríamos lograrlo también al operar con enums, lo cual nos resolvería un escenario bastante frecuente:

// C# 9:
if (result.StatusCode is not (HttpStatusCode.OK or HttpStatusCode.Created))
{
// También con enums
}

¿Interesante, verdad? En definitiva, creo que estas incorporaciones al lenguaje son bastante potentes y probablemente acabarán popularizando definitivamente el uso de patrones en C#, que hasta ahora era bastante limitado.

Como ocurre siempre que hay cambios de este tipo, tendremos que acostumbrarnos al uso de una nueva sintaxis, pero estoy seguro de que el pequeño esfuerzo valdrá la pena y al final saldremos ganando porque en muchas ocasiones podremos expresar lo mismo en menos espacio, y ganando en claridad :)

Publicado en Variable not found.

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

Picando Código

rvm-prompt: muestra la versión actual de Ruby en el prompt de tu terminal

mayo 31, 2021 02:30

rvm-prompt es una herramienta que muestra la versión actual de Ruby. Podemos usarla para mostrar esta información en nuestro prompt en la terminal:

rvm-prompt
Para agregarlo, en nuestro archivo de configuración ~/.bashrc, ~/.profile o lo que sea, tenemos que agregar el comando a la variable PS1. El ejemplo de la documentación:

PS1=”\$(~/.rvm/bin/rvm-prompt) $PS1″

Para el resultado de la imagen con los colores, la rama de GitHub y demás, yo uso en mi ~/.bashrc:

PS1=’\[\033[01;32m\]\u\[\033[01;34;10m\] \[\033[01;34m\]\w\[\033[01;33m\] \e[31m`rvm-prompt`\e[0m\[\033[01;36m\]$(__git_ps1)\n\[\033[01;34m\]\$\[\033[00m\] ‘

Como podemos leer en la documentación, hay varios parámetros que le podemos pasar al comando para mostrar el intérprete (i), versión (v), nivel de parche (p), gemset actual (g), revisión (r), arquitectura (a), un caracter unicode representando el intérprete (u) y la salida de imprimir “system” en Ruby (s). No tengo ni idea de dónde viene el caracter unicode. Con Ruby MRI muestra ⦿ y para JRuby ☯.

Me resulta bastante útil saber en qué versión de Ruby estoy trabajando, y también me recuerda actualizarla si me quedo en una versión patch antigua.

El post rvm-prompt: muestra la versión actual de Ruby en el prompt de tu terminal fue publicado originalmente en Picando Código.

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

Variable not found

Enlaces interesantes 446

mayo 31, 2021 06:05

Enlaces interesantes

Ahí van los enlaces recopilados durante la semana pasada, con muchas novedades de la Build 2021. Espero que os resulten interesantes. :-)

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

Xamarin / Maui

Otros

Publicado en Variable not found.

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

Blog Bitix

Herramientas de línea de comandos para monitorizar GNU/Linux

mayo 29, 2021 04:00

A veces un proceso del sistema consume gran cantidad de procesador, memoria o realiza muchas operaciones de lectura o escritura en el almacenamiento, simplemente se desea obtener o monitorizar cierta información del sistema. Hay varias herramientas en GNU/Linux para monitorizar los procesos del sistema, en almacenamiento, la red y temperatura de componentes, otros comandos permiten obtener infomación de los principales componentes hardware y software de la computadora.

Linux

En GNU/Linux hay una colección de herramientas de línea de comandos para obtener información y monitorizar el estado del sistema. Estas herramientas permiten dar respuesta a las preguntas ¿qué porcentaje del procesador están usando los procesos?, ¿cuánta memoria están usando los procesos y cuanta queda libre?, ¿cual es el espacio de almacenamiento usado y cuanto espacio de almacenamiento queda libre?, ¿cual es la tasa de transferencia para el disco y red? o ¿que temperatura tiene el procesador?.

Otra información que es posible conocer es cuanta memoria tiene el sistema, cuánto almacenamiento tiene el sistema en total, cual es el modelo de procesador, cuál es el modelo de tarjeta gráfica o cual es el entorno de escritorio.

Las siguientes herramientas permiten monitorizar en tiempo real el estado del sistema y obtener información del mismo, tanto para los procesos, almacenamiento y red.

Contenido del artículo

Monitorizar procesos del sistema

Estas herramientas permiten monitorizar los procesos del sistema, principalmente en consumo de procesador y memoria.

top

Esta herramienta permite monitorizar los procesos del sistema en tiempo real, ver cuánto porcentaje de procesador se está usando, cuanta memoria se está usando y qué procesos lo están haciendo además de conocer qué cantidad de memoria total tiene el sistema.

Es una herramienta que está entre la colección de herramientas instaladas por defecto en la mayoría de sistema GNU/Linux.

Com la tecla h se accede al panel de configuración en el que es posible personalizar los colores, ver las teclas de acceso para mostrar información y otras opciones de configuración que se mantienen entre diferentes ejecuciones del programa.

1
2
$ top -u picodotdev

top.sh

Comando top

Comando top Comando top

Comando top

htop

htop es una herramienta con el mismo propósito que top pero un poco más avanzada mostrando algo más de información, permite ver los procesos del sistema, consumo de procesador y memoria que están usando. Permite ver el uso del procesador por núcleo de CPU.

Al igual que top permite ordenar la lista de procesos por uso de procesador, uso de memoria, o tipos de CPU consumidos en orden descendente o en orden ascendente. Ofrece integración con el comando lsof.

Con la tecla F1 es posible ver las opciones de configuración de la utilidad.

1
2
$ sudo pacman -S htop
$ htop
htop.sh

Comando htop Comando htop

Comando htop

ps

El comando ps permite obtener información del estado de los procesos del sistema en el instante que se ejecuta. Posee varias opciones para filtrar los procesos que devuelve y su información de estado.

1
2
$ ps

ps.sh

Comando ps

Comando ps

bpytop

bpytop es una herramienta similar a top y htop que muestra información del sistema en tiempo real pero incluyendo también tráfico de red y almacenamiento además del procesador, memoria, procesos. Algunas estadísticas las muestra en formato gráfica utilizando texto para ver más rápidamente el porcentaje de utilización del recurso.

Con la tecla M se accede al menu del programa donde configurar los diferentes paneles de información que muestra el programa.

1
2
$ sudo pacman -S bpytop
$ bpytop
bpytop.sh

Comando bpytop

Comando bpytop Comando bpytop

Comando bpytop

free

El comando free permite ver la memoria física del sistema y la cantidad de memoria virtual o swap.

1
2
$ free -h

free.sh

Comando free

Comando free

Monitorizar almacenamiento

Las siguientes herramientas permiten monitorizar el almacenamiento del sistema. Permiten ver cuál es la capacidad de todas las unidades conectadas, cuanto espacio de almacenamiento queda libre en cada una de ellas y cual es la tasa de transferencia de almacenamiento que está utilizando el sistema tanto en lectura como en escritura.

df

El comando df permite ver información del almacenamiento de las unidades conectadas al sistema en el momento de ejecutar el comando. Permite ver en cada una de ellas su capacidad total y espacio libre restante que le queda, dispositivo hardware, sus particiones y puntos de montaje. Con la opción -h muestra los datos en unidades más comprensibles como KiB, MiB y GiB en vez de en bytes.

1
2
$ df -h

df.sh

Comando df

Comando df

iotop

El comando iotop permite ver en tiempo real la tasa de transferencia de lectura y escritura que están empleando los procesos del sistema. Se puede ordenar los procesos por cantidad de lectura o cantidad de escritura.

Esta herramienta requiere permisos de superusuario en su ejecución.

1
2
$ sudo pacman -S iotop
$ sudo iotop
iotop.sh

Comando iotop

Comando iotop

lsof

El comando lsof permite conocer cuales son los archivos abiertos por los procesos del sistema en el momento de ejecutar el comando.

1
2
$ sudo pacman -S lsof
$ lsof
lsof.sh

Comando lsof

Comando lsof

Monitorizar red

Las siguientes herramientas permite monitorizar la entrada y salida del trafico red.

netstat

El comando netstat permite ver cuales son las conexiones de red establecidas por los procesos del sistema y su estado. Con la opción -c monitoriza el tráfico de red en tiempo real o de forma continua.

1
2
$ sudo pacman -S netstat-nat
$ netstat-nat
netstat.sh

tcpdump

El comando tcpdump permite capturar el tráfico de red de una interfaz de red, para un puerto de red o para un nombre de host como origen o destino específico. Con la opción -c se limita la captura a un número de paquetes determinado. También es posible guardar la captura a un archivo para analizarlo una vez terminada la captura con la opción -w.

1
2
3
4
5
6
7
$ sudo pacman -S tcpdump
$ sudo tcpdump -i any -c5 -nn port 80
$ sudo tcpdump -i any -c5 -nn src 192.168.1.6
$ sudo tcpdump -i any -c5 -nn dst 192.168.1.6

$ sudo tcpdump -i any -c10 -nn -w webserver.pcap port 80
$ tcpdump -nn -r webserver.pcap
tcpdump.sh

Comando tcpdump

Comando tcpdump

Otras herramientas de monitorización

uptime

El comando uptime permite ver cuánto tiempo lleva en funcionamiento el sistema desde su último apagado o reinicio.

1
2
$ uptime

uptime.sh

Comando uptime

Comando uptime

iostat

El comando iostat muestra información de entrada y salida del sistema agrupando la información por dispositivo de almacenamiento. A diferencia de iotop no muestra cual es el proceso que está realizando la operación de entrada y salida.

1
2
$ sudo pacman -S sysstat
$ iostat
iostat.sh

Comando iostat

Comando iostat

sensors

El comando sensors permite obtener información de la temperatura que incorporan varios de los componente de las computadoras. Este comando muestra la temperatura del procesador para cada uno de sus núcleos, también del dispositivo de almacenamiento NVMe.

1
2
$ sensors

sensors.sh

Comando sensors

Comando sensors

watch

El comando sensors no muestra la temperatura en tiempo real, únicamente en el momento de su ejecución. El comando watch permite ejecutar un comando según el intervalo de tiempo deseado, utilizado con el comando sensors permite dotarle a este de monitorización casi en tiempo real configurando el intervalo de ejecución cada un segundo.

1
2
$ watch -n 1 sensors

watch.sh

Comando watch

Comando watch

neofetch

El comando neofetch muestra la información básica del sistema en el que se ejecuta. Aunque la información no es muy detallada contiene la descripción del los componentes de hardware y software principales del sistema como procesador, memoria, tarjeta gráfica, kernel, distribución de GNU/Linux, entorno de escritorio, número de paquetes instalados y algunas informaciones adicionales menos relevantes.

1
2
$ sudo pacman -S neofetch
$ neofetch
neofetch.sh

Comando neofetch

Comando neofetch

hwinfo

Aunque el comando hwinfo no es un comando de monitorización permite conocer diversa y detallada información del hardware del sistema. La información incluye datos sobre el modelo de procesador, modelo de placa base, unidad de almacenamiento y tarjeta gráfica. Esta información es útil en caso de necesitar algún paquete ya que algunos son específicos según el hardware del sistema por ejemplo los controladores de la tarjeta gráfica.

1
2
$ sudo pacman -S hwinfo
$ hwinfo --cpu
hwinfo.sh

Comando hwinfo Comando hwinfo

Comando hwinfo

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

Blog Bitix

Cómo ordenar arrays y colecciones de objetos en Java

mayo 27, 2021 06:00

Al implementar un algoritmo es común querer iterar los elementos de una colección en un orden según un criterio, por ejemplo, si se trata de números de menor a mayor, si se trata de fechas de menor a mayor y si se trata de personas por orden alfabético del nombre, de menor a mayor edad o de menor a mayor antigüedad en la empresa, también es posible la necesidad de iterar en orden inverso. El JDK de Java proporciona interfaces para implementar la ordenación de objetos y que ya implementan algunos de los algoritmos de ordenación conocidos.

Java

Entre las clases proporcionadas en el JDK de Java se proporciona un amplio conjunto de clases dedicadas a colecciones que son el fundamento de muchos algoritmos de programación y programas. Las clases de colecciones sirven para almacenar referencias a objetos, algunas colecciones no tiene un orden definido como Set y Map y otras definen un orden en la iteración de la colección como List pero no un orden entre los elementos, otras colecciones como TreeSet y TreeMap guardan los elementos ordenados según un criterio manteniéndose ordenada incluyendo al realizar inserciones y eliminaciones de elementos.

Hay varios algoritmos de ordenación conocidos como la ordenación por burbuja o bubble-sort, por inserción, merge-sort o quicksort cada uno con diferentes propiedades de complejidad o consumo de memoria. Normalmente no es necesario implementar estos algoritmos, sino que ya están implementados en las bibliotecas y en el caso de Java en las clases del JDK.

El usar colecciones ordenadas por un orden es una funcionalidad común al implementar programas lo único que es necesario es utilizar la colección adecuada y únicamente crear una clase que implemente la interfaz Comparator que determina el orden entre dos elementos, aplicando la comparación a los elementos de la colección con el algoritmo de ordenación ser ordena la colección.

Algoritmo de ordenación bubble-sort Algoritmo de ordenación merge-sort

Algoritmo de ordenación bubble-sort y merge-sort

Contenido del artículo

La interfaz Comparator

La interfaz Comprator es una interfaz funcional, por tener un único método a implementar, que recibe dos argumentos y devuelve un entero. Los argumentos son los dos objetos a comparar y el resultado indica cual es el orden de los elementos entre sí.

Si el resultado es un -1 se indica que el argumento a tiene un orden menor que b, si devuelve un 0 el orden de los elementos es el mismo y si devuelve un 1 el argumento a tiene un orden superior a b.

Estas son implementaciones de Comparator utilizando referencias de métodos.

1
2
Comparator<Person> ageComparator = Comparator.comparing(Person::getAge);
Comparator<Person> hireComparator = Comparator.comparing(Person::getHired);
Comparators.java

Para ordenar cadenas alfabéticamente también es necesario crear un comparador, sin embargo, la ordenación de cadenas alfabéticamente no es tan simple como utilizar el método comprateTo de la clase String. Para ordenar cadenas alfabéticamente en Java hay de tener en cuenta letras con tildes, mayúsculas y minúsculas que varían según el idioma de las palabras, el método comprteTo que podría usarse para crear un Comprator no es válido y puede producir resultados inesperados ya que el String.compareTo ordena según el código de los caracteres sin tener en cuenta tildes ni mayúsculas ni minúsculas.

Esta es la implementación de un Comparator que ordena cadenas en orden ascendente de forma alfabética utilizando la clase Collator.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private static class NameComparator implements Comparator<Person> {

    private Collator collator;

    public NameComparator() {
        this.collator = Collator.getInstance(new Locale("es"));
        collator.setStrength(Collator.TERTIARY);
    }

    @Override
    public int compare(Person o1, Person o2) {
        return collator.compare(o1.getName(), o2.getName());
    }
}
NameComparator.java

Con la clase Comparator es posible ordenar cualquier clase, en este ejemplo de clase Person se ordenan los objetos según su edad, fecha de contratación y nombre. Como en este caso es posible tener varias implementaciones de Comprator para una misma clase para ordenar los objetos por diferentes criterios.

La interfaz Comparable

Otra interfaz relacionada con la ordenación es la interfaz Comparable, es una interfaz que pueden implementar los objetos, la ordenación que se establece en la ordenación se le denomina el orden natural.

A diferencia de la clase Comparator de la que es posible crear varias implementaciones, las clases sólo pueden implementar una vez la interfaz Comparable.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static class Person implements Comparable<Person> {

    private String name;
    private int age;
    private LocalDate hired;

    public Person(String name, int age, LocalDate hired) {
        this.name = name;
        this.age = age;
        this.hired = hired;
    }

    ...

    @Override
    public int compareTo(Person o) {
        if (age < o.getAge()) {
            return -1;
        } else if (age > o.getAge()) {
            return 1;
        } else {
            return 0;
        }
    } 
}
Person-comparable.java

Cómo ordenar los elementos un array

La clase Arrays contiene varios métodos de utilidad entre ellos varios dedicados a la ordenación de los elementos de un array tanto para elementos primitivos como para objetos. Hay métodos que utilizan el la ordenación natural de la interfaz Comparable y hay métodos en los que es posible indicar la clase Comparator con el orden deseado entre los elementos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.github.picodotdev.blogbitix.javasort;

...

public class Main {

    public static void main(String[] args) {
        int[] array = new int[] { 64, 47, 33, 82, 91, 1, 45 };
        List<Integer> list = Arrays.stream(array).boxed().collect(Collectors.toList());
        List<Person> persons = List.of(
                new Person("Juan", 56, LocalDate.of(1982, 3, 26)),
                new Person("María", 24, LocalDate.of(2018, 8, 7)),
                new Person("Marisa", 63, LocalDate.of(2021, 4, 17)),
                new Person("Antonio", 41, LocalDate.of(2020, 5, 2))
        );

        Comparator<Person> nameComparator = new NameComparator();
        Comparator<Person> ageComparator = Comparator.comparing(Person::getAge);
        Comparator<Person> hireComparator = Comparator.comparing(Person::getHired);

        ...
    }

    ...
}
Main.java
1
2
3
4
5
System.out.println();
System.out.println("Array (sorted)");
int[] arraySorted = Arrays.copyOf(array, array.length);
Arrays.sort(arraySorted);
Arrays.stream(arraySorted).forEach(i -> System.out.println(i));
Array-sort.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Array (unsorted)
64
47
33
82
91
1
45

...

Array (sorted)
1
33
45
47
64
82
91

...
System.out-array

Cómo ordenar los elementos de una colección

Las clase Collections es el equivalente de la clase Arrays para las colecciones, también tiene métodos de utilidad en este caso para las colecciones. Tiene un método sort para ordenar los elementos de una lista según el orden natural y para ordenar los elementos de la lista según el criterio de un Comparator.

A tener en cuenta que tanto los métodos sort de Arrays como de Collections no devuelven una nueva instancia de array o colección ordenada sino que modifican la instancia de array o colección que se proporciona para ordenar.

1
2
3
4
5
System.out.println();
System.out.println("List (sorted)");
List<Integer> listSorted = new ArrayList<>(list);
Collections.sort(listSorted);
listSorted.stream().forEach(i -> System.out.println(i));
List-sort.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
System.out.println();
System.out.println("Persons (sorted, natural)");
List<Person> personsSortedNatural = new ArrayList<>(persons);
Collections.sort(personsSortedNatural);
personsSortedNatural.stream().forEach(i -> System.out.println(i));

System.out.println();
System.out.println("Persons (sorted, name)");
List<Person> personsSortedName = new ArrayList<>(persons);
Collections.sort(personsSortedName, nameComparator);
personsSortedName.stream().forEach(i -> System.out.println(i));

System.out.println();
System.out.println("Persons (sorted, age)");
List<Person> personsSortedAge = new ArrayList<>(persons);
Collections.sort(personsSortedAge, ageComparator);
personsSortedAge.stream().forEach(i -> System.out.println(i));

System.out.println();
System.out.println("Persons (sorted, hired)");
List<Person> personsSortedHired = new ArrayList<>(persons);
Collections.sort(personsSortedHired, hireComparator);
personsSortedHired.stream().forEach(i -> System.out.println(i));
Person-sort.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
List (unsorted)
64
47
33
82
91
1
45

Persons (unsorted)
Person(name=Juan, age=56, hired=1982-03-26)
Person(name=María, age=24, hired=2018-08-07)
Person(name=Marisa, age=63, hired=2021-04-17)
Person(name=Antonio, age=41, hired=2020-05-02)

...

List (sorted)
1
33
45
47
64
82
91

Persons (sorted, natural)
Person(name=María, age=24, hired=2018-08-07)
Person(name=Antonio, age=41, hired=2020-05-02)
Person(name=Juan, age=56, hired=1982-03-26)
Person(name=Marisa, age=63, hired=2021-04-17)

Persons (sorted, name)
Person(name=Antonio, age=41, hired=2020-05-02)
Person(name=Juan, age=56, hired=1982-03-26)
Person(name=María, age=24, hired=2018-08-07)
Person(name=Marisa, age=63, hired=2021-04-17)

Persons (sorted, age)
Person(name=María, age=24, hired=2018-08-07)
Person(name=Antonio, age=41, hired=2020-05-02)
Person(name=Juan, age=56, hired=1982-03-26)
Person(name=Marisa, age=63, hired=2021-04-17)

Persons (sorted, hired)
Person(name=Juan, age=56, hired=1982-03-26)
Person(name=María, age=24, hired=2018-08-07)
Person(name=Antonio, age=41, hired=2020-05-02)
Person(name=Marisa, age=63, hired=2021-04-17)
System.out-collection

Invertir el orden

La interfaz Comprable establece un orden ya sea ascendente o descendente según el criterio que implementa, si en un caso se desea el orden inverso del comprador la propia interfaz Comparator permite obtener un Comparator con el orden inverso al de la instancia. También es posible obtener un comprador que ordene las referencias nulas al principio si es que hay alguna en la colección.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
System.out.println();
System.out.println("Array (sorted descending)");
Integer[] arraySorted = Arrays.stream(array).boxed().toArray(Integer[]::new);
Arrays.sort(arraySorted, Collections.reverseOrder());
Arrays.stream(arraySorted).forEach(i -> System.out.println(i));

System.out.println();
System.out.println("List (sorted descending)");
List<Integer> listSortedNatural = new ArrayList<>(list);
Collections.sort(listSortedNatural, Comparator.<Integer>naturalOrder().reversed());
listSortedNatural.stream().forEach(i -> System.out.println(i));

System.out.println();
System.out.println("Persons (sorted descending, age)");
List<Person> personsSortedAge = new ArrayList<>(persons);
Collections.sort(personsSortedAge, ageComparator.reversed());
personsSortedAge.stream().forEach(i -> System.out.println(i));
Sort-reverse.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Array (sorted descending)
91
82
64
47
45
33
1

List (sorted descending)
91
82
64
47
45
33
1

Persons (sorted descending, age)
Person(name=Marisa, age=63, hired=2021-04-17)
Person(name=Juan, age=56, hired=1982-03-26)
Person(name=Antonio, age=41, hired=2020-05-02)
Person(name=María, age=24, hired=2018-08-07)
System.out-reverse
Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run

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

Picando Código

Elastic 7.13.0: Actualizaciones de los clientes oficiales Elasticsearch y Enterprise Search

mayo 26, 2021 06:42

El martes pasado se publicó la versión 7.13.0 de Elastic: Las mejoras clave incluyen búsqueda con buena relación costo-efectividad en más datos a través de snapshots buscables y el nivel congelado, mejoras de datos impulsados por analistas sobre la marcha con campos de tiempo de ejecución y más integraciones con Microsoft (muchos más detalles en el enlace). Como en cada nueva versión del Stack, esto significó también que en mi equipo publicamos los clientes oficiales para Elasticsearch y Elastic Enterprise Search.

Clientes Elasticsearch

Tenemos clientes oficiales para Elasticsearch en Ruby, Python, PHP, Perl, JavaScript, .NET, Rust, Go y Java. Soportamos todas las APIs de Elasticsearch y tenemos todo un sistema de testeo contra la especificación lo que nos asegura compatibilidad completa. La mayoría de los clientes ya están disponibles en la versión 7.13.0 en sus respectivos sistemas de dependencias e instalación de paquetes. Pueden leer más sobre los clientes en este enlace.

Elastic 7.13.0

Clientes Enterprise Search

El proyecto de Elastic Enterprise Search empezó con los clientes Ruby y Python, y de a poco se irán sumando más lenguajes. Una de las novedades de la versión 7.13.0 es que se publica la primera versión estable del cliente Elastic Enterprise Search para PHP. Ha sido genial trabajar en equipo con mis compañeros del equipo de Clientes y el equipo Enterprise Search. La suerte que tuve de terminar en este trabajo es algo que no doy por sentado. Tenemos un equipo políglota trabajando en código abierto en tecnologías interesantes y con gente que sabe muchísimo y es muy buena gente.

En el post Elastic Enterprise Search – cliente oficial en Ruby explico más qué son Elastic Enterprise Search, Workplace Search y App Search, así como detalles de la implementación del cliente en Ruby.

En cuanto a las novedades técnicas, hay varias cosas nuevas interesantes en la API 7.13.0 de Enterprise Search, particularmente en lo que respecta a Workplace Search:

Nuevos métodos de autenticación en Workplace Search

El sistema soporta autenticación Básica HTTP y autenticación a través de tokens Elasticsearch a partir de esta versión. La API también, así que el cliente lo soporta también. Todos los APIs de Workplace Search soportan ambos métodos de autenticación y tokens de administración de Workplace Search (la autenticación que se podían usar hasta ahora), pero todavía necesitamos pasar por el proceso de OAuth para Búsqueda y Analítica.

El código funciona así:

host = 'https://id.ent-search.europe-west2.gcp.elastic-cloud.com'
basic_auth = { user: 'enterprise_search', password: 'changeme' }

workplace_search_client = Elastic::EnterpriseSearch::WorkplaceSearch::Client.new(
  host: host,
  http_auth: basic_auth
)

Y en el caso de tokens, en vez de un hash con usuario y contraseña, pasamos un String con el token ya sea en la inicialización o seteándolo como propiedad.

Nuevas APIs

Particularmente interesantes las APIs que nos permiten gestionar fuentes de contenido “content sources”. Workplace Search funciona en base a fuentes de contenido que se unen en una misma experiencia de búsqueda, y ahora podemos integrar una fuente nueva desde nuestro código Ruby. Si tenemos las credenciales de autenticación básica HTTP, ya no necesitamos crear las fuentes de contenido desde la interfaz web:

  • document: Obtener un documento por ID de una fuente de contenido específica.
  • delete_all_documents: Elimina todos los documentos de una fuente dada.
  • content_source: Obtiene una fuente de contenido por ID
  • create_content_source: Crea una fuente.
  • delete_content_source: Elimina una fuente por ID.
  • list_content_sources: Lista todas las fuentes.
  • put_content_source: Actualiza una fuente de contenidos.

Un ejemplo de código:

# Crear una fuente de contenido:
client.create_content_source(name: 'picando-codigo')

# Obtener una fuente de contenido por ID:
content_source_id = client.create_content_source(name: 'libros').body['id']
client.content_source(content_source_id)

# Eliminar una fuente de contenido por ID:
client.delete_content_source(content_source_id)

# Obtener una lista de todas las fuentes de contenido:
client.list_content_sources

# Actualizar una fuente de contenido:
body = {
  name: new_name,
  schema: { title: 'text', body: 'text', url: 'text' },
  display: { title_field: 'title', url_field: 'url', color: '#f00f00' },
  is_searchable: true
}
client.put_content_source(id, body: body)

La verdad ambos productos se están poniendo cada vez mejor, y hay mucha cosa nueva e interesante en el horizonte para futuras versiones!
Si tienen cualquier duda sobre el cliente para Elasticsearch o Elastic Enterprise Search, estoy a las órdenes para intentar ayudar 🙂

El post Elastic 7.13.0: Actualizaciones de los clientes oficiales Elasticsearch y Enterprise Search fue publicado originalmente en Picando Código.

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

Picando Código

Siete días en el Picandoverso – Mayo 4: el lejano y salvaje verde

mayo 26, 2021 11:00

Otra semana que pasa y se va el mes de mayo, y ya nos acercamos a la mitad de 2021. En Escocia el tiempo se olvidó que era primavera y ha habido un clima bastante particular que después de un largo invierno de encierro genera bastante mal humor. Pero se supone que se vienen días mejores. Mientras tanto es hora de golpear las teclas de mi teclado continuamente en armonía hasta formar esta manifestación de píxeles en tu pantalla a la que denomino “Siete días en el Picandoverso”. Espero que se haya entendido la referencia a Futurama en el título del post…

Siete días en el Picandoverso - Mayo 4: en lejano y salvaje verde

Tecnología

👩‍💻 Un estudio de GitHub reveló que (¡sorpresa!) las interrupciones y reuniones arruinan la productividad de los programadores. Tras un estudio que probablemente no tenga demasiado peso científico debido a su corto alcance, descubrieron que: “Con interrupciones mínimas o nulas, desarrolladores tenían un 82% de chance de tener un buen día, pero cuando se les interrumpía la mayor parte del día, las chances de tener un buen día caía a 7%”.

Otro descubrimiento que no sorprende es que odiamos las reuniones: “Con un promedio de dos reuniones al día las chances de que desarrolladores se sientan como que han progresado hacia sus metas fue de 74%, pero aumentando a un promedio de tres reuniones por día se traducía en un 14% de chance de progresar”. La noticia en The Register, y el estudio en GitHub.

🪄🎩 Se publicó la versión 3.0 de Magit: una interfaz de usuario basada en texto para Git que se concentra en flujos de trabajo optimizados. Los comando se invocan usando secuencia mnemotécnicas de teclas. Intenté usarlo por un tiempo, pero estoy tan acostumbrado a la terminal con Git que al final termino volviendo a la terminal para hacer cosas como rebases interactivos. Debería darle otra oportunidad y aprender a hacer esas cosas más complejas que git add, commit y push.

🔗 Si usamos curl, su creador nos pide que participemos de la encuesta de curl 2021: Por octavo año consecutivo volvemos en 2021 con la encuesta anual de curl. Acaba de publicarse el formulario y amaría que gastes 10 minutos de tu ocupada vida en decirnos cómo crees que funciona curl, qué no funciona y que deberíamos hacer. No tememos traqueo en el sitio web y no tenemos métricas o medidas de uso de curl o la biblioteca libcurl. La única forma de aprender lo que los usuarios y la gente en general piensa de nosotros y cómo funciona curl es preguntando. Enlace a la encuesta.

🔥🦊 ❤ En un blog post de Mozilla Hacks, cuentan cómo mejoraron la estabilidad de Firefox en Linux. Además de un montón de detalles interesantes sobre cómo obtienen la información necesaria a través de los reportes de error de los usuarios, cuentan cómo la colaboración entre distintos proyectos de software libre y código abierto resultó en mejoras de las herramientas y contribuciones entre Firefox y otros proyectos como Fedora, Debian, Mesa, GTK, glib, PCSC, SQLite y más. Una historia de amor de código abierto.

☠ Aparentemente Internet Explorer todavía no estaba muerto. Yo creía que no existía más, sobretodo ahora que Google Chrome es el nuevo Internet Explorer 6, pero Microsoft anunció que dejará de darle soporte al navegador el 15 de junio de 2022 en favor de Microsoft Edge. Así que a partir de esa fecha, va a volver a ser cool usar Internet Explorer

💰 Se armó relajo en IRC. El staff de la red IRC de software libre y código abierto más grande del mundo, Freenode, renunció tras una adquisición hostil. Aparentemente un narcisista Trumpeano millonario dueño de un servicio de VPNs compró de manera sospechosa la red a uno de los dirigentes que no tenía realmente derecho a venderla. Yo les creo, nunca confíen en los millonarios, siempre son los malos. Una lástima porque la red Freenode ha sido una via de comunicación oficial para muchos proyectos de código abierto y software libre por muchos años. El canal #picandocodigo sigue ahí, por más que su uso haya sido muy limitado en los últimos años…

La nueva red que busca reemplazar a Freenode se llama Libera.chat, y varios proyectos grandes ya mudaron sus canales para ahí. Podemos leer más sobre la debacle en este enlace.

Ruby

💎 En su serie de tips “One Ruby Thing”, Andy Croll nos cuenta cómo optar por deshabilitar el rastreo FLoC en Rails. Nos recuerda que Google es una megacorporación distópica monopólica que utiliza su dominio del mercado para enriquecer su monopolio de la publicidad online y que debemos hacer lo posible para no alimentar a la bestia. Como siempre, recomiendo pasarse a un navegador web más ético como Firefox en vez de Google Chrome.

💎 El 8 de julio de 2021 se realiza la primera conferencia sobre el lenguaje de programación Crystal. En un tweet, se anunció que Matz va a estar dando una charla en la conferencia. “Crystal se para en los hombros de muchos gigantes, y Ruby es uno de los más altos”. Podemos comprar la entrada en este enlace.

💎 Ya he compartido varios enlaces sobre Ractors en Ruby 3, pero otra característica nueva es la clase Fiber::SchedulerInterface. Nos permite escribir schedulers más avanzados basados en bucles de eventos para entrada y salida sin bloquear en Ruby (a lo frameworks como EventMachine). Recientemente resurgió un artículo muy bueno sobre el tema Ruby 3.0 and the new FiberScheduler interface. Mientras leía esto me colgué a probar cosas y tengo algunas ideas para implementar más cosas, tengo investigación por delante…

💎🧑‍🚀 Mañana 27 de mayo es el último jueves del mes, por lo que vuelve Ruby Galaxy: el meetup virtual de Ruby. Se va a transmitir a través de su canal de Twitch a las 19:00 UTC. Todavía no estaban anunciadas las charlas cuando escribí esto, pero de todas formas voy a estar en el stream y comentando en el chat.

Videojuegos

🦔 Parece que Sonic va a tener un video para mostrar novedades que se vienen como parte del 30º Aniversario de Sonic. El jueves 27 de mayo podemos sintonizar a youtube.com/sonic y twitch.tv/sonic a las 11:00 AM (Ciudad de México) / 1:00 PM (Buenos Aires) para una primera vista a los próximos proyectos, colaboraciones y eventos. Podemos seguir a Sonic en Twitter en sus cuenta para para Latinoamérica.

🍌 Se subió a archive.org la iso de un DVD llamado “The Future of Videogames Vol. 1”, basado en E3 2001. Cuenta con un montón de trailers en una resolución bastante buena de 720x480. Habrá que descargar ese ISO a ver qué más trae. Entre los trailers se encuentra el de Donkey Kong Racing, un juego originalmente desarrollado para Game Cube pero que fue cancelado tras la compra de Rare por Microsoft:
YouTube Video

🎮 He estado jugando Mega Man Zero en Mega Man Zero Legacy Collection en Nintendo Switch. Ya me queda poco para terminarlo, pero siento que tengo que compartir este Honest Trailer del juego.

Películas/Televisión

📺 Netflix presentó una imagen de la serie animada MECH CADETS, basada en los cómics de Boom! Studios Mech Cadet Yu por Greg Pak y Takeshi Miyazawa. Va a ser producida por Boom! Studios, el estudio japonés Polygon Pictures Inc. y Aaron Lam (escritor y productor ejecutivo). Vendrá acompañada de otras dos series y películas de creadores asiático-americanos.

🎥 Marvel publicó el primer trailer de la película The Eternals, y se ve muy bien. Esperemos que se pueda ir a ver al cine:
YouTube Video

Picando Código

Los posts de Picando Código pueden seguirse por:

También estoy en Twitter y Mastodon, donde además de compartir lo que se publica en el blog publico alguna cosa más.

7 días en el Picandoverso recientes:

El post Siete días en el Picandoverso – Mayo 4: el lejano y salvaje verde fue publicado originalmente en Picando Código.

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

Picando Código

Mini pique: Git mv y ver la historia de archivos que movimos

mayo 25, 2021 01:30

Estoy trabajando en un cambio bastante grande en el cliente Ruby de Elasticsearch. La historia resumida es que el código de una de las gemas va a ser combinado en otra (#1274). Trabajando en ese código, moví muchos archivos de un lugar a otro, pero me resultaba importante mantener la historia o el git log de todos los archivos que moví.

Podemos usar el comando git mv aunque es lo mismo que mover los archivos, eliminar los originales del control de versiones con git rm y agregar los nuevos. Git va a reconocer el cambio como que “renombramos” los archivos. De Git FAQ: Git tiene un comando para renombrar git mvpero es sólo por conveniencia. El efecto es indistinguible de eliminar el archivo y agregar otro con distinto nombre pero el mismo contenido.

Ahora, después de haber movido varios archivos fui a verificar que la historia se mantuviera. Y en principio al usar git log, sólo veía desde el commit en que los había movido. Así que salí a buscar y encontré en la documentación de git log:

–follow
Seguir listando la historia de un archivo más allá de los renombres (funciona sólo para un archivo único).

Así que la historia sigue estando ahí, sólo que hay que usar git log – follow archivo.

El post Mini pique: Git mv y ver la historia de archivos que movimos fue publicado originalmente en Picando Código.

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

Variable not found

Cómo saber si un objeto no es nulo (edición C# 9)

mayo 25, 2021 09:56

.NET Core

Hace algunos años hablábamos de que la forma más correcta de determinar si un objeto es nulo en C# era utilizando el operador is:

var invoice = _invoiceRepository.GetById(18);
if(invoice is null)
{
// Hacer algo
}

Como vimos en su momento, esta opción era mejor que utilizar una comparación directa como invoice == null porque el operador de igualdad podía ser sobrecargado y, por tanto, su comportamiento podría ser modificado, mientras que el operador is no es sobrecargable.

Sin embargo, al comenzar al usar esta fórmula, encontrábamos un pequeño inconveniente cuando queríamos determinar justo lo contrario, es decir, saber cuándo un objeto no es nulo, pues la sintaxis se volvía algo más pesada:

var invoice = _invoiceRepository.GetById(18);
if(!(invoice is null))
{
// Hacer algo
}

La introducción de los patrones de combinación and, or y not de C# 9 nos ponen la cosa bastante más sencilla. Cuando usamos esta versión del lenguaje, podemos expresar esa comparación con algo tan elegante como lo siguiente:

var invoice = _invoiceRepository.GetById(18);
if(invoice is not null)
{
// Hacer algo
}

Recordad que C#9 es el usado por defecto en proyectos .NET 5, pero también podemos utilizarlo con versiones anteriores de .NET Core o .NET Framework si estamos utilizando una versión actualizada de Visual Studio y configuramos el proyecto para que use la última versión disponible, bien desde el cuadro de diálogo Propiedades del Proyecto > Build > Advanced, o bien añadiendo al .csproj las siguientes líneas:

<Project ...>
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
...
</Project>

Aparte, estos patrones de combinación pueden ser utilizados en otros escenarios. Los veremos más adelante, en un post donde profundizaremos en ello :)

Publicado en Variable not found.

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

Picando Código

Pocket Bravery – nuevo juego de pelea basado en los clásicos de los 90s como Street Fighter y King Of Fighters

mayo 24, 2021 11:00

Pocket Bravery es un juego de lucha original inspirando en los clásicos como Street Fighter, Fatal Fury y King of Fighters. Los gráficos están hechos a mano en pixel art, y la estética está influenciada por los diseños de los juegos de lucha del Neo Geo Pocket como Pocket Fighters y varios más que podemos encontrar en NEOGEO POCKET COLOR SELECTION Vol. 1.

Está siendo desarrollado por Statera Studio, un estudio de Río de Janeiro, Brasil, y buscan financiarlo con una campaña en Indiegogo.

Pocket Bravery

Apunta a tener una jugabilidad dinámica y responsiva, enfocada en las animaciones de los personajes con el objectivo de obtener fluidez durante las peleas. En la página de la campaña señalan por ejemplo un ataque con 22 cuadros individuales de animación. A pesar de la inspiración en leyendas del género, Pocket Bravery fue creado para ser un juego de luchas nuevo y único, con personajes originales y un sistema de combate muy interesante.

Hay un demo disponible en Steam, y también podemos agregarlo a nuestra wishlist. Si bien por ahora está disponible únicamente para Windows, lo pude jugar perfectamente en Linux con Proton. El demo se juega súper fluido y está bastante bueno (recomiendo mirar el video al final de este post para apreciar las animaciones). Me gustó mucho la animación de los personajes y el arte de los escenarios. Los personajes tienen mucha personalidad “local”. Se caracterizan por 4 atributos en su gameplay: fuerza, velocidad, rango y dificultad. También cuentan en la pantalla de selección con una biografía que incluye su nacionalidad, nombre completo, habilidades, arquetipo y algunos de los comandos de ataque.

Pocket Bravery

Los dos personajes principales parecen ser Nuno y Sebastian, un brasilero y un escocés respectivamente. La “internacionalidad” del juego se siente un poco mejor que en Street Fighter, donde el brasilero Blanka hacía ruidos y gruñidos únicamente. Hay toda una historia por detrás que incluye poderes sobrenaturales y una organización criminal global. Cada personaje cuenta también con una biografía, lo que le termina dando algo de razón al por qué estas personas deciden irse a las piñas y patadas.

Por ahora han mostrado 10 escenarios, 6 de los cuales están basados en ubicaciones reales y nos invitan a intentar reconocerlas. Entre las pantallas a elegir se encuentran Portugal, Brasil, Escocia, Nigeria, Estados Unidos, Japón, China, India y Rusia. La de Escocia me da que está inspirada en George Square en Glasgow… Los escenarios cuentan con animaciones y variaciones para cada uno de los 3 rounds, y se diseñaron para coincidir con los personajes y la historia general del juego.

Pocket Bravery

Las imágenes son del demo, así que la versión final puede ser distinta. Se está desarrollando con la accesibilidad en mente desde un principio, por ejemplo con efectos para personas daltónicas. Va a estar disponible en al menos 12 idiomas incluyendo Portugués, Español e Inglés.

En cuanto al control, usa cuatro botones con dos piñas y dos patadas. Como es tradicional, podemos realizar distintas combinaciones para ejecutar poderes especiales, super especiales y ataques finales. Además de estar tan cargado de personalidad con su estilo propio, tiene varias características que lo hacen muy interesante. Cuenta con un sistema “Elemental”, donde cada personaje tiene un elemento que representa su fuerza, física o sobrenatural. Cargando las tradicionales barras podemos ejecutar ataques elementales. También cuenta con un sistema de cancelación, para cancelar ataques especiales, y el viejo y querido “Taunt” en el que provocamos al contrincante con distintos resultados según la personalidad y relación entre los personajes.

Va a incluir los modos de pelea clásicos: Arcade, VS, Online, Entrenamiento y Combo Maker – un nuevo modo original para enseñar a principiantes de los juegos de lucha cómo realizar combos.

Statera nos cuenta que aproximadamente entre un 50 y 60% del contenido del juego ya está terminado. Si se logra financiar a través de la campaña de Indiegogo, se va a empezar a trabajar en el modo online a partir de Julio de este año. Y alcanzada la meta, Pocket Bravery va a ser publicado para PC (Linux, Mac y Windows), Nintendo Switch, Playstation 4 y 5, XBox One/X/S durante el segundo semestre de 2022. ¡No puedo esperar a poder jugarlo en Switch!

Si te gustan los juegos de pelea de este estilo, te invito a sumarte a la campaña en Indiegogo. Como es tradicional hay varias metas a alcanzar con más financiación, incluyendo un sistema “Tag” (el tradicional sistema de batallas dobles popularizado por títulos como KOF) y asistentes (a lo Marvel Vs Capcom), modo historia, edición de colores de personajes, versión móvil del juego, personajes extra y más. Ya me sumé a la campaña para jugarlo en Steam cuando esté disponible, pero lo espero también para Nintendo Switch.

YouTube Video

El post Pocket Bravery – nuevo juego de pelea basado en los clásicos de los 90s como Street Fighter y King Of Fighters fue publicado originalmente en Picando Código.

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

Variable not found

Enlaces interesantes 445

mayo 24, 2021 06:05

Enlaces interesantes

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

Por si te lo perdiste...

.NET Core / .NET

ASP.NET Core / ASP.NET / Blazor

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

    Web / HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Xamarin / Mobile

    Otros

    Publicado en Variable not found.

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

    Blog Bitix

    Conceptos teóricos generales de los procesadores de computadora

    mayo 20, 2021 08:00

    Los procesadores es uno de los principales circuitos integrados que posee toda computadora. Es el encargado de ejecutar el código de los programas y gobierna el resto de componentes. A pesar de su pequeño tamaño son elementos con gran complejidad, para aumentar su rendimiento se aplican diferentes técnicas que los procesadores han incorporando de forma paulatina. Al hablar de procesadores hay varios conceptos que es necesario comprender.

    Intel

    AMD

    ARM

    Los procesadores actuales tienen una capacidad de cómputo en varios órdenes de magnitud a la de tan toso unas décadas antes. Buena parte de ese rendimiento es conseguido a través de la mejora de los procesos litográficos que permiten fabricar transistores cada vez más pequeños. Con transistores más pequeños se mejoran varios aspectos vitales de los procesadores entre ellos se reduce su consumo de energía, permiten añadir más transistores en el mismo espacio físico para implementar cachés más grandes o implementar conceptos que mejoren el rendimiento y finalmente son más baratos de fabricar.

    Desde la aparición de los circuitos integrados que sustituyeron a las válvulas de vacío, según la ley de Moore cada dos año en número de transistores que incluye un procesador se duplica, esta ley se ha estado cumpliendo hasta ahora y por consiguiente la potencia de los procesadores. Uno de los primeros microprocesadores de Intel como el 4004 tenía únicamente 2300 transistores a 10 micrómetros, un Intel Core o un Ryzen llegan superan los 2 mil millones de transistores a 7 nanómetros.

    Varios de los conceptos teóricos aplicados en los procesadores está destinado a aumentar el rendimiento en monohilo, así ha sido hasta el desarrollo de los procesadores con múltiples núcleos tanto de Intel como de AMD y también de ARM. Desde el año 2010 el aumento de rendimiento de los procesadores también se consigue mediante la inclusión de mayor número de núcleos, para tareas paralelizables. Un amyor número de núcleos consigue un aumento de rendimiento en una proporcion similar a la proporción de aumento de núcleos. Crear procesadores multinúcleo es otra forma de seguir aumentando el rendimiento de forma más sencilla que mejorando aún más el rendimiento en monohilo. Los procesadores actuales van desde los 4 núcleos hasta cantidades como 16 para ordenadores de escritorio y hasta 64 o más para computadoras destinadas a hacer funciones de servidor.

    Procesador Intel 4004 Procesador Intel 8086 Procesador Pentium

    Procesadores Intel 4004, 8086 y Pentium

    Procesador AMD Athlon XP Procesador Sun UltraSparc II Procesador RISC-V

    Procesadores de AMD, Sun Ultra Sparc y SiFive RISC-V

    Los siguientes apartados son una explicación sencilla, básica y resumida de los conceptos que se utilizan en los procesadores de los que se puede. Esta información está ampliada en la wikipedia.

    Contenido del artículo

    Arquitectura

    Los computadores siguen una organización definida en el modelo de von Neumann en el que una computadora consta de:

    • Una unidad de procesamiento que contiene lógica de aritmética y los registros del procesador.
    • Una unidad de control que contiene un registro de instrucción y un contador de programa.
    • Memoria que almacena los datos y las instrucciones del programa.
    • Almacenamiento externo.
    • Mecanismos de entrada y salida.

    Esquema de computadora Arquitectura de Von Neumann

    Esquema de computadora y arquitectura de Von Neumann

    Componentes de un procesador

    Los componentes son los siguientes:

    • Caché: es una memoria pequeña comparada con la memoria principal o el almacenamiento secundario pero mucho más rápida que estas.
    • Unidades funcionales: la unidad de lógica aritmética o ALU permite realizar operaciones del cálculo matemático y binarias, la unidad de coma flotante o FPU permite realizar operaciones en coma flotante. La unidad de predicción de de saltos permite predecir cuál será el resultado de una condición, otras unidades son la unidad de generación de direcciones o AGU, la load–store unit translation lookaside buffer o TLB permite traducir direcciones virtuales a direcciones físicas y es parte de la unidad de gestión de memoria o MMU. El controlador integrado de memoria o IMC gestiona las transferencias entre la memoria principal y el procesador.
    • Registros: son unas pequeñas zonas de memoria en las que operan las instrucciones del procesador.
    • Unidad de control: es la encargada de enviar las señales a las otras unidades del procesador.

    Tamaño de palabra

    El tamaño de palabra es la unidad de datos nativa del procesador media en número bits. Los registros del procesador suelen tener tener el mismo tamaño de palabra que el procesador y la máxima cantidad de información que se suele poder transferir desde y a la memoria en una única operación coincide con el tamaño de palabra. Los procesadores x86-64 tiene un tamaño de palabra de 64 bits.

    Endianness

    La arquitectura de un procesador define cómo se almacenan los datos en memoria, puede ser de dos formas litte-endian o big-endian. En la primera el byte de datos menos significativo se almacena en la dirección de memoria más baja, por el contrario en big-endian el byte más significativo se almacena en la dirección más baja.

    Que una arquitectura de procesador elija entre estos dos tipos de endianness no es significativo, simplemente se ha de mantener de forma consistente al almacenar, cargar los datos y transmitirlos a otras computadoras.

    En el protocolo IP se utiliza big-endian, por el contrario el la mayoría de procesadores de arquitectura de procesadores como x86, ARM y RISC-V utilizan litte-endian.

    Big endian Little endian

    Big y little endian

    Almacenamiento de computadora, NUMA y UMA

    El significado de las siglas de NUMA son Non-uniform memory access y de UMA son Uniform memory access, en el primer caso el tiempo de acceso a memoria no es uniforme y depende de la ubicación del la memoria relativa al procesador, en este caso para un procesador es más rápido acceder a su memoria local que a la memoria de otro procesador.

    Los procesadores actuales son significativamente más rápidos que la memoria principal por ello suelen tener pequeñas cachés de memoria más rápidas, al mismo tiempo se evita que varios procesadores compitan por el acceso a la memoria principal. Este modelo NUMA mejora el rendimiento pero ocasiona el problema que cuando un procesador requiere los datos de otro ocasiona que los datos han de ser movidos con una penalización en la latencia.

    AMD lo implementó el almacenamiento NUMA en los procesadores Opteron con HyperTransport e Intel con QPI.

    Memoria y jerarquía de memoria

    La memoria contiene los datos y las instrucciones de los programas, el procesador lee los datos de la memoria los carga en las cachés y los registros del procesador, realiza el cálculo deseado y los resultados son almacenados de nuevo en la memoria principal.

    Hay cuatro niveles de almacenamiento.

    • Interno: formado por los registros del procesador y sus cachés internas.
    • Memoria principal: formada por la memoria RAM volátil cuyo contenido se pierde si se apaga la computadora.
    • Secundaria: es una memoria persistente conectada a la computadora y que conserva los datos aún después de apagar la computadora.
    • Terciaria: es una memoria que se puede extraer y conectar del equipo bajo demanda.

    La memoria se puede diferenciar en las siguientes propiedades:

    • Volatilidad: si persiste al apagado de la computadora.
    • Mutabilidad: si es posible cambiar su contenido.
    • Accesibilidad: el modo de lectura permitido si es aleatorio o ha de ser secuencial.
    • Direccionamiento.
    • Capacidad: el tamaño de la memoria.
    • Rendimiento: el tiempo de acceso necesario.
    • Uso de energía.
    • Seguridad.
    • Vulnerabilidad y confiabilidad.

    La diferencia de velocidad de procesamiento de la CPU y la velocidad de acceso a la memoria principal y secundaria hace que para aumentar el rendimiento de la CPU sea necesario una memoria pequeña pero más rápida que la memoria principal en el procesador. En la jerarquía de memoria en el nivel superior su latencia es inferior, su velocidad de transferencia es mayor pero su tamaño es menor. Si la tasa de aciertos en la caché es alta el procesador aumenta significativamente al no ser penalizado por la latencia de acceso a la memoria principal.

    Jerarquía de caché Jerarquía de caché

    Jerarquía de caché

    Memoria virtual

    La memoria principal del sistema es limitada aun siendo de varias decenas de GB. La memoria virtual es una técnica que permite ampliar la memoria del sistema utilizando el siguiente nivel de la jerarquía de memoria , normalmente el almacenamiento persistente de mayor capacidad.

    El sistema operativo con ayuda de funciones de gestión de memoria implementadas en el procesador se encarga de mover fuera de la memoria principal la que no se está utilizando y de cargar en la memoria principal la que se necesite.

    Microcódigo

    El microcódigo es un software que traduce las instrucciones en operaciones a ejecutar en el chip. Esta traducción de instrucciones permite los desarrolladores de chips flexibilidad en la implementación. Por ejemplo, en una versión mejorada del chip se puede soportar el mismo conjunto de instrucciones que versiones anteriores o soportar diferentes conjuntos de instrucciones en el mismo chip.

    Arquitecturas de conjunto de instrucciones

    La arquitectura del conjunto de instrucciones o ISA es el modelo abstracto de una computadora, define los tipos de datos soportados, los registros, el hardware de soporte para la memoria principal y el modelo de entrada y salida. También forma parte el conjunto de instrucciones soportadas y su codificación binaria.

    El conjunto de instrucciones es una de las características mas relevantes de un procesador, los programas compilados a código máquina están codificados en forma binarias según las instrucciones de una ISA.

    Instrucción addi de un procesador MIPS

    Instrucción addi de un procesador MIPS

    Conjuntos de instrucciones

    El conjunto de instrucciones soportados por un procesador depende de su arquitectura. La arquitectura más popular en ordenadores de escritorio es la x86, en los dispositivos móviles como teléfonos inteligentes es ARM, otras arquitecturas alternativas son RISC-V que es una arquitectura abierta sin costes de licencia, otras son MIPS, POWER y Sparc.

    Los lenguajes de programación de alto nivel como C traducen los programas a un binario ejecutable por la computadora, Java traduce los programas en la compilación a un bytecode independendiente de la arquitectura del procesador y es en tiempo de ejecución cuando se realiza la traducción del bytecode a las instrucciones que entiende el procesador.

    Dos aproximaciones al conjunto de instrucciones son CISC que proporciona un conjunto amplio de instrucciones complejas y RISC que proporciona un conjunto reducido de instrucciones más simples. El equivalente de una instrucción CISC pueden ser varias RISC. Los procesadores CISC tienen la ventaja de hacer que los programas tengan menor tamaño pero hace que los procesadores sean más complejos, por el contrario en los procesadores RISC los programas son más grandes al necesitar más instrucciones pero los procesadores son más sencillos. La diferencia entre un procesador CISC y RISC no es muy significativa con la utilización de microcódigo.

    Las instrucciones se agrupan en las siguientes categorías:

    • Establecimiento de datos y operaciones con memoria cómo establecer un registro con un valor y mover datos de un registro a otro.
    • Operaciones aritméticas y lógicas como sumar, restar, multiplicar o dividir entre los valores de dos registros, realizar operaciones binarias o comprar dos valores.
    • Control de flujo como saltar a otro punto del programa, salto condicional o llamada a una función.
    • Instrucciones de coprocesador como cargar o almacenar datos en los registros del procesador.

    Las instrucciones single instruction multiple data o SIMD operan múltiples datos de forma paralela con una única instrucciones, esto mejora significativamente el rendimiento respecto si hubiera que ejecutar una instrucción por cada dato. Es una forma diferente de conseguir paralelismo.

    En la arquitectura x86 las instrucciones MMX, SSE y AVX son variantes de instrucciones SIMD. El primer procesador con instrucciones MMX fabricado por Intel fue el Pentium MMX. El primer procesador de Intel con instrucciones SSE fue el Pentium 3 y el primero con AVX fue a partir de la familia Sandy Bridge.

    Ciclo de ejecución del procesador

    La principal tarea de un procesador es ejecutar instrucciones, para ello el procesador sigue un ciclo de obtener la siguiente instrucción de la memoria principal según el contador del programa, decodificación en la que se determina que instrucción se ejecuta y ejecución en la que se utilizan los diferentes componentes del procesador como la unidad de lógica aritmética.

    Pipelining de instrucciones

    Los procesadores tiene varios componentes, para mantenerlos todos en funcionamiento con el objetivo de maximizar el rendimiento las instrucciones se ejecutan en varias etapas. En un momento determinado el procesador posee varias instrucciones en diferentes etapas de ejecución. Cada uno de estos pasos de ejecución es un segmento de ejecución, denominando a estos procesadores como segmentados.

    El primer procesador segmentado fabricado por Intel fue el Intel 386 con un pipeline de tres etapas.

    Un procesador como los Intel Pentium 4 llegaba a tener un pipeline de ejecución de hasta 31 etapas. El problema es que un pipeline tan largo no mejora el rendimiento cuando se producen saltos al ejecutar instrucciones condicionales y el predictor de bifurcaciones falla en el predicción, en un salto el pipeline se vacía y comienza en la primera etapa con la primera instrucción a la que se ha saltado.

    Pipeline de 4 segmentos

    Pipeline de 4 segmentos

    Hazards

    Hay varios peligros en la ejecución paralela que ha de tenerse en cuenta para un buen funcionamiento de los procesadores segmentados. Los riesgos son de tres tipos, de datos, estructurales y de control.

    En los riegos de datos una instrucción de lectura posterior a una instrucción de escritura para un dato ha de producirse después, una instrucción de lectura posterior a una de escritura para un dato ha de producirse después y dos instrucciones de escritura sobre el mismo dato ha de producirse en el mismo orden del programa.

    Los riesgos estructurales consisten en que dos instrucciones en el pipeline necesiten el mismo recurso como la ALU, en este caso las instrucciones han de esperar.

    Los riesgos de control consisten en que se hayan insertado en el pipeline instrucciones que finalmente han de descartarse por una mala predicción en un salto en el programa.

    Ejecución fuera de orden

    La ejecución fuera de orden permite aprovechar los ciclos de CPU que de otra manera no se utilizarían. Con este paradigma, el procesador ejecuta las instrucciones según la disponibilidad de los datos de entrada y las unidades de ejecución, en vez de en el orden original del programa. Esto permite evitar tiempos inactivos esperando a que la instrucción precedente se complete en el caso de que sean independientes.

    Ejecución especulativa

    La ejecución especulativa es otra optimización que permite aumentar el rendimiento que consiste en ejecutar instrucciones aún antes de saber si son necesarias.

    Los programas incluyen sentencias condicionales, en función de su evaluación el flujo del programa continúa por una rama de la condición u otra, hasta no evaluarse la condición no se sabe porque rama se continúa. La ejecución especulativa ejecuta las instrucciones de ambas ramas descartando los resultados de una cuando se conoce que rama finalmente se ha seguido.

    Esta técnica aumenta el rendimiento pero con la cual se han descubierto varios problemas relativos a la seguridad con su Explicación del fallo de seguridad Meltdown y Spectre en los microprocesadores Intel que han afectado a los procesadores que la implementaron tanto de Intel, de AMD como de ARM.

    Paralelismo

    Para aumentar el rendimiento los procesadores intentan ejecutar las operaciones de forma paralela.

    Bit

    Una forma de aumentar el paralelismo es aumentar el tamaño de palabra del procesador. Un procesador con una palabra de 8 bits necesita dos ciclos para realizar la suma de dos números de 16 bits, un procesador de 16 bits solo necesita un ciclo con lo que en la práctica es duplicar el rendimiento del procesador en este tipos de operaciones.

    Escalar y superescalar

    Un procesador escalar en todo momento solo ejecuta una instrucción, en cambio un procesador superescalar ejecutar múltiples instrucciones usando varias unidades de ejecución. Los procesadores superescalares tienen varias unidades de ejecución del mismo tipo, por ejemplo un procesador superescalar tiene varias unidades ALU o de FPU. El procesador trata de mantener cada una de estas diferentes unidades con instrucciones a ejecutar. Un procesador superescalar con dos ALU es capaz de realizar el doble de operaciones por ciclo de reloj y por tanto supone duplicar el rendimiento en este tipos de operaciones.

    El primer procesador superescalar fabricado por Intel fue el Pentium.

    Multihilo

    Un procesador con procesamiento multihilo o SMT trata de mejorar el rendimiento del procesador superescalar. Cada núcleo físico es visto por el sistema operativo como dos o más núcleos lógicos, cada procesador lógico tiene sus propios registros de control sin embargo las unidades de ejecución son compartidas por los hilos. Cada hilo puede ser detenido o interrumpido de forma independiente al resto de hilos.

    El número de hilos que permite un procesador por cada cada núcleo puede ser tan simple como dos, dependiendo de la microarquitectura del procesador pueden ser un número más grande como cuatro u ocho.

    El primer procesador con hyper-threading fabricado por fue el Intel Pentium 4.

    Los procesadores y sistemas operativos conceden a los programas un tiempo de ejecución en el procesador, transcurrido el tiempo sin interrumpidos y se continua con otro proceso, el proceso no tiene el control de cuando se interrumpe su ejecución, a este estilo de compartir el recurso del procesador se le denomina multitarea preemtiva. Incluso con la velocidad de los procesadores de mononucleo en los que solo se ejecutan un solo programa la sensación es que los programas se están ejecutando de forma paralela.

    Multinúcleo

    Un procesador con procesamiento paralelo o SMP incorpora varios núcleos físicos en el mismo procesador. Cada núcleo es independiente y ejecuta un proceso en cada núcleo de forma simultánea. En un procesador mononúcleo la programación simultánea era simulada mediante la apropiación preemptiva junto con la rapidez del procesador para en la ejecución de instrucciones y cambios de contexto de un procesador a otro.

    El primer procesador multinúcleo fabricado por Intel fue el Pentium D.

    Número de núcleos

    Los primeros procesadores tenían un único núcleo o unidad de cómputo denominados single-core, posteriormente se ha implementado en un mismo procesador varios núcleos denominados multi-core.

    Los procesadores multi-core de escritorio consumno están diseñados para ejecutar eficientemente tanto código en serie como en paralelo poniendo gran énfasis en la ejecución de un único hilo, para ello dedican espacio en el procesador para implementar ejecución fuera de orden, pipelines profundos, son superescalares y tienen cachés grandes.

    Los procesadores many-core están especializados en ejecutar gran cantidad de operaciones en paralelo, para ello tienen una gran cantidad de núcleos de computación. Si un procesador muti-core tiene unos pocos núcleos al estar limitados el número de ellos por la coherencia de la caché los procesadores many-core se cuentan por miles. Las GPU son un ejemplo del procesador many-core.

    Seguridad

    Los procesadores incluyen funcionalidades con la colaboración del sistema operativo para que la computación de los programas sea segura, con el objetivo de que un programa no se apropie de toda la capacidad de computación o que la memoria de un programa esté aislada de otros programas.

    Anillos de seguridad de un procesador

    Anillos de seguridad de un procesador

    Modo protegido

    Los procesadores incluyen un modo de ejecución con privilegios especiales que son exclusiva del sistema operativo. Algunas instrucciones solo pueden ser ejecutadas en el modo protegido por seguridad.

    El primer procesador con un modo protegido fabricado por Intel fue el 386.

    Cifrado de memoria

    Para minimizar las consecuencias de una fallo de seguridad los procesadores más recientes destinados a la computación en la nube, donde el entorno de computación es compatido aún estando en ciertamedia aislado lógicamente, también incluyen funcionalidades para cifrar completamente la memoria de los datos y programas, de modo que aunque un programa tenga acceso a la memoria de otro la información al estar cifrada no sea legible.

    Rendimiento de procesador

    El rendimiento de un procesador se mide en instrucciones por ciclo o IPC, instrucciones por segundo o IPS, operaciones de coma flotante por segundo FLOPS, rendimiento por vatio o PPW.

    Una variable que impacta en el rendimiento es el número de transistores con el que está construido el procesador, a mayor número de transistores es posible incluir cachés más grandes, mayor número de núcleos y mayor número de componentes. Sin embargo, un mayor número de transistores implica una mayor cantidad de energía consumida.

    Los diseñadores de procesadores han de encontrar un equilibrio óptimo entre número de transistores, tamaño del chip, coste de fabricación, energía consumida y disipada en forma de calor y precio del procesador.

    Una mejora de los procesos litográficos que permiten crear transistores de menor tamaño es beneficioso para los procesadores ya que mejoran el número de transistores que se pueden incluir en el procesador en el mismo espacio, hacen los procesadores más pequeños, la energía consumida y el calor disipado es menor y el precio de fabricación del procesador se abarata.

    Tipos de procesadores

    La CPU es el principal componente de una computadora que se encarga de ejecutar las instrucciones del programa, otro tipo de procesador especializado son las GPU dedicadas al procesamiento de gráficos o en tareas de inteligencia artificial. Las GPU están especializadas en ejecutar gran cantidad de operaciones de forma paralela.

    Un system on chip o SoC incluye la mayoría de componentes de un sistema es un único chip en vez de estar separados. Un SoC puede incluir tanto la CPU, la memoria, los puertos de entrada/salida y otros elementos como almacenamiento secundario, la GPU y otros elementos especializados en comunicaciones y procesamiento de datos. Esto les hace más baratos que un sistema en el que los componentes están formados por chips individuales.

    Gestión de energía

    Los procesadores para reducir su consumo de energía y el calor que dispan emplean diferetnes técnicas como variar el voltaje de funcionamiento o la frecuencia de funcionamiento.

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

    Picando Código

    Siete días en el Picandoverso – Mayo III: New Generation

    mayo 19, 2021 03:30

    Decimo séptima entrega de esta serie de posts que repasan los últimos 7 días en lo que respecta a tecnología, Ruby, Cómics, Videojuegos y demás. Esta vez desde el 12 hasta el 19 de mayo de 2021 inclusive:

    Siete días en el Picandoverso – Mayo III: New Generation

    Tecnología

    🖥 KDE cambió el nombre de su paquete de aplicaciones, bibliotecas y plugins. Solía llamarse simplemenet KDE, después KDE SC, y recientemente KDE Applications. Ahora se llama KDE Gear, y recientemente publicaron un release nuevo: 21.04.1. Voy a tener que actualizar mi KDE Neon a ver qué tal estas nuevas versiones.

    ⌨ System76, una de las marcas más conocidas que proveen computadoras de escritorio y laptops con Linux pre-instalado, sacó un teclado mecánico llamado “Launch”. Es completamente personalizable y configurable, funciona en Linux, Windows y macOS y es de código abierto. Se ve bastante lindo y estoy necesitando un buen teclado, pero el precio es un poco elevado, empezando en USD 285.

    🔐 1Password publicó finalmente su cliente oficial para Linux. En el post comentan bastante sobre las características de la aplicación. Usé 1Password bastante en trabajos anteriores y recuerdo que me molestaba bastante no tener una aplicación nativa. Parece que no sólo publicaron la aplicación para dejar contentos a usuarios de Linux, le pusieron todo. Está desarrollada en Rust en el backend y la interfaz de usuario está escrita en React interactuando a través de Neon con el backend Rust. Está soportada en Debian, Ubuntu, CentOS, Fedora, Arch Linux, Red Hat Enterprise Linux y Snap, pero también está disponible el tar.gz. También publicaron que para devolver un poco a la comunidad de código abierto, están patrocinando algunos proyectos además de haber liberado algunas bibliotecas de desarrollo propio, y proveer el servicio de manera gratuita a proyectos de código abierto. También está disponible un post más técnico (¡con diagramas y estadísticas!): Behind the scenes of 1Password for Linux

    📷Amazon Ring es la red de vigilancia civil más grande nunca vista en Estados Unidos según The Guardian. La gente que no leyó 1984 de George Orwell sigue comprando dispositivos de vigilancia y los instala en sus hogares.

    📝 Noticias de Emacs de esta semana Emacs News.

    Ruby

    🛤 El incidente que hubo en Basecamp desencadenó en mucha charla y controversia, particularmente con Rails y su futuro. Richard Schneeman, una de las tantas personas que ha contribuido mucho a Rails, escribió un artículo explicando cómo funciona el desarrollo de Rails desde su punto de vista, y observa varias ideas al respecto: The room where it happens: How Rails gets made.

    📨 En Turbocharge HTTP Requests in Ruby, Dmitry Ishkov explica cómo mejorar los pedidos HTTP desde Ruby aprovechando la característica keep-alive de HTTP.

    Disponible nueva versión de Rubocop, 1.15 con muchos bugs corregidos y algunas mejoras.

    💎 Ruby 3.1 agrega Array.intersect? que dados 2 arrays x e y, nos permite reemplazar x.intersection(y).empty? con x.intersect?(y). Podemos leer más en este post de BigBinary, parte de su serie de posts con cosas nuevas en Ruby 3.1.

    Comics

    🟢 Ya se sabía, pero Al Ewing confirmó que el número 50 de The Immortal Hulk va a ser el final de la serie. Por un lado está bueno que haya podido contar la historia que quiso contar y la termine antes de perder calidad. Ha sido excelente desde el primer número y es una serie que recomiendo mucho. Por el lado negativo, va a ser difícil seguir una de las mejores series en la historia de Hulk para la persona a cargo de escribirlo, y me da miedo pensar quién será. Leí un comentario por ahí sin ningun fundamente que Chip Zdarsky iba a ser el siguiente autor, y me encantaría. Pero no es más que un posteo al azar por internet…

    Videojuegos

    Se siente como que hace mucho tiempo que no se agregaban títulos nuevos a la colección de NES y Super NES de Nintendo Online. Hoy Nintendo publicó el video de los títulos nuevos que se vienen el 26 de Mayo. Joe & Mac tenía idea que ya estaba, pero no, era Joe & Mac 2. Creo que lo novedoso es Magical Drop 2, un juego que nunca salió fuera de Japón. Está interesante probar juegos que nunca hubiera jugado de otra manera, pero me sigue pareciendo como que Nintendo está rascando el fondo de la olla y publicando los pocos juegos que encuentra. Podrían incluir más plataformas de Nintendo en el servicio, o títulos más conocidos. De última es gratis con el servicio Nintendo Online, pero se supone que son para justificar el precio también 🤷🏻‍♂️

    YouTube Video

    Películas/TV

    Salió un trailer de la cuarta entrega de Hotel Transylvania. Las primeras me resultaron entretenidas, así que seguramente mire esta cuando salga:

    YouTube Video

    Y hasta acá llegamos. Un “7 días” un poco corto, pero creo que me saqué las ganas de escribir esta semana con el repaso de la pandemia, que vendría a ser un post en el mismo universo que los 7 días. Porque si bien el blog Picando Código es un universo, a su vez distintos posts pertenecen a distintos universos dentro de ese mega(ultra|hiper|súper|multi)-universo. Algo así como el universo bolsillo que creó Franklin Richards a finales de los 90, creo que fue por 1997 o por ahí (no vale googlear) cuando Onslaught asesinó a los Avengers y Fantastic Four y renacieron todos en Heroes Reborn y después volvió todo a como estaba antes en Heroes Return. No confundir con el cómic actual “Heroes Reborn” de Marvel que no estoy leyendo porque me tienen cansado los mega eventos y este en particular viene de Jason Aaron en Avengers, y lo que viene escribiendo en ese título es horrible.

    Pueden seguir los posts de Picando Código por RSS, Twitter (cuenta únicamente con posts del blog) y canal de Telegram. También estoy en Twitter y Mastodon, donde además de compartir lo que se publica en el blog publico alguna cosa más.

    Otros 7 días en el Picandoverso:

    El post Siete días en el Picandoverso – Mayo III: New Generation fue publicado originalmente en Picando Código.

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

    Variable not found

    Crear bloques de código Razor reutilizables en Blazor con Render Fragments

    mayo 19, 2021 06:38

    Blazor

    Durante la implementación de páginas o componentes Blazor en archivos .razor, es relativamente frecuente encontrarse con casos en los que nos interesa reutilizar un bloque de código Razor en más de un punto. 

    Por ejemplo, observad el siguiente ejemplo:

    <h1>Todo list</h1>
    <h2>Pending tasks</h2>
    <ul>
    @foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
    {
    <li>@item.Task, owned by @item.Owner and created at @item.CreatedAt</li>
    }
    </ul>
    <h2>Finished tasks</h2>
    <ul>
    @foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
    {
    <li>@item.Task, owned by @item.Owner and created at @item.CreatedAt</li>
    }
    </ul>

    En este componente, podemos ver claramente que estamos repitiendo los dos bloques de código encargados de mostrar los elementos de cada una de las listas, por lo que, si en el futuro quisiéramos cambiar la forma de mostrar un TodoItem, tendríamos que modificar el interior de los dos bloques. Es frecuente en estos casos optar por crear un nuevo componente que se encargue de ello, por ejemplo, llamado TodoListItem:

    <li>@Item.Task, owned by @Item.Owner and created at @Item.CreatedAt</li>
    @code {
    [Parameter]
    public TodoItem Item { get; set;}
    }

    De esta forma ya tendremos el código de renderización del TodoItem centralizado y podremos simplificar el bloque anterior eliminando la duplicidad:

    <h1>Todo list</h1>
    <h2>Pending tasks</h2>
    <ul>
    @foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
    {
    <TodoListItem Item="item" />
    }
    </ul>
    <h2>Finished tasks</h2>
    <ul>
    @foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
    {
    <TodoListItem Item="item" />
    }
    </ul>

    Aunque conceptualmente la solución que hemos implementado es correcta, introduce un problema en nuestra aplicación: por el mero hecho de querer evitar la duplicación de código, estamos introduciendo en la página un número indeterminado de componentes, lo cual podría afectar drásticamente a su rendimiento.

    Por llevarlo al extremo, imaginad que esas listas tienen miles de elementos. En este caso, en nuestra página estaríamos introduciendo miles de componentes, con lo que esto implica:

    • Deberían instanciarse miles de componentes (objetos).
    • Deberían ejecutarse los eventos del ciclo de vida de cada componente al crearlos, inicializarlos, renderizarlos, etc.
    • Mientras se encuentren en la página cada componente ocuparía memoria, ya sea en cliente (Blazor WebAssembly) o en servidor (Blazor Server).

    Esto podría llegar incluso a hacer una página inutilizable, por lo que es importante disponer de otros métodos para crear y reutilizar bloques de código HTML sin necesidad de crear componentes. Esta es una de las utilidades de los render fragments.

    Introducing Render Fragments

    Los fragmentos de renderización, o render fragments, son delegados reutilizables que permiten introducir contenido en páginas u otros componentes Blazor. Los hemos visto ya algunas veces por aquí, como cuando echamos un vistazo a los componentes con cuerpo y componentes genéricos.

    Estos delegados reciben como parámetro un objeto de tipo RenderTreeBuilder, usado para configurar el contenido que deseamos insertar en la página cuando son invocados. Por ejemplo, el siguiente código muestra cómo crear un render fragment que muestra la hora actual, y cómo puede ser invocado desde el cuerpo de una página Blazor:

    @page "/time"
    <p>La hora actual es: @CurrentTime</p>
    <p>Y vuelvo a repetirla: @CurrentTime</p>
    ...
    @code {
    private RenderFragment CurrentTime = builder =>
    {
    builder.AddMarkupContent(1, "<time>" + DateTime.Now + "</time>");
    };
    }

    Podéis leer más sobre la generación de componentes usando RenderTreeBuilder en este post de Chris Sainty.

    El código anterior es lo suficientemente claro y fácil de escribir, pero aún podemos mejorarlo. Gracias a la magia del tooling de Blazor, es posible introducir código Razor directamente en el cuerpo del delegado, por lo que podríamos simplificarlo de esta forma:

    @code {
    private RenderFragment CurrentTime = __builder =>
    {
    <time>@DateTime.Now</time>
    };
    }

    Por convención, el parámetro RenderTreeBuilder del delegado debe llamarse __builder. En caso contrario, fallará en compilación.

    Estos miembros pueden ser también estáticos, por lo que podrían ser compartidos entre distintos componentes. Por ejemplo, si creásemos un archivo llamado Utils.razor con el siguiente código, todos los componentes podrían usar nuestro render fragment simplemente haciendo referencia a él a través su clase (@Utils.CurrentTime):

    @* Archivo Utils.razor *@
    @code {
    public static RenderFragment CurrentTime = __builder =>
    {
    <time>@DateTime.Now</time>
    };
    }

    Los ejemplos anteriores eran bastante sencillos, porque el contenido no dependía de ningún valor externo, pero también es posible enviar al delegado datos para que los utilice a la hora de componer la salida.

    Por ejemplo, volviendo al ejemplo con el que comenzamos este post, tiene bastante sentido enviar al delegado un objeto de tipo TodoItem para generar la descripción de cada tarea, para lo que utilizaremos en esta ocasión el tipo RenderFragment<TodoItem>:

    @code {
    private RenderFragment<TodoItem> ItemView = item => __builder =>
    {
    <li>
    @item.Task, owned by @item.Owner and created at @item.CreatedAt
    </li>
    };
    }

    La sintaxis es algo más compleja, pero básicamente se trata de un delegado que acepta un parámetro de tipo TodoItem, y retorna otro delegado, que ya es el que recibe el RenderTreeBuilder y define el contenido a retornar.

    Para utilizar este fragmento parametrizado bastaría con suministrarle el valor en el momento de realizar la llamada:

    <h1>Todo list</h1>
    <h2>Pending tasks</h2>
    <ul>
    @foreach (TodoItem item in TodoItems.Where(i=>!i.IsDone).OrderBy(i=>i.Priority))
    {
    @ItemView(item)
    }
    </ul>
    <h2>Finished tasks</h2>
    <ul>
    @foreach (TodoItem item in TodoItems.Where(i=>i.IsDone).OrderBy(i=>i.DateFinished))
    {
    @ItemView(item)
    }
    </ul>

    ¡Y esto es todo! Espero que os haya resultado interesante y os sea de utilidad para mejorar vuestras aplicaciones Blazor :)

    Publicado en: www.variablenotfound.com.

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

    Variable not found

    Enlaces interesantes 444

    mayo 17, 2021 06:32

    Enlaces interesantes

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

    Por si te lo perdiste...

    .NET Core / .NET

    ASP.NET Core / ASP.NET / Blazor

    Azure / Cloud

    Conceptos / Patrones / Buenas prácticas

    Data

      Web / HTML / CSS / Javascript

      Visual Studio / Complementos / Herramientas

      Xamarin

      Otros

      Publicado en Variable not found.

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

      Blog Bitix

      Los mejores juegos gratuitos o free to play de PC, consolas y móvil

      mayo 16, 2021 05:00

      Para jugar no hace falta comprar los juegos en el mismo momento en que los juegos se publican, en poco tiempo bajan de precio de forma significativa. Pero incluso hay muchos juegos de muy buena calidad que son completamente gratuitos, los hay para todas las plataformas ya sean PC, consolas e incluso teléfonos móviles. Hay juegos gratuitos de varios géneros ya sea de disparos, estrategia o rol, algunos tienen mecánicas de juegos de varios de estos géneros.

      El sector de los videojuegos es más grande a nivel económico incluso que los sectores del cine y otros tipos de espectáculos deportivos con ámbito mundial. Son muchos los juegos que se lanzan a lo largo del año para las diferentes plataformas de videojuegos, ya sea en PC, consolas y más recientemente también para los dispositivos móviles o smartphones.

      Una buena parte de los videojuegos tiene un precio elevado al inicio de su comercialización, sin embargo, al cabo de unos meses o un año reducen su precio considerablemente junto con los descuentos y ofertas periódicas que se publican en cada una de las plataformas.

      Algunos juegos incluso son gratuitos, su modelo de negocio se basa en tener un gran número de jugadores y ofrecen compras integradas que permitan conseguir algunos recursos que proporcionen alguna pequeña ventaja o progresar más rápido. Algunos de estos juegos son conocidos como pagar para ganar o pay-to-win o pagar para progresar o pay-to-progress en los que aunque no es imprescindible, el querer ganar o progresar es más difícil o requiere mucho más tiempo a los jugadores que no realizan compras en el juego.

      Hay juegos gratuitos o free-to-play para todas las plataformas en las que se pueden jugar, que sean gratuitos no quiere decir que sean peores, muchos de los free-to-play de los los incluídos en este artículo son juegos que están al mismo nivel de los juegos de pago. Además son juegos que para ampliar el número de jugadores que pueden jugarlos no tienen unos requisitos gráficos elevados.

      Hay juegos gratuitos de varios géneros y objetivos aunque la mayoría tienen en común requerir jugar con otros jugadores a través de internet, en equipo contra otro grupo de jugadores y un componente de mejorar para enganchar a continuar jugando.

      Algunos de estos juegos son competitivos en el que el objetivos es derrotar al equipo adversario, en los que se juega en equipo y no depende únicamente de la habilidad propia la regla más importante para afrontar las partidas es intentar hacerlo siempre lo mejor posible, por muy bueno que sea un jugador si el resto de compañeros tienen mala suerte, no son hábiles o el algoritmo de emparejamiento o match making genera una partida contra personajes que tienen ventaja es más difícil ganar y por ello no conviene frustrarse.

      Contenido del artículo

      Los mejores juegos gratuitos para PC y consolas

      La mayoría de juegos están disponibles tanto para PC como para consolas, en algunos casos aunque el juego tenga el mismo nombre la versión para consolas es una adaptación del juego en PC ambientado en el mismo mundo pero una versión más reducida. Cada persona tiene motivos para preferir jugar en PC o jugar en consola.

      De disparos

      Los juegos gratuitos del género de disparos o shooters son bastante populares, hay varios gratuitos en los que juegan algunos millones de personas. Aunque se pueden jugar de forma individual o en equipo con otras personas algunos están desarrollados con la intención de jugar en equipo con amigos.

      Fortnite Apex Legends Destiny 2

      Fortnite, Apex Legends y Destiny 2

      Counter-Strike: Global Ofensive Call Of Duty®: Infinite Warfare

      Counter-Strike: Global Ofensive y Call Of Duty®: Infinite Warfare

      Estrategia por equipos

      Estos juegos requieren jugar en equipo contra otro equipo en una batalla con un tiempo limitado de duración, en función de juego es posible elegir el personaje al que se encarna que tiene unas habilidades únicas respecto a otros personajes y que lo hace más adecuado en ciertas situaciones de batalla pero quizá más vulnerable en otras batallas o contra otros personajes.

      Del juego World of Warhips he escrito un par de artículos describiendo en que consiste el juego y una pequeña guía con la estrategia básica para las batallas. Aunque los artículos tratan sobre la versión para consola que tiene varias diferencias de la versión del juego para PC, las mecánicas y estrategía es en gran medida similar.

      League of Legends Dota 2 World of Warships

      League of Legends, Dota 2 y World of Warships

      World of Tanks War Thunder

      World of Tanks y War Thunder

      Estrategia individual

      En estos juegos se juega contra otros jugadores pero se hace en partidas uno contra uno.

      Hearthstone Auto Chess Magic The Gathering

      Hearthstone, Auto Chess y Magic The Gathering

      Rol

      El género de rol y los MMORPG es otro estilo de juegos muy popular entre los juegos gratuitos. En el aspecto de rol permiten mejorar el personaje con nuevas habilidades, subir de nivel para hacerlo más poderosas, varios tipos de personajes entre los que elegir y conseguir mejores armas y armaduras para entrar en combate con enemigos cada vez más poderosos.

      Path of Exile Albion Online

      Path of Exile y Albion Online

      Otros

      Otros juegos muy conocidos son los siguientes, estos están en varias categorías de juegos.

      Dauntless Neverwinter Dungeon Defenders II

      Dauntless, Neverwinter y Dungeon Defenders II

      Heavy Metal Machines Rocket League

      Heavy Metal Machines yRocket League

      Los mejores juegos gratuitos para móvil

      Los dispositivos móviles también son una plataforma utilizable para jugar y varias empresas se dedican en exclusiva a esta plataforma de juegos. Son juegos con partidas más cortas más adaptadas a jugar en momentos esporádicos, su ventaja es que se puede jugar a ellos en cualquier lugar, simplemente hace falta tener un poco de tiempo disponible.

      Clash Royale Brawl Stars PUBG

      Clash Royale, Brawl Stars y PUBG

      Candy Crush Clash of Clans Among Us

      Candy Crush, Clash of Clans y Among Us

      Genshin Impact Plants vs. Zombies Teamfight Tactics

      Genshin Impact, Plants vs. Zombies y Teamfight Tactics

      Otros juegos con precio reducido

      También hay juegos que aunque no son gratuitos al tener unos años desde su publicación en ofertas puntuales es posible comprarlos a un precio muy reducido entre 3 € y 20 €, incluso juegos que en su momento eran la última novedad muy esperada y que fueron publicados con un precio de lanzamiento de 70 €. Hay páginas que monitorizan el precio de las tiendas y permiten encontrar las mejores ofertas de juegos para PC y consola.

      En las tiendas digitales de Steam, Epic Store, GOG y PS Deals es posible comprarlos y ver los juegos en oferta.

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

      Blog Bitix

      El patrón de diseño Factory, ventajas sobre new y diferencias con Builder

      mayo 14, 2021 02:00

      El patrón de diseño Factory es uno de los patrones dedicados a la creación de instancias. El patrón Factory proporciona varias ventajas sobre la palabra reservada new que proporcionan los lenguajes de programación orientada a objetos para la creación de instancias. Es muy utilizado en muchas librerías, en ocasiones también es necesario implementar una clase que implemente este patrón por lo que es muy útil conocer y usar este patrón en las ocasiones que sea adecuado.

      Java

      El patrón de diseño factoría o Factory es uno de los más empleados en multitud de ocasiones en el código propio y aunque no se implemente al ser usado en multitud de librerías también en muchos casos conviene conocer sus principios y ventajas frente a otros métodos.

      El patrón de diseño Factory es uno de patrones ya identificados como útiles en los que su aplicación es adecuada. Los patrones de diseño se clasifican en las áreas funcionales de creacionales dedicados a la creación de objetos, de comportamiento centrados en la comunicación entre objetos, estructurales para mantener de forma sencilla relaciones entre entidades y finalmente los patrones de concurrencia empleados en la aplicación concurrente con múltiples hilos de ejecución.

      Contenido del artículo

      Las limitaciones de instanciar objetos con la palabra reservada new

      Los lenguajes de programación orientada a objetos modelan los conceptos que trata una aplicación mediante clases que define las propiedades del objeto y los métodos que permiten el acceso o modifican el estado del objeto, esto proporciona encapsulación y es uno de los conceptos fundamentales de la programación orientada a objetos entre los que también se encuentran el polimorfismo, herencia y composición.

      1
      2
      3
      4
      5
      
      public abstract class Shape {
      
          public abstract double getPerimeter();
          public abstract double getArea();
      }
      Shape.java
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      public class Circle extends Shape {
      
          private double radious;
      
          public Circle(double radious) {
              this.radious = radious;
          }
      
          @Override
          public double getPerimeter() {
              return 2 * Math.PI * radious;
          }
      
          @Override
          public double getArea() {
              return Math.PI * radious * radious;
          }
      }
      Circle.java
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      public class Square extends Shape {
      
          private double length;
      
          public Square(double length) {
              this.length = length;
          }
      
          @Override
          public double getPerimeter() {
              return 4 * length;
          }
      
          @Override
          public double getArea() {
              return 2 * length;
          }
      }
      Square.java

      Las clases son una definición de las entidades de una aplicación en tiempo de compilación, en tiempo de ejecución un programa crea instancias individuales de las clases. En el lenguaje de programación Java para la creación de instancias de objetos se usa la palabra reservada new. Al emplear esta palabra reservada el lenguaje en tiempo de ejecución devuelve una referencia a la instancia creada, habitualmente se asigna la referencia a una variable.

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      
      public class Main {
      
          public static void main(String[] args) {
              Shape shape1 = new Square(2d);
              System.out.printf("Shape perimeter: %s%n", shape1.getPerimeter());
              System.out.printf("Shape area: %s%n", shape1.getArea());
      
              Shape shape2 = new Circle(2d);
              System.out.printf("Shape perimeter: %s%n", shape2.getPerimeter());
              System.out.printf("Shape area: %s%n", shape2.getArea());
          }
      }
      Main-new.java

      La palabra reservada new o una equivalente es el mecanismo que emplean muchos lenguajes de programación orientados a objetos como Java y C#. Al ser un mecanismo proporcionado por el lenguaje es sencillo de utilizar en la mayoría de ocasiones.

      Aunque la palabra reservada new es una opción válida para crear instancias tiene algunas limitaciones que en algunas ocasiones requieren una alternativa.

      Una de sus limitaciones es que la palabra reservada new siempre devuelve una instancia del tipo concreto que explícitamente se crea, a veces el tipo concreto de la instancia no se conoce en tiempo de compilación, por ejemplo en tiempo de compilación se sabe que se necesita un Shape pero solo hasta en tiempo de ejecución no se sabe si la instancia a construir es un Square o un Circle.

      Otra limitación es que el código que usa new tiene la responsabilidad de crear las instancias, a veces interesa delegar esta responsabilidad en otra clase para no repetirla múltiples veces.

      Para evitar las las limitaciones de la palabra new se suele emplear alguno de los patrones creacionales, como el patrón de diseño Factory.

      El patrón de diseño Factory

      El patrón Factory es un patrón de diseño creacional que tiene como función crear instancias de objetos sin las limitaciones de la palabra reservada new. El patrón Factory solventa las dos limitaciones comentadas de new, pudiendo crear diferentes tipos de instancias que implementen una interfaz o hereden de una clase común, al mismo tiempo el código de creación de las instancias queda encapsulado en la clase que implementa el patrón Factory abstrayendo al código que lo usa de la responsabilidad de creación de instancias.

      La creación de instancias es una de las tareas más comunes que realiza un programa, de modo que habitualmente es necesario implementar una clase factoría o en caso de usar una librería utilizar una factoría implementada por una clase de la librería. Un contenedor de IoC y su inyección de dependencias que también tienen como misión delegar en ellos la creación de instancias requieren implementar una factoría propia que el contenedor invoca.

      El patrón Factory es tan simple como una clase con uno o varios métodos que devuelven la instancia que la factoría crea, el método de la factoría puede recibir parámetros. El método factoría puede ser estático si la creación de las instancias depende de únicamente los parámetros que recibe el método o el método puede ser de instancia y estar basado además de los parámetros que recibe en propiedades de la instancia de la factoría.

      El patrón Factory hay dos categorías: Factory Method Pattern que se basan en un único método y Abstract Factory Pattern que son una indirección más pudiendo sustituir una implementación de factoría por otra haciendo posible devolver cada una diferentes instancias.

      Como muchos patrones de diseño añade cierta complejidad en el diseño de las clases del programa con lo que su uso debe estar justificado con el objetivo de simplificar el código o la necesidad de evitar las limitaciones de la palabra new.

      Diagrama de clases del patrón de diseño Factory

      Diagrama de clases del patrón de diseño Factory

      Diferencias con el patrón de diseño Builder

      El patrón de diseño Builder es otro patrón creacional dedicado a la creación de instancias, aunque comparte objetivos las implementación es diferente del patrón Factory.

      Una diferencia entre el patrón Factory y el patrón Builder es que el patrón Factory crea la instancia en un único paso con la invocación de un método de la factoría que lo devuelve inmediatamente, el patrón Builder suele requerir la invocación de varios métodos y un método final build que realiza la creación de la instancia con una API fluída.

      Los Builder son objetos con estado y requieren crear una instancia de Builder, el patrón Factory no requiere crear una instancia y se puede compartir entre varios objetos que la necesitan.

      Por el contrario el patrón Builder proporciona más control sobre los pasos de la creación de la instancia y proporciona más flexibilidad para variar la representación interna de la instancia creada. Otra diferencia es que el Builder crea instancias con diferente composición de objetos.

      Ejemplo de patrón de diseño Factory

      En el siguiente ejemplo de implementación en Java de patrón Factory Method se observa que el método factoría en función del parámetro se devuelve una instancia u otra empleando una sentencia if-else. Esta factoría además contiene otros dos métodos de factoría específicos para tipos concretos de Shape.

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      public class ShapeFactory {
      
         public static Shape create(String type) {
             if (type.equals("square")) {
                 return new Square(1);
             } else if (type.equals("circle")) {
                 return new Circle(1);
             } else {
                 throw new IllegalArgumentException();
             }
         }
      
         public static Square createSquare(double length) {
             return new Square(length);
         }
      
         public static Circle createCircle(double radious) {
             return new Circle(radious);
         }
      }
      ShapeFactory.java

      La sentencia if-else hace que no se cumpla el principio O (Open-closed) de SOLID, si se añade un nuevo tipo requiere modificar el código del método en vez de proporcionar una extensión. Para evitar la sentencia if-else en una factoría hay varias opciones.

      Con la incorporación de los default methods en Java el método factoría es posible implementarlo en una interfaz de Shape no requiriendo una clase ShapeFactory dedicada que lo contenga.

      El siguiente programa crea una figura según el parámetro indicado que solo se conoce en tiempo de ejecución según el argumento proporcionado al lanzar el programa y una instancia de cada tipo de figura, se observa que la clase Main no utiliza directamente los constructores de ninguna figura.

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      
      public class Main {
      
          public static void main(String[] args) {
              String shapeType = args[0];
              Shape shape1 = ShapeFactory.create(shapeType);
              System.out.printf("Shape perimeter: %s%n", shape1.getPerimeter());
              System.out.printf("Shape area: %s%n", shape1.getArea());
      
              Shape shape2 = ShapeFactory.createSquare(2d);
              System.out.printf("Shape perimeter: %s%n", shape2.getPerimeter());
              System.out.printf("Shape area: %s%n", shape2.getArea());
      
              Shape shape3 = ShapeFactory.createCircle(2d);
              System.out.printf("Shape perimeter: %s%n", shape3.getPerimeter());
              System.out.printf("Shape area: %s%n", shape3.getArea());
          }
      }
      Main-factory.java

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

      Blog Bitix

      Herramientas para mejorar una página web en SEO, conformidad estándares y rendimiento

      mayo 08, 2021 11:00

      Hay múltiples herramientas que permiten evaluar y mejorar un sitio web en la conformidad de estándares web, rendimiento y SEO. Con los resultados de estas herramientas se han de realizar correcciones en el sitio web para mejorar las páginas del sitio web. La conformidad a estándares web, rendimiento, SEO y enlaces rotos son criterios por los que los buscadores clasifican las páginas en las páginas de resultados. Con un mejor posicionamiento es posible mejorar el número de visitas que los buscadores atraen al sitio web y realizando cambios mejorar la experiencia de usuario.

      HTML

      Para todos los sitios web es importante y para algunos la fuente más importante de visitas que recibe un sitio web es a través de búsquedas orgánicas de buscadores como Google o como el buscador DuckDuckGo que respeta tu privacidad.

      El número de visitas depende de la calidad del contenido, el número de búsquedas que coincide con ese contenido y la competencia por las palabras clave con otros sitios web o algo tan simple como el título de los artículos.

      Sin embargo, otra parte para maximizar el rendimiento de un sitio web se debe a otros factores que los rastreadores e indexadores web utilizan para mostrar en las páginas de resultados de las búsquedas y su posición con otros resultados. Conviene que el HTML y CSS de la página cumpla con los estándares, tenga un buen rendimiento, la estructura de la página y contenido tenga palabras clave o no haya enlaces rotos a otras páginas o recursos en el sitio web.

      Contenido del artículo

      Evaluar SEO

      El SEO es un arte ya que no hay una medida precisa de qué criterios emplean los buscadores para clasificar las páginas indexadas en las páginas de resultados. Sin embargo, sí hay algunas pautas que sí contribuyen a un mejor posicionamiento.

      Incluir palabras clave y conocer la intención de búsqueda de los usuarios para satisfacer su búsqueda con un contenido apropiado. Asignar un título adecuado y preciso teniendo en cuenta la intención de búsqueda del usuario no mejora el SEO pero si mejora el número de clics que los usuarios lo acceden.

      Los datos estructurados o enriquecidos web permiten a los buscadores mostrar en la página de resultados las páginas con un formato que destaca sobre el resto, como incluir una imagen o vídeo. Los datos enriquecidos al igual que un buen título mejoran el porcentaje de usuarios que hacen click en el resultado y por tanto ayuda a conseguir más visitas.

      La herramienta Google Search Console proporciona métricas de visitas, informa de los errores que se detectan en la indexación o mejoras a realizar en las páginas asi como cuales son las principales palabras clave, artículos, tasa de clics o CTR y posición de las páginas en los resultados. Otros análisis de SEO son evaluar las páginas según las palabras clave y las pálabras clave de la competencia, algunas de estas herramientas son de pago pero ofrecen unas métricas básicas de modo gratuito.

      Evaluar conformidad a estándares web

      La organización W3C es la encargada de definir los estándares de la web. Cumplir los estándares es importante ya que de lo contrario es posible que algún navegador o versión de los mismo no muestre correctamente el contenido del sitio web, lo que ocasiona la pérdida de una visitas por una mala experiencia de usuario. Al mismo tiempo en caso de que el contenido de la página no cumpla los estándares los rastreadores quizá no sean capaces de indexar todo el contenido del sitio web o en la clasificación de las páginas de resultados acabe posicionado peor que otros artículos.

      La W3C proporciona tres herramientas para evaluar la conformidad de una página a los estándares web para el HTML y feeds. En caso de que el sitio web genere contenido en formato amp promovido por Google hay otra herramienta para evaluar la conformidad. Conociendo los elementos en los que la página no es conforme se han de realizar cambios para solventar los errores.

      Accesibilidad

      Otro aspecto de conformidad de estándares que ha de ser tenido en cuenta es hacer que las páginas del sitio web sean accesibles para que el acceder a la páginas no suponga ninguna barrera a ningún usuario incluyendo a aquellos que poseen algún tipo de discapacidad. Algunas pruebas de accesibilidad es incluir un texto alternativo para todas las imágenes de la página web y que el contraste de color de la fuente y de fondo tenga un mínimo de diferencia.

      Evaluar rendimiento

      El tiempo que tarda en descargar una página es otro criterio que los buscadores tiene en cuenta para clasificar los resultados. Se considera como positivo para la experiencia de usuario que una página tarde el menor tiempo posible en descargarse por ello los motores de búsqueda lo tienen en cuenta en la indexación. En el tiempo de descarga influyen factores como el tamaño en KiB de la página, número de peticiones y recursos que carga la página.

      Estas dos herramientas proporcionan información sobre la página, con resultado proporcionado es posible hacer modificaciones para tratar de mejorar en los puntos con peor evaluación.

      Buscar enlaces rotos

      Con el paso de tiempo algunos de los enlaces incluidos en los artículos quedarán rotos al cambiarlos de ubicación el propietario de esos sitios web. Que un sitio web contenga enlaces rotos proporciona una mala experiencia de usuario y posiblemente los buscadores lo tomen como un criterio negativo si el sitio web tiene muchos enlaces rotos.

      De forma periódica conviene analizar el sitio web para buscar enlaces o recursos rotos. También hay herramientas para automatizar la búsqueda y con los resultados corregirlos.

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

      Header Files

      Introducción a boost::program_options

      mayo 07, 2021 03:16

      Introducción

      Toda aplicación de C++ (y C) tienen una función que sirve de punto de entrada, es decir, es la función que el sistema operativo llama cuando ha terminado de preparar al proceso y va a comenzar la ejecución del código propiamente dicho (puede que ocurran algunas cosas antes, pero no entraremos en eso). Esta función es la tradicionalmente conocida como main, y tiene la siguiente sintaxis base:

      int main(int argc, char *argv[]) {}
      

      Donde el valor de retorno es el valor de retorno del proceso, y los dos parámetros son el número de argumentos recibidos, y una lista de dichos argumentos previamente separados por espacios. El primer argumento es el nombre del ejecutable (aunque puede variar dependiendo cómo haya sido lanzado el proceso).

      C y C++ no limitan la forma de utilizar los argumentos de entrada, pero cada sistema operativo tiene sus estándares (costumbres). Por ejemplo, en Windows lo normal es usar / como prefijo para indicar opciones, mientras que Unix y Linux usan - para opciones en formato corto (una sola letra), y -- para el formato largo. Pero de nuevo, cada programador es libre de usar el formato que desee, aunque lo mejor es adherirse al estándar del sistema.

      La forma de extraer e interpretar los argumentos también se deja a merced de cada programador, y normalmente es un proceso tedioso ya que hay que lidiar con listas de opciones, formato de cada una, comandos no reconocidos, argumentos inválidos, etc. Por suerte, hay algunas ayudas como getopt en sistemas GNU, QCommandLineParser de Qt, y boost::program_options, que es mi preferida y de la que hablaré hoy. Aunque no pueda cubrirla al 100%, ya que es bastante extensa, trataré de indicar algunos de los casos de uso más frecuentes.

      Gestión de opciones

      Antes que nada, comentar una metodología de trabajo habitual cuando se desarrolla una aplicación con argumentos por línea de comandos: delegar todo este trabajo en una clase. Esto reduce la cantidad de código en el main (recomendable), desacopla la gestión de parámetros de su interpretación, abstrae de los detalles de implementación (nombre del parámetro, biblioteca para interpretarlos, tipo de dato, gestión de errores, etc.), y centraliza toda la variabilidad propia de los parámetros de ejecución. Así, un ejemplo (aleatorio) sería:

      // command_line_options.h
      #include <string>
      
      struct CommandLineOptions {
          std::string input_path;
          std::string output_path;
      
          std::string lang;
      
          int error_level = 0;
          bool verbose = false;
      
          bool parse(int argc, char* argv[]);
      };
      
      #include "command_line_options.h"
      
      int main(int argc, char* argv[]) {
          CommandLineOptions options;
      
          if (!options.parse(argc, argv)) { return 1; }
      
          // Use 'options'
          setLanguage(options.lang);
          initLog(options.error_level, options.verbose);
          // ...
      }
      

      boost::program_options

      Boost, como en muchas cosas, es la gran navaja suiza de C++ (otro tanto es la biblioteca Poco, que la dejo para quien no la conozca, así como mi querido Qt). De entre todos sus módulos, suelo sacar mucho provecho de program_options, que simplifica la gestión de argumentos de entrada de un programa. A lo largo del artículo usaré el alias po para referirme a este espacio de nombres.

      Su funcionamiento podríamos dividirlo en tres partes:

      • Definición de opciones
      • Análisis de los argumentos
      • Uso de las opciones

      Definición de opciones

      Acá listaremos todas las opciones que nuestra aplicación reconoce, indicando su nombre, tipo y descripción. Para ello usamos la clase options_description.

      En el siguiente ejemplo definimos los posibles comandos -? / --help, --input / -i, --output / -o, --language, --error-level, -v / --verbose:

      po::options_description po_desc("Allowed options");
      po_desc.add_options()
        ("help,?", "shows this help message")
        ("input,i", po::value<std::string>()->required(), "input path")
        ("output,o", po::value<std::string>()->required(), "output path")
        ("language", po::value<std::string>()->default_value("en"), "UI language")
        ("error-level", po::value<int>()->default_value(0), "error level")
        ("verbose,v", po::bool_switch()->default_value(false), "show verbose log")
        ;
      

      Cada opción se define con el nombre de la misma, pudiendo añadir el formato corto. A continuación se puede especificar el tipo (con un valor por defecto si fuese el caso), o si es obligatoria. Por último, se añade una descripción de la opción, que será la mostrada en la línea de comandos al solicitar la ayuda.

      En lo particular me gusta darle valores por defecto a las opciones no obligatorias; de esta forma se simplifica el flujo posterior, la validación de la entrada y hace nuestro código un poco más robusto ante omisiones.

      Un ejemplo de argumentos para nuestra aplicación anterior sería: app --input file.txt -o output.txt --error-level 5 -v.

      Parámetros ocultos

      Es posible definir los parámetros en diversos options_description. Los principales usos son el de poder discriminar cuáles se usan (por ejemplo, en base a la versión del sistema anfitrión, licencia del cliente, variables de entorno, etc.), o el de definir parámetros ocultos (ya veremos a qué me refiero).

      Boost sólo puede interpretar un único conjunto de opciones, así que la solución pasa por unificar las que necesitemos como paso previo a la interpretación:

      po::options_description po_desc_hidden("Hidden options");
      po_desc_hidden.add_options()
        ("gold", po::bool_switch()->default_value(false), "give you a lot of gold")
      ;
      
      po::options_description cmdline_options;
      cmdline_options.add(po_desc).add(po_desc_hidden);
      

      Sugerencia👀

      Como nota de experiencia, sugiero desactivar el formateo automático de código para esta sección, a fin de mantener cada opción en una línea. Esto mejora la lectura del código y mantiene más limpio el historial de cambios en el repositorio. Por ejemplo, si usáis clang-format se puede hacer de la siguiente forma:

      // clang-format off
      po_desc.add_options()
        // ...
        ;
      // clang-format on
      

      Análisis de los argumentos

      El siguiente paso es parsear (analizar gramaticalmente) la línea de comandos:

      po::variables_map po_vm;
      try {
        po::store(po::command_line_parser(argc, argv).options(cmdline_options).run(), po_vm);
        po::notify(po_vm);
      } catch (po::error &e) {
        std::cout << e.what() << '\n';
        std::cout << po_desc << '\n';
        return false;
      } catch (...) {
        std::cout << "Unknown error\n";
        std::cout << po_desc << '\n';
        return false;
      }
      

      Si la línea de comandos tiene algún error (normalmente parámetros desconocidos o formato incorrecto), capturaremos la excepción (mostrando el error si lo conocemos) y luego mostramos una ayuda para que el usuario sepa cuál es la sintaxis correcta (std::cout << po_desc << '\n'). Nótese que en esta línea no usamos cmdline_options sino po_desc, que es la que contiene la lista de opciones pública; si mostrásemos cmdline_options estaríamos revelando todas las opciones del programa (y en este ejemplo no nos interesa). Por último, indicamos que la función parse ha fallado devolviendo un false.

      Uso de las opciones

      Ahora tenemos la línea de comandos descompuesta en las opciones que hemos definido, y almacenadas en la variable po_vm; solamente nos queda poner los valores correctos a las variables.

      Existen varias formas de acceder a estas opciones, aunque las tres más comunes son:

      • Verificando si la opción ha sido escrita por el usuario: po_vm.count("option_name") > 0.
      • Sabiendo que existe (bien por el método anterior, o porque hemos indicado que siempre tenga un valor por defecto), podemos acceder a su valor: po_vm["option_name"].as<T>, donde T es el tipo de datos que hemos indicado en la definición. Aviso⚠: acceder de esta forma a una opción no definida o sin valor lanza una excepción. Por mi parte, en lo posible trato de que todas las opciones no obligatorias tengan un valor por defecto.
      • Asociando una opción a una variable: esta opción es muy práctica, aunque no la suelo usar simplemente porque me gusta separar mentalmente el análisis de la interpretación, sabiendo que no tengo valores a medias en caso de error. Para asociar una opción a una variable solamente tenemos que indicarlo en la definición de la opción: ("language", po::value<std::string>(&lang), "UI language").
      if (po_vm.count("help")) {
        std::cout << po_desc << '\n';
        return false;
      }
      
      input_path = po_vm["input"].as<std::string>();
      output_path = po_vm["output"].as<std::string>();
      
      lang = po_vm["language"].as<std::string>();
      
      error_level = po_vm["error-level"].as<int>();
      verbose = po_vm["verbose"].as<bool>();
      

      Otros tópicos

      Argumentos posicionales

      Los argumentos posicionales son aquellos cuya semántica viene dada por su posición en la lista de argumentos. Por ejemplo app input.txt output.txt podría tener dos argumentos posicionales, donde el primero representa al ruta del fichero de entrada y el segundo la ruta del de salida.

      De nuestro ejemplo anterior, supongamos queremos que el fichero de entrada y el de salida sean posicionales:

      po::positional_options_description po_pos;
      po_pos.add("input", 1);
      po_pos.add("output", 1);
      

      Los argumentos se seleccionan en el orden en el que se definen, y se asocian a la opción que se indica. El número después del nombre indica cuántos argumentos de ese tipo se esperan, donde -1 indica ilimitados (como sugiere la lógica, no se pueden definir nuevos argumentos posicionales una vez se define uno ilimitado).

      Por último, es necesario añadirlos al analizador:

      po::store(po::command_line_parser(argc, argv).options(cmdline_options).positional(po_pos).run(), po_vm);
      

      Argumentos en UNICODE

      Me gustaría hacer un comentario aparte acerca de cuando los argumentos no usan una codificación ANSI: si necesitamos leer un fichero y su ruta (path) viene dado como argumento de la línea de comandos, es probable que dicha ruta contenga caracteres fuera del espectro de ANSI: vocales acentuadas, la española Ñ, caracteres en cirílico, un nombre de usuario en chino tradicional, etc. Por supuesto, aunque este quizá sea el escenario más tradicional, podríamos encontrar el mismo problema en muchos otros.

      Este problema lo planteé en Stack Overflow hace ya un tiempo; expongo acá la respuesta como complemento del artículo. Importante⚠: esta solución está enfocada a Windows.

      • Cambiar el punto de entrada para que acepte cadenas de texto en UNICODE: int wmain(int argc, wchar_t* argv[]).
      • Usar boost::program_options::wvalue en lugar de boost::program_options::value cuando el argumento espere valores en UNICODE.
      • Usar un tipo de datos std::wstring para estos argumentos.
      • Usar boost::program_options::wcommand_line_parser en lugar de boost::program_options::command_line_parser para aceptar la lista de argumentos en wchar_t*.

      Aun más

      Este breve tutorial deja por fuera algunas otras opciones, que enumero a continuación:

      • Uso de sintaxis no estándar).
      • Permitir argumentos no registrados (por ejemplo, para re-enviarlos a otro comando).
      • Uso de argumentos provenientes del punto de entrada WinMain.
      • Validadores personalizados (por ejemplo, que sea requiera un e-mail y sea el propio Boost el que compruebe que la entrada corresponde con un formato de e-mail válido).

      Estos tópicos están documentados en este anexo de Boost.

      Ejemplo completo

      Se puede probar la mayoría del código de este artículo en vivo.

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

      Coding Potions

      Cómo crear un reloj analógico con Javascript - Sortilegios 02

      mayo 03, 2021 12:00

      Qué vamos a hacer

      Seguimos con la serie de artículos en las que vamos a construir pequeños proyectos como excusa para aprender programación. En este segundo episodio vamos a seguir con el día 2 de #Javascript30, el reto de construir 30 pequeñas aplicaciones web con Javascript.

      Este tiene un enunciado sencillo, pero no te dejes engañar, si no sabes cómo abordarlo puede que al principio te cueste. Para este reto se pide crear un reloj analógico con CSS y JS.

      🗺️ Hoja de ruta

      • Crear el HTML con los elementos a dibujar
      • Dar estilos a los elementos para que parezcan un reloj
      • Pasar la hora actual a grados
      • Coger los elementos del HTML y pasarle los estilos para que marquen la hora

      Manos a la obra 👷

      Creado la vista y los estilos

      Lo que voy a hacer es hacerlo con SVG. Aunque también se puede hacer con elementos HTML he preferido hacerlo así para dar alguna pincelada sobre SVG.

      Empecemos por el círculo:

      <div class="clock">
        <svg class="circle" viewBox="0 0 120 120" version="1.1"
          xmlns="http://www.w3.org/2000/svg">
          <circle cx="60" cy="60" r="60" />
        </svg>
      </div>
      

      Creamos un div simplemente para meter el SVG que contendrá la esfera del reloj y las manecillas.

      Como ves, en SVG existe un elemento destinado a crear círculos. Las propiedades cx y cy marcan la posición del círculo dentro del reloj, en esta caso al ser la mitad que la viewBox, se situará en el centro.

      De momento simplemente voy a posicionar el reloj en el centro usando CSS.

      .clock {
        margin: 0px auto;
        width: 650px;
        height: 650px;
      }
      

      Como no hemos definido un color para el círculo por defecto se pintará en negro. Para añadir un color tienes que hacerlo mediante la propiedad fill, la puedes añadir en el HTML o en el CSS:

      <circle cx="60" cy="60" r="60" fill="#fabada" />
      
      circle {
        fill: #fabada;
      }
      

      Creando las manecillas

      Vamos ahora con las manecillas. La idea es crear 3 líneas dentro del SVG (una para cada manecilla). En principio las voy a crear para que salgan desde la parte de arriba y vayan hasta el centro del reloj.

      Las líneas serían así:

      <line x1="60" y1="0" x2="60" y2="60" class="hours" />
      <line x1="60" y1="0" x2="60" y2="60" class="minutes" />
      <line x1="60" y1="0" x2="60" y2="60" class="seconds" />
      

      Los parámetros x1 y y1 sirven para indicar la posición dentro del SVG del punto de partida. En este caso el punto de partida es 60 (el radio del círculo) para el eje X y 0 en el Y para que se coloque arriba en el centro del reloj.

      Los parámetros x2 y y2 son para el punto de destino de la línea, en nuestro caso el centro del reloj, por lo tanto 60 y 60.

      También he añadido clases CSS para poder cambiar el color de las líneas usando la propiedad stroke. Esto no lo hago con el fill porque con stroke quiero que sea como una propiedad de un borde, así puedo añadir stroke-linecap para redondearlo y que no sea una línea rectangular.

      Además, para las 3 líneas he puesto que el transform-origin esté en el centro para que al rotar la línea se haga desde el centro del reloj.

      .hours,
      .minutes,
      .seconds {
        transform-origin: center;
        stroke-linecap: round;
        stroke-width: 3px;
      }
      .hours {
        stroke: cyan;
      }
      .minutes {
        stroke: lime;
      }
      .seconds {
        stroke: fuchsia;
      }
      

      Vamos ahora con el Javascript.

      Calculando la rotación de las manecillas

      El Javascript parece que puede ser muy complicado pero si lo piensas no lo es tanto.

      Simplemente lo que necesitamos es calcular los grados de rotación de cada manecilla pasando la hora actual. Haz una prueba, en el CSS pon esto:

      .minutes {
        stroke: lime;
        transform: rotate(90deg);
      }
      

      ¿Ves que la manecilla de los minutos ahora apunta hacia la derecha? Eso es gracias al transform-origin, tan solo tenemos que sacar los grados entre 0 y 360 (360 porque una circunferencia tiene 360 grados). Ya puedes quitar lo del transform.

      Lo primero que he hecho es crear una función que se autoejecuta para que se lance cuando se cargue el Javascript.

      (function () {
         calculateHourDegrees();
         calculateMinuteDegrees();
         calculateSeconds();
      })();
      

      Simplemente llamo a 3 funciones que voy a crear ahora para calcular los grados de cada manecilla.

      Lo siguiente que hago es crear una función que servirá para hacer una especie de regla de 3 para poder calcular los grados:

      function linearMap(value, min, max, newMin, newMax) {
          return newMin + (newMax - newMin) * (value - min) / (max - min);
      }
      

      Es muy simple, pasas un número value y con min y max pones el rango que tiene ese valor, es decir, el mínimo valor y el máximo que puede tener ese número. Por último, pasas el nuevo valor mínimo con newMiny el nuevo máximo con newMax y la función te devolverá el nuevo valor en el nuevo rango.

      Pongamos un ejemplo. Imagina que queremos calcular los grados (entre 0 grados y 360 grados como hemos dicho) de la manecilla de los minutos. Pongamos que son las 12:33, la llamada a esa función sería así:

      linearMap(33, 0, 60, 0, 360);
      

      El primer parámetro son los minutos, 33, min y max son 0 y 60 porque los minutos como mucho pueden ser 60 y el nuevo valor mínimo y el máximo es 0 y 360. En otras palabras, es una simple regla de 3 que uso para sacar los grados.

      Sabiendo esto ya podemos crear la función para calcular la manecilla de las horas:

      function calculateHourDegrees() {
        const currentHour = new Date().getHours() - 12;
        const angle = linearMap(currentHour, 0, 12, 0, 360);
        document.querySelector(".hours").style.transform = `rotate(${angle}deg)`;
      }
      

      Lo primero que hago es sacar la hora actual y le resto 12 (porque puede ir hasta 24 pero en un reloj analógico nos vale con 12).

      Saco el ángulo con la función que he explicado antes y lo que hago es seleccionar con el querySelector el elemento del HTML con la línea de las horas para ponerle como estilo el transform con los grados.

      Las otras funciones, la de los minutos y segundos es igual:

      function calculateMinuteDegrees() {
        const currentMinutes = new Date().getMinutes();
        const angle = linearMap(currentMinutes, 0, 60, 0, 360);
        document.querySelector(".minutes").style.transform = `rotate(${angle}deg)`;
      }
      
      function calculateSeconds() {
        const currentMinutes = new Date().getSeconds();
        const angle = linearMap(currentMinutes, 0, 60, 0, 360);
        document.querySelector(".seconds").style.transform = `rotate(${angle}deg)`;
      }
      

      Con eso ya se calculan los grados del reloj cuando cargamos el HTML y el Javascript, pero falta algo, ir actualizando el reloj según cambia la hora. Para ello voy a envolver el calculo de los grados dentro de un setInterval:

      (function () {
        setInterval(() => {
         calculateHourDegrees();
         calculateMinuteDegrees();
         calculateSeconds();
        }, 1000);
      })();
      

      El setInterval lo que va a hacer es llamar a las 3 funciones cada segundo (por eso pone 1000 porque son 1000 milisegundos). El reloj quedaría así:

      En la imagen se aprecia un círculo negro con 3 manecillas

      Por cierto, en el HTML, en cada línea, he ajustado el valor y1 para que cada manecilla mida distinto, como en los relojes analógicos. Esto va al gusto de cada uno.

      Por último para dejarlo fino fino voy a meter dentro del CSS una transición de la propiedad transform de las manecillas de las horas y de los minutos para que cuando cambie el valor del ángulo el cambio no sea brusco.

      .hours,
      .minutes,
      .seconds {
        transform-origin: center;
        stroke-linecap: round;
      }
      
      .hours {
        stroke: fuchsia;
        stroke-width: 3px; 
        transition: transform 1s ease-in-out;
      }
      .minutes {
        stroke-width: 2px; 
        stroke: lime;
        transition: transform 1s ease-in-out;
      }
      .seconds {
        stroke: white;
      }
      

      Pero esto no acaba aquí, como detalle final voy a pintar las típicas líneas para marcar las horas alrededor de las manecillas.

      Ahora lo que voy a hacer es crear 12 líneas (una para señalar cada hora) dentro del SVG:

      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      <line x1="60" y1="5" x2="60" y2="10" class="line" />
      

      He ajustado el x e y de cada punto para que midan poquito y se dibujen cerca del borde. Ahora, como pasa con las manecillas hay que calcular los grados de cada una de las líneas para que se dibujen alrededor del círculo. Lo primero el CSS, parecido a las manecillas:

      .line {
        stroke-width: 1px;
        stroke: white;
        stroke-linecap: round;
        transform-origin: center;
      }
      

      Y por último el Javascript. Antes de seguir leyendo piensa primero cómo lo harías e intenta hacerlo sin mirar cómo lo he hecho yo.

      Yo lo que se me ha ocurrido es tener en una lista de objetos todas las líneas para recorrerla con bucle for e ir poniendo los ángulos a cada línea.

      function calculateLines() {
        const lines = document.querySelectorAll(".line");
        const numberLines = lines.length;
        for (let i = 0; i < numberLines; i++) {
          const line = lines[i];
          const angle = linearMap(i, 0, numberLines, 0, 360);
          line.style.transform = `rotate(${angle}deg)`;
        }
      }
      

      Con querySelectorAll pillo en forma de lista todos los elementos del HTML con la clase “line”. Usando un bucle for recorro las líneas y con la función de linearMap que hemos creado antes calculo los grados para cada línea. Fíjate en el detalle que ahora el primer parámetro que paso es i, es decir, el número de esa línea, que va desde 0 al número de líneas que haya, así se reparten las líneas entre la circunferencia y se quedan a la misma distancia.

      Lo bueno de hacerlo así es que si ahora decides que quieres más o menos líneas alrededor de la esfera simplemente tienes que añadir o quitar las líneas del HTML. Automáticamente se calculará su posición para que queden repartidas en el reloj.

      Resultado final:

      En la imagen se aprecia un círculo negro con 3 manecillas y líneas alrededor para cada 5 minutos

      Demo y código fuente

      https://codepen.io/Frostq/pen/XQNXpq

      Deberes

      Si quieres seguir practicando cosas con el reloj te propongo este par de ejercicios:

      • Añadir los números del reloj en su posición, es decir los números del 1 al 12. Para que quede mejor haz que los números 1, 3, 6 y 9 sean más grandes y el resto más pequeños.

      • Añade una esfera interna al reloj, en pequeño con las iniciales de los días de la semana: L, M, X, J, V, S y D. Añade otra manecilla que se mueva indicando el día de la semana actual.

      Conclusiones

      Este tipo de proyectos te recomiendo que primero los intentes hacer sin mirar el código que aparece aquí (puedes leer más o menos como lo planteo por encima) porque cuando lo haces por ti mismo y te sale sin mirar la solución te sientes muy bien.

      Si lo has hecho mirando y copiando mi código no pasa nada, al principio este tipo de ejercicios suele costar, pero te recomiendo que vuelvas a intentar hacerlo sin mirar dentro de un tiempo.

      También te digo que el código que yo explico puede que no sea el mejor o el más óptimo, simplemente es mi solución, seguramente haya otras mucho mejores.

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

      Bitácora de Javier Gutiérrez Chamorro (Guti)

      Preload del OPCache en PHP

      mayo 02, 2021 05:29

      Sigo profundizando en el rendimiento de WordPress y también de tu web. En esta ocasión hablaré de las mejoras en cuando al Zend Opcache que si bien se introdujeron en PHP 7.4, no han empezado a despertar el interés hasta el lanzamiento de PHP 8.

      Opcache (php_opcache) es una vieja extensión introducida en PHP 5.5, aunque estaba disponible de manera separada desde el 5.3 creo recordar. Su misión es esencialmente cachear el código parseado/compilado de PHP en memoria. Ya sabéis que tanto si usamos JIT como si no, cada vez que se carga una página el motor de PHP lee de disco sus archivos, lo interpreta, carga sus include/require y finalmente la ejecuta. Ese proceso es lento y por eso Opcache suele producir bastantes beneficios.



      Preload del OPCache en PHP

      El inconveniente de Opcache es que para cada instancia que se lanza de PHP, ya sea esta vía CGI como FastCGI se debe regenerar el contenido de ese caché, lo cual lleva tiempo. Eso es lo que viene a solucionar el preload o preloading que mencionaba. La directiva preload le indica a Opcache qué archivo PHP debe ejecutar al cargarse. Para ello modificamos nuestro php.ini o el archivo de configuración de PHP que usemos para que contenga lo siguiente:

      opcache.preload = "/home/htdocs/www/_opcache.preload.php"

      Efectivamente /home/htdocs/www/_opcache.preload.php es la ruta física (del sistema de archivos del sistema) que contiene el archivo con los contenidos a precargar. Este ejemplo lo que indica son las clases de WordPress necesarias y que he obtenido de internet aplicándole algunas mejoras:

      declare(strict_types=1);
      $wp_dir = dirname(__FILE__) . '/';
      $preload_patterns =
      [
      	$wp_dir . 'wp-includes/Text/Diff/Renderer.php',
      	$wp_dir . 'wp-includes/Text/Diff/Renderer/inline.php',
      	$wp_dir . 'wp-includes/SimplePie/**/*.php',
      	$wp_dir . 'wp-includes/SimplePie/*.php',
      	$wp_dir . 'wp-includes/Requests/**/*.php',
      	$wp_dir . 'wp-includes/Requests/*.php',
      	$wp_dir . 'wp-includes/**/class-*.php',
      	$wp_dir . 'wp-includes/class-*.php',
      ];
      
      $exclusions =
      [
      	$wp_dir . 'wp-includes/class-simplepie.php',
      	$wp_dir . 'wp-includes/SimplePie/File.php',
      	$wp_dir . 'wp-includes/SimplePie/Core.php',
      	$wp_dir . 'wp-includes/class-wp-simplepie-file.php',
      	$wp_dir . 'wp-includes/class-snoopy.php',
      	$wp_dir . 'wp-includes/class-json.php',
      ];
      
      foreach ($preload_patterns as $pattern)
      {
      	$files = glob($pattern);
      	foreach ($files as $file)
      	{
      		if (!in_array($file, $exclusions, true))
      		{
      			opcache_compile_file($file);
      		}
      	}
      }
      



      Preload del OPCache en PHP

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

      Bitácora de Javier Gutiérrez Chamorro (Guti)

      Optimizaciones de .htaccess para tu web y también para WordPress

      abril 20, 2021 10:59

      Cuando me preparé para escribir Nulled WP-Rocket y cómo parchearlo necesariamente me tuve que familiarizar con el funcionamiento del plugin WP-Rocket. Me di cuenta que aunque es el más rápido del mercado, todavía lo podía ser aún más, puesto que había algunas optimizaciones elementales a nivel de .htaccess que no habían aplicado.

      Como forma de devolverles el favor decidí sugerírselas por lo que supongo que pronto las tendrán disponibles. De hecho también les apunté a optimizar estáticamente sus imágenes (PNG y SVG) que con FileOptimizer ahorrarían casi 64 KB., aunque en esto no me hicieron caso.



      Optimizaciones de .htaccess para tu web y también para WordPress

      A lo que íbamos, estos ajustes que menciono los puedes aplicar tu mismo, tanto si usas WP-Rocket como cualquier otro plugin de caché (WP Fastest Cache, Super Cache, W3 Total Cache), e incluso si no utilizas ninguno en absoluto.

      Se encargan de modificar algunas opciones del servidor web Apache para:
      – Aumentar la velocidad de carga estableciendo la fecha de expiración de los recursos (Leverage Browser Caching).
      – Reducir el tamaño de las páginas y por tanto aumentando su velocidad activando la compresión GZIP, Deflate y la nueva Brotli.
      – Mejorar la seguridad con algunos nuevos encabezados.
      – Mejorar el rendimiento gracias a Google Page Speed en el caso de que mod_pagespeed.so esté instalado.

      # Use UTF-8 encoding for anything served text/plain or text/html
      AddDefaultCharset UTF-8
      # Force UTF-8 for a number of file formats
      
      	AddCharset UTF-8 .atom .css .js .json .rss .vtt .xml
      
      # FileETag None is not enough for every server.
      
      	Header unset ETag
      
      # Since we’re sending far-future expires, we don’t need ETags for static content.
      # developer.yahoo.com/performance/rules.html#etags
      FileETag None
      
      	
      		
      			Header unset Pragma
      			Header append Cache-Control "public"
      			Header unset Last-Modified
      		
      	
      	
      		
      			Header unset Pragma
      			Header append Cache-Control "public"
      		
      	
      
      # Expires headers (for better cache control)
      
      	ExpiresActive on
      	ExpiresDefault                              "access plus 1 month"
      	# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
      	ExpiresByType text/cache-manifest           "access plus 0 seconds"
      	# Your document html
      	ExpiresByType text/html                     "access plus 0 seconds"
      	# Data
      	ExpiresByType text/xml                      "access plus 0 seconds"
      	ExpiresByType application/xml               "access plus 0 seconds"
      	ExpiresByType application/json              "access plus 0 seconds"
      	# Feed
      	ExpiresByType application/rss+xml           "access plus 1 hour"
      	ExpiresByType application/atom+xml          "access plus 1 hour"
      	# Favicon (cannot be renamed)
      	ExpiresByType image/x-icon                  "access plus 1 week"
      	# Media: images, video, audio
      	ExpiresByType image/gif                     "access plus 4 months"
      	ExpiresByType image/png                     "access plus 4 months"
      	ExpiresByType image/jpeg                    "access plus 4 months"
      	ExpiresByType image/webp                    "access plus 4 months"
      	ExpiresByType video/ogg                     "access plus 4 months"
      	ExpiresByType audio/ogg                     "access plus 4 months"
      	ExpiresByType video/mp4                     "access plus 4 months"
      	ExpiresByType video/webm                    "access plus 4 months"
      	# HTC files  (css3pie)
      	ExpiresByType text/x-component              "access plus 1 month"
      	# Webfonts
      	ExpiresByType font/ttf                      "access plus 4 months"
      	ExpiresByType font/otf                      "access plus 4 months"
      	ExpiresByType font/woff                     "access plus 4 months"
      	ExpiresByType font/woff2                    "access plus 4 months"
      	ExpiresByType image/svg+xml                 "access plus 1 month"
      	ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
      	# CSS and JavaScript
      	ExpiresByType text/css                      "access plus 1 year"
      	ExpiresByType application/javascript        "access plus 1 year"
      
      # Gzip compression
      
      	# Active compression
      	SetOutputFilter DEFLATE
      	# Force deflate for mangled headers
      	
      		
      			SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
      			RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
      			# Don’t compress images and other uncompressible content
      			SetEnvIfNoCase Request_URI \
      			\.(?:gif|jpe?g|png|rar|zip|exe|flv|mov|wma|mp3|avi|swf|mp?g|mp4|webm|webp|pdf)$ no-gzip dont-vary
      		
      	
      	# Compress all output labeled with one of the following MIME-types
      	
      		AddOutputFilterByType DEFLATE application/atom+xml \
                        application/javascript \
                        application/json \
                        application/rss+xml \
                        application/vnd.ms-fontobject \
                        application/x-font-ttf \
                        application/xhtml+xml \
                        application/xml \
                        font/opentype \
                        image/svg+xml \
                        image/x-icon \
                        text/css \
                        text/html \
                        text/plain \
                        text/x-component \
                        text/xml
      	
      	
      		Header append Vary: Accept-Encoding
      	
      
      
      	AddType text/html .html_gzip
      	AddEncoding gzip .html_gzip
      
      
      	SetEnvIfNoCase Request_URI \.html_gzip$ no-gzip
      
      
      	RewriteEngine On
      	RewriteBase /
      	RewriteCond %{HTTPS} on [OR]
      	RewriteCond %{SERVER_PORT} ^443$ [OR]
      	RewriteCond %{HTTP:X-Forwarded-Proto} https
      	RewriteRule .* - [E=WPR_SSL:-https]
      	RewriteCond %{HTTP_ACCEPT} image/webp
      	RewriteCond "%{DOCUMENT_ROOT}/wp-content/cache/wp-rocket/%{HTTP_HOST}%{REQUEST_URI}/.no-webp" !-f
      	RewriteRule .* - [E=WPR_WEBP:-webp]
      	RewriteCond %{HTTP:Accept-Encoding} gzip
      	RewriteRule .* - [E=WPR_ENC:_gzip]
      	RewriteCond %{REQUEST_METHOD} GET
      	RewriteCond %{QUERY_STRING} =""
      	RewriteCond %{HTTP:Cookie} !(wordpress_logged_in_.+|wp-postpass_|wptouch_switch_toggle|comment_author_|comment_author_email_) [NC]
      	RewriteCond %{REQUEST_URI} !^(/(.+/)?feed/?.+/?|/(?:.+/)?embed/|/(index\.php/)?wp\-json(/.*|$))$ [NC]
      	RewriteCond %{HTTP_USER_AGENT} !^(facebookexternalhit).* [NC]
      	RewriteCond "%{DOCUMENT_ROOT}/wp-content/cache/wp-rocket/%{HTTP_HOST}%{REQUEST_URI}/index%{ENV:WPR_SSL}%{ENV:WPR_WEBP}.html%{ENV:WPR_ENC}" -f
      	RewriteRule .* "/wp-content/cache/wp-rocket/%{HTTP_HOST}%{REQUEST_URI}/index%{ENV:WPR_SSL}%{ENV:WPR_WEBP}.html%{ENV:WPR_ENC}" [L]
      
      
      ServerSignature Off
      Options -Indexes
      
      	ModPagespeed on
      	ModPagespeedRewriteLevel OptimizeForBandwidth
      	ModPagespeedForceCaching on
      	ModPagespeedEnableCachePurge on
      	ModPagespeedEnableFilters collapse_whitespace,extend_cache,combine_heads,elide_atttributes,remove_quotes
      	ModPagespeedEnableFilters prioritize_critical_css,rewrite_css,fallback_rewrite_css_urls,move_css_to_head
      	ModPagespeedEnableFilters canonicalize_javascript_libraries,rewrite_javascript,defer_javascript,make_google_analytics_async
      	ModPagespeedEnableFilters recompress_images,sprite_images,recompress_webp,responsive_images,resize_images,lazyload_images,convert_jpeg_to_progressive
      	ModPagespeedEnableFilters convert_png_to_jpeg,convert_jpeg_to_webp
      
      
      	Header set Connection keep-alive
      
      
      	Header set X-XSS-Protection "1; mode=block"
      	Header always append X-Frame-Options SAMEORIGIN
      	Header set X-Content-Type-Options nosniff
      	Header always set Referrer-Policy "no-referrer, strict-origin-when-cross-origin"
      
      
      	mod_gzip_on Yes
      	mod_gzip_dechunk Yes
      	mod_gzip_item_include file \.(html?|txt|css|js|php|pl)$
      	mod_gzip_item_include handler ^cgi-script$
      	mod_gzip_item_include mime ^text/.*
      	mod_gzip_item_include mime ^application/x-javascript.*
      	mod_gzip_item_exclude mime ^image/.*
      	mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
      
      
      	#BrotliCompressionQuality 6
      	AddOutputFilterByType BROTLI_COMPRESS application/atom+xml \
      		                          application/javascript \
      		                          application/json \
      		                          application/rss+xml \
      		                          application/vnd.ms-fontobject \
      		                          application/x-font-ttf \
      		                          application/xhtml+xml \
      		                          application/xml \
      		                          font/opentype \
      		                          image/svg+xml \
      		                          image/x-icon \
      		                          text/css \
      		                          text/html \
      		                          text/plain \
      		                          text/x-component \
      		                          text/xml
      

      Lo mejor de todo es que no necesitas ningún plugin para aumentar el rendimiento de tu web, esté construida en la plataforma que esté construida, porque son añadidos que puedes agregar directamente a tu servidor web usando el .htaccess. Como ves, comprueban que la funcionalidad esté disponible en el entorno antes de usarse, así que no tienen riesgo de romper nada, eso sí, haz una copia de seguridad del archivo antes de tocarlo, no vaya a ser que se estropeé algo.

      Unas pocas opciones son de mi propia cosecha, es decir, las he evaluado yo sin haberlas visto mencionadas en demasiado sitios. Sin embargo la mayoría están sacadas de un sitio y de otro.



      Optimizaciones de .htaccess para tu web y también para WordPress

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

      Bitácora de Javier Gutiérrez Chamorro (Guti)

      Nulled WP-Rocket y cómo parchearlo

      abril 17, 2021 04:51

      Hay una cosa que muchos no saben y que que el gestor de contenidos o CMS WordPress es código abierto con licencia GPL (GNU Lesser General Public License), lo cual exige que todos sus componentes también lo sean. Entre otras cosas significa que cualquiera es libre de hacer lo que quiera con él, incluso venderlo o modificarlo siempre y cuando el producto resultante también sea GPL.

      Hace unos años, en un afán de simplificar mi instalación quité el caché de WordPress. Sin embargo las cosas han ido cambiando y bien es cierto que después de estar con W3 Total Cache no probé los actuales líderes en cuanto a cacheo, todos ellos plugins de pago: WP-Rocket y WP Fastest Cache Premium. WP-Rocket se lanzó en 2013 y tiene un equipo de trabajo detrás, es decir no es la obra de un hombre solo como muchos otros.

      Hice unas pruebas y me di cuenta que con WP-Rocket y con WP Fastest Cache todo mejoraba mucho. Sin embargo eran plugins de pago, el primero desde 49$/año y el segundo desde 49$ de por vida. ¿Pero cómo pueden ser de pago siendo GPL? Bueno, porque GPL no impide cobrar por el software siempre y cuando se proporcione el código fuente del mismo y la libertad de estudiarlo y modificarlo entre otras.



      Nulled WP-Rocket y cómo parchearlo

      Según indican desde WP-Media el plugin WP-Rocket está instalado en cerca de 1,5 millones de webs, así que a 49$ por año podéis hacer números vosotros mismos…

      En efecto han surgido multitud de servicios que ofrecen plugins comerciales de WordPress a precio más ventajoso. El sistema que utilizan es sencillo, compran uno y cómo es GPL lo modifican y lo venden más barato, algo que es perfectamente legal.

      En el caso de WP-Rocket no es necesario llegar a tanto, porque sus autores WP-Media tienen el código disponible en Github. No tenemos más que descargarlo o clonarlo y nos haremos con él, por lo que se puede ofrecer gratuitamente sin problemas.

      Queda inicializarlo mediante composer, la parte más tediosa si no lo tenemos instalado y que con muchas dependencias de software nos instalará las dependencias que necesita:

      composer require wp-media/wp-rocket



      Nulled WP-Rocket y cómo parchearlo

      El primer paso para tenerlo nulled, nuleado o registrado es introducir nuestros datos de usuario. Es lo mismo que deben hacer aquellos que lo han comprado desde wp-rocket.me, sólo que nosotros nos los inventaremos.

      license-data.php

      // Your license KEY.
      if ( ! defined( 'WP_ROCKET_KEY' ) ) {
      	define( 'WP_ROCKET_KEY', '1234567');
      }
      
      // Your email, the one you used for the purchase.
      if ( ! defined( 'WP_ROCKET_EMAIL' ) ) {
      	define( 'WP_ROCKET_EMAIL', 'gmail@gmail.com' );

      También debemos introducir nuestra clave privada, que de nuevo es inventada:
      wp-rocket.php

      define( 'WP_ROCKET_PRIVATE_KEY'         , '365tredstq3gy5egftae45t4y5twegd');

      Ahora queda retornar que el software está licenciado y que su licencia es válida hasta el timestamp que queramos, en este caso 2030:

      inc/Engine/License/API/UserCLient.php

      public function get_user_data() {
      	return (object) [
      		'licence_account'    => '-1',
      		'licence_expiration' => 1893456000,
      	];
      }

      Y finalmente saltarnos las validaciones de claves que están en un par de sitios del mismo archivo:

      functions/options.php

      function rocket_valid_key() {
      	return true;
      }
      
      function rocket_check_key() {
      	Logger::info( 'LICENSE VALIDATION PROCESS STARTED.', [ 'license validation process' ] );
      
      	$rocket_options                   = [];
      	$rocket_options['consumer_key']   = 'activated';
      	$rocket_options['consumer_email'] = 'gmail@gmail.com';
      	$rocket_options['secret_key']     = 'activated';
      
      	if ( ! get_rocket_option( 'license' ) ) {
      		$rocket_options['license'] = '1';
      	}
      
      	Logger::info( 'License validation successful.', [ 'license validation process' ] );
      
      	set_transient( rocket_get_constant( 'WP_ROCKET_SLUG' ), $rocket_options );
      	delete_transient( 'rocket_check_key_errors' );
      	rocket_delete_licence_data_file();
      
      	return $rocket_options;
      }

      Para ahorrarte el trabajo puedes descargar tu mismo el archivo nulled_wp-rocket_3.8.8.zip (2,5 MB en formato ZIP), que además he aprovechado para optimizar con FileOptimizer, así que podríamos decir que es aún más rápido que el genuino.



      Nulled WP-Rocket y cómo parchearlo

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

      Header Files

      Huevos de Pascua

      abril 07, 2021 03:16

      Primero que nada, ¡feliz Pascua de Resurrección! Como viene siendo tradición desde hace muchísimos años, una de las actividades que realizan los pequeños en este domingo es la búsqueda de pequeños (o no tan pequeños) huevos de chocolate que han sido escondidos por diversos lugares, los llamados huevos de Pascua. De forma similar, los huevos de Pascua virtuales son mensajes, detalles o características ocultas en una aplicación, película, etc.

      El primer huevo de Pascua informático que se conoce está en el arcade Starship 1 (1977). Pulsando los botones de una forma específica aparecía un mensaje y el jugador ganaba 10 partidas gratis. Este huevo de Pascua tardó 30 años en ver la luz.

      El término huevo de Pascua fue acuñado por Steve Wright, director de desarrollo de Atari. Warren Robinett, programador de Atari, introdujo una función secreta en el videojuego Adventure (1979). Al pasar el personaje sobre un punto específico de la pantalla y siguiendo una secuencia de pasos, aparecía un mensaje que decía “Creado por Warren Robinett” (en ese entonces no se incluían los nombres de los programadores en los créditos). Tiempo después se animó a los desarrolladores a introducir más características ocultas de este tipo, y se les llamó huevos de Pascua porque estaban escondidos y los jugadores tenían que encontrarlos.

      warren_robinett

      Los huevos de Pascua no son exclusivos de los videojuegos. Por ejemplo (y aunque parezca que soy un dinosaurio) Microsoft Office 97 incluía un pequeño simulador de vuelo en Excel, y un pinball en Word, y hasta antes de Windows XP, el salvapantallas comenzaba a mostrar nombres de volcanes al escribir volcano.

      Apple es bien conocida por muchos guiños sutiles en el diseño de sus productos, aunque no pueden considerarse huevos de Pascua en su definición más pura. Dos de mis favoritos so el logo de la aplicación TextEdit con el mensaje del anuncio Here’s to the Crazy Ones, y el icono que usa para mostrar ordenadores Windows en red:

      textedit

      pc_icon

      El primer huevo de Pascua de Apple fue la inclusión de las firmas de todos los integrantes del equipo del Macintosh en su carcasa. Otro ejemplo más reciente es el de Mac OS X Mountain Lion (2012), en el que las aplicaciones descargadas tenían temporalmente la fecha 24 de enero de 1984 (lanzamiento del primer Macintosh).

      os_x_mountain_lion

      Google por su parte es muy dado a los huevos de Pascua, siendo mi favorito el coche de Mario Kart que lanzaron por el Día Internacional de Mario en el 2018 (10 de marzo, Mar10).

      mario_karts

      Para cerrar, me gustaría añadir uno que introduje en las aplicaciones que desarrollo en STT (2DMA, 3DMA y iSen), y que ya algún usuario ha descubierto por su cuenta 😄. En la ficha de cliente de la BBDD se cambia el icono del cliente por una tarta si se abre la ficha del mismo el día de su cumpleaños.

      2dma

      En https://eeggs.com es posible encontrar una gran listado de huevos de Pascua, ¿cuáles conocíais ya?

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

      Una sinfonía en C#

      Observables en Javascript RXjs ¿Qué es un Subject?

      marzo 22, 2021 12:00

      En este cuarto y último post sobre Observables en Javascript vamos a ver el último tema que, según mi opinión, forma parte de los conceptos más básicos para comprender los observables. Vamos a hablar de Subjects. Si no has leídos los posts anteriores dejo la lista:

      ¿Qué es un Subject?

      En pocas palabras, es un objeto que permite crear un observable para controlarlo manualmente, por ejemplo.

      // Creamos un subject que emitirá _strings_
      let subject = new Subject<string>();
      // nos suscribimos
      subject.subscribe({
          next: (value) => {
              console.log(value);
          }
      });
      // invocamos manualmente el método next y emitimos un valor
      subject.next('Hola');
      
      

      En el ejemplo de arriba vemos el funcionamiento más básico de Subject, crear un observable que podemos controlar, luego nos suscribimos y el resultado es el esperado, emite un valor cuando invocamos manualmente el método next.

      Hola
      

      Crear nuestros propios observables y controlarlos

      Entonces, podríamos tranquilamente hacer algo así:

      let subject = new Subject<number>();
      
      subject.subscribe({
          next: (value) => console.log(value),
          complete: () => console.log('complete'),
          error: (value) => console.log('Error ' + value)
      });
      
      let i = 0;
      let counter = 0;
      let interval = setInterval(()=>{
          i++;
          if(i === 10){
              counter++;
              i = 0;
              subject.next(counter);
          }
      }, 200);
      
      document.querySelector("#stopButton")?.addEventListener("click", ()=>{
          subject.complete();
          clearInterval(interval);
      });
      
      document.querySelector("#errorButton")?.addEventListener("click", ()=>{
          subject.error("Mi Error custom");
      });
      
      

      Y vemos que el resultado es el esperado, hasta que presionamos Stop y se ejecuta el complete

      1 
      2 
      3 
      4 
      complete
      

      Si en lugar de Stop forzamos un error el Subject deja de emitir también.

      1 
      2 
      3 
      4 
      5 
      6 
      7 
      Error Mi Error custom
      

      Controlar otros observables

      El último ejemplo que es el más interesante es que, ya que los operadores son observables (o funciones que retornan observables) podemos utilizar un Subject para controlar otros Observables, por ejemplo.

      let onStop = new Subject<void>();
      
      // creamos un observable del evento click de un botón
      // agregamos un operador TakeUntil para que deje de recibir eventos cuando onStop finalice.
      const observer = {
          next: (item : any) => console.log(` X:${item.clientX} Y:${item.clientY}`),
          complete: () => console.log("complete"),
          error: () => console.log("error")
      };
      
      let onStop = new Subject<boolean>();
      
      fromEvent(document, "click").pipe(takeUntil(onStop)).subscribe(observer);
      
      document.querySelector("#stopButton")?.addEventListener("click", ()=>{
          onStop.next(true);
      });
      
      

      En el ejemplo de arriba simplemente controlamos el takeUntil gracias a nuestro Subject, cuando se next es true. La idea de hacerlo así es que un evento proveniente de otro lugar puede detener a un evento diferente. O que si lo hacemos manualmente (por ejemplo el usuario hace click) podemos detener un flujo de ejemplos o varios, recordemos que un Subject al igual que cualquier Observable controla múltiples suscripciones.

      Nada más por hoy, nos leemos.

      » 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