Fundamentos de JavaScript: por qué deberías saber cómo funciona el motor

18/09/2018
Artículo original


Foto de Lee Attwood en Unsplash

Nota: este artículo es una traducción del original en inglés de Rainer Hahnekamp con su permiso expreso. Rainer es un ingeniero de software austríaco con amplia experiencia y que trabaja como freelance en el área de Viena. Puedes seguirlo en Twitter.

En este artículo quiero explicar lo que un desarrollador de software que utiliza JavaScript debe saber sobre los motores de este lenguaje para que el código escrito se ejecute correctamente de manera apropiada.

Más abajo verás una función con una sola línea de código que lo que hace es devolver la propiedad lastname del objeto que se le pase como argumento. Veremos que, con sólo añadir una propiedad a cada objeto, ¡acabamos con una caída del rendimiento de más del 700%!

Como explicaré en detalle, la falta de tipos estáticos en JavaScript es lo que provoca este comportamiento. Lo que antes se veía como una ventaja sobre otros lenguajes como C# o Java, ha resultado ser más bien una especie de "pacto con el diablo".

Pisando el freno a máxima velocidad

Por lo general, no necesitamos conocer el funcionamiento interno del motor que ejecuta nuestro código. Los fabricantes de navegadores invierten mucho en hacer que los motores ejecuten el código muy rápido.

¡Genial!

Dejemos que los demás hagan el trabajo pesado. ¿Por qué preocuparse por el funcionamiento de los motores?

En el ejemplo de código a continuación tenemos cinco objetos que almacenan los nombres y apellidos de los personajes de Star Wars. La función getName devuelve el valor de la propiedad lastname del objeto que se le pase. Medimos el tiempo total que tarda esta función en ejecutarse mil millones de veces:

(()  =>  {
  const  han  =   {firstname:  "Han",   lastname:  "Solo"};
  const  luke  =  {firstname:  "Luke",  lastname:  "Skywalker"};
  const  leia  =  {firstname:  "Leia",  lastname:  "Organa"};
  const  obi  =   {firstname:  "Obi-Wan",  lastname:  "Kenobi"};
  const  yoda  =  {firstname:  "",  lastname:  "Yoda"};
  const  people  =  [
    han,  luke,  leia,  obi,
    yoda,  luke,  leia,  obi
  ];
  const  getName  =  (person)  =>  person.lastname;
  console.time("engine");
  for(var  i  =  0;  i  <  1000  *  1000  *  1000;  i++)  {
    getName(people[i  &  7]);
  }
  console.timeEnd("engine");
})();

En un Intel i7 4510U, el tiempo de ejecución es de aproximadamente 1.2 segundos. Por el momento, todo bien. Ahora añadimos otra propiedad a cada objeto y lo ejecutamos de nuevo:

(() => {
  const han = {
    firstname: "Han", lastname: "Solo", 
    spacecraft: "Falcon"};
  const luke = {
    firstname: "Luke", lastname: "Skywalker", 
    job: "Jedi"};
  const leia = {
    firstname: "Leia", lastname: "Organa", 
    gender: "female"};
  const obi = {
    firstname: "Obi", lastname: "Wan", 
    retired: true};
  const yoda = {lastname: "Yoda"};
  const people = [
    han, luke, leia, obi, 
    yoda, luke, leia, obi];
  const getName = (person) => person.lastname;
  console.time("engine");
  for(var i = 0; i < 1000 * 1000 * 1000; i++) {
    getName(people[i & 7]);
  }
  console.timeEnd("engine");
})();

Nuestro tiempo de ejecución es ahora de 8.5 segundos, lo que es aproximadamente 7 veces más lento que nuestra primera versión. Esto es lo que se siente al pisar los frenos yendo a toda velocidad. ¿Cómo ha podido ocurrir esto?

Es hora de echar un vistazo más de cerca al motor.

Fuerzas combinadas: Intérprete y compilador

El motor es la parte encargada de leer y ejecutar el código fuente. Cada uno de los principales proveedores de navegadores tiene su propio motor. Mozilla Firefox tiene Spidermonkey, Microsoft Edge tiene Chakra/ChakraCore y Apple Safari llama a su motor JavaScriptCore. Google Chrome utiliza V8, que también es el motor de Node.js. El lanzamiento del V8 en 2008 marcó un momento crucial en la historia de los motores. V8 reemplazó la relativamente lenta interpretación de JavaScript del navegador.

La razón detrás de esta mejora tan grande radica principalmente en la combinación de intérprete y compilador. Hoy en día, los cuatro motores utilizan esta técnica. El intérprete ejecuta el código fuente casi de inmediato. El compilador traduce el código fuente en código máquina que el sistema del usuario ejecuta de manera directa.

A medida que el compilador genera el código de la máquina, aplica optimizaciones. Tanto la compilación como la optimización dan como resultado una ejecución de código más rápida a pesar del tiempo extra necesario en la fase de compilación.

La idea principal detrás de los motores modernos es combinar lo mejor de ambos mundos:

  • Rápido inicio de la aplicación del intérprete.
  • Rápida ejecución del compilador.

Sabiduría Yoda | ALWAYS TWO THERE ARE AN INTERPRETER AND A COMPILER | image tagged in yoda wisdom | made w/ Imgflip meme maker

Un motor moderno usa un intérprete y un compilador. Fuente: imgflip.com

Lograr ambos objetivos comienza con el intérprete. En paralelo, el motor marca las partes de código ejecutadas con frecuencia como "Hot Path" (Ruta Caliente) y las pasa al compilador junto con la información contextual recopilada durante la ejecución. Este proceso permite al compilador adaptar y optimizar el código para el contexto actual.

Llamamos al comportamiento del compilador "Just in Time" o simplemente JIT. Cuando el motor funciona bien, puedes imaginar ciertos escenarios en los que JavaScript incluso supera a C++ en velocidad. No es de extrañar que la mayor parte del trabajo del motor se dedique a esa "optimización contextual".

Interacción entre el Intérprete y el Compilador

Tipos estáticos en tiempo de ejecución: Caché en línea

El almacenamiento en caché en línea, o IC (de Inline Caching en inglés), es una técnica de optimización importante dentro de los motores JavaScript. El intérprete debe realizar una búsqueda antes de poder acceder a la propiedad de un objeto. Esa propiedad puede ser parte del prototipo de un objeto, tener un método getter o incluso ser accesible a través de un proxy. La búsqueda de la propiedad es bastante gravosa en términos de velocidad de ejecución.

El motor asigna cada objeto a un "tipo" que genera en tiempo de ejecución. V8 llama a estos "tipos", que no forman parte del estándar ECMAScript, clases ocultas o formas de objeto. Para que dos objetos compartan la misma forma de objeto, ambos objetos deben tener exactamente las mismas propiedades y en el mismo orden. Por lo tanto, un objeto {firstname: "Han", lastname: "Solo"} se asignaría a una clase diferente que {lastname: "Solo", lastname: "Han"}.

Con la ayuda de las formas de los objetos, el motor conoce la ubicación en memoria de cada propiedad. El motor incluye directamente esas ubicaciones en la función que accede a la propiedad (_harcodeadas_).

Lo que hace el almacenamiento en caché en línea es eliminar las operaciones de búsqueda. No es de extrañar que esto produzca una gran mejora en el rendimiento.

Volviendo a nuestro ejemplo anterior: todos los objetos de la primera ejecución tenían tan solo dos propiedades, firstname y lastname, y definidas en el mismo orden. Supongamos que el nombre interno de esta forma de objeto fuese p1. Cuando el compilador aplica IC, asume que a la función sólo se le va a pasar la forma de objeto (o "tipo" interno) p1 y que devuelve el valor lastname de inmediato.

Compilación con caché en línea

En la segunda ejecución, sin embargo, tenemos que tratar con 5 formas de objeto diferentes. Cada objeto tenía una propiedad adicional y a yoda le faltaba el firstname, que está en blanco. ¿Qué sucede una vez que estamos tratando con múltiples formas de objetos?

Patos por todas partes o Tipos Múltiples

La programación funcional tiene el conocido concepto de duck typing (si camina como un pato, nada como un pato y suena como un pato, seguramente es un pato ), en el que la buena calidad de código exige funciones que puedan manejar múltiples tipos. En nuestro caso, siempre que el objeto pasado tenga la propiedad lastname, todo irá bien.

