Hola, soy Dani.

Soy un desarrollador de software y creador de contenido educativo sobre programación.

Me gustan los sistemas de software limpios y ordenados y me interesa la usabilidad y la accesibilidad. Me fascina poder crear experiencias simples y pensadas para seres humanos en primer lugar. Estoy ubicado en la zona de Madrid (España).

Estoy disponible como freelance o como consultor a partir de septiembre de 2022. Todavía esto está por desarrollar, pero si te interesa mucho, podemos ir hablando ya 🙂


Estoy intentando poner orden en los archivos de mi página web, pero mientras tanto puedes encontrar un historial en bruto de las cosas que he escrito aquí.

⬇️ ⬇️ ⬇️

  • pkg es el gestor de paquetes de FreeBSD. Es una alternativa a ports que permite instalar paquetes adicionales en el sistema que no vienen con el sistema base.

    Es posible listar los paquetes instalados utilizando pkg-info(8). Sin embargo, mi principal problema con casi todos los gestores de paquetes es lo mal que diferencian entre paquetes instalados por mi y paquetes instalados como dependencia.

    Me interesa sacar una lista de los paquetes verdaderamente instalados por mi. Esos paquetes que puse explícitamente en mi terminal al escribir algún comando pkg install. Por supuesto es importante saber la lista de dependencias que hay instaladas en mi sistema, pero si estoy fabricando una copia de seguridad con el listado de paquetes que he instalado en mi máquina, me parece importante ocultar las dependencias que no haya pedido, porque puede ocurrir que en el futuro esa dependencia también cambie con respecto a mis programas y deje de ser necesaria (o aparezcan otras nuevas).

    pkg-query(8) es el front-end para la base de datos en la que pkg guarda su configuración y estado de los distintos paquetes. Lo bonito es que cuando se instala un paquete, sí recuerda si se ha hecho a consecuencia de ser una dependencia, o porque se haya pedido explícitamente. Así que se le puede pedir un filtro que deje fuera los paquetes que se han instalado automáticamente para listar sólo los que sí se han instalado intencionalmente con el siguiente comando: pkg query -e '%a = 0' '%n'.

    danirod@beastie:~ % pkg query -e '%a = 0' '%n' | head
    ImageMagick7
    alacritty
    base64
    claws-mail
    compton
    doas
    dsbmixer
    en-freebsd-doc
    feh
    firefox
  • Amo jq, pero a la vez daría lo que fuera por poder aprender del todo su lenguaje de consulta porque a veces se me atraganta.

    Sería muy fácil con jq hacer una búsqueda en un array de objetos en función de lo que vale uno de los campos de cada objeto, ¿verdad? Hablo de un array como este:

    [
      {
        "nombre": "Carmen",
        "departamento": "Ventas",
        "superior": "Ana"
      }
    ]

    Pero ¿qué pasa si en vez de un array tengo un objeto a modo de índice inverso? Me refiero a la clásica de fabricar con este array un objeto donde como valores tengo cada uno de los valores del array, pero como clave tengo algún identificador concreto de cada objeto. Al fin y al cabo, buscar items en una lista tiene complejidad O(n), pero hacerlo en un mapa tiene complejidad O(1). Es más rápido hacer lista["Ana"] que hacer lista.find(i => i.name == "Ana").

    {
      "Carmen": {
        "nombre": "Carmen",
        "departamento": "Ventas",
        "superior": "Ana"
      }
    }

    El secreto está en usar to_entries y from_entries para convertir entre objeto y array de entries. jq 'to_entries | from_entries' < input.json se anula entre sí. Entre ambas sentencias podemos plantar un select para hacer un filtro de aquellos elementos del nuevo array generado por to_entries que filtre por una condición concreta para .[].value.whatever.

    jq 'to_entries | select(.[].value.superior == "Ana") | from_entries' < empleados.json

    La salida de to_entries es un array, por lo que cuando se hace el select para filtrar elementos que cumplan un criterio, hay que empezar la query con un .[], para poder hacer introspección sobre cada elemento del array. Realmente no es muy complejo, pero me lo apunto porque me ha costado un poco de ensayo y error dar con el orden correcto en el que tengo que poner los puntos y los corchetes.

  • Retomo el tema importar entradas en WordPress de forma masiva.

    Recientemente volví a tirar de ese script para traerme un JSON con tweets viejos a danirod.es. Ni siquiera los he hecho públicos. Por el momento sólo los he hecho privados porque lo que me interesa es tenerlos consolidados. Tampoco son tweets recientes; estamos hablando de tweets que tienen más de una década. Todos los enlaces externos dan HTTP 404, están fatal escritos y, en general, es contenido poco interesante para internet. Pero me apetecía tenerlos a mano.

    Uso el plugin Syndication Links para conectar entradas que sean importadas de otras redes sociales con su URL de referencia; las sindicaciones de Hacker News son un ejemplo. En el caso de tweets de una cuenta que ya ni siquiera existe, es bastante irrelevante plantar un u-syndication. Aun así, me apetecía ponerlo.

    En el caso del plugin Syndication Links, la metadata sobre sindicación se guarda como un campo personalizado de WordPress llamado mf2_syndication. Con la API XML-RPC de WordPress es posible crear campos personalizados rellenando el array custom_fields. Hay que tener en cuenta que no es un clave-valor simple, sino que es un array de objetos, con su campo key para el título del campo, y su campo value para el valor del campo. En el módulo de npm, los campos se dan usando la key customFields en la llamada a newPost:

    client.newPost({
      title: '...',
      content: '...',
      customFields: [
        { key: 'field-1', value: 'value value' },
        { key: 'field-2', value: 'more value more value' }
      ]
    }, (e, id) => console.log(e, id));

  • Del anuncio que hizo hace un par de semanas Google anunciando la disponibilidad global de Workspace:

    Starting today, you can enable the integrated experience in Google Workspace by turning on Google Chat. Use Rooms in Google Chat as a central place to connect, create and collaborate with others.

    Había olvidado completamente que Google Chat también es un producto que existe. Lo he abierto con curiosidad y me he encontrado con un historial viejo de Google Hangouts, por lo que asumo que definitivamente este es el reemplazo de Hangouts Chat. El reemplazo de las llamadas de Google Hangouts fue Google Meet.

    Sin embargo, no hay que confundir Google Meet con Google Duo, el cual me pareció ver hace poco que seguía vivo. No corre la misma suerte Google Allo, que parece que sí ha muerto. ¿Y Google Spaces? En la entrada se le menciona, pero si visito https://spaces.google.com se me redirige a la página de inicio de Google Workspace también.

    No sé si en Google son conscientes de lo cómico que resulta de puertas para fuera su política sobre aplicaciones de mensajería que nacen, crecen, se reproducen y mueren, pero es que es difícil de narices saber qué productos coexisten a la vez en su ecosistema.

  • Hablemos de Slack. Slack tiene un menú de acceso rápido que permite saltar rápidamente a otra conversación pulsando ⌘K. Se trata de otro acto de respeto hacia los power users que consideran que levantar las manos del teclado para agarrar el teclado, desplazarlo a la barra lateral, localizar la conversación y hacer clic en ella es una pérdida de tiempo pudiendo teclear ⌘-K-D-A-N-I-Enter. Muy estilo Vim. Es otra forma diferente de mostrar respeto.

    Pero sigue sin ser abrir en ventana nueva. No puedo trabajar con dos conversaciones a la vez. A lo sumo con una conversación abierta en la pantalla principal y un hilo abierto en la barra lateral. No puedo consultar información o un código que me hayan enviado por una conversación mientras la discuto o hablo sobre ella por otra conversación. Todo lo que puedo hacer es utilizar su versión web desde mi navegador web y abrir múltiples instancias de la misma aplicación web, y aun así estoy condenado a tener en todas las instancias la misma barra lateral y el mismo encabezado en la parte superior.

    Ojo, no estoy negando que haya complejidad. Me leí el post que publicaron hace un par de años en su blog de ingeniería. Entiendo que todo es una aplicación React gigante con estados de Redux globales. Aquí sólo me estoy lamentando en voz alta de que hayamos aceptado esta nueva normalidad plagada de aplicaciones no nativas sin considerer este tipo de casos de uso más avanzados.

    Vamos, Slack, aquí te dejo una idea de cómo se podría ver. Haría también el concepto de cómo sería ese pop-up: una ventana nueva donde sólo se vea la barra de título del sistema operativo y luego el área principal con la conversación (sin barras laterales ni superiores), pero se acaba mi descanso de la hora de comer y no tengo tiempo de diseñar esa pantalla.

    Concepto. Este menú no existe. (Ojalá.)
  • elfcat

    elfcat es un visualizador gráfico para inspeccionar las estructuras de datos y bytes internos de un binario ELF. Genera un archivo HTML que tiene metainformación y que con el ratón permite ver cada estructura de datos de forma individual. Está programado en Rust.

    Esto me hubiese venido estupendamente hace un tiempo cuando me dedicaba en mis ratos libres a escribir parsers ELF y ese tipo de cosas como parte del sistema operativo homebrew que estaba construyendo y que sabe Dios cuándo retomaré.

  • I made 56874 calls to explore the telephone network. Here’s what I found. Tan innecesario como interesante, un análisis detallado del tipo de personas o máquinas que responden al otro lado cuando te pones a llamar a números de teléfono masivamente. Quizá mi favorito, como no podría ser de otro modo, es el contestador automático sobre el apocalipsis zombie.

  • Lo pienso seguir haciendo, pero al menos es buena idea saber qué riesgos tiene hacerlo. (Pista: si hay moho que ves, es que hay mucho más moho que no ves, lo cual es tan interesante como asqueroso…)

  • Leyendo el gigante manual de PostgreSQL, por supuesto que existen operadores para hacer un SELECT de campos de un JSON volcado en un campo de este tipo.

    El operador ->> permite extraer el contenido de un campo si se pone entre comillas simples su ruta a su derecha, o de un array si se pone su índice como un número.

    SELECT details->>'date' FROM invoices;
  • Retweet no significa aprobación.

    Me gusta no significa aprobación.

    Enlaces a sitios web externos no implican afiliación.

    Mis opiniones son mías y no las de mis «empleadores» actuales, pasados o futuros.

    A estas alturas me suena a texto machete, pero en el fondo me hace tanta gracia que me lo he puesto en la barra lateral junto al resto de parrafazos, porque estoy dejando últimamente la barra lateral muy tipo «bloguea como si estuvieses en 2005».