Cómo hacer Tooltips personalizados solamente con CSS

26/10/2017
Artículo original

Los tooltips son esas ventanitas flotantes pequeñas de color amarillo que aparecen cuando pasas por encima de algunos elementos de las interfaces de usuario de muchas aplicaciones. Los controla el sistema y poco puedes hacer para controlar su aspecto o ubicación.

En el caso concreto de la Web, las páginas HTML solamente ofrecen este tipo de tooltips en caso de que utilicemos el atributo title de algunos elementos:

Pero en el caso de querer mostrarlo en elementos concretos de un texto, por ejemplo, no tendremos tanta suerte. Además de que no podemos controlar el aspecto que tienen ni cómo se muestran.

Mediante JavaScript es fácil detectar el movimiento del cursor sobre un determinado elemento o un trozo de texto y mostrar una capa con el aspecto que deseemos en el lugar que queramos, pero no deja de ser un trabajo pesado y tedioso, además de propenso a errores y que carga un poco más la página.

En este post voy a explicar cómo podemos crear tooltips con el aspecto que deseemos, que aparezcan donde queramos y utilizando exclusivamente  CSS, sin escribir nada de código.

Para ello haremos uso de varias cuestiones fundamentales de CSS.

  • Elementos de datos de HTML5
  • Pseudo-elementos y pseudo-clases de CSS
  • La función CSS attr()
  • Posicionamiento de elementos

entre otras, y las utilizaremos para crear tooltips automáticos al pasar por encima de ciertas palabras de un texto, sin usar JavaScript.

El efecto que queremos conseguir es este:

Fíjate en cómo al pasar por encima se muestra el tooltip, que tiene el fondo negro semi-transparente para dejar ver un poco lo que hay debajo, y el texto blanco, con los bordes redondeados, pero podría tener el aspecto que quisiésemos, limitados tan solo por las propiedades CSS que le podamos aplicar (que son muchas).

¡Vamos allá!

 Para conseguir el efecto de tooltip, lo que tenemos que hacer es, en primer lugar, definir qué palabras o conjuntos de palabras van a mostrarlos, para lo cual deberemos rodearlos con algún elemento (generalmente un <span>) marcado con alguna clase que denote que es un tooltip. En un alarde de originalidad vamos a llamar a dicha clase .tooltip:

JavaScript es un <span class="tooltip">lenguaje de programación</span> interpretado,

De este modo denotaremos que esas palabras ("lenguaje de programación") van a mostrar un tooltip y además podemos usar también esta clase para darle un aspecto ligeramente distinto de modo que sean fáciles de localizar. Por ejemplo, en este caso yo les he puesto un borde inferior a rayas y les he cambiado el cursor:

span.tooltip {
border-bottom: 1px dashed gray;
cursor: pointer;
position:relative;
}

Además fíjate en otra cosa importante: le he colocado un position:relative;. El objeto de esto es crear un sistema de referencia para posicionamiento dentro de este elemento que me permitirá ahora colocar otros elementos (el tooltip) dentro de él de modo que estén posicionados como necesite a partir de los bordes de la caja de este <span>.

Si no sabes bien de qué va esto me temo que te toca repasar el modelo de cajas y cómo funciona el posicionamiento de elementos en HTML. Te puedo sugerir un recurso sensacional en el que aprenderás eso y mucho más apoyado por mi para resolverte las dudas.

Bien. Ahora hace falta decidir el contenido del tooltip y luego mostrarlo.

Lo primero lo conseguiremos mediante el uso de atributos de tipo data- de HTML5. Estos atributos están diseñados para poder incluir información extra de cualquier tipo dentro de una etiqueta HTML sin necesidad de recurrir a atributos existentes o inventarse atributos nuevos. Ofrecen una API específica en JavaScript para poder leerlos y escribirlos y además se validan perfectamente como HTML correcto semánticamente, por lo que no te darán problemas si tu web tiene que estar validada.

En nuestro caso vamos a definir un atributo data-info (pero podríamos haberle llamado de cualquier manera) para meter la información del tooltip:

<span class="tooltip" data-info="Un lenguaje de programación es...">

con el texto del tooltip metido en el atributo (lo he abreviado aquí con puntos suspensivos).

Para generar el tooltip a partir de este contenido lo que haremos será utilizar el pseudo-elemento CSS ::before. Éste genera un elemento "ficticio", que antes no estaba, y que se coloca antes del contenido del elemento al que se le aplica. De modo que si escribiese:

span.tooltip::before {
    content: 'Un texto aquí';
}

Lo que obtendría sería un elemento colocado dentro del span pero antes del contenido de éste con el texto 'Un texto aquí'.

Sabiendo esto lo tenemos muy fácil y solo nos faltan dos detalles por solucionar:

  • ¿Cómo ponemos en este elemento el contenido del texto del tooltip?
  • Cómo hacemos que se muestre al pasarle por encima solamente?

Para lo primero tenemos que echar mano de la función CSs llamada attr(). Ésta lo que hace es obtener el valor de un atributo que le indiquemos del elemento sobre el que está actuando, en función del selector CSS. Dado que el texto del tooltip lo tenemos en el atributo data-info, lo que tenemos que escribir es simplemente:

span.tooltip::before {
    content: attr(data-info);
}

 

Con lo que ya tendremos el contenido deseado. Ahora debemos, en primer lugar, ocultar este elemento ya que solo queremos que se vea cuando le pasemos por encima con el curso, darle un aspecto determinado que es el que queremos que tenga, y además (muy importante) posicionarlo de manera apropiada para que se vea bien al pasarle por encima.

Para esto último, como el span forma ahora un sistema de referencia porque tiene posición relativa, lo único que tenemos que hacer es posicionarlo de modo relativo o absoluto dentro de éste, por lo que no es muy difícil:

span.tooltip::before {
    display: none; /* Lo ocultamos*/
    content: attr(data-info);
    color: white;
    font-size: 0.8em;
    padding: 2px 8px;
    background: rgba(0,0,0,0.8);
    border-radius: 5px;
    position: absolute; /* Lo posicionamos de modo absoluto dentro del span */
    top: 1.5em;
    left: 5px;
    min-width: 500px;
    z-index: 999;
}

Ahora ya tenemos nuestro tooltip con el aspecto deseado y la posición apropiada, justo debajo del texto que hemos marcado con el span. Aunque, eso sí, oculto de momento.

Nota: le he colocado un ancho mínimo porque si no lo hacemos tomará como ancho exactamente el mismo que tenga el elemento que lo contiene, es decir, el texto de las palabras seleccionadas para mostrar el tooltip. Si tenemos palabras cortas se quedaría muy estrecho y crecería hacia abajo, quedando feo.

De acuerdo. Lo único que nos falta es mostrar el nuevo elemento cuando pasemos el cursor por encima. Esto clama a una pseudo-clase :hover, como en los enlaces. Pero ¿cómo combinamos esta pseudo-clase con el pseudo-elemento ::before que queremos mostrar? Muy fácil así:

span.tooltip:hover::before {
    display: block;
}

Con esto lo que le estamos diciendo al navegador es que cuando se pase por encima de un elemento span con la clase .tooltip aplicada, le aplique a su pseudo-elemento ::before la propiedad display:block, que anula la que tiene por defecto (display:none), y por lo tanto lo que va a hacer es mostrarlo.

¡Listo! Con esto ya tenemos un sistema de tooltips funcional, tal y como queríamos.

Te dejo aquí la página de ejemplo que he creado (ZIP, 1.13KB), de la que grabé la animación del principio, para que puedas verlo todo junto. Verás que es bastante sencillo, pero he querido explicarlo a fondo para que no quede lugar a muchas dudas.

Una cosa importante: el problema de utilizar los tooltips de esta manera es que no cumplen con las reglas de accesibilidad AA de la W3C. Si necesitas que tu sitio sea accesible tendrías que añadirle también la info del tooltip al elemento mediante atributos ARIA. Otra opción es añadirlos automáticamente usando JavaScript (no me gusta mucho, pero no soy experto en accesibilidad) o directamente usar el atributo ARIA para tomar el valor (por ejemplo aria-label) y no utilizar el atributo data- como hemos hecho ahora. La decisión es tuya, pero debes ser consciente de este detalle.

¡Espero que te resulte útil!

¿Cómo crear una empresa en Internet?

26/10/2017
Artículo original

Espero y este post sea de tu agrado, no olvides compartirlo en su redes sociales y sobre todo.. Gracias por seguirnos!

¿Estas por iniciar un negocio en Internet? ¿Necesitas un poco de ayuda? No te preocupes! Hoy te doy algunos tips para crear una empresa en Internet y que ésta tenga éxito en la web ¿Necesito mucho? Realmente no! Únicamente seguir una estrategia y dedicarle mucho tiempo y esfuerzo. A más de 1 año de ser cofundador de una éxitosa empresa de servicios de TI puedo decirte cuales son las claves y puntos importantes a tomar en cuenta a la hora de crear una empresa en Internet. Por qué no, no basta con crearte una página web .blogspot.com y colocar tu

Compartir en Facebook

Comparte en Google Plus

Compartir en Twitter

El post ¿Cómo crear una empresa en Internet? aparece en el Blog de Jonathan Melgoza

Teoría de Categorías en Scala - Composición

24/10/2017
Artículo original

Este es el primer artículo de una serie que voy a escribir sobre teoría de categorías.

Llevaba un tiempo queriendo aprender teoría de categorías, ya que he leido bastante sobre el tema y parece que tiene aplicaciones en el desarrollo de software.

Esta serie de artículos se basan en gran libro que Bartosz Milewski escribió hace un tiempo. Para aportar mi granito de arena, he decidido implementar los ejercicios que propone usando scala y tests basados en propiedades con scalacheck. Espero que te guste esta serie, y no dudes en comentar tu opinión o sugerir/corregir el contenido.

Categoría: La esencia de la composición

Antes de continuar, ¿Qué es una categoría?, de Wikipedia:

En teoría de categorías, una categoría es una estructura algebraica que consta de una colección de objetos, conectados unos con otros mediante flechas tales que se cumplen las siguientes propiedades básicas: las flechas se pueden componer unas con otras de manera asociativa, y para cada objeto existe una flecha que se comporta como un elemento neutro bajo la composición.

En resumen, una categoría está formada por objetos y flechas, y pueden componerse. Esto se aprecia mejor con una imagen:

Arrows and objects category Theory
Ejemplo de Categoría

Cada flecha puede componerse, p.e: Si A -> B y B -> D entonces debe existir una flecha de A -> D. Las flechas pueden llamarse morfismos, son funciones, y como tales se pueden componer. En la imagen de arriba tienes:

  • f: A -> B
  • g: B -> D
  • Si componemos esas dos, también tienes h: A -> D.

La composición se lee de derecha a izquierda, por tanto g∘f quiere decir g(f(x)).

Propiedades de la composición

  1. Asociatividad: f∘(g∘h) == (f∘g)∘h == f∘g∘h.
  2. Para cada objeto existe una flecha unidad, que va del objeto a sí mismo: f∘IDₐ = f == IDₐ∘f = f, como muestro debajo:

Category Theory, object identity

Ejemplos en Scala

Ahora que ya he comentado un poco de la teoría, vamos a implementarlo en scala. Como dije al principio, voy a usar tests basados en propiedades para comprobar que el objeto creado cumple las propiedades algebraicas de una categoría. Estas propiedades son la Identidad y asociatividad.

El código de abajo es la definición de una categoría, puedes consultar el código en el fichero Category.scala de mi respositorio.

object Category {
  def Id[T](x: T) = x
  def compose[A, B, C](f: A => B, g: B => C): A => C =
     f andThen g
}

Es bastante simple, una función identidad y otra función que compone dos funciones. Para comprobar que cumple las propiedades, he escrito los siguientes tests.

Tests basados en propiedades

Para mantener el artículo limpio, muestro solo el código necesario, puedes consultar el código completo de los tests en github.

Propiedad Identidad

property("a == Id(a)") {
  check(forAll { i:String =>
    Category.Id(i) === i
  })
}

property("Id∘f = f") {
  check(forAll { i: Int =>
    Category.Id(square(i)) === square(i)
  })
}

property("f∘Id = f") {
  check(forAll { i: Int =>
    f(Category.Id(i)) === f(i)
  })
}

La primera propiedad manifiesta que para todo String posible que se le pase a la función identity, la identidad siempre será la cadena de texto que se le pasó a la función.

La segunda y tercera propiedad indican que no importa cómo se componga la función identidad con otra función f, ya que el resultado siempre será esa función f.

Propiedad asociativa

property("Associativity: h∘(g∘f) = (h∘g)∘f = h∘g∘f"){
  check(forAll { i: Int =>
    Category.compose(Category.compose(f, g), h)(i) === Category.compose(f, Category.compose(g, h))(i)
  })
}

Como puedes ver, este test comprueba que la propiedad asociativa es cierta.

Si ejecutas estos tests, verás que todos pasan:

Category theory property tests

Eso es todo para esta primera parte, espero que te haya gustado. Me gustaría saber tu opinión, te animo a comentar abajo.

Recursos

¿Qué es la máquina virtual de Java o Java Virtual Machine?

23/10/2017
Artículo original

Cabecera

Los lenguajes de programación de alto nivel como C, C++, Java o Python, por citar unos pocos, sirven para comunicarse con una computadora mediante algo más fácil de entender para un humano que el "lenguaje máquina" o el lenguaje ensamblador, que está muy cercano a la máquina. Estos lenguajes se llaman "de alto nivel" porque están en un nivel de abstracción mucho mayor que el que ofrece un ordenador: tienen bucles, condicionales, matrices, tipos de datos...

Tradicionalmente, el compilador de un lenguaje de alto nivel se encargaba de traducir ese lenguaje "sencillo" en lenguaje máquina, directamente utilizable por el computador a través del sistema operativo. Es decir, cuando compilamos un programa en C++ lo que obtenemos es un programa ejecutable, por ejemplo para Windows, que este sistema operativo es capaz de ejecutar directamente contra el procesador, en un lenguaje "entendible" por este.

Sin embargo muchos lenguajes modernos como Java o C# (y otros lenguajes de la plataforma .NET), lo que hacen es utilizar un paso intermedio entre estos dos estados: entre el código de alto nivel en el que escribimos las aplicaciones y el de bajo nivel que sale del proceso de compilación.

Cuando compilas una aplicación escrita en lenguaje Java, en realidad éste no se compila a lenguaje máquina, directamente entendible por el sistema operativo, sino a un lenguaje intermedio denominado Byte Code. Lo mismo ocurre con las aplicaciones .NET que se compilan también a un lenguaje intermedio llamado MSIL (MicroSoft Intermediate Language).

Entre el Byte Code (o el MSIL en el caso de .NET) y el sistema operativo se coloca un componente especial llamado Máquina virtual que es el que realmente va a ejecutar el código. Esta idea, por cierto, no tiene nada que ver con las máquinas virtuales a las que estamos acostumbrados hoy en día que ejecutan sistemas operativos completos, sino que es un concepto mucho más antiguo. Es más, tampoco te vayas a pensar que esto lo inventó Java. Ni mucho menos. Ya en los años '80, mucho antes de Java, el lenguaje Pascal generaba p-Code que era algo muy similar, e incluso había compiladores de COBOL (¡sí, de COBOL!) que hacían lo mismo.

En el caso de Java, La Java Virtual Machine o JVM toma el código Byte Code resultante de compilar tu aplicación Java y lo compila a su vez a código nativo de la plataforma en la que se está ejecutando. La ventaja principal de este esquema es que es muy fácil crear un programa en Java y que luego éste se pueda ejecutar en cualquier sistema operativo para el cual exista una implementación de la JVM (hoy en día, casi literalmente todos).

El siguiente esquema ilustra bien cuál es este proceso:

Esquema de funcionamiento de la compilación Java

Si lo necesitas, puedes repasar en este enlace los conceptos básicos de la plataforma Java.

Para entendernos, la JVM es una abstracción de una máquina real, que es capaz de entender el Byte Code creado por el compilador de Java y traducirlo en instrucciones nativas equivalente que a su vez el sistema operativo actual es capaz de entender, ejecutando realmente la aplicación.

Existen implementaciones de la JVM para prácticamente la totalidad de sistemas operativos del mercado, no solo para los tres que hemos visto en la figura anterior. Por eso, en la práctica, los programas Java se pueden ejecutar en teoría en cualquier sitio y de ahí su famoso eslogan "Escribe una vez, ejecuta en todas partes".

El secreto del "Write once, write everywhere" es, precisamente, la existencia de la JVM, que es la base de toda la filosofía de la plataforma Java.

La Java Virtual Machine, al igual que su contrapartida real, ejecuta los programas como si fuera una computadora, para lo cual utiliza diversos componentes, del mismo modo que los usaría un procesador real. Entre los componentes más importantes se encuentran los registros, la pila, el recolector de basura... En la siguiente figura, basada en una que aparece en la documentación oficial, podemos los más importantes:

Componentes de la JVM

No vamos a entrar en los detalles de cada uno de los componentes. Para esto tienes la documentación oficial y además es un tema complejo. Pero sí que es interesante conocer un poco mejor uno de ellos: el compilador JIT.

El Compilador Just In-Time o JIT

La ejecución del Byte Code es lenta comparada con la del código nativo en cada plataforma. La JVM primero tiene que cargar el código y traducirlo a instrucciones nativas para poder ejecutarlo, haciendo otras muchas cosas por el medio, como comprobar los tipos, recoger basura, etc...

Una manera de optimizarlo y conseguir un alto rendimiento es hacer una compilación inmediata, lo antes posible, al código nativo de cada plataforma, cacheando además los resultados para reutilizarlos. Esto es básicamente lo que hace este componente, cuyo nombre viene de esta función: Just In-Time Compiler o compilador en tiempo real.

OJO, esto no quiere decir que compile todo el código a código nativo (para eso tendríamos un compilador para cada plataforma, como ocurre con C++, por ejemplo). Lo que hace la JVM es decidir en cada momento qué partes del código se deben compilar con el JIT y cuáles almacenar, cuándo ejecutarlas, etc...

Dado que esa compilación también lleva tiempo, el compilador JIT suele compilar a código nativo el código que se use con mayor frecuencia, pudiendo notar una pequeña demora en la ejecución la primera vez que se compila.

De hecho según la implementación concreta de la JVM, el proceso del compilador JIT puedes estar más o menos optimizado. Por ejemplo, en el caso de JRockKit, la JVM oficial de Oracle, el optimizador para JIT se ejecuta en segundo plano todo el tiempo analizando el uso que se hace del Byte Code y optimizando todo el tiempo, en función de las circunstancias, qué partes de la aplicación se compilan a código máquina. Además todo el tiempo, en función de la ejecución real, se realizan optimizaciones sobre el código original para generar posteriormente a través del compilador JIT nuevo código máquina súper-optimizado. En el artículo Understanding Just-In-Time Compilation and Optimization puedes encontrar más detalles.

El concepto de compilador JIT es posterior al de la JVM y sistemas similares, ya que de hecho se inventó para mejorar el rendimiento de éstos.

En resumen

Para resumir en cuatro puntos clave todo lo explicado anteriormente:

  • Los programas Java se compilan a un formato binario .class que contiene instrucciones en un lenguaje de bajo nivel especial independiente de la plataforma, llamado Byte Code.
  • Existe un componente de Java que debe estar instalado en cada sistema operativo que se llama Java Virtual Machine o JVM. La JVM es capaz de entender el Byte Code y ejecutarlo con las instrucciones equivalentes que haya en el sistema operativo y procesador actuales en el que estemos ejecutándolo.
  • Esto es una gran ventaja porque nos permite ejecutar código Java en cualquier plataforma para la que exista una JVM (en la práctica, casi cualquiera).
  • Dado que la ejecución mediante la JVM es más lenta que la nativa, en ocasiones puede notarse cierta lentitud. Para ello un componente especial de la JVM llamado Compilador JIT o Compilador en Tiempo Real, compila a código nativo las partes de código más comunes o que requieren mayor desempeño.

¡Espero que te resulte útil!

FRIKADAS: Tu corazón será tu nuevo sistema de autenticación

20/10/2017
Artículo original

Investigadores de la Universidad de Búfalo han descubierto un método de seguridad que usa las medidas del corazón para identificar y autenticar al usuario. Según la nota de prensa que ha publicado la propia universidad, el método se basa en un radar Doppler de bajo nivel para determinar las dimensiones del corazón.

La exploración inicial dura aproximadamente ocho segundos, pero después de eso el sistema puede monitorear continuamente el corazón del usuario para asegurarse de que otro usuario no ha puesto en marcha el aparato. Además de facilitar el acceso y el cierre de sesión, el sistema mejora la seguridad, ya que el corazón de cada usuario tiene un conjunto único de dimensiones.

El profesor adjunto Wenyao Xu de la universidad de Búfalo afirma en la nota de presa: "No se han encontrado dos personas con corazones idénticos."

El escáner de corazón sería, junto con la huella dactilar, el escáner del iris, o el reconocimiento facial, un tipo más de seguridad biométrica tan en auge hoy en día. Sin embargo, según el profesor Xu, el nuevo sistema presenta ciertas ventajas frente a las herramientas biométricas actuales. En primer lugar, no requiere contacto, es decir, se trata de un dispositivo pasivo con el que los usuarios no se tienen que molestar en autenticarse cuando se conectan. Y en segundo lugar, monitorea a los usuarios constantemente. Esto significa que el aparato que esté controlando dejará de funcionar si una persona diferente se pone delante de él. Por lo tanto, las personas no tienen que acordarse de cerrar la sesión cuando estén lejos de sus equipos.

Para aquellos que pueden estar preocupados por los posibles efectos en la salud de los escaneos, Xu mencionó en el comunicado en prensa que la fuerza de la señal "es inferior a la de cualquier dispositivo Wi-Fi. Su lector tiene una potencia de 5 miliVatios, lo cual supone menos del 1% de la radiación de nuestros smartphones”, Por lo tanto se puede concluir que no plantea problemas de salud.

El próximo reto para el profesor Xu y su equipo consiste en miniaturizar su invento. Su intención es fabricar un dispositivo muy pequeño que pueda colocarse sobre la esquina de un teclado.

Resumiendo, las tres ideas principal son:

  1. Se trata de un nuevo sistema de autenticación biométrica que utiliza la geometría del corazón del usuario para identificarlo y autenticarlo para usar la máquina.
  2. El sistema tarda ocho segundos en escanear un corazón y, a continuación, puede monitorizar al usuario constantemente para determinar si otro usuario intenta acceder a la máquina.
  3. El escáner puede monitorizar un corazón a 30 metros de distancia y no presenta riesgos para la salud, han afirmado los investigadores.

Multiples vulnerabilidades en RubyGems

19/10/2017
Artículo original

Existen múltiples vulnerabilidades en la version de RubyGem que incluye Ruby. Ha sido reportado en el blog oficial de Rubygems.

Detalles

Fueron reportadas las siguientes vulnerabilidades:

  • una vulnerabilidad de secuestro de peticiones DNS. (CVE-2017-0902)
  • una vulnerabilidad en secuencia de escape ANSI. (CVE-2017-0899)
  • una vulnerabilidad del tipo DoS en el comando query. (CVE-2017-0900)
  • una vulnerabilidad en el instalador de gemas, la cual permite a una gema maliciosa el sobreescribir archivos arbitrariamente. (CVE-2017-0901)

Se recomienda a los usuarios de Ruby a actualizar o tomar alguna de las siguientes opciones tan pronto como sea posible.

Versiones afectadas

  • Ruby 2.2: 2.2.7 y anteriores
  • Ruby 2.3: 2.3.4 y anteriores
  • Ruby 2.4: 2.4.1 y anteriores
  • antes de trunk revision 59672

Soluciones alternativas

Si no puede actualizar Ruby, actualice RubyGems a la última versión. RubyGems 2.6.13 y posteriores incluye la corrección para éstas vulnerabilidades.

gem update --system

Si no puede actualizar RubyGems, puede aplicar el parche correspondiente como una solución alternativa.

En el caso de trunk, actualice a la última revisión.

Créditos

Este reporte está basado en el blog oficial de RubyGems.

Historial

  • Publicado originalmente: 2017-08-29 12:00:00 UTC
  • Agregado el número CVE: 2017-08-31 2:00:00 UTC
  • Mención de la actualización de Rubies: 2017-09-15 12:00:00 UTC

Publicado por usa el 2017-08-29
Traducción de Espartaco Palma

Chuleta de comandos para Org-Mode

18/10/2017
Artículo original

Este artículo es una referencia rápida de los comandos más útiles de org-mode, es una versión de una página del manual oficial, pero ya que lo visitaba una y otra vez, decidí crear este post que recopile los comandos que más uso.

Org mode quick reference
Org mode quick reference

Para empezar, con <TAB>, ciclas sobre las distintas secciones, expandiendo/contrayendo.

Secciones

* Top level headline
** Second level
*** 3rd level
    some text
*** 3rd level
    more text

* Another top level headline

Ciclar

Para ciclar sobre las secciones:

  • <TAB> Cicla el sub-árbol.
  • S-<TAB> o C-u <TAB> Cicla globalmente todas las secciones.
  • C-u C-u C-u <TAB> Expande todas las secciones.

Moverse entre secciones

  • C-c C-n Siguiente sección.
  • C-c C-p Sección anterior.
  • C-c C-f Siguiente sección del mismo nivel.
  • C-c C-b Sección anterior del mismo nivel.
  • C-c C-u Ir subiendo a secciones superiores.

Insertar/modificar secciones

  • M-<RET> Insertar una nueva sección, del mismo nivel que el actual. Si el cursor está a mitad del párrafo, divide el párrafo en dos secciones.
  • M-S-<RET> Inserta un elemento TODO en el mismo nivel.
  • <TAB> En una sección vacía, ciclar sobre los niveles.
  • M-<left>/<right> Incrementar/decrementar un nivel.
  • M-S-<left>/<right> Incrementar/decrementar un nivel el sub-árbol actual.
  • M-S-<up>/<down> Mover el sub-árbol arriba o abajo.
  • C-c C-w Mover la sección o región a otro lugar.
  • C-x n s/w Reducir/ampliar el bufer al sub-árbol actual.

Árboles dispersos

Útil para ocultar partes del documento en las que no estamos trabajando actualmente.

  • C-c <barra inclinada> Pregunta cómo crear el árbol.
  • C-c <barra inclinada> r Crea el árbol disperso en base a una expresión regular, para desmarcar el resaltado pulsar C-c C-c.

Listas

  • No ordenadas: -, + o *.
  • Ordenadas: 1. o 1)
  • Descripción: Usa :: para separar el término de la descripción.
  • <TAB> Ocultar los elementos.
  • M-<RET> Insertar nuevo elemento al mismo nivel.
  • M-S-<RET> Insertar elemento con checkbox.
  • M-S-<up>/<down> Mover elementos arriba/abajo, incluyendo sub elementos.
  • M-<left>/M-<right> Incrementar/decrementar nivel.
  • M-S-<left>/<right> Incrementar/decrementar nivel incluyendo sub elementos.
  • C-c C-c Si hay un checkbox, cambiar el estado.
  • C-c - Ciclar sobre los distintos tipos de listas.

Notas al pie

  • C-c C-x f Cuando el cursor está en una referencia, ir a la definición (o vice versa), de lo contrario crea una nueva nota al pie.
  • C-c C-c Saltar entre definición y referencia.

Un ejemplo de una nota al pie 1. El código que la genera es:

Un ejemplo de una nota al pie[fn:1].

[fn:1] Click en return para volver a la referencia

Enlaces

Sintáxis: [[enlace][descripción]] o solo [[enlace]], una vez creado, se puede editar con C-c C-l.

Si no hay una URL, el enlace se considera interno al documento:

[[#custom-id]]
[[My Target][Find my target]]

El último ejemplo, busca en el documento actual <<My Target>> y enlaza a él.

  • C-c l Almacena un enlace a la posición actual.
  • C-c C-l Inserta el enlace, pregunta por la url y descipción, si se llama con el prefijo C-u, se usa autocompletado.
  • C-c C-l Con el cursor en un enlace, lo edita.
  • C-c C-o o mouse-1 o mouse-2 abre el enlace.

TODO items

Toda sección comenzando con TODO es un elemento TODO (lista de tareas).

  • C-c C-t Cicla entre los distintos estados (unmarked) -> TODO -> DONE -> (unmarked).
  • S-<right>/<left> Igual que arriba, pero solo para el elemento actual.
  • C-c / t Ver la lista como un árbol disperso.
  • C-c a t Muestra la lista de tareas global.
  • S-M-<RET> Inserta una nueva tarea.
  • C-c , Establecer prioridad de la tarea (Entre A,B,C).
  • S-<up>/<dwn> Ciclar entre prioridades.

TODO checkboxes

Se pueden crear listas de tareas compuestas de varios elementos, y con C-c C-c se marcan como completadas, para crear una tarea nueva M-S-<RET>.

* TODO Organize party [0/3]
  - [ ] call people [0/2]
    - [ ] Peter
    - [ ] Sarah
  - [ ] order food

TODO Items checkboxes
TODO Items checkboxes

Markup

  • *negrita* => negrita.
  • /Cursiva/ => Cursiva.
  • =code= y ~verbatim~ -> code, verbatim.
  • +tachar+ -> tachar.
  • _subrayar_ -> subrayar

Imágenes y tablas

Sintáxis de las tablas:

| HEADER1  | header2  |
|----------+----------|
| content1 | contend2 |

Creating tables in org-mode
Creating tables in org-mode

Las imágenes son enlaces: [[./img/a-image.jpg]]

Código fuente

Para incluir código fuente:

 #+BEGIN_SRC emacs-lisp
     (defun org-xor (a b)
        "Exclusive or."
        (if a (not b) b))
 #+END_SRC

generará lo siguiente:

(defun org-xor (a b)
  "Exclusive or."
  (if a (not b) b))

Para editar el código en un buffer que soporte dicho lenguaje, C-c '

Fuente


  1. Click en return para volver a la referencia. [return]

Historia del lenguaje C#: pasado, presente y evolución

18/10/2017
Artículo original

Evolución del lenguaje C#

Aún recuerdo la primera vez que le eché un vistazo a C# a principios de los 2000. Microsoft había liberado la primera gran versión del lenguaje. En aquel momento pensé que era como Java, pero hecha por Microsoft, que le habían dado otro nombre y lo habían metido dentro de Visual Studio. Y no era el único que había pensado esto. En una antigua entrevista, el padre de Java, James Gosling, lo había llamado una imitación. "Es un tipo de Java con fiabilidad, productividad y seguridad nula," había dicho. ¡Buff!

Ha habido muchísimos cambios en los últimos 15 años. Dudo de que nadie haga hoy en día una valoración similar. En este tiempo, Java ha liberado 4 grandes versiones, mientras que C# ha liberado 6. Los lenguajes han tomado caminos diferentes, y C# ha experimentado muchísima innovación. Hoy me gustaría echar la vista atrás a la historia de C# y destacar algunas de las claves en su evolución.

¿Cómo era el lenguaje en sus versiones más tempranas? ¿Cómo ha evolucionado hasta la fecha?

C# Versión 1

Cuando echas la vista atrás, la primera versión de C# sí que se parecía un montón a Java. Como parte de sus objetivos de diseño escritos para ECMA, buscaba ser un "lenguaje orientado a objetos de uso general moderno y sencillo". En aquel momento, podría haber hecho algo peor que mirar hacia Java para alcanzar dichos fines.

Pero si analizases la versión 1.0 de C# 1.0 hoy, te marearías. Carecía de capacidades asíncronas sólidas y de muchas de las "pulidas" funcionalidades relacionadas con los genéricos que hoy en día damos por sentadas. En realidad, carecía de genéricos en general. ¿Y LINQ? Nada. Eso tardaría aún unos cuantos años en salir.

La versión 1 de C# parecía estar bastante desprovista de funcionalidad, comparado con hoy. Al final te veías escribiendo código pesado. Pero bueno, como con cualquier versión 1.0: por algún lado hay que empezar...

C# Versión 2

Aquí las cosas se empiezan a poner interesantes. Repasemos algunas de las principales características de C# 2.0, lanzado en 2005, junto con Visual Studio 2005 (no te olvides de echarle un vistazo al libro escrito por el creador de NDepend, Patrick Smacchia, sobre .NET 2.0, disponible en inglés y francés).

Aunque Microsoft puede que haya empezado con un lenguaje orientado a objetos bastante genérico, la versión 2 de C# lo cambió todo enseguida. Se pusieron a la tarea tras la salida de la versión inicial y fueron a por varias de las frustraciones que causaba. Y lo hicieron muy en serio.

Con genéricos, tienes tipos y métodos que pueden operar sobre un tipo arbitrario mientras que aún conservan seguridad de tipos. Así, por ejemplo, tener una List<T> te permite tener una List<string> o una List<int> y realizar operaciones seguras de tipo en esas cadenas de caracteres o ints mientras iteras por ellas. Esto es mucho mejor que crear clases derivadas de listas (ListInt?) o convertir a un Objeto para cada operación.

¡Ah!, y hablando de iteradores, la versión 2 de C# los presentó. Para resumir, esto te permite iterar por los ítems de una Lista (u otros tipos Enumerable) con un bucle foreach. Tener esto como un elemento de primera clase del lenguaje mejoró ostensiblemente la legibilidad del código y la capacidad para poder entenderlo.

Y aún así, Microsoft seguía intentando ponerse a la altura de Java. Java ya había liberado versiones que incluían genéricos e iteradores. Pero eso cambiaría pronto a medida que los lenguajes seguían una evolución diferente.

C# Versión 3

La versión 3 de C# apareció a finales de 2007, junto con Visual Studio 2008, aunque la funcionalidad completa aparecería con la versión 3.5 de C#. ¡Y menuda actualización resultó ser!. Yo me atrevería a afirmar que ese momento consolidó a C# como un lenguaje de programación realmente formidable. Veamos algunas de sus características principales en esta versión:

Con mirada retrospectiva, muchas de estas características parecen tanto inevitables como inseparables. De hecho, no es fácil destacar una sobre otra ya que todas encajan estratégicamente muy bien juntas.

Otros haciendo este mismo análisis no tendrán ese problema, y dirán que, de largo, la novedad más importante de la versión 3 de C# fue las expresiones de consulta, también conocidas como LINQ (Language INtegrated Query).

Yo creo que hay más matices y sutilezas puesto que, desde mi punto de vista, los árboles de expresión, las expresiones lambda y los tipos anónimos fueron los cimientos sobre los que se creó LINQ. Disertaciones aparte, lo cierto es que nos encontramos con un concepto realmente revolucionario. Microsoft había empezado a allanar el terreno para hacer de C# un lenguaje funcional orientado a objetos híbrido.

En concreto, con esta versión ya se podían programar búsquedas declarativas tipo SQL para llevar a cabo operaciones en colecciones, entre otras cosas. En vez de tener que crear un bucle para computar la media de una lista de enteros, ahora se podía hacer con algo tan fácil como list.Average(). La combinación de expresiones de búsqueda y métodos de extensión hizo parecer que las listas de enteros se habían hecho más inteligentes.

Llevó un tiempo hasta que los programadores realmente entendieron cómo integrar el concepto, pero a la larga lo lograron. Y ahora, unos años más tarde, el código es mucho más conciso, sencillo y funcional.

C# Versión 4

La versión 4 de C# nació con el estigma de no suponer una innovación rompedora como sí lo había sido su antecesora versión 3. Y es que con la versión 3, Microsoft hizo que el lenguaje dejase de estar a la sombra de Java y empezó a destacar. Rápidamente el lenguaje se estaba convirtiendo en una opción elegante.

De todos modos, la versión 4 de C# sí introdujo alguna cosas chulas.

Los tipos interop embebidos aliviaron problemas a la hora del despliegue. La covarianza y contravarianza genéricas te dan mucha potencia, pero son demasiado académicas y probablemente sean más valoradas entre los creadores de frameworks y bibliotecas. Los argumentos opcionales y con nombre te permiten eliminar muchas sobrecargas de método y ofrecen comodidad. Pero ninguna de estas cosas altera el paradigma propiamente dicho.

Esa distinción sí se la llevan los tipo dinámico. Con esta característica, Microsoft introdujo en la versión 4 de C# la capacidad de anular el compilador al tiparlo en tiempo de compilación. Es decir, al usar el tipo de referencia dinámico, te puedes llegar a pegar un tiro en el pie como en los lenguajes de tipado dinámico como JavaScript. Puedes crear una "x = cadena de caracteres" dinámica y luego añadirle 6, dejando al tiempo de ejecución determinar qué diablos tiene que pasar después.

Esto lo digo un poco de broma obviamente. Está claro que cabe la posibilidad de cometer errores, pero también otorga mucha potencia dentro del lenguaje.

C# Versión 5

Con la versión 5 de C#, Microsoft liberó una versión con el foco muy puesto en la innovación del lenguaje. Esta es la lista de las características más importantes:

No quiero que se me malinterprete. El atributo caller info está muy bien. Te permite recuperar información sobre el contexto en el que estás sin tener que recurrir a un montón de código reflejo repetitivo. Me encanta esta funcionalidad en realidad.

Pero async y await son las verdaderas estrellas de esta versión. Cuando esto salió en 2012, Microsoft cambió las reglas del juego nuevamente al meter la asincronía en el lenguaje como un participante de primera clase. Si ya has gestionado operaciones largas y la implementación de páginas web con retro-llamadas, probablemente adores esta funcionalidad.

C# Versión 6

Con las versiones 3 y 5, Microsoft había hecho algunas cosas bastante impresionantes en un lenguaje orientado a objetos (la versión 2 también, pero estaban copiando conceptos de Java con esas funciones que introdujeron). Con la versión 6 se alejaron de la idea de sacar una novedad dominante estrella y, en vez de eso, liberaron muchas características para hacer felices a los usuarios del lenguaje de programación. Aquí enumero unas cuantas:

Si las vemos de forma individual, todas estas características del lenguaje son muy interesantes. Pero si las valoramos en conjunto, observamos un patrón que nos llama la atención. En esta versión, Microsoft se ha esforzado en eliminar repeticiones en el lenguaje y hacer que el código sea más ligero y legible. Así que para los aficionados del código escueto y limpio, esta versión del lenguaje fue una gran victoria.

¡Ah!, e hicieron otra cosa con esta versión, aunque no se puede considerar como una función tradicional del lenguaje, per se. Liberaron Roslyn, el compilador, como servicio. Microsoft ahora usa C# para hacer C#, y te dejan usar el compilador como parte de tus esfuerzos de programación.

C# Versión 7

Finalmente hemos llegado ya a la versión 7 de C#. Es la versión actual en la fecha que se ha escrito este artículo. Tiene cosas muy chulas y revolucionarias que ya estaban en el ADN de la versión 6, pero sin el compilador como servicio. Aquí van las novedades de esta versión:

Todas ofrecen nuevas capacidades para que los desarrolladores puedan escribir código más limpio que nunca. En concreto, creo que Microsoft ha dado solución a problemas que venían desde muy lejos al condensar la declaración de variables que se pueden a usar con la palabra clave out y al permitir valores de devolución múltiples vía tuplas.

Además, Microsoft le está dando un uso más amplio al lenguaje. .NET ahora va dirigido a cualquier sistema operativo y tiene la vista puesta de forma firme en la nube y en la portabilidad. Esto es lo que más ocupa la mente y el tiempo de los diseñadores del lenguaje, además de pensar en nuevas características.

Conozco C# desde hace 15 años, y el lenguaje ha estado bajo desarrollo más tiempo que eso. Será emocionante ver qué funcionalidades y características deparará el futuro.

Este artículo es una traducción del original en inglés escrito por Erik Dietrich en el blog de NDepend, con permiso de Erik y de esta empresa. Fotografía de la portada por Homar - CC0, adaptada para este post por campusMVP.

TRUCO: Auto-montar un disco virtual en una tarjeta SD al iniciar Windows 10

13/10/2017
Artículo original

Este es un truco genérico que, de hecho, puede servirte para muchas otras cosas...

Si tienes un tablet o un ordenador convertible barato con Windows 10, lo habitual será que no vayan muy sobrados de almacenamiento. Muchos de estos dispositivos vienen con tan solo 32GB de almacenamiento, ya que esto suele ser lo más caro del aparato. Windows 10 de hecho, se suele "comer" al menos 8 o 10 de estos GB por lo que no solemos andar muy sobrados de espacio. Todos estos aparatos suelen tener almacenamiento expandible mediante tarjetas MicroSD. En la actualidad este tipo de almacenamiento externo es muy barato. Puedes comprar una de 64GB de clase 10 y 100MB/s por menos de 30€, por lo que la verdad es que la opción de utilizarlas en este tipo de dispositivos no está nada mal.

El caso es que, te compras la estupenda y rapidísima tarjeta microSD pero, oh sorpresa, no puedes instalar nada en ella porque al ser extraíble el sistema no te lo permite, e incluso algunos programas no te permiten usarla tampoco para almacenamiento. ¡Qué contrariedad! Cosas de Windows...

Un ejemplo muy claro de esta molestia es OneDrive. Si intentas decirle a OneDrive que quieres usar una carpeta en la tarjeta microSD para almacenar los archivos que sincronizas te dice que no es posible usar un medio extraíble.

Y es que, una de las cosas que solemos hacer muchos para tener nuestros archivos siempre a mano es sacarle partido a algún almacenamiento en la nube estilo Dropbox, Google Drive, OneDrive o pCloud (uno de mis grandes descubrimientos recientes, con interesantes características y además son europeos). El caso es que estos servicios (menos pCloud) requieren mucho espacio en disco para almacenar los archivos que tengas en la nube. Y aunque ahora la moda es incluir una funcionalidad para que solo se sincronicen los archivos que quieras, en el caso de OneDrive aunque fueron los pioneros retiraron esta capacidad al lanzar Windows 10 (vergüenza debiera darles, cuando ahora todos les copian) y Dropbox solo lo incluye en las versiones para empresas (ni siquiera si les pagas la versión Plus, ya les vale). GDrive es el único que lo tiene, con el nuevo Google Drive Stream que están lanzando estos días, pero a medida que accedes a las carpetas o si sincronizas muchas cosas te acaba ocupando mucho espacio también ya que la caché de los archivos la hace en tu perfil de usuario, o sea, en C: y no deja cambiar esta ubicación. El único amistoso en este sentido es pCloud, que no lo necesita al ser una carpeta en la nube directamente, y la pequeña caché local que utiliza, no obstante, te deja colocarla donde quieras.

Debido a ellos, este tipo de aplicaciones son muy avariciosas con el espacio en disco, y no se llevan bien con el hecho de usar una tarjeta externa que puedes retirar en cualquier momento...

Además de este tipo de aplicaciones, el caso es que para muchos programas almacenar en la tarjeta microSD no es una opción. Ahora te voy a contar cómo deshacerte de esta limitación.

Discos virtuales

Desde hace mucho tiempo, desde Windows 7 si no me equivoco, el sistema operativo Windows tiene la capacidad de gestionar discos duros virtuales nativamente. Se trata de archivos con la extensión .vhd (Virtual Hard Drive) o .vdhx (un formato ma´s moderno y extensible) que son los mismos que usa Hyper-V, el producto de virtualización de Microsoft, también incluido de serie con Windows (aunque no instalado por defecto).

Gracias a esta capacidad es muy sencillo crear un disco virtual, que se verá como un disco real a todos los efectos, y podremos utilizarlo como si se tratase de una unidad física existente. Esto incluye archivos de disco virtual guardados en un medio de almacenamiento externo como una tarjeta microSD.

Lo único que tenemos que hacer para crear un nuevo disco virtual en Windows 10 es pulsar con el botón derecho en el botón de inicio y elegir la opción "Gestión de disco" (yo tengo el sistema en inglés, pero debe de ser aproximadamente eso):

Otra opción es buscar "Computer management" en el inicio y asegurarnos de arrancarlo con permisos de administrador (opción el pulsarlo con el botón derecho), ya que sin ellos no podremos hacer nada en este caso:

Esto nos abre una nueva ventana en la que tenemos todos los discos del sistema para gestionar. En el menú "Acciones" tenemos dos opciones relacionadas con la gestión de discos virtuales: crearlos y adjuntarlos:

Si escoges crear un nuevo disco virtual te aparece una pantalla como la siguiente:

En ella deberás introducir la ruta donde quieres crear el VHD, que en este caso sería la tarjeta SD, y el tamaño del disco en MB o GB. Escoge bien el tamaño adecuándolo a tus necesidades.

Luego te da a elegir dos cosas más muy importanes:

  • El formato del disco virtual: el VHD debería ser suficiente ya que no necesitas tener un tamaño mayor de 2TB, pero por otro lado yo te recomendaría que elijas el VHDX porque es más resistente a fallos (como que retires la tarjeta de golpe sin avisar, aunque jamás debieras hacerlo). Eso sí, si escoges VHDX no podrás leerlo en Windows 7 si te levas la tarjeta a este sistema en algún momento.
  • El tipo de disco: si escoges el tamaño fijo el disco ocupará ese espacio máximo desde el primer minuto, aunque no le metas información dentro. Aunque está recomendado el de tamaño fijo yo suelo elegir el de tamaño dinámico porque le puedes poner el tamaño que quieras y solo te ocupará lo que realmente sea necesario para la información que contiene. De este modo podrías tener una tarjeta SD pequeña de 16GB pero decirle que el disco sea de 128GB, y funcionaría bien. Solo debes tener cuidado de no pasarte de 16Gb. En el futuro cuando necesites más espacio, simplemente copias el archivo .vhdx a una nueva tarjeta más grande y listo.

Listo. Una vez creado puedes adjuntarlo a tu sistema usando la otra opción de menú. Se le asignará una letra y podrás acceder a él como una unidad de disco física normal, solo que en realidad estarás usando el almacenamiento extraíble. OneDrive te dejará poner su carpeta en la nueva unidad, y podrás crear enlaces simbólicos o juntions para redirigir carpetas a dicha unidad 8de esto, si me lo pedís, hablaré otro día pues es un tema muy interesante).

Pero no nos llega solo con esto...

Adjuntarlo automáticamente

Lo anterior está bien pero no nos sirve de mucho si en cuanto arranca el sistema no tenemos de nuevo la unidad adjunta. Y es que adjuntar manualmente una unidad como acabamos de hacer no es una operación persistente. Es decir, cuando el sistema operativo se reinicie por una actualización o cualquier otro motivo, la unidad no estará ahí. Ouch!

Para lograr que siempre esté disponible lo que tenemos que hacer es adjuntarla automáticamente con un script antes siquiera de que el usuario se loguee en el sistema. Para ello abrimos una línea de comandos con permisos de administrador, y escribimos gpedit para lanzar el gestor de políticas del sistema, y nos vamos al nodo Configuración de la computadora·Ajustes de Windows·Scripts (inicio/cierre):

Al editar esta política nos sale la ventana en primer plano de la figura anterior que nos permite asignar ciertos scripts para ser ejecutados cuando arranca el equipo, antes de que ningún usuario se loguee siquiera.

Lo que tenemos que hacer es escribir un pequeño script a ejecutar que haga lo que queremos: adjuntar el VHDX como una unidad, antes de nada.

Vete a la carpeta del SD en el que has creado el disco virtual, y en el mismo sitio crea un archivo de texto (por ejemplo adjunta-VHDX.txt) con el siguiente contenido:

select vdisk file="D:\Discos\OneDrive.vhdx"
attach vdisk

siendo lo que va entre comillas dobles la ruta a tu unidad de disco virtual.

Este archivo de texto lo usaremos como argumento del comando diskpart que gestiona los discos desde la línea de comandos. Así que crea otro archivo de texto con el mismo nombre pero con extensión .bat y dentro pon lo siguiente:

diskpart /s "D:\Discos\adjunta-VHDX.txt"

siendo lo que va entre comillas la ruta al archivo de texto anterior.

Ahora en el diálogo que tenías abierto en el gestor de políticas, asigna ese script pulsando sobre "Añadir" y poniendo la ruta al .bat que acabas de crear.

¡Listo! Ahora, cada vez que arranque el equipo se adjuntará una unidad de disco, a todos los efectos como otra cualquiera, solo que estará sustentada en un disco virtual guardado en tu tarjeta microSD, y podrás utilizarla como verdadero almacenamiento adicional, como si le hubieras colocado un disco duro interno nuevo al ordenador.

Esto además de lo que he contado tiene otras ventajas como por ejemplo que puedes mover de golpe miles de archivos que estén dentro de la unidad virtual copiando un único archivo (el .vhdx), por lo que copiarlo a cualquier lado es mucho más rápido (se copia mucho más rápido un archivo que miles de ellos aunque el espacio que ocupen sea el mismo). 

Solo un consejo: ahora sí, antes de retirar la tarjeta microSD asegúrate de desvincular el disco o podrías tener problemas. De hecho si es un tablet, lo mejor es que si necesitas quitar la tarjeta antes lo apagues, y luego la pongas de nuevo antes de encenderlo.

¡Espero que te sea útil!

Principios de una arquitectura limpia: mantenible y testeable

13/10/2017
Artículo original

Arquitectura

Estoy seguro de que si te dedicas a programar, conoces a Robert "Uncle" Martin. Su libro Clean Code es uno de los más recomendados en la lista de libros que todo desarrollador debería leer. Martin, con sus cosas buenas y malas, es uno de los desarrolladores más influyentes del panorama ingenieril. Fuerte defensor de TDD, de la cobertura de tests y otras buenas prácticas, y además cuenta con muchas personas que siguen sus enseñanzas a rajatabla.

Recientemente, Bob Martin, ha publicado un nuevo libro llamado Clean Architecture. ¿Pero qué se entiende por arquitectura limpia?

Clean Code

Como comentaba antes, Clean Code es un libro muy recomendable, que desgrana algunas ideas importantes para poder escribir código limpio.

El código limpio es aquel código que está estructurado de forma compresible, que es claro en sus intenciones, fácil de leer, que es fácilmente mantenible y que está testeado. En el libro se van dando algunas ideas para conseguir escribir código limpio, hablando de principios SOLID, de la importancia de dar nombres a variables y clases etc. En GenbetaDev ya hemos hablado del libro y de sus ideas en alguna ocasión

Principios de una arquitectura limpia

Aunque seamos capaces de escribir código limpio, podemos encontrarnos que al crecer nuestro sistema, la arquitectura del mismo sea un lastre. Y es que no es lo mismo escribir código limpio para un proyecto sencillo, que para un proyecto complejo compuesto de varios componentes obligados a cooperar. A veces las arquitecturas son demasiado complejas, nos obligan a repetir código, o nos hacen tener demasiadas dependencias entre componentes, causándonos muchos problemas.

Los conceptos de cohesión y acoplamiento, también pueden aplicarse a nivel de arquitectura.

Si utilizáis programación orientada a objetos, seguro que conocéis los conceptos de cohesión y acoplamiento. Esos conceptos también pueden aplicarse de forma parecida a los componentes de un sistema, ya sean dlls o archivos jar, estos tienen que cooperar unos con otros. Y la manera en la que cooperen, pueden hacer un sistema fracasar. Pero si seguimos una serie de principios para controlar estas dos variables, nuestra arquitectura será más limpia y manejable.

Cohesión

  • The Reuse/Release Equivalence Principle: que nos dice que los componentes deben poder ser desplegados de forma independiente sin afectar a los demás. Las clases, o código que van en ese componente, deben tener una relación, y por tanto deben poderse desplegar de forma conjunta.

  • The common closure principle: se podría decir que hablamos del principio de responsabilidad única (SRP) aplicado a componentes. La idea es agrupar clases que puedan cambiar por la misma razón en un solo componente. Si tenemos que hacer un cambio, y hay que tocar varios componentes, esto supondrá tener que desplegarlos todos, en lugar de sólo uno.

  • The common reuse principle: este principio nos habla de evitar a aquellos que utilizan un componente depender de cosas que no necesitan. Si un componente depende de otro, hay que intentar que sea porque necesita todas las clases que lo componen. Lo contrario nos obligará a trabajar más cuando nos toque hacer el despliegue. De esta manera será más fácil reutilizar componentes.

Conseguir cumplir estos tres principios a la vez es algo bastante difícil, por lo que a veces hay que aceptar compromisos. Por ejemplo es común sacrificar un poco la reusabilidad, para conseguir que los componentes sean fáciles de desplegar.

Acoplamiento

  • The Acyclic Dependencies Principle: si trazamos líneas entre los componentes para representar las dependencias entre ellos, tenemos que intentar que no existan ciclos. Es decir, que el cambio en un componente, no acabe desencadenando en la necesidad de hacer cambios en cadena en los demás componentes, que obliguen a volver a modificar el componente inicial. Cuando eso sucede, es difícil conseguir una versión estable del sistema, ya que hay que hacer multitud de cambios en los distintos componentes hasta que todo vuelve a funcionar.

  • The stable dependencies Principle: todo sistema tiende a cambiar y evolucionar, pero no todos los componentes cambian con la misma frecuencia, ni es igual de fácil modificarlos. Este principio nos dice que un componente que cambia a menudo no debería depender de otro que es difícil modificar, ya que entonces será también difícil de modificar.

  • The stable Abstractions Principle: este principio nos dice que si un componente de nuestro sistema va a cambiar poco ya que es difícil modificarlo, debe estar compuesto mayoritariamente por interfaces y clases abstractas. De esta manera el componente será fácilmente extensible, y no afectará tanto al resto de la arquitectura.

Características de una arquitectura limpia

Además de cumplir los principios anteriormente descritos, una arquitectura limpia se caracteriza por:

  • Independiente de los frameworks. Los frameworks deberían ser herramientas, y no obligarnos a actuar de una determinada manera debido a sus restricciones.

  • Testable. Debemos poder probar nuestras reglas de negocio sin pensar en base de datos, interface gráfica u otros componentes no esenciales de nuestro sistema.

  • Independiente de la UI. Si la UI cambia a menudo esto no puede afectar al resto de nuestro sistema, que tiene que ser independiente.

  • Independiente de la base de datos. Deberíamos poder cambiar de Oracle, a SQL Server, a MongoDB, a Casandra o a cualquier otra base de datos sin que afectara demasiado a nuestro sistema.

  • Independiente de cualquier entidad externa. No deberíamos saber nada de entidades externas, por lo que no deberemos depender de ellas.

Todas estas características, según Bob Martin, se agrupan en el siguiente gráfico:

Clean arquitecture

Partes de una arquitectura limpia

Entidades

Las entidades son las que incluyen las reglas de negocio críticas para el sistema. Estas entidades pueden ser utilizadas por distintos componentes de la arquitectura, por lo que son independientes, y no deben cambiar a consecuencia de otros elementos externos.

Una entidad deberá englobar un concepto crítico para el negocio, y nosotros tendremos que separarlo lo más posible del resto de conceptos. Esa entidad recibirá los datos necesarios, y realizará operaciones sobre ellos para conseguir el objetivo deseado.

Casos de uso

En este caso nos encontramos con las reglas de negocio aplicables a una aplicación concreta. Estos casos de uso siguen un flujo para conseguir que las reglas definidas por las entidades se cumplan. Los casos de uso, solo definen como se comporta nuestro sistema, definiendo los datos de entrada necesarios, y cual será su salida. Los cambios en esta capa no deberían afectar a las entidades, al igual que los cambios en otras capas externas no deberían afectar a los casos de uso.

Es importante que no pensemos en como los datos que genera un caso de uso serán presentados al usuario. No deberemos pensar en HTML, o en SQL. Un caso de uso recibe datos estructurados y devuelve más datos estructurados.

Adaptadores de interfaz

Los datos generados por los casos de uso y las entidades, tienen que transformarse en algo entendible por la siguiente capa que los va a utilizar y de eso se encarga esta capa. Pensando en MVC por ejemplo, los controladores y las vistas, pertenecerían a esta capa, y el modelo, serían los datos que se pasan entre los casos de uso y los controladores para luego poder presentar las vistas.

Lo mismo aplicaría para por ejemplo, presentar información a un servicio externo, ya que en esta capa definiríamos la manera en la que los datos de las capas internas se presenta al exterior.

Frameworks y drivers

En la capa más externa es, como dice Bob Martin, donde van los detalles. Y la base de datos es un detalle, nuestro framework web, es un detalle etc.

Fronteras o límites

Una frontera (o como dicen los aglosajones, boundaries) es una separación que definimos en nuestra arquitectura para dividir componentes y definir dependencias. Estas fronteras tenemos que decidir dónde ponerlas, y cuándo ponerlas. Esta decisión es importante ya que puede condicionar el buen desempeño del proyecto. Una mala decisión sobre los límites puede complicar el desarrollo de nuestra aplicación o su mantenimiento futuro.

Una mala decisión sobre los límites entre componentes puede complicar el desarrollo de nuestra aplicación o su mantenimiento futuro

Por ejemplo, podemos sentirnos tentados de pensar que las reglas de negocio deben poder guardar información directamente en la base de datos. Como ya hemos visto antes, la base de datos es un detalle, así que esto deberíamos evitarlo. En ese punto deberíamos trazar una frontera. Nuestras reglas de negocio, se comunicarían siempre con una interface, sin saber nada sobre la base de datos. La base de datos en cambio, si sabrá cosas sobre las reglas de negocio, ya que tiene que transformar los datos en sentencias SQL que puedan almacenar la información.

Otra ventaja adicional de este enfoque, es que podemos retrasar ciertas decisiones. Podemos empezar a desarrollar todas nuestras reglas de negocio, sin tener en cuenta su persistencia, ya que esa parte se realiza a través de una interface. Primero podemos utilizar objetos en memoria, y según avancemos, ir añadiendo sistemas más sofisticados. Al final podremos elegir entre usar una base de datos relacional, NoSQL, o incluso guardar la información en archivos.

En definitiva, debemos pensar en nuestro sistema, como un sistema de plugins, de forma que los componentes estén aislados y podamos sustituir unos por otros sin demasiados problemas.

Las fronteras de una arquitectura limpia

En el esquema de arquitectura limpia que hemos visto anteriormente, podemos ver dónde se han trazado las fronteras o límites. Entre entidades y casos de uso, hay una frontera. Lo mismo con los adaptadores de interface, o los frameworks y drivers. Las fronteras son importantes, porque añadirlas cuando no las necesitamos pude crearnos muchos problemas, pero no añadirlas cuando las necesitamos pude generar otros tantos (añadirlas después, es siempre es mucho más costoso).

La separación en fronteras es importante, pero mucho más importante es la gestión que hagamos de las dependencias entre estas capas. Para ello siempre hay que seguir la regla de las dependencias.

La regla de las dependencias

Esta regla es muy importante, ya que sin ella, nuestra arquitectura no sería más que un bonito diagrama. Las capas interiores de una arquitectura limpia, no deben saber nada de las capas exteriores. Por ejemplo la capa de entidades, no puede saber de la existencia de los casos de uso, y los casos de uso no deben saber nada de la existencia de los adaptadores de interface. Así las dependencias están controladas y van siempre en un solo sentido.

Estructuras de datos simples

A la hora de traspasar una frontera, deberemos utilizar estructuras de datos simples, evitando utilizar conceptos como DatabaseRows o similares. Pensando en los casos de uso, estos deben recibir estructuras de datos como datos de entradas, y deben devolver estructuras de datos como salida. Como decía antes, no nos interesa que un caso de uso tenga conocimientos sobre HTML o SQL. Lo contrario nos lleva a una falta de independencia, con todo lo que eso conlleva (despliegue, actualización, tests etc.)

Las capas interiores de una arquitectura limpia, no deben saber nada de las capas exteriores

En ocasiones al pasar datos a los casos de uso, podemos pensar que es buena idea utilizar las entidades como datos de entrada o salida. Al fin y al cabo comparten mucha información. Pero esto no deja de ser un error, ya que aunque al principio la información parezca similar, en el futuro los casos de uso y las entidades cambiarán de muy diferentes maneras, obligándonos a tratar con la dependencia que hemos creado.

Fronteras parciales

A veces, por motivos de organización y mantenimiento, nos interesa crear fronteras parciales. Este tipo de fronteras las tenemos que planificar de forma similar a una frontera real, pero en lugar de empaquetarla en un componente aislado, la dejamos que forme parte de otro componente. Así nos ahorramos parte del esfuerzo de crear un componente nuevo, que no estamos seguros de que vaya a necesitarse. Obtenemos algunas de sus ventajas, dejando todo preparado por si es necesario dar ese último paso.

Conclusión

Aunque puede que no sea la parte más importante de un proyecto de software, la arquitectura juega siempre un papel importante. Ignorar esta fase pude traernos muchos problemas en el futuro, por lo que nunca está de más prestarle un poco de atención. Cuando diseñamos una arquitectura hay que tener muchas cosas en cuenta, como la separación de componentes, las dependencias entre ellos y la manera en la que cruzaremos las fronteras entre los mismos.

Como desarrolladores de software, queremos conseguir desarrollar programas que funcionen y que sean útiles para sus usuarios, pero también que sean fácilmente mantenibles y rápidamente extensibles. Y sin duda para esto no viene nada mal tener algunos conceptos de como debe construirse una arquitectura limpia.

Libro | Clean Code Gráfico | Clean Architecture: A Craftsman's Guide to Software Structure and Design Imagen | acavoulacos

También te recomendamos

Aprender de la arquitectura del mejor software Open Source

Usando MVP e inversión de dependencias para abstraernos del framework en Android

Esto es lo que puedes ahorrar al comprar un vehículo eléctrico

-
La noticia Principios de una arquitectura limpia: mantenible y testeable fue publicada originalmente en Genbeta Dev por rubenfa .

Página Anterior Página Siguiente