El almacenamiento en caché en línea elimina la costosa búsqueda de la ubicación de memoria de una propiedad. Funciona mejor cuando en cada acceso a la propiedad el objeto tiene la misma forma. Esto se denomina IC monomórfica.

Si tenemos hasta cuatro formas de objeto diferentes, estamos en un estado de IC polimórfico. Al igual que en el monomórfico, el código máquina optimizado "conoce" ya las cuatro ubicaciones. Pero tiene que comprobar a cuál de los cuatro objetos posibles pertenece el argumento que se le pasa. Esto provoca una disminución del rendimiento.

Una vez que superamos el umbral de cuatro, la cosa empeora considerablemente. Ahora estaríamos en lo que se conoce como un IC megamórfico. En este estado ya no hay caché local de las posiciones de memoria. En su lugar, se tiene que buscar desde una caché global. Esto es la causa de la bajada drástica del rendimiento que hemos mencionado anteriormente.

Polimórfico y Megamórfico en Acción

A continuación, vemos una caché en línea polimórfica con 2 formas de objeto diferentes:

Caché en línea polimórfica

Y ahora la caché en línea megamórfica de nuestro ejemplo de código, con 5 formas de objeto diferentes:

Caché en línea megamórfica

Clases ECMAScript al rescate

Bien, así que teníamos 5 formas de objetos y nos encontramos con un IC megamórfico. ¿Cómo podemos arreglar esto?

Tenemos que asegurarnos de que el motor marca los 5 objetos de la misma forma. Esto significa que los objetos que creamos deben contener todas las propiedades posibles. Podríamos usar literales de objetos, pero en mi experiencia las clases JavaScript/ECMAScript son la mejor solución.

Para propiedades que no están definidas, simplemente le pasamos un null o las dejamos fuera. El constructor se asegura de que estos campos estén inicializados con un valor:

(()  =>  {
  class  Person  {
    constructor({
      firstname  =  '',
      lastname  =  '',
      nave  =  '',
      empleo  =  '',
      genero  =  '',
      retirado  =  false
    }  =  {})  {
      Object.assign(this,  {
        firstname,
        lastname,
        nave,
        empleo,
        genero,
        jubilado
        });
    }
  }
  const  han  =  new  Person({
    firstname:  'Han',
    lastname:  'Solo',
    nave:  'Falcon'
  });
  const  luke  =  new  Person({
    firstname:  'Luke',
    lastname:  'Skywalker',
    empleo:  'Jedi'
  });
  const  leia  =  new  Person({
    firstname:  'Leia',
    lastname:  'Organa',
    genero:  'female'
  });
  const  obi  =  new  Person({
    firstname:  'Obi-Wan',
    lastname:  'Kenobi',
    jubilado:  true
  });
  const  yoda  =  new  Person({  lastname:  'Yoda'  });
  const  people  =  [
    han,
    luke,
    leia,
    obi,
    yoda,
    luke,
    leia,
    obi
  ];
  const  getName  =  person  =>  person.lastname;
  console.time('engine');
  for  (var  i  =  0;  i  <  1000  *  1000  *  1000;  i++)  {
    getName(people[i  &  7]);
  }
  console.timeEnd('engine');
})();

Cuando ejecutamos de nuevo esta función, vemos que nuestro tiempo de ejecución vuelve a ser de 1.2 segundos. ¡Misión cumplida!

En resumen

Los motores JavaScript modernos combinan los beneficios del intérprete y del compilador: rápido inicio de la aplicación y rápida ejecución del código.

El almacenamiento en caché en línea es una potente técnica de optimización. Funciona mejor cuando sólo se pasa una forma de objeto a la función optimizada.

Mi ejemplo extremo muestra los efectos de los diferentes tipos de caché en línea y las penalizaciones de rendimiento de los cachés megamórficos.

Usar clases JavaScript es una buena práctica. Los transpiladores de tipo estático, como TypeScript, hacen que sea más probable la utilización de las cachés monomórficas y por lo tanto de un mejor rendimiento.

Lecturas adicionales:

Arduino Zumo Siguelineas Evita obstaculos

16/09/2018
Artículo original

Este proyecto forma parte de un trabajo para la asignatura Robótica Móvil y Neurobótica del Máster en Ciencia de Datos de la ETSIIT. Sus autores son Cristina Heredia y Alejandro Alcalde. El código está disponible en HerAlc/LineFollowerObstacles

El proyecto se compone de dos partes. La primera consiste en crear un robot que siga un camino pre-establecido marcado con líneas, además, debe evitar cualquier obstáculo que se encuentre en el camino. En caso de encontrarse con un obstáculo, el robot se detiene unos segundos y avisa, si pasado un tiempo el obstáculo sigue presente, el robot da media vuelta y sigue en camino contrario. La segunda parte se trata de implementar este funcionamiento en el entorno simulado Vrep.

Arduino

Para esta parte se ha implementado un Zumo capaz de conducir por sí mismo con las siguientes funcionalidades:

  • Sigue Líneas
  • Detección de obstáculos y toma de decisiones.
  • Avisos sonoros a los obstáculos.

Para el sigue líneas se usan tres sensores sigue líneas para detectar cambios de intensidad.

Antes de comenzar, el robot necesita calibrar el giroscopio y los sensores sigue líneas. Para ello hay que pulsar el botón A una vez, esto calibra el giroscopio. Pulsar el botón A una segunda vez calibra los sensores sigue líneas.

Cabe mencionar que se parte del ejemplo Sigue Líneas de Arduino, disponible en el IDE de Arduino, las modificaciones realizadas han sido las siguientes:

Se integra código del ejemplo MazeSolver, que hace uso del giroscopio, en concreto los ficheros TurnSensor.h y TurnSensor.cpp. Esto permite calibrar el giroscopio, el cual es necesario para conseguir que el robot de media vuelta e inicie el camino contrario si el obstáculo que encuentra en su camino no se retira en tres segundos.

Para la detección de obstáculos se hace uso de un sensor de proximidad. La distancia a la que se para el robot se fija al valor 6. Cuando el robot se detiene al encontrar un obstáculo, avisa con una melodía simple compuesta por nosotros y similar al claxon de un vehículo. Finalmente, cuando el robot espera más de tres segundos y el obstáculo sigue presente, avisa con otra melodía compuesta por nosotros e inicia al camino en sentido opuesto girando 180 grados.

Vídeo

VREP

Para el proyecto en VREP se ha implementado un robot Sigue Líneas que se detiene ante la presencia de un obstáculo. Si el obstáculo se retira durante la simulación, el robot sigue su camino. Si el obstáculo no se retira, el robot permanece sin moverse para evitar chocar con él.

Para seguir las líneas se ha adaptado el ejemplo de VREP LineFollowerBubbleRob para hacerlo funcionar con Zumo, usando el modelo proporcionado en clase. La adaptación ha consistido en modificar el código de BubbleRob, que solo tiene dos motores, y hacerlo funcionar en el Zumo, con cuatro motores. Para ello se han tenido que ajustar parámetros de torque, peso etc de acuerdo al Zumo. También se desactivó el comportamiento cíclico de los motores.

Para evitar obstáculos se añade al robot un sensor de proximidad frontal, de forma similar al que se hizo en clase. Cuando el objeto se detecta a una distancia mínima pre-establecida, se detienen los motores del robot durante tres segundos. Transcurrido este tiempo, se vuelven a leer los datos del sensor de proximidad para comprobar si el objeto sigue presente o no. En caso de estar presente, el robot permanece parado, de lo contrario reanuda su marcha.

A continuación se muestra el código principal que controla el Zumo.

function sysCall_actuation()
    currTime = sim.getSimulationTime()
    result,distance=sim.readProximitySensor(noseSensor)

    if (result == 1 and distance < .25) then
        speed = 0
        if (objectDetected == false) then
            timeOjectDetected = sim.getSimulationTime()
            objectDetected = true
        end
        --sim.addStatusbarMessage(tostring(timeOjectDetected))
    end
    timeWaitingDetectedObject = currTime - timeOjectDetected
    sim.addStatusbarMessage(tostring(timeWaitingDetectedObject))
    -- After 3 seconds, check if continue foward or turn back
    if (timeWaitingDetectedObject > 3 ) then
        result,distance=sim.readProximitySensor(noseSensor)
        if (result == 0) then
            speed = -5
            timeOjectDetected = 0
            objectDetected = false
        end
    end

    -- read the line detection sensors:
    sensorReading={false,false,false}
    for i=1,3,1 do
        result,data=sim.readVisionSensor(floorSensorHandles[i])
        if (result>=0) then
            -- data[11] is the average of intensity of the image
            sensorReading[i]=(data[11]<0.3)
        end
    end

    rightV=speed
    leftV=speed

    if sensorReading[1] then
        leftV=0.03*speed
    end
    if sensorReading[3] then
        rightV=0.03*speed
    end
    -- When in forward mode, we simply move forward at the desired speed
    sim.setJointTargetVelocity(frontLeftMotor,leftV)
    sim.setJointTargetVelocity(frontRightMotor,rightV)
    sim.setJointTargetVelocity(rearLeftMotor,leftV)
    sim.setJointTargetVelocity(rearRightMotor,rightV)
end

En el código se lleva la cuenta del tiempo transcurrido desde la última vez que se detuvo el robot, para decidir cuando se debe hacer la siguiente lectura del sensor de proximidad. La distancia máxima de detección de objetos se fija a 0.25.

Para el funcionamiento del sigue líneas se emplean tres sensores sigue líneas (Izquierdo, central y derecho), ubicados en la parte delantera del Robot. Dichos sensores se colocan con el eje z hacia abajo. De todos los datos proporcionados por los sensores se usa la intensidad media de la imagen para ajustar la velocidad de los motores. Aunque se incorporó un sensor central, no ha sido necesario su uso, ya que el robot sigue las líneas bien con los otros dos.

En los ficheros adjuntos se proporcionan vídeos de ejemplo de ambas prácticas.

Vídeo VREP

Payara, Kotlin y Mysql

13/09/2018
Artículo original

Bueno comenzando la parte de los servicios vamos a tener básicamente dos endpoints uno para consultar preguntas y otro para insertar preguntas.

GET /services/questions/getAll
POST /services/questions/add

Por tanto creamos el archivo kotlin QuestionService en la carpeta question (primero crear la carpeta question), si no encontramos preguntas regresaremos 404, esto es a modo de ejemplo si quieres puedes regresar 200 y un array vacio es tu gusto y desenio, usaremos Gson como librería para parsear JSON a Kotlin y viceversa:

package question

import com.google.gson.Gson
import javax.ws.rs.*
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response

@Path("/question")
class QuestionService {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/getAll")
    fun getAllQuestionsApi(): Response {
        val questions = getAllQuestions()
        return if(questions.isEmpty()) {
            Response.status(404).build()
        } else {
            Response.status(200).entity(Gson().toJson(questions)).build()
        }
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)

leer más

Cómo hacer un ribbon destacado usando solo CSS y el pseudoelemento ::before

13/09/2018
Artículo original

En este post vamos a aprender una técnica muy sencilla pero resultona para simular una cinta (ribbon, en inglés) con un texto superpuesta con CSS sobre un elemento del contenido.

La aplicación práctica más habitual para esta técnica es la clásica cinta que pone Nuevo por encima de un producto (o cualquier otra característica que se quiera destacar) en un e-commerce, pero realmente se puede aplicar en muchos otros casos, como posts de un blog, imágenes o vídeos de una galería, un listado de usuarios…

Existen muchos tutoriales y generadores online para que no tengas que partir de cero para hacer esto, aunque la mayoría de estos te añaden marcado extra porque presuponen que puedes tocar el HTML directamente. En la mayoría de casos va a ser así, pero a veces hay situaciones especiales en las que el HTML viene impuesto desde el backend (cof, cof, webforms...), y mire usted por dónde, no se puede cambiar tan alegremente.

La técnica de este post es muy similar a los ribbons que usamos en nuestro propio catálogo de cursos de programación. Para el ejemplo (y para simplificar el código HTML) voy a usar una imagen que representa la ficha de uno de nuestros cursos:

Lo que representa esta imagen, en realidad también serían varios elementos HTML, pero poniéndolo como una imagen lo verás todo más claro en el código del ejemplo. Por cierto, el ribbon que vamos a hacer no es el que ve abajo a la derecha. Vamos a colocar uno nuevo arriba a la izquierda, completamente editable desde nuestro CSS. Esta cinta llevará escrita la palabra "¡Nuevo!" para, obviamente, destacar novedades.

Las claves de esta técnica son varias. Si ya las conoces te resultará muy sencillo, pero lo desgloso aquí para los más novatos:

  • Como no podemos tocar el HTML, vamos a incluir el contenido (es solo una palabra) dentro del pseudoelemento ::before
  • El pseudoelemento ::before por defecto es una "caja" como cualquier elemento CSS, así que primero le daremos esa apariencia biselada en los extremos con un truco de magia de CSS
  • Finalmente giraremos el pseudoelemento 45º en sentido contrario a las agujas del reloj y posicionaremos nuestra cinta arriba a la izquierda de manera absoluta con respecto al contenedor del producto. Así crearemos la ilusión de una cinta envolviendo el extremo de la caja.

Preparando el contenedor y el ribbon (la cinta)

Bien, primero el HTML. Aquí tenemos nuestro producto envuelto por el div "curso":


<div class="curso">
    <img src="ficha-curso.png" />
</div>

El pseudoelemento ::before  lo añadimos y mostramos desde nuestro CSS:


.curso {
            /*Le damos al contenedor las medidas de la imagen y lo centramos*/

            width:351px;
            height:170px;
            box-sizing:border-box;
            margin: calc(50vh - 170px) auto;

            /* Esto es importante. Para posicionar el ::before
            de forma absoluta, necesitamos que el contenedor
            tenga position:relative */

            position:relative;
        }

.curso::before { position:absolute; top:0; left:0; display: block; box-sizing:border-box; content:"¡Nuevo!"; text-transform:uppercase; font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-size: 13px; text-align:center; font-weight: 700; color: #fff; /*Le damos medidas estas y color para verlo ahora más claro, pero luego lo modificaremos*/ background: purple; height:40px; width:150px;
}

Como puedes ver, le he dado provisionalmente un ancho y un alto de 40px y un color de fondo para que veas claramente lo que vamos a hacer a continuación.

Ahora modificaremos esto. Le damos un alto igual a cero y añadimos bordes de 25px de ancho del mismo color, y el color de fondo lo pondremos transparente:


.curso::before {
    
        [...]

        background: transparent; 
        height:0;
        width:150px;                      
        border:25px solid purple;
    }

Jugando con los bordes del ::before

El resultado visualmente es similar. Pero ahora viene "la magia", fíjate qué ocurre si ponemos todos los bordes amarillos excepto el inferior (y de paso ajustamos la altura de línea del texto):


.curso::before {

        [...]
                    
        border:25px solid yellow;
        border-bottom:25px solid purple;
        line-height:23px;
    }

Ahora la forma del borde inferior con respecto a los otros tiene los extremos biselados. ¿Ves a dónde quiero llegar? A continuación convertimos los bordes amarillos en transparentes y Voilà!


.curso::before {
    
        [...]    
                  
        border:25px solid transparent;
        border-bottom:25px solid purple;
        line-height:23px;
    }

Bien, nos aprovechamos de la forma que toma el borde inferior para crear nuestra cinta. Ahora la giramos 315 grados, o, lo que es lo mismo, -45 grados (45 grados en sentido contrario a las agujas del reloj)


.curso::before {
    
        [...]    
               
        transform: rotate(-45deg);
    }

Si lo inspeccionas con las herramientas de desarrollo de tu navegador, verás claramente cómo se ha girado el elemento.

Ajustes finales

Por defecto se gira respecto al centro, pero si quieres especificar el centro del giro puedes usar la propiedad transform-origin. En nuestro caso no lo usaremos y lo que haremos será ajustar los valores del posicionamiento absoluto de nuestra cinta con top y left para colocarlo arriba a la izquierda. 

Si quisieras colocarlo en otra de las esquinas solo tendrías que jugar con estos valores y/o usar el borde superior como fondo. Por cierto, también podrías usar ::after en lugar de ::before, o incluso usar ambos a la vez en diferentes esquinas.

Otro detalle final podría ser añadirle un poquito de sombra para enfatizar la superposición, y te quedaría así:


    .curso::before {
    
        [...]    
               
        box-shadow: 0px 3px 5px -3px #000;
    }

Por cierto, puedes descargarte aquí el ejemplo completo (ZIP 25KB) de este tutorial.

Importante: Esto no lo puedes hacer directamente sobre una imagen

En las imágenes no funcionan los pseudoelementos ::before y ::after, necesitas un elemento contenedor que la envuelva para usarlos. Lo explica perfectamente nuestro tutor José Manuel Alarcón en este post de su blog. Por cierto, que con él puedes aprender muchísimo sobre CSS y JavaScript (y te resolverá directamente tus dudas) en nuestro VIII Máster de Front End (aprovecha ahora que tenemos las inscripciones abiertas).

Si puedes tocar HTML aún se puede hacer mejor

Como comentaba al principio, en este tutorial lo hemos hecho de la forma más espartana, tocando solo CSS porque es como lo hemos hecho en nuestro catálogo. Pero si tienes la posibilidad de añadir HTML, por ejemplo, un simple span, puedes colocar en él el texto y usar los pseudoelementos ::before y ::after para crear unos pliegues que den efecto 3D.

A mí me parece más divertido el desafío de hacerlo sin añadir más HTML, pero si quieres que lo desgranemos en otro post, déjanoslo en los comentarios.

Cómo habilitar el arranque desde un USB en un portátil Asus Zenbook (y otros)

11/09/2018
Artículo original

Una de las cosas que más odio hacer es formatear e instalar de nuevo mi sistema operativo. Y eso es especialmente cierto en el caso de hacerlo con un ordenador nuevo. En este caso, al proceso habitual de instalar y configurar todas mis aplicaciones una a una (¡y uso muchas!) le precede el enorme tedio de actualizar por etapas el sistema operativo y además quitarle todo el crapware que incluyen de serie (antivirus inútiles, ofertas varias, juegos...). Para hacerlo más insultante todavía, ahora el crapware no solo lo incluyen los fabricantes sino que la propia Microsoft, en Windows 10, incluye diversas aplicaciones ya pre-instaladas o con su icono para que al darle se instalen: juegos diversos,  versiones de prueba de Office... Hasta Netflix. Total, que es una pesadilla.

Para tratar de minimizar todo esto, según me llega un ordenador nuevo, ya sin mirar siquiera lo que tiene me gusta instalarle mi propia licencia de Windows desde cero. Generalmente tengo en una llave USB la versión más reciente del sistema, que ya incluye todas las actualizaciones excepto quizá las de unas pocas semanas. Esto me ahorra bastante tiempo de actualizaciones y reinicios, además de evitarme el crapware del fabricante (aunque no el de Microsoft).

El problema es que desde hace unos años muchos de los ordenadores nuevos vienen con sistemas de protección para que un usuario con poca experiencia no pueda cometer alguna insensatez metiendo una llave USB que encuentren por ahí. Vamos, que vienen "capados" de modo que no te permiten arrancar desde un USB. Es más, la mayoría ya no te dejan instalar otro sistema operativo directamente (olvídate, por defecto, de instalar Linux, por ejemplo). Pasa en los chipsets basados en UEFI en lugar de en la tradicional BIOS.

IMPORTANTE: lo que describo a continuación está probado en un equipo determinado de mi propiedad e implica tocar en ajustes de bajo nivel del hardware de tu equipo, por lo que siempre es delicado. A mi me ha funcionado y es probable que te funcione a ti también, pero lo haces bajo tu responsabilidad. No te garantizo que te funcione en tu equipo ni que, si tocas donde no debes, te puedas cargar algo. Toma nota de todo lo que hagas en una libreta (o incluso vete sacando fotos) de modo que si algo va mal puedas deshacer los pasos uno a uno hacia atrás y dejar todo como estaba en caso de que algo no vaya como es debido. ¡Avisado quedas!. Allá vamos...

 

¿Cómo podemos solucionarlo?

Bueno, en cada ordenador es un poco diferente porque depende del sistema de gestión de bajo nivel que lleve montado el equipo, aunque generalmente se hará de manera muy similar.

Como ejemplo de cómo conseguirlo paso a paso, voy a mostrar cómo lo he hecho en un equipo nuevo que he tenido que preparar recientemente. En el tuyo si no es igual será muy parecido, así que espero que te sirva. Se trata de equipo portátil ASUS Zenbook UX430UAR.

Este modelo, al igual que muchos de los modernos, tiene activado el modo protegido por defecto y además ofrece un tipo de arranque rápido que impide que aparezca información alguna en pantalla mientras se arranca. Solo veremos el logo de Asus y unos puntitos de Windows dando vueltas mientras arranca. En cualquier caso poco tiempo los veremos porque al llevar un disco SSD muy rápido el arranque desde cero apenas tarda nada, así que tendremos que ser rápidos.

No te olvides de pinchar la llave USB que quieres utilizar para, posteriormente, arrancar el sistema, o al menos una similar. De este modo la tendremos disponible para las configuraciones que vamos a realizar.

Lo que necesitamos es acceder a la configuración de bajo nivel del sistema, para lo cual, nada más darle al botón de encendido debemos pulsar repetidamente la tecla F2 de modo que le dé tiempo a detectarlo y mostrarnos la pantalla de administración en lugar de arrancar Windows directamente. Veremos algo parecido a esto:

Perdón por la calidad de algunas de las imágenes, pero las fotos sacadas directamente en una pantalla de ordenador con un móvil es casi imposible que salgan bien. He hecho lo que he podido...

Se trata de la administración simplificada de la UEFI. Lo primero que debemos hacer es acceder a la configuración avanzada pulsando F7 (Advanced Mode). Una vez dentro de ésta nueva interfaz nos movemos a la pestaña "Boot" y en el primer desplegable (en el caso del Asus, en el tuyo puede estar en otra posición), desactivaremos la opción de "Fast boot", arranque rápido eligiéndolo en el desplegable:

A continuación tenemos que ir ajustando otros parámetros.

Vámonos a la pestaña "Security" y busquemos el apartado "Secure Boot" (arranque seguro) . Al entrar en este sub-apartado tendremos la opción de deshabilitar el control de arranque seguro (Secure Boot Control), un poco más abajo:

Al hacerlo ya podremos volver a donde antes, a "Boot", y ahora sí nos dejará habilitar la opción CSM Support (soporte de CSM) que antes estaba deshabilitada:

CSM son las siglas de Compatibility Support Module. Básicamente es un módulo que permite a un sistema con UEFI arrancar en modo de compatibilidad, como si fuera una antigua BIOS de toda la vida. Habilita esta opción CSM.

Al habilitar CSM se nos ofrecen unas nuevas opciones relacionadas con el funcionamiento modo BIOS. En concreto nos muestra más opciones de arranque que la única inicial que era "Windows Boot Manager". Lo que ocurre es que ahora como segunda opción nos ofrece una partición oculta del disco duro, pero no nuestro USB, que es lo que nos interesa a nosotros:

Bien, si despliegas la primera opción de arranque debería ofrecerte la llave USB como una de las opciones de la lista desplegable. En mi caso una llave USB de la marca Corsair (que es muy rápida e ideal para las instalaciones):

En la segunda opción de arranque (Boot option #2) elige el Windows Boot Manager para que arranque Windows cuando no esté la llave USB pinchada.

Si ahora vuelves al modo "simple" pulsando F7 deberías ver arriba a la derecha, en las opciones de arranque disponibles, las dos nuevas opciones: primero la llave USB y luego Windows:

¡Listo!

Si ahora pulsas F10 para salir de la UEFI grabando los cambios, el sistema debería arrancar de nuevo y reconocer la llave USB. Si tienes una imagen arrancable de Windows o de Linux copiada en él, lo reconocerá y arrancará la instalación tal y como deseábamos.

Una vez que lo hayas instalado puedes dejar la configuración como la tenemos o, si te sientes más tranquilo (por aquello de las garantías por si tienes que llevarlo a reparar), puedes deshacer los pasos descritos más arriba y dejar el sistema UEFI tal  como venía de fábrica. Si ya tienes el sistema operativo instalado debería funcionar igual y ya no necesitas arrancar desde la llave USB.

¡Espero que te resulte útil!

Cómo configurar ASP.NET Core 2.1 en Linux en menos de 10 minutos

10/09/2018
Artículo original

Nota: este artículo es una traducción al español del artículo original de Daniel Crabtree, con su permiso expreso. Visita el blog de Daniel o síguelo en Twitter.

Me sorprendió gratamente lo fácil que resultó configurar e instalar ASP.NET Core 2.1 en Linux. La primera vez lo hice en 15 minutos, sin experiencia previa con .NET Core en Linux. La segunda vez, en producción, me llevó 5 minutos siguiendo estas instrucciones.

En este artículo, muestro cómo instalar .NET Core runtime en CentOS, cómo poner en marcha el proyecto de ejemplo de ASP.NET Core ejecutándose en Kestrel, y cómo configurar tanto el código como el firewall para habilitar el acceso remoto a la aplicación. Finalmente, comento qué haría de manera diferente para el uso real de la aplicación en producción.

Paso 1 - Instalar el runtime

Para ejecutar ASP.NET Core en Linux, deberás empezar por instalar el runtime (las bibliotecas de tiempo de ejecución) de la plataforma.

En CentOS 7, tuve que agregar un paquete de RPM que contenía las referencias del gestor de paquetes YUM, actualizar YUM, y luego instalar el runtime de .NET Core, así:

sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm 
sudo yum update
sudo yum install aspnetcore-runtime-2.1 

Microsoft facilita documentación de las instrucciones similares para las diferentes distribuciones de Linux, incluidas RHEL, Ubuntu, Debian, Fedora, CentOS, openSUSE y SLES.

Si también deseas compilar y desarrollar en Linux, deberás instalar el SDK en vez del runtime. En CentOS, instalarías el SDK con esta instrucción:

sudo yum install dotnet-sdk-2.1 

Paso 2 - Publicar y desplegar

El siguiente paso es publicar y desplegar tu proyecto.

Crear un proyecto de ejemplo

Para poder hacer la prueba rápidamente, creé un nuevo proyecto demo de ASP.NET Core 2.1, ejecutando este comando:

dotnet new razor -o CoreTest

Para que este comando funcione, deberás tener instalado el SDK y no solo el runtime, como acabamos de ver. En mi caso, solamente ejecuté esto en mi máquina Linux para desarrollo local, ya que en el servidor tan solo instalé el runtime.

Agregar soporte para acceso remoto (opcional)

Por defecto, el proyecto ASP.NET Core 2.1 se ejecutará en el servidor web Kestrel en localhost.

Si necesitas que se pueda acceder al sitio web de forma remota (externa) desde otros ordenadores, deberás vincularlo de manera diferente.

Para hacer esto, entré en los archivos del proyecto recién creado y abrí Program.cs. Aquí agregué una llamada al método UseUrls de la siguiente manera:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
  WebHost.CreateDefaultBuilder(args)
    .UseUrls("http://0.0.0.0:5000")
    .UseStartup<Startup>();

Esto vincula el sitio web ASP.NET Core a todas las direcciones IP del servidor, en lugar de solo a localhost , que es lo que hace de manera predeterminada.

Compilar y desplegar

Para compilar el proyecto, simplemente ejecuté:

dotnet publish --configuration Release

Esto construye y coloca todos los archivos necesarios para el despliegue, de forma predeterminada en la carpeta bin/Release/netcoreapp2.1/publish.

Para desplegar, simplemente hay que copiar todos los archivos y directorios de esta carpeta de publicación a /home/coretest en el servidor Linux.

En mi caso, copié los archivos usando SmarTTY , pero podría haber usado FTP o cualquier otro método para copiar los archivos a la carpeta destino.

Paso 3 - Ejecutar la aplicación web

Ahora que .NET Core está instalado y el proyecto está desplegado, queremos ejecutarlo.

Ejecutar Directamente

El método más simple es ir al directorio del sitio web, /home/coretest, y ejecutarlo desde la línea de comandos:

/usr/bin/dotnet CoreTest.dll

Esto ejecuta la aplicación de inmediato, pero es susceptible a bloqueos y debe iniciarse manualmente después de cualquier reinicio del servidor.

Ejecutar como un servicio

Es mucho mejor ejecutar el sitio web como un servicio, de modo que se ejecute automáticamente al arrancar el sistema, tras haberse reiniciado, y también se reinicia automáticamente en caso de algún fallo.

Definir el servicio

Para ejecutar el sitio web como un servicio, tendrás que crear un archivo de definición de servicio. Al mío lo llamé kestrel-coretest y lo creé en esta ubicación:

/etc/systemd/system/kestrel-coretest.service

En este archivo, agregué los siguientes contenidos:

`[Unit] Description=.NET Core Test App

[Service] WorkingDirectory=/home/coretest ExecStart=/usr/bin/dotnet /home/coretest/CoreTest.dll Restart=always RestartSec=10 SyslogIdentifier=dotnet-coretest User=daniel Environment=ASPNETCORE_ENVIRONMENT=Production

[Install] WantedBy=multi-user.target`

Tendrás que modificar la variable User a algo apropiado para tu servidor, y deberás asegurarte de que los archivos que has desplegado tienen los permisos apropiados.

Arrancar el servicio

Después de guardar el archivo de definición del servicio, habilítalo utilizando este comando:

sudo systemctl enable kestrel-coretest.service

Ahora ya puedes arrancar el servicio con:

sudo systemctl start kestrel-coretest.service

Y verifica su estado con este comando:

sudo systemctl status kestrel-coretest.service

También puedes comprobar qué aplicaciones web se están ejecutando usando:

ps -A all | grep dotnet

Esto es especialmente útil cuando se ejecutan varios sitios web en diferentes puertos, ya que mostrará una lista de todos los que están en ejecución.

Paso 4 - Probarlo en localhost

Ahora ya puedes probar el sitio web intentando acceder a él a través de la dirección local (localhost). Desde tu conexión SSH al servidor, usa wget de la siguiente manera:

wget http://localhost:5000

Esto debería descargar la página de inicio y almacenarla en un archivo llamado index.html en el directorio actual. Si no tienes wget puedes instalarlo usando:

sudo yum install wget

Paso 5 - Abrir el firewall y probarlo de forma remota (Opcional)

Si has habilitado el acceso remoto en el paso 2, ahora puedes abrir, por ejemplo, el puerto 5000 en el firewall e intentar conectarte desde una computadora remota.

Puede abrir el puerto 5000 usando firewalld:

sudo firewall-cmd --add-port=5000/tcp --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Si ha funcionado deberías ver 5000/tcp en la sección de puertos tras haber ejecutado el último comando. Debería verse algo como esto:

public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: ssh dhcpv6-client
ports: 5000/tcp
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:

Ahora intenta acceder al sitio web de forma remota desde un navegador en otra máquina de la red local. Si la dirección IP de tu servidor Linux es, por ejemplo, 192.168.1.5 navega a http://192.168.1.5:5000 en tu navegador para acceder. Deberías ver el sitio de demostración:

Truco extra: desinstalación del SDK o del runtime

Si instalas el SDK en el servidor pero luego decides que solo necesitas el runtime (o si simplemente quieres eliminar .NET Core), en CentOS, puedes eliminar el SDK usando:

sudo yum remove dotnet-sdk-2.1

Del mismo modo, puedes eliminar el runtime usando:

sudo yum remove aspnetcore-runtime-2.1

Puede que también quieras eliminar las dependencias que vinieron con el SDK o con el runtime pero que no se están utilizando. En ese caso escribe:

sudo yum autoremove

Cambios a realizar en una implementación en producción

Este tutorial te ha mostrado cómo instalar .NET Core y poner a andar un sitio web ASP.NET Core de manera rápida.

Pero en realidad esto no es adecuado para ejecutarse, tal cual, en producción, ya que Kestrel es un servidor web bastante básico.

Para producción querrás poner un servidor web con más funciones, como IIS, Nginx o Apache delante de Kestrel.

Mi despliegue en Linux

En mi despliegue definitivo en Linux, voy a deshabilitar el acceso remoto eliminando el uso de UseUrls del paso 2, y deshaciendo el paso 5. Luego ejecutaré Apache delante de Kestrel, que actuará como un proxy inverso hacia Kestrel, que responderá solo a localhost.

En mi caso, esto me va a permitir migrar lentamente algunos sitios web existentes de PHP a C# y .NET Core. Planeo usar un proxy inverso hacia algunas URLs de .NET Core, mientras continúo sirviendo otras URL usando PHP. Esto me permitirá migrar todos los sitios web poco a poco en un periodo de tiempo.

"OpenAPI, estandarizando los contratos de las API". Entrevista a Pedro J. Molina

07/09/2018
Artículo original

Openapi Portada

El café humea en la mesa, mientras el tono de llamada del Hangout se repite en mis auriculares, y repaso las preguntas que quiero hacerle a Pedro J. Molina; uno de los mayores expertos en OpenAPI de nuestro país.

Es miembro del ISA – Grupo de investigación de Ingeniería de software aplicada- de la Universidad de Sevilla, liderado por Antonio Ruiz junto a Pablo Fernández, y doctor en informática, especializado en modelado y generación de código.

Recientemente se ha integrado dentro de la Iniciativa OpenAPI, en donde participa de forma activa en la TSC (Technical Steering Committee) y el BGB (Business Governance Board). Y se ha prestado para una larga entrevista donde repasamos esta tecnología.

Una de las cosas que más me llaman la atención de Pedro, es la claridad y sencillez con que contesta las preguntas técnicas de todo nivel que le voy lanzando durante esta hora larga de conversación.

Busca la forma de explicar conceptos muy complejos, para lograr que lleguen a la mayoría de los lectores sin perder, en ningún momento, la rigurosidad de una mente científica.

¿Qué es OpenAPI? Api Consumers

Es un estándar para definir contratos de Api. Los cuales describen la interfaz de una serie de servicios que vamos a poder consumir por medio de una signatura. Por ejemplo, saber que vamos a tener una operación de sumar que va a recibir dos números y que estos son del tipo “entero”.

Tecnologías previas, como CORBA o SOAP, han intentado estandarizar las operaciones RPC (Llamadas a procedimientos remotos) que satisfagan la necesidad de comunicar dos máquinas que están separadas entre sí; y que requieran conocer como una tiene que invocar a la otra, especificando qué tipo de mensajes se aceptan, y qué es lo que se va a devolver.

¿Qué ventaja tiene el enfoque de OpenApi en comparación con definir un API de modo informal?

Se puede exponer un servicio de manera informal, pero la comunicación de cómo acceder al mismo también lo será. Esta comunicación informal puede inducir o incluir equivocaciones al escribirla, describirla, o ser incompleta; siendo una documentación con muy poco valor para poder consumir el servicio, porque depende de que esté bien expresado y sea bien entendido.

Ese es el contrato en que hay que ponerse de acuerdo.

Si hacemos el esfuerzo de formalizar esa definición (contrato), podemos conseguir que una máquina también la aproveche para poder conectarse al servicio y, consumiendo la especificación OpenAPI, construir un Proxy de integración. Es decir, una clase que automáticamente se conecta al servicio.

¿Pero esto no lo hacía ya los WSDL? ¿Qué falló para que sean tecnologías residuales en la actualidad? Arquitectura SOA

CORBA y WSDL son tecnologías para comunicar servicios distribuidos. El paradigma SOA (Arquitectura orientada al servicio) es un estilo arquitectónico basado en la comunicación entre servicios distribuidos débilmente acoplados y altamente interoperables. SOA fue abrazado por la industria y los grandes fabricantes como Oracle, Microsoft, IBM, etc., los cuales se lanzaron a construir sus propias pilas de productos SOA que resultaron ser muy complejas y, en ocasiones, incompatibles entre sí.

Al final la complejidad del uso de la pila de productos SOA fue tal, que la industria abandonó la aplicación de los estándares más farragosos. Y con la llegada de los primeros smartphone y sus limitadas capacidades de cálculo y almacenamiento, migraron del XML al JSON (aunque el primero es mucho más extensible que el segundo) y de los Webservices (y sus sobres SOAP) a las comunicaciones REST.

¿Qué relación tiene Swagger con OpenAPI?

OpenAPI viene de Swagger.

A finales de la década pasada empezó a crecer la economía de las API’s, la cual indica que toda aplicación que quiera escalar y triunfar en el mercado debe incluir una Interfaz de Programación de Aplicaciones potente.

En 2011, Tony Tam crea el proyecto Swagger API para automatizar la documentación y la generación del SDK (framework de desarrollo) de clientes de sus API’s. Diseñando un formato muy sencillo para describir el interfaz, en JSON o YAML, pero lo suficientemente formal como para que las máquinas lo puedan utilizar para crear Proxys (para clientes) y Skeletons (para servidores).

El éxito de Swagger fue fulgurante, convirtiéndose en un estándar “de facto”, y siendo adquirido por la empresa SmartBear que ha liberado todo el proyecto añadiéndolo a la Linux Foundation y atrayendo a los actores más importantes en la industria como Microsoft, IBM, PayPal, Adobe, Google, SAP, etc.

Y dentro de la Linux Foundation está la Iniciativa OpenAPI que es un comité dentro de la Fundación Linux que rige la evolución de la especificación.

¿Por qué está siendo tan apoyado por los fabricantes?

Si bien Swagger ya era libre, SamrtBear lo adquirió y liberó para hacerlo realmente neutral y que los diferentes fabricantes pudieran desarrollar sus 'API Middleware' -productos que se colocan entre tu API y el cliente final-, que te dan servicios de autenticación, autorización, seguridad, identificación, gestión de pagos, entre otros.

En una API que soporte 100 endpoints, la complejidad viene al tener que configurar nuestra herramienta para cada uno de ellos. Sin embargo, al tener una definición del contrato OpenAPI, con un sistema de drag & drop podría configurar de modo automático la herramienta de forma muy sencilla.

Los fabricantes de Middelware son los primeros interesados en que haya un estándar abierto para todos. Y, como hay negocio, se han puesto de acuerdo en dicho estándar siendo este el incentivo que ha conseguido que todos estén convergiendo hacía OpenAPI; incluso los usuarios de RAML, otro framework para definir las API, que se ha integrado en el comité de la Fundación Linux.

“El objetivo de OpenAPI es construir un estándar en la definición de las API para humanos, pero haciendo hincapié en el interfaz para máquinas, facilitando que se configuren e integren de forma autónoma”

Pero no se circunscribe a los fabricantes, también actores tan importantes como la Banca o los gobiernos han entendido las ventajas de la estandarización en OpenAPI . Así Holanda se ha convertido en el primer estado que ha adoptado la definición para el desarrollo de sus aplicaciones.

¿Quién compone OpenAPI? Investigadores de ISA

Hay una representación por cada organización que es miembro de la iniciativa. España está presente por el Grupo de investigación de Ingeniería de software aplicada (ISA) de la Universidad de Sevilla, al igual que por la startup 42 Crunch dedicada a la seguridad y gestión de amenazas a aplicaciones y datos.

El Grupo ISA hace una labor de investigación, más que orientada al negocio, trabajando en métricas de SLA donde se determina la definición de nivel de servicio. El objetivo es proponer extensiones orientadas a la medición y definición de niveles de servicio dentro de los contratos OpenAPI, aplicando la investigación realizada en la implantación industrial.

NdE. Las ramificaciones e implantaciones de estas extensiones al estándar OpenAPI aporta un valor crítico en las capacidades de aplicación de los contratos entre los interfaces de integración. En ningún otro estándar se comprende de manera específica el soporte a la medición de la calidad del servicio.

Dentro de la iniciativa OpenAPI existen tres comités diferenciados:

  • El Comité Técnico, en donde se discute el estándar y se añaden extensiones. Se trabaja con reuniones semanales en donde se revisa el estado de los desarrollos.
  • Comité de Gobierno, en donde se revisa el estado de la iniciativa desde un punto de vista de gestión.
  • Comité de Marketing, donde se diseñan y ponen en marcha acciones de formación, difusión, visibilización, etc. Y que utiliza los fondos aportados por las organizaciones miembros para realizar los eventos o campañas.

Además, de un par de personas a tiempo completo en la Linux Foundation, dedicadas a OpenAPI.

¿Cuáles son los mayores impedimentos que te encuentras en el día a día al trabajar en los comités?

Hay que conciliar los intereses de muchos fabricantes, que cada uno quiere añadir sus propias funcionalidades, las cuales hay que discutir y acordar. Todas las cosas diseñadas por comité son un poquito más lentas que cuando swagger lo construían dos personas y añadían funcionalidad muy rápido, llevando el producto hacia donde querían.

Las cosas en comité van despacio

Lo bueno es que al estar integrado en la Linux Foundation se garantiza que todo lo que se aporta está libre de patentes y que todo el mundo lo va a poder utilizar sin restricciones.

Hay veces que nos gustaría sacar las cosas más rápido, pero es necesario priorizar los temas, y los procesos van un poquito más despacio. Es importante ser cautos con las publicaciones para dar tiempo que la industria implemente estos estándares.

Por ejemplo, aún hay muchos utilizando OpenApi 2.0 mientras esperaban herramientas en desarrollo para la versión 3.0, que lanzamos hace un año.

En resumen, las dificultades de trabajar en un comité no son mayores que las de cualquier proyecto distribuido. Además, la gente que los compone tiene un gran nivel y vienen con las reuniones muy bien preparadas.

¿Puedo trabajar en la versión 3.0 apuntando a un contrato 2.0? Openapi Versiones

En el cambio de versión ha habido ruptura de compatibilidad porque había que reorganizar el estándar e implicaba una mejora muy importante. Sin embargo, próximamente vamos a publicar la versión 3.1, y en esta no va a haber ningún tipo de ruptura ya que somos especialmente cuidadosos de añadir nuevas definiciones que sean interoperables con las versiones anteriores.

¿Yo, como una persona física puedo aportar a la iniciativa OpenAPI?

Si. La manera más normal es acceder al proyecto en GitHub de la especificación y dar de alta una Issue en donde describir al detalle la petición o la aportación que quieres añadir. El comité técnico semanalmente revisa el proyecto en GitHub y, aquellas que son interesantes de desarrollar, puede solicitarte que hagas una contribución (pull request) en donde definas la implementación técnica de la propuesta o abrir una nueva tarea interna para planificar su debate y construcción.

También en el proyecto en GitHub tenemos los documentos de cada una de las reuniones del comité para poder hacer un seguimiento de alguna propuesta o del trabajo realizado, así como del roadmap de la siguiente versión que se está “cocinando”.

La Fundación Linux garantiza el acceso libre y gratuito a los trabajos de la Iniciativa OpenAPI

Por ejemplo, en la futura versión 3.1 se describen los “Overlays” que permiten extender la descripción de la API, y que podrían valer para publicar la documentación de una misma interfaz en varios idiomas; o sistemas de encriptación y firmas digitales utilizando JWE (JSON Web Encryption).

Estando previsto hacer el anuncio de esta nueva versión el próximo 24 de septiembre en la conferencia “API Strategy & Practice”.

¿Qué aportaciones habéis hecho al estándar?

Pedro Molina 01

Desde la Universidad de Sevilla hemos propuesto un par de herramientas para el prototipado rápido de contratos con OpenAPI. Oas-generator y Oas-tools.

A titulo personal desarrollo una librería en TypeScript (openapi-ts) y otra en .Net CORE para la creación de contratos OpenApi 3.0.x, otra que construye definiciones 3.0 para Baucis (framework de construcción de APIs REST).

Además de publicaciones en el blog de la iniciativa, como el de “Tres escenarios comunes para aprovechar la especificación OpenAPI”, y la participación semanal en el comité técnico.

Más información | Iniciativa OpenAPI de la Fundación Linux, GitHub de OpenAPI, Página de la ISA en la Universidad de Sevilla

Middleware en Redux-mx

06/09/2018
Artículo original

La parte que yo creo es mas interesante en la librería Redux es la creación de Middlewares y de ese modo extender Redux mas allá de lo que el equipo de Redux nos pueden ofrecer en la librería.

Esta funcionalidad se la agregue a mi pequeño clon, ahora se puede definir Middlewares que reciban el store (estado) y las acciones que se han lanzado, el ejemplo mas común es hacer un logger de las acciones. Este el ejemplo de este Middleware:

package examples.simple.middlewares;

import mx.com.betotto.redux.Middleware;

public class LoggerMiddleware extends Middleware {

    public LoggerMiddleware() {
        this.middleware = store -> next -> action -> {
            System.out.println("Action dispatched:  " + action.toString());

            //System.out.println("State from Middleware:  " + store.getState());
            return next.apply(action);
        };
    }
}

Como se puede ver tiene una firma muy parecida a el logger que viene en la documentación de Redux

const logger = store => next => action => {
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

leer más

Desarrollo Web móvil: ¿Qué diferencia existe entre el Viewport y la resolución de pantalla de un dispositivo?

06/09/2018
Artículo original

El viewport de un dispositivo es el tamaño teórico que tiene la pantalla de dicho dispositivo, el cual no tiene por qué coincidir con la resolución real de la pantalla (de hecho no coincide casi nunca en dispositivos modernos). La resolución se refiere al número de píxeles que puede mostrar dicha pantalla.

Así por ejemplo, los primeros tablets iPad de Apple, el iPad 1 y el 2, tenían una resolución de pantalla de 1024x768 píxeles, y el tamaño por defecto de su viewport era exactamente el mismo (o sea, una ratio de pixels de 1x). Sin embargo, cuando lanzaron el iPad 3 que ya tenía una pantalla "retina", la resolución física de la misma era 2048x1536, mientras que el tamaño del viewport seguía siendo el mismo: 1024x768 (es decir, la mitad, y por tanto una ratio de píxeles de 2x).

La resolución se refiere al número de píxeles que puede mostrar dicha pantalla; mientras que el viewport es el tamaño en píxeles que simula el navegador móvil que tiene la pantalla cuando visualiza una página web.

Al ancho del viewport se le suele denominar ancho en píxeles CSS, ya que no son píxeles reales sino los que el navegador (y por lo tanto sus hojas de estilo) ven en la práctica de cara a diseñar el layout de la página.

De hecho, para ver la diferencia existente entre el ancho del viewport de un navegador y la resolución de pantalla solo hay que pensar en un entorno de escritorio convencional en el que, al contrario que en un dispositivo móvil, las ventanas se pueden redimensionar a voluntad y la diferencia entre ambos conceptos se hace muy evidente:

Lo que ocurre es que esto es tan natural para nosotros que no pensamos en ello siquiera. En el caso de los dispositivos móviles, al ser todas aplicaciones a pantalla completa, la cosa se complica porque el navegador simula tener un ancho en píxeles inferior al que realmente existe y puede visualizar la pantalla con el objeto de que todo se vea mucho mejor.

¿Qué quiere decir esto en la práctica?

Pues quiere decir que, en un dispositivo móvil, la pantalla realmente tiene capacidad para mostrar imágenes de 2048 píxeles de ancho y 1536 de alto. Y si abres una imagen de estas dimensiones la mostrará perfectamente. Sin embargo, al visualizar algo en el navegador móvil del dispositivo, el área disponible "simula" una resolución de pantalla menor, en este caso la mitad de la capacidad de resolución real, e indica a la página que su resolución es de 1024x768. Es decir, en este caso el viewport es menor (la mitad) que las dimensiones de la pantalla.

¿Por qué se hace esto?

Bueno, pues porque si se usase toda la capacidad de la pantalla para visualizar la página, se vería muy pequeña y no sería útil.

Imagínate por un momento un diseño de página web no-responsiva que tiene un ancho de 2048px, y que se ve de fábula en el monitor de un ordenador con resolución FullHD+ (o sea, 2160 píxeles de ancho, algo mayor). Algo así:

Si la visualizas en un ordenador con una pantalla de menor resolución, digamos 1280x1024, lo que ocurrirá es que le saldrán unas barras de desplazamiento para que puedas moverte y ver la página entera., algo así:

Una cosa muy incómoda, pero al menos podrás ver la página.

Ahora imagina que ves la misma página en la pantalla del iPad 3, pudiendo aprovechar toda la resolución que tiene y que soporta de sobra ese ancho. Es decir, imagina que el viewport y la resolución del iPad 3 tuvieran las mismas dimensiones. ¿Qué verías en ese caso?... Pues algo diminuto que no te permitiría leer nada sin hacer zoom. Algo así:

Con el concepto de viewport, es como si la pantalla del iPad 3, a efectos del navegador, tuviera menos resolución y las cosas se verán a un tamaño más recomendable y fácil de gestionar. En el caso de nuestra página hipotética se verá un trozo de la misma y podrás desplazarte para ver el resto, lo cual, como en el ordenador de poca resolución, es una cosa incómoda, pero podrás leer.

Obviamente en una página bien diseñada con Responsive Web Design (RWD), se visualizaría bien, pues se detectaría una resolución determinada (1024px de ancho) que haría que los contenidos se distribuyesen correctamente y que se viesen a un tamaño apropiado sin necesidad de hacer zoom. Esto es lo que experimentas cada vez que ves una página móvil bien diseñada en un tablet o teléfono, por ejemplo así:

Donde todo se ve a un tamaño adecuado y además se redistribuyen (e incuso ocultan) elementos para aprovechar mejor el espacio (aunque esto no tiene nada que ver con el viewport, sino con RWD).

Cambiar el ancho del viewport en un dispositivo móvil

Es posible indicar otro ancho diferente para el viewport al que tienen por defecto los dispositivos, utilizando la etiqueta "meta" de nombre viewport, que va en la cabecera de la página:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

En este caso, esta etiqueta indica al navegador que se quiere establecer el ancho del viewport al número de píxeles de ancho que realmente tiene la pantalla del dispositivo. Fijando el factor de escala en 1.0 se evita el redimensionamiento del contenido para que quepa en la pantalla, lo cual podría provocar que el texto fuese demasiado pequeño. Además se permite que el usuario cambie ese factor de escala libremente.  Esta es la configuración recomendada para todas las páginas web, salvo casos especiales.

Con esta etiqueta podríamos establecer también un ancho diferente al que tiene por defecto. Incluso un tamaño mayor que la resolución física de la pantalla. En ese hipotético caso, el viewport sería mayor que las dimensiones de la misma (algo poco recomendable). También, si existiese un dispositivo (hoy en día ya no los hay) cuya resolución física fuese inferior a las dimensiones declaradas para el viewport, el efecto sería el mismo.

En resumen: es importante tener claro que el concepto de viewport y de resolución de pantalla son independientes, uno puede ser mayor que el otro y viceversa, y que su relación determina cómo se visualizará un contenido web en la pantalla.

Servir sitios y aplicaciones web en Azure usando un archivo ZIP (Run From Package)

30/08/2018
Artículo original

Se trata de una (relativamente) nueva característica de Azure Web Apps que ha pasado inadvertida para la mayor parte de los usuarios de la nube de Microsoft y que, sin embargo, es muy interesante.

Cuando creamos una aplicación Web en Azure disponemos de una carpeta /site/wwwroot en la raíz que es la que alberga los archivos de nuestra aplicación. El despligue de la aplicación a esta carpeta se puede hacer de múltiples formas: copiando por FTP los archivos, haciendo un push a una rama de un repositorio Git en Gihub o en cualquier otro servicio de Git en la nube (incluyendo un propio repo local en el Azure Web site), e incluso desplegando desde un disco en la nube como Dropbox o OneDrive. Al final se trata tan solo de hacer llegar a esa carpeta los archivos pertinentes. A partir de ese momento el Sitio Web de Azure los ejecuta (si son de aplicaciones backend) o los sirve a Internet (en caso de archivos estáticos).

Desde mediados de febrero de este año 2018 existe una nueva opción muy interesante denominada Run From Package, y que no es ni más ni menos que servir nuestros archivos directamente desde un archivo ZIP que puede estar ubicado en cualquier sitio del mundo. Es decir, lo puedes compartir en tu Dropbox, un una release de Github, en otro servidor o en un Blob de Azure Storage de la misma zona (que es lo que recomienda Microsoft), pero con que sea accesible públicamente llega.

Lo que hace el servicio es montar el .zip en la carpeta /site/wwwroot, como si realmente estuvieran allí, y actúa con ellos como si los hubiésemos copiado mediante cualquier otro medio de despliegue. Espera que lo aclare mejor, que la sutileza igual no se pilla bien:

Azure Web Sites NO copia el zip, lo descomprime en esa carpeta y luego se pone a usarlo. No. Lo que hace es utilizar el ZIP directamente desde su ubicación. ¿Cuánto mola eso?

Aparte de molar mucho, esto tiene varias ventajas:

  • Atomicidad: cuando ejecutas un sitio web desde wwwroot tienes muchos archivos y carpetas y debes gestionarlos uno a uno. Si por ejemplo los copias por FTP (o con cualquier otro método), no se copian todos a la vez, sino uno a uno, por lo que la aplicación puede quedar en un estado indefinido mientras se están copiando, con una mezcla temporal y dinámica de archivos en la que algunos se han actualizado y otros están todavía con la versión antigua. Con Run From Package esto no ocurre y el despliegue de la aplicación se hace para todos los archivos a la vez, en el momento en el que se monta el ZIP. A la hora de desplegar no hay que copiar los archivos sino que el ZIP se monta y en el momento de hacerlo todos los archivos están disponibles a la vez.
  • Rapidez: especialmente en aplicaciones grandes, con muchos archivos. Dado que por debajo la app lee los archivos desde el ZIP a medida que los necesita (con una pequeña caché) no es necesario copiar o descomprimir miles de archivos, por lo que el despliegue es muy, muy rápido aunque haya muchos o sean grandes. Esto hace que además se inicien muy rápido también.
  • Versionamiento: si vas dejando los ZIPs de todas las versiones que vayas desplegando en sus ubicaciones originales, cambiar a una versión anterior cualquiera si algo va mal es tan fácil como apuntar de nuevo al archivo antiguo. Incluso podrías tener varios slots de la misma aplicación, cada uno apuntando a versiones diferentes de la aplicación.
  • Servir aplicaciones para otras personas/empresas: simplemente les pasas la ubicación del archivo y podrán tener la aplicación funcionando en unos segundos, como veremos enseguida. Si sacas una nueva versión, actualizas el ZIP y todo el mundo se actualiza automáticamente (aunque no lo verán hasta que se reinicie la Webapp en Azure, cosa que se hace periódicamente o que se puede forzar si los avisas: enseguida lo veremos). Cada app que "tire" del ZIP además podría configurar unos parámetros propios desde la interfaz de Azure para, por ejemplo, usar su propio almacenamiento externo o una clave de API que necesite la aplicación.

Estas aplicaciones ejecutadas desde un ZIP remoto también tienen sus limitaciones:

  • La principal limitación es que, dado que se ejecutan desde un ZIP remoto, son aplicaciones de solo lectura. Es decir, no se puede escribir a disco desde las aplicaciones. Esto, en realidad, es casi más una ventaja que una limitación ya que una app en Azure debería escribir la información en cualquier caso a almacenamiento externo a las Azure Webapps. Por ejemplo, si los usuarios suben archivos anotarás el nombre del archivo en una base de datos (o una tabla de Azure Storage) y el archivo en sí deberías guardarlo en un Blog de Azure Storage. Así que realmente no es un grave problema.
  • Solo funciona (al menos por el momento) en Web Apps bajo Windows. De todos modos puedes usar .NET, .NET Core, Node.js Python, Java, PHP... no es una limitación tampoco demasiado grave.

Bueno, ya está bien de hablar. Vamos a la práctica. Simplemente quédate con un dato: WEBSITE_RUN_FROM_ZIP.

Este es el nombre del parámetro de la Azure Web App que debes configurar para indicarle en dónde está el ZIP que quieres utilizar. Vas a la interfaz de tu Azure Web App en el portal de Azure, en el apartado de "Settings" creas este apartado, y listo.

Nota: hace unos días anunciaron en GitHub que, dado que la característica se llama "Run From Package" (Ejecutar Desde Paquete), en breve podrás usar también el ajuste WEBSITE_RUN_FROM_PACKAGE en vez del anterior, que es el que yo uso en el vídeo. Funcionarán ambos siempre, aunque no aclaran cuál tendrá preferencia en caso de definir ambos.

Te lo enseño en la práctica en funcionamiento, en el siguiente vídeo:

[youtube:FWnsZpbSJX8]

Aquí te dejo también algunos enlaces de interés:

¡Espero que te parezca útil!

Página Siguiente