Weblogs Código

Poesía Binaria

¡Batalla de VPS! Clouding.io VS DigitalOcean VS Amazon Web Services

noviembre 20, 2017 04:15

Comparativa Clouding, Amazon AWS, DigitalOcean

¡Vamos a probar servicios VPS! En concreto voy a analizar tres empresas con las que he tenido experiencia: Amazon Web Services, DigitalOcean y Clouding.io. Veremos algunas de sus características y cuál de ellos elegir en nuestro caso para que nuestro proyecto tenga éxito. Además de una comparativa práctica analizando los planes más básicos de cada uno de ellos (que nos pueden salir gratis durante un tiempo).

Presentamos el duelo

Actualmente la oferta de servidores VPS es grande y muchas compañías se han sumado ofreciendo su versión personal. Intentando ofrecer productos novedosos y a precios asequibles tanto a particulares que quieran alojar sus páginas, servicios, curriculum o vida digital en Internet así como a empresas, para alojar su web, correo u ofrecer sus servicios a través de la red.

En este post vamos a analizar tres empresas: Clouding.io, Digital Ocean y Amazon Web Services.

Clouding.io

Es una empresa española con sede en Barcelona dedicada, desde 2014, a proporcionar a particulares y empresas soluciones VPS económicas, sencillas y adaptables al usuario. Con datacenters en España, y soporte en español. Desde el principio destaca su capacidad de crear un VPS con memoria, núcleos, disco (SSD) y Sistema Operativo que queramos. Tienen disponibles varias versiones CentOS, Debian, Ubuntu y Windows Server disponibles, así como aplicaciones como Magento, Prestashop o Plesk con las que no tendrás que complicarte demasiado la vida y tendrás un entorno listo y configurado. Las aplicaciones o sistemas con licencia encarecen el precio mensual.

Clouding.io también proporciona un servicio de copias de seguridad automáticas para que no tengas que preocuparte en caso de desastre, firewall con el que puedes configurar los puertos de tu servidor que se pueden ver desde el exterior, servidor DNS para configurar dónde apuntan tus dominios y red privada entre servidores.

Desde la semana pasada está disponible la funcionalidad de los Snapshots, con los que puedes tener instantáneas de tu Servidor en cuestión de segundos. Que nos pueden resultar muy útiles a la hora de hacer cambios o actualizaciones en nuestro VPS. Los podemos utilizar para revertir cambios en un servidor cuando estemos realizando tareas delicadas o para lanzar nuevos servidores con la misma configuración.

Sus planes van desde 1 núcleo de CPU, 1GB de RAM y 25GB de disco SSD desde 11.92€/mes (o 0.0163€/hora IVA incluido), aunque puedes aumentar y disminuir tu máquina cuando quieras dependiendo de cuánto la utilices y así ajustar tus costes, teniendo en cuenta que lógicamente no puedes reducir tu almacenamiento para evitar pérdida de datos. La máquina debe reiniciarse para adoptar la nueva configuración, pero no tarda mucho.

Además, el disco SSD de Clouding.io es redundante, consta de triple réplica. Todos tus datos se guardarán en un cluster de almacenamiento compartido en diferentes hosts, lo que hace muy improbable la pérdida de datos debido a fallos de hardware,

Digital Ocean

Comenzaron sus andanzas en 2011, pero no fue hasta 2013 cuando se convirtieron en una de las primeras empresas en ofrecer servidores VPS con discos SSD. Dividen su oferta en packs fijos, es decir, no podemos personalizar los núcleos, CPU y RAM a nuestro gusto, sino que elegimos el paquete que más nos conviene, aunque podemos aumentarlo en cualquier momento. Sólo ofrecen sistemas operativos libres como Ubuntu, Debian, CentOS, Fedora, CoreOS o FreeBSD en varias versiones, aplicaciones listas para ser usadas como WordPress y entornos LAMP, LEMP, RoR o MEAN entre otros.

Como servicios extra proporcionan copias de seguridad automáticas, monitorización, red privada, DNS, firewall, IPs flotantes, almacenamiento extra y almacenamiento de objetos, entre otros.

Sus planes van desde 1 núcleo de CPU con 512MB de RAM y 20GB de SSD desde $6.05/mes, a día 28 de Octubre de 2017, 5.21€/mes (o 0.0072€/hora IVA incluido). Y dejan elegir el centro de datos donde estará nuestro VPS entre diversos países de Europa, América o Asia.

Amazon Web Services o AWS

Empezaron en 2006 y son uno de los proveedores más grandes del mundo y personalizables que existen. Tienen una oferta de servidores grandísima y puedes hacer cualquier cosa imaginable con ellos. Hablando de VPS, tenemos que hablar de su servicio EC2 con el que podemos contratar máquinas con una determinada CPU y RAM, a las que tendremos que enchufar discos duros, que pueden ser magnéticos o SSD (LBS) y dichos ordenadores tendrán que tener tarjetas de red para estar conectados a una red VPC. Todo esto, podemos configurarlo de forma sencilla, porque por defecto viene muy bien. Pero no nos evita tener que leer mucha documentación para saber cómo funcionan las cosas, cosas que podremos complicar hasta decir basta.

Otros servicios incluidos en AWS son DNS, almacenamiento de objetos, arquitecturas Serverless, servicios administrados, notificaciones, envío de correo masivo, monitorización y mucho más.

La flexibilidad de sus servicios es su gran punto a favor y su gran desventaja. A veces se te pueden escapar costes ocultos en transferencia de datos o algún servicio que has contratado sin leer mucho. También tienen muchos planes de pago anticipado (puedes pagar un servidor de forma anticipada durante 3 años) con los que te puedes ahorrar dinero. Aunque incluyen una calculadora de costes y algunos planes gratuitos, a veces es complicado utilizarlos. Además, he de destacar que el soporte gratuito es muy lento, pudiendo tardar varios días y recibiendo una respuesta muy escueta. Disponen de un servicio de soporte, pero hay que pagarlo aparte.

Aunque no tienen planes como Clouding.io o DigitalOcean, podemos contratar un VPS con 1 núcleo y 512MB de RAM (t2.nano, no indicado para usar la CPU muy intensamente porque baja su rendimiento) al que le podemos conectar un disco SSD de 20GB por ($7.71/mes o 6.64€/mes, por un lado pagaremos $0.0058/hora y por otro $0.11 por GB al mes). Para aumentar el servicio deberíamos crear una máquina más grande y enchufar el disco duro de la máquina anterior. También podemos crear un disco duro más grande (o más pequeño) que el que tenemos, copiar lo que necesitamos del disco principal y convertir en principal el nuevo disco duro.

En este caso podría haber introducido en la comparativa Amazon Lightsail, que es el servicio básico y de bajo coste de Amazon, pero he decidido incluir AWS porque es con éste con el que tengo experiencia.

¿Qué necesito?

Si queremos alojar una web o un blog, por supuesto que necesitamos un servidor que esté conectado las 24h a Internet. Lo grande que deba ser ese servidor irá en función de lo pesada que sea nuestra web. Por ejemplo, si tenemos muchas imágenes, fotografías o vídeo debemos contratar espacio en consecuencia, o si tenemos muchas visitas y la generación de las páginas es compleja (por ejemplo tenemos un WordPress cargado de plugins), necesitaremos más memoria y CPU. Puede que a priori podamos decantarnos por un servicio de hospedaje compartido, éste será mucho más sencillo de administrar, pero seguramente no tengamos libertad para configurar el servidor o instalar programas. Al mismo tiempo, si eres desarrollador te interesará tener una configuración muy específica para que funcionen tus servicios y para garantizar una disponibilidad y seguridad apropiadas.

También es muy importante la ubicación de los centros de datos. Si vamos a proporcionar nuestros servicios dentro de España, podríamos elegir Clouding.io puesto que tienen datacenters en España y, como la IP es española puede que tengamos algo mejor SEO. Además, si manejamos datos personales europeos, nos interesará que estos datos se almacenen en países de Europa.

Debemos plantearnos también la contratación de servicios extra. Es decir, aunque en mi VPS puedo montar lo que yo quiera, existe software de monitorización, copias de seguridad, servidores de correo, balanceadores de carga, bases de datos, firewalls, routers, contenedores, etc, hemos de analizar el coste de su montaje y mantenimiento y compararlo con la contratación del servicio en nuestro proveedor. Es decir, si instalar y mantener el servicio de copias de seguridad me va a costar 1€ de almacenamiento y 8h de un empleado al mes, puede que me interese contratar un servicio de 4€ al mes. Aunque también puede que seas de los artesanos que les gusta montarse todo ellos mismos.

Quiero analizar ahora algunos de los aspectos que considero importantes en mi experiencia con estas tres empresas.

Firewall

Nos permitirán activar y desactivar los puertos que se verán desde el exterior en nuestros servidores. Normalmente, para que nuestros programas funcionen habrá servicios públicos y privados y estos últimos no deben poder ser accedidos desde fuera. Los tres proveedores nos permiten cambiarlos de forma fácil, añadiendo y eliminando puertos o rangos de puertos para determinadas direcciones IP. Ya que, puede que queramos que nuestra IP de casa pueda acceder a puertos de administración de nuestro servidor y así ocultar el servicio al exterior. De todas formas, aunque podemos hacer esto desde el panel de control de cualquier proveedor, dado que en nuestro VPS podemos instalar el sistema que queramos y configurarlo a nuestro gusto, siempre nos podemos complicar la vida un poco más.

Aumentar y disminuir el servicio

Nuestras necesidades no son las mismas todo el tiempo. Tal vez nuestra empresa haya lanzado una campaña de publicidad masiva y debamos esperar muchas visitas durante unas semanas, aunque con el tiempo se normalizará. Así que hemos visto que en estos proveedores aumentar las máquinas es relativamente fácil, pero no disminuirlas cuando baje la marea. Por ejemplo, Digital Ocean deberá aumentar el disco, CPU y RAM a la vez al estar planteado en planes, y en el futuro será difícil volver a como estábamos antes; en Amazon, tendremos que cambiar la máquina principal, y si nos hemos leído la documentación no tiene por qué ir nada mal; pero en Clouding sólo tendríamos que aumentar CPU y RAM para volver a bajar dichos valores más adelante. Tenemos que medir el coste de este tipo de cambios tanto con el precio del servicio como en el tiempo invertido en su realización y mantenimiento.

Por otro lado, podríamos montar un VPS como balanceador de carga al que, en un momento añadamos más servidores esclavos. Algunos como AWS o Digital Ocean nos van a proporcionar el balanceador de carga, aunque en cualquiera de ellos podremos instalar uno y configurarlo a mano.

API

Proveedores como Digital Ocean y AWS nos proporcionan una API con la que podemos interactuar para añadir, eliminar, analizar o modificar servicios a nuestro gusto. Requiere algo de programación y leer manuales pero nos permite automatizar muchas tareas, ahorrar tiempo y dinero.

Clouding.io lanzará su propia API en 2018.

Certificación de datacenter

Tanto si es un proyecto personal como profesional necesitamos el hospedaje de mayor calidad. Y así asegurar la mayor disponibilidad y tolerancia a errores. Clouding.io tiene el certificado TIER IV del Uptime institute lo que garantiza una disponibilidad del 99.995%. AWS o Digital Ocean no tienen certificación Tier, aunque los primeros aseguran tener algo equivalente a Tier III+ (99.99% asegurado). Por parte de Digital Ocean, muchos de los centros de datos con los que trabajan tienen certificación Tier III o IV, aunque habría que buscar dicha información en el centro de datos con el que trabajamos si nos la piden para un tema empresarial. De todas formas, Digital Ocean asegura en su SLA el 99.99% de disponibilidad.

Red pública y privada

Es común que nuestros proyectos deban ser procesados en varios servidores. Separando, por ejemplo, servidor web, de base de datos, almacenamiento o caché; creando una infraestructura compleja. El problema viene cuando queremos que algunos servidores no sean accedidos por el público. Es decir, no queremos que nuestro servidor de base de datos, con toda nuestra información, sea accesible desde Internet. Pero nuestro servidor web sí que se podrá conectar y pedir información. Imaginemos que nuestro servidor web tiene dos tarjetas de red, una de ellas conectada a Internet y la otra conectada al servidor de base de datos.

Con AWS sí podemos crear este tipo de configuraciones. Podemos hacer que un VPS no sea accesible desde Internet al no tener IP pública y debamos trabajar con él a través de un servidor puente (el web, en este caso). Pero tanto en Clouding.io como Digital Ocean, todas nuestras máquinas tendrán una IP pública, aunque siempre podremos configurar los firewalls para no aceptar ninguna conexión desde el exterior y sólo aceptar conexiones internas.

Por su parte, Clouding.io tiene una VLAN privada por cliente. Y esto simplificará las reglas de nuestro firewall interno.

Resolución inversa

Nuestras máquinas tendrán un nombre, que podrá coincidir con nuestro dominio. Pero también hay una forma de preguntarle a nuestra IP a qué host pertenece:

host 123.123.132.123
123.132.123.123.in-addr.arpa domain name pointer midominio.com

Esto es muy importante, por ejemplo si nos montamos nuestro servidor de correo, para evitar que nuestros mensajes caigan en la bandeja de correo no deseado, ya que los servidores de destino verifican si la IP desde la que viene un mensaje pertenece al dominio al que dice pertenecer. Del mismo modo, otros sistemas de verificación pueden verificar así que dicha IP pertenece a un servicio tuyo.
Para ello es importante que nuestro proveedor nos permita establecer dicha resolución inversa. Sólo Clouding.io nos permite hacerlo desde el panel de control, aunque AWS y Digital Ocean permiten hacerlo si contactamos con ellos, justificando por qué queremos hacerlo y puede tardar hasta 3 días.

Cambio de IP

Nuestros servicios no siempre serán los mismos. A veces empezamos contratando un servidor pequeño que, poco a poco vamos ampliando. Otras veces el proceso de ampliación requiere varios días y podemos querer trabajar en un VPS nuevo desde cero y, cuando todo esté listo, hacemos que la IP de nuestros servicios no apunte al viejo servidor, sino al nuevo.

En AWS podemos hacerlo, desenchufamos la IP de un servidor y la enchufamos en otro. En Digital Ocean desde hace algo más de un año, podemos utilizar su servicio de IPs flotantes (floating IPs) que nos permite especificar a qué servidor apunta una determinada IP. En Clouding, por el momento no podemos hacerlo, por lo que tendríamos que avisar de nuestras intenciones al soporte técnico o cambiar la IP en el DNS de nuestro dominio (si son pocos dominios no hay problema, lo malo es cuando tienes más de 20). Clouding.io tampoco garantiza que te vuelvan a dar la misma IP de un servidor que has destruido.

De cualquier modo, cuando nuestra infraestructura está creciendo es conveniente hacer un VPS que actúe como firewall, filtro de seguridad y que redirija el tráfico a servidores internos. De este modo será mucho más fácil sustituir servidores.

Coste de servidores apagados

Cuando tengo mi VPS apagado, ¿cuesta dinero? Es decir, mi servicio no está consumiendo CPU, ni RAM en este momento. Tanto Clouding.io como DigitalOcean te pueden estar cobrando por ese servidor. Y es algo totalmente normal. Por un lado, tienes tu instancia reservada, como es un VPS, tu servidor es sólo un trozo de una máquina mucho más grande, pero el proveedor te tiene que garantizar que no vendrán otros clientes, ocuparán todo el servidor y cuando vayas a arrancarlo no haya CPU disponible para ti. Por otro lado está la dirección IP, que la tienes reservada. Y, por último, el disco duro, tus datos están almacenados en algún lado y tienes varios GB que son tuyos. Por este motivo, si no vas a utilizar más una máquina, asegúrate de destruirla, o si vas a utilizarla más adelante pero no quieres pagar, haz una copia de seguridad, y cárgatela.

Otros, como AWS te cobrarán menos por las máquinas paradas. Es más, si tienes direcciones IPs reservadas sin asignar te cobrarán por ellas. También te cobrarán por el almacenamiento, pero no lo harán por las máquinas apagadas.

Dentro de muy poco, en 2018, Clouding.io lanzará la característica del archivado de VMs. Cuando la Máquina Virtual esté archivada, algo equivalente a apagada, sólo cobrarán el disco SSD, no la RAM ni la CPU.

Probando los servidores

Por último, antes de decantarnos por un servicio u otro, he decidido utilizar los créditos gratuitos que ofrece cada proveedor para hacer algunas pruebas. En los tres casos he cogido el plan más barato que ofrecen. Y el centro de datos, en el caso de Clouding.io está en Barcelona; AWS en Dublín (Irlanda) y DigitalOcean en Frankfurt (Alemania).

Ping

Vamos a ver el tiempo de respuesta del servidor para ello hacemos ping a la IP de nuestro servidor, he esperado 3 resultados y he hecho la media. En mi caso, estos son los resultados:
Clouding.io: 25ms
DigitaslOcean: 44ms
AWS: 46ms

Depende de la zona geográfica desde la que intentemos acceder. Y, como es lógico, para un servicio en España, el centro de datos más cercano es el que menos tarda en responder.

Procesador

Todos los proveedores hablan de núcleos de procesador. Pero las arquitecturas van cambiando con el tiempo. Así que vamos a analizar el procesador que acompaña cada servicio. Para ello he mirado el archivo /proc/cpuinfo, aunque sólo sea la información que nos da el hipervisor, podemos hacernos una idea. Eso sí, estos tests están hechos a finales de octubre de 2017, puede que cuando contrates un nuevo VPS en algún proveedor, éste haya mejorado su hardware ligeramente.

DigitalOcean: Intel(R) Xeon(R) CPU E5-2650L v4 a 2199.9MHz y 30MB de caché. 4399.99 bogomips.
Clouding.io: Intel Xeon E3-12xx v2 (Ivy Bridge) a 2199.9MHz y 16MB de caché. 4399.99 bogomips.
AWS: Intel(R) Xeon(R) CPU E5-2676 v3 a 2400MHz y 30MB de caché. 4800 bogomips. (pero va por créditos y no podemos usar la CPU al 100% durante mucho tiempo seguido).

Tampoco nos podemos fiar mucho de la lectura del procesador que veamos aquí, puesto que será la información que el hipervisor quiere que veamos. Pero es un punto de referencia.

Veamos ahora cómo se comportan estos sistemas. Para ello, instalamos sysbench en cada uno de ellos y vemos los resultados.

sysbench --test=cpu run

Este test estresará un poco la CPU y mediremos cuánto tarda cada uno de los VPS en calcular 10000 números primos (el que menos tiempo tarda gana).
Clouding.io: 10.5054s (min=0.78ms; max=1.85ms; avg=1.05ms)
AWS: 11.5947s (min=1.05ms; max=1.36; avg=1.16ms)
DigitalOcean: 11.6888s (min=1.11ms; max=2.45ms; avg=1.17ms)

Memoria

Ahora, vamos a pasar un test de lectura y escritura en RAM, ya que es tan importante (o más) el comportamiento de la memoria que el del procesador.

Lectura

sysbench --test=memory run

El test por defecto hace una lectura de 100GB de memoria y mide cuánto tarda en hacerlo (el que menos tarda gana).
Clouding.io: 50.6292s (2022.55MB/s)
DigitalOcean: 55.7104s (1838MB/s)
AWS: 68.7397s (1489.68MB/s)

Escritura

Ahora la clave será escribir en la RAM.

sysbench --test=memory --memory-oper=write run

Clouding.io: 47.4001s (2160.33MB/s)
DigitalOcean: 55.4930s (1845.28MB/s)
AWS: 68.3684s (1497.77MB/s)

Sistema de archivos

Los tres servicios que estamos comparando utilizan discos SSD. Pero, claro, hay muchos tipos de SSD, los hay más rápidos y más lentos. Aquí también entrarán en juego los posibles cachés que tenga la máquina y no sólo el comportamiento del disco. Eso está bien, al final evaluamos cómo se comportará nuestro VPS con una aplicación real. He hecho varios tests, seqrd (lectura secuencial), seqwr (escritura secuencial) y rndrw (lectura y escritura aleatoria).

Tenemos que tener en cuenta el tipo de almacenamiento que utilizan los diferentes proveedores:

  • Clouding.io usa un cluster de almacenamiento en red y, como vimos antes, con triple replicación.
  • AWS utiliza EBS (Elastic Block Storage) que también es un almacenamiento en equipos remotos.
  • DigitalOcean no especifica si utiliza almacenamiento remoto o no. Sólo especifican en algunos foros que utilizan una combinación de varios tipos de RAID. Aunque recientemente han sacado un servicio de almacenamiento de objetos que sí utiliza un tipo de almacenamiento remoto.

¡Veamos qué tal se comportan!

sysbench --test=fileio --file-total-size=4G prepare
sysbench --test=fileio --file-test-mode=TEST --file-total-size=4G --max-requests=20000 run
sysbench --test=fileio --file-total-size=4G cleanup
Lectura secuencial Escritura secuencial L/E aleatoria
DigitalOcean: 3.2280s (1.24GB/s) 6.2885s (651.35MB/s) 2.4857s (125.72MB/s)
Clouding.io: 26.7962s (152.86MB/s) 20.1470s (203.31MB/s) 18.6981s (16.71MB/s)
AWS: 62.7478s (65.28MB/s) 66.2642s (61.81MB/s) 6.5534s (47.68MB/s)

Es tu oportunidad de elegir

¿No te decides con estas pruebas? Anímate y prueba los servicios de forma gratuita. Puedes aprovechar para hacer pruebas de rendimiento o alojar una aplicación real para ver qué tal se comporta. No olvidéis dejarme un comentario con vuestras impresiones.

The post ¡Batalla de VPS! Clouding.io VS DigitalOcean VS Amazon Web Services appeared first on Poesía Binaria.

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

Variable not found

Enlaces interesantes 301

noviembre 20, 2017 07: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

Web Applications with ASP.NET Core Architecture and Patterns guidance (Updated for ASP.NET Core 2)

Azure / Cloud

    Data

    HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Xamarin

    Otros

    Publicado en Variable not found.

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

    Koalite

    Cómo acceder a recursos nativos desde una aplicación web

    noviembre 20, 2017 05:06

    Hoy en día es cada vez más habitual utilizar tecnologías propias de la web para desarrollar interfaces de usuario. Ya no son sólo aplicaciones web, sino que aplicaciones que tradicionalmente se desarrollaban como aplicaciones de escritorio acaban siendo desarrolladas usando HTML y Javascript (o algún lenguaje transpilado a él).

    Que eso sea algo bueno o malo es bastante discutible, y de hecho no faltan las quejas ante el elevador consumo de recursos de estas aplicaciones, como el consumo de memoria y CPU de Slack o de Visual Studio Code.

    Si bien es cierto que la potencia de HTML y CSS ha crecido mucho y permite crear interfaces muy vistosos y usables (dejando de lado ese consumo de recursos), también es cierto que cuando llega el momento de interactuar con la plataforma en que se ejecutan estas aplicaciones “web” la cosa se complica.

    No hay que olvidar que todas estas tecnologías están diseñadas para ser ejecutadas en el contexto de un navegador, limitando el acceso a la plataforma subyacente por motivos de seguridad, y eso hace que utilizar determinadas APIs o dispositivos hardware sea complicado. Muy complicado. Aunque las APIs de HTML continúan avanzando, a corto plazo siempre encontraremos cosas que no podemos hacer desde él y hay que buscar soluciones más imaginativas para poder salir del paso.

    En este post vamos a ver algunas alternativas que podemos utilizar. No son las únicas alternativas existentes, y si alguien conoce más y las añade a los comentarios se lo agradeceré eternamente.

    Olvidarnos de HTML

    Si necesitamos acceder a APIs que están disponibles para aplicaciones nativas pero no para aplicaciones web, lo primero que deberíamos plantearnos es si realmente tiene sentido hacer una aplicación web.

    Puede que nos resulte cómodo desarrollar el interfaz de usuario como una aplicación web porque estemos acostumbrados a ello. O que queramos aprovechar las ventajas de despliegue y ubicuidad que supone una aplicación web. Pero, seamos realistas, si tu aplicación web tiene que estar usando un montón de APIs que no están disponibles para ella, tal vez nos hayamos equivocado de tecnología y sea mejor recurrir a una aplicación nativa en lugar de estar luchando contra la tecnología que estas usando.

    Tendrás que buscar soluciones para la parte de despliegue, actualización, etc., pero existen vías para ello y quizá sea más sencillo (y productivo) eso, en lugar de estar buscando atajos para saltarte las limitaciones de una aplicación web.

    Plugin del navegador

    Una opción que se ha utilizado tradicionalmente para evitar las limitaciones de las aplicaciones web es recurrir a un plugin del navegador. Desde los controles ActiveX y los applets de Java hasta Silverlight (¿alguién se acuerda de él?), pasando por Flash y otro montón de plugins que se instalaban en el navegador para ralentizarlo, añadirle agujeros de seguridad y, de paso, ofrecer alguna funcionalidad extra, como acceso al portapapeles o a los almacenes de certificados digitales.

    Hoy en día me atrevería a decir que este tipo de soluciones han caído en desuso y han sido sustituidos por una variante: las extensiones de los navegadores, que muchas veces acaban cumpliendo una función similar a los plugins clásicos.

    La principal pega que tienen los plugins es que implica desarrollar para cada navegador, con las APIs propias del navegador, y además necesitamos que el usuario se las instale. Además, el soporte para plugins en los navegadores es cada vez más restringido. Incluso el soporte para las extensiones es relativamente limitado (por ejemplo en dispositivos móviles) y la distribución de extensiones a veces implica pasar por plataformas concretas de publicación.

    Plugin de PhoneGap/Electron

    Si hemos optado por empaquetar nuestra aplicación con PhoneGap, Electron o similar, podemos utilizar los puntos de extensión que nos brindan los propios frameworks.

    Normalmente todas estas herramientas nos permiten crear plugins con parte nativa y parte Javascript. Lo bueno de esto es que podemos tener un API uniforme accesible desde nuestra aplicación web, y luego desarrollar distintas implementaciones nativas de ese API para cada plataforma que queramos soportar.

    Otra ventaja de este enfoque es que, puesto que estamos empaquetando nuestra aplicación con el navegador, ya partimos de la base de que el usuario se tiene que instalar algo (ya no accede por una URL, sino que ejecuta una aplicación), por lo que utilizar o no un plugin no supone un esfuerzo adicional para él, como sí ocurre con los plugins del navegador.

    Como contrapartida, perdemos parte de las ventajas de una aplicación web “pura” y será necesario pensar en una estrategia de despliegue y actualización apropiada para nuestra aplicación. Además, a la hora de desarrollar el plugin podemos encontrar ciertas limitaciones (que no suelen ser graves) en las posibilidades que nos ofrece PhoneGap o Electron.

    Comunicación con aplicación nativa

    Otra alternativa, más laboriosa pero muy potente, es desarrollar una aplicación nativa y dejar que nuestra aplicación web se comunique con ella a través de algún protocolo estándar, como HTTP o WebSockets.

    De esta forma, podemos desarrollar una aplicación completamente independiente que use cualquier cosa existente en la plataforma host, sin ningún tipo de restricción, y con el lenguaje y las herramientas que más nos gusten. Esta aplicación deberá exponer un API que pueda ser invocado desde nuestra aplicación web, generalmente mediante peticiones HTTP o, si nos vemos ganas y lo necesitamos, con WebSockets.

    La ventaja de esta idea es que nos da máxima flexibilidad a la hora de implementar la interacción con la plataforma, pero a cambio nuestra aplicación nativa deberá preocuparse no sólo de implementar la funcionalidad que necesitamos, sino también de mantener un servidor web al que se conecta nuesta aplicación web y lidiar con temas como CORS y certificados SSL.

    Igual que en el caso de los plugins del navegador, nuestros usuarios deberán descargar, instalar, actualizar y mantener esta aplicación nativa, lo que supone un handicap adicional.

    Esquema de URI personalizado

    Exite otra opción, quizá menos glamurosa pero que puede ser muy efectiva, que es la de recurrir a esquemas de URIs personalizados. Quizá a muchos os suenen enlaces con esta pinta:

    ed2k://|file|Random_Pr0n.avi|149504|9691ee246d63d45ea71954c4d|/

    Son los enlaces que usaba edonkey y que heredaron sus sucesores para localizar identificar ficheros en las redes p2p. Los enlaces constan del esquema, este caso ed2k://, y la información asociada. En la configuración del sistema operativo es posible asociar cada esquema con una aplicación, y el sistema se encargará de invocar esa aplicación al pulsar en URIs que se ajusten a ese esquema, indicándole el contenido de la URI.

    Aunque esto es dependiente tanto del sistema operativo como del navegador, prácticamente todos los sistemas operativos y navegadores modernos, tanto móviles como de escritorio, lo soportan. La manera de configurarlo en cada uno es diferente, por ejemplo en Windows se usa el registro o en Android se usan intents.

    La ventaja frente a la alternativa anterior es que nos ahorramos tener que implementar un servidor web en nuestra aplicación nativa, pero a cambio la manera de comunicar ambas aplicaciones es más restringida, ya que no podremos devolver fácilmente resultados desde la parte nativa a la parte web, y el tipo de parámetros a pasar hacia la parte nativa está limitado por la longitud máxima de las URLs que soporte el navegador. Además, el despliegue puede ser algo más complicado ya que tenemos que registrar nuestro esquema de URI personalizado y, por supuesto, seguimos con la limitación de tener que obligar al usuario a instalar una aplicación adicional.

    Resumen

    Cuando tienes cierta soltura desarrollando interfaces de usuario basados en HTML y Javascript es muy tentador recurrir a ellos para cualquier tipo de aplicación, y la verdad es que el rango de aplicaciones para los que son una solución perfectamente válida es cada vez mayor.

    No obstante, hay momentos en los que, por el propio diseño de las tecnologías web, no podemos acceder a recursos nativos que son necesarios para nuestra aplicación. Llegado ese punto, es razonable preguntarse si no sería mejor hacer directamente una aplicación nativa y olvidarse del mundo web, y probablemente haya casos en los que sea así.

    Si la interacción entre la parte web y nativa es lo suficientemente pequeña y acotada, utilizar alguna de las técnicas expuestas en este post nos puede ayudar a salvar el escollo y seguir aprovechando las ventajas de desarrollar un interfaz de usuario con HTML y Javascript.

    Posts relacionados:

    1. Cómo testear el frontend de una aplicación web
    2. Acceso a recursos externos con PhoneGap/Cordova

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

    Blog Bitix

    Usar un resolver para recuperar propiedades en GraphQL

    noviembre 18, 2017 09:00

    GraphQL

    Cuando en una consulta de GraphQL se indican las propiedades a devolver GraphQL usa para cada una de ellas lo que en la implementación de Java se llama un resolver y en otras implementaciones data fetcher. En la mayoría de casos las propiedades serán propiedades de un objeto y en estos casos se usará un PropertyDataFetcher usando en Java la convención de los java beans o la clave de un mapa. En el caso de que cierto dato no esté almacenado en el objeto sino en un repositorio externo es necesario usar un resolver para devolver esa propiedad en la consulta.

    Por ejemplo, supongamos que en el ejemplo de la librería en el caso de los libros le añadimos un nuevo dato para el ISBN que está almacenado en un sistema externo, en otro repositorio. La nueva definición del esquema quedaría de la siguiente forma, basta con añadir la nueva propiedad al tipo Book y su tipo que será String.

    Para que una consulta que recupere el ISBN funcione correctamente es necesario implementar un resolver creando una clase que implemente la interfaz GraphQLResolver<Book> en la que se incluya un método get por cada propiedad del tipo Book que esté alamcenada en otro repositorio. Estos métodos get reciben como parámetro el objeto Book a partir del cual como contexto es posible tener los datos del libro del que hay que recuperar el ISBN, posiblemente utilizando su identificativo. En el ejemplo simplemente se devuelve un dato aleatorio pero perfectamente en caso necesario se podría usar un repositorio que lo recupere del sistema de información que lo almacena.

    A la hora de definir el servicio de GraphQL hay que proporcionar el resolver personalizado.

    Los resolvers permiten almacenar la información en dos bases de datos distintas, una podría ser almacenar una información una base de datos relacional, otra información en una base de datos NoSQL, dos bases de datos relacionales distintas o inlcuso proporcionado por una API distinta. En cualquier caso para el usuario de la API y del servicio es transparente como esté almacenada la información.

    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...

    Picando Código

    [Plugin WordPress] List Category Posts – 0.73

    noviembre 17, 2017 10:26

    Frankenstein WordPressLa semana pasada publiqué una nueva versión de List Category Posts después de 8 meses sin tocar el código. List Category Posts es un plugin de WordPress que permite publicar una lista de posts pertenecientes a una categoría dada, además de unos cuantos parámetros más y personalizaciones. Desde hace un tiempo que la mayoría de las actualizaciones constan más de revisar e incluir Pull Requests de otros usuarios que escribir código propio. Es por la única razón que sigo viendo código PHP, pero como comenté al alcanzar el millón de descargas, es un proyecto exitoso.

    Esta nueva versión incluye dos características bastante interesantes, implementadas por otros usuarios. Los usuarios más experientes de WordPress conocerán las taxonomías: Un mecanismo para agrupar posts, enlaces o tipos de posts personalizados. Las categorías y etiquetas son ejemplos de taxonomías por defecto en WordPress. Desde hace varias versiones podemos crear nuestras propias taxonomías, lo que le da a WordPress más poder como CMS.

    La clase que usa List Category Posts, WP_Query, permite usar taxonomías para la búsqueda de posts. Mediante distintos parámetros, podemos buscar posts con un valor determinado para una taxonomía. También se pueden buscar por varias taxonomías a la vez. Y esto es lo que tiene de nuevo la versión 0.73 del plugin:

    Para usar una taxonomía:

    [catlist taxonomy="persona" terms="Fernando"]

    Soporta las relaciones lógicas OR y AND. Para AND, se usa el signo de suma: [catlist taxonomy='topic' terms='topic1+topic2']  . Para OR la coma: [catlist taxonomy='topic' terms='topic1,topic2']

    Para taxonomías múltiples:

    • OR[catlist taxonomies_or="tax1:{term1_1,term1_2};tax2:{term2_1,term2_2,term2_3}"]
    • AND[catlist taxonomies_and="tax1:{term1_1,term1_2};tax2:{term2_1,term2_2,term2_3}"]

    El plugin está probado con el resplandeciente WordPress 4.9. Una característica muy interesante de esta nueva versión de WordPress es que permite usar shortcodes en widgets de texto. Si bien List Category Posts inclue un widget, nunca me dió la paciencia para agregar todas las opciones al widget. Mi idea inicial era tener una interfaz gráfica cómoda para incluir en el editor de texto de WordPress. Así, uno podría crear fácilmente un shortcode con todas las opciones posibles, eligiéndolas de un menú visual, y llevaría esa misma funcionalida al widget. Pero nunca me dió la paciencia para sentarme a hacerlo. Pero gracias a esta nueva versión de WordPress, se puede usar un shortcode directamente en el texto del widget.

    Lee más e instala List Category Posts desde el sitio oficial de WordPress.

    Podés ver el código fuente en GitHub, y si te interesa contribuir de alguna forma con el proyecto, no dudes en contactarme.

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

    Adrianistán

    Estadística en Python: análisis de datos multidimensionales y regresión lineal (Parte IV)

    noviembre 15, 2017 03:48

    Hasta ahora hemos tratado con una única variable por separado. Ahora vamos a ver qué podemos hacer con varias variables en la misma muestra (conjunto de datos). Nos interesará saber si están relacionadas o no (independencia o no). Si existe relación (estan correlacionadas) vamos a construir un modelo de regresión lineal.

    Distribución conjunta de frecuencias

    En el caso de dos variables, podemos construir una distribución conjunta de frecuencias. Se trata de una tabla de doble entrada donde cada dimensión corresponde a cada variable y el valor de las celdas representa la frecuencia del par. Para ello podemos usar crosstab también (de hecho, su uso original es este).

    Ejemplo: En las votaciones a alcalde de la ciudad de Valladolid se presentaban Rafael, Silvia y Olga. Analiza los resultados e informa de quién fue el ganador de las elecciones. ¿Quién fue el candidato favorito en el barrio de La Rondilla?

    usuario,voto,distrito
    Nerea,Rafael,Centro
    Esteban,Olga,Centro
    Ismael,Silvia,Centro
    Silvia,Rafael,Centro
    Susana,Rafael,Centro
    Laura,Rafael,Centro
    Raquel,Olga,Centro
    Eduardo,Olga,La Rondilla
    Javier,Silvia,La Rondilla
    Saturnino,Rafael,La Rondilla
    Segundo,Olga,La Rondilla
    Celia,Silvia,La Rondilla
    Olga,Rafael,La Rondilla
    Casimiro,Silvia,La Rondilla
    Rafael,Silvia,La Rondilla

    import pandas as pd
    
    df = pd.read_csv("votos.csv")
    tab = pd.crosstab(df["voto"],df["distrito"],margins=True) # margins si queremos generar la fila y columna All
    total = tab["All"][:-1]
    winner = total.idxmax()
    print("El ganador de las elecciones fue "+str(winner))
    
    rondilla = tab["La Rondilla"][:-1]
    winner_rondilla = rondilla.idxmax()
    print("El ganador en el distrito de La Rondilla fue "+str(winner_rondilla))

    Como podéis ver, un humando podría haber sacado estas conclusiones observando simplemente la tabla conjunta de frecuencias. ¿Quién tiene más votos en total? Rafael, con 6 en All (la suma de los distritos). ¿Quién ha sacado más votos en La Rondilla? Silvia, con 4 en la columna de La Rondilla. Por último, ¿votó más gente en el Centro o en La Rondilla? Votaron más en La Rondilla (8 votos), que en el Centro (7 votos).

    A las frecuencias All se las llama comúnmente distribuciones marginales. Cuando discriminamos las frecuencias a un solo valor de una variable, se habla de distribuciones condicionadas, en este caso hemos usado la distribución de votos condicionada al distrito La Rondilla. Estas distribuciones son univariantes como habréis sospechado.

    Gráfico XY o bivariante

    Una manera muy útil de observar posibles correlaciones es con el gráfico XY, solamente disponible para distribuciones bivariantes. Cada observación se representa en el plano como un punto. En Matplotlib podemos dibujarlo con scatter.

    Ejemplo: Represente el gráfico XY de las variables ingresos y gastos de las familias.

    id,ingresos,gastos
    1,1000,800
    2,1100,885
    3,1100,880
    4,1200,1000
    5,1400,1000
    6,900,750
    7,600,550
    8,2000,1200
    9,1200,1000
    10,1250,1000
    11,3000,2400
    12,2200,1700
    13,1700,1300
    14,1650,1220
    15,1825,1500
    16,1435,1200
    17,980,800
    18,1050,800
    19,2105,1680
    20,1280,1020
    21,1590,1272
    22,1300,1040
    23,1200,1000
    24,1110,890
    25,850,680

    import pandas as pd
    import matplotlib.pyplot as plt
    
    df = pd.read_csv("ingresos_gastos.csv")
    plt.scatter(df["ingresos"],df["gastos"])
    plt.xlabel("Ingresos")
    plt.ylabel("Gastos")
    plt.show()

    En la imagen podemos ver cada dato representado por un punto. En este ejemplo puede apreciarse como los puntos estan en torno a una línea recta invisible.

    Covarianza

    Para medir la relación entre dos variables podemos definir la covarianza:

    \(
    cov_{x,y}=\frac{\sum_{i=1}^{N}(x_{i}-\bar{x})(y_{i}-\bar{y})}{N}
    \)

    Pandas trae el método cov para calcular la matriz de covarianzas. De esta matriz, obtendremos el valor que nos interesa.

    import pandas as pd
    
    df = pd.read_csv("ingresos_gastos.csv")
    
    covarianza = df.cov()["ingresos"]["gastos"]

    ¿Y la covarianza qué nos dice? Por si mismo, bastante poco. Como mucho, si es positivo nos dice que se relacionarían de forma directa y si es negativa de forma inversa. Pero la covarianza está presente en muchas fórmulas.

    Coeficiente de correlación lineal de Pearson

    \(
    r_{x,y}=\frac{cov_{x,y}}{s_{x}s_{y}}
    \)

    Uno de los métodos que usa la covarianza (aunque Pandas lo va a hacer solo) es el coeficiente de correlación lineal de Pearson. Cuanto más se acerque a 1 o -1 más correlacionadas están las variables. Su uso en Pandas es muy similar a la covarianza.

    import pandas as pd
    
    df = pd.read_csv("ingresos_gastos.csv")
    r = df.corr(method="pearson")["ingresos"]["gastos"]

    En este ejemplo concreto, el coeficiente de correlación de Pearson nos da 0.976175. Se trata de un valor lo suficientemente alto como para plantearnos una correlación lineal. Es decir, que pueda ser aproximado por una recta. Si este coeficiente es igual a 1 o -1, se puede decir que una variable es fruto de una transformación lineal de la otra.

    Ajuste lineal

    Vamos a intentar encontrar un modelo lineal que se ajuste a nuestras observaciones y nos permita hacer predicciones. Esta recta se llamará recta de regresión y se calcula de la siguiente forma:

    \(
    \hat{y}-\bar{y}=\frac{cov_{x,y}}{s_{x}^2}(x-\bar{x})
    \)

    Usando las funciones de varianza, media y covarianza Pandas no es muy complicado hacer una recta de regresión:

    def recta(x):
        pendiente = df.cov()["ingresos"]["gastos"]/df["ingresos"].var()
        return pendiente*(x-df["ingresos"].mean())+df["gastos"].mean()

    Que podemos probar visualmente:

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    
    df = pd.read_csv("ingresos_gastos.csv")
    
    def recta(x):
        pendiente = df.cov()["ingresos"]["gastos"]/df["ingresos"].var()
        return pendiente*(x-df["ingresos"].mean())+df["gastos"].mean()
    
    line = [recta(x) for x in np.arange(3000)]
    plt.scatter(df["ingresos"],df["gastos"])
    plt.plot(line)
    plt.show()

    Sin embargo SciPy ya nos trae un método que calcula la pendiente, la ordenada en el origen, el coeficiente de correlación lineal de Pearson y mucho más en un solo lugar. Es mucho más eficiente, se trata de linregress.

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy import stats as ss
    
    df = pd.read_csv("ingresos_gastos.csv")
    pendiente, ordenada, pearson, p, error = ss.linregress(df["ingresos"],df["gastos"])
    
    def recta(x):
        return pendiente*x + ordenada
    
    recta = np.vectorize(recta)
    linea = recta(np.arange(3000))
    plt.scatter(df["ingresos"],df["gastos"])
    plt.plot(linea)
    plt.show()

    Además, para calcular los valores del gráfico, he usado vectorize de NumPy, que permite mapear los arrays nativos de NumPy. Más eficiente. Mismo resultado.

    La ley de Ohm

    ¿Quién no ha oído hablar de la Ley de Ohm? Se trata de una ley que relaciona la diferencia de potencial con el amperaje dando lugar a la resistencia. La ley fue enunciada por George Simon Ohm, aunque no exactamente como la conocemos hoy en día. En este ejemplo vamos a deducir de cero la ley de Ohm. Este ejercicio se puede hacer en casa con datos reales si se dispone de un polímetro (dos mejor) y una fuente de alimentación con tensión regulable. Este ejercicio pueden hacerlo niños sin problema.

    Olvida todo lo que sepas de la ley de Ohm

    Es posible apreciar que en un circuito con una bombilla, si introducimos una pieza cerámica, la intensidad de la bombilla disminuye.

    Cuando la corriente no atraviesa la resistencia
    Cuando la corriente atraviesa la resistencia

    ¿Qué ocurre exactamente? ¿Por qué la bombilla tiene menos intensidad en el segundo caso?

    Vamos a aislar la resistencia. Ponemos un voltímetro y un amperímetro y vamos cambiando la tensión de entrada. Anotamos la corriente medida en cada caso.

    voltaje,intensidad
    5,1.01
    6,1.19
    7,1.4
    8,1.62
    9,1.81
    10,2.01

    Podemos intentar hacer un ajuste lineal a estos datos. De modo, que una vez sepamos la intensidad, podamos predecir el voltaje.

    import pandas as pd
    from scipy import stats as ss
    
    df = pd.read_csv("voltaje_intensidad.csv")
    pendiente, ordenada_origen, pearson, p, error = ss.linregress(df["intensidad"],df["voltaje"])
    
    print(pearson) # 0.99969158942375369 es un valor que indica una muy fuerte correlación lineal

    Como Pearson nos da un número muy próximo a 1, podemos definir un modelo matemático siguiendo la regresión lineal.

    Este modelo matemático se define así:

    def voltaje(intensidad):
        return intensidad*pendiente + ordenada_origen

    Y es lo que se conoce como la Ley de Ohm. En realidad, la ordenada en el origen tiene un valor muy cercano a cero, podemos quitarlo.

    Así nos queda un modelo capaz de predecir lel voltaje en base a la intensidad y la pendiente de la recta. Ahora puedes probar cambiando la resistencia y observando que siempre ocurre lo mismo. Tenemos el voltaje por la pendiente del modelo. Este valor de la pendiente es lo que en física se llama resistencia y se mide en ohmios. Así se nos queda entonces la ley de Ohm que todos conocemos:

    \(
    V= IR
    \)

    En este caso la pendiente equivalía a 4.94 Ω, valor muy cercano a los 5 Ω que dice el fabricante.

    La entrada Estadística en Python: análisis de datos multidimensionales y regresión lineal (Parte IV) aparece primero en Blog - Adrianistan.eu.

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

    Poesía Binaria

    Monta microservicios web rápidamente en Python con web.py

    noviembre 13, 2017 05:00


    Python es uno de los lenguajes de moda. En sus múltiples usos: para escritorio, aplicaciones científicas, web, scripting y mucho más. Algo que también está de moda son los microservicios. Grosso modo, un microservicio es un componente independiente que implementa una funcionalidad de nuestra aplicación. Será una pieza de un puzzle mayor que, dadas unas especificaciones, podremos mejorar, reescribir, cambiar de lenguaje, utilizar bases de datos diferentes, etc.

    Y como ambas tecnologías están de moda, vamos a juntar lo mejor de los dos mundos y combinarlo. Porque gracias a versatilidad de Python, de la cantidad y calidad de muchas de las bibiliotecas que podemos utilizar, se convierte en un lenguaje con el que rápidamente podemos desarrollar proyectos mucho más grandes.

    Diseño con microservicios versus diseño monolítico

    Históricamente, las aplicaciones web se han diseñado de forma que funcionan como un todo. Tanto la gestión de usuarios, contenidos, cuentas, etc. Utiliza una base común, o un framework y todo funciona dentro del mismo proyecto de código. Normalmente el rendimiento de esto es muy bueno y no nos dará muchos problemas. Aunque cuando las aplicaciones web se van haciendo más y más grandes, y junto con ellas, los equipos de trabajo van apareciendo algunos problemas:

    • Todo el mundo termina trabajando en el mismo repositorio y todo el mundo ve todo el código de la aplicación. Y puede que no nos interese que un nuevo empleado de nuestra empresa lo pueda ver todo. O incluso que se toquen, o modifiquen, partes del código que no deben modificarse. Porque al final, muchos programadores toman atajos. O incluso alguien, tal vez quiera arruinar nuestro proyecto.
    • Cuando nuestro sistema recibe muchas visitas, debemos escalarlo para que éste pueda atender a más usuarios. Puede ser tan sencillo como introducir balanceo de carga, aunque eso nos obliga a tener que pasar todo por el balanceador. Puede que haya partes que no necesitemos balancear, o que necesiten menor replicación. O puede que haya partes más fáciles de replicar que otras.
    • En aplicaciones grandes, por razones de mantenimiento, deberíamos poder activar y desactivar funcionalidades simples, en lugar de poner la web entera en mantenimiento.
    • Se pueden dar casos en los que tengamos que interactuar con una aplicación de terceros. Pero no podamos o queramos modificarla. Por lo que podemos crear pequeños servicios para controlarla remotamente.
    • No estamos limitados a un lenguaje de servidor. Puede que parte de la aplicación esté hecha en C++, otra parte en PHP y otras partes en Perl, Python o Java.
    • Por seguridad, hemos de ser conscientes de que el sistema más seguro es el que no está conectado, o es menos accesible. Por eso mismo, deberíamos configurar un esquema de red en el que la información más sensible esté lo más separada posible del usuario final. Que el mismo servidor web no tiene acceso a los datos de usuarios directamente es una situación ideal. Hacer que éstos estén detrás de una red interna accesible sólo a través de un sistema intermedio nos puede dar algo más de confianza. Ya que los datos no tienen conexión directa. Al mismo tiempo que reducimos en cierto modo la capacidad de algún cyberdelincuente, reducimos la capacidad de acceso a datos de algunos empleados de nuestra empresa.

    Para ello, podemos optar por un diseño basado en APIs web que se van conectando las unas con las otras. Algunas APIs serán públicas, tendrán acceso desde el exterior; otras, en cambio, serán privadas, y sólo serán accesibles dentro de la red local donde estén situadas.

    Lo primero que podemos pensar es que este enfoque es lento. Que lo es. Es decir, no es lo mismo que nuestra aplicación monolítica llame a una función para que inserte un registro en una base de datos a que la aplicación llame a una función que haga una petición HTTP a otra máquina para que ésta inserte el registro en base de datos. Además, para hacer la petición HTTP, en el lado del cliente (el que pide) y el servidor (el que inserta el registro) debe haber un tratamiento y filtrado de datos que propicie el envío y recepción de datos. Al final, un proceso que tarda menos de 1ms se transforma en unos 2 o 3ms. Así que tenemos que tener claro los pros y los contras de nuestro caso concreto antes de optar por un diseño u otro. Y, aunque la velocidad de los servicios puede verse afectada cuando hay pocos usuarios, tal vez las posibilidades de escalado que nos brinda la nueva arquitectura pueda hacer que el sistema no se resienta demasiado cuando el número de usuarios crezca.

    REST contra el mundo

    Aunque un sistema REST no es que sea lo más rápido del mundo. Aunque podemos aprovecharnos de la madurez y longevidad de un montón de bibliotecas y sistemas que utilizan HTTP para comunicarse. Este tipo de comunicación está muy documentada y podemos crear rápidamente sistemas que se comuniquen de esta forma. Hay muchos sistemas que utilizan protocolos propios para realizar la comunicación y el intercambio de datos. Pero si lo que realmente queremos es diseñar un sistema en poco tiempo y que funcione bien deberíamos optar por sistemas con cierta madurez. Si diseñamos un protocolo desde cero tendremos que implementar el cliente y el servidor.

    Podríamos utilizar otros protocolos basados en HTTP, como SOAP, aunque no será demasiado costoso implementarlo a partir del ejemplo de abajo.

    Crear servicios con Python

    Python es un lenguaje que podemos utilizar para casi cualquier cosa. Muchos programas de escritorio, móviles, incluso de IoT se han desarrollado en Python. Y el ecosistema web no iba a ser menos. Aunque tenemos varias opciones, he optado por utilizar web.py por su simplicidad y su documentación.

    Primero vamos a instalar la biblioteca web.py con pip:

    pip install web.py

    ¡Vamos al código! Quiero realizar un ejemplo sencillo, porque esto se puede complicar hasta decir basta. El ejemplo tendrá un listado de países con un código numérico, nombre y código ISO. El código se podría mejorar utilizando una base de datos en lugar de un diccionario, incluyendo el método PATCH, o creando una clase padre para unificar un poco el código, pero en esta ocasión quiero mostrar el funcionamiento básico del servicio REST:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    # coding=utf-8

    import web
    import json

    urls = (
        '/paises(/.*)?', 'paises',
    )

    application = web.application(urls, globals()).wsgifunc()

    class paises:
        paises = { 34: { 'España', "ES"}, 33: {"Francia", "FR"} }
        codes = { 400 : '400 Bad Request',
                  404 : '404 Not Found',
                  405 : '405 Method Not Allowed',
                  409 : '409 Conflict'
                  }
        def __init__(self):
            web.header('Content-Type', 'application/json', unique=True)

        def GET(self, pais=None):
            try:
                columns = [ 'codigo', 'nombre', 'iso' ]
            if pais is None:
                    output = []
            for i,v in self.paises.items():
                        output.append(dict(zip(columns, [i] + list(v))))
            else:
                    pais = int(pais[1:])
                    if self.paises[pais] is None:
                        raise Exception('Pais no encontrado', 404)
                    else:
                        output = []
                        output.append(dict(zip(columns, [pais] + list(self.paises[pais]))))

                return json.dumps(output, ensure_ascii=False, encoding='utf8')
            except Exception, e:
                msg, code = e.args if len(e.args)==2 else (e.args, 404)
                raise web.HTTPError(self.codes[code], data="Error: " + str(msg) + "\n")

        def POST(self, pais=None):
            try:
                if pais is not None:
                    raise Exception('No permitido', 404)
               
                input = web.input(code=None, nombre=None, iso=None)
                if not input['code'] or not input['nombre'] or not input['iso']:
                    raise Exception("Faltan datos de entrada", 400)

                pais = int(input['code'])
                if pais in self.paises:
                    raise Exception("Elemento existente", 409)
               
                self.paises[pais] = { input['nombre'], input['iso'] }
                web.created()
                web.header('Location', '/paises/'+str(pais))
                return ''
            except Exception, e:
                msg, code = e.args if len(e.args)==2 else (e.args, 404)
                raise web.HTTPError(self.codes[code], data="Error: " + str(msg) + "\n")

        def PUT(self, pais=None):
            try:
            if pais is None or len(pais)==1:
                    raise Exception('Pais no indicado', 405)
               
                pais = int(pais[1:])
                input = web.input(nombre=None, iso=None)
                if not input['nombre'] or not input['iso']:
                    raise Exception("Faltan datos de entrada", 400)
               
                if pais not in self.paises:
                    raise Exception("Elemento no encontrado", 404)
               
                self.paises[pais] = { input['nombre'], input['iso'] }
                return ''

            except Exception, e:
                msg, code = e.args if len(e.args)==2 else (e.args, 404)
                raise web.HTTPError(self.codes[code], data="Error: " + str(msg) + "\n")

        def DELETE(self, pais=None):
            try:
            if pais is None or len(pais)==1:
                    raise Exception('Pais no indicado', 405)
               
                pais = int(pais[1:])          
                if pais not in self.paises:
                    raise Exception("Elemento no encontrado", 404)
               
                del self.paises[pais]
                return ''

            except Exception, e:
                msg, code = e.args if len(e.args)==2 else (e.args, 404)
                raise web.HTTPError(self.codes[code], data="Error: " + str(msg) + "\n")

    if __name__ == "__main__":
        app = web.application(urls, globals())
        app.run()

    Probando nuestro código

    Para probar el código basta con ejecutar la aplicación:

    python webservice.py
    http://0.0.0.0:8080/

    Se creará un servidor web en nuestra máquina utilizando el puerto 8080 y podremos utilizar cualquier navegador o cURL para hacer pruebas.
    Como punto interesante, destaco que podemos hacer modificaciones en nuestro código sin necesidad de volver a ejecutar el programa. Es decir, podemos modificar nuestros ficheros y volver a cargar las URLs sin problemas. Además, incluye un buen depurador de Python que nos indicará los errores en el código y nos ayudará a solventarlos.

    Montando el servicio en servidor

    Nginx + uWSGI
    Si queremos montar un servidor para correr este servicio, recomiendo encarecidamente, utilizar un servidor web como Nginx o Apache en lugar de exponer al exterior el programa en Python. Ya que los servidores web colocarán una capa de filtrado y aislamiento entre el programa y el usuario final. Además, como son muy utilizados en producción, las versiones estables intentan tener todos los fallos corregidos.
    No es raro que algún servidor de desarrollo, como puede ser este creado por web.py, no se entienda bien con algunos datos de entrada que no cumplan las normas y provoque un fallo en nuestro sistema. Mientras que un Apache, suele estar bien preparado (casi siempre) ante atacantes avispados.

    En Apache, podríamos utiliza FastCGI o mod_wsgi y con Nginx podríamos utilizar un proxy HTTP o WSGI. Un punto positivo de la utilización de Python en un entorno web es que los scripts están cargados en memoria y ejecutándose todo el tiempo, por lo que podemos tener alguna información precargada o conexiones a base de datos abiertas sin necesidad de ejecutar todo desde cero a cada petición entrante. Aunque, para atender muchas peticiones entrantes, y a veces concurrentes, es necesario que dispongamos de varias ejecuciones del script. O, al menos, un programa que controle dichas ejecuciones, lance y destruya instancias de la aplicación. En mi caso, en algún servicio que he hecho he preferido utilizar uWSGI para gestionar las instancias de mi aplicación en Python.

    UWSGI cuenta con un protocolo de comunicación tanto para Apache como Nginx que harán que estos servidores web hablen con uWSGI cuando entre una petición enviándole los datos de entorno y usuario. UWSGI verá si hay alguna instancia en ejecución de la aplicación que no esté atendiendo a nadie y le mandará los datos a ésta. Si no hay ninguna, intentará lanzar una nueva, dentro de los límites que establezcamos o esperará que haya una libre durante un tiempo para atender la petición. Cuando la aplicación haya generado una salida, uWSGI la recibirá y éste se la enviará al servidor web que ya es el que se peleará con el usuario. De todas formas el servidor web siempre tendrá la primera y la última palabra. Y con esto me refiero a que el servidor web debe contar con sus propios sistemas de seguridad y filtros que decidirán si pasar o no la petición a uWSGI, e incluso si son ficheros estáticos los pueden servir ellos mismos. Y una vez pasada la petición, antes de enviarla al usuario, podrán modificar o insertar cabeceras, incluso filtrar o comprimir el contenido generado.

    proxy con protocolo uWSGI

    UWSGI permite configuración en archivos ini, xml, json o yaml. La configuración es muy similar en lo que se refiere a los nombres de los campos de configuración y el ejemplo lo haré con archivos ini. Para ello crearemos el archivo /etc/uwsgi/apps-available/webservice.ini donde webservice es el nombre de nuestro servicio. Con el siguiente contenido:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    [uwsgi]
    socket = 127.0.0.1:9091
    chdir = /var/www/myapplication/webservice/
    wsgi-file = /var/www/myapplication/webservice/www/webservice.py
    pp=/var/www/myapplication/webservice/www
    module=webservice
    processes = 4
    threads = 2
    stats = 127.0.0.1:9191
    pidfile = /var/run/webservice.pid
    callable=application

    Se lanzan 4 procesos con 2 hilos cada uno. Y el servidor uWSGI escuchará por el puerto 9091 dentro de la máquina local. Es importante establecer el callable adecuado. Como vemos en nuestra aplicación Python, la línea:

    1
    application = web.application(urls, globals()).wsgifunc()

    Crea el objeto de aplicación llamado application. Ese será nuestro callable. Por último, es recomendable activar las stats. Será otro servidor web escuchando en el puerto 9191 y sólo para conexiones locales. Esto servirá para monitorizar la salud de nuestro servidor y que tengamos algo de control y posibilidad de mejorar el servicio y saber cómo se está comportando.

    Luego, haremos un enlace desde /etc/uwsgi/apps-available/webservice.ini a /etc/uwsgi/apps-enabled/webservice.ini, como vemos, es muy parecido a otros sistemas en los que podemos activar y desactivar servicios mediante la creación y borrado de enlaces.

    Luego en Nginx, la configuración que colocaremos en /etc/nginx/sites-available/webservice podría ser algo como esto:

    1
    2
    3
    4
    5
    6
    7
    8
    server {
      listen 80;
      server_name apps.example.com;
        location /webservice/ {
        uwsgi_pass 127.0.0.1:9091;
        include uwsgi_params;
      }
    }

    Tras esto, creamos un enlace desde /etc/nginx/sites-available/webservice a /etc/nginx/sites-enabled/webservice y reiniciamos el servidor Nginx.

    Lo bueno del protocolo utilizado por uWSGI es que es mucho más rápido que el HTTP. Por lo que la comunicación entre el servidor web y uWSGI será muy rápido. UWSGI es un protocolo binario, y si viéramos el contenido de dicha comunicación nos costaría mucho entender algo (como humanos), aunque no es un protocolo estándar, y algunas veces no podremos utilizarlo. Hace poco tuve un caso particular en el que no podía instalar el plugin uWSGI para Nginx y tuve que configurar uWSGI para utilizar HTTP para la comunicación con el servidor web.

    UWSGI como servidor HTTP

    Y este caso utilizaré para el ejemplo a continuación. Dado que uWSGI será un servidor privado no es necesario configurar una conexión HTTPs entre el servidor web y uWSGI. Esta conexión será local o estará dentro de una red interna, por lo que estaríamos introduciendo complejidad extra e innecesaria en la comunicación. De todas formas, la comunicación entre el servidor web y el usuario sí que puede ser HTTPs.

    Lo primero, una vez instalado el paquete uWSGI y el plugin para Python es crear el archivo /etc/uwsgi/apps-available/webservice.ini donde webservice es el nombre de nuestro servicio. El contenido del archivo será el siguiente:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [uwsgi]
    plugin=python,http
    http = webservice:4480
    chdir = /var/www/myapplication/webservice/
    wsgi-file = /var/www/myapplication/webservice/www/webservice.py
    pp=/var/www/myapplication/webservice/www
    module=webservice
    processes = 4
    threads = 2
    stats = 127.0.0.1:9191
    pidfile = /var/run/webservice.pid
    callable=application

    Para este caso, se creará un servidor HTTP en el puerto 4480 que utilizará la aplicación situada en /var/www/myapplication/webservice/www/webservice.py. Finalmente en /etc/nginx/sites-available/webservice configuramos la URL /webservice/ como proxy inverso al puerto 4480, ponemos algo así:

    1
    2
    3
    4
    5
    6
    7
    server {
      listen 80;
      server_name apps.example.com;
        location /webservice/ {
            proxy_pass http://127.0.0.1:4480/;
      }
    }

    Ya sólo queda crear un enlace desde /etc/nginx/sites-available/webservice a /etc/nginx/sites-enabled/webservice y reiniciar el servidor Nginx.

    Foto principal: frank mckenna

    The post Monta microservicios web rápidamente en Python con web.py appeared first on Poesía Binaria.

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

    Variable not found

    Enlaces interesantes, ¡300!

    noviembre 13, 2017 07:55

    Enlaces interesantes, ¡300!Ahí van los enlaces recopilados durante la semana pasada, celebrando en esta ocasión la tricentésima edición de la serie que comenzó hace ya más de siete años. Como siempre, 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

    Componentes

    Otros

    Publicado en Variable not found.

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

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

    CAS2017: Conferencias Agile-Spain

    noviembre 12, 2017 07:18

    La CAS2017 ha sucedido hace un par de días, y empiezo a leer las primeras impresiones, felicitaciones, quejas, enhorabuenas y protestas... Mi visión sobre la CAS2017  ¿Qué ha sucedido este año? Desde mi punto de vista personal, no puedo dar muchos detalles del contenido. ¡He visto muy pocas charlas! :) La CAS tiene cada vez para mi más de punto de encuentro que de conferencia dónde aprender.

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

    Blog Bitix

    Devolver mensajes de error descriptivos en GraphQL

    noviembre 12, 2017 10:30

    GraphQL

    Por defecto GraphQL devuelve errores con mensajes descriptivos para los errores del cliente como son los errores de sintaxis en la sentencia de consulta o mutación, en el caso de que el campo solicitado no exista o no se ha indicado ninguno. En el caso de Java si se lanza una excepción en la clase repositorio que guarda los datos o en la lógica de negocio y no se captura GraphQL indicará que se ha producido un error interno en el servidor. Esto no es muy descriptivo y es mejor indicar errores más útiles para el usuario de la API como podría ser que no se tienen permisos para realizar la modificación o el error que se ha producido al validar los datos y por los que la operación no se ha completado.

    Los errores en GraphQL usando el lenguaje Java se gestionan implementando en una clase la interfaz GrapQLError, este podría ser en caso de una excepción que además de heredar de Exception implemente la interfaz GraphQLError. Sin embargo, GraphQL cuando una clase hereda de Exception lo considera un error interno del servidor y para no dar información interna del servicio a los clientes como mensaje indica únicamente Internal Server Error(s) while executing query.

    Para que GraphQL muestre el error personalizado deseado lanzando excepciones hay que adaptar esa excepción y que implementa GraphQLError con una clase que únicamente implemente la interfaz GraphQLError pero no herede de Exception. Esta sería una clase adaptador necesaria.

    Para adaptar las clases excepción hay que cambiar el comportamiento de la clase GraphQLErrorHandler de modo que transforme las excepciones a la clase GraphQLError propia. Esta clase se indica al construir el objeto SimpleGraphQLServlet y ServletRegistrationBean.

    En el caso de este ejemplo solo un usuario de nombre admin tiene permitido hacer modificaciones en la colección de libros guardados en la clase repositorio LibraryRepository. Por otro lado, cuando se añade un libro se hace una validación de los datos comprobando que el autor del libro a añadir exista en la librería. Estos son las peticiones válidas.

    Y estas las inválidas que devuelve los mensajes propios más descriptivos de los errores o validaciones realizadas en el servidor de más utilidad para un usuario del servicio.

    La interfaz GraphQLError posee el método getMessage() para devolver la descripción del mensaje pero con el método getExtensions() es posible incluir cualquier dato en forma de clave-valor que deseemos como un código de error o cualquier otra información deseada. El caso de la excepción PermissionException devuelve dos datos adicionales foo y fizz, en un caso real se implementaría una lógica más útil para devolver estos datos adicionales posiblemente proporcionándolos en el constructor u obteniéndolos con la referencia a algún objeto.

    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...

    Cuaderno de software

    ¿Tiene sentido la CAS?

    noviembre 12, 2017 09:24

    En la Conferencia Agile Spain 2016 fui “chair técnico”. Básicamente, me eligieron para ejercer un criterio a la hora de seleccionar “charlas técnicas”. Fui elegido junto con gente del mundo de la consultoría agile, gente de UX/Design thinking, etc… Cada uno “de mundos distintos”.

    Como chair técnico, lo primero que hice fue descartar todas las charlas técnicas. Había cosas sobre escalabilidad con docker, cosas sobre TDD, etc, etc…. Todo descartado.

    Lo que hice fue dar voz a técnicos para que hablaran de cosas que pudieran interesar a todos los perfiles (incluyendo el perfil técnico, claro). Así Xabi habló de comunicación, Eduardo sobre postponer decisiones, Nacho sobre cómo elegir stack tecnológico, Kini sobre cultura, Modesto y Roni sobre aprendizaje, Carlos  sobre problema versus solución (ésta ni la tuve que elegir yo), Rubén, Isidro y Apa sobre cultura ágil y técnica, Nestor sobre Devops desde un punto de vista economics, Dani sobre hiring de equipos, Gamarra sobre katas (¡quizá la charla más técnica!)….

    Si, como técnico, consideras que la comunicación, cultura, decisiones, stack, aprendizaje, problemas vs soluciones, etc, etc, etc… no son temas para ti, tienes un problema grave.

    Si como UX/Design thinking, manager, etc… crees que escuchar a técnicos hablar de estos temas no es para ti, tienes el mismo maldito problema grave.

    No estuve, pero me atrevo a decir que cuando nació la CAS la comunidad de software de España era muy inmadura. Básicamente, allí fue todo el mundo al que interesaba el agilismo a cholón, fuera cual fuera su perfil. Ahora, cada perfil tiene sus propios eventos: software craftsmanship, UXSpain, etc, etc, etc…

    Estamos en una encrucijada:

    • o la CAS es el evento especializado para coach agile, con lo que el resto de perfiles sobran
    • o la CAS es un “evento raro”, donde los perfiles vienen a ofrecer lo suyo, al resto de perfiles. Un lugar donde el éxito es que a tu charla vengan “the others”.

    Se elija lo que se elija, valdría la pena hacer una apuesta clara, eliminar el ruido y potenciar la señal.

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

    Jesús Perales

    Ruido Blanco (Whitenoisefy)

    noviembre 08, 2017 08:47

    Ruido Blanco (Whitenoisefy)

    Comunmente cuando me encuentro trabajando puedo distraerme con facilidad, despues de investigrar un poco, encontre algunas cosas que me fuerón de utilidad,una es la tecnica pomodoro.

    Utilizar la técnica pomodoro pude maximizar mis tareas en menos tiempo y aumentar mi productividad, el podmodoro funcionaba pero, sabia que podría hacerlo mejor si conseguía bajar el numero de interrupciones.

    Así fue como encontré el ruido blanco que se describe como una señal de sonido que contiene todas las frecuencias y estas son de la misma potencia y se dice podría tener los siguientes beneficios:

    • Aumenta el enfoque mientras mejora la privacidad
    • Ayuda a dormir bloqueando las distracciones
    • Relaja a los bebés.
    • Relaja y reduce el estrés.
    • Calma los dolores de cabeza y las migrañas
    • Máscaras de tinnitus (zumbido de las orejas)

    Compre unos audífonos y continuamente abría una pagina para escuchar ruido blanco, quizá me quede sordo algún día.

    Note mucho la diferencia, ya no me resultaba difícil concentrarme, adiós a las interrupciones.

    En el trabajo que estoy actualmente mueven las reglas de la red por lo que la pagina que accedía ayer, hoy podría estar bloqueada, por lo tanto me di a la tarea de crear una extensión en javascript para Firefox (Whitenoisefy), utilizando el API de sonido para crear ruido blanco.

    Afortunadamente para mi, alguien ya había pensado en esto y creo ruido blanco, rosa y café, solo tenia que aprender a empaquetar Web Extensions.

    Para este pequeño proyecto, no utilice jQuery, ni Angular, solo vanilla javascript( ¡vanilla rocks! ).

    Por otro lado, aproveche para probar Bootstrap 4 , las cards, los nuevos botones outline se ven muy bien, el sistema de rejillas es mas poderoso, me sorprendió la evolución de este framework, aun recuerdo su versión 2 compitiendo con Pure de Yahoo.

    Como no soy muy bueno creando logos tome alguno free de internet , los combine con Gimp, creo que se ve muy mal y debería mejorarlo, para que estén acorde a los botones de la nueva interfaz de Firefox, parece que fue ayer que Australis llegaba a mis ojos, que nostalgia ver como evoluciona todo.

    Quízá la llegue a portar a Google Chrome ( Eww ).

    Pueden descargar la extensión en la siguiente URL

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

    Blog Bitix

    Ejemplo de GraphQL para una interfaz de un servicio con Spring Boot y Java

    noviembre 07, 2017 06:00

    GraphQL es una alternativa a una interfaz REST con las ventajas de poder realizar varias consultas en una misma petición y devolviendo únicamente los datos que requiera el cliente. Es una especificación y hay una implementación para los lenguajes de programación más populares entre ellos Java. Este artículo es una introducción con un ejemplo completo que muestra cómo se hacen consultas y modificaciones en los datos.

    GraphQL

    Con anterioridad las aplicaciones que lo necesitaban ofrecían una interfaz como un servicio mediante web services, sin embargo, esta tecnología era complicada por usar XML y no de fácil utilización en clientes JavaScript. La evolución que a día de hoy sigue siendo mayoritaria son las interfaces REST que emplean la semántica de los verbos del protocolo HTTP para realizar operaciones de búsqueda, creación, modificación y eliminación y normalmente empleando JSON como formato para intercambiar los datos. Sin embargo, REST no está exento de algunos problemas como la necesidad de realizar varias peticiones a cada uno de los recursos que ofrece si se necesitan datos de varios de ellos, otro es que los datos ofrecidos por los servicios REST está prefijados en tiempo de desarrollo no adaptándose a lo que necesita el cliente. En cierta medida estas dos cosas se pueden implementar en la interfaz REST con algunos parámetros pero requiere codificarlo explícitamente.

    Más recientemente ha aparecido otra forma de implementar una interfaz de un servicio con GraphQL considerándose una alternativa mejor a REST que solventa los dos problemas de las interfaces REST anteriores. REST ofrece en varios endpoints los recursos que pueden ser accedidos mediante los verbos HTTP (GET, PUT, POST, DELETE), en GraphQL por el contrario hay un único endpoint, los puntos de entrada al grafo y los tipos que se relacionan entre si que son consultados para obtener los datos con el lenguaje de consulta que ofrece GraphQL.

    En GraphQL se define un esquema con la definición de los tipos en la API, se diferencia la obtención de los datos que es realizada por las queries y de las modificaciones que es realizada por los mutators, el esquema se puede definir en un archivo de texto como en este ejemplo o de forma programática con código que es necesario para algunas personalizaciones. Otras tareas que pueden ser necesarias en una API son autenticación que es posible capturando los datos del contexto provenientes en los datos o como cabeceras de la petición posiblemente en forma de token de OAuth y la autorización en la lógica del servicio en base al sujeto autenticado. Se puede usar datos propios con scalar para los cuales se ha de proporcionar una clase que realice la transformación implementando una clase GraphQLScalarType. Posee funcionalidades de introspección y también filtrado, paginación, gestión de errores y cacheo aunque esto último es menos efectivo en GraphQL al depender de los datos a devolver que solicite el cliente.

    Para usar GraphQL hay que definir un schema que incluye los tipos, sus propiedades y tipos. También se pueden usar fragmentos para reutilizar partes de la definición de los tipos. Cada type representa una entidad que definen las propiedades que posee ya sean datos escalares o referencias a otras entidades formando de esta manera grafos de objetos, los tipos de las variables que poseen una exclamación al final quiere decir que son opcionales, por defecto todos los datos son distinto de nulo. Las listas se definen con corchetes y el tipo entre ellos.

    Una definido el esquema hay que desarrollar los resolvers que son encargados de obtener los datos seguramente de una base de datos externa ya sea una base de datos SQL o NoSQL en este caso utilizando una clase que implementa el patrón repositorio y que abstrae del sistema de persistencia donde se almacenan los datos.

    Los mutators son los encargados de procesar las peticiones de modificación.

    Usando una aplicación de Spring Boot para ofrecer el servicio hay que realizar la contribución adecuada al contenedor de dependencias, en Java GraphQL se define como un servlet al cual hay que proporcionarle la configuración de los resolvers, mutators, procesador de contexto que en este caso se utiliza para la autenticación y definición del esquema entre otras posibles cosas.

    El lenguaje de consulta GraphQL permite consultar el grafo de objetos y recuperar los datos deseados. En el siguiente ejemplo se obtienen los libros, los autores y los libros con los datos de sus autores de una clase que implementa el patrón repository. En el ejemplo los datos del repositorio están definidos en la propia clase de forma estática pero como su función es abstraer de donde se obtienen los datos el cambio sería sencillo para que los obtuviese de una base de datos SQL o NoSQL ya que los cambios estarían encapsulados principalmente en esa clase. Los datos son devueltos en formato JSON.

    Una de las ventajas de GraphQL sobre REST es que es posible realizar una única petición lo que en REST podrían ser varias. Por ejemplo, la siguiente consulta obtiene en una única consulta todos los libros, todos los autores y el autor con identificativo 1 de la biblioteca, esto mejora el rendimiento ya que en REST se hubiesen requerido varias peticiones una para obtener libros, otra para los autores y otra para el autor 1. La otra ventaja sobre REST es que se devuelven únicamente los datos que el cliente solicita y no una lista prefijada por el desarrollador de la interfaz.

    Las consultas puede hacerse mediante una petición GET o POST de HTTP.

    Las peticiones de modificación se envían mediante POST. Este es el caso para añadir un libro a la biblioteca y los casos de que el autor del libro no sea válido o que el usuario que añade el libro no tenga permisos. En el ejemplo los errores no son descriptivos de lo que realmente ha sucedido, habría que hacer el tratamiento de errores adecuado para que los mensajes fuesen más descriptivos.

    La forma explicada en las guías de GraphQL para Java es que el mutator reciba los datos y este delegue la funcionalidad en una clase que implemente el patrón repository que abstrae del sistema de almacenamiento (base de datos SQL, NoSQL o cualquier otro), además, este patrón repository o clase de lógica de negocio se recomienda que implemente la funcionalidad necesaria para aplicar la autorización. En el ejemplo aunque de forma sencilla solo en usuario admin tiene permitido añadir libros, en un proyecto es posible realizar la autenticación usando Keycloak como sistema de OAuth, usar el token de OAuth para implementar la autorización y un framework de seguridad como Apache Shiro para aplicar los permisos a las funcionalidades.

    Los artículos Autenticación con OAuth y Keycloak en un servicio REST con JAX-RS y Spring Boot y Integrar autenticación OAuth con Keycloak, Shiro, Apache Tapestry y Spring Boot pueden servir como base para añadir autenticación OAuth a un servicio GraphQL con Keycloak.

    Finalmente, el archivo de construcción de Gradle del ejemplo con las dependencias necesarias.

    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...

    Variable not found

    Personalizar las plantillas de controladores y vistas para ASP.NET Core MVC

    noviembre 07, 2017 07:55

    ASP.NET Core MVCTrabajando con ASP.NET Core MVC desde Visual Studio, seguro que alguna vez habéis creado vistas, controladores u otros elementos utilizando las plantillas disponibles en el IDE, seleccionando la opción del menú Add > New Scaffolded Item, por lo que os habréis encontrado con un cuadro de diálogo como el siguiente:

    Cuadro de diálogo de adición de scaffolded item

    Tras seleccionar el tipo de elemento a crear, aparecerá un segundo cuadro de diálogo solicitándonos información sobre el mismo. Por ejemplo, en la siguiente captura de pantalla se muestra el diálogo de creación de vistas MVC, donde debemos introducir el nombre de la vista, la plantilla a utilizar, la clase del Modelo para vistas tipadas, etc:

    Cuadro de diálogo de creación de vista MVC

    Después de cumplimentar estos datos, se generará automáticamente el código fuente del elemento indicado. Hasta aquí bien, pero, ¿qué ocurre si ese código generado no se ajusta exactamente a nuestras necesidades? O preguntándolo de otra forma, ¿es posible modificar las plantillas de generación de código utilizadas en estos casos para adaptarlas a nuestras preferencias?

    Pues sí, y vamos a ver cómo :)

    Modificando las plantillas de generación de código a nivel de equipo

    En ASP.NET Core 2.0, las plantillas utilizadas para generar estos artefactos desde Visual Studio se encuentran en la carpeta del SDK de .NET Core %programfiles%\dotnet\sdk\NuGetFallbackFolder\microsoft.visualstudio.web.codegenerators.mvc\2.0.0\Templates, organizadas a su vez en subcarpetas según el tipo de elemento:
    • ControllerGenerator → Generadores de controladores MVC.
    • MvcLayout → Layouts de MVC.
    • RazorPageGenerator → Generadores de páginas Razor.
    • Startup → Plantilla de clase Startup.
    • ViewGenerator → Generadores de vistas MVC.
    Contenido de la carpeta de plantillas

    A su vez, en cada una de estas carpetas encontramos las plantillas disponibles, escritas con sintaxis Razor (bye bye, T4). Por ejemplo, en el caso de los generadores de controladores MVC, encontramos las siguientes, que corresponden con los distintos tipos de controlador que podemos crear usando las herramientas de scaffolding:

    Archivos de plantillas de generación de controladores

    Aunque ya se puede intuir por el nombre de los archivos, la correspondencia con las plantillas ofrecidas en el cuadro de diálogo de scaffolding es la siguiente:
    Plantillas de scaffold
    1. ApiControllerWithActions.cshtml
    2. ApiControllerWithContext.cshtml
    3. ApiEmptyController.cshtml
    4. ControllerWithActions.cshtml
    5. EmptyController.cshtml
    6. MvcControllerWithContext.cshtml
    El código que podemos encontrar en cada una de estas plantillas es fácil de descifrar si estamos familiarizados con la sintaxis Razor. Por ejemplo, si echamos un vistazo a una plantilla simple, como es `EmptyController.cshtml”, nos encontraremos un código como el siguiente:
    @inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;

    namespace @Model.NamespaceName
    {
    public class @Model.ClassName : Controller
    {
    public IActionResult Index()
    {
    return View();
    }
    }
    }
    Seguro que ya véis claro cómo podemos personalizar esta plantilla a nivel de equipo: basta con editar ese archivo (requiere permisos de administrador) y ponerlo a nuestro gusto. Obviamente, los cambios que hagamos tendrán efecto a partir de ese momento, aunque sólo en nuestro equipo.

    Por ejemplo, podríamos modificar la plantilla EmptyController.cshtml de la siguiente forma. Observad que lo único que hemos hecho es añadir un comentario de documentación con <summary>:
    @inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;

    namespace @Model.NamespaceName
    {
    /// <summary>Controller class for @Model.ClassName.Replace("Controller", string.Empty)</summary>
    public class @Model.ClassName : Controller
    {
    public IActionResult Index()
    {
    return View();
    }
    }
    }
    Así, si ahora creamos un scaffolded item de tipo “MVC Controller Empty”, obtendremos el siguiente resultado:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;

    namespace MyMvcApp.Controllers
    {
    /// <summary>Controller class for Test</summary>
    public class TestController : Controller
    {
    public IActionResult Index()
    {
    return View();
    }
    }
    }
    Esto podemos hacerlo con cada una de las plantillas que nos interese para personalizar el código generado. Podemos aprender muchas cosas interesantes simplemente observando el código original, donde veremos qué información recibimos desde el entorno en la propiedad Model y cómo utilizarla.

    Modificando las plantillas de generación de código a nivel de proyecto

    Anteriormente hemos visto cómo modificar las plantillas de generación de código, pero con algunas limitaciones, puesto que los cambios que hagamos:
    • Sólo están visibles en nuestro equipo, pues estamos tocando archivos locales.
    • Se aplicarán en todos los proyectos a partir de ese momento.
    • No estarán incluidos en Git o el sistema de control de código fuente que utilicemos.
    Carpeta Templates en el proyecto ASP.NET Core Sin embargo, es posible, y además bastante sencillo, crear plantillas específicas para determinados proyectos, que además puedan ser introducidas en el control de código fuente como parte de los mismos y ser compartidas por el equipo de desarrollo.
    Para ello, simplemente tenemos que copiar el contenido de la carpeta %programfiles%\dotnet\sdk\NuGetFallbackFolder\microsoft.visualstudio.web.codegenerators.mvc\2.0.0\Templates a la carpeta Templates del proyecto donde queramos incluirlas. El resultado, visto en el explorador de soluciones de Visual Studio, sería algo como lo mostrado en la captura de pantalla lateral.

    A partir de ese momento estas plantillas serán las utilizadas para crear los elementos, por lo que cualquier cambio aplicado a ellas serán tenidos en cuenta :)

    Por ejemplo, podríamos modificar la plantilla Templates/EmptyController.cshtml de la siguiente forma para añadir información en el encabezado del archivo y hacer que por defecto los controladores hereden de una clase llamada CustomBaseController de nuestro proyecto:
    // 
    // File Creation: @DateTime.Now.ToLongDateString()
    // Author: @Environment.UserName from @Environment.MachineName
    // License: MIT
    //
    @inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using @(Model.NamespaceName).Extensions;

    namespace @Model.NamespaceName
    {
    public class @Model.ClassName : CustomBaseController
    {
    public IActionResult Index()
    {
    return View();
    }
    }
    }
    Así, el resultado obtenido al crear un nuevo controlador vacío podría ser el siguiente:
    // 
    // File Creation: domingo, 29 de octubre de 2017
    // Author: josem from PC-JMA
    // License: MIT
    //
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using CustomTemplatesDemo.Controllers.Extensions;

    namespace CustomTemplatesDemo.Controllers
    {
    public class DefaultController : CustomBaseController
    {
    public IActionResult Index()
    {
    return View();
    }
    }
    }
    ¡Espero que os sea útil!

    Publicado en Variable not found.

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

    Variable not found

    Enlaces interesantes 299

    noviembre 06, 2017 07: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

      Publicado en Variable not found.

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

      proyectos Ágiles

      Entrevistas sobre Agilidad, transformación, organización, cultura y software

      noviembre 06, 2017 07:08

      A continuación aparecen diversas entrevistas interesantes y los temas que se tratan en ellas. No dudéis en leerlas (u oírlas o verlas 🙂 ) para conocer más sobre Agilidad, transformación Digital, nuevos modelos organizativos y cambio cultural.

      CRV

      • Cristina Ramos Vega’s Blog: Impulsando la Agilidad en grandes corporaciones: Haz clic aquí para ir al artículo.
        • Qué es y qué no es Agile.
        • Cómo la transformación digital está forzando la introducción de Agile en las empresas.
        • Para qué Agile.
        • El cambio cultural, resistencia y límites. Agile no es para todo el mundo.
        • Impedimentos para la adopción de Agile.
        • Prácticas ágiles más efectivas.
        • Organizaciones simples y desescaladas.
        • Agilidad en las escuelas.

       

      TYSC-Logo-160x160

      • Radio Comunidad Valenciana – Programa “Tecnología y sentido común”: Haz clic aquí para ir al podcast
        • Qué problemática resuelve Agile.
        • Cuerpos de conocimiento relacionados con la Agilidad.
        • Agilidad en grandes organizaciones.
        • Adopción de Agile y dificultades.
        • Encaje de Agile con la estrategia de la compañía.
        • ¿hay que usar la Agilidad para todo?

       

      cadena_ser

      • Cadena SER: ‘Scrum’, el método de trabajo que hará que su empresa sea más eficiente: Haz clic aquí para ir al artículo
        • La empresa tradicional vs la empresa ágil.
        • Qué es Scrum.
        • Los principios ágiles son más importantes que las prácticas de Scrum.

       

      drive_to_improve-logo

       

      Randstad

       

      revista-emprendedores-haz-tu-negocio-mas-eficiente

       


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

      Koalite

      No pierdas el tiempo escribiendo tests

      noviembre 06, 2017 05:06

      Hace unos días me dieron la oportunidad de hablar sobre testing en el grupo de CrossDevelopment Madrid y algunos me han pedido que compartiera la presentación que utilicé. Aunque podéis descargarla, lo cierto es que sin explicaciones acompañándola sirve de poco. Este post pretende ser una pequeña guía de lo que conté allí. No va a ser tan extenso como la presentación porque sería aburridísimo, pero dado que la mayoría de lo que comenté ya lo había escrito antes en el blog, servirá para repasar algunas ideas sobre testing y enlazaré posts que profundizan en esas ideas (en ocasiones, bastante más de lo que permitía el tiempo de la charla). Aun así, va a ser un post algo más largo de lo habitual.

      Las dificultades iniciales

      Empecé a utilizar tests automatizados hace mucho, sobre el año 2003 más o menos. En aquella época descubrí NUnit y decidí que sería buena idea evitarme probar a mano las aplicaciones, así que me puse a escribir tests sobre la aplicación que estaba desarrollando.

      Era la típica aplicación de la época: N capas, servicios web SOAP, modelo anémico, capa de lógica de negocio que no hacía nada… Os podéis imaginar el resultado. La única forma de testear algo era atacando la base de datos, y para eso tenía que lanzar a mano un script para crearla, otro para preparla… Un desastre. Ejecuté muy pocas veces esos tests y no me sirvieron de nada.

      De ahí pasé a comprender el concepto de test unitario. La clave era testear código independiente de factores externos. Y empecé a escribir tests unitarios. Sólo había un problema: casi no tenía código que no dependiera de algo complicado (una base de datos, un socket, un servidor, etc.). Así que acabé testeando mi modelo anémico. Es decir, mis tests básicamente se limitaban a comprobar que era capaz de asignar propiedades y luego leerlas. Muy poco útil.

      Por suerte tropecé con nuevos conceptos: inversión de dependencias, inversión de control y mocks. Muchos mocks. Bueno, y fakes, y stubs, y spies y demás amigos. Ahora ya podía testear todo lo que quisiera, sólo necesitaba ocultar las depedencias detrás de interfaces y mockearlas.

      Era una época en la que había aprendido de mis errores iniciales y había montado un servidor de integración continua para lanzar los tests con frecuencia e incluso todavía me creía las métricas, por lo que esto de los mocks era algo maravilloso que me permitía tener unos informes estupendos en los que la cobertura rozaba el 100%.

      ¿El problema? Que mis tests no me decían gran cosa. Los tests de interacción era frágiles, difíciles de leer, complicados de mantener y, cuando fallaban, muchas veces no estaba seguro de si lo que estaba mal era el test o el código de producción. De hecho, desde hace mucho tiempo trato de evitar utilizar mocks todo lo que puedo.

      Todo esto puede llevarte a sentirte un poco engañado con esto de testing. Al final o testeas cosas inútiles (getters y setters) o tienes tests frágiles con mocks que no hay quién entienda. Puede parecer un poco exagerado, pero si dais una vuelta por GitHub seguro que encontráis alguna aplicación de esas que se supone que están bien hechas donde muchos tests caen en uno de esos dos casos.

      Bueno, la realidad es un poco más complicada y mi problema real es que no tenía ni idea de cómo escribir buenos tests.

      Para qué escribimos tests

      Para poder escribir buenos tests, lo primero que tenemos que entender es para qué queremos tests, y para eso siempre me gusta recurrir a esta cita de Kent Beck (padre de TDD):

      A mi me pagan por escribir código que funcione, no por escribir tests.

      Nunca podemos perder esto de vista. Los tests son un medio para ayudarnos a conseguir mejorar la calidad de una aplicación, no un fin en si mismo. Escribir tests que no te aportan nada, no tiene ningún sentido. En mi caso, lo fundamental es que un test me aporte confianza en que el código de producción funciona.

      Para ello, necesito:

      • Que valide algo útil. Si el test va a limitarse a validar que los getters y setters funcionan, probablemente mi confianza global en el código de producción no aumente mucho. Me interesa testear las partes de mi aplicación más complicadas, las que más lógica tienen, las que son más propensas a fallos, las que son más importantes para mi aplicación.
      • Que sea comprensible. Si el test es complicado de entender, es díficil saber si realmente testea lo que quiero, y además en caso de fallo tampoco sé si está mal el código de producción o el código de tests (y no, TDD no te salva de esto).
      • Que se ejecuten con frecuencia. No vale de nada tener una suite de 8000 tests, si nunca la ejecutas. Hay que buscar una forma de que se ejecuten con cierta periodicidad (idealmente en cada commit).

      Además, siempre hay que recordar que nada es gratis y los tests tampoco lo son, por lo que también intento que mis tests sean fáciles de mantener y extender.

      Con el tiempo, el código de producción cambia y si hacer cambios en él te supone tener que arreglar cientos de tests que no están relacionados directamente con ese cambio al final en lugar de ayudar se convierten en un elemento de fricción que frena el desarrollo. Si introducir un nuevo parámetro en un constructor hace que tenga que tocar 420 usos de ese constructor en los tests, a lo mejor me lleva a no introducir el parámetro, o hacerlo opcional, o sacrificar el diseño que quiero conseguir en aras de minimizar el mantenimiento de los tests.

      Cómo escribir mejores tests

      Seguro que existen muchas formas de conseguir cumplir con los puntos anteriores, pero la mejor forma que he encontrado hasta ahora es analizar la forma en que se relaciona el código de producción con los tests.

      Al escribir tests encontramos distintos tipos de código a testear. A veces tenemos funciones puras, que son muy cómodas de testear porque sólo necesito pasarles parámetros, obtener resultados, y comprobar si son los esperados. En ocasiones tenemos código que depende del estado interno del objeto o módulo al que pertenece y por tanto el test es algo más complejo. Otra veces ni siquiera podemos observar el resultado del código ejecutado directamente, y tenemos que hacerlo a través de otros objetos. En el peor de los casos, nuestro código no tiene ningún estado observable y lo único que hace es generar efectos colaterales.

      Merece la pena profundizar en los tipos de dependencias que podemos encontrar entre el código de producción y su estado observable para conocerlos mejor.

      Recurriendo otra vez a citas pegadizas, esta vez de Edsger Dijstra:

      La simplicidad es prerrequisito de la fiabilidad

      Si lo que estamos buscando es poder fiarnos de nuestro código y nuestros tests, debemos intentar que nuestro código sea lo más simple posible y tratar de acercarnos a los primeros casos (funciones puras) y alejarnos de los últimos (interacciones no observables directamente). Conseguir esto es más sencillo de lo que parece, y muchas veces podemos encapsular gran parte de la lógica en un modelo sin demasiadas dependencias y refactorizar nuestro código para que tenga menos dependencias.

      Si nos centramos en la parte de los tests propiamente dichos, debemos intentar desacoplarlos al máximo del código que no están testeando para evitar que se vean afectados por cambios en la aplicación que no tienen nada que ver con ellos. Si esto lo hacemos bien, ganaremos además legibilidad en nuestros tests porque tendrán menos código y quedará más claro qué partes son importantes para el test y qué partes no.

      Existen muchas técnicas para ello. Por ejemplo, podemos evitar depender de otros constructores usando ObjectMothers. Cuando eso se nos queda corto y necesitamos tantas configuraciones de objetos que el ObjectMother ya no es práctico, podemos utilizar builders para independizarnos de APIs que no estamos testeando. Y si los builders nos parecen muy trabajosos de escribir, podemos recurrir a métodos de creación.

      Todas estas alternativas ayudan a construir tests más legibles, en los que es más sencillo añadir nuevos casos de tests y que son más resistentes a cambios en código de producción que tiene poco que ver con ellos.

      La pirámide de los tests

      Todo esto está muy bien y nos puede ayudar a escribir tests unitarios que nos aporten más confianza, pero en una aplicación no todo se puede testear con tests unitarios. Normalmente se habla de la pirámide de los tests, que es una forma de categorizar los tests en base a su tipo:

      test-pyramid

      La idea es que en la base de la pirámide tendremos los tests unitarios, es decir, aquellos que son (teóricamente) fáciles de escribir y rápidos de ejecutar. Según vamos ascendiendo por la pirámide, encontramos tests que cuesta más escribir, tardan más en ejecutarse y comprueban partes más grandes del sistema. Se supone que siempre deberías tener muchos tests unitarios, menos de integración, y aún menos de extremo a extremo.

      Desgraciadamente, la pirámide de los tests no siempre funciona.

      Hay aplicaciones, que por el tipo de aplicación que son, no se prestan bien a ser testeadas con tests unitarios, aislados de todo, y necesitan otro tipo de tests. A veces la pirámide te puede llevar a usar diseños muy poco apropiados. Un ejempo claro es una aplicación de reporting contra una base de datos. Si te dejas guiar por la pirámide (y por las técnicas que hemos visto antes), es tentador acabar cargando mucha información en memoria para poder realizar las consultas allí y testearlas con tests unitarios. Lo malo es que cuando llegues a producción y tengas, en lugar de 10 registros, 10 millones, esa aproximación no es válida.

      Hay que ser consciente del tipo de aplicación que estás desarrollando y no sacrificar características básicas, como un rendimiento razonable, en aras de mantener un dibujo muy bonito de una pirámide que has visto por ahí.

      Al salir del mundo de los tests unitarios todo se complica un poco, pero poniendo algo de sentido común se pueden hacer cosas bastante razonables.

      Existen algunos conceptos básicos que debemos tener en cuenta al testear una base de datos: es necesario ser capaz de crearla fácilmente y volver a un estado conocido entre test y test, y debemos prestar especial atención a la forma en que preparamos el setup del test porque suelen requerir más código que en los test unitarios, pero es factible escribir tests legibles contra una base de datos.

      Testear un servidor web tampoco es una tarea imposible. Ni escribir tests contra el interfaz de usuario.

      Obviamente, este tipo de tests son más costosos de desarrollar y de mantener, por lo que lo ideal es poder testear el máximo posible de la aplicación sin ellos (volvemos a la pirámide), pero eso no quiere decir que a veces no sean necesarios.

      Otros usos de los tests

      Una vez que tienes montada una infraestructura de testing, y has conseguido que tu servidor de integración continua los lance periódicamente, es un buen momento para intentar aprovecharla al máximo. Los tests no sólo son útiles para testear “lógica de negocio”, sino que también los puedes utilizar para validar convenciones y evitar despistes.

      Siempre hay partes del código que requieren trabajo repetitivo y que es fácil hacer mal, y los tests nos pueden ayudar a evitarlo. Por ejemplo, asignar la ruta a un controlador usando un atributo, o añadir un atributo Description a un valor enumerado para luego poder mostrar un texto más amigable en pantalla. A veces podemos cambiar el diseño para aprovechar el sistema de tipos y evitar este tipo de errores, pero si no es posible, se pueden escribir tests que validen nuestras convenciones. Para esto, los tests de aprobación resultan especialmente prácticos.

      Con un poco de imaginación se pueden acabar escribiendo tests para validar muchas propiedades de nuestro código y garantizar que se siguen cumpliendo con el paso del tiempo.

      Conclusiones

      Al final, cualquier aplicación se va a testear y lo mejor que puedes hacer es elegir cómo. Intenta evitar que los tests los haga un cliente en producción y dormirás más tranquilo y serás más feliz.

      Para ello, los tests automatizados son una herramienta muy útil (aunque no la única), siempre y cuando te sirvan para aumentar la confianza que tienes en que la aplicación funciona.

      El coste de escribir tests no es despreciable (tampoco el de los errores en producción, ojo), así que trata de asegurarte de escribir tests que te aporten algo. Escribir tests sólo por quedar bien no vale para nada.

      Ni todas las aplicaciones ni todos los equipos de desarrollo son iguales. Habrá casos en que compense utilizar un tipo de test frente a otro. Merece la pena conocer distintas técnicas de testing y trazar una estrategia de testing adecuada a cada caso, teniendo en cuenta el escenario que tienes delante.

      Posts relacionados:

      1. Tests de integración con Entity Framework (II): Escribiendo tests cómodamente
      2. Deconstruyendo la pirámide de los tests
      3. Cómo NO escribir tests unitarios: Resumen final

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

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

      Casio fx-CG50 (Introducción)

      noviembre 05, 2017 06:40

      En el artículo de Calculadoras programables, os explicaba como en su momento opté por sustituir mi FX-4000P por una FX-6300G, no porque realmente necesitase representaciones gráficas, sino porque era la evolución tecnológica lógica. Pues bien, a día de hoy, y tras estar con la Casio FX-991SPX durante casi dos años, con un excelente resultado, ha […]

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

      La entrada Casio fx-CG50 (Introducción) aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

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

      Adrianistán

      Estadística en Python: media, mediana, varianza, percentiles (Parte III)

      noviembre 04, 2017 06:30

      Siguiendo en nuestro camino de la estadística descriptiva, hoy vamos a ver como calcular ciertas medidas relativas a una variable.

      Fichero de ejemplo

      alumno,nota
      Araceli,9
      Manuel,5
      Pablo,7
      Íñigo,4
      Mario,3
      Raúl,4
      Verónica,6
      Darío,10
      Laura,4
      Silvia,6
      Eduardo,2
      Susana,8
      María,5

      Medidas de centralización: media, mediana y moda

      La media aritmética se define como la suma de N elementos dividida entre N. Se trata una medida bastante conocida entre la gente, aunque tiene el inconveniente de que es muy susceptible a valores extremos.

      La mediana es el valor que dentro del conjunto de datos es menor que el 50% de los datos y mayor que el 50% restante.

      La moda es el valor más repetido (solo aplicable a variables discretas).

      Para calcular estas medidas, simplemente seleccionamos la variable estadística del DataFrame y usamos los métodos mean, median y mode respectivamente.

      Ejemplo: Calcula la media, la mediana y la moda de las notas de los alumnos en el examen

      import pandas as pd
      
      df = pd.read_csv("notas.csv")
      
      media = df["nota"].mean()
      mediana = df["nota"].median()
      moda = df["nota"].mode()
      print("""
          Media: %d
          Mediana: %d
          Moda: %d
      """ % (media,mediana,moda))

      Medidas de posición: cuartiles y percentiles

      El concepto es igual al de mediana, salvo que aquí la división ya no es en el 50%. El 25% de las observaciones es menor que el primer cuartil. Los cuartiles abarcan el 25%, 50% y 75% de las observaciones. Los percentiles son una generalización con cualquier porcentaje.

      Ejemplo: ¿Cuál es la nota que tiene como mínimo el 10% más nota de la clase?

      Este enunciado nos pide calcular el percentil 90.

      Usamos quantile y el porcentaje.

      p90 = df["nota"].quantile(0.9)

      El resultado es que el 10% con más nota de la clase ha sacado un 8,8 como mínimo. Mencionar que existen distintos tipos de interpolación para este cálculo. En la referencia podemos consultar cual nos conviene más.

      Medidas de dispersión: desviación típica, rango, IQR, coeficiente de variación

      La desviación típica mide la dispersión de los datos respecto a la media. Se trata de la raíz cuadrada de la varianza, que en sí misma no es una medida de dispersión. Para calcular la desviación típica usamos std y var para la varianza. (ddof=0 es necesario si quieres seguir la definición de desviación típica y varianza de algunas bibliografías, la razón es que hay un parámetro de ajuste que Pandas pone a 1, pero otras librerías ponen a 0). En Excel es la diferencia que hay entre DESVEST.M (ddof=1) y DESVEST.P (ddof=0).

      std = df["nota"].std(ddof=0)
      var = df["nota"].var(ddof=0)
      assert(np.sqrt(var) == std)

      El rango es la diferencia entre el máximo y el mínimo y el rango intercuartílico o IQR es la diferencia entre el tercer y el primer cuartil.

      rango = df["nota"].max() - df["nota"].min()
      iqr = df["nota"].quantile(0.75) - df["nota"].quantile(0.25)

      El coeficiente de variación es una medida que sirve para comparar entre dos muestras, cuál varía más y cuál es más estable. Es una simple división, de la desviación típica sobre la media, sin embargo, SciPy nos ofrece una función ya preparada.

      import pandas as pd
      import scipy.stats as ss
      
      df = pd.read_csv("notas.csv")
      
      cv = df["nota"].std(ddof=0) / df["nota"].mean()
      cv2 = ss.variation(df["nota"])
      assert(cv == cv2)

      Medidas de asimetría

      Para saber si los datos estan repartidos de forma simétrica existen varios coeficientes: Pearson, Fisher, Bowley-Yule, etc

      Para no liarnos demasiado, podemos usar la función skew de SciPy.

      asimetria = ss.skew(df["nota"])

      Para valores cercanos a 0, la variable es simétrica. Si es positiva tiene cola a la derecha y si es negativa tiene cola a la izquierda.

      Y con esto hemos visto los datos que se pueden extraer de una sola variable.

      La entrada Estadística en Python: media, mediana, varianza, percentiles (Parte III) aparece primero en Blog - Adrianistan.eu.

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

      Blog Bitix

      Ordenar alfabéticamente cadenas con la clase Collator en Java

      noviembre 04, 2017 10:00

      Sin usar la clase Collator incluida en el JDK al ordenar alfabéticamente una lista de palabras obtendremos en algún caso un resultado que nos extrañará y seguramente no sea lo que esperamos. La clase String implementa la interfaz Comprable pero esta ordenación es en base al valor del código unicode sin tener en cuenta los diferentes niveles de diferencias que se pueden usar según el Locale y la clase Collator.

      Java

      Quizá algunos digan que la aparentemente sencilla tarea de ordenar una lista de palabras es algo fácil de hacer con cualquier lenguaje de programación. La realidad es que puede complicarse en una buena cantidad si se ha de realizar con los caracteres del alfabeto de algunos lenguajes. La ordenación no es tan simple como realizar lo siguiente en código Java:

      Primeramente nos daremos cuenta de que la siguiente lista de nombres de provincias las ordena de una forma que quizá no es la que esperamos. En algunas páginas web se puede observar este error en algunos elementos de selección de opciones de nombres de paises, ciudades, provincias u otro conjunto de elementos que suelen estar ordenados alfabéticamente para facilitar el encontrar el elemento a seleccionar pero que si se da el caso de que hay variación de palabras con tildes y sin ellas y minúsculas y mayúsculas se presenta la ordenación incorrecta.

      Como se observa las palabras con letras mayúsculas se ordenan antes que las palabras con letras en minúscula independientemente de la letra del alfabeto, seguramente esta no es la ordenación deseada. En algunos lenguajes como el español algo similar ocurre con las palabras que llevan tilde en alguna letra. Convertir las palabras a mayúsculas o minúsculas o eliminar las tildes por los mismos sin tilde previamente a hacer la ordenación además de no ser una buena solución no sirve para otros lenguajes con diferentes formas de tilde y marcas en las letras.

      En Java la solución es utilizar la clase Collator, esta clase establece varios niveles en las que las letras se consideran diferentes. Por ejemplo, en español las letras e y f se consideran diferencias primarias (diferentes letras), e y é son diferencias secundarias (diferentes tildes) y e y E son diferencias terciarias (diferencias entre mayúsculas y minúsculas). Las diferencias entre caracteres dependen del Locale y un Collator se obtiene en base a él con el método estático getInstance​(Locale). Con el método setStrength(int) se establece el nivel de diferencias deseadas.

      Usando la clase Collator y realizando la ordenación de la misma lista anterior el resultado es diferente y seguramente más apropiado. Como la clase Collator implementa la interfaz Comparable podemos usarla como el comparador aplicar en el método Arrays.sort(T[], Comparator<? super T>) o List.sort(Comparator<? super E>).

      Utilizando el Collator con solo diferencias primarias Cantabria se ordena al final de la lista por tener las letras a, A y Á una diferencia primaria con C. Con diferencias secundarias las letras A y a se ordenan antes que Á por tener diferencias secundarias. Finalmente, con diferencias terciarias a se ordena antes que A.

      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...

      Adrianistán

      Estadística en Python: manipulando datos en Pandas (Parte II)

      noviembre 01, 2017 02:40

      Antes de pasar a otros temas vamos a mencionar como podemos manipular los DataFrame en Pandas. Imaginemos que tenemos una tabla con datos de estatura y peso. Podemos generar una nueva columna con el índice de masa corporal. Veamos como se puede hacer

      Fichero de ejemplo

      nombre,peso,altura
      Basilio,67,1.5
      Arturo,80,1.7
      Cristina,50,1.4
      Alfonso,100,2.0
      Nerea,70,1.8

      Seleccionado datos

      A veces queremos quedarnos con parte de los datos que cumplen una condición. Hay varias maneras de hacerlo.

      Ejemplo: Quédate con los datos de Nombre y Altura de los pacientes con peso igual o superior a 70

      import pandas as pd
      
      df = pd.read_csv("pesos.csv")
      
      tab = df.loc[df["peso"] >= 70,["nombre","altura"]]

      import pandas as pd
      
      df = pd.read_csv("pesos.csv")
      
      tab = df[df["peso"] >= 70][["nombre","altura"]]

      import pandas as pd
      
      df = pd.read_csv("pesos.csv")
      
      tab = df.query("peso >= 70")[["nombre","altura"]]

      Cualquiera de estos tres métodos pueden usarse indistintamente.

      Apply

      Apply es una función de DataFrame muy potente que permite aplicar una función a todos las columnas o a todas las filas.

      Ejemplo: Calcule el IMC (Índice de Masa Corporal) con los valores de la tabla

      import pandas as pd
      
      df = pd.read_csv("pesos.csv")
      
      def imc(x):
          return x["peso"]/(x["altura"]**2)
      
      df["imc"] = df.apply(imc,axis=1)
      print(df)

      Drop

      ¿Qué pasa si queremos borrar algún dato o columna?

      Si queremos borrar columnas:

      df.drop(["peso"],axis=1)

      Si queremos borrar datos:

      df.drop(ELEMENTOS,inplace=True)
      
      # puede haber una condicion compleja
      df.drop(df[df["altura"] < 1.6].index,axis=0,inplace=True)

      Construyendo el DataFrame a mano

      Normalmente los datos los leeremos de algún archivo o base de datos (read_csv, read_json, read_html, read_sql, read_hdf, read_msgpack, read_excel, read_pickle, read_gbq, read_parquet, …) pero puede darse el caso de que necesitemos ingresar los datos manualmente. El constructor de DataFrame admite diccionarios, arrays de NumPy y arrays de tuplas.

      import pandas as pd
      import numpy as np
      
      datos_dict = {"peso": [50,60], "altura": [1.6,1.7]}
      df = pd.DataFrame(data=datos_dict)
      
      datos_numpy = np.array([[50,1.6],[70,1.7]])
      df = pd.DataFrame(data=datos_numpy,columns=("peso","altura"))
      
      datos_tuple = [(50,1.6),(70,1.7)]
      df = pd.DataFrame(data=datos_tuple,columns=("peso","altura"))

       

      Concatenar DataFrames

      Si tenemos varios DataFrames de características similares (columnas iguales) podemos unirlos. Hay que tener cuidado con los índices. Si el tema de los índices te da igual, usa ignore_index.

      import pandas as pd
      
      df = pd.read_csv("pesos.csv")
      
      datos1 = [("Emilio",78,1.6),("Rosa",80,1.8)]
      df1 = pd.DataFrame(data=datos1,columns=("nombre","peso","altura"))
      
      datos2 = [("Agustín",75,1.6),("Ana",90,1.8)]
      df2 = pd.DataFrame(data=datos2,columns=("nombre","peso","altura"))
      
      df = pd.concat([df,df1,df2],ignore_index=True)

      Join DataFrames

      Si vienes del mundo SQL quizá te suene el tema de los JOIN. En Pandas existe un potente sistema de join, similar al usado en las bases de datos SQL más importantes y con excelente rendimiento. Pandas soporta joins de tipo LEFT, RIGHT, OUTER e INNER.

      import pandas as pd
      
      df = pd.read_csv("pesos.csv")
      
      otros_datos = [("Nerea",19),("Irena",21)]
      tab_edad = pd.DataFrame(data=otros_datos,columns=("nombre","edad"))
      
      tab_right = pd.merge(df,tab_edad,on="nombre",how="right")
      tab_left = pd.merge(df,tab_edad,on="nombre",how="left")
      tab_inner = pd.merge(df,tab_edad,on="nombre",how="inner")
      tab_outer = pd.merge(df,tab_edad,on="nombre",how="outer")

      Con esto ya sabemos lo básico para manejarnos con DataFrames de Pandas

       

      La entrada Estadística en Python: manipulando datos en Pandas (Parte II) aparece primero en Blog - Adrianistan.eu.

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

      Adrianistán

      Estadística en Python: Pandas, NumPy, SciPy (Parte I)

      octubre 31, 2017 04:11

      Recientemente SciPy anunció que lanzaba la versión 1.0 tras 16 años en desarrollo. Con motivo de este acontecimiento voy a realizar una serie de tutoriales sobre estadística en Python. Quiero hacer especial énfasis en la resolución de problemas estadísticos, con problemas reales que vamos a ir resolviendo. Usaremos Python 3, NumPy, Pandas, SciPy y Matplotlib entre otras.

      ¿Por qué Python para estadística?

      Python se ha convertido en uno de los lenguajes más usados en la comunidad de data science. Se trata de un lenguaje cómodo para los matemáticos, sobre el que es fácil iterar y cuenta con unas librerías muy maduras.

      Instalando el stack estadístico en Python

      Yo voy a usar Python 3. Tanto si estamos en Windows, macOS o Linux podemos instalar NumPy, SciPy, Pandas y Matplotlib con este simple comando

      pip3 install numpy scipy matplotlib pandas --user

      ¿Para qué sirve cada cosa?

      NumPy sirve para realizar cálculos numéricos con matrices de forma sencilla y eficiente y tanto SciPy como Pandas la usan de forma interna. NumPy es la base del stack científico de Python.

      SciPy es una colección de módulos dedicados a diversas áreas científicas. Nosotros usaremos principalmente el módulo stats.

      Pandas es una librería que permite manipular grandes conjuntos de datos en tablas con facilidad. Además permite importar y exportar esos datos.

      Matplotlib permite realizar gráficos y diagramas con facilidad, mostrarlos en pantalla o guardarlos a archivos.

      Estadística descriptiva

      Vamos a comenzar con la parte de la estadística que trata de darnos un resumen del conjunto de datos.

      Para ello vamos a definir el concepto de variable estadística como la magnitud o cualidad de los individuos que queremos medir (estatura, calificaciones en el examen, dinero en la cuenta,…). Las variables pueden ser cualitativas o cuantitativas y dentro de las cuantitativas pueden ser continuas y discretas.

      Fichero de ejemplo

      En este post voy a usar este fichero para los ejemplos (guardalo como notas.csv).

      alumno,nota
      Araceli,9
      Manuel,5
      Pablo,7
      Íñigo,4
      Mario,3
      Raúl,4
      Verónica,6
      Darío,10
      Laura,4
      Silvia,6
      Eduardo,2
      Susana,8
      María,5

      Cargando datos

      Para cargar datos y manipular los datos usamos Pandas. Pandas permite cargar datos de distintos formatos: CSV, JSON, HTML, HDF5, SQL, msgpack, Excel, …

      Para estos ejemplos usaremos CSV, valores separados por comas:

      import pandas as pd
      
      df = pd.read_csv("notas.csv")

      df es un objeto de tipo DataFrame. Es la base de Pandas y como veremos, se trata de un tipo de dato muy flexible y muy fácil de usar. Si ahora hacemos print a df obtenemos algo así:

      Tabla de frecuencias

      Si tenemos una variable discreta o cualitativa una cosa habitual que se suele hacer es construir la tabla de frecuencias. Con ella sabemos cuantas veces se repite el valor de la variable. Para crear tablas de frecuencia usamos crosstab. En index indicamos la variable que queremos contar y en columns especificaos el nombre de la columna de salida. crosstab devuelve otro DataFrame independiente.

      Ejemplo: ¿Cuántos alumnos han sacado un 5 en el examen?

      import pandas as pd
      
      # Leer datos
      df = pd.read_csv("notas.csv")
      
      # Generar tabla de frecuencias
      tab = pd.crosstab(index=df["nota"],columns="frecuencia")
      print(tab)
      
      # Buscar el elemento 5 (el elemento que cumple la condición de que su índice es igual a 5)
      fila = tab.loc[tab.index == 5]
      # Obtenemos el valor "frecuencia" de la fila
      x = fila["frecuencia"]
      x = int(x)
      print("%d alumnos han sacado un 5" % x)

      Ejemplo: ¿Cuántos alumnos han aprobado (sacar 5 o más)?

      import pandas as pd
      
      df = pd.read_csv("notas.csv")
      
      tab = pd.crosstab(index=df["nota"],columns="frecuencia")
      print(tab)
      
      x = tab.loc[tab.index >= 5]["frecuencia"].sum()
      x = int(x)
      print("%d alumnos han aprobado el examen" % x)

      En estos ejemplo usamos loc para devolver las filas que cumplan la condición descrita entre corchetes. En el último ejemplo como el resultado son varias filas, nos quedamos con la parte de las frecuencias y sumamos todo, para así obtener el resultado final.

      Diagrama de sectores

      Una forma sencilla de visualizar datos que ya han sido pasados por la tabla de frecuencias es el diagrama de sectores. Usando Matplotlib podemos generar gráficos en los que podemos personalizar todo, pero cuyo uso básico es extremadamente simple.

      import pandas as pd
      import matplotlib.pyplot as plt
      
      df = pd.read_csv("notas.csv")
      
      tab = pd.crosstab(index=df["nota"],columns="frecuencia")
      
      plt.pie(tab,labels=tab.index)
      plt.xlabel("Notas del examen")
      plt.savefig("notas.png")

      Gráfica generada por Matplotlib

      Ejemplo: Haz un diagrama de sectores donde se vea claramente el porcentaje de aprobados frente al de suspensos

      import numpy as np
      import pandas as pd
      import matplotlib.pyplot as plt
      
      df = pd.read_csv("notas.csv")
      
      tab = pd.crosstab(index=df["nota"],columns="frecuencia")
      
      aprobados = tab.loc[tab.index >= 5]["frecuencia"].sum()
      suspensos = tab.loc[tab.index < 5]["frecuencia"].sum()
      
      data = np.array([aprobados,suspensos])
      plt.pie(data,labels=["Aprobados","Suspensos"],autopct="%1.1f%%")
      plt.xlabel("Notas del examen")
      plt.savefig("notas.png")

      Destacar que aquí hemos usado np.array para crear un array de NumPy en vez de una lista nativa de Python.

      Diagrama de barras

      De forma similar es posible generar un diagrama de barras:

      Ejemplo: Genere un diagrama de barras de las notas obtenidas por los alumnos en el examen

      import pandas as pd
      import matplotlib.pyplot as plt
      
      df = pd.read_csv("notas.csv")
      
      tab = pd.crosstab(index=df["nota"],columns="frecuencia")
      
      plt.bar(tab.index,tab["frecuencia"])
      plt.xlabel("Notas del examen")
      plt.savefig("notas.png")

       

       

      La entrada Estadística en Python: Pandas, NumPy, SciPy (Parte I) aparece primero en Blog - Adrianistan.eu.

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

      Variable not found

      Enlaces interesantes 298

      octubre 30, 2017 07: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

      Patata caliente

      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...

      Poesía Binaria

      Software libre para edición de vídeo en GNU/Linux. ¿Por dónde empezar?

      octubre 27, 2017 08:14

      Me encanta el mundo de la edición de vídeo. Y, durante algunos años tuve que dejar un poco de lado ese mundo cuando cambié mi sistema operativo principal por GNU/Linux. Puesto que, las herramientas disponibles en los primeros años del siglo XXI no eran compatibles con GNU/Linux. A día de hoy tenemos muchas opciones, tanto libres como privativas, aunque dada mi experiencia, no todas valen si tu propósito es realizar un montaje de vídeo sin aumentar demasiado tus niveles de estrés.

      Mi experiencia personal

      Tanto en el post como en los vídeos trataré de mi experiencia personal. Ésta ha posido ser muy buena con algunos programas y mala con otros. Cada uno de nosostros es un mundo y nuestra interacción con el software un misterio (conozco gente a la que le funcionaba bien Windows ME). Así que, sobre todo en el mundo GNU/Linux en el que cada distribución es un universo y está optimizada de una forma u otra, tiene unas versiones determinadas de unas bibliotecas y un kernel tuneado con las opciones que los responsables de la distro creen convenientes; puede que un programa que a mí no me arranca, a otro usuario sí que le funcione y viceversa.

      Actualmente utilizo Linux Mint 18.2 derivada de Ubuntu 16.04 y todos los programas han sido probados en mayor o menor medida antes de hablar de ellos.

      Software para edición de vídeo en GNU/Linux

      Como en un post anterior puse la primera parte del vídeo, pondré primero la segunda parte.

      Segunda parte

      Primera parte

      Programas recomendados

      Aunque en el vídeo hablo de Pitivi, LiVES, Openshot, Kdenlive, Cinelerra, Blender, Flowblade y Shotcut, que son los desarrollos más grandes para realizar un montaje de vídeo (podremos utilizar muchos programas más en la edición de vídeo, y quiero detallarlos en futuros posts). Me tomaré la libertad de resaltar algunos:

      Kdenlive

      Este programa mezcla perfectamente una curva de aprendizaje suave con gran potencia de edición y estabilidad. Personalmente siempre lo recomiendo para realizar montajes de vídeo en GNU/Linux y, sobre todo desde hace unos años. Cada versión nueva corrige muchos de los problemas anteriores. No es perfecto, porque ni siquiera los programas comerciales más avanzados lo son, pero podemos hacer grandes cosas con él.
      Incluso es muy sencillo desarrollar efectos si te pones a programar.
      Además, un detalle que me sorprendió, fui capaz de abrir un archivo hecho con una versión de Kdenlive de hace 6 años sin ningún problema, por lo que, aunque han añadido características al formato, han sabido mantener la retrocompatibilidad.

      Blender

      En realidad es un editor de 3D para modelado, texturizado, animación, juegos, composición, montaje y muchas más cosas. Desde hace años, es casi exclusivamente el programa que utilizo para el montaje de vídeo. Aunque al principio puede llegar a ser duro (se basa mucho en atajos de teclado) y el tratamiento de los clips de vídeo tal vez no es al que estamos acostumbrados si venimos de Premiere, Movie Maker, MediaStudio, etc. Al final te acostumbras. Y descubres que la edición básica de clips (cortar, mover, transición, overlay, etc) llega a ser muy rápida y mecánica. Además, al ser en realidad un editor 3D podemos aprovechar muchas de sus características a la hora de editar vídeo, podemos crear transiciones personalizadas, insertar objetos 3D, pequeñas animaciones y mucho más. Todo sin salir del programa.

      Como punto débil he de señalar que tiene pocos efectos predefinidos y crear nuevos efectos o filtros puede ser horrible. Por ejemplo, introducir un efecto sepia puede ser duro y el render tardará bastante tiempo. Eso sí, va a salir un sepia perfecto, y personalizable al 100%.

      Cinelerra

      Con un desarrollo constante es otro de los grandes. Podemos invertir horas en edición de vídeo y tener un sistema 100% estable. Aunque para mi gusto los dibujos son muy feos, la experiencia final es muy buena. Claro está, si inviertes unas horas en hacerte con el programa. Cinelerra es de nuevo otro ejemplo con el que, los que vienen de otro programa van a quedar en shock con la forma de hacer las cosas de éste. Pero en cuanto te propones crear un pequeño vídeo, y realizar el ejercicio de descargar clips de la cámara digital e insertar algún efecto, no te decepcionará.

      Openshot

      Dejamos Openshot en el limbo. La primera versión prometía, pero era inestable. La segunda versión ha tardado más de lo previsto y aún quedan muchos aspectos por pulir y bugs que corregir. Están garantizando compatibilidad con Windows y Mac OSX. Las herramientas las van perfeccionando poco a poco aunque no lo veo usable del todo. Tiene problemas muy difíciles de depurar en el uso de memoria y CPU, aunque va progresando poco a poco.
      Si te gusta experimentar, o vas a montar un vídeo sin muchos clips, capas o efectos, puedes darle una oportunidad.

      ¿Cómo montas tú el vídeo en GNU/Linux?

      ¿Qué programa sueles utilizar para obtener los mejores resultados? ¿Qué característica le pedirías a tu editor perfecto?

      The post Software libre para edición de vídeo en GNU/Linux. ¿Por dónde empezar? appeared first on Poesía Binaria.

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

      Poesía Binaria

      Píldora Bash: Incluir archivos en nuestro script sin miedo a que nos cambien el directorio de ejecución

      octubre 24, 2017 05:50

      Incluir archivos en Bash sin complicaciones

      Es una buena práctica en cualquier lenguaje de programación, siempre que sea posible, el tener el código dividido en varios archivos. Esas divisiones harán que nuestro código esté mejor organizado en bloques o compartimentos diferenciados. Y, por supuesto, en nuestros shell scripts no vamos a ser menos.

      De hecho, es muy común tener varios shell scripts en un directorio y todos ellos compartirán un código común. Y, por supuesto, está muy feo copiar y pegar ese código común en todos los archivos. Principalmente porque si necesitamos modificar ese código, debemos cambiarlo en todos los archivos, y eso puede llegar a ser muy pesado.

      Por ello, vamos a hacer un pequeño programa, un hola mundo para la inclusión de scripts en Bash. Muy sencillo:
      principal.sh:

      1
      2
      3
      4
      5
      #!/bin/bash

      . funciones.sh

      holamundo

      functiones.sh:

      1
      2
      3
      4
      function holamundo()
      {
              echo "Hola Mundo!"
      }

      ¿Por qué no ./funciones.sh?

      Cuando utilizamos ./funciones.sh, o bash funciones.sh estaremos ejecutando el fichero y, las variables y funciones declaradas dentro de funciones.sh no podrán ser llamadas desde principal.sh. Más o menos nos interesa que el archivo principal contenga las declaraciones que hay en funciones.sh.

      Muy sencillo, ahora la pega

      Esto en teoría está muy bien, pero en la pŕactica se nos van a presentar algunos problemas:

      No llamamos al script desde el directorio donde está creado

      Sino que ponemos su ruta absoluta o relativa, o puede que lo tengamos en el $PATH. En mi caso yo tengo el script en /home/gaspy/scripts/principal.sh. Si estoy en mi $HOME puedo escribir:

      scripts/principal.sh

      o
      /home/gaspy/scripts/principal.sh

      o si tengo /home/gaspy/scripts en mi variable PATH, puedo estar en /tmp o en cualquier ruta de mi ordenador y ejecutar simplemente:
      principal.sh

      El problema aquí es que principal.sh siempre buscará functiones.sh en el directorio actual desde el que lo estemos ejecutando, y no siempre entrará en /home/gaspy/scripts/.

      Una solución muy sencilla es escribir la ruta absoluta donde se encuentra el fichero al incluirlo (en principal.sh):

      1
      2
      3
      4
      5
      #!/bin/bash

      . /home/gaspy/scripts/funciones.sh

      holamundo

      Así, en principio, no vamos a tener problema. Aunque si compartimos nuestros scripts con nuestros amigos, o los publicamos en github, estaríamos obligando a todo el mundo a tener el script en la misma ruta, y a lo mejor alguien quiere tenerlos en otro directorio, /home/pepe/scripts, /home/alice/scripts/, o /home/strawberry/local/bin.

      Entonces tenemos que hacer que esta inclusión sea independiente de la ruta desde la que se llame el programa. Para ello tenemos varias formas. En principal.sh:

      1
      2
      3
      4
      5
      6
      #!/bin/bash

      SCRIPT_SOURCE="$(dirname "${BASH_SOURCE[0]}")"
      . "$SCRIPT_SOURCE"/funciones.sh

      holamundo

      De esta manera, almacenamos en la variable SCRIPT_SOURCE el directorio donde está el script que hemos llamado, es decir, principal.sh y en función de la ruta de principal.sh incluimos funciones.sh . Por lo que, si nuestro funciones.sh está dentro de lib podríamos hacer:

      1
      2
      SCRIPT_SOURCE="$(dirname "${BASH_SOURCE[0]}")"
      . "$SCRIPT_SOURCE/lib/funciones.sh"

      Son importantes las comillas dobles que vemos en el código, ya que si el directorio donde se encuentran los archivos tiene espacios (o, mejor dicho, tiene algún carácter del $IFS) podemos tener un problema al localizarlos.

      Por ahora, todo parece funcionar bien. Tanto si llamamos desde ruta absoluta, relativa, incluimos el ejecutable en el $PATH o ejecutamos el archivo desde el mismo directorio. Pero, como somos muy enrevesados, vamos a dar una vuelta de tuerca más. Creando un enlace al archivo ejecutable en otro directorio y llamando a éste.

      ln -s /home/gaspy/scripts/principal.sh /usr/local/bin
      /usr/local/bin/principal.sh

      Aunque bien podríamos haber ejecutado simplemente principal.sh porque /usr/local/bin/ suele estar en el $PATH y no tenemos por qué poner la ruta completa. El problema aquí es que el script que ejecutamos es /usr/local/bin/principal.sh y funciones.sh no está en /usr/local/bin sino en /home/gaspy/scripts/ por lo que debemos resolver la ruta del enlace para hallar el directorio real donde se encuentran los archivos. Para ello podemos hacer lo siguiente:

      1
      2
      3
      4
      5
      6
      #!/bin/bash

      SCRIPT_SOURCE="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
      . "
      $SCRIPT_SOURCE/funciones.sh"

      holamundo

      En realidad, tener un enlace al ejecutable no es tan raro. Puede que tengamos una serie de scripts en un directorio y sólo queramos que el ejecutable esté en el $PATH o tal vez tengamos un directorio cuyos archivos se ejecutan cuando ocurre un evento, en lugar de tener los ejecutables en dicho directorio podemos tener enlaces a los mismos y si algo cambia no tenemos que tocar los archivos, sólo los enlaces.

      Hasta el momento, igual que utilizamos ${BASH_SOURCE[0]}, podríamos utilizar $0, al final es el nombre del archivo que se ha ejecutado. Aunque $BASH_SOURCE tiene una funcionalidad extra, contiene todos los archivos que se han incluido para llegar al archivo actual. Si A, incluye B y B incluye C, desde A, BASH_SOURCE sólo contendrá un archivo, desde B, contendrá 2 y desde C contendrá 3.

      Archivos que no deben ser ejecutados

      En nuestro caso, funciones.sh no debe ser ejecutado directamente. Podemos quitarle permisos de ejecución, pero tampoco nos frenaría demasiado. El objetivo de no poder ejecutar el script es que pueda revelar información del sistema o pueda causar un mal funcionamiento. Así que estaría bien que el script se detuviera cuando intentemos ejecutarlo. Esto lo haremos con $BASH_SOURCE, contando cuántos elementos tiene. En este caso, si el array tiene sólo un elemento estaremos ante una ejecución directa del programa. Si $BASH_SOURCE tiene más elementos, estaremos incluyendo el archivo desde otro ejecutable principal. Con esto, incluso podremos restringir quién nos incluye.

      Podemos hacer por ejemplo en funciones.sh

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

      if ((${#BASH_SOURCE[@]}==1)); then
              echo "Este archivo no puede ser ejecutado" >&2
              exit
      fi

      function holamundo()
      {
              echo "Hola Mundo!"
      }

      echo "Inicializo funciones..."

      Por lo tanto, lo primero que verifica el archivo es que no es una ejecución directa. Así el programa funcionará si llamamos a principal.sh (incluso mostrando el mensaje “Inicializo funciones…” y nos mostrará un error si llamamos a funciones.sh. También podemos hacer exit directamente, eso ya, a nuestro gusto.

      Foto principal: Olga Gorbunova

      The post Píldora Bash: Incluir archivos en nuestro script sin miedo a que nos cambien el directorio de ejecución appeared first on Poesía Binaria.

      » 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