Weblogs Código

Variable not found

Enlaces interesantes 318

abril 25, 2018 06:06

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

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

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

Variable not found

Generar redirecciones HTTP 307 y 308 en ASP.NET Core MVC

abril 24, 2018 06:55

ASP.NET Core MVCHace unos días hablábamos de las, a veces desconocidas, redirecciones HTTP 303, 307 y 308, y comentábamos el por qué de su existencia y los escenarios en los que debían ser utilizadas si queremos seguir de forma más estricta el protocolo HTTP a la hora de implementar APIs o aplicaciones.

Pues bien, al hilo de esto, en este post veremos muy rápidamente cómo podemos implementar estas redirecciones en ASP.NET Core MVC que, como veréis, nos lo pone bastante fácil.

En ASP.NET Core MVC, las redirecciones se implementan básicamente retornando resultados de tipos como RedirectResult, LocalRedirectResult, RedirectToActionResult o RedirectToRouteResult. El fin de todos ellos es el mismo: retornar al lado cliente una respuesta con el código HTTP 3xx indicando en el encabezado location la dirección del recurso donde se encuentra el recurso solicitado.

Normalmente no instanciaremos estas clases directamente, sino que utilizaremos los atajos proporcionados por la clase Controller, como en el siguiente ejemplo:
public class RedirectionTestController: Controller
{
public IActionResult Temporal()
{
// Returns HTTP 302 pointing to /redirectiontest/ok
return RedirectToAction("Ok");
}

public IActionResult Ok()
{
return Content("Ok!");
}
}
En la clase Controller existen multitud de métodos destinados a facilitar la construcción de retornos de los distintos tipos redirección, que utilizaremos dependiendo de cómo queramos indicar el destino de las mismas. Por ejemplo:
  • El método Redirect() permite generar redirecciones hacia URL arbitrarias.
  • RedirectToAction() nos permitirá generar redirecciones hacia controladores/acciones de nuestra aplicación.
  • RedirectToRoute() generará redirecciones partiendo de los parámetros de ruta especificados.
  • RedirectToPage() genera redirecciones hacia páginas Razor.
  • LocalRedirect() permite generar redirecciones hacia recursos locales, pertenecientes al mismo sitio web.
Por defecto estas redirecciones serán transitorias (HTTP 302), aunque cada uno de estos métodos presenta una variante, cuya denominación acaba en “Permanent”, que permite generar redirecciones permanentes (HTTP 301), como en el siguiente ejemplo:
public class RedirectionTestController: Controller
{
...
public IActionResult Permanent()
{
// Returns a permanent redirection (HTTP 301) to /redirectiontest/ok
return RedirectToActionPermanent("Ok");
}
}
Petición POST con redirección HTTP 307De esta forma, mediente llamadas a métodos como RedirectToActionPermanent(), RedirectToRoutePermanent() o RedirectToPagePermanent() podremos crear objetos IActionResult que, al ejecutarse, generarán redirecciones permanentes.

Pues bien, observad que hasta ahora sólo hemos hablado de redirecciones HTTP 301 y 302, que son las más habituales, pero ASP.NET Core MVC también nos ofrece facilidades para implementar los códigos 307 y 308.

Como sabemos, lo que estos dos tipos de redirección aportan sobre los conocidos HTTP 301 y 302 es, básicamente, que con ellos indicamos explícitamente al agente de usuario que la petición a la dirección indicada en el encabezado location debe ejecutarse respetando el verbo original.

Por ello, si observamos las ayudas e intellisense cuando vamos a retornar una redirección observaremos que, por cada uno de los métodos de generación de redirecciones que hemos comentado antes, existe un método adicional cuyo nombre finaliza por “PreserveMethod”.

Por ejemplo, en el siguiente bloque de código utilizamos estos métodos para generar redirecciones a acciones con códigos 307 y 308:
public IActionResult TemporalPreserve() 
{
// HTTP 307, temporal redirect preserving method
return RedirectToActionPreserveMethod("Ok");
}
public IActionResult PermanentPreserve()
{
// HTTP 308, permanent redirect preserving method
return RedirectToActionPermanentPreserveMethod("Ok"); //
}
Y de nuevo, tendremos este sufijo en prácticamente todos los métodos que generan resultados de redirección, ya sean transitorios o permanentes: RedirectPreserveMethod(), RedirectToRoutePreserveMethod(), LocalRedirectPermanentPreserveMethod(), etc., por lo que siempre tendremos a mano la posibilidad de optar por unos códigos de retorno u otros y ser más explícitos en las respuestas.

Hey, ¿y los métodos y clases para generar redirecciones HTTP 303?

Pues me temo que se quedaron en el camino :(

De momento no existen formas directas de generar este tipo de redirecciones con las herramientas proporcionadas de serie en el framework. Supongo que el motivo se deberá a que se entiende que su uso no será frecuente y, supongo también, para no “ensuciar” la clase Controller con demasiadas variantes de métodos de generación de redirecciones.

Pero bueno, esto no quiere decir que sea difícil conseguirlo: obviamente nada nos impide retornar un StatusCode(303) o bien un  crear nuestro propio IActionResult personalizado para este tipo de redirecciones, algo que no se antoja demasiado complicado.

Venga, os lo dejo como deberes ;)

Publicado en Variable not found.

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

Poesía Binaria

Configurar Postfix para enviar correo a través de otro servidor SMTP [Gmail, sendgrid, mailgun y más]

abril 23, 2018 08:16

Es una configuración muy común para los servidores de correo saliente. Sirve, por ejemplo para cuando queremos que los correos que enviemos desde nuestro ordenador se hagan a través de otro servidor SMTP, cuando hemos contratado un servicio externo para enviar correo para nuestra empresa, hacer redirecciones temporales o tener un servidor intermedio a la hora de recibir correos (por ejemplo, si queremos aplicar filtros antes de enviar los correos).

Por eso en el título de la entrada está Gmail. Podemos configurar un pequeño servidor de pruebas, o incluso en nuestra máquina local, para que los correos que se envíen con sendmail, o que nos envíen a nuestra máquina, se hagan a través de nuestra cuenta de Gmail (debemos activar el acceso por SMTP, y tendremos que tener cuidado de escuchar solo en localhost o poner reglas de firewall para que no todo el mundo pueda acceder a nuestro puerto de envío de correo). También están los casos de sendgris y mailgun, servicios que podemos utilizar para enviar correos desde un dominio determinado. Estos servicios están bien si desde nuestro servidor se envía un gran volumen de correo (existen muchos más servicios similares).

La otra opción es enviar un correo a través de nuestro propio servidor. Esto puede ser útil en caso de tener varios servidores de envío. Podemos hacer que varios servidores reciban correo y lo envíen a un servidor central. Si uno de los servidores pequeños ve que el central está muy saturado, puede esperar y entregarlo dentro de un rato. También es muy útil la creación de filtros para una empresa, es decir, la empresa tiene contratado el servicio de envío de correo a través de un proveedor externo, pero nosotros queremos tener algo de control como aplicar filtros, revisar virus, o eliminación de metadatos, o que debamos saber si hablan mal de nosotros, que para eso somos administradores de sistemas, aunque no te diré cómo, pero no es muy difícil.

Iré explicando el proceso desde el principio, por lo que si ya has instalado postfix o tienes cierta experiencia con él, podrás saltarte algunas partes. No profundizaré mucho en la configuración de Postfix porque es un mundo, solo en lo que nos atañe para esta tarea.

Instalación de Postfix

Para estos ejemplos he utilizado un equipo con Ubuntu Server 16.04, por lo que vais a ver cosas como apt para instalar los programas. No será muy diferente a hacer todo esto en una Debian, o cualquier otra distribución basada en Ubuntu o Debian. Puede que para otras distribuciones difiera un poco, pero la idea básica es la misma. Lo primero, es instalar Postfix:

sudo apt install libsasl2-modules ca-certificates postfix

Tras eso, veremos una ventana como la siguiente. Aquí podemos elegir configurarlo como Satélite. De hecho, nuestro servidor no hará el envío de correo de forma directa, sino a través de otra máquina.
Postfix como Satélite

La siguiente pregunta del instalador, es el nombre del host. Éste debe ser un FQDN, vamos un nombre que identifique la máquina y que su DNS resuelva. De forma que si desde otra máquina hacemos ping o telnet, la máquina la encontremos y sea esta máquina a la que estamos accediendo:
Elegir dominio de Postfix

El siguiente paso, será especificar el servidor a través del cual vamos a enviar los correos realmente:
Postfix, indicando el servidor Relay

En este punto debemos especificar el host y el puerto. Os pongo aquí varios hosts y puertos de algunos servicios comunes:

  • Gmail. smtp.gmail.com:25
  • Sendgrid. smtp.sendgrid.net:587
  • Mandrill. smtp.mandrillapp.com:587
  • Sparkpost. smtp.sparkpostmail.com:587
  • Mailgun. smtp.mailgun.org:25

O incluso podemos utilizar cualquier otro servidor de correo de otro proveedor (que hay cientos de servicios gratuitos y de pago para esto). Puede que para algunos de los servicios haya que realizar una activación previa del servicio SMTP, o incluso configurar DNS para poder trabajar correctamente.

Por último, debemos revisar, en el fichero /etc/postfix/main.cf que la línea que menciona myhostname tiene nuestro nombre de host de correo (el FQDN que introdujimos en la segunda pantalla).

Configurar servidores SMTP, usuarios y contraseñas

Cualquier servidor hoy en día, debe tener activada la autentificación de usuarios, de modo que no permita que cualquiera envíe correos desde ese servidor. Era muy habitual en el pasado encontrar servidores abiertos desde los que poder enviar cualquier cosa y, tanto en el mundo fantástico y maravilloso de la piruleta, como en el diseño original del sistema de correo debía ser así. El problema es que mucha gente aprovechaba para hacer cosas malas.

El primer acceso que vamos a configurar es el que va desde el servidor de correo Postfix que estamos montando hasta el servidor de correo a través del cual vamos a enviar los mensajes. Para ello creamos el archivo /etc/postfix/sasl_passwd, en realidad, podemos llamarlo como queramos, aunque es una buena idea colocarlo dentro de /etc/postfix. Dentro del archivo incluimos lo siguiente (con los corchetes y todo):

1
[mail.dominio.com]:puerto usuario:contraseña

En esta línea incluiremos el nombre del servidor desde el que realmente enviaremos los correos (Gmail, sendgrid, mailgun…), el puerto, y el nombre de usuario y contraseña desde el que enviaremos los correos. Si, por ejemplo, estamos utilizando nuestra dirección de Gmail para enviar correo, solo podremos utilizar como remitente nuestra propia dirección, aunque tienen por ahí una opción para poder utilizar más direcciones de correo de Google de esta forma. Otros servicios permiten, a través de una clave única enviar correos desde cualquier dirección de correo dentro de un dominio o subdominio, deberá ser nuestro Postfix el que determine qué un usuario no pueda enviar correo con el e-mail de otro.

Una vez tenemos /etc/postfix/sasl_passwd creado, vamos a crear una tabla de búsqueda (lookup-table) para Postfix de esta forma:

sudo postmap /etc/postfix/sasl_passwd

De esta forma creamos el archivo /etc/postfix/sasl_passwd.db . Lo siguiente será proteger los dos archivos de contraseñas, tanto el que no tiene extensión (que, si queremos lo podemos borrar y no pasa nada, o también custodiarlo en otra máquina o con otro usuario, ya como queramos). Para protegerlo, hacemos que solo root sea capaz de acceder a los archivos:

sudo chown root:root /etc/postfix/sasl_passwd.db /etc/postfix/sasl_passwd
sudo chmod 0600 /etc/postfix/sasl_passwd.db /etc/postfix/sasl_passwd

Aunque, si sois frikis como yo, y no queréis escribir mucho podéis hacer lo siguiente (más info aquí):

sudo chown root:root /etc/postfix/sasl_passwd{,.db}
sudo chmod 0600 /etc/postfix/sasl_passwd{,.db}

Por último, tenemos que configurar nuestro servidor relay editando el archivo /etc/postfix/main.cf , primero añadiendo (o verificando) que el relay host está configurado, es decir, el servidor a través del cual enviamos realmente los correos:

1
relayhost = [mail.dominio.com]:puerto

Podemos poner o buscar esta información cerca de myhostname, mydestination, mynetworks.

Ahora, en el mismo archivo, por ejemplo, al final del archivo, podemos poner un comentario y a continuación las siguientes líneas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Configuración de seguridad para el servidor saliente

# Usas SASL para autentificarnos antes de enviar correos
smtp_sasl_auth_enable = yes

# Utilizar el mapa /etc/postifx/sasl_passwd.db
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd

# No permitr métodos de autentificación anónima
smtp_sasl_security_options = noanonymous

# Utilizar el fichero de certificados raíz del sistema
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

# Utilizar STARTTLS para enviar los correos
smtp_use_tls = yes

Configurar autentificación en nuestro servidor

Nuestro servidor debe identificarse cuando se conecta para mandar correos a través de otra máquina (como vimos, que puede ser Gmail, Sendgrid, Mailgun, etc). Pero claro, puede que queramos que nuestros usuarios también se identifiquen en nuestro servidor cuando se conecten. De esta forma, cualquier persona no podrá enviar e-mails. Es algo que no es fundamental, pero sí recomendado si no queremos que nuestro servidor se transforme en una máquina zombie que envíe correos sin piedad, lo que puede desembocar en que el servidor de envío a través del cual mandamos los mensajes nos termine desactivando el usuario y, si el dominio es nuestro, éste pierda reputación y nos resulte tremendamente difícil que el mundo no se crea que enviamos SPAM.

Para configurar esta seguridad, vamos a crear una pequeña base de datos local con los usuarios permitidos. Seguro que si tenemos miles de usuarios deberíamos tener métodos de autentificación más grandes y distribuidos, pero, por experiencia en la mayoría de los casos, utilizando SASL, como antes, va muy bien.

Instalaremos algunas utilidades, si no las tenemos ya:

sudo apt install libsasl2-2 sasl2-bin

Ahora, debemos asegurarnos de que el demonio saslauthd se ejecuta automáticamente al iniciar el sistema. Para ello, editaremos /etc/default/saslauthd y nos aseguremos de que la línea que comienza por START y por MECHANISM se mantiene así:

1
2
3
4
5
6
# Should saslauthd run automatically on startup? (default:no)
START=yes
.....
.....
# Example MECHANISMS="pam"
MECHANISMS="sasldb"

Tras ello, reiniciaremos el demonio:

sudo service saslauthd restart

Ahora debemos configurar de nuevo algunas cosas en /etc/postfix/main.cf:

1
2
3
4
5
smtpd_sasl_auth_enable = yes
smtpd_sasl_local_domain=
smtpd_recipient_restrictions= permit_sasl_authenticated,
        reject_unauth_destination
smtpd_sasl_security_options=noanonymous

Tras esto, debemos crear (o editar) el archivo /etc/postfix/sasl/smtpd.conf donde especificaremos el tipo de autentificación permitida a la hora de conectarnos e intentar enviar correo a través de Postfix. Dejaremos las siguientes líneas:

1
2
pwcheck_method: saslauthd
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5 NTLM

Por supuesto, podríamos (y deberíamos) eliminar PLAIN sobre todo, para que nadie pueda identificarse con contraseñas en texto plano. Aunque, la práctica me dice que a veces puede que necesitemos activarlo temporalmente. De hecho, es problema de los usuarios si utilizan PLAIN para identificarse, los clientes de correo siempre intentan identificarse con el método más seguro disponible.

Tras todo esto, reiniciamos Postfix:

sudo service postfix restart

Por último, podemos tener un pequeño problema a la hora de identificar usuarios, y es que, Postfix, muchas veces está configurado con un chroot o una jaula. Eso quiere decir que postfix será incapaz de ver archivos fuera de su jaula, como por ejemplo, el socket de identificación de saslauthd, por lo tanto, no podrá identificar correctamente a los usuarios. Para ello, solo tenemos que introducir el directorio /var/run/saslauthd dentro de la jaula, de esta forma:

sudo mount --bind /var/run/saslauthd /var/spool/postfix/var/run/saslauthd

Este comando deberíamos ejecutarlo siempre que se inicie el sistema, podríamos incluir una línea en /etc/fstab o un script de arranque personalizado.

Manejo de usuarios

Si queremos crear o cambiar la contraseña de los usuarios del sistema deberíamos hacer:

sudo saslpasswd2 -c [nombre de usuario]

O también

sudo saslpasswd2 -c [nombre de usuario]@[dominio]

Para eliminar un usuario podremos utilizar -d en lugar de -c y para listar todos los usuarios del sistema tenemos el comando:

sudo sasldblistusers2

Restringir dispositivos de red

Esto depende de dónde se van a enviar los mensajes. Por seguridad es una buena opción restringir los interfaces de red permitidos para cada servicio que instalamos en nuestros servidores, haciendo que los servicios solo escuchen en aquellos dispositivos para los que estén ofreciendo servicio. Así que, en Postfix también podemos decidir esto. En /etc/postfix/main.cf debemos configurar:

1
inet_interfaces = all

Con esta configuración estaremos escuchando en todos los dispositivos de red que tenga la máquina. Si lo hacemos así, debemos restringir el acceso a puertos (si queremos) a otros usuarios. Podríamos utilizar, en lugar de all, la opción loopback-only si solo escuchamos en localhost. Esto puede ser útil si tenemos un servicio web instalado en este servidor que necesite enviar correos. También podemos poner las direcciones IP o nombres de host desde los que escuchamos separados por comas (si son varios).

Si cambiamos algo aquí tendremos que reiniciar el servidor postfix.

Enviando un mensaje

Es momento de enviar un mensaje de prueba. Tenemos muchas opciones. Podemos utilizar sendmail de la siguiente forma:

/usr/sbin/sendmail correo@destinatario.com
From: direccion@origen
To: correo@destinatario.com
Subject: Asunto del mensaje
Texto del mensaje, que puede tener.
muchas líneas y ser muy largo.

Tras ello, podemos pulsar Control+D y terminaremos de escribir el mensaje. Supuestamente debe haberse enviado si todo está correcto.

También podemos tirar de funciones de varios lenguajes de programación como mail() para PHP. O de scripts como gemail.sh con el que se podrán enviar archivos adjuntos de forma fácil. Por supuesto, también podemos configurar nuestro cliente de correo favorito para enviarlos.

Logs y depuración

Y, como todo en este mundo informático puede fallar, es importante tener a mano los logs. En estos logs podremos ver mensajes que nos dirán qué está pasando en cada momento. Tendremos dos /var/log/mail.log que nos dirá qué está pasando con cada mensaje, IDs de correos, mensajes que entran en cola o no, errores al mandar un mensaje a otro servidor o al recibirlo, etc. También podemos encontrar errores en /var/log/mail.err sobre todo en lo relativo a fallos en el demonio, conexiones que no se han podido establecer o intentos de hacer cosas raras como por ejemplo poner muchas líneas de encabezados en un mensaje (podemos tener 20, 30 líneas, pero cuando alguien intenta meter 100 o 200 líneas, puede que esté intentando hacer algo malo, o que no esté enviando bien el mensaje).

Foto principal: Designed by Freepik

The post Configurar Postfix para enviar correo a través de otro servidor SMTP [Gmail, sendgrid, mailgun y más] appeared first on Poesía Binaria.

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

Koalite

Lo mínimo a entender sobre transacciones

abril 23, 2018 05:06

Cualquiera que se dedice a esto de desarrollar software conoce el concepto de transacción. O al menos eso quiero pensar. Otra cosa es ya hasta qué punto seamos conscientes de lo que realmente implica utilizar una transacción, cuándo y por qué debe usarse, los distintos tipos que existen, etc.

Como siempre, no pretendo entrar en detalles demasiado complejos sobre el maravilloso mundo de las transacciones, entre otras cosas porque tampoco soy un experto en la materia, pero sí me gustaría dar algunas ideas generales sobre el tema.

La transacción como unidad

Una de las características más conocidas de las transacciones es su atomicidad. Entenderlas como un todo o nada. Es el clásico ejemplo de la transferencia entre cuentas bancarias: si decremento el saldo de una cuenta, tiene que incrementarse en otra; la operación no puede quedar a medias porque desaparecería dinero (o aparecería, según el orden en que hagas las operaciones). Para muchos ésta es la esencia de una transacción.

Esto se corresponde con la A de ACID y era algo que ofrecen prácticamente todas las bases de datos relacionales. Con las llegada de las bases de datos NoSQL y sus tradeoffs para ganar escalabilidad, rendimiento y esas cosas tan necesarias para algunos, esto ya no está tan garantizado.

Si bien la atomicidad de la transacción es una característica básica y fundamental de la misma, no podemos limitarnos a pensar en las transacciones desde ese punto de vista. Necesitamos tener en cuenta también la corrección de lo que estamos haciendo y, eso nos lleva a pensar en la interrelación entre transacciones que se ejecutan de forma concurrente.

Los problemas de la concurrencia

Es muy raro (cada vez más) que una aplicación no ejecute de forma concurrente operaciones sobre una base de datos. No hace falta que la aplicación soporte usuarios concurrentes, incluso siendo una aplicación “monousuario”, es frecuente que además de las operaciones iniciadas por el usuario se ejecuten procesos en segundo plano que también toquen la base de datos (y no usar una base de datos tampoco resuelve el problema).

Al llegar a estos escenarios hay que empezar a pensar qué cosas nos pueden ocurrir cuando dos transacciones se ejecutan a la vez y qué pasa con los datos que están siendo modificados mientras se ejecuta una transacción. Esto puede dar lugar a tres problemas fundamentales:

Lecturas sucias

Las lecturas sucias se producen cuando una transacción puede leer datos que están siendo escritos por otra transacción antes de que ésta realice el commit. Un ejemplo de esto sería cuando la transacción 1 modifica un registro en la tabla Customer, la transacción 2 lo lee con las modificaciones, y luego la transacción 1 hacer un rollback, descartando las modificaciones. En ese caso, la transacción 2 habrá estado operando en base a unos datos que realmente no son válidos.

Lecturas no repetibles

Podríamos solucionar el problema de las lecturas sucias haciendo que las modificaciones realizadas por una transacción no sean visibles al resto hasta que se realice el commit.

Aun así, podríamos encontrarnos con otro problema: el de las lecturas no repetibles, que se producen cuando una transacción modifica datos que han sido leídos por otra. Por ejemplo, si la transacción 1 lee el registro Customer {Id = 1, Name = 'Paco'}, la transacción 2 modifica ese registro y realiza el commit, y la transacción 1 lo vuelve a leer, los datos que obtendrá son diferentes de los que obtuvo al principio.

Lecturas fantasma

Nuevamente esto tiene solución si hacemos que una transacción no pueda modificar datos que hayan sido leídos por otra, pero todavía nos quedaría otro problema por resolver: las lecturas fantasma.

Una lectura fantasma se produce cuando una transacción lanza una misma consulta dos veces y obtiene distinto conjunto de resultados. Si en las lecturas no repetibles podían cambiar los valores obtenidos para una fila, en una lectura fantasma podrían aparecer o desaparecer filas. El ejemplo sería si la transacción 1 consulta los clientes de Madrid y obtiene 10 filas, a continuación la transacción 2 inserta un nuevo cliente también de Madrid y hace su commit, y la transacción 1 vuelve a consultar clientes de Madrid obteniendo 11 filas.

Para solucionar esto no nos basta con proteger los datos ya leídos por cada transacción (como hacíamos para evitar las lecturas no repetibles), sino que necesitamos proteger los datos que potencialmente podrían afectar a nuevas consultas de una transacción (lo que muchas veces implica controlar tablas completas y no sólo filas individuales).

Aislamiento de transacciones

Las bases de datos ofrecen lo que se conoce como niveles de aislamiento para poder definir el tipo de garantías que esperamos durante la ejecución de una transacción. Se configuran a nivel de transacción (cada transacción puede tener un nivel de aislamiento distinto), y con ellos podemos controlar cuáles de los problemas estamos dispuestos a evitar, y cuáles podemos asumir. Cada base de datos puede implementar estos niveles de aislamiento de diferente forma, por lo que las características que vamos a ver pueden variar (especialmente en cuanto al coste a pagar por usar cada nivel), pero en general suelen ser bastante parecidas.

Read uncommitted

No hay ninguna garantía. Las transacciones no están aisladas unas de otras, por lo que se pueden producir todos los problemas que hemos visto anteriormente.

Read committed

Se garantiza que sólo se leen datos escritos por transacciones que hayan realizado su commit. De esta forma evitamos lecturas sucias. Claro, que esto no es gratis. Para conseguirlo, si intentamos leer un registro que ha sido modificado por otra transacción, nos quedaremos bloqueados hasta que esa transacción termine, ya sea con un commit o con un rollback.

Todavía se pueden producir lecturas no repetibles y lecturas fantasmas, porque no se evita que otra transacción modifique datos que hemos leído.

Repeatable read

Además de las garantías de Read committed, asegura que cada vez que leamos un registro nos devolverá siempre la misma información. Es decir, evitamos lecturas sucias y lecturas no repetibles. El precio a pagar por este aumento de garantías, además del que teníamos en Read committed, es que cuando una transacción lee un registro, el registro queda bloqueado para el resto de transacciones que intenten modificarlo, por lo que tendrán que esperar a que acabe la primera transacción antes de poder hacerlo.

Aun así, seguimos sin protegernos de lecturas fantasmas, ya que el bloqueo se realiza a nivel de registro, y podría ocurrir que otra transacción añada registros a una tabla que hemos leído.

Serializable

El máximo nivel de aislamiento (al menos de los definidos en SQL-92). Nos garantiza que no se produce ninguno de los problemas descritos anteriormente, es decir, no tendremos lecturas sucias, nuestras lecturas serán repetibles, y no habrá lecturas fantasmas. Para lograrlo esta vez el precio que pagamos es mayor, y además de los bloqueos que padecemos e inducimos con los niveles anteriores, necesitamos bloquear los rangos de registros que estamos leyendo. En el peor de los casos, eso implicaría bloquear enteras las tablas leídas por la transacción para evitar que otras puedan insertar nuevos registros.

Existen otros niveles de aislamiento pero son menos habituales. Si alguien tiene curiosidad (es un tema entretenido), puede leer un poco sobre control de concurrencia multiversión y aislamiento basado en instantáneas como alternativa eficiente para mantener la serializabilidad transaccional.

Transacciones en el mundo real

Todo esto puede parecer que está muy bien como culturilla general, pero que si no eres DBA te da más o menos lo mismo. Total, si estás desarrollando un API Web que usa un (micro)ORM para acceder a la base de datos, el mundo de las transacciones queda muy lejos y lo único que ves es por ahí una llamada a dbContext.SaveChangesAsync o similar. Lamentablemente, es posible que tu framework o tu modelo pueda ignorar la persistencia, pero tú no.

A la hora de definir agregados de dominio (o lo que sea que uses para encapsular operaciones contra tus datos) necesitas pensar en las garantías que le vas a pedir a la base de datos y en los problemas de concurrencia que puedes encontrar.

Por poner un ejemplo muy básico, suponte que tienes una clase Order que representa un pedido y contiene OrderLines con las líneas. Al persistir en base de datos (vía ORM o manual), probablemente tengas dos tablas, Orders y OrderLines, y probablemente en la tabla Orders haya información que se calcule a partir de sus líneas, por ejemplo el total del pedido.

Con una visión alegre y despreocupada del mundo transaccional podrías pensar que con usar una transacción que garantice la atomicidad, es decir, que si se guardan las líneas se guarda la cabecera, ya tienes todo resuelto, pero no es así. El nivel de aislamiento por defecto de transacciones suele ser Read committed, y no es suficiente en este escenario si dos usuarios modifican a la vez un pedido.

Imagina que dos transacciones, T1 y T2, intentan agregar a la vez una línea a un mismo pedido:

  1. T1: Carga el pedido, añade la línea L1 en memoria y se dispone a guardar.
  2. T2: Carga el pedido (sin ver la línea añadida por T1 porque no hay sido committed), añade otra línea L2 y se dispone a guardar.
  3. T1: Inserta la línea L1 en la tabla OrderLines.
  4. T2: Inserta la línea L2 en la tabla OrderLines.
  5. T1: Actualiza el pedido en la tabla Order, incluyendo en el total del pedido el importe de la línea L1, pero no el de la línea L2, ya que no sabe nada de ella.
  6. T2: Actualiza el pedido en la tabla Order, incluyendo en el total sólo su línea.

¿Ves el problema? En la base de datos el pedido contendrá las dos líneas (L1 y L2), pero su total no incluirá el importe de ambas, sólo el de L2 porque cuando T2 actualizó la base de datos no sabía nada de L1 y no la incluyó en el cálculo del total.

Éste es un caso muy simple, pero nos obliga a pensar en las garantías con que ejecutamos cada transacción. Podríamos aumentar el nivel de aislamiento, que en este caso sería necesario que fuera Serializable, o buscar alternativas como hacer un bloqueo a nivel de registro o utilizar técnicas de concurrencia optimista, pero lo que está claro es que no debemos ignorar el problema.

Todas estas opciones tienen sus inconvenientes (lo digo por si estabas pensando que la solución era usar siempre el máximo nivel de aislamiento) y hay que buscar un equilibrio entre alcanzar la solución correcta, tener el código lo más simple posible, y mantener un rendimiento razonable evitando bloqueos e interbloqueos. Lo siento, pero la concurrencia siempre es complicada.

Quizá se pueda pensar que esto es un problema de las bases de datos relacionales porque obligan a guardar información en varias tablas y por eso hay que gestionar inconsistencias, pero el mundo de las bases de datos NoSQL también tiene su cuota de problemas de los que hay que ser consciente.

Posts relacionados:

  1. Eventos de Dominio y Transacciones

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

Blog Bitix

La sentencia try-with-resources de Java

abril 21, 2018 12:30

Java

Una de las novedades que incorporó Java 7 es la sentencia try-with-resources con el objetivo de cerrar los recursos de forma automática en la sentencia try-catch-finally y hacer más simple el código. Aquellas varaibles cuyas clases implementan la interfaz AutoCloseable pueden declararse en el bloque de inicialización de la sentencia try-with-resources y sus métodos close() serán llamados después del bloque finally como si su código estuviese de forma explícita.

Un ejemplo de código que lee una línea de un fichero usando la sentencia try-with-resources es la siguiente de Java 7. Como se observa no es necesario llamar de forma explícita al método close para liberar los recursos de la instancia de la clase BufferedReader.

Anteriormente a Java 7 esto se debía hacer de la siguiente manera con unas pocas lineas más de código algo menos legibles.

El código es similar pero no es equivalente. Observesé que require declarar la variable br fuera del ámbito de la sentencia try-catch-finally donde se usa. Además, si se produce una excepción en el bloque try y posteriormente en el bloque finally en Java 6 la excepción del bloque try se enmascara y la que se lanza es la del bloque finally.

La excepción que se lanza en el bloque try y usando el método Throwable.addSuppressed() que se añadió en la API en Java 7 junto con el método Throwable.getSuppressed() se obtienen las excepciones enmascaradas o suprimidas en la sentencia try-with-resources. El orden de ejecución de los bloques de una sentencia try-with-resources es el indicado en los números emitidos con el método println.

La mayoría de clases relacionadas con entrada y salida implementan la interfaz AutoCloseable como las relacionadas con el sistema de ficheros y flujos de red como InputStream, también las relacionadas con la conexión de base de datos mediante JDBC con Connection.

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

Blog Bitix

La controversia sobre las excepciones checked y uncheked

abril 20, 2018 03:00

Java

Las excepciones son una forma de gestionar las condiciones de error que se dan en los programas. En el lenguaje C se utiliza el valor de retorno de la función para determinar la condición de error que se ha producido, el problema es que comprobar el valor de retorno puede ignorarse y la gestión de errores está mezclada con la tarea del programa.

El lenguaje Java utiliza un mecanismo de excepciones, las excepciones son objetos que se lanzan cuando se produce una condición de error. Todas las excepciones en Java heredan de Throwable subdividiéndose en Error y Exception, las primeras son condiciones de error del sistema y las segundas condiciones de error del programa. A su vez las Exception pueden ser checked si heredan de esta y son aquellas que el compilador fuerza a que sean capturadas no pudiendo ignorarse, han de capturarse en una construcción try catch o declarar que el método puede lanzar la excepción no capturada. Las excepciones uncheked heredan de RuntimeException que heredan a su vez de Exception pero tienen la particularidad de que no es necesario capturarlas ni declararlas como que se pueden lanzar debido a que se consideran condiciones de error en la programación como un acceso a un array fuera de rango que produce un ArrayIndexOutOfBounds, el conocido NullPointerException cuando se utiliza una referencia nula, otro es ArithmeticException que se produce al dividir por 0.

Algunas ventajas de las excepciones son:

  • Separar el código que gestiona los errores del código con el caso principal del programa.
  • Propagar errores hacia arriba en la pila de llamadas.
  • Agrupar y diferenciar entre diferentes tipos de errores.

Hay una cierta polémica sobre si las excepciones checked son una buena idea. Entre los motivos que se alegan en contra de su uso están que cambiar la firma de un método añadiendo una nueva excepción como lanzable hace que el código que usase ese método podría ocasionar errores de compilación y que hace necesario el tratarla o declararla en la cadena de métodos hasta que sea tratada. Otro motivo es que a mayor nivel en la jerarquía de llamada en los métodos se necesitarán manejar una lista amplia de excepciones.

En el lado contrario las excepciones se consideran que son buenas porque conocer las condiciones de error o excepción que puede lanzar el método forma parte del contrato del método y es necesario para realizar un correcto manejo de errores. Las excepciones checked pueden parecer un incordio pero son necesarias para hacer un correcto manejo de errores y evitar que el programa falle por no tratar las condiciones de error de las que advertirían. Por otro lado no deberían silenciarse con un bloque catch vacío sin una buena razón. En las excepciones checked el compilador es capaz de advertir si alguna excepción no ha sido capturada o lanzada.

Como regla general las excepciones checked se usan cuando el programa es capaz de recuperarse del error y tratarlo adecuadamente, las uncheked cuando se trata de un error de programación o no se puede hacer nada para recuperarse.

En el siguiente código se observa como capturar, lanzar y declarar excepciones en las firmas de los métodos en Java en una construcción try catch finally.

En el aparatado referncia incluyo unos buenos enlaces que amplian y detallan más apropiadamente la controversia sobre las excepciones checked y unchecked.

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

Blog Bitix

La controversia sobre las excepciones checked y unchecked

abril 20, 2018 03:00

Java

Las excepciones son una forma de gestionar las condiciones de error que se dan en los programas. En el lenguaje C se utiliza el valor de retorno de la función para determinar la condición de error que se ha producido, el problema es que comprobar el valor de retorno puede ignorarse y la gestión de errores está mezclada con la tarea del programa.

El lenguaje Java utiliza un mecanismo de excepciones, las excepciones son objetos que se lanzan cuando se produce una condición de error. Todas las excepciones en Java heredan de Throwable subdividiéndose en Error y Exception, las primeras son condiciones de error del sistema y las segundas condiciones de error del programa. A su vez las Exception pueden ser checked si heredan de esta y son aquellas que el compilador fuerza a que sean capturadas no pudiendo ignorarse, han de capturarse en una construcción try catch o declarar que el método puede lanzar la excepción no capturada. Las excepciones uncheked heredan de RuntimeException que heredan a su vez de Exception pero tienen la particularidad de que no es necesario capturarlas ni declararlas como que se pueden lanzar debido a que se consideran condiciones de error en la programación como un acceso a un array fuera de rango que produce un ArrayIndexOutOfBounds, el conocido NullPointerException cuando se utiliza una referencia nula, otro es ArithmeticException que se produce al dividir por 0.

Algunas ventajas de las excepciones son:

  • Separar el código que gestiona los errores del código con el caso principal del programa.
  • Propagar errores hacia arriba en la pila de llamadas.
  • Agrupar y diferenciar entre diferentes tipos de errores.

Hay una cierta polémica sobre si las excepciones checked son una buena idea. Entre los motivos que se alegan en contra de su uso están que cambiar la firma de un método añadiendo una nueva excepción como lanzable hace que el código que usase ese método podría ocasionar errores de compilación y que hace necesario el tratarla o declararla en la cadena de métodos hasta que sea tratada. Otro motivo es que a mayor nivel en la jerarquía de llamada en los métodos se necesitarán manejar una lista amplia de excepciones.

En el lado contrario las excepciones se consideran que son buenas porque conocer las condiciones de error o excepción que puede lanzar el método forma parte del contrato del método y es necesario para realizar un correcto manejo de errores. Las excepciones checked pueden parecer un incordio pero son necesarias para hacer un correcto manejo de errores y evitar que el programa falle por no tratar las condiciones de error de las que advertirían. Por otro lado no deberían silenciarse con un bloque catch vacío sin una buena razón. En las excepciones checked el compilador es capaz de advertir si alguna excepción no ha sido capturada o lanzada.

Como regla general las excepciones checked se usan cuando el programa es capaz de recuperarse del error y tratarlo adecuadamente, las uncheked cuando se trata de un error de programación o no se puede hacer nada para recuperarse.

En el siguiente código se observa como capturar, lanzar y declarar excepciones en las firmas de los métodos en Java en una construcción try catch finally.

En el aparatado referncia incluyo unos buenos enlaces que amplian y detallan más apropiadamente la controversia sobre las excepciones checked y unchecked.

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

Adrianistán

Natural Language Understanding con Snips NLU en Python

abril 17, 2018 12:57

Uno de los campos más importantes de la inteligencia artificial es el del tratamiento del lenguaje natural. Ya en 1955, en el primer evento sobre Inteligencia Artificial, y promovido entre otros por John McCarthy (creador de Lisp) y Claude Shannon (padre de la teoría de la información y promotor del uso del álgebra de boole para la electrónica), estas cuestiones entraron en el listado de temas.

En aquella época se era bastante optimista con las posibilidades de describir el lenguaje natural (inglés, español, …) de forma precisa con un conjunto de reglas de forma similar a las matemáticas. Luego se comprobó que esto no era una tarea tan sencilla.

Hoy día, es un campo donde se avanza mucho todos los días, aprovechando las técnicas de machine learning combinadas con heurísticas propias de cada lenguaje.

Natural Language Understanding nos permite saber qué quiere decir una frase que pronuncia o escribe un usuario. Existen diversos servicios que provee de esta funcionalidad: IBM Watson, Microsoft LUIS y también existe software libre, como Snips NLU.

Snips NLU es una librería hecha en Rust y con interfaz en Python que funciona analizando el texto con datos entrenados gracias a machine learning y da como resultado un intent, o significado de la frase y el valor de los slots, que son variables dentro de la frase.

¿Qué tiempo hará mañana en Molina de Aragón?

Y Snips NLU nos devuelve:

  • intent: obtenerTiempo
  • slots:
    • cuando: mañana
    • donde: Molina de Aragón

Pero para esto, antes hay que hacer un par de cosas.

Instalar Snips NLU

Instala Snips NLU con Pipenv (recomendado) o Pip:

pipenv install snips-nlu

pip install snips-nlu

 

Datos de entrenamiento

En primer lugar vamos a crear un listado de frases que todas expresen la intención de obtener el tiempo y lo guardamos en un fichero llamado obtenerTiempo.txt. Así definimos un intent:

¿Qué tiempo hará [cuando:snips/time](mañana) en [donde:localidad](Molina de Aragón)?
¿Qué tal hará [cuando:snips/time](pasado mañana) en [donde:localidad](Ponferrada)?
¿Qué temperatura habrá [cuando:snips/time](mañana) en [donde:localidad](Frías)?

La sintaxis es muy sencilla. Cada frase en una línea. Cuando una palabra forme parte de un slot, se usa la sintaxis [NOMBRE SLOT:TIPO](texto). En el caso de [donde:localidad](Frías). Donde es el nombre del slot, localidad es el tipo de dato que va y Frías es el texto original de la frase. En el caso del slot cuando, hemos configurado el tipo como snips/time que es uno de los predefinidos por Snips NLU.

Creamos también un fichero llamado localidad.txt, con los posibles valores que puede tener localidad. Esto no quiere decir que no capture valores fuera de esta lista, como veremos después, pero tienen prioridad si hubiese más tipos. También se puede configurar a que no admita otros valores, pero no lo vamos a ver aquí.

Burgos
Valladolid
Peñaranda de Bracamonte
Soria
Almazán
Íscar
Portillo
Toro
Fermoselle
Sahagún
Hervás
Oña
Saldaña
Sabiñánigo
Jaca

Ahora generamos un fichero JSON listo para ser entrenado con el comando generate-dataset.

generate-dataset --language es --intent-files obtenerTiempo.txt --entity-files localidad.txt > dataset.json

Entrenamiento

Ya estamos listos para el entrenamiento. Creamos un fichero Python como este y lo ejecutamos:

import io
import json
from snips_nlu import load_resources, SnipsNLUEngine

load_resources("es")

with io.open("dataset.json") as f:
    dataset = json.load(f)

engine = SnipsNLUEngine()

engine.fit(dataset)

engine_json = json.dumps(engine.to_dict())
with io.open("trained.json",mode="w") as f:
    f.write(engine_json)

El entrenamiento se produce en fit, y esta tarea puede tardar dependiendo del número de datos que metamos. Una vez finalizado, generama un fichero trained.json con el entrenamiento ya realizado.

Hacer preguntas

Ha llegado el momento de hacer preguntas, cargando el fichero de los datos entrenados.

import io
import json
from snips_nlu import SnipsNLUEngine, load_resources

load_resources("es")

with io.open("trained.json") as f:
    engine_dict = json.load(f)

engine = SnipsNLUEngine.from_dict(engine_dict)

phrase = input("Pregunta: ")

r = engine.parse(phrase)
print(json.dumps(r, indent=2))

Ahora sería tarea del programador usar el valor del intent y de los slots para dar una respuesta inteligente.

Te animo a que te descargues el proyecto o lo hagas en casa e intentes hacerle preguntas con datos retorcidos a ver qué pasa y si guarda en los slots el valor correcto.

La entrada Natural Language Understanding con Snips NLU en Python se publicó primero en Adrianistán.

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

Variable not found

¿Aún usas ToString() para obtener el nombre de los elementos de un enum?

abril 17, 2018 11:50

C#Va un truquillo muy rápido que, aparte de al que os habla, quizás a alguno más os provoque un momento “ahh, pues claro, cómo no se me había ocurrido antes” ;)

Y es que el problema con las costumbres es que son difíciles de quitar; por ejemplo, probablemente muchos estamos acostumbrados a obtener el nombre de los miembros de un enum de la siguiente forma:
public enum Color { Red, Green, Blue }
static void Main(string[] args)
{
Console.WriteLine(Color.Red.ToString());
// Output: Red
}
A priori es correcto: llevamos años haciéndolo así y no nos ha ido mal del todo. Bueno, o sí, pero probablemente no por este motivo ;)

Aun a sabiendas de que el operador nameof existe desde hace ya bastante tiempo y habiéndolo utilizado en muchas ocasiones, a veces la costumbre hace que no nos planteemos otros casos de uso, como con los enum que veíamos arriba.

El siguiente código, totalmente equivalente al anterior, muestra cómo podemos utilizar este operador para obtener el nombre del miembro:
public enum Color { Red, Green, Blue }
static void Main(string[] args)
{
Console.WriteLine(nameof(Color.Red));
// Output: Red
}
Fijaos que, además de que sigue siendo tipado fuerte, acabamos de conseguir un microbeneficio adicional: nos hemos llevado a tiempo de compilación (porque, recordad, nameof se resuelve en compilación) la obtención del nombre, por lo que el rendimiento será mejor que la llamada a ToString(), que es resuelta en tiempo ejecución.

Publicado en Variable not found.

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

xailer.info

Lanzamiento de Xailer 5.1.0

abril 16, 2018 05:45

Estimados usuarios de Xailer,

Hoy publicamos una nueva actualización de Xailer, la versión 5.1.0 que básicamente corrige todos los errores encontrados hasta la fecha y se prepara internamente para el salto inminente a Xailer 6 que incorporará la última versión de Harbour, MinGW, SQLite, MySQL, MariaDB y Scintilla.  Más información en los siguientes enlaces:

https://www.xailer.com/?lonuevo
http://www2.xailer.com/download/?es&file=1

Un cordial saludo
[El equipo de Xailer]

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

Variable not found

Enlaces interesantes 317

abril 16, 2018 06:55

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

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

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

Blog Bitix

Prompt de la terminal personalizado en carpetas de git con el intérprete Bash

abril 13, 2018 10:30

GNU
Linux

Uno de los sistemas de control de versiones más utilizado es Git. Bash es el intérprete de comandos por defecto en la mayoría de distribuciones GNU/Linux. A la hora de trabajar en la terminal y estando como directorio actual en un directorio que está bajo el control de versiones de git bash por defecto no muestra ninguna información del estado de los archivos en su repositorio en el símbolo del sistema o prompt.

El intérprete Zsh y Oh-My-Zsh ofrece mediante sus temas soporte para los repositorios de git modificando el símbolo del sistema para mostrar más información acerca del estado. En Bash también es posible añadir soporte para que muestre información como la rama actual en la que se está trabajando, si hay archivos modificados o no añadidos al control de versiones, si hay archivos en el stash y una comparación entre la rama actual y la del origen o upstream.

Prompt de la terminal por defecto y en carpeta de git
  • * la presencia de este caracter indica que hay cambios en alguno de los archivos bajo el control de versiones.
  • + indica que hay archivos añadidos al stash
  • = indica que la rama está en el mismo estado que en upstream, en su lugar puede mostrarse el caracter > para indicar que la rama en local está por delante de la rama en remoto o mostrarse < para lo contrario.

El script necesario para añadir el soporte a repositorios git en Bash es git-prompt.sh. Una vez descargado su funcionalidad se personaliza con varias variables de entorno tal y como está documentado en el comentario al inicio de este script que se añaden en el archivo de perfil del usuario de inicio .bashrc. Añadidas unas variables de entorno que empiezan por GIT_PS1 y hecho el source del script junto con la utilización de la variable PROMPT_COMMAND en lugar de PS1 para posibilitar la información de estado con colores al estar en un directorio git se muestra el prompt del ejemplo anterior.

Este es el archivo .bashrc completo de la distribución Arch Linux con el soporte para el script git-prompt.sh y algunas opciones personalizadas.

La documentación completa con todas las opciones de personalización están en las primeras líneas de comentario del script.

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

Adrianistán

Introducción a Prolog, tutorial en español

abril 12, 2018 10:16

Prolog es un lenguaje de programación lógico, quizá uno de los más populares de este paradigma ya que fue el primero en implementarlo, en el año 1972 en Francia.

Durante un tiempo, se creyó que PROLOG supondría la revolución de los lenguajes de programación, siendo uno de los estandartes de los lenguajes de programación de quinta generación. Tanto se creía que Borland, la famosa empresa de compiladores para MS-DOS, tenía Turbo Prolog, junto a Turbo C++, Turbo Pascal, Turbo Assembler y Turbo Basic.

Desafortunadamente, Prolog no triunfó como se esperaba y solo fue usado dentro del mundo de la Inteligencia Artificial. Existe un estándar ISO sobre Prolog (al igual que Ada, C++ y otros lenguajes estandarizados) pero no es demasiado influyente. Tampoco ha habido ningún éxito en alguna de las versiones propuestas para dotarle de orientación a objetos al lenguaje.

Prolog sin embargo ha influenciado a algunos lenguajes como Erlang, que toman algunos aspectos de él.

No obstante, Prolog sigue siendo un lenguaje muy interesante, diferente al resto de lenguajes (tanto imperativos, como funcionales), así que pongámonos a ello.

Actualmente existen varios compiladores de Prolog: SWI Prolog, GNU Prolog, Visual Prolog (al mucha gente no lo considera Prolog de verdad), …

La mejor versión bajo mi punto de vista es SWI Prolog, que tiene una librería de predicados bastante extensa. Es rápido y permite generar ejecutables nativos (realmente no lo son, pero la máquina virtual de Prolog ocupa muy poco y apenas se nota en el tamaño del ejecutable).

Lo podéis descargar gratuitamente desde su página oficial que como curiosiad, está hecha en SWI Prolog. También está en la paquetería de las distribuciones GNU/Linux habituales.

Nuestro primer programa

Para empezar con Prolog voy a tomar un camino distinto de muchos tutoriales y voy a empezar haciendo un programa con Entrada/Salida y que se ejecute como un binario independiente. La potencia de Prolog no está ahí especialmente, pero es un buen punto de partida

Para ello definimos un predicado main de la siguiente forma y lo guardamos en un fichero main.pl.

main :- 
	write("Hola Mundo"),
	nl,
	write("¿Cuál es tu nombre? "),
	read_string(user_input,['\n'],[],_,Nombre),
	write("Hola "),write(Nombre),nl,
	halt.

¿Qué hace el programa? Imprime en pantalla Hola Mundo (write), una nueva línea (nl), lee un string de teclado con el separador \n y lo unifica con la variable Nombre. Esto ya veremos que significa, pero de momento puedes pensar que Nombre es una variable de salida y que a partir de ahí Nombre tiene un valor establecido.

Compilamos con el siguiente comando:

swipl --goal=main --stand_alone=true -o main -c main.pl

Con esto le decimos a SWI Prolog que el objetivo a demostrar es main y que nos genere un fichero stand_alone (independiente).

Y ejecutamos el fichero ejecutable como uno más.

Ahora que ya sabemos como se generan programas compilados, vamos a introducirnos más en lo que hace especial a Prolog.

La terminal de Prolog

Prolog fue diseñado con una terminal interactiva en mente. La idea era que el usuario fuese introduciendo preguntas y el programa en Prolog fuese contestando. Este enfoque, similar a usar un programa desde el REPL de tu lenguaje, no ha acabado cuajando, pero es necesario pasar por él. Más adelante veremos como con SWI Prolog no hace falta usar la terminal. La terminal se abre escribiendo swipl en la línea de comandos:

Vemos un símbolo de interrogación. Eso nos permite saber que estamos en una terminal de Prolog. Podemos escribir write(“Hola”). y tener nuestro hola mundo tradicional, aunque no es muy interesante, pero sí es muy interesante que después escribe true. Más adelante veremos por qué.

Los programas Prolog

En Prolog los programas son algo diferentes a lo que estamos acostumbrados. Realmente en Prolog no hay programas sino una base de datos. Un programa se compone de predicados, muy parecidos a los del Cálculo de Predicados. Los predicados aportan información sobre las relaciones entre elementos. Todos los predicados tienen que acabar en punto.

Siguiendo las mismas normas que el cálculo de predicados:

  • Las constantes empiezan por minúscula
  • Las variables empiezan por mayúscula
  • Las funciones son constantes seguidas de N teŕminos. Son funciones estrictamente matemáticas.
  • Los predicados pueden ser atómicos o compuestos, con operadores lógicos (and, or, implica, etc)

Prolog durante la ejecución, va a intentar demostrar que el predicado es cierto, usando el algoritmo de backtracking. Y ahí está la verdadera potencia de Prolog. Veamos unos ejemplos:

fruta(manzana).
fruta(naranja).
fruta(platano).

Guarda ese archivo con extensión .pl y ejecuta swipl comida.pl.

Ahora en la terminal podemos hacer preguntas. ¿Es la manzana una fruta? Prolog responde verdadero. ¿Es la pera una fruta? Prolog responde que falso, porque según el archivo comida.pl, no lo es. Prolog no es inteligente, no sabe que significan las palabras, simplemente actúa siguiendo un conjunto de normas formales.

Hemos dicho que Prolog tiene variables. Una variable en Prolog es un marcador de hueco, es algo que no existe, porque no es ninguna constante en específico. Veamos la potencia de las variables con este otro predicado.

En este caso pedimos demostrar fruta(X).. Prolog buscará la primera solución que demuestra el predicado, que es que X valga manzana. Aquí podemos pulsar ENTER y Prolog se para o pulsar N y Prolog busca otra solución. ¿Potente, verdad? Prolog realiza un proceso interno que se llama unificación, es importante saber como funciona para ver que hace Prolog en realidad.

Unificación

La unificación es un proceso que combina dos predicados en uno que encaja. Para ello buscamos las sustituciones de valores con los que dos predicados son compatibles (realmente solo se pueden modificar las variables). No obstante, Prolog busca siempre el unificador más general, que es aquel que unifica dejando los predicados de forma más genérica posible, es decir, que pueden usarse con más valores.

Espero que esta imagen aclare el concepto de unificación. Básicamente Prolog para intentar demostrar un predicado intentará unificar con otros predicados del programa. Así cuando ponemos por ejemplo comida(manzana) , unifica con comida(X) así que Prolog toma ese predicado para continuar.

Backtracking

Cuando Prolog intenta demostrar un predicado aplica el algoritmo de backtracking. Este algoritmo recorre todas las soluciones posibles pero de forma más inteligente que la fuerza bruta. Backtracking intenta conseguir una solución hasta que un predicado falla, en ese momento, Prolog va hacia atrás y continúa por otra vía que pueda seguir.

Cada predicado es un nodo. Si un predicado falla se vuelve atrás. Esto es muy interesate ya que Prolog técnicamente puede ejecutar código hacia atrás.

Un predicado Prolog sigue esta estructura:

Predicados avanzados

Pero los predicados de Prolog no tienen por qué ser así de simples. Normalmente se usa el operador :- para indicar algo que para que se cumpla la parte de la izquierda, tiene que cumplirse la parte de la derecha antes (en cálculo de predicados es ←).

Por ejemplo, todas las frutas son comidas, así que podemos añadir esto a nuestro archivo.

fruta(manzana).
fruta(naranja).
fruta(platano).

comida(X) :- fruta(X).

Y los predicados en Prolog se pueden repetir y repetir y repetir. Prolog siempre intenta demostrar de arriba a abajo, si falla un predicado, prueba con el siguiente más para abajo y termina cuando no hay más predicados que probar. ¡Así que podemos definir comida en base a más predicados!

fruta(manzana).
fruta(naranja).
fruta(platano).

carne(pollo).
carne(vaca).
carne(cerdo).
carne(caballo).

comida(paella).
comida(pulpo).

comida(X) :- fruta(X).
comida(X) :- carne(X).

Operaciones

En Prolog existen varios operadores importantes:

  • , (coma) AND
  • ; (punto y coma) OR
  • A = B, se intenta unificar A y B. Devuelve true si funciona
  • A \= B es falso si A y B unifican
  • A is B, se evalúa B (es decir, se calcula lo que representa) y se unifica con A
  • A =:= B , evalúa A, evalúa B y los compara. Verdadero si son iguales
  • A =\= B, evalúa A, evalúa B y los compara. Falso si son iguales
  • Y muchos otros como =<, >=, >, < que tienen el comportamiento esperado.
  • Las operaciones matemáticas solo se pueden introducir en expresiones que vayan a ser evaluadas.

Quiero prestar especial atención en símbolo de igual que no es asignación sino unificación, pero puede parecerse. Estas dos líneas son equivalentes:

X = 5
% y 
5 = X

Ya que en ambos casos se unifica X con 5.

Veamos un ejemplo de todo esto, ya mucho más realista.

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%   Programa restaurante  %
%%%%%%%%%%%%%%%%%%%%%%%%%%%	

% menu

entrada(paella).
entrada(gazpacho).
entrada(pasta).

carne(filete_de_cerdo).
carne(pollo_asado).

pescado(trucha).
pescado(bacalao).

postre(flan).
postre(nueces_con_miel).
postre(naranja).

% Valor calorico de una racion

calorias(paella, 200).
calorias(gazpacho, 150).
calorias(pasta, 300).
calorias(filete_de_cerdo, 400).
calorias(pollo_asado, 280).
calorias(trucha, 160).
calorias(bacalao, 300).
calorias(flan, 200).
calorias(nueces_con_miel, 500).
calorias(naranja, 50).

% plato_principal(P) P es un plato principal si es carne o pescado

plato_principal(P):- carne(P).
plato_principal(P):- pescado(P).

% comida(Entrada, Principal, Postre)

comida(Entrada, Principal, Postre):-
        entrada(Entrada),
        plato_principal(Principal),
        postre(Postre).
    
% Valor calorico de una comida

valor(Entrada, Principal, Postre, Valor):-
        calorias(Entrada, X),
        calorias(Principal, Y),
        calorias(Postre, Z),
        sumar(X, Y, Z, Valor).

% comida_equilibrada(Entrada, Principal, Postre)

comida_equilibrada(Entrada, Principal, Postre):-
        comida(Entrada, Principal, Postre),
        valor(Entrada, Principal, Postre, Valor),
        menor(Valor, 600).


% Conceptos auxiliares

sumar(X, Y, Z, Res):-
        Res is X + Y + Z.             % El predicado "is" se satisface si Res se puede unificar
                                      % con el resultado de evaluar la expresion X + Y + Z 
menor(X, Y):- 
        X < Y.                        % "menor" numerico

dif(X, Y):-
        X =\= Y.                      % desigualdad numerica 


% cuantas comidas llevan naranja de postre
%

comidas_con_naranja :-
	write("Comidas con naranja: "),
	aggregate_all(count,comida(_,_,naranja),X),
	write(X),
	nl.

Lo cargamos con swipl restaurante.pl y podemos contestar a las siguientes preguntas de forma sencilla:

¿Qué valor calórico tiene la comida de paella, trucha y flan?

valor(paella,trucha,flan,N).

Dime una comidas equilibrada que lleve naranja de postre

comida_equilibrada(X,Y,Z),Z=naranja.

¿Cuántas comidas con naranja hay?

comidas_con_naranja.

Variables anónimas y predicados predefinidos

Si te fijas en el predicado comidas_con_naranja, es diferente al resto. Esta programado de otra forma. La salida por pantalla se hace con write como ya habíamos visto al principio, write es un predicado que siempre es cierto y en caso de backtracking simplemente pasa. aggregate_all es también otro predicado incluido en SWI Prolog, para en este caso, contar cuantas soluciones tiene el predicado comida(_,_,naranja). X unifica en aggregate_all con el valor que toca (que es una constante, los números son constantes) y en las siguientes sentencias (write), X ha sido sustituido por el valor con el que unificó en aggregate_all.

Por otro lado, ¿qué significa la barra baja? Es una variable anónima. Cuando no usamos una variable en más lugares y no nos interesan sus valores podemos simplemente poner barra baja.

Listas en Prolog

Prolog tiene un tipo de dato más avanzado, las listas. Su tamaño es dinámico y es conveniente distinguir la cabeza de la cola. La cabeza es el primer elemento de una lista, la cola el resto de la lista.

Las listas se crean con corchetes:

X = [1,2,3,4,5],
sumlist(X,N).

sumlist es un predicado de SWI que hace la suma de todos los elementos de una lista.

Con la barra podemos separar cabeza y cola de una lista:

[1,2,3,4] = X,
[H|T] = X.

Implementando sumlist

Vamos a ver como se puede implementar sumlist con Prolog de forma sencilla.

sumar([],0).
sumar([H|T],N) :-
	sumar(T,X),
	N is X+H.

El predicado es sencillo, para un caso base de lista vacía, la suma es 0. Para otros casos más complejos separamos la lista en cabeza H y cola T y la suma es N. Esta suma se define como el resultado de sumar T (queda en X) y la cabeza H.

Assert y retract

¿Podemos crear predicados en tiempo real? Claro. Prolog provee de los predicados especiales assert para añadir y retract para eliminar un predicado.

Operador de corte

Prolog tiene un operador muy controvertido, el operador de corte, !. Se trata de un operador que permite no volver hacia atrás. Hay que intentar no usarlo, pero muchas veces mejora el rendimiento.

Metaprogramación

Prolog tiene un gran soporte para la metaprogramación. Assert y Retract son ya formas de metaprogramación, no obstante Prolog tiene muchas más.

call permite ejecutar código Prolog desde Prolog.

setarg permite modificar un término de un predicado y arg unifica una variable con el término de un predicado que queramos. nb_setarg es la versión de setarg que en caso de backtracking no deshace la operación. Un ejemplo de esto lo podemos encontrar en la definición de aggregate_all en la librería de SWI Prolog:

aggregate_all(count, Goal, Count) :-
    !,
    aggregate_all(sum(1), Goal, Count).
aggregate_all(sum(X), Goal, Sum) :-
    !,
    State = state(0),
    (  call(Goal),
           arg(1, State, S0),
           S is S0 + X,
           nb_setarg(1, State, S),
           fail
    ;  arg(1, State, Sum)
).

 

Debug con trace

Prolog posee un predicado muy útil para hacer debugging. Es trace y con el predicado que vaya a continuación podremos inspeccionar todas las operaciones del motor de backtracking.

La entrada Introducción a Prolog, tutorial en español se publicó primero en Adrianistán.

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

Picando Código

Actualización a Montevideo Bicis con datos de accidentes 2017

abril 10, 2018 01:00

Montevideo Bicis es un sitio web que presenta información objetiva para gente que quiera circular en bicicleta en Montevideo. Aprovecha Datos Abiertos de la Intendencia de Montevideo y y UNASEV. Después de un buen tiempo, UNASEV liberó los datos de accidentes de tránsito de 2017. Como era de esperarse, el archivo es inconcistente con los csv liberados años anteriores… Pero bueno, por lo menos los datos están y son procesables por lenguaje de máquina.

El proceso fue más o menos el mismo que para los datos de 2016, y ya quedó actualizado en el sitio. Otra pequeña mejora que le hice fue convertir a Float los números de los accidentes que ocurrieron con bicicleta, para que el porcentaje sobre el total sea más preciso. Pueden visitar la página de accidentes para ver la nueva información. La cantidad total de muertes registradas en accidentes de tránsito subió un poco respecto al año anterior, pero la cantidad de accidentes que involucraron bicicletas bajó.

Montevideo Bicis

Ojalá algún día Montevideo se convierta en una ciudad amigable para los ciclistas… Si les interesa el tema, pueden visitar MontevideoBicis.com, y ver el código fuente en GitHub. Y si tienen ideas o críticas sobre el sitio, son más que bienvenidos a compartirlos en los comentarios de este post.

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

Variable not found

Cómo incluir scripts en la página desde vistas parciales ASP.NET Core MVC con DynamicSections

abril 10, 2018 06:55

ASP.NET CoreCuando desarrollamos una aplicación ASP.NET Core MVC, es muy frecuente encontrar que nuestras vistas definen secciones de contenido usando la directiva @section que más adelante se incluyen en el Layout para formar parte de la información enviada al lado cliente.

Sin embargo, ya sabemos que no es posible utilizar secciones en vistas parciales o cualquier otro tipo de componentes, lo cual complica algunos escenarios.

Por ejemplo, es relativamente frecuente que en el lado servidor generemos código HTML utilizando algún mecanismo de reutilización de marcado, como vistas parciales, templated helperstag helpers, etc., y que éste requiera recursos extra como scripts o CSS que obligatoriamente deberíamos incluir en las vistas principales donde se utilicen.

Más en el terreno práctico, imaginad que creamos una vista parcial que requiere que las páginas donde vayamos a usarla incluyan una referencia a un archivo de scripts y un bloque de inicialización como el siguiente:
@* Partial view: _MyComponent.cshtml *@
<div class="my-component">
<!-- UI goes here -->
</div>

<!-- And now, add dependencies and initialization code -->
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" ></script>
<script>
// Initialization code goes here
$(".my-component").setup();
</script>
Aquí es donde comienzan los problemas. Si lo enfocamos como hemos mostrado anteriormente y resulta que en una misma página podemos tener más de una instancia de dicha vista parcial, estaremos incluyendo en la página las referencias y código de inicialización más de una vez, lo cual podría traernos bastantes dolores de cabeza:
<html>
<body>
...
<!-- My Component, instance #1 -->
@Html.Partial("_MyComponent.cshtml")
...
<!-- My Component, instance #2 -->
@Html.Partial("_MyComponent.cshtml")
...
</body>
</html>
Además de problemas de rendimiento y mal funcionamiento de la aplicación, también podría ocurrir que al insertar la parcial en la página, los scripts quizás aparecerían antes de referenciar los componentes o frameworks utilizados, pues normalmente esto suele hacerse al cerrarse el <body>.

Por ejemplo, en el código anterior nuestro componente utiliza JQuery, por lo que necesitamos que este framework se cargue antes de que nuestro código aparezca en la página, cosa que no siempre podremos asegurar. Así, el código enviado a la página podría ser el siguiente:
<html>
<body>
...
<!-- My Component, instance #1 -->
<div class="my-component">
<!-- UI goes here -->
</div>
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" ></script>
<script>
// Initialization code goes here
$(".my-component").setup();
</script>
...

<!-- My Component, instance #2 -->
<div class="my-component">
<!-- UI goes here -->
</div>
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" ></script>
<script>
// Initialization code goes here
$(".my-component").setup();
</script>
...
<script src="/scripts/jquery.min.js"></script>
</body>
</html>
Obviamente, esto provocaría un error en la página.

En este post vamos a ver un posible enfoque para solucionar este escenario. La idea es bastante sencilla, pues simplemente vamos a hacer posible que cada vista parcial o componente pueda registrar código HTML que luego sea renderizado en un lugar específico de la página. El objetivo sería algo similar a lo que conseguimos con la definición de secciones @section y @RenderSection(), pero aplicable a cualquier tipo de vista parcial.

¿Qué queremos conseguir?

Creo que para tenerlo más claro, lo mejor es comenzar a explicarlo viendo lo que pretendemos obtener. La idea es que desde cualquier componente de la Vista en aplicaciones MVC podamos registrar bloques de código (scripts, CSS o lo que sea) mediante un tag helper como el siguiente:
@* _MyComponent.cshtml *@
<div class="my-component">
<!-- UI goes here -->
</div>

<register-block dynamic-section="scripts" key="my-component">
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" />
</register-block>
Observad que el sistema que vamos a crear es muy similar a las secciones que en Razor definimos de forma estática con @section y renderizamos con @RenderSection(), por lo que llamaremos a nuestro componente Dynamic Sections.
El tag helper <register-block> que veis en el código anterior no generará nada en la vista desde la que se renderice nuestra vista parcial "_MyComponent.cshtml"; simplemente irá guardando el contenido en un diccionario en memoria asociado a la sección “scripts” y bajo la clave “my-component”. Si existen en una página varias instancias de nuestro componente, en memoria sólo se registrará una vez porque todas utilizarán la misma clave y sección.

Ya desde el layout o cualquier otro punto, podemos introducir el contenido que ha ido siendo registrado mediante otro tag helper:
@* _Layout.cshtml *@
<html>
<body>
...
<dynamic-section name="scripts" />
</body>
</html>
Como podréis suponer, el tag helper <dynamic-section> renderizará el contenido asociado a la sección dinámica llamada “scripts” justo en el punto de la página en el que ha sido invocado. Ahí es donde se incluirá todo el código registrado por los distintos componentes que conforman la página.

Profundizando un poco en la idea podemos conseguir cosas bastante interesantes. Por ejemplo, jugando con la clave única del bloque de código podemos conseguir que determinados componentes incluyan en la página un código común para todas las instancias, y luego un código específico por cada instancia:
@* _MyComponent.cshtml *@
@model string
@{
var id = "c" + DateTime.Now.Ticks; // Unique id for this instance
}
<div class="my-component" id="@id">
<h3>@Model</h3>
</div>

<register-block dynamic-section="scripts" key="my-component-common">
<!-- Common initialization code for my component (generated only once) -->
</register-block>

<register-block section="scripts" key="my-component-@id">
<!-- Initialization code for @id -->
</register-block>
De esta forma, si renderizamos una vista como la siguiente, en la que se introducen dos instancias distintas del la vista parcial "_MyComponent", obtendremos el resultado mostrado justo a continuación:
@* View: Index.cshtml *@
...
<div class="jumbotron">
@Html.Partial("_MyComponent", "Hello, world")
@Html.Partial("_MyComponent", "How are u doing?")
</div>
...

<!-- Página renderizada -->
<html>
<body>
...
<div class="jumbotron">
<div class="my-component" id="c636551599516842141">
<h1>Hello, world</h1>
</div>
<div class="my-component" id="c636551599516868415">
<h1>How are u doing?</h1>
</div>
</div>
...

<!-- Common initialization code for my component (generated only once) -->
<!-- Initialization code for c636551599516842141 -->
<!-- Initialization code for c636551599516868415 -->
</body>
</html>

Implementando el core

Como podremos ver a nivel de código, el funcionamiento creo que es el más simple posible, pues aprovechamos la posibilidad que nos brinda ASP.NET Core para almacenar información personalizada en la colección HttpContext.Items. El contenido que vayamos guardando ahí persistirá exclusivamente durante la petición actual y será descartado cuando ésta finalice, por lo que parece el lugar indicado para ir almacenando los contenidos registrados mediante el tag helper <register-block> hasta que sean introducidos en la página por <dynamic-section>.
Spoiler: el código fuente del proyecto está disponible en GitHub, y también podéis usarlo directamente descargando el paquete NuGet. Al final del post tenéis más información al respecto.
Aunque hemos comentado que estas funcionalidades de registro y obtención de bloques de código estarán disponibles en forma de tag helper, con muy poco esfuerzo podríamos implementarlas también en forma de HTML helpers, por lo que vamos a crear el “core” del sistema de forma que podamos utilizarlo desde ambas opciones.
public static class DynamicSectionsHelpers
{
private static string HttpContextItemName = nameof(DynamicSectionsHelpers);

/// <summary>
/// Registers a code block in a dynamic section.
/// </summary>
public static void RegisterBlock(this HttpContext httpContext,
string dynamicSectionName, string key, string content)
{
var sections = (SectionDictionary)httpContext.Items[HttpContextItemName];
if (sections == null)
{
sections = new SectionDictionary();
httpContext.Items[HttpContextItemName] = sections;
}
dynamicSectionName = dynamicSectionName ?? string.Empty;
key = key ?? string.Empty;
if (!sections.ContainsKey(dynamicSectionName))
{
sections[dynamicSectionName] = new BlockDictionary();
}
sections[dynamicSectionName][key] = content;
}

/// <summary>
/// Gets the content of the specified dynamic section.
/// </summary>
public static string GetDynamicSection(this HttpContext httpContext,
string dynamicSectionName, bool remove = true)
{
var result = string.Empty;
var sections = (SectionDictionary)httpContext.Items[HttpContextItemName];
if (sections == null)
return result;

dynamicSectionName = dynamicSectionName ?? String.Empty;
if (dynamicSectionName == "*")
{
// Get all sections
result = GetSectionAndRemoveWhenRequested(sections, remove, sections.Keys.ToArray());
}
else if (sections.ContainsKey(dynamicSectionName))
{
// Get only the specified section
result = GetSectionAndRemoveWhenRequested(sections, remove, dynamicSectionName);
}

return result;
}

private static string GetSectionAndRemoveWhenRequested(SectionDictionary sections,
bool remove, params string[] sectionNames)
{
var sb = new StringBuilder();
foreach (var sectionName in sectionNames)
{
var contents = sections[sectionName].Select(c => c.Value);
foreach (var content in contents)
{
sb.Append(content);
}

if (remove) // This section can be obtained only once!
{
sections.Remove(sectionName);
}
}
return sb.ToString();
}

private class BlockDictionary : Dictionary<string, string>
{
public BlockDictionary() : base(StringComparer.CurrentCultureIgnoreCase) { }
}

private class SectionDictionary : Dictionary<string, BlockDictionary>
{
public SectionDictionary() : base(StringComparer.CurrentCultureIgnoreCase) { }
}
}
Creo que el código se entiende relativamente bien, pero comentaré rápidamente sus dos principales métodos, porque el resto ya son detalles de implementación:
  • El método RegisterBlock() es el encargado de registrar un contenido en una sección, utilizando la clave que le llega como parámetro.
  • El método GetDynamicSection() retorna el contenido de todos los bloques registrados en una sección determinada. Sin embargo, hemos implementado una convención que puede venir bien en algunas ocasiones: si el nombre de la sección a renderizar es “*”, se renderizarán todas las secciones registradas. También, mediante un parámetro es posible indicar si el contenido debe ser eliminado del diccionario una vez haya sido obtenido (por defecto, true).

Implementación de HTML Helpers

Partiendo de la clase anterior, que prácticamente implementa todo lo que necesitamos, crear helpers HTML que faciliten su uso desde vistas es trivial.
public static class DynamicSectionsHtmlExtensions
{
/// <summary>
/// Registers a code block in a dynamic section.
/// </summary>
public static string RegisterBlock(this IHtmlHelper helper,
string dynamicSectionName, string key, string content)
{
helper.ViewContext.HttpContext
.RegisterBlock(dynamicSectionName, key, content);
return string.Empty;
}

/// <summary>
/// Gets the content of the specified dynamic section.
/// </summary>
public static IHtmlContent DynamicSection(this IHtmlHelper helper,
string dynamicSectionName = "*", bool remove = true)
{
var blocks = helper.ViewContext.HttpContext
.GetDynamicSection(dynamicSectionName, remove);
return new HtmlString(blocks);
}
}
De esta forma, podremos hacer uso de construcciones como las siguientes para registrar un contenido:
...
@Html.RegisterBlock("scripts", "my-component", "<script>console.log('Hello, world!');</script>")
...
Y también para renderizar las secciones más adelante:
<html>
<body>
...
@Html.DynamicSection("scripts")
</body>
</html>
Recordad que para que los extensores de IHtmlHelper estén disponibles en una vista debemos importar en ella el espacio de nombres donde han sido definidos, bien mediante algo como @using DynamicSections en la propia vista, o bien de forma global en /Views/_ViewImports.cshtml.

Implementación de tag helpers

Comencemos creando el tag helper <register-block>. Como podemos observar, el código es bastante simple porque lo único que hace es renderizar el contenido de la etiqueta y llamar al método DynamicSections.RegisterBlock(), visto anteriormente, para almacenarlo. Tras ello, elimina el contenido para que no sea volcado a la página en ese momento.
[HtmlTargetElement("register-block")]
public class RegisterBlockTagHelper: TagHelper
{
[ViewContext, HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }

public string Key { get; set; }
public string DynamicSection { get; set; }

public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var childContents = await output.GetChildContentAsync(useCachedResult: true);
var content = childContents.GetContent();
ViewContext.HttpContext.RegisterBlock(DynamicSection, Key, content);
output.SuppressOutput();
}
}
A continuación, veamos el tag helper <dynamic-section>, cuyo código es igual de sencillo:
[HtmlTargetElement("dynamic-section", Attributes="Name")]
public class DynamicSectionTagHelper : TagHelper
{
[ViewContext, HtmlAttributeNotBound]
public ViewContext ViewContext { get; set; }

public string Name { get; set; }
public bool Remove { get; set; }

public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.SuppressOutput();
output.Content.SetHtmlContent(ViewContext.HttpContext.GetDynamicSection(Name, Remove));
}
}
Recordad que para que los nuevos tag helpers estén disponibles en una vista debemos importarlos mediante usando la directiva como sigue, bien en la propia vista o bien de forma global en /Views/_ViewImports.cshtml:
@addTagHelper *, DynamicSections
El código anterior asume que los tag helpers han sido definidos en un ensamblado llamado DynamicSections. Si no es así, sustituid este nombre por el que hayáis usado (por ejemplo, el nombre del ensamblado principal de vuestro proyecto).

Me viene bien este código, ¿cómo lo incluyo en mi proyecto?

Lo primero, recuerda que no debes confiar en el código de extraños ;-D

Pero si es tu intención, mejor que copiarlo y pegarlo desde el blog quizás te sea más sencillo acudir a descargarlo desde el repositorio en GitHub, o utilizar directamente el paquete NuGet “DynamicSections” donde ya lo tendrás cocinado. En cualquier caso, es interesante que eches un vistazo primero a la home del proyecto en GitHub, donde encontrarás algo de documentación y algunos ejemplos de uso.

Por supuesto, feedback is welcome ;)

Publicado en Variable not found.

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

Adrianistán

Mónada ST en Haskell: STRef, STArray

abril 09, 2018 10:32

Haskell tiene una librería muy completa, con muchas estructuras de datos y, en este caso, mónadas muy interesantes. En este caso vamos a hablar de la mónada ST. ST son las siglas de State Threads. Fueron definidas por Simon Peyton-Jones en un paper de 1994 y son una forma muy curiosa que tiene Haskell de permitirnos escribir código con estructuras de datos mutables. La gracia está en que se garantiza que es imposible que la mutabilidad interna que hay dentro de la mónada ST salga al exterior, de modo que de cara al exterior son funciones puras igualmente. Esta mónada no sirve para comunicarse con el exterior como sí sirve IO pero si podemos usar ST antes que IO, mejor.

STRef

La estructura más fundamental es STRef, que permite tener una variable mutable dentro de Haskell. Las funciones que permiten su manejo son newSTRef, readSTRef, writeSTRef y modifySTRef. Creo que es bastante evidente para qué sirve cada una, pero veamos un ejemplo práctico.

Vamos a hacer una función que sume todos los elementos de una lista. Existen varias formas de hacerlo de forma pura en Haskell:

sumar :: Num a => [a] -> a
sumar [] = 0
sumar (x:xs) = x+sumar xs

Una versión recursiva

sumar :: Num a => [a] -> a
sumar = foldr1 (+)

Una versión más compacta

En un lenguaje de programación imperativo sin embargo posiblemente haríamos algo parecido a esto:

int sumar(int[] nums){
    int suma = 0;
    for(int i=0;i<nums.length;i++){
        suma += nums[i];
    }
    return suma;
}

Con ST podemos hacer código que se parezca a la versión imperativa, lo cuál es útil en determinados algoritmos y también si queremos ganar eficiencia.

import Control.Monad.ST
import Control.Monad
import Data.STRef

sumar :: Num a => [a] -> a
sumar xs = runST $ do
	x <- newSTRef 0
	
	forM_ xs $ \n -> 
		modifySTRef x $ \x -> x+n

	readSTRef x

Lo primero que tenemos que hacemos es ejecutar la mónada ST con runST y a continuación la función. Allí creamos una variable mutable con valor inicial 0. Después lanzamos forM_ que itera sobre cada elemento de la lista xs ejecutando para cada elemento la función que llama a modifySTRef que ejecuta a su vez la función que suma el valor del acumulador con el valor del elemento actual de la lista.

Por último, la función finaliza devolviendo el valor de x.

Como veis, el código no tiene nada que ver a las otras versiones de Haskell y tiene un gran parecido con la versión de Java. No obstante, el código sigue siendo puro, sin efectos colaterales.

STArray

Pero no solo tenemos STRef, también tenemos STArray que es un array de tamaño fijo con estas mismas propiedades. Las funciones básicas son newListArray, readArray, writeArray y getElems.

Por ejemplo, podemos implementar el algoritmo de Bubble Sort de forma imperativa con STArray:

import Control.Monad
import Control.Monad.ST
import qualified Data.Array.ST as ST
import Data.STRef

bubblesort :: [Int] -> [Int]
bubblesort xs = runST $ do
	let l = length xs
	temp <- newSTRef $ head xs
	array <- ST.newListArray (0,l-1) xs :: ST s (ST.STArray s Int Int)

	forM_ [0..l] $ \i -> do
		forM_ [1..(l-1)] $ \j -> do
			prev <- ST.readArray array (j-1)
			actual <- ST.readArray array j	
			if prev > actual then do
				writeSTRef temp prev
				ST.writeArray array (j-1) actual
				t <- readSTRef temp
				ST.writeArray array j t
			else do
				return ()

	ST.getElems array
			
main :: IO ()
main = do
	let orden = bubblesort [3,4,1,2]			
	putStrLn ("Orden: "++(show orden))

La mónada ST junto con las funciones de Control.Monad nos permiten escribir código que parece imperativo y con mutabilidad, sin perder la pureza y la seguridad de Haskell.

La entrada Mónada ST en Haskell: STRef, STArray se publicó primero en Adrianistán.

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

Picando Código

Entrevista en Sputnik Mundo

abril 09, 2018 10:30

A raíz del artículo que escribí para la diaria sobre Ética hacker, fui entrevistado en Sputnik Mundo. El artículo:

Hackers: ¿amigos o enemigos de los ciudadanos?
Sputnik consultó al desarrollador y hacker cívico Fernando Briano para contarte qué es el software libre, en qué consiste su ética, y por qué es la bandera de los piratas informáticos.

Una de las ideas con estos textos es aclarar la definición de hacker que generalmente se malinterpreta como ciber-criminal. Pero hablé de software libre, del derecho a reparar, privacidad y más.

Fue una entrevista por teléfono, lo que lo hizo más difícil para mí en cuanto a nervios. Pero creo que el resultado final es bastante bueno. Los invito a leer la nota, y desde acá mando un agradecimiento a Sputnik y a Angelina por la entrevista.

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

Picando Código

Daniela Vázquez: científica de datos extrae y abre datos del Parlamento Uruguayo

abril 09, 2018 09:00

Si bien Uruguay ha avanzado bastante en el tema de Datos Abiertos, todavía queda mucho por hacer. El Parlamento en particular no publica Datos Abiertos. Sí publica diarios de sesiones, pero en PDF, una de las pesadillas de los activistas de datos. No pueden ser directamente procesados en código de máquina a diferencia de formatos como CSV, JSON, txt y demás. Tampoco hay una API de dónde obtener los datos, hay que descargarlos desde el sitio web del Parlamento.

Pero esto no impidió que Daniela Vázquez obtuviera y procesara los datos. En su blog escribió “Scrapeando las Sesiones Parlamentarias de Uruguay”, donde nos cuenta detalladamente cómo programó un scraper para descargar los archivos PDF usando rvest y extrajo el texto a través de pdftools, herramientas del lenguaje de programación R. ¡Lectura muy recomendable! A pesar de no haber visto nunca nada en R, el post está muy bien explicado y me resultó muy entendible.

Este estudio desencadenó en “¿De qué se habló en el Parlamento uruguayo desde 2017?“, donde hace un análisis de los textos levantados en la entrada anterior, y presenta distintos estudios de las sesiones de Diputados y Senadores: Con qué frecuencia se reunieron, Qué tan largas fueron las sesiones, Palabras más comunes y Análisis de sentimiento.

Sentimiento en sesiones del Senado

Además de explicar cómo hizo cada estudio, su post está complementado con gráficas como la que ven en la imagen que acompaña este post. La información objetiva y clara, como debe ser para que cada uno la interprete críticamente y saque sus conclusiones.

Como si todo esto fuera poco, Daniela subió el código fuente de su trabajo a GitHub, y comparte los datos en formato CSV para las sesiones de Diputados y Senadores. Muchas gracias Daniela por compartir tu conocimiento y gran trabajo. Que haya cada vez más rebeldes de los datos 💪

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

Variable not found

Enlaces interesantes 316

abril 09, 2018 06:59

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

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

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

Koalite

Programar no es desarrollar

abril 09, 2018 05:06

Me encanta programar. Probablemente sea la parte de mi trabajo que más disfruto. Y creo que soy capaz de valorar la importancia de programar bien y el impacto que ello tiene en el buen desarrollo de un proyecto de software.

Recuerdo cuando, hace unos 20 años, estaba empezando la carrera de Ingeniería Informática y pensaba que programar era algo sin importancia. Algo de lo que podían ocuparse “esos de FP”, porque nosotros, los auténticos ingenieros, nos encargaríamos de la parte de análisis, diseño, gestión de proyecto y demás cosas importantes. Total, rellenar los huecos de nuestros fantásticos diseños no era muy distinto de niños coloreando sus cuadernos de dibujo, y para eso no hacía falta gente con nuestras enormes capacidades. Huelga decir que, además de ser un planteamiento arrogante que sóla la juventud y la necedad pueden sostener, estaba completamente equivocado.

Por suerte los tiempos han cambiado (al menos en parte), y ese enfoque en el que los programadores son los últimos monos, ya no está tan en boga. Es verdad que sigue habiendo reductos en los cuales la jerarquía basada en jefes de proyectos, analistas funcionales, analistas programadores, programadores, etc., se mantiene, pero me atrevería a decir que es una visión que está en declive y se está dejando de ver la programación como una tarea mecánica destinada a la gente con menos experiencia para empezar a valorarla más.

Claro que, como siempre que cambian las tornas, corremos el riesgo de irnos al otro extremo. Al de entender la programación como la parte fundamental del desarrollo de software. Y me temo que tampoco es así. Hace tiempo que dejé de creer en la separación rígida entre roles en el mundo del desarrollo de software, pero pese a ello, y sin pretender en ningún momento sonar arrogante, considero importante distinguir entre programar y desarrollar software. Entre programadores y desarrolladores de software.

Pero, ¿qué se supone que debe hacer un programador? Como mínimo, ser capaz de escribir la secuencia de pasos para resolver un problema en un lenguaje que sea capaz de interpretar una máquina. Hay gente muy buena en eso. Humanos con los que puedes hablar en lenguaje natural y se encargan de convertirlo a C, Python, o el lenguaje de turno. Y no sólo eso. Lo hacen con un código elegante, eficiente, fácil de mantener, acorde a las buenas prácticas de turno y que cumple todas las expectativas de los crafters más exigentes.

Vamos a ver un ejemplo.

Programando Bien™

Supongamos que tenemos que añadir una pequeña funcionalidad a un producto que ya existe: editar un tweet. Como somos afortunados, resulta que Twitter está desarrollado siguiendo todas las buenas prácticas de turno y su código es fácil de modificar.

OJO: esta simplificación es mentira y no tiene sentido para la escala de Twitter, pero es sólo un ejemplo.

Por supuesto, estamos usando DDD y arquitecturas poligonales con cebolla, así que nuestro código empieza por un servicio de aplicación/caso de uso tal que así:

public class TweetEditor {
  // Inyectados por constructor
  private readonly IUnitOfWork uow;
  private readonly ITweetRepository repository;

  public void EditTweet(Guid tweetId, string newContent) {
    using (var tx = uow.Start()) {
      var tweet = repository.GetById(tweetId);
      tweet.UpdateContent(newContent);
      tx.CommitChanges();
    }
  }
}

Vamos bien. Tenemos un servicio que recibe sus dependencias por constructor, utiliza una entidad de dominio, Tweet, un repositorio, y el patrón UnitOfWork. Todo muy de libro. En nuestra entidad implementamos la (simple) lógica de negocio:

public class Tweet {

  // ... todo lo que existiera antes en la entidad Tweet

  public void UpdateContent(string newContent) {
    Check.Require(!string.IsNullOrWhiteSpace(newContent));

    this.content = newContent;
  }
}

Perfecto. Lógica encapsulada en la entidad y garantizando que se mantienen los invariantes adecuados.

Desde un punto de vista de programación esta implementación es estupenda (asumiendo siempre que lo de las arquitecturas poligonales y el diseño OO sea lo tuyo). Tenemos código claro, con nombres bien escogidos, responsabilidades aisladas, estamos escribiendo código idiomático al lenguaje y arquitectura elegidos, aplicamos patrones de diseño ampliamente conocidos y contrastados…

Todo lo que nos falta

Si la programación que hemos realizado en el ejemplo anterior es tan buena (o al menos no es horrible), ¿podemos estar tranquilos? La verdad es que no. Puede que el código cumpla todo eso que hemos mencionado antes, pero la implementación que hemos hecho de la funcionalidad presenta un montón de problemas que no están relacionados, al menos directamente, con el código que hemos escrito.

Pensemos un poco en lo que implica poder editar el contenido de un tweet.

¿Qué va a ocurrir con las respuestas a ese tweet? Es decir, ¿qué ocurre si escribo un tweet diciendo “El Real Madrid es el mejor”, alguien me contesta, “Qué razón tienes”, y luego lo edito y pongo “El Real Madrid es el peor”. ¿Se deben mantener las respuestas? ¿Se debe notificar a quien ha respondido para que pueda editar su respuesta? ¿Hace falta que los usuarios puedan saber si un tweet se editó y en qué momento?

Algo similar ocurre con los retweets. ¿Al cambiar el contenido del tweet se deshacen los retweets anteriores? ¿Se notifica a los usuarios para que elijan qué hacer con su retweet? ¿Y con los tweets que citan al original? Con los likes pasa algo parecido. Y con los momentos que enlazan al tweet.

Además, twitter es una aplicación distribuida con clientes parcialmente conectados. ¿En las aplicaciones móviles vamos a actualizar los tweets que están en el timeline para que vean el nuevo contenido? ¿Los dejamos desactualizados a menos que refresquen? Y eso en los clientes oficiales, ¿que hacemos con los demás clientes? ¿Exponemos un nuevo API para que puedan detectar los cambios?

Claro que también tenemos que pensar en los usuarios. ¿Van a entender la funcionalidad? ¿De qué manera se la vamos a exponer? ¿La vamos a desplegar a todos a la vez? ¿Probamos primero con algún grupo de usuarios? Y ya que hablamos de desplegar, ¿cómo haremos el paso a producción? ¿Hace falta modificar los datos existentes, por ejemplo para añadir una fecha de actualización? Si es así, ¿qué reglas vamos a seguir para actualizarlos?

Y todavía no hemos dicho nada de pruebas, porque ahora que hemos empezado a ver todas las implicaciones que tiene esto de editar un tweet, habrá que preparar una buena batería de pruebas (idealmente automatizada, pero al menos manual) para comprobar que todos estos casos que estamos encontrando funcionan como deben.

Cuando empezamos a analizar el problema nos damos cuenta de que el código que hemos escrito antes, por muy bonito, sólido y limpio que sea, no vale de mucho.

Desarrollar software

Creo que con este ejemplo podemos empezar a intuir a qué me refiero con que programar no es desarrollar.

En cuanto saltamos del divertido mundo de las katas y los puzzles al Mundo Real™, la parte de programación “pura” empieza a perder peso. Programar es una parte necesaria para desarrollar software, pero no es suficiente y tampoco es la más importante.

Seamos realistas, en las aplicaciones de línea de negocio que desarrollamos la mayoría de nosotros, gran parte de las funcionalidades no son nada complicadas de implementar y se basan en guardar algo en una base de datos, pintarlo en la pantalla adecuada, y hacer un par de cálculos por el camino.

Lo complicado suele venir de cómo interactúan esas funcionalidades con todas las demás que ya existen en el sistema, con cómo vamos a cambiar la forma de trabajar de los usuarios actuales, con cómo vamos a adaptar los datos ya existentes para que sean compatibles con las nuevas funcionalidades o de qué forma podrá venderse la funcionalidad de forma atractiva y eficiente.

En definitiva, la parte complicada es pensar, analizar y diseñar la solución desde el punto de vista de procesos y operativa, no de código.

Visto así, resulta tentador volver a pensar en jerarquías rígidas en las que unos señores muy listos hacen la labor de análisis funcional, transmiten ese análisis a arquitectos y diseñadores, los cuales pasan unos diagramas UML a programadores y listo. A pintar y colorear. Sin embargo no creo que eso funcione realmente.

Es cierto que es imposible que alguien sea bueno en todo, por lo que pretender que una persona sea a la vez experto en negocio, en experiencia de usuario, en usabilidad, en bases de datos, en despliegues en la nube y en otras mil cosas no tiene sentido. Pero hay alternativas.

Siempre se pueden construir equipos en los que quienes dominan estas áreas colaboren estrechamente para el desarrollo de cada funcionalidad, pero incluso si no existe esa disponibilidad de expertos, lo que sí esperaría de un desarrollador de software es que sea consciente de que es necesario preocuparse por esas cosas. Las hará mejor o peor, podrá necesitar más o menos ayuda externa para las áreas en las que más pueda cojear, pero al menos lo intentará.

En mi caso, trabajo en un equipo pequeño. Eso hace que a la hora de buscar compañeros le demos mucha importancia a esa capacidad de resolver problemas y tener una visión más amplia que escribir un código bonito. Sencillamente porque no nos podemos permitir tener una separación de roles en las que alguien analiza un problema, se lo pasa a otra persona para que diseñe el UX, y luego dan unas pantallas a un programador para que las implemente. Por supuesto le damos mucha importancia a la calidad del código, pero si no va acompañada de una preocupación por todo lo demás, no nos sirve de nada.

Por suerte, al igual que la mayoría del código que escribimos no tiene gran complejidad, muchas veces todas estas cosas que rodean al código tampoco son tan sumamente complejas vistas por separado como para necesitar grandísimos expertos en la materia. A lo mejor como desarrollador no eres experto, por ejemplo, en el negocio para el que estás desarrollando, o no tienes grandes dotes para el diseño de interacción, pero con que hagas pequeños avances en estas áreas, con que te preocupes un poco, el resultado mejorará enormemente.

Igual que ser capaz de escribir no te hace escritor, ser capaz de programar no te hace desarrollador. Hay mucho más que eso.

Posts relacionados:

  1. Desarrollar es sólo el principio
  2. Lo peor de desarrollar software
  3. No todo el mundo necesita programar (aunque es divertido)

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

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

Galardones de FileOptimizer

abril 08, 2018 10:43

¿Por qué use C++ Builder con FileOptimizer? desvelaba mi apuesta personal por Embarcadero C++ Builder desde hace más de 20 años. Volvemos a hacer balance de FileOptimizer, porque aunque la psicología evolutiva dice que quitamos importancia a los traumas sufridos en el pasado como simple condición de supervivencia, ello choca con un defecto que tenemos […]

Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

La entrada Galardones de FileOptimizer aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

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

Blog Bitix

Introducción a NIO.2, el sistema de entrada/salida de Java

abril 07, 2018 12:00

Una de las tareas más importante que realizan algunas aplicaciones es el manejo de la entrada y salida ya sea al sistema de ficheros o a la red. Desde las versiones iniciales de Java se ha mejorado soporte añadiendo programación asíncrona de E/S, permitir obtener información de atributos propios del sistema de archivos, reconocimiento de enlaces simbólicos y facilitado de algunas operaciones básicas.

Java

En las primeras versiones de Java el sistema de entrada/salida proporcionado en el paquete java.io era básico. En la versión 1.4 de Java se añadió un nuevo sistema de entrada/salida llamado NIO para suplir algunas de sus deficiencias que posteriormente en Java 7 se mejoró aún más con NIO.2. Entre las mejoras se incluyen permitir navegación de directorios sencillo, soporte para reconocer enlaces simbólicos, leer atributos de ficheros como permisos e información como última fecha de modificación, soporte de entrada/salida asíncrona y soporte para operaciones básicas sobre ficheros como copiar y mover ficheros.

Las clases principales de esta nueva API para el manejo de rutas, ficheros y operaciones de entrada/salida son las siguientes:

  • Path: es una abstracción sobre una ruta de un sistema de ficheros. No tiene porque existir en el sistema de ficheros pero si si cuando se hacen algunas operaciones como la lectura del fichero que representa. Puede usarse como reemplazo completo de java.io.File pero si fuera necesario con los métodos File.toPath() y Path.toFile() se ofrece compatibilidad entre ambas representaciones.
  • Files: es una clase de utilidad con operaciones básicas sobre ficheros.
  • FileSystems: otra clase de utilidad como punto de entrada para obtener referencias a sistemas de archivos.

Con la clase Path se pueden hacer operaciones sobre rutas como obtener la ruta absoluta de un Path relativo o el Path relativo de una ruta absoluta, de cuanto elementos se compone la ruta, obtener el Path padre o una parte de una ruta. Otros métodos interesantes son relativize(), normalize(), toAbsolutePath(), resolve(), startsWith() y endsWith().

Utilizando estas clases expondré algunos ejemplos siendo el primero recorrer el listado de archivos o también se podría hacer el listado de forma recursiva de un directorio e imprimir la información de cada archivo como nombre, si es un enlace simbólico, permisos propietario, fecha de última modificación y tamaño utilizando los siguiente métodos similar a lo que hace el comando ls de GNU/Linux:

Al igual que es posible leer los permisos también es posible establecerlos con el método Files.setPosixFilePermissions().

Las operaciones de crear directorios o archivos, copiar archivos, moverlos y eliminarlos son muy comunes de modo que la clase Files ofrece varios métodos que con una única línea permite hacer estas operaciones de forma sencilla. El siguiente ejemplo crea un archivo, lo copia, lo mueve y finalmente lo elimina.

Para leer el contenido de archivos la clase Files ofrece los métodos newBufferedReader(), newBufferedWrite(), newInputStream() y newOutputStream() junto con otros como readAllLines() y readAllBytes().

En cuanto a la programación de entrada/salida asíncrona se ofrecen dos paradigmas uno basado en la clase Future y otro en funciones de rellamada o callbacks. La programación asíncrona evita bloquear el hilo que ejecuta el código y aprovecha mejor los procesadores multinúcleo con lo que se mejora el rendimiento de las aplicaciones. Para los ficheros se usa la clase AsynchronousFileChannel y para flujos de red AsynchronousSocketChannel.

Si se desea profundizar más en NIO y NIO.2 el libro The Well-Grounded Java Developer dedica un capítulo introductorio en el que me he basado para realizar este artículo, el libro Java I/O, NIO and NIO.2 está completamente dedicado al nuevo sistema de entrada/salida de Java y el tutorial Java Basic I/O también está muy bien como introducción.

En el artículo monitorizar archivos con Java muestro como recibir eventos cuando se añade, elimina o modifica algún archivo de los observados usando la clase WatchService.

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 el comando ./gradlew run.

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

proyectos Ágiles

Master en Agile – MMA 2018

abril 02, 2018 09:57

En octubre de 2018 se iniciará el Barcelona una nueva edición del Master en Métodos Ágiles (MMA) en La Salle (Universitat Ramon Llull). Tras 5 exitosas ediciones del formato anterior en forma de Postgrado y 2 de Máster, y dada la cantidad de conocimiento y áreas en las que se han ido extendiendo los principios ágiles, se ampliaron contenidos y profesorado para transformarlo en Máster, el primero a nivel mundial sobre Agile.

MMA-banner

Es una oportunidad única para aprender de profesionales de primer nivel, con varios años de experiencia específica en Agile, aplicando principios y métodos ágiles en contextos diversos, especializándose en aspectos concretos,  investigando sobre nuevas técnicas y ponentes en conferencias nacionales e incluso internacionales.

Asignaturas Temas Profesores
Fundamentos & Inception Principios y métodos más conocidos (Scrum, Lean, Kanban y XP). Facilitadores e impedimentos. Inception y conceptualización ágil de proyecto. Xavier Albaladejo
Agustín Yagüe
Scrum Estimación y planificación ágil, framework de Scrum, retrospectivas, métricas ágiles, herramientas ágiles físicas, radiadores de información. Tiago Garcez
Silvia Sistaré
Personas y equipos Gestión de personas, gestión de conflictos, motivación e incentivos, facilitación compartida, contratación ágil, coaching de equipos, liderazgo. Steven Wallace

Manuel Lopez

Joserra Díaz

Ingeniería Ágil User eXperience y prototipado en Agile, ALM ágil, eXtreme Programing, Software Craftsmanship, arquitectura ágil, enterprise software development ágil, DevOps, testing ágil. Marc Pifarré

Álvaro García

Pablo Gómez

TDD, BDD, Legacy code Desarrollo guiado por pruebas (de aceptación y unitarias), cómo trabajar con código heredado y reducir la deuda técnica. Carlos Ble
Rubén Bernárdez
Agustín Yagüe
Enterprise Learning & personal efficiency Comunidades de Práctica, Open Spaces, Talent development, gamification, Productividad y aprendizaje personal en Agile. Steven Wallace
Manuel Lopez
Vanesa Tejada
Kanban, Lean & continuous improvement Kanban, Lean Thinking, Lean Software Development, Mejora Continua en Agile Xavier Quesada

Ángel Medinilla

Cultura y Agile Management Tipos de cultura empresarial, gestión del cambio organizativo, Agile Management. Jasmina Nikolic
Jaume Gurt
Gabriel Prat
Lean Startup y Enterprise Product Management Lean Startup, innovación, Design Thinking, Impact Mapping, lanzamiento de startups ágiles, Agile Product Management, Product Portfolio Management, Roadmapping, Budgeting for Agile Gabriel Prat
Ángel Díaz-Maroto
Mattijas Larsson
Scaling Agile & Rollout Escalado (LESS, Spotify, Nexus, SAFe), desescalado y auto-organización empresarial (reinventing organizations, sociocracy, …), contratos ágiles. Adrian Perreau
Fernando Palomo
Xavier Albaladejo
Trabajo Final de Máster Durante el Máster se elaborará un caso práctico de introducción y aplicación de Agile en una empresa, incluyendo la parte de transformación organizativa y de cultura

Algunos comentarios de los alumnos en ediciones anteriores en forma de Postgrado: “Cuando se acaba la clase, estamos ya esperando a que llegue la semana que viene para ver un nuevo tema con el siguiente profesor”. “Muy práctico”. “La calidad y diversidad de puntos de vista entre los profesores le aporta mucho valor”.  Más detalles en: Mejora de la situación laboral los alumnos del PMA trans un año.

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

El Máster tendrá una duración de un año académico y se realizará viernes tarde y sábado por la mañana.

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

–> Ver también cuál ha sido la Mejora de la situación laboral de los alumnos tras un año y los Principales aspectos valorados por los alumnos.

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

Poesía Binaria

Cómo permitir solo la ejecución de una instancia de nuestros scripts

abril 02, 2018 08:21

Aunque me gusta que los programas sean flexibles y nos permitan una ejecución libre y sin restricciones. Además, quiero que permitan editar varios archivos a la vez y realizar múltiples conexiones. Hay software o scripts en los que debemos asegurar que sólo se haga una ejecución simultánea ante diferentes condiciones. Por ejemplo, si es un script para realizar copias de seguridad de nuestro sistema, tal vez nos interese que sólo se pueda lanzar una vez, porque sería un problema que se realicen dos copias de seguridad a la vez. Si recopilamos información de salud del sistema, podemos empeorar dicha salud si estamos recopilando varias veces esos datos, podríamos empeorar el rendimiento del sistema. O si estamos realizando una captura de pantalla a través de un script nuestro, puede ser contraproducente lanzar el mismo programa de forma simultánea varias veces.

Así que, en principio, tenemos que tener claro a qué nivel queremos restringir la ejecución de las instancias de nuestro programa. Es decir, ¿sólo permitimos una ejecución en todo el sistema? ¿una por usuario? ¿una por usuario y directorio de trabajo? Nosotros debemos definir la restricción de acuerdo a nuestras necesidades.

Una ejecución a nivel de sistema

Como siempre, hay muchas formas de complicarnos la vida con estos scripts. Ya depende de lo grande que sea nuestro script y la precisión u opciones que necesitemos. Empezaremos con algo muy sencillo, utilizando el comando pidof para buscar el número de instancias con el mismo nombre que el programa actual. Si ejecutamos, por ejemplo:

pidof -x firefox
6249 6178 6068 5979

Este comando nos devolverá los PIDs de los procesos que se llaman firefox. Y, por supuesto, podemos contar el número de comandos que hay (con wc -w). Del mismo modo podemos construir un script en bash que busque entre todos los programas en ejecución los que se llaman como él y decida detener la ejecución si hay más de uno:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

MYPID=$$
MYNAME=$0
PROCS=$(pidof -o "$MYPID" -x "$(basename "$MYNAME")")
echo "Inicio---"
echo $PROCS | wc -w

if [ "$(echo $PROCS | wc -w)" -gt 0 ]; then
     echo "Ya hay una instancia en ejecución. Saliendo"
     exit;
fi

echo "Hago una espera para simular que el script hace cosas, y que tarda un poco."
echo "Nos dará algo de tiempo para iniciar más ejecuciones del mismo script"
sleep 10
echo "El script ha terminado"
echo " ---------------- "

En este caso nuestra ejecución de pidof es más compleja, podíamos hacer la misma petición de antes (sin -o “$MYPID”), pero me gusta cómo queda el código así. En este caso, le estamos diciendo a pidof que excluya de la lista de PIDs que nos devuelve la PID actual. Luego, en la sentencia condicional if miramos si este número es mayor que 0 (-gt 0). Si lo es, ya existe un proceso que se llama como el proceso actual (sacamos el nombre con $(basename $0), así cogemos el nombre del archivo que se ha ejecutado, lo podemos poner a mano, pero así es más genérico). En caso de no incluir -o “$MYPID” en la línea de pidof, deberíamos comparar con (-gt 1), ya que pidof en este caso siempre va a devolver al menos una PID, la del proceso actual.

En este ejemplo vemos que se ha colocado primero el resultado de pidof en una variable. Podríamos hacer la sentencia de golpe (pidof … | wc …), pero algunos intérpretes como Bash, internamente cuando encuentran el pipe (|), hacen un fork del proceso actual, es como si lo duplicaran, de forma que justo en el momento en el que estamos mirando los procesos, hay un proceso más con el mismo nombre. Podríamos, por ejemplo, añadir un número más en la comparación (-gt 1 en lugar de -gt 0; o -gt 2 en lugar de -gt 1), pero al ser un tema de comportamiento interno del intérprete, puede que no funcione bien en futuras versiones o en otros intérpretes.

El principal problema que encontramos en este enfoque es que buscamos procesos por su nombre. Y cualquiera podría crear un proceso con el mismo nombre, tenerlo en ejecución siempre y evitaría que pudiéramos ejecutar el programa (intencionadamente o sin querer). Por otro lado, esto sería a nivel de sistema, es decir, no podríamos establecer restricciones tipo una instancia por usuario. También es lento, bueno, aunque el script se ejecuta muy rápido, no es eficiente. Para obtener las PID de los procesos que se llaman igual, pidof, internamente mira todas las PID de todos los procesos y hace una comparación con una cadena de caracteres, lo que pueden ser varios millones de operaciones. Además, esto puede traernos problemas, ya que la ejecución de pidof tarda un tiempo y si el programa se ejecuta dos veces muy seguidas una de otra, pueden pasar tres cosas (y no podemos controlar el resultado):

  • Que todo vaya bien y funcione perfectamente. Es el caso ideal, pero un fallo es probable.
  • Que los dos procesos detecten al otro proceso y ninguno de los dos se inicie.
  • Que ningún proceso detecte otro proceso y se inicie dos veces.

De todas formas, este es un buen método, y muy rápido de programar, sin complicarnos la vida.

Directorio o fichero de bloqueo

Otra opción es crear un directorio o fichero de bloqueo. Por un lado, es más rápido crear directorios, aunque en los ficheros podemos escribir dentro (vale, dentro de los directorios también podemos escribir, pero tendremos que crear archivos y para leer un valor debemos primero comprobar que un fichero existe y nos eternizaríamos demasiado). Esta primera aproximación la haremos utilizando comandos básicos, y la usaremos cuando no podamos utilizar flock (la siguiente opción), ya sea por tratarse de un sistema empotrado con versiones reducidas del sistema, o tengamos alguna incompatibilidad.
Esta aproximación nos permitirá:

  • Establecer varios tipos de bloqueo: a nivel de sistema, usuario o con el criterio que queramos, cambiando el nombre del directorio.
  • La probabilidad de tener problemas de condición de carrera como antes es menor, pero existe.
  • El bloqueo depende del fichero o directorio y no del nombre de proceso. Por lo que varios procesos pueden utilizar el mismo bloqueo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

LOCKDIR="/tmp/myprocess_"$(whoami)

if [ -d "$LOCKDIR" ]; then
     echo "Ya hay una instancia en ejecución. Saliendo"
     exit;
fi
trap "rmdir $LOCKDIR" EXIT
mkdir "$LOCKDIR"

echo "Creando un proceso que hace muchas operaciones durante un tiempo"
sleep 10
echo "El script ha terminado"
echo " ---------------- "

En este ejemplo, lo primero que hacemos es crear un nombre de directorio de bloqueo ($LOCKDIR). Si éste existe, no dejamos pasar y cerramos el programa. Si no existe, creamos el directorio y hacemos que a la salida se borre (con trap … EXIT capturamos la salida del programa y ejecutamos un comando). Lo malo es que si matamos el proceso (nosotros o el sistema operativo debido a un fallo), nos vemos obligados a borrar manualmente el directorio /tmp/myprocess_USUARIO para poder volver a ejecutar el programa.

File locks

Llegamos hasta un comando con mucha potencia a nivel de sistema operativo. Con este programa podemos gestionar la ejecución de órdenes a partir de un bloqueo hecho sobre un fichero o directorio que gestionará si podemos o no podemos ejecutar el programa. (Atómico?)

Lo más sencillo es probarlo en dos terminales. Aquí comprobaremos los diversos modos de trabajo:

flock /tmp/bloqueo -c “echo INICIO; sleep 10; echo FIN”

Con esto, estableceremos un bloqueo sobre /tmp/bloqueo (un nombre de archivo que acabamos de inventarnos) y hacemos que cuando esté desbloqueado, escribamos en pantalla “INICIO”, luego esperemos 10 segundos y finalmente escribamos “FIN” en pantalla. Algo muy sencillo, pero en el mundo real, en lugar de una espera de 10 segundos, podremos ejecutar una tarea que sea peligrosa, y no pueda ser hecha por varios procesos a la vez. Pensemos en la lectura/escritura de ficheros o dispositivos, acceso por red a un determinado recurso, etc; o, como es nuestro caso, a la ejecución de un script que no puede ser ejecutado dos veces simultáneas.

El experimento que quiero llevar a cabo es el siguiente. Mientras transcurren los 10 segundos de espera del script anterior, debemos ejecutar, entro terminal lo siguiente:

flock /tmp/bloqueo -c “echo \”HOLA MUNDO\””

De forma que, cuando el recurso /tmp/bloqueo no esté bloqueado, se escriba en pantalla HOLA MUNDO. De esta forma veremos que HOLA MUNDO no se escribirá inmediatamente, sino cuando concluya la espera de 10 segundos del otro terminal. Hemos establecido, una forma de comunicación entre dos procesos diferentes, en forma de bloqueo. Aunque, como vemos ahora, el segundo proceso esperará de forma indefinida a que el primero termine. Y, el primero puede no terminar nunca. ¿Por qué? Bien porque se haya quedado el proceso colgado, o porque hayamos matado de mala manera el proceso, o simplemente el proceso está tardando mucho y debemos cancelar la espera y ponernos a otra cosa. Cosas que pasan en el mundo real. Esto lo podemos hacer de la siguiente manera:

flock -w 3 /tmp/bloqueo -c “echo \”HOLA MUNDO\””

De esta forma, flock esperará durante 3 segundos (también podemos poner fracciones de segundo) antes de darse por vencido y no ejecutar nada. Es decir, si antes de 3 segundos, el recurso se libera, escribiremos HOLA MUNDO; si no, no haremos nada, simplemente saldremos. Además, devolveremos un código de salida, por defecto, el código será 0 si se ha podido coger el recurso y ejecutar la orden (el hola mundo), de lo contrario, devolverá 1. Aunque este valor lo podemos personalizar con -E valor. Para ver el código de salida podemos hacer:

flock -w 3 /tmp/bloqueo -c “echo \”HOLA MUNDO\””
echo $?
1

Ahora, integremos flock en nuestro script. De manera que podamos ejecutar un proceso por usuario del sistema:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash

LOCKFILE="/tmp/myprocess_"$(whoami)
MYPID=$$

function exit_error() {
    echo "Ya hay una instancia en ejecución. Saliendo"
    exit 1
}

(
    flock -n 3 || exit_error
    echo "Creando un proceso que hace muchas operaciones durante un tiempo"
    sleep 10
    echo "El script ha terminado"
    echo " ---------------- "
) 3> "$LOCKFILE"

Con el uso de flock no tenemos que preocuparnos por los posibles problemas de desbloqueo ni condiciones de carrera. Es decir, el sistema operativo se encargará de gestionar los recursos bloqueados, en este caso, nuestro fichero de bloqueo (/tmp/myprocess_USUARIO). Si por algún casual el proceso muere, el núcleo del sistema operativo se encargará de eliminar el bloqueo para que otro proceso ocupe su lugar, lo mismo pasa cuando el proceso termina. Además, si dos procesos llaman a flock a la vez, sólo uno obtendrá el bloqueo exclusivo.

En este caso, en lugar de vincular el bloqueo a un archivo directamente, lo hemos vinculado a un descriptor (un descriptor no es más que un número con el que nos referimos al recurso que queremos utilizar, porque para un ordenador es mucho más fácil manejar números). Y dicho descriptor (el 3, porque yo lo valgo, pero que puede ser un número mayor a 2 porque 0, 1 y 2 están pillados; aunque podemos poner el 100 por ejemplo) lo relacionamos con el archivo en cuestión.

Aunque esta sintaxis puede ser un poco liosa y confusa. A mí me gusta más llamar a una función de bloqueo (y quitarme del medio los paréntesis, y el número 3 escrito en varias partes del programa, porque me gustaría más ponerlo en forma de variable pero en la última línea no podremos ponerlo). Así que, vamos a modificar un poco el código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash

LOCKFILE="/tmp/myprocess_"$(whoami)
LOCKFD=150
MYPID=$$

function lock() {
    echo {LOCKFD}>$LOCKFILE
    flock -n $LOCKFD
}

function exit_error() {
    echo "Ya hay una instancia en ejecución. Saliendo"
    exit 1
}

lock || exit_error

echo "Creando un proceso que hace muchas operaciones durante un tiempo"
sleep 10
echo "El script ha terminado"
echo " ---------------- "

La clave aquí está en que la función lock crea el archivo vinculado al descriptor $LOCKFD (¡lo he sacado a una variable aparte!), aunque la forma de hacerlo aquí con echo {LOCKFD}>$LOCKFILE requiere Bash 4.1. Si lo queremos hacer con versiones anteriores debemos recurrir a eval (que personalmente, prefiero evitar). Luego hacemos el flock en el que nos aseguramos de devolver 0 si todo va bien y 1 si algo va mal. De esta forma, la función lock se basta y se sobra para establecer el bloqueo y en el resto del código no nos tenemos que preocupar de nada más.

Detección de scripts sobas

Algo que puede pasar mientras ejecutamos los scripts es que uno tarde demasiado en finalizar. Por ejemplo, lanzamos un script de copia de seguridad, que lo mismo puede estar 4 o 5 horas en ejecución si tenemos muchos datos, o si estamos comprimiendo o codificando nuestras backups. Así que, vamos a incluir en nuestro script un pequeño aviso del tiempo que lleva el otro proceso ejecutándose.

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
#!/bin/bash

readonly LOCKFILE="/tmp/myprocess_"$(whoami)
LOCKFD=150
readonly MYPID="$$"
readonly MYNAME="$(basename "$0")"

function lock() {
    local PID="$(cat $LOCKFILE)"
    echo {LOCKFD}<>$LOCKFILE

    flock -n $LOCKFD
    local STATUS=$?

    if [ $STATUS = 0 ]; then
        # Escribimos nuestra PID
        echo $MYPID >&${LOCKFD}
        return 0
    else
        local PROCNAME="$(cat /proc/$PID/comm 2>/dev/null)"
        if [ "$PROCNAME" != "$MYNAME" ]; then
            echo "Error, el proceso ejecutado no coincide con el que debe"
            exit 1
        fi
        local FROMTIME=$(awk -v ticks="$(getconf CLK_TCK)" -v epoch="$(date  %s)" '
            NR==1 { now=$1; next }
            END { printf "%9.0f", epoch - (now-($20/ticks)) }'
/proc/uptime RS=')' /proc/$PID/stat | xargs -i date  "%d/%m/%Y %H:%M:%S" -d @{})
        echo "El proceso $PID ($PROCNAME) lleva abierto desde $FROMTIME"
        return 1
    fi
}

function exit_error() {
    echo "Ya hay una instancia en ejecución. Saliendo"
    exit 1
}

lock || exit_error

echo "Creando un proceso que hace muchas operaciones durante un tiempo"
sleep 10
echo "El script ha terminado"
echo " ---------------- "

En este caso, escribimos la PID del proceso que bloquea el recurso en el mismo fichero de recurso (podríamos utilizar otro, pero bueno). Entonces, cuando falla el establecimiento del bloqueo, buscamos el nombre del proceso con la PID que hay escrita y a partir de ahí averiguamos cuándo se inició el mismo (tenemos que mirar en stat y analizar el resultado, por eso el script de awk). Luego, presentamos en pantalla cuándo se inició dicho proceso.

Conclusión

Dependiendo de nuestro script podremos utilizar una u otra solución o, incluso otra totalmente diferente. Aunque es importante asegurarse que cuando nuestro script esté utilizando recursos o elementos que no deban ser utilizados simultáneamente, hagamos un bloqueo antes de causar un desastre. Imaginad por ejemplo que inicio dos veces una copia de seguridad diaria de base de datos que escribe sobre el mismo fichero o que empiezo el envío de un mailing dos veces, antes de que se marque como enviado. Esto puede dar lugar a errores en copias de seguridad o envíos de correo no deseado, o correos duplicados que, pueden causar que muchos usuarios se den de baja de nuestra lista de distribución.

¿Qué método prefieres para bloquear tu script?

Foto principal: unsplash-logoMILKOVÍ

The post Cómo permitir solo la ejecución de una instancia de nuestros scripts appeared first on Poesía Binaria.

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

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

¿Por qué C++ Builder?

abril 02, 2018 07:40

Empecé usando C++ Builder en 1996 con la versión 1.0, muchos lo conocíamos todavía como Ebony, el nombre en clave del producto. Desde entonces, he sido un apasionado de este entorno de desarrollo, en realidad ya lo era antes con Turbo C, lo que no dejan de ser un argumento más en favor de mi […]

Artículo publicado originalmente en Bitácora de Javier Gutiérrez Chamorro (Guti)

La entrada ¿Por qué C++ Builder? aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

» 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

Sponsors

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