Quantcast
Channel: Adictos al trabajo
Viewing all 996 articles
Browse latest View live

Desarrollo de un API Rest con Spark Framework y Spring

$
0
0
En este tutorial vamos a desarrollar un API Rest usando Spark Framework y Spring para implementar el acceso a toda la capa de persistencia de datos.

Índice de contenidos.


1. Introducción

En el anterior tutorial Spark Framework, os presenté un nuevo framework Java con el poder desarrollar vuestras aplicaciones web de una manera sencilla y rápida.

Pues siguiendo con el anterior tutorial, en éste, os voy a enseñar cómo poder desarrollar un API Rest usando Spark y a su vez aprenderemos a inyectar el contexto de Spring dentro de nuestra aplicación y así implementar toda la capa de persistencia de la misma.

Puedes bajarte el código fuente de la aplicación que vamos a desarrollar pinchando aquí.


2. Entorno

El tutorial se ha llevado a cabo con el siguiente entorno:

  • Hardware: MacBook Pro 15 pulgadas (2 Ghz Intel Core i7, 8GB DDR3)
  • Sistema Operativo: macOS Sierra 10.12
  • Java: Java 8
  • Spark: Version 2.5.4
  • IDE: Eclipse Neon

3. Definición de la aplicación

Para el este tutorial, lo que queremos es desarrollar una aplicación que consistirá en un API Rest que servirá para gestionar información sobre películas. De modo que tendremos 5 entradas en nuestro API:

  • GET /movie – Consultar las películas de la base de datos.
  • GET /movie/:id – Consultar una película. El ID de la misma vendrá informado dentro de la URL de la petición.
  • POST /movie – Alta de una película. Los datos de la misma viajarán en formato json en el body de la petición.
  • PUT /movie/:id – Modificación de una película. El ID de la misma vendrá informado dentro de la URL de la petición.
  • DELETE /movie/:id – Borrado de una película. El ID de la misma vendrá informado dentro de la URL de la petición.

El estándar de intercambio de datos que vamos a usar será JSON. Por lo que será necesario usar algún provider para el mapping de datos. Para este ejemplo usaremos Jackson.

También queremos que los datos que maneja la aplicación se queden almacenados en una base de datos. Para ello vamos a embeber una base de datos HSQLDB en donde almacenar los datos de cada una de las películas.

Para el acceso a datos usaremos spring-jdbc por lo que será necesario inyectar el contexto de Spring en la aplicación desarrollada con Spark.


4. Creando el proyecto

Para la creación del proyecto usaremos maven. Si no lo tienes instalado o no sabes usarlo, es un buen momento para hacerlo.

Lo primero de todo será crear un proyecto vacío y después configurar el fichero pom.xml con todas las dependencias del proyecto. A continuación te dejo un fragmento del fichero pom.xml con las dependencias:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spark.version>2.5.4</spark.version>
    <jackson.version>2.3.3</jackson.version>
    <spring.version>4.2.0.RELEASE</spring.version>
    <spring.data>1.10.5.RELEASE</spring.data>
    <hsqldb.version>2.3.3</hsqldb.version>
</properties>

<dependencies>

    <!-- spark -->
    <dependency>
        <groupId>com.sparkjava</groupId>
        <artifactId>spark-core</artifactId>
        <version>2.5.4</version>
    </dependency>

    <!-- Jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.8.5</version>

    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.8.5</version>
    </dependency>

    <!-- Spring Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- Spring Context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- Spring JDBC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- HyperSQL DB -->
    <dependency>
        <groupId>org.hsqldb</groupId>
        <artifactId>hsqldb</artifactId>
        <version>${hsqldb.version}</version>
    </dependency>

    <!-- logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.21</version>
    </dependency>

    <!-- junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
        <scope>test</scope>
    </dependency>

</dependencies>

Como podéis ver, las dependencias a tener en cuenta en el proyecto serán las siguientes:

  • spark-core: spark framework
  • jackson: usado para el mapping de objetos json
  • spring: dar soporte a acceso a la base de datos
  • hsqldb: base de datos que vamos a embeber en nuestra aplicación
  • slf4j: soporte para logging
  • junit: desarrollo de test unitarios

Llegados a este punto, ya podemos empezar a desarrollar nuestra aplicación.


5. Levantando el contexto de Spring en nuestra aplicación

Como vamos usar Spring para poder acceder a base de datos, tenemos que configurar de alguna manera nuestra aplicación para que sea capaz de usar el contexto de Spring y de esa manera levantar los beans que nos permitirán acceder a la base de datos.

Como vimos en el anterior tutorial, Spark Framework, Spark se arranca a través del main() de nuestra aplicación. Al querer inyectar Spring en nuestra aplicación vamos a tener que cambiar un poco la forma de arrancarlo, de forma que sea Spring quien se encargue de arrancar Spark pasándole los beans que serán necesarios para poder acceder a la capa de datos.

Con Spring podemos crear beans mediante su definición en ficheros XML o a través de anotaciones en las clases Java. En el siguiente trozo de código usaremos @Configuration para indicar que la clase que contiene dicha anotación forma parte de la configuración de Spring.

Del mismo modo activaremos el escaneo de beans de la aplicación, para que Spring los instancie, a través de la anotación @ComponentScan.

Por últimno, la configuración de la aplicación es cargada por la clase AnnotationConfigApplicationContext que se encargará de levantar el contexto de Spring en nuestra aplicación. Dicha clase recibe por parámetro la/s clase/s que contiene/n la configuración de la aplicación.

@Configuration
@ComponentScan({"com.adictos.spark.movies"})
public class Application {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class, DataBaseConfig.class);
    new MoviesApplication(ctx.getBean(MoviesService.class));
    ctx.registerShutdownHook();
  }

}

Como podéis ver, será Spring quien arranque la aplicación desarrollada con Spark (MoviesApplication.java), pasándole al constructor de la misma la instancia del bean de la capa de servicios que desarrollaremos, que le permitirá acceder a la capa de datos.

Qué duda cabe, que para que podamos obtener la instancia del bean de servicio (MoviesService.class), la clase que lo implementa debe estar anotada con @Service para que Spring pueda instanciarla en el arranque de la aplicación y la añada al contexto.

@Service
public class MoviesServiceImpl implements MoviesService {

  @Autowired
  private MoviesRespository moviesRepository;

  public Movie addMovie(Movie movie) {
    return moviesRepository.addMovie(movie);
  }

  public void deleteMovie(Long id) {
    moviesRepository.deleteMovie(id);
  }

  public Movie updateMovie(Movie movie) {
    return moviesRepository.updateMovie(movie);
  }

  public Movie getMovie(Long id) {
      return moviesRepository.getMovie(id);
  }

  public List getMovies() {
    return moviesRepository.getMovies();
  }

}

6. Creando la capa de acceso a datos

Como hemos dicho al principio, usaremos spring-jdbc para acceder a la base de datos. Para ello, primero será necesario configurar el DataSource que usaremos para crear la conexión con la base de datos.

Para crear el DataSource de la base de datos vamos a crear una clase anotada con @Configuration para indicarle a Spring la configuración.

Mediante la clase EmbeddedDatabaseBuilder podremos crear una base de datos embebida en memoria, en este caso HSQLDB. Y con @Bean inyectaremos el Datasource en el contexto de Spring para tenerlo disponible y accesible desde otros beans.

@Configuration
public class DatabaseConfig {

  @Bean
  public DataSource dataSource() {
    EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
    EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).setName("movies").addScript("ddl.sql").addScript("dml.sql").build();
    return db;
  }

}

Como se puede ver en el código fuente, el builder nos permite lanzar scripts tras la creación de la misma. En nuestro caso, vamos a crear la tabla MOVIE y vamos a cargarla con unos registros de ejemplo.

CREATE TABLE MOVIE(
  ID INT PRIMARY KEY,
  TITLE VARCHAR(255),
  DIRECTOR VARCHAR(255),
  YEAR INT,
  SYNOPSIS VARCHAR(1024)
  );

  INSERT INTO MOVIE(ID, TITLE, DIRECTOR, YEAR, SYNOPSIS)
    VALUES(1, 'How to train your dragon', 'Dean DeBlois, Chris Sanders', 2010, 'Hiccup (Jay Baruchel) is a Norse teenager ...');
  INSERT INTO MOVIE(ID, TITLE, DIRECTOR, YEAR, SYNOPSIS)
    VALUES(2, 'Interstellar', 'Christopher Nolan', 2014, 'In Earth''s future, a global crop blight and second Dust Bowl are...');
  INSERT INTO MOVIE(ID, TITLE, DIRECTOR, YEAR, SYNOPSIS)
    VALUES(3, 'Star Wars: The Force Awakens', 'J.J.Abrams', 2015, 'Thirty years after the defeat of the Galactic Empire, Han ...');
  INSERT INTO MOVIE(ID, TITLE, DIRECTOR, YEAR, SYNOPSIS)
    VALUES(4, 'The revenant', 'Alejandro González Iñárritu', 2016, 'While exploring the uncharted wilderness in 1823, ...');

Llegados a este punto, ya podemos crear nuestro repositorio, el cual se encargará de acceder a la base de datos. Para ello definiremos la interfaz del repositorio y anotaremos su implementación con @Repository para indicar a Spring que esa clase es la que se va a encargar de realizar el acceso a la base de datos.

Bastará con usar JdbcTemplate de Spring para poder acceder a la base de datos. Para ello, será necesario pasar en el constructor del repositorio la instancia del DataSource que acabamos de crear:

@Repository
public class MoviesRepositoryImpl implements MoviesRespository {

    private static final String SYNOPSIS = "synopsis";
    private static final String DIRECTOR = "director";
    private static final String TITLE = "title";
    private static final String YEAR = "year";
    private static final String ID = "id";

    private JdbcTemplate template;

    @Autowired
    public MoviesRepositoryImpl(DataSource ds) {
        template = new JdbcTemplate(ds);
    }

    @Override
    public Movie addMovie(Movie movie) {
        final String query = "insert into movie(id, title, director, year, synopsis) values(:id, :title, :director, :year, :synopsis)";
        final Map params = new HashMap();
        params.put(ID, movie.getId());
        params.put(TITLE, movie.getTitle());
        params.put(DIRECTOR, movie.getDirector());
        params.put(YEAR, movie.getYear());
        params.put(SYNOPSIS, movie.getSynopsis());
        template.update(query, params);
        return movie;
    }

    @Override
    public void deleteMovie(Long id) {
        final String query = "delete from movie where id = :id";
        final Map params = new HashMap();
        params.put(ID, id);
        template.update(query, params);
    }

    @Override
    public Movie updateMovie(Movie movie) {
      ...
    }

    @Override
    public Movie getMovie(Long id) {
      ...
    }

    @Override
    public List getMovies() {
      ...
    }

}

7. Configurando nuestros recursos REST con Spark

Hasta ahora no hemos hecho nada más que configurar Spring e implementar la capa de servicio y de datos. Lo que ahora toca es definir los distintos recursos REST y su implementación. Para ello vamos a usar Spark Framework.

Las recursos a definir serán los siguientes:

  • GET /movie – Consultar las películas de la base de datos.
  • GET /movie/:id – Consultar una película. El ID de la misma vendrá informado dentro de la URL de la petición.
  • POST /movie – Alta de una película. Los datos de la misma viajarán en formato json en el body de la petición.
  • PUT /movie/:id – Modificación de una película. El ID de la misma vendrá informado dentro de la URL de la petición.
  • DELETE /movie/:id – Borrado de una película. El ID de la misma vendrá informado dentro de la URL de la petición.
public class MoviesApplication {

      private static final String ID_PARAMETER = "id";
      private MoviesService moviesService;

      public MoviesApplication(final MoviesService moviesService) {
          this.moviesService = moviesService;
          initializeRoutes();
      }

      private void initializeRoutes() {

          port(8080);

          get("/movie", (req, res) -> {
              return moviesService.getMovies();
          }, new JsonTransformer());

          get("/movie/:id", (req, res) -> {
              final String id = req.params(ID_PARAMETER);
              return moviesService.getMovie(Long.valueOf(id));
          }, new JsonTransformer());

          post("/movie", (req, res) -> {
              final Movie movie = readBody(req.body());
              return moviesService.addMovie(movie);
          }, new JsonTransformer());

          delete("/movie/:id", (req, res) -> {
              final String id = req.params(ID_PARAMETER);
              moviesService.deleteMovie(Long.valueOf(id));
              return "";
          });

          put("/movie/:id", (req, res) -> {
              final Movie movie = readBody(req.body());
              return moviesService.updateMovie(movie);
          }, new JsonTransformer());

      }

      private Movie readBody(final String jsonData) throws JsonParseException, JsonMappingException, IOException {
          final ObjectMapper objectMapper = new ObjectMapper();
          return objectMapper.readValue(jsonData, Movie.class);
      }
}

¿Y ya está? Pues sí, con el código anterior acabamos de montar nuestro API REST en cuestión de 5 minutos. No tenemos que preocuparnos por el servidor, por la configuración de los servicios o por tener que definir ningún tedioso fichero XML. Spark lleva embebido un servidor Jetty que levantará en cuanto arranquemos la aplicación y de lo único que tendremos que preocuparnos es de probar nuestra aplicación 😉

Como el estándar de intercambio de datos debe de ser JSON, se hace uso de Jackson para la conversión de los objetos JSON a clases Java. Para ello, cada vez que el endpoint reciba datos JSON en el body de la petición, se hará una conversión a través de la clase ObjectMapper que nos proporciona Jackson.

private Movie readBody(final String jsonData) throws JsonParseException, JsonMappingException, IOException {
    final ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(jsonData, Movie.class);
}

Del mismo modo, las respuestas que debe dar nuestra aplicación deben ser de tipo JSON. Para ello, Spark nos permite indicar el tipo de conversor que se va a usar para para transformar los datos. Para ello será necesario implementar la interfaz ResponseTransformer en la cual se hará la conversión de datos:

public class JsonTransformer implements ResponseTransformer {
    public String render(Object model) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(model);
    }
}

8. Probando la aplicación

Para probar la aplicación, la arrancaremos a través del main() de la clase Application.java. Esta clase levantará el contexto de Spring y arrancará nuestro API REST en el puerto 8080.

Una vez hemos arrancado la aplicación, instalaremos un cliente REST en nuestro navegador para poder hacer pruebas. Yo tengo instalado en Chrome DHC, pero cualquier otro te servirá para hacer las pruebas.

Vamos a recuperar el listado de películas. Para ello introduce la siguiente URL ‘http://localhost:8080/movie’ y lanza una petición de tipo GET:

Vamos a probar a recuperar la película cuyo ID es el 3. Para ello lanza una petición de tipo GET a la siguiente URL: ‘http://localhost:8080/movie/3’

Probemos a actualizar una película. Bastará con lanzar una petición de tipo PUT a ‘http://localhost:8080/movie/1’, enviando en el body el json con los datos de la película:


9. Conclusiones

En este tutorial hemos visto cómo montar un API Rest es muy sencillo gracias a Spark. Gracias a que está basado en Java 8 y se apoya en el desarrollo de funciones Lambda, el código fuente es muy limpio, claro y conciso.

En este tutorial he querido ir un poco más allá e integrarlo con Spring para que veáis que el desarrollo no tiene porque estar 100% hecho con Spark. Podemos aprovechar las potencia de ambos frameworks y combinar su uso a nuestro gusto y necesidad.

Aunque el objetivo no era hablar de Spring, este ejemplo también te servirá para ver cómo inyectar el contexto de Spring dentro de una aplicación que no está 100% desarrollada con Spring y levantar el contexto sin ningún tipo de fichero XML basando la configuración en clases Java anotadas.

Espero que el tutorial os haya sido de utilidad.

Un saludo.


ATEM Television Studio – Guía para conexiones y emisión en directo

$
0
0

En este tutorial explico qué es el ATEM Television Studio, cómo configurarlo con el ordenador y cómo se puede emitir en directo con un aparato de streaming.

Índice de contenidos


1. Introducción

ATEM Television Studio, para quien no lo conozca es uno de los productos de la empresa Blackmagic. Se trata de una mezcladora digital que permite crear contenidos en directo con alta definición de la forma más fácil.

Para usar el ATEM no es necesario un profesional. Funciona tanto con cámaras HD económicas de consumo masivo como con modelos profesionales de gran tamaño. Tiene un diseño portátil, metálico y resistente.

Incluye conexiones de audio y vídeo compatibles con prácticamente todos los formatos SD y HD 720p o 1080i. Cuenta con 4 entradas HDMI/SDI, salidas de programa y multiview, puerto USB, Ethernet y conexiones para señales de audio digital AES/EBU.

Incluye un software (ATEM Software Control) con una interfaz elegante que permite controlar el dispositivo y brinda mayores opciones desde el punto de vista creativo. Los distintos controles hacen posible alternar entre diferentes cámaras y cambiar el tipo de transición. Se pueden cargar elementos gráficos para mostrarlos inmediatamente o componer imágenes, regular la mezcla de audio…

En este tutorial que hemos preparado mi compañera Sonia Sieiro y yo, no vamos a explicar a fondo ATEM Television Studio, puesto que hay varios modelos de mezcladora, sino que vamos a explicar cómo configurarlo para usarlo por primera vez y cómo se puede usar este aparato junto con otro aparato llamado VIDIU Teradek que sirve para hacer streaming.


2. Manejo mezcladora


2.1. Conexión mezcladora

  1. Conectar la mezcladora mediante cable ethernet al ordenador
  2. Conectar las cámaras mediante HDMI a la mezcladora y switch por USB
  3. Comprobar que en ambas cámaras la salida de vídeo HDMI está en la misma calidad que la mezcladora (Camera- rueda de configuración abajo a la izquierda – set video standard) o en AUTO
  4. Comprobar que en ATEM Setup utility y en Preferencias del programa la IP es: 192.168.10.50



  5. Ir a preferencias de red, en ethernet y configurar la IP como aparece en la imagen (IMPORTANTE copiar la subred)

  6. Conectar el monitor a la salida multiview de la mezcladora y comprobar que llega la señal de las cámaras.
  7. Abrir ATEM software control y comprobar que recibe la señal.


2.2. Audio


Para emitir audio, lo conectamos a cualquiera de las cámaras, después nos vamos al menú Audio de la mezcladora y comprobamos que en dicha cámara el sonido está en ON y que el resto está apagado. En la señal de Master podemos comprobar si el audio está siendo enviado a Programa.


2.3. Poner mosca (gráfico)

  1. Generamos con photoshop (o cualquier otro programa de edición de imagen) el Logo o imagen que queramos incrustar de manera que el fondo o todo lo que no sea logo sea de color negro, ya que esto lo interpretará la mezcladora como transparente. Lo hacemos sobre un lienzo del tamaño de la imagen que vayamos a emitir (p.e 720p).
  2. Vamos al menú media e importamos la imagen, nos aseguramos de que la imagen que queremos poner como mosca esté en Media player.


  3. Volvemos al menú Switcher y en el menú lateral pulsamos en Upstreamg Key 1 y ponemos los siguientes ajustes:

  4. Después nos aseguramos que tenemos iluminada la tecla KEY 1 y si no, la pulsamos:


  5. Una vez hecho esto debería incrustarse en nuestra imagen la marca de agua. Tenemos que tener en cuenta que tenemos que cambiar de cámara desde el bus de programa ya que si lo hacemos por transición la marca desaparecería.

3. Conexión Streaming

Antes de hacer nada con el aparato de streaming lo que debemos hacer es crear un evento en nuestra cuenta de Youtube:

  1. Lo primero que tenemos que hacer es conectar el ordenador por cable en la misma red en la que vamos a realizar el streaming.
  2. Una vez conectado vamos a Youtube y creamos un nuevo evento en nuestro canal desde gestor de vídeos- emisión en directo – eventos – nuevo evento en directo.
  3. Fijamos fecha y hora y dejamos el evento en público.
  4. Una vez guardado en configuración de la ingestión ponemos los siguientes parámetros:
  5. Una vez creado desconectamos el ordenador de la red y tendremos que utilizar otro ordenador para hacer el seguimiento.

Para conectar el aparato de Vidiu Teradek vamos a realizar los siguientes pasos:

  1. Conectamos el streaming a la red.
  2. Conectamos el cable de red que hemos quitado antes del ordenador al streaming.
  3. Conectamos por HDMI desde la salida de programa de la mezcladora a la entrada HDMI del streaming.
  4. Encendemos el streaming.
  5. Cuando sale “checking internet conection” pulsamos el botón de menú y le damos a SETUP.
  6. Elegimos Wi-fi o Wired. En este caso tenemos que elegir Wired ya que para emitir realización en directo el Wifi no tiene suficiente ancho de banda por lo general.
  7. Una vez que elegimos la opción por cable nos da a elegir entre DHCP o manual. Elegimos DHCP.
  8. Por lo general suele conectarse automáticamente. Si no, comprobamos la IP.
  9. Esperamos a que se conecte y comprobamos que le llega la señal de vídeo.
  10. En el menú vamos a Broadcast settings y en la pestaña eventos-existing- elegimos el que hemos creado en Youtube para la retransmisión.
  11. Una vez que tenemos seleccionado el evento para comenzar la emisión pulsamos el botón rojo – start broadcasting?- Live
  12. Comprobamos que en la pantalla pone LIVE y que hay 3 luces azules en la pantalla

La foto de debajo muestra un esquema de conexiones con dos cámaras, un monitor multiview y el aparato de streaming:



4. Conclusiones

Como veis es muy sencillo usar la mezcladora digital y conseguir una retransmisión en directo. Solamente hay que seguir los pasos uno a uno, hacernos un esquema de qué queremos hacer y cómo lo vamos a conectar es lo más importante. Y probar, porque como más se aprende es a través del conocido aprendizaje por ensayo y error. Si estáis pensando en adquirir equipo para realizaciones en directo o simplemente queréis hacer streaming, los dos aparatos que mencionamos más arriba son una buena opción de compra.

Primeros pasos con Wiremock para simular APIs

$
0
0

En este tutorial vamos a ver una introducción a la versión Standalone de WireMock para poder mockear los servicios a los que se conectan nuestras aplicaciones.

1.Introducción

El desarrollo de aplicaciones basadas en servicios es una realidad desde hace lustros que no hace más que reforzarse con la llegada de los microservicios: de una forma u otra nuetros desarrollos acaban consultando datos a servicios API basados en HTTP (SOAP, REST…). Esto establece unas dependencias que dificultan separar y aislar el desarrollo de cada una de las partes: si un servicio A depende de un servicio B y éste no está terminado de desarrollar, la programación del servicio A se verá afectada negativamente.

Es fundamental contar con algún mecanismo para modelar las interacciones con los servicios a los que nos conectamos, de modo que podamos contar con "mocks" que nos permitan romper momentáneamente la dependencia con la implementación real de esos servicios.

Es algo sencillo que cualquiera puede hacer en casi cualquier plataforma. Es fácil crear en nodeJS o Python un script que levante un servidor HTTP y devuelva los valores que queramos. También lo es en Java, pero pienso que debemos centrarnos en resolver el problema y no en crear nuevos, así que mejor si usamos una herramienta completa y contrastada, en vez de desarrollar la nuestra propia.

Wiremock es un producto bastante completo y en continua evolución. Ya lo usábamos para el desarrollo de test de integración (es muy fácil de usar en Java con su API), pero nunca la había usado como herramienta standalone fuera del código de los tests. La verdad es que me ha sorprendido gratamente por su facilidad de uso y lo rápido que hemos podido implantarla. Espero que este tutorial te anime a probarla.

2. Descarga y arranque de Wiremock standalone

Para descargar la versión standalone podemos ir a los repositorios de Maven y localizar la que deseemos:

http://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/

En el caso de este tutorial, la última versión fue la 2.5.1, así que la podemos descargar con nuestro navegador o con un simple wget

wget http://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/2.5.1/wiremock-standalone-2.5.1.jar

Para comenzar a usar Wiremock es muy sencillo: se ejecuta como cualquier JAR de Java, siempre que tengamos Java bien instalado en nuestra máquina:

java -jar wiremock-standalone-2.5.1.jar

Nos saluda con un bonito ascii-banner:

Y además nos indica en el puerto en el que está escuchando. Si tenías otra aplicación escuchando en ese puerto siempre puedes cambiarlo con el parámetro –port:

java -jar wiremock-standalone-2.5.1.jar --port 98080

Puedes encontrar más opciones de configuración de arranque con el parámetro –help:

java -jar wiremock-standalone-2.5.1.jar -help

En la renovada página de documentación de Wiremock puedes encontrar una buena descripción de lo que hace cada parámetro: http://wiremock.org/docs/running-standalone/

3. Mappings y ficheros.

Si has ejecutado Wiremock Standalone como te he indicado anteriormente, podrás observar que se han creado automáticamente dos ficheros en el mismo lugar en el que reside el .jar:

  • mappings: contiene ficheros en formato JSON con las combinaciones de request a las que atienda y las responses que tiene que dar.
  • _files: contiene los ficheros con el contenido de las responses, y que son referenciados desde los mappings. Es posible no usar un fichero y especificar la respuesta en la configuración pero es algo que, a poco que crezca la respuesta, no te recomiendo.

Entonces configurar las combinaciones de request/response que necesitamos es una tarea muy sencilla. Sigue estos pasos:

3.1. Creamos el mapping:

Vamos al directorio mappings.

Creamos un fichero con extensión JSON y el nombre que nos parezca, como por ejemplo: route.json.

Incluimos el siguiente código:

{
    "request": {
        "method": "GET",
        "url": "/route"
    },
    "response": {
        "status": 200,
        "body": "Hola! Esto no es el servidor real, es Wiremock ;-)"
    }
}

Este código indica a Wiremock que cuando reciba una request de tipo GET en la ruta /route, deberá devolver un HTTP 200 (OK) con el body indicado más abajo. En esta primera prueba no nos hará falta el fichero en el directorio __files.

Ahora arrancamos wiremock, que detectará la nueva ruta que está en un fichero con extensión .json en su directorio de mappings. Para ver el comportamiento vamos a arrancarlo con el parámetro –verbose:

java -jar wiremock-standalone-2.5.1.jar --verbose

Con un navegador (o un wget, curl o telnet) accedermos a http://localhost:8080/route, cumpliendo el request que hemos especificado

Y podemos ver en la consola que todo ha ido bien:

Como he usado Google Chrome para hacer la petición, ha intentado además bajar el "favicon" de la página, y Wiremock, al estar en modo verbose, nos indica que no tiene mapping:

3.2. Fichero de respuesta

Si nuestra response es más extensa, es seguro que querremos especificarla en un fichero separado. Para ello:

  • Preparamos un fichero de texto simple con el body de la respuesta. Lo guardamos en el ficero __files con un nombre cualquiera y cualquier extensión, como por ejemplo: route.txt. El contenido puede ser
Hola! Esto no es el servidor real, es Wiremock, y además ¡te respondo desde un fichero! ;-)
  • Y vamos a indicar un nuevo mapeo en el directorio mappings, similar al anterior pero indicando que lea la respuesta de un fichero. Se hace cambiando el atributo "body" de la respuesta por "bodyFileName":
{
    "request": {
        "method": "GET",
        "url": "/routeFile"
    },
    "response": {
        "status": 200,
        "bodyFileName": "route.txt"
    }
}

Por defecto va a buscar en el directorio __files el fichero indicado: "route.txt". Sencillo ¿no?

Arrancamos de nuevo e servidor y hacemos una petición HTTP GET a http://localhost:8080/routeFile

Así que ya lo tienes bien fácil para empezar a hacer tu servidor de "mocks" si conoces las entradas y las salidas.

Es posible complicar bastante esta lógica: estableciendo patrones en las request o devolviendo respuestas más complejas. Puedes consultar en estas secciones de la documentación: http://wiremock.org/docs/request-matching/ y http://wiremock.org/docs/stubbing/

4. Modo Proxy para Grabación

Si tu desarrollo se está conectando a un servidor y las respuestas no son triviales (lo habitual realmente), es posible que el trabajo de especificar resquest/responses en el mapping se haga eterno… ¡No te preocupes! Wiremock puede actuar de intermediario entre cliente y servidor, grabando las comunicaciones.

La idea es que Wiremock se sitúa entre el cliente y el servidor, actuando como proxy. Durante esta fase, podemos decir a Wiremock que grabe las peticiones del cliente y las respuesta que le da el servidor a estas peticiones:

  • Graba un fichero con la ruta en formato JSON que incluye la request y la response.
  • Graba el body de la respuesta en el directorio __files

Posteriormente, si desactivamos en Wiremock la opcion de grabación, y dejamos al cliente apuntando a Wiremock, podremos prescindir del servidor, que es lo que buscábamos…

¿Cómo lo hacemos?

Vamos a ver un ejemplo con nuestro Google Chrome (cliente) y una página Web que devuelve JSON. En este caso usaré: http://date.jsontest.com/

Si escribimos la url http://date.jsontest.com/ en google Chrome, se conecta a este servicio de Internet y nos devuelve una respuesta como la siguiente:

Así tenemos un cliente (Chrome) que se conecta a un servicio (http://date.jsontest.com/) en condiciones normales:

Lo que vamos a hacer es intentar sustituir al servidor (http://date.jsontest.com/) para por ejemplo si queremos trabajar sin tener acceso a Internet:

Para ello interpondremos a Wiremock entre ambos, de modo que Chrome se va a conectar a Wiremock y éste, actuando como Proxy se va a conectar al servidor (http://date.jsontest.com/) y le va a proporcionar a Chrome las respuestas que obtenga, como si fuese transparente.

Mientras se produce la comunicación Wiremock va a ir construyendo los ficheros de mappings y __files automáticamente.

Para ello vamos a arrancar Wiremock indicándole que actúe como proxy y que grabe la comunicación. Le indicaremos que todas las peticiones que reciba, las redirija al servidor http://date.jsontest.com/:

java -jar wiremock-2.5.1-standalone.jar –proxy-all="http://date.jsontest.com/" –record-mappings –verbose

Ahora, en el cliente (Chrome), en vez de acceder a http://date.jsontest.com/ vamos a acceder a la URL de Wiremock:

Podemos ver en la imagen como Google Chrome ha recibido la información como si accediese al servidor realmente.

Vemos en la salida de Wiremock que ha detectado la petición y la ha redirigido al servidor, grabando la respuesta:


Como curiosidad, el servidor ha respondido con la compresión GZIP activa, así que la respuesta que muestra por el log está en este formato y no la podemos interpretar a simple vista. No te preocupes que Wiremock la almacena sin comprimir en __files.

Ahora paramos el servidor (cmd+c o ctrl+c, aunque hay forma administrativa de hacerlo a través de una interfaz REST) y vemos a ver qué ha escrito Wiremock:

Para el Mapping ha creado el fichero "mapping-(root)-fntv3.json":

{
  "id" : "e05c2f66-0b0b-335d-aa3f-27151f4da0ae",
  "request" : {
    "url" : "/",
    "method" : "GET"
  },
  "response" : {
    "status" : 200,
    "bodyFileName" : "body-(root)-fntv3.json",
    "headers" : {
      "Access-Control-Allow-Origin" : "*",
      "Content-Type" : "application/json; charset=ISO-8859-1",
      "X-Cloud-Trace-Context" : "10f1b68647af1cce777ec4e108373494;o=1",
      "Vary" : "Accept-Encoding",
      "Date" : "Sat, 04 Feb 2017 12:24:39 GMT",
      "Server" : "Google Frontend",
      "Cache-Control" : "private"
    }
  },
  "uuid" : "e05c2f66-0b0b-335d-aa3f-27151f4da0ae"
}

Y como se puede leer en el atributo "bodyFileName", en el directorio __files etá el fichero de respuesta que ha devuelto:

{
   "time": "12:24:39 PM",
   "milliseconds_since_epoch": 1486211079402,
   "date": "02-04-2017"
}

Ahora ya estamos en condiciones de desconectarnos del servidor. Paramos Wiremock para que no grabe más y lo levantamos normalmente:

java -jar wiremock-standalone-2.5.1.jar

Estamos en este paso:

Como tenemos la nueva ruta especificada, si hacemos una petición a http://localhost:8080 nos va a response con el JSON que grabamos antes:


Como ves es la respuesta que hemos grabado, y por tanto, siempre nos devuelve la misma hora con los mismos segundos. Ya si queremos trabajar con ese JSON sin conexión al servidor podemos hacerlo.

Existen formas de hacer que esta plantilla de respuesta tenga cierto dinamismo, pero eso lo dejamos para otro tutorial.

5. Conclusiones

En este tutorial hemos visto cómo usar Wiremock en su versión Standalone para poder crear nuestro servidor de respuestas "fake" con muy poco esfuerzo y así poder desarrollar nuestros clientes de servicios sin depender del estado del desarrollo del servidor.

Convertir una imagen en acuarela con Adobe Photoshop CC

$
0
0

En este tutorial aprenderemos a transformar una imagen mediante Photoshop para que parezca pintada con manchas de acuarela.

Índice de contenidos


1. Introducción

En este tutorial utilizaremos una imagen de manchas de colores y una fotografía para realizar un efecto de acuarela con el programa Photoshop de manera muy sencilla y en menos de 10 minutos.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2,5 Ghz Intel i7, 16GB).
  • Sistema Operativo: MacOS Sierra 10.12
  • Adobe Photoshop CC 2015 17.0

3. Preparación

En primer lugar elegiremos los dos archivos que vamos a utilizar para nuestra imagen. Necesitaremos una fotografía que nos guste, a ser posible sin fondo, ya que la mancha quedará más limpia, y una mancha que tenga unos colores que nos gusten. En este caso utilizaremos las siguientes:



4. Orden de capas

Para comenzar importamos ambas fotografías a nuestro proyecto de Photoshop y pulsamos botón derecho en la capa fondo y seleccionamos capa a partir de fondo.

Después, colocamos en el panel de capas la imagen de la mancha por encima de la fotografía.

Por lo general si la imagen tiene calidad, la imagen de la mancha será más pequeña que la fotografía, por lo que la seleccionamos y con “Cmd + T” cambiamos el tamaño hasta que tenga las medidas aproximadas de lo que queremos que sea nuestra imagen final.


Por último, la imagen de la mancha la metemos en un grupo con el botón derecho y “Grupo a partir de capas” o el atajo de teclado “Cmd + G”


5. Gama de colores

Lo siguiente que tenemos que hacer es ocultar la capa de las manchas pulsando el ojo que tenemos al lado de cada capa y seleccionar la imagen de nuestra fotografía.

Una vez oculta (en nuestra pantalla sólo tiene que aparecer la fotografía), seleccionamos la capa de la fotografía y en el menú superior, dentro de selección pulsamos gama de colores y colocamos los siguientes ajustes:


Después, vamos a selección de nuevo y pinchamos en invertir o utilizamos el atajo de teclado “Shift + cmd + I”, para invertir la selección.

Sin perder esa selección, en la ventana capas pulsamos en el grupo que teníamos oculto para seleccionarlo, y pulsamos en el icono de máscara de capa. Una vez hecho esto mostramos la capa que teníamos oculta y obtendremos algo parecido a esto:



6. Retoques finales

Una vez hechos los pasos anteriores, como veréis aún seguimos viendo la fotografía por debajo. Para solucionar ésto, pinchamos en la capa de la fotografía y creamos una nueva capa, en el menú superior “Capa – nueva – capa” que será transparente por lo que no apreciaremos ningún cambio de momento.

Es importante que esta capa transparente se encuentre entre la fotografía y el grupo en el que teníamos metida la capa.

Lo siguiente que haremos es, teniendo la capa transparente seleccionada, utilizar la herramienta bote de pintura para rellenarla de blanco.


Llegados a este punto, si el resultado no os gusta, tendréis que volver al paso de la selección con gama de colores y con el cuentagotas hacer otra selección.

Según la mancha que utilicéis os quedará un resultado distinto. En mi caso, por ejemplo, los colores se han quedado con muy poco contraste. Si queremos cambiar tanto el tono como la saturación o cualquier otro ajuste, seleccionamos la capa de la mancha que teníamos dentro del grupo y creamos una capa de ajuste.

También podemos, pinchando en la capa de la mancha y pulsando “Cmd+T” hacer la mancha más grande, más pequeña, rotarla, etc., viendo ya el resultado final.



7. Otros ejemplos

Utilizando esta misma técnica podemos obtener otros ejemplos como los siguientes:


Encripta las contraseñas de tu setting.xml con Maven

$
0
0
En este artículo se hará uso mediante una demostración de la opción para encriptar contraseña de nos facilita Maven.

Índice de contenidos

1. Introducción

La motivación de este tutorial surgió por la necesidad de compartir mi fichero settings.xml con un compañero. En este fichero settings aparecían mis credenciales, con las que accedía a ciertos servidores. La solución más sencilla hubiera sido borrarlas.
<servers>
		<server>
			<id>nexus</id>
			<!--Pon tus credenciales -->
			<username></username>
			<password></password>
		</server>
	</servers>
Pero pensé que habría alguna forma de encriptar mis contraseñas para poder compartir mi fichero settings.xml sin problemas y que encima me siguiera funcionando en mi repositorio local. Os intentaré explicar esta opción de Maven con una demostración provocando una situación similar a la que me hizo descubrirla.

2. Entorno

El tutorial está escrito usando el siguiente entorno:
  • Hardware: Portátil Pro 15′ (2,5 GHz Intel Core i7, 8 GB DDR3).
  • Sistema Operativo: Mac OS El Capitán 10.11
  • Entorno de desarrollo: Neon.2 Release (4.6.2)
  • Apache Maven 3.3.9
  • Docker Engine 1.12.5, build 7392c3b
  • Docker Machine version 0.8.2, build e18a919
  • Docker Kitematic 0.12.0

3. Preparando el entorno para la demo

Para el ejemplo vamos a utilizar la aplicación que describe Natalia Roales en su tutorial con algunas modificaciones. En nuestra aplicación no tendremos capa de persistencia y en vez de saludar, validaremos NIFs. Para la validación de NIFs vamos a hacer uso de una librería alojada en un repositorio Nexus local que dockerizaremos y configuraremos en nuestro settings.xml. El código de la aplicación lo podéis encontrar en mi github . !!! Empecemos !!!.

3.1. Aplicación Spring Boot para validar NIF

Nuestra aplicación tiene que validar si un DNI o NIE pasado en la url es correcto o no. Si es correcto, mostrará el siguiente mensaje Y si no lo es Si queréis generar NIEs o DNIs correctos para vuestras pruebas podéis hacer uso de la siguiente página. Para provocar casos incorrectos, bastará con cambiar la letra . Para que el código aplicación de Natalia cumpla con esta nueva funcionalidad,el servicio tiene que hacer uso de una librería para validar NIF. Como podemos ver en la siguiente imagen, nuestro proyecto no dispone de esta librería.



Usaremos docker para crear un repositorio local al que conectarnos y poder bajarnos la dependencia.

3.2. Dockerizando Nexus

Para dockerizar el Nexus sólo tenemos que ejecutar el siguiente comando:
$> docker run -d -p 8081:8081 --name nexus sonatype/nexus:oss
Esto nos bajará la imagen sonatype/nexus:oss y nos levantará nuestro Nexus en el puerto 8081. Si accedes a la siguiente url deberías ver lo que aparece en la siguiente imagen:



Si no estás muy familiarizado con Docker, te recomiendo que le eches un vistazo al tutorial de Jorge Pacheco.

3.3. Configurando nuestro Nexus en el settings.xml

Una vez levantado nuestro Nexus, debemos configurar nuestro setttings.xml (alojado en la ruta ${user.home}/.m2 de nuestro repositorio local). El settting apuntando a nuestro Nexus debería quedar de la siguiente forma:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">

	<mirrors>
	 <mirror>
	  <id>nexus</id>
	  <mirrorOf>*</mirrorOf>
	  <url>http://192.168.99.100:8081/nexus/content/groups/public</url>
	 </mirror>
    </mirrors>

	<profiles>
     <profile>
	  <id>nexus</id>
	  <repositories>
	   <repository>
	    <id>central</id>
	    <url>http://192.168.99.100:8081/nexus/content/repositories/central</url>
	    <releases>
		 <enabled>true</enabled>
	    </releases>
		<snapshots>
		 <enabled>true</enabled>
	    </snapshots>
	   </repository>
      </repositories>
	  <pluginRepositories>
	   <pluginRepository>
	    <id>central</id>
	    <url>http://192.168.99.100:8081/nexus/content/repositories/centrall</url>
	    <releases>
		 <enabled>true</enabled>
	    </releases>
		<snapshots>
		 <enabled>true</enabled>
	    </snapshots>
	   </pluginRepository>
      </pluginRepositories>
     </profile>
	</profiles>
	<activeProfiles>
	 <activeProfile>nexus</activeProfile>
	</activeProfiles>
	<servers>
	 <server>
	  <id>nexus</id>
	  <username>admin</username>
	  <password>admin123</password>
   	 </server>
	</servers>
   </settings>

3.4. Subiendo la dependencia al Nexus

Para subir la dependencia del validador de NIF a nuestro repositorio, nos bajamos los ficheros valnif-2.0.1.jar y valnif-2.0.1.pom de la siguiente url. Abrimos la terminal, nos posicionaremos en la ruta donde tengamos los ficheros descargados y ejecutamos el siguiente comando:
$> mvn deploy:deploy-file \
-DgroupId=com.aeat.valida \
-DartifactId=valnif \
-Dversion=2.0.1 \
-DgeneratePom=false \
-Dpackaging=jar \
-DrepositoryId=nexus \
-Durl=http://192.168.99.100:8081/nexus/content/repositories/releases/ \
-DpomFile=./valnif-2.01.pom \
-Dfile=./valnif-2.01.jar
Si todo ha ido bien, la salida esperada debería ser la siguiente:



3.5. Comprobando la conectividad con el nuestro repositorio

Para comprobar que tenemos conectividad con nuestro repositorio, sólo tenemos que actualizar  nuestro proyecto y ver que se descarga la dependencia.
$> mvn -U clean install
Al actualizar el proyecto, se debería haber descargado la dependencia de nuestro Nexus y haber desaparecido los errores. Con este último paso, estamos listo para empezar a encriptar nuestras contraseñas.

4. Encriptando nuestras contraseñas

En este apartado explicaremos todos los pasos necesarios para encriptar con Maven y comprobaremos que seguimos teniendo conectividad con la password encriptada.

4.1. Generar una clave maestra encriptada

El primer paso es generar una password maestra. La password maestra se utiliza como entrada en la función de encriptación / desencriptación de las password que encriptamos en nuestros servidores, de este modo, sólo una persona con la password maestra podrá realizar el proceso de desencriptado con éxito. Para generar esta password maestra tenemos que ejecutar el siguiente comando:
$> mvn --encrypt-master-password <password>
Si tienes la versión de Maven 3.2.1 o superior,no deberías pasarle tu password como argumento en el comando. A partir de la version 3.2.1 o superiores el parámetro password es opcional y si no lo pones, se solicitará al ejecutar, de la siguiente forma:



Se aconseja ejecutar el comando sin pasarle la password como parámetro,ya que si no, esta quedará registrada “en claro” en el historial, como podemos ver en esta imagen:



La salida tras ejecutar el comando para generar la password maestra es la siguiente:



Creamos un fichero llamado settings-security.xml dentro de nuestro repositorio local (${user.home}/.m2) con la password maestra anteriormente generada:
<settingsSecurity>
  <master>{jlgYBiSDIdsHs3N8neqZoEwN0+4WfwUzch2JDzm18fg=}</master>
</settingsSecurity>
El fichero settings-security.xml también lo podemos guardar dentro de una memoria USB y hacer referencia  a él desde nuestro settings-security.xml de nuestro repositorio local la siguiente manera:
<settingsSecurity>
  <master>/Volumes/{Nombre Unidad}/settings-security.xml</master>
</settingsSecurity>


4.2. Encriptar la password de nuestro Nexus

Ya solamente tenemos que encriptar la contraseña de nuestro Nexus (admin123), ejecutando el siguiente comando:
$> mvn --encrypt-password admin123
Generando la siguiente salida:



Ya sólo tenemos que cambiar nuestra contraseña “en claro” de nuestro server dentro del settings.xml por nuestra contraseña encriptada
<servers>
		<server>
			<id>nexus</id>
			<username>admin</username>
			<password>{e8Lcy5Mt1xUHQPNpoaWrqmz33lpE74ewBLzqDr/AGPQ=}</password>
		</server>
	</servers>


4.3. Configurando nuestro Nexus en el settings.xml

Como último paso, vamos a comprobar la conectividad con nuestro Nexus con la password encriptada. Para ello, borraremos la dependencia de validador de nuestro repositorio local y volvemos a actualizar el proyecto, obteniendo la siguiente salida:



Vemos que sigue descargándose la dependencia igual que antes pero con la password encriptada.

5. Conclusiones

Hemos podido comprobar que podemos encriptar nuestras contraseñas de manera fácil y sencilla.Y si lo hace Whatsapp con los mensajes, nosotros no vamos a ser menos con nuestras contraseñas.

6. Referencias

Capturar pantalla y retransmitir en directo con OBS (Open Broadcaster Software)

$
0
0

OBS, un programa de código abierto que te permite grabar o hacer retransmisiones en directo de lo que sucede en tu escritorio.

Índice de contenidos


1. Twitch

Twitch (también conocido como Twitch.tv) es una plataforma que ofrece un servicio de streaming de video en vivo. Este sitio principalmente se enfoca a los videojuegos. Los jugadores suben sus partidas para que otros usuarios puedan seguirlas en directo. Ya las nuevas generaciones no se contentan con seguir éstas partidas una vez están grabadas y subidas por los jugadores sino que hay cada vez más una tendencia al video on demand.

Pero no me voy a centrar en la comunidad gamer. Los propios jugadores conocen ya sus herramientas para crear contenido en la red. En el tutorial me voy a centrar en un programa Open Source llamado OBS (Open Broadcaster Software).


2. OBS

Yo no conocía OBS y el otro día fui a grabar una charla con la cámara y un trípode para hacer la edición posteriormente y me fijé que había un chico haciendo streaming y solamente tenía un ordenador y una webcam. Le pregunté de qué manera lo hacía y le hablé de un aparato que uso yo en las charlas llamado Vidiu Teradek.

Cuando me dijo que era un programa Open source gratis y que no era complicado, empecé a investigar por internet y a ver tutoriales y vi que efectivamente es muy sencillo de usar y pensé: seguro que hay gente como yo que no lo conoce y que alguna vez ha pensado de qué manera podía hacer una grabación de su pantalla y mostrarlo en directo.

Open Broadcaster Software permite captura de fuentes de video en tiempo real, composición de escena, codificación, grabación y retransmisión. La transmisión de datos puede realizarse a través del protocolo Real Time Messaging Protocol, y puede ser enviado a cualquier destino que soporte RTMP (por ejemplo Youtube), incluyendo muchos presets para sitios de streaming como Twitch y DailyMotion.2.

Para la codificación de video, OBS es capaz de utilizar el codec libre x264, y la aceleración por hardware de Intel (Quick Sync) AMD y Nvidia (NVENC) para codificar transmisiones de video en el formato H.264/MPEG-4 AVC.3. El audio puede ser codificado utilizando MP3 o AAC.


Qué necesitas para hacer streaming con OBS


  1. Un portátil o PC
  2. Una conexión estable. Con 0.7 mbps de subida a 1mbps ya sería suficiente para empezar pero si la conexión es buena pues mejor tener un poco más de ancho de banda. (Se puede ver la velocidad en Test de velocidad por ejemplo).


  3. Un micrófono. Mientras más calidad de audio mejor, pero no necesitas lo más caro.
  4. OPCIONAL: Una webcam. Y digo opcional porque por ejemplo alguien que quiera simplemente grabar sus partidas de juego y retransmitirlas no necesita cámara pero si queremos grabar una charla en una universidad, en el trabajo, grabarnos en casa y hacer streaming sí necesitaremos una.

Cómo empiezo a usar OBS


Ya seas de windows, de Mac o de Linux en la página web puedes descargar cualquiera de éstas versiones.



Nos vamos a la web de OBS y descargamos e instalamos. Viene con la versión de 32 y 64 bits. No hay mucha diferencia entre los dos, pero el de 64 es mejor. Usa el que le corresponda a tu sistema operativo: si tienes uno de 32 bits pues 32, si tienes de 64 pues ése. Al instalar OBS, puede que te soliciten instalar algunas cosas extra así que instala eso también.

Yo he descargado para el tutorial la versión de MacOS

Abrimos el programa y creamos un perfil nuevo

Vamos a configuración:

En las siguientes opciones seleccionamos las que necesitemos. Es conveniente no seleccionar la casilla de “ocultar el cursor sobre proyectores” pues cuando estemos haciendo streaming de nuestra pantalla no saldría nuestro cursor



En la pestaña de Emisión podremos elegir dónde queremos el servicio, si en twitch, youtube, dailymotion…

Si elegís twitch, en el menú desplegable de “servidor” tendréis que elegir el que os pille más cerca de donde os encontréis. Si os encontráis en Madrid como yo, el que mejor os vendría sería EU: Paris, FR. Existen programitas que hacen ping a todos los servidores y puedes saber cuál te dará mejor resultados porque será el que menor tiempo de respuesta te de.

Find your best twitch tv server

En esa página web podemos descargarlo

Si elegís Youtube como servidor dejáis el servidor que viene por defecto. Pero en este caso tendréis que meter la Clave de transmisión. Para ello necesitaréis crear un evento en directo en vuestro canal:




Rellenáis el título y la descripción, en la pestaña de configuración avanzada yo suelo dejarlo tal y como viene: Creamos el evento. Dentro esta vez de “configuración de la ingestión” elegimos el tipo de ingestión.

La frecuencia de bits que tenemos que elegir irá en función de la velocidad de subida que tengamos. Yo voy a elegir la opción de 720p.

Si vas empezando, lo ideal es como entre 700 y 1200 de bitrate. Eso da como para 480p más o menos. Lo sé, 480p suena como poco y muchos queremos empezar con 720 o hasta pensamos en rendirnos si no se puede. 480p no se verá perfecto, pero casi cualquiera podrá verlo y si lo configuramos bien se verá bastante aceptable. Como mínimo te aseguro que 480p bien configurado se ve mejor que un 720p muy forzado



Se nos desplegará un menú para seleccionar nuestro codificador. De las tres barras que nos aparecen con una dirección debemos copiar la que dice “nombre de la emisión” y pegar el código en OBS donde pone Clave de la transmisión.


En la pestaña de Salida de OBS podemos definir el bitrate de vídeo, el codificador y el bitrate de audio así como la ruta donde queremos que se guarde la grabación, su calidad y el formato (.mov, .mp4, .flv…)

Para opciones más avanzadas pinchamos en Modo de salida: avanzado



Ahora tenemos que crear escenas y agregar las distintas fuentes que queramos:



No voy a poner ejemplos de partidas en algún juego puesto que no soy gamer. Como quiero enfocar el tutorial más para ponentes que quieran grabarse la pantalla voy a usar de ejemplos capturas de pantalla con presentaciones. En el cuadro de Fuentes agregamos Captura de pantalla.



El ejemplo de la foto no es del todo verdadero puesto que no es una grabación en tiempo real, es un vídeo que se grabó a parte con una cámara profesional pero lo he puesto para que veáis cómo podemos componer en OBS. Lo agregué en Fuentes > agregar Fuente multimedia. Si queremos retransmitir una charla con una presentación o donde vayamos a enseñar código en directo y queremos componer nuestro canvas poniendo el logo de nuestra empresa o algún logo que queramos usar podemos hacerlo. Para tapar el fondo de escritorio nos podríamos preparar unas imágenes en algún programa tipo Photoshop o similar y las meteríamos como Imagen. En el ejemplo de abajo se puede ver.

Para iniciar la transmisión en youtube tenemos que pinchar a la derecha donde dice “Iniciar Transmisión” y esperar a que en la página de youtube en Sala de control en directo lo detecte y lo inicie.

En la foto inferior se ve cómo entra el audio tanto de la fuente multimedia como por el micro del ordenador. Si tenéis los ajustes bien, cualquier fuente de sonido que tengáis debería detectárosla.


4. Conclusiones

Como veis es un programa muy sencillo que permite agregar muchas fuentes según lo que necesitemos o tengamos conectado. Y es una buena herramienta para cuando queremos ir más allá a la hora de mostrar algo de nuestra pantalla porque tenemos mucha libertad para componer el canvas y elegir fuentes.

Entender el vectorscopio, monitor forma de onda y el histograma para la corrección de color

$
0
0

Entender el vectorscopio, el monitor forma de onda y el histograma para Corrección de color.

Índice de contenidos


1. Introducción

En video existen dos herramientas fundamentales a la hora de inspeccionar una señal, se trata del vectorscopio y el monitor forma de onda.

A simple vista pueden parecer sólo una maraña de líneas sin sentido, pero veremos que en realidad es bastante fácil interpretarlos y son de bastante utilidad.

La señal de video compuesto está formada por dos elementos: por un lado la señal de luminancia o luma, la cual aporta lo que podríamos visualizar como una imagen en escala de grises o distintas densidades, y por otro lado la señal de crominancia o chroma que es la que aporta el color a la imagen.

  1. Vectorscopio: informa sobre el balance de blancos y las dominantes de color. Sirve para evaluar los tonos de color presentes en la imagen.
  2. Forma de onda: informa sobre los niveles de luminancia de la imagen. Sirve para evaluar el contraste general de la imagen.
  3. Histograma: es otra manera de visualizar la distribución de luces y sombras. También sirve para evaluar el brillo y el contraste.
  4. RGB parade: igual que forma de onda pero separando los 3 componentes RGB.

2. Vectorscopio

El vectorscopio es el instrumento encargado de medir y representar la señal de crominancia exclusivamente, ya que la luminancia no aporta información de color, por lo que una escala de grises no tendrá representación alguna en nuestro vectorscopio.

Otro parámetro que controlamos con el vectorscopio es que la señal de vídeo se sitúa correctamente entre los valores de 0 y 100 marcados con círculos concéntricos. Si el vector sobresale de los márgenes tendremos una señal fuera de norma que es preciso corregir, y meterla en “norma”.

Una manera fácil de hacerlo es simplemente aplicando un filtro que Final Cut trae por defecto: Broadcast Safe (retransmisión segura). Este filtro convierte nuestra imagen en “legal” para emisión, evitando colores demasiado saturados y luminancias al margen del 100%.

La señal de crominancia se organiza en dos subportadoras, por una parte el chroma (conocido coloquialmente como saturación), codificada como la amplitud y por otra parte el tono codificado en la fase.

Los valores que representa el vectorscopio se leen o interpretan de la siguiente manera:

[R] corresponde a Rojo, [Mg] corresponde a Magenta, [B] corresponde a Azul, [Cy] corresponde a Cyan, [G] corresponde a verde y [Yl] que corresponde al color amarillo. Cada punto en el vectorscopio será entonces un color individual de la imagen que en determinado momento tengas a la vista en tu línea de tiempo.

La línea recta que apunta hacia aproximadamente las 11:00 si fuera un reloj, marca la región donde se localiza el color de la piel humana, independientemente de la raza.

La distancia que separa un punto de los antes mencionados, del centro del vectorscopio, representa su saturación, (cuanto más cercanos estén los vectores al centro menor saturación, y cuanto más alejados, más saturados) y el ángulo de la línea que va desde el punto al centro del vectorscopio representa su tono o también llamado matiz en algunos sitios.

En la foto de arriba vemos un ejemplo de una foto saturada. La distribución de los puntos en el vectorscopio se concentra hacia la zona correspondiente a los colores azul [B] y verde [G] de la rueda de colores.

Si quieres trabajar en la corrección de color o etalonaje debes usar el vectorscopio para calibrar el color de tus planos entre una escena y otra. De lo contrario, es muy probable que al visualizar las secuencias puedas encontrarte con diferencias, que a veces son pequeñas, pero si quieres tener o entregar un trabajo de la forma más profesional posible hay que tener éstas cosas en cuenta.

Un ejercicio recomendable es situar una imagen con diferentes tonos bien definidos (cielo, cesped, rostros, etc) y con la herramienta Crop ir recortando la imagen para aislar esos objetos de diferentes colores. Así los identificarás rápidamente en el Vectorscopio. Igual de sano es poner unas barras de calibración, verás que te coinciden con esos targets.

Ejemplos con otras fotos: (la fotos están sacadas de Pixabay Todas las imágenes y videos en Pixabay son publicadas libres de derechos de autor bajo la licencia Creative Commons CC0. Puedes descargarlas, modificarlas, distribuirlas y usarlas libres de pago para cualquier uso, aún para aplicaciones comerciales. No es necesaria atribución)

Vemos en el vectorscopio como la información de color se concentra en la zona de la piel humana

Aquí nos indica las dominantes azul y verde en la imagen Hay una gran saturación de los amarillos y naranjas. Recordamos que cuanto más alejado estaba el vector del centro, mayor saturación tenía.

3. Monitor forma de onda

Monitor forma de onda es un instrumento de medida utilizado en televisión para ver y medir la señal de vídeo.

El eje horizontal del gráfico corresponde a la imagen de vídeo (de izquierda a derecha) y el eje vertical es la intensidad de la señal en unidades llamadas IRE (siglas que provienen del Institute of Radio Engineers). Si sacais el monitor forma de onda en Final cut y le dais a la rueda de ajustes veréis que las unidades que vienen son IRE y milivoltios.

Un monitor forma de onda nos permite ver en tiempo real como cambian los valores. Es fundamental para exponer correctamente. Sirve también para calibrar distintos equipos y para asistir al etalonaje en la postproduccion.

Puedes especificar si la forma de onda YC muestra tanto la información de luminancia como de crominancia, o solo la información de luminancia.

Qué debemos saber para manejarlo:

  • espacio de color en el que vamos a trabajar
  • entender cómo funcionan las curvas gamma y qué es el rango dinámico

4.Rango dinámico

Para entender el concepto de gamma aplicado al proceso de imágenes, hay que entender primero los conceptos de “lineal”, “logarítimico” y “rango dinámico”. Si bien la explicación de éstos conceptos nos llevaría al menos un extenso artículo para cada uno, sí haremos aquí un pequeño acercamiento.

El rango dinámico de una imagen sería la escala que hay entre la máxima luminosidad y el valor más oscuro. El nivel de detalle. Si hacemos una escala de medidas entre una superficie negra que refleje solo un 1% de luz y por ejemplo una superficie blanca que refleje el 90%, el ojo humano, se mueve entre ese 1% – 90%, que es donde percibe casi todos los valores de contraste y detalle, por encima de ese 90% y hasta llegar al 100% hablaríamos de superblanco. El ojo humano no percibe detalle en ellos, sólo una luminosidad cegadora. Por tanto se puede decir que el ojo humano tiene una respuesta NO LINEAL respecto a la luminosidad del mundo real.

A medida que aumenta la luminosidad, la percepción de tal luminosidad por parte del ojo NO es proporcional. Cuando exponemos nuestro ojo a una luz determinada el ojo tiene una sensación de luz y si duplicamos la cantidad de luz, no tenemos la sensación de que haya el doble de luz, notamos simplemente un incremento en la luminosidad, pero no que sea el doble de luz. Matemáticamente esto tiene una forma de expresarse: una función logarítmica.

La GAMMA es un valor numérico que aporta la valiosa información para saber cuánto se oscurecerá o brillará una imagen al ser reproducida por un dispositivo.

Por eso es necesario introducir una CORRECIÓN DE GAMMA que evite la alteración de la visualización de la imagen y que equilibre dispositivos con distinta gamma, pues dependiendo del entorno donde estemos visualizando la imagen y de la calibración del dispositivo, veremos la imagen de una forma u otra.

Las cámaras pueden utilizar tres tipos de curvas de gamma:

  • Curva de gamma lineal: representa los valores de brillo que captura un sensor antes de que se haga ningún procesamiento. Una imagen con gama lineal es una imagen no acabada y que necesita de una corrección de color o LUT para verse correctamente.
  • Gamma logarítmica: Es una curva gamma que optimiza el rango dinámico. Genera una imagen más lavada que requerirá un proceso posterior de corrección de color. La curva de gama logarítmica representa valores de brillo de forma perceptual (no real).
  • Con las curvas de gamma logarítmicas se obtienen imágenes de mayor calidad ya que se evita quemar las altas luces y empastar las sombras como hacen habitualmente las cámaras que registran con rangos dinámicos inferiores. Pero es necesario un proceso de postproducción y corrección de color hasta alcanzar el aspecto definitivo de las imágenes

  • Gamma corregida: Genera un contraste adecuado para la visualización final. Se consigue menos rango dinámico pero el material sale de la cámara con un look más definitivo. Se utiliza en producciones donde no está prevista la corrección de color en postproducción: televisión de flujo, reportajes informativos, directos, etc.
  • Cada fabricante de cámaras tiene su propia curva de gamma log y la correspondiente LUT para hacer la conversión a BT-709 (estandariza el formato de televisión HD en formato panorámico 16:9, utiliza los colores imaginarios, colores artificiales, falsos o ficticios.).

¿Cómo interpretar un monitor forma de onda?

La unidad de medida más habitual es como he dicho más arriba el IRE: 1 voltio dividido en 140 unidades (-40 a 0 tenemos la señal infranegro, no hay información visual, son los negros empastados y la información de sincro), (o a 100 información visual)

Medimos la señal desde el negro absoluto hasta el blanco absoluto. Nunca deberíamos llegar a los 100 porque si no tendríamos los blancos quemados.

El software de etalonaje mide el color en valores RGB (de 0 a 1023 en un espacio de 10 bits) la equivalencia con el rango leal de broadcast medido en unidades IRE es 0=64 y 100=940 (gris medio 440). Para exponer correctamente debemos hacerlo a partir de un gris medio (aquí entra la famosa carta de grises).


4. Histograma

Por su parte el histograma, similar al usado en fotografía, es otra manera útil de evaluar el contraste.

Un histograma es una gráfica de barras utilizada en estadística que nos muestra la frecuencia con la que se repiten determinados valores.

La distribución de las barras nos indica si la imagen está demasiado iluminada o incluso quemada o por el contrario está oscura. A la derecha del histograma se sitúan luces y brillos y a la izquierda los oscuros.

Un histograma sin información en la zona de sombras ni en la zona media, que aglutina toda la información en la zona derecha de la imagen (luces), es muy probable que esté sobreexpuesta.

Un histograma que acumule la información en la zona de sombras (izquierda), con poca o ninguna información en la zona media y la zona de luces, es muy probable que sea demasiado oscura; subexpuesta

Un histograma con toda la información acumulada en una misma zona, nos habla de una imagen con poco contraste. Tanto si la información lumínica se concentra a la derecha, a la izquierda, o en la zona media. Ya sabes que el contraste lumínico se da por la diferencia entre luces y sombras en una imagen, por lo que si ésta carece de diferenciación entre luces y sombras, la imagen resultante se conoce como una imagen de bajo contraste.


5. RGB Parade

Esta herramienta muestra formas de onda que representan los niveles de los canales rojo, verde y azul de un clip.

Las formas de onda aparecen en un gráfico de forma consecutiva, resulta útil para ver la distribución de los componentes de color de un clip. Los niveles de cada canal de color se miden proporcionalmente entre sí mediante una escala de 0 a 100.

Los parades RGB son, en mi opinión, la herramienta más útil para detectar dominantes de color. Los blancos, grises y negros se caracterizan por tener la misma cantidad de rojo, verde y azul. Por tanto, las alturas de parade deberían ser similares en esas zonas superior, de tonos altos, e inferior, en la de sombras. Si un color determinado está más alto que otro en esas zonas eso quiere decir que hay un color dominante.


6. Conclusiones

Aunque las herramientas de análisis de imágenes son de utilidad con cualquier clip de vídeo aislado, la verdadera importancia de las herramientas que acabamos de analizar aparece a la hora de igualar clips de vídeo grabados en diferentes situaciones de iluminación o con videocámaras diferentes. Comparar los histogramas, parades y vectorscopios resulta clave para que dos planos consecutivos tengan las mismas cualidades visuales y no haya “saltos” de tonos que sobresalten al espectador.

Con los histogramas podremos comprobar que todos los planos están iluminados de forma similar, con los parades igualaremos el tono y, finalmente, con el vectorscopio podremos comprobar que la saturación de todos los planos de la escena es similar.

Aunque podríamos hacer las tareas de igualación de planos sin usar estas herramientas, el ojo, cuando lleva un rato analizando colores, se cansa y cuanto más tiempo pasemos corrigiendo color más difícil encontraremos visualizar los sutiles matices entre un plano y otro.

Éstas herramientas objetivas serán siempre unas excelentes aliadas.

Wiremock: plantillas dinámicas

$
0
0

En este tutorial vamos a ver cómo podemos usar las extensiones de Wiremock para poder generar un servidor de Mocks con respuestas dinámicas.

Índice de contenidos


1. Introducción

En un tutorial anterior vimos cómo podemos usar Wiremock standalone para poder crear un servidor de mocks de los servicios a los que nos conectamos. Así, podremos aislarnos del estado de los servicios, y basar nuestro desarrollo en una especie de contrato.

Wiremock, básicamente toma un patrón de request de entrada y proporciona una respuesta que normalmente viene expresada en forma de fichero de texto estático que hemos asignado. Pero en ocasiones esto no es suficiente: es posible que queramos dar algo más de sentido a la respuesta, y en vez de tener un carácter estático, que dependa de la request o que se genere a base de fragmentos que se multiplican dentro de un bucle.

Para ello Wiremock cuenta con un mecanismo de extensión para poder crear nuestro propio sistema de salida o usar otros ya existentes. En este tutorial veremos cómo usar las extensiones de Handlebars y de las conocidas plantillas de Apache Velocity.

2. Las extensiones de Wiremock

En ocasiones es necesario que la respuesta obtenida en Wiremock tenga cierto dinamismo para simular las condiciones reales. No estamos hablando de implementar la lógica del servidor al cual sustituímos, pero sí de cambiar un poco la respuesta. Imaginemos casos como:

  • Cada respuesta tiene un identificador único.
  • Una parte de la respuesta reproduce algún parámetro de la query.
  • La respuesta tiene que contar con timeStamps… Al final lo que queremos hacer es imitar las características mínimas que necesita nuestro servicio de la parte servidora.

Wiremock no incluye capacidades para poder realizar este tipo de salidas, pero sí que cuenta con mecanismos de extensión para que puedas crear tus propios plugins y así poder personalizar su comportamiento para casos especiales. Podrás hacer cosas como:

  • Tratamiento de parámetros.
  • Manipulación total de la respuesta.
  • Matchers de las request personalizados.
  • O escuchar tráfico HTTP raw.

Puedes consultar los primeros detalles de la extensión de Wiremock en su documentación: http://wiremock.org/docs/extending-wiremock/

Pero para devolver respuestas dinámicas ya tenemos dos extensiones que se encargan de ello, así que podemos ahorrarnos parte del trabajo:

  • Las plantillas con Handlebars.
  • La extensión para usar Apache Velocity.

3. Plantillas con Handlebars

Handlebars es una librería de plantillas en Javascript que Wiremock incluye de forma nativa para poder crear respuestas dinámicas a partir de la información de la request.

En la documentación oficial de Wiremock puedes ver la sintáxis para las operaciones con la información de la request: http://wiremock.org/docs/response-templating/

¿Cómo lo podemos usar?

En el directorio maps de la versión Wiremock standalone podemos crear o editar un fichero .json del mapping, añadiendo el atributo "transformers" en la respuesta con el valor adecuado:

{
    "request": {
        "method": "GET",
        "url": "/api/handlebars"
    },
    "response": {
        "status": 200,
        "bodyFileName": "templateHandlebars.vm",
        "transformers": ["response-template"]
    }
}

Y en el fichero de template podemos incluir la expresión de Handlebars:

<html>
  <body>
    <p>The first part of the path was: {{request.path.[0]}}. </p>
  </body>
</html>

En la ejecución del servidor debemos usar el parámetro –global-response-templating para que se active el procesamiento de Handlebars:

java -jar wiremock-standalone-2.5.1.jar --global-response-templating

Con esto devolveremos una respuesta como esta cuando un cliente acceda a http://localhost:8080/api/handlebars

The first part of the path was:api

Donde "api" es el primer elemento del path con el que nos han llamado.

Si quieres recoger un parámetro puedes cambiar la plantilla y ampliar:

<html>
  <body>
    <p>The first part of the path was: {{request.path.[0]}}. </p>
    <p>And the value of the &quot;page&quot; parameter was: {{request.query.page}}</p>
  </body>
</html>

Ahora podemos probar a llamar con esta URL: http://localhost:8080/api/handlebars?page=2

Supongo que no te habrá funcionado porque en el mapping tienes que cambiar el matching de url por el matching de urlPath:

"request": {
    "method": "GET",
    "urlPath": "/api/handlebars"
}

Así, podrá atender cualquier petición a ese path, con independencia de los parámetros. El resultado:

And the value of the "page" parameter was: 2

Puedes acceder a más elementos de la petición, como las cabeceras, cookies o el propio body de un POST.

4. Usando la extension para Apache Velocity

Hasta ahora hemos visto el sistema que incorpora Wiremock por defecto: handlebars, pero sólo nos deja acceder a información de las peticiones. En algunos casos nos puede valer, pero en otros quizá querramos más procesamiento.

Wiremock cuenta con la posibilidad de crear extensiones en Java que permiten manipular las respuestas en diferentes momentos del ciclo de vida. Puedes encontrar más información en: http://wiremock.org/docs/extending-wiremock/

Lo que vamos a hacer ahora es aprovechar el trabajo de Adam York, que ha creado una extensión de Wiremock para poder usar plantillas de Apache Velocity en las respuestas, con toda la potencia que esto conlleva. (https://github.com/adamyork/wiremock-velocity-transformer)

En primer lugar podemos descargar la última versión de la librería en su repositorio de GitHub: https://github.com/adamyork/wiremock-velocity-transformer/releases en el caso de este tutorial era la 1.4. Está en dos formas: extensión para usarla si ya tenemos wiremock -wiremock-velocity-transformer-1.4.jar- y versión standalon que no necesita de nada más (wiremock-velocity-transformer-standalone-1.4.jar). Me decantaré por la segunda.

¿Cómo lo usamos?

Muy sencillo: creamos un mapping

{
    "request": {
        "method": "GET",
        "url": "/api/velocity"
    },
    "response": {
        "status": 200,
        "bodyFileName": "templatevm.vm",
        "transformers": ["body-transformer", "response-template"]
    }
}

… y creamos una plantilla con expresiones de Velocity.

<html>
  <body>
  #set( $foo = &quot;Velocity&quot; )
  #set ($random = $math.getRandom())
  Hello $foo this is a random number: $random!
  </body>
</html>

Ejecutamos la versión standalon que nos acabamos de bajar y que lleva Wiremock standalone en su interior:

Y el resultado es el esperado:

Hello Velocity this is a random number: 0.05027841145154954!

Esto es, utiliza la variable "foo" cuyo contenido es "Velocity" y muestra un número aletorio. A partir de aquí eres libre de tunear la respuesta con las expresiones de Velocity que creas conveniente.

5. Combinando HandleBars y Velocity

Hasta la fecha de este tutorial, la versión de Wiremock con Veloctiy contiene en su interior una versión de Wiremock que todavía no tenía activa la funcionalidad de plantillas de Handlebars. Pero no es problema, podemos sobreescribir las clases de Wiremock gracias al sistema de classloaders de Java.

En primer lugar preparamos el mapping:

{
    "request": {
        "method": "GET",
        "url": "/api/velocity"
    },
    "response": {
        "status": 200,
        "bodyFileName": "templatevm.vm",
        "transformers": ["body-transformer", "response-template"]
    }
}

Y preparamos la plantilla:

<html>
  <body>
  #set( $foo = &quot;Velocity&quot; )
  #set ($random = $math.getRandom())
  Hello $foo this is a random number: $random!
  And the first parameter of the path was: {{request.path.[0]}}
  </body>
</html>

Para ejecutarlo esta vez necestaremos de la ayuda del parámetro -cp de Java para explicitar el ClassPath. Esto nos obligará a expecificar la clase de entrada. También hace falta pasar por parámetro la clase de la extensión de Wiremock para Velocity de Adam York:

java -cp "wiremock-standalone-2.5.0.jar:wiremock-velocity-transformer-standalone-1.4.jar" com.github.tomakehurst.wiremock.standalone.WireMockServerRunner --global-response-templating --extensions com.github.adamyork.wiremock.transformer.VelocityResponseTransformer --verbose

El resultado es la combinación de ambos métodos:

Hello Velocity this is a random number: 0.6999034253976487!
And the first parameter of the path was: api

He dejado el código de este tutorial en este repo de GitHub: https://github.com/4lberto/wiremock-demo-vel-handlebars

6. Conclusiones

En este tutorial hemos visto cómo podemos, de una forma sencilla, aprovechar dos extensiones de Wiremock para poder dotar de dinamismo a las respuestas que programamos, y así poder dar un uso más amplio a este servidor de Mocks.


Análisis estático de código con Checkstyle

$
0
0

Hoy día la calidad de nuestro código no debería ser un factor negociable, y gracias a Checkstyle veremos que es muy fácil mantener el control sobre el cumplimiento de unos mínimos exigibles.

Índice de contenidos

Checkstyle Logo

1. Introducción

La calidad del código depende, entre otros factores, de los defectos que se introducen en el mismo durante las fases de desarrollo o mantenimiento.

Para mantener o aumentar la calidad de nuestro código debemos ayudarnos, entre otras herramientas, de técnicas de análisis estáticos de código que, básicamente, se encargan de buscar defectos en el código sin necesidad de que este se ejecute.

El catálogo de estas técnicas es amplio, algunos ejemplos pueden ser: Análisis de valores extremos, análisis del flujo de control y de datos, reglas de codificación, reglas de seguridad o análisis de variable viva, entre otros muchos.

Por suerte, actualmente existen múltiples herramientas que mantienen implementaciones de estas técnicas para evaluar de manera automática el código implementado en distintos lenguajes.

JavaScript:

  • JSLint
  • JSHint

Objective-C:

  • Clang

Java:

  • Checkstyle
  • FindBugs
  • PMD

En este tutorial vamos a centrarnos en Checkstyle, herramienta OpenSource distribuida bajo licencia GNU LGPL. Veremos cómo realizar análisis, cómo configurar su alcance y, finalmente, cómo extender sus comprobaciones mediante reglas propias.

¡Al lío!


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.5 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: macOS Sierra 10.12.3.
  • Entorno de desarrollo: IntelliJ IDEA ULTIMATE 2016.3
  • Apache Maven 3.3.0.
  • JDK 1.8.0_111
  • Checkstyle 7.1.2

3. Ejecución y Configuración de Checkstyle

Por lo general, la ejecución de checkstyle se realizará desde otra de las muchas herramientas para las que se mantienen plugins de integración, actualmente: Eclipse, IntelliJ, NetBeans, Maven o Gradle, SonarQube ó Jenkins, sin embargo, en este momento nos vamos a centrar en la ejecución manual del análisis.

Lo primero que debemos realizar para poder proceder con el análisis es descargar la última release de Checkstyle desde sourceForge ó de su versión en desarrollo desde github.

Para proceder con la ejecución basta con hacer uso del fichero JAR descargado como se muestra a continuación:

$ java -jar checkstyle-7.5.1-all.jar -c /sun_checks.xml MyClass.java

Con la opción -c establecemos el fichero de configuración que delimitará el alcance de nuestro análisis.

Por defecto, checkstyle nos ofrece 2 ficheros de configuración que podremos utilizar:

  • sun_checks.xml: Comprobará si nuestro código sigue un conjunto de estándares y buenas prácticas entre las que se incluyen las convenciones de Sun ó el JLS (Java Language Specification).
  • google_checks.xml: Comprobará si nuestro código sigue los estándares de Google para proyectos java (Pueden consultarse aquí).

Cualquiera de estas dos configuraciones nos servirá para conocer el estado de nuestro código a grandes rasgos y sin entrar en condiciones particulares.

En cualquier caso siempre podemos generar nuestros propios ficheros de configuración delimitando el alcance de los análisis, basta con seguir unas pautas como se puede ver aquí.

El resultado de la ejecución consistirá en un volcado por pantalla del total de las violaciones de las reglas que checkstyle haya detectado en su análisis, como se puede ver en el ejemplo a continuación:

$ java -jar checkstyle-7.5.1-all.jar com.puppycrawl.tools.checkstyle.Main -c /sun_checks.xml ~/IdeaProjects/Calculadora/src/
Starting audit...
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:0: Falta el archivo package-info.java. [JavadocPackage]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:3: Falta el comentario Javadoc. [JavadocType]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:4:1: '{' en la columna 1 debería estar en la línea anterior. [LeftCurly]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:5:5: Clase 'Calculadora' se parece a diseñada para la extensión (puede ser una subclase), pero el método 'suma' no tiene javadoc que explica cómo hacerlo de forma segura. Si la clase no está diseñado para la extensión de considerar la posibilidad de la clase 'Calculadora' final o haciendo el método 'suma' anotación  static/final/abstract/empty, o la adición permitido para el método. [DesignForExtension]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:5:5: Falta el comentario Javadoc. [JavadocMethod]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:5:21: El parámetro operando1 debería ser final. [FinalParameters]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:5:36: El parámetro operando2 debería ser final. [FinalParameters]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:5:50: '{' no está precedido de espacio en blanco. [WhitespaceAround]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:9:5: Clase 'Calculadora' se parece a diseñada para la extensión (puede ser una subclase), pero el método 'resta' no tiene javadoc que explica cómo hacerlo de forma segura. Si la clase no está diseñado para la extensión de considerar la posibilidad de la clase 'Calculadora' final o haciendo el método 'resta' anotación  static/final/abstract/empty, o la adición permitido para el método. [DesignForExtension]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:9:5: Falta el comentario Javadoc. [JavadocMethod]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:9:22: El parámetro operando1 debería ser final. [FinalParameters]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:9:37: El parámetro operando2 debería ser final. [FinalParameters]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:9:51: '{' no está precedido de espacio en blanco. [WhitespaceAround]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:13:5: Clase 'Calculadora' se parece a diseñada para la extensión (puede ser una subclase), pero el método 'multiplica' no tiene javadoc que explica cómo hacerlo de forma segura. Si la clase no está diseñado para la extensión de considerar la posibilidad de la clase 'Calculadora' final o haciendo el método 'multiplica' anotación  static/final/abstract/empty, o la adición permitido para el método. [DesignForExtension]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:13:5: Falta el comentario Javadoc. [JavadocMethod]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:13:27: El parámetro operando1 debería ser final. [FinalParameters]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:13:42: El parámetro operando2 debería ser final. [FinalParameters]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/main/java/com/ejemplos/Calculadora.java:13:56: '{' no está precedido de espacio en blanco. [WhitespaceAround]
Audit done.
Checkstyle ends with 18 errors.

4. Estructura y funcionamiento

Las funcionalidades incluidas en Checksyle se encuentran implementadas en módulos formando una estructura de árbol. Los módulos más cercanos a la raíz, el módulo “núcleo” de Checkstyle, implementan la interfaz FileSetCheck que, básicamente, toma un conjunto de ficheros de entrada y como resultado lanza mensajes de error.

Checkstyle ofrece una serie de implementaciones por defecto de FileSetCheck:

  • AbstractFileSetCheck.
  • AbstractHeaderCheck.
  • FileLengthCheck.
  • FileTabCharacterCheck.
  • HeaderCheck.
  • JavaPackageCheck.
  • RegexpHeaderCheck.

Nos vamos a centrar en la implementación TreeWalker, que ese encarga de transformar cada fichero de entrada Java en un árbol sintáctico (AST) y obtener el resultado de aplicar secuencialmente validaciones en forma de Checks (implementaciones de la clase abstracta AbstractCheck).

Es decir, TreeWalker se dedica a recorrer en profundidad (depth-first) el árbol AST, llamando a los Checks correspondientes según los siguientes pasos:

  1. Antes de que se ejecute ningún Check, TreeWalker permite realizar inicializaciones a través del método beginTree().
  2. A continuación y recursivamente irá descendiendo por cada nodo desde el nodo raíz hasta los nodos hojas, llamando al método visitToken() de cada Check para comprobar las validaciones correspondientes.
  3. Cuando un sub-árbol haya sido evaluado se lanzará el método leaveToken() dejando constancia de que la sub-estructura ya ha sido recorrida.
  4. Finalmente, cuando se vuelva al nodo raíz para finalizar el recorrido se lanzará el método finishTree().

A continuación veremos cómo, mediante la implementación de la clase abstracta AbstractCheck, podemos implementar el método visitToken donde implementaremos la lógica de nuestras reglas de validación.


5. Desarrollar nuestras propias reglas

Como en otras ocasiones, vamos a proceder con la generación de un proyecto basado en maven, donde vamos a definir las dependencias de nuestro proyecto. El fichero pom quedará como el que se puede ver a continuación:



    4.0.0

    com.adictosaltrabajo.tutoriales.checks
    checkstyle-checks
    1.0-SNAPSHOT

    
        
            com.puppycrawl.tools
            checkstyle
            7.1.2
        

        
        
            org.hamcrest
            hamcrest-all
            1.3
            test
        
        
            org.mockito
            mockito-all
            test
            1.10.19
        
        
            junit
            junit
            test
            4.12
        
    

A continuación, vamos a proceder con la implementación de una regla de ejemplo mediante TDD para lo que necesitamos poder probar el Check que vamos a desarrollar.

Para esto último vamos a hacer uso de 3 clases creadas por el equipo de desarrollo de Checkstyle y que se puede encontrar en su repositorio de github, en la sección de test, a saber: com.puppycrawl.tools.checkstyle.BaseCheckTestSupport, com.puppycrawl.tools.checkstyle.AuditEventUtFormatter y com.puppycrawl.tools.checkstyle.BriefUtLogger.

Haremos que nuestra clase de pruebas extienda la clase BaseCheckTestSupport y estableceremos los diferentes Tests. A continuación se muestra la clase de test.

package com.adictosaltrabajo.tutoriales.checks;

import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
import org.junit.Test;

import java.io.File;
import java.io.IOException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

public class MyExampleCheckTest extends BaseCheckTestSupport {

    public static final String MAX_LENGTH_TO_ERROR_TEST = "15";
    public static final String MAX_LENGTH_TO_NON_ERROR_TEST = "50";

    @Override
    protected String getPath(String filename) throws IOException {
        return super.getPath("checks" + File.separator
                + "myExampleCheck" + File.separator + filename);
    }

    @Test
    public void getRequiredTokensShouldReturnEmptyArray() {
        final MyExampleCheck checkObj = new MyExampleCheck();
        assertThat(checkObj.getRequiredTokens(), is(CommonUtils.EMPTY_INT_ARRAY));
    }

    @Test
    public void getAcceptableTokensShouldReturnMethodDefTokenArray() {
        final MyExampleCheck checkObj = new MyExampleCheck();
        final int[] actual = checkObj.getAcceptableTokens();
        final int[] expected = {
            TokenTypes.METHOD_DEF
        };

        assertThat(actual, is(expected));
    }

    @Test
    public void validationShouldReturnMultipleErrorsWhenMethodNameLengthExceedTheMaximum() throws Exception {
        final DefaultConfiguration checkConfiguration = createCheckConfig(MyExampleCheck.class);
        checkConfiguration.addAttribute("maxLength", MAX_LENGTH_TO_ERROR_TEST);
        final String[] expected = {
                "37: the method name is too long, only "
                  + MAX_LENGTH_TO_ERROR_TEST + " method name length are allowed",
                "42: the method name is too long, only "
                  + MAX_LENGTH_TO_ERROR_TEST + " method name length are allowed",
                "51: the method name is too long, only "
                  + MAX_LENGTH_TO_ERROR_TEST + " method name length are allowed",
                "61: the method name is too long, only "
                  + MAX_LENGTH_TO_ERROR_TEST + " method name length are allowed"
        };
        verify(checkConfiguration, getPath("RestControllerHandler.java"), expected);
    }

    @Test
    public void validationShouldReturnNonErrorsWhenMethodNameLengthDoNotExceedTheMaximum() throws Exception {
        final DefaultConfiguration checkConfiguration = createCheckConfig(MyExampleCheck.class);
        checkConfiguration.addAttribute("maxLength", MAX_LENGTH_TO_NON_ERROR_TEST);
        final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
        verify(checkConfiguration, getPath("RestControllerHandler.java"), expected);
    }

    @Test
    public void validationShouldReturnNonErrorsWhenClassHasNoMethods() throws Exception {
        final DefaultConfiguration checkConfiguration = createCheckConfig(MyExampleCheck.class);
        checkConfiguration.addAttribute("maxLength", MAX_LENGTH_TO_ERROR_TEST);
        final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
        verify(checkConfiguration, getPath("OtherClassToTest.java"), expected);
    }
}

Los ficheros RestcontrollerHandler.java y OtherClassToTest.java se encuentran en la ruta /test/resources/checks/myExampleCheck de nuestro proyecto.

La regla consistirá en comprobar que los métodos de nuestras clases no mantengan más de 15 caracteres. Esta regla en sí no tiene mucho sentido, pero nos servirá a modo de ejemplo para mostrar cómo proceder para implementar reglas.

A continuación, el código de nuestra regla:

package com.adictosaltrabajo.tutoriales.checks;

import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;

public class MyExampleCheck extends AbstractCheck {

    private int maxLength = 15;

    public int[] getDefaultTokens() {
        return new int[]{TokenTypes.METHOD_DEF};
    }

    public void setMaxLength(int maxLength) {
        this.maxLength = maxLength;
    }

    @Override
    public void visitToken(DetailAST ast) {
        DetailAST methodNameNode = ast.findFirstToken(TokenTypes.IDENT);

        if(methodNameNode.getText().length() > maxLength) {
            String message = "the method name is too long, only "
                               + this.maxLength + " method name length are allowed";
            log(methodNameNode.getLineNo(), message);
        }
    }
}

A continuación vamos a explicar el código de la anterior clase:

  • El primer punto, como hemos visto anteriormente, pasa por extender la clase AbstractCheck.
  • Seguidamente, el método getDefaultTokens establece los tokens que identificarán los tipos de nodo sobre los que el método vistToken de este check en particular se ejecutará. Como la regla de validación que estamos creando comprueba la longitud del nombre de los métodos, estableceremos como único token a validar TokenTypes.METHOD_DEF. La clase TokenTypes nos ofrece la totalidad de los tipos de nodos existentes en el árbol AST.
  • Posteriormente establecemos un parámetro configurable que marcará la longitud máxima de los nombres de método.
  • Para finalizar implementamos el método visitToken donde estableceremos la lógica de validación. En su interior, la llamada al método log(methodNameNode.getLineNo(), message); da de alta una violación de la regla en el análisis global.
  • Tan solo queda establecer los parámetros de configuración a través del fichero de configuración xml:





    
        
            
        
    

Tras empaquetar el proyecto podremos ejecutar el análisis incluyendo nuestra nueva regla mediante:

java -classpath ./target/checkstyle-checks-1.0-SNAPSHOT.jar:checkstyle-7.5.1-all.jar com.puppycrawl.tools.checkstyle.Main -c /config.xml ~/IdeaProjects/Calculadora/src/

El resultado será similar al que se puede ver a continuación:

Starting audit...
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:20: the method name is too long, only 10 method name length are allowed [MyExample]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:25: the method name is too long, only 10 method name length are allowed [MyExample]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:30: the method name is too long, only 10 method name length are allowed [MyExample]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:35: the method name is too long, only 10 method name length are allowed [MyExample]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:40: the method name is too long, only 10 method name length are allowed [MyExample]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:45: the method name is too long, only 10 method name length are allowed [MyExample]
[ERROR] /Users/jrodriguez/IdeaProjects/Calculadora/src/test/java/com/ejemplos/CalculadoraTest.java:50: the method name is too long, only 10 method name length are allowed [MyExample]
Audit done.
Checkstyle ends with 7 errors.s

6. Conclusiones

Como hemos visto, el análisis estático de código es una parte primordial en el mantenimiento y aseguramiento de la calidad de nuestro código y mediante Checkstyle podemos hacerlo de forma cómoda, rápida y sencilla.

Además, podemos establecer el alcance de nuestros análisis y generar nuestras propias validaciones de calidad en caso de que nuestro contexto así lo requiera.

¡Ya no tenéis excusas para no proceder con estos análisis!


7. Referencias

Crónica de una gestora cultural reconvertida: Women Techmakers Experience

$
0
0

¿Qué hace una chica como yo en un sitio como este? Muy sencillo: disfrutar. Todo comenzó con un tuit, mucho morro y la inestimable ayuda del equipo de Autentia. Lo que fue un intento desesperado de ir al ‘must’ del finde se convirtió en un ‘¡he ganado una entrada para el Women Techmakers!’. ¡Gracias Google Campus por esa maravillosa entrada!


A continuación, os comento cuáles fueron para mí las presentaciones top del evento:

Big Data y recursos humanos o cómo mejorar la calidad en tu empresa. Esto es una regla de tres más o menos sencilla: cuanto mayor sea la satisfacción de tu trabajador en tu empresa, mayor serán sus resultados y, por ende, mayor será la satisfacción de tu cliente con tu producto. Para ello Laila El Qadi (@lailaelqadi) llevó a cabo una magnífica sesión sobre cómo utilizar los datos para optimizar resultados. Desde ese momento, y tras otra intervención de Laila ya de público, me declaré fan incondicional de esta gran profesional.


Tras Laila el ritmo no decayó, sino todo lo contrario. Y es que Marta Verde tocó mi corazoncito con la espectacular muestra de su trabajo: arte y tecnología. Sin duda, Marta es toda una artesana del siglo XXI que nos brindó la oportunidad de conocer una infinidad de referentes más que inspiradores.

Laura Lacarra (@lauralacarra) estuvo más que pletórica en su intervención. El despertar de la zona de confort, nuevas formas de aprendizaje y cómo hacer un huevo frito juntos en un mismo espacio y en una misma charla. Con ella llegaron la curiosidad, las ganas, las nuevas formas de consumir información, el reciclaje, la motivación y el aprender haciendo como un básico ya no para el mundo de la programación, sino para cualquier tipo de profesión actual. ¡Laura mola!


Mi gran descubrimiento fue, sin duda, Isabel Cano (@IsiAngeath) y su charla sobre arte y videojuegos. ¿Se puede pedir más? Tuve la oportunidad de poder hablar con ella y la única palabra que rondaba mi cabeza era ‘crack, crack, crack, crack’. Por cierto, ¿sabíais que Isi está haciendo su propio videojuego sobre las cartas de Van Gogh?


Cristina Fernández Sanz (@cristinafsanz) puso la magia al evento con una intervención llena de ideas y referentes para convertirte en toda una maga del front. Un apunte, ella fue otra de las responsables que me ayudó a conseguir la entrada. ¡Gracias!


Y este es mi pequeño resumen acerca de este evento redondo. Women Techmakers es una experiencia más que necesaria, como necesario es dar voz a todas esas mujeres que hacen posible que la innovación sea algo de todas y todos sin importar género.

No quisiera acabar este post sin mencionar el trabajazo y la buena organización de todo el equipo implicado en el proyecto. Gracias por hacerlo así de bien.

¡Hasta la próxima!

Primeros pasos con Python

$
0
0

Primeros pasos con Python. Instalación del entorno, paquetería, y un ejemplo con expresiones regulares y por supuesto TESTS.

Índice de contenidos


1. Consideraciones iniciales

Dado que la versión considerada como oficial es la 3.x, en este tutorial trabajaremos sobre la versión 3.6.0 de Python. De manera extraoficial la versión 2.7 está ampliamente extendida, y es posible que encontremos muchas aplicaciones que están escritas en esta versión o librerías específicas que no estén soportadas en la nueva versión.

Debemos tener en cuenta que entre ambas versiones hay un cambio de major, lo que supone que es bastante probable que tengamos que hacer modificaciones al código original si necesitamos que corra bajo la nueva versión o plantearnos el utilizar la 2.7.

No obstante, casi todo lo explicado en este tutorial es fácilmente extrapolable a ambas versiones.

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.5 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: macOS Sierra 10.12.2
  • Entorno de desarrollo: PyCharm Professional 2017.1 EAP
  • Python 3.6

1.1. Objetivo del tutorial

En este tutorial aprenderemos a instalar el intérprete Python y su gestión de paquetes.

Además, crearemos un pequeño script que reciba argumentos de ejecución, y cuyo propósito es validar el formato de un fichero CSV de entrada utilizando expresiones regulares.


1.2. Instalación de Python

Lo primero que debemos instalar es el propio Python. Para ello, algo tan simple como dirigirnos a su página web oficial y descargarnos el paquete instalable de la versión que nos interese y que corresponda a nuestro sistema. Podéis acceder directamente desde aquí.

En algunos sistemas, por ejemplo en máquinas que ejecuten alguna distribución de Linux ya contaremos con una instalación de Python. La manera más sencilla de saberlo es ejecutar el comando

python3 --version

Si lo tenemos correctamente instalado podremos esperar un resultado similar a este:

Versión Python instalada


2. Gestión de paquetes en Python y librerías externas

Existen multitud de librerías que podemos incorporar a nuestros desarrollos para aumentar la funcionalidad de una manera sencilla. Existen algunas integradas en el propio Python, y podemos consultar la documentación en: The Python Standard Library

El propio Python cuenta con una herramienta para la gestión de paquetes, llamada pip. Esta es la herramienta recomendada, aunque más adelante comentaremos una segunda alternativa.


2.1. pip

Esta es la herramienta recomendada para la instalación de paquetes. Para la misma de la herramienta podremos dirigirnos a su página oficial: pip. Este paso no será necesario para distribuciones de Python a partir de la versión 3.4, ya que pip está incluido como parte del paquete oficial, y por lo tanto ya lo tendremos instalado. En cualquier caso, podemos consultar la documentación oficial sobre la instalación de paquetes en Installing Python Modules.

Instalar un paquete (o librería) es tan sencillo como ejecutar el comando:

pip install <paquete>


2.2. Macports

Podemos utilizar como alternativa específica para Mac la utilidad Macports. Esto nos servirá no sólo para este propósito, sino como gestor de paquetes en general. Para ello, necesitaremos tener instaladas las herramientas de XCode:

xcode-select --install

Cuando contemos con ello, procedemos a instalar el gestor de paquetes macports desde: http://www.macports.org/ y por fin podremos instalar los paquetes de Python necesarios mediante:

sudo port install <paquete>


3. Librerías incluidas en el paquete estándar

Disponemos de multitud de librerías incluidas en el paquete oficial, conocido como Python Standard Library (consultar en https://docs.python.org/3/library/). De las incluidas, las que utilizaremos en el tutorial son:

  • unittest: desarrollo de test unitarios
  • sys: parámetros y funciones del sistema. Especialmente útil a la hora de recuperar argumentos de ejecución, como veremos después.
  • re: manejo de expresiones regulares
  • argparse: parseo de argumentos
  • logging: gestión de logs
  • errno: errores estándar predefinidos
  • cobertura: informe de lineas cubiertas por nuestros tests (incluye integración con el IDE y CLI)

4. Comenzando con el desarrollo

Todo desarrollo es la solución a un problema planteado. En nuestro caso, y para comenzar por algo muy sencillo, nos centraremos en validar el formato de un fichero para evitar problemas a la hora de procesarlo. Dicho fichero se recibirá como parámetro de la ejecución. En caso de error, se indicará su tipo y la línea del fichero para poder corregirlo.

El formato de nuestro fichero es muy sencillo:
<nombre>;<apellido(s)>;<teléfono>;<email>
donde <email> es un valor opcional.


4.1. Al turrón

Crearemos un primer test encargado de comprobar si la expresión regular que hemos diseñado acorde con el formato esperado se comporta de forma correcta.

Con fines didácticos y para ilustrar el trabajo con cadenas, tomaremos la aproximación inicial de validar el formato de cada campo por separado. La solución óptima pasa por validar el registro en su conjunto aprovechando toda la potencia de las expresiones regulares.

Al desarrollar mediante TDD, podemos comprobar que los errores en tiempo de ejecución (recordad que Python es interpretado), son bastante explicativos. Haciendo uso de la librería unittest incluida, obtendremos el siguiente código:

format_validator_test.py
"""
	Test for the different values of inputs to the regex validator
	"""

	import unittest

	from lesson_1.format_validator import FormatValidator


	# Test Suite in order to organize our tests by groups of functionality
	class FormatValidatorTest(unittest.TestSuite):
	    class NameTests(unittest.TestCase):  # Tests for Name string
	        def test_ShouldPassWhenValidNameFormat(self):
	            # given
	            name_ok = "Yair SegundoNombre"
	            validator = FormatValidator()

	            # when
	            validator.check_valid_name(name_ok)

	            # then
	            self.assertIsNone(validator.error, "Error detected")

	        def test_ShouldFailWhenEmptyName(self):
	            # given
	            empty_name = ""
	            validator = FormatValidator()

	            # when
	            validator.check_valid_name(empty_name)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Name is empty", "Not recognized empty name")

	        def test_ShouldFailWhenNoneAsName(self):
	            # given
	            validator = FormatValidator()

	            # when
	            validator.check_valid_name(None)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Name is None", "Not recognized none name")

	        def test_ShouldFailWhenNotValidName(self):
	            # given
	            ko_name = "_*("
	            validator = FormatValidator()

	            # when
	            validator.check_valid_name(ko_name)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Name error", "Not recognized name error")

	    class SurnameTests(unittest.TestCase):  # Tests for Surname string
	        def test_ShouldPassWhenValidSurnameFormat(self):
	            # given
	            surname_ok = "Segura Albarracín"
	            validator = FormatValidator()

	            # when
	            validator.check_valid_surname(surname_ok)

	            # then
	            self.assertIsNone(validator.error, "Error detected")

	        def test_ShouldFailWhenEmptySurname(self):
	            # given
	            empty_surname = ""
	            validator = FormatValidator()

	            # when
	            validator.check_valid_surname(empty_surname)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Surname is empty", "Not recognized empty surname")

	        def test_ShouldFailWhenNoneAsSurname(self):
	            # given
	            validator = FormatValidator()

	            # when
	            validator.check_valid_surname(None)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Surname is None", "Not recognized none surname")

	        def test_ShouldFailWhenNotValidSurname(self):
	            # given
	            ko_surname = "_*("
	            validator = FormatValidator()

	            # when
	            validator.check_valid_surname(ko_surname)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Surname error", "Not recognized surname error")

	    class PhoneNumberTests(unittest.TestCase):  # Tests for Phone number field
	        def test_ShouldPassWhenValidPhoneFormat(self):
	            # given
	            phone_ok = "912345678"
	            validator = FormatValidator()

	            # when
	            validator.check_valid_phone(phone_ok)

	            # then
	            self.assertIsNone(validator.error, "Error detected")

	        def test_ShouldFailWhenEmptyPhone(self):
	            # given
	            empty_phone = ""
	            validator = FormatValidator()

	            # when
	            validator.check_valid_phone(empty_phone)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Phone number is empty", "Not recognized empty phone number")

	        def test_ShouldFailWhenNoneAsPhone(self):
	            # given
	            validator = FormatValidator()

	            # when
	            validator.check_valid_phone(None)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Phone number is None", "Not recognized none phone number")

	        def test_ShouldFailWhenNotValidPhone(self):
	            # given
	            ko_phone = "_*("
	            validator = FormatValidator()

	            # when
	            validator.check_valid_phone(ko_phone)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Phone number error", "Not recognized phone number error")

	    class EmailTests(unittest.TestCase):  # Tests for Email field
	        def test_ShouldPassWhenValidEmailFormat(self):
	            # given
	            email_ok = "foo@bar.com"
	            validator = FormatValidator()

	            # when
	            validator.check_valid_email(email_ok)

	            # then
	            self.assertIsNone(validator.error, "Error detected")

	        def test_ShouldPassWhenEmptyEmail(self):
	            # given
	            empty_email = ""
	            validator = FormatValidator()

	            # when
	            validator.check_valid_email(empty_email)

	            # then
	            self.assertIsNone(validator.error, "Error detected")

	        def test_ShouldPassWhenNoneAsEmail(self):
	            # given
	            validator = FormatValidator()

	            # when
	            validator.check_valid_email(None)

	            # then
	            self.assertIsNone(validator.error, "Error detected")

	        def test_ShouldFailWhenNotValidEmail(self):
	            # given
	            ko_email = "foo.bar@com"
	            validator = FormatValidator()

	            # when
	            validator.check_valid_email(ko_email)

	            # then
	            self.assertIsNotNone(validator.error, "No error found, but should!")
	            self.assertEqual(validator.error, "Email error", "Not recognized email error")


	if __name__ == "__main__":
	    unittest.main(FormatValidatorTest)  # Executing our TestSuite

Y la clase que hace pasar los tests:

format_validator.py
"""
	Format validation using regular expressions
	"""
	import re


	class FormatValidator(object):
	    def __init__(self):
	        self.error = None
	        self._text_regex = r"[A-Za-z\s]+"
	        self._phone_regex = r"\d{9,9}"
	        self._email_regex = r"[\w\d]+@[\w\d]+\.[\w\d]+"
	        return

	    def check_valid_name(self, name: str) -> bool:
	        return self.__check_field(name, self._text_regex, "Name")

	    def check_valid_surname(self, surname: str) -> bool:
	        return self.__check_field(surname, self._text_regex, "Surname")

	    def check_valid_phone(self, phone_number: str) -> bool:
	        return self.__check_field(phone_number, self._phone_regex, "Phone number")

	    def check_valid_email(self, email: str) -> bool:
	        if email is None or email is "":
	            return True
	        return self.__check4regex(pattern=self._email_regex, text=email, checked_field="Email")

	    def __check_field(self, field: str, pattern: str, field_name: str) -> bool:
	        if self.__check_input(text=field, checked_field=field_name):
	            return self.__check4regex(pattern=pattern, text=field, checked_field=field_name)

	    def __check_input(self, text: str, checked_field: str) -> bool:
	        if text is None:
	            self.error = ("{} is None".format(checked_field))
	            return False
	        elif text == "":
	            self.error = ("{} is empty".format(checked_field))
	            return False
	        return True

	    def __check4regex(self, pattern, text, checked_field: str) -> bool:
	        if re.match(pattern=pattern, string=text) is None:
	            self.error = ("{} error".format(checked_field))
	            return False
	        return True

Como consecuencia del diseño con TDD, obtenemos una cobertura de test muy alta, en este caso concreto, del 100%.


4.2. Trabajando con parámetros de entrada

Ahora que ya tenemos nuestra pieza de validación, lo único que nos queda por hacer es recibir un fichero como parámetro y hacer pasar su contenido por nuestro validador. Para ello, lo primero es poder tratar los parámetros de entrada.

Python provee una librería para estos menesteres, y mediante la cual podemos conseguir un resultado bastante profesional: argparse.

El código final, con su informe de cobertura:

file_check_test.py
"""
Test for the main program
"""

import errno
import unittest

from file_check import FileCheck


# Test Suite in order to organize our tests by groups of functionality
class FileCheckTest(unittest.TestSuite):
    class ParsingTests(unittest.TestCase):
        def test_ArgumentModelCreationOK(self):
            # given
            fc = FileCheck()
            # when

            # then
            self.assertIsNotNone(fc._args, "Object not initialized")
            self.assertTrue("file" in fc._args)

        def test_shouldFailWhenFileIsNone(self):
            # given
            fc = FileCheck()
            # when
            with self.assertRaises(SystemExit) as cm:
                fc.check_file()
            # then
            self.assertEqual(cm.exception.code, errno.EINVAL)

        def test_shouldFailWhenFileIsDirectory(self):
            # given
            fc = FileCheck()
            fc.file = "/"
            # when
            with self.assertRaises(SystemExit) as cm:
                fc.check_file()
            # then
            self.assertEqual(cm.exception.code, errno.EISDIR)

        def test_shouldFailWhenFileNotExist(self):
            # given
            fc = FileCheck()
            fc.file = "test_file.csv"
            # when
            with self.assertRaises(SystemExit) as cm:
                fc.check_file()
            # then
            self.assertEqual(cm.exception.code, errno.ENOENT)

        def test_shouldPassWhenCorrectFile(self):
            # given
            fc = FileCheck()
            fc.file = "test_ok.csv"
            # when

            # then
            self.assertTrue(fc.check_file())

        def test_shouldFailWhenIncorrectFile(self):
            # given
            fc = FileCheck()
            fc.file = "test_ko.csv"
            # when

            # then
            self.assertFalse(fc.check_file())


if __name__ == "__main__":
    unittest.main(FileCheckTest)  # Executing our TestSuite
file_check.py
"""
Document validation against predefined format
"""
import argparse
import csv
import errno
from pathlib import Path

from format_validator import FormatValidator


class FileCheck(object):

    def __init__(self):
        self.file = None
        # Parsing arguments of the invocation
        self.parser = argparse.ArgumentParser(description='Check csv file against predefined format')
        self.parser.add_argument('--file',
                                 help='file location')
        self._args = self.parser.parse_args()
        self.file = self._args.file

    def check_file(self) -> bool:
        if self.file is None:
            exit(errno.EINVAL)
        path = Path(str(self.file))
        if not path.exists():
            exit(errno.ENOENT)
        elif path.is_dir():
            exit(errno.EISDIR)

        # File correct
        fv = FormatValidator()
        with open(str(self.file), newline="") as csv_file:
            reader = csv.reader(csv_file, delimiter=";", quotechar="|")
            for row in reader:
                if not fv.check_valid_name(row[0]) or not fv.check_valid_surname(row[1]) \
                        or not fv.check_valid_phone(row[2]) or not fv.check_valid_email(row[3]):
                    return False
        return True


if __name__ == "__main__":
    fc = FileCheck()
    if fc.check_file():
        print("File format is valid")
        exit(0)
    else:
        print("File format not compliant")
        exit(1)

5. Conclusiones

Como habéis podido comprobar es tremendamente sencillo crear una aplicación desde 0 con este lenguaje. Además, el mismo lenguaje puede ser utilizado como script, orientado a objetos y según el paradigma de programación funcional. Hay una comunidad muy activa de desarrolladores y un sin fin de librerías externas que nos permitiran aumentar su funcionalidad.

En futuros tutoriales ampliaremos el uso de Python en tareas de automatización, machine learning, etc.

El código fuente está disponible en el siguiente repositorio: https://github.com/ysegalb/python-tutorial.git
El mismo repositorio irá creciendo con las nuevas lecciones que vayamos añadiendo.

5.1. Deberes para casa

Hay muchas mejoras y “peoras” que podemos hacer con este ejemplo para cacharrear con el lenguaje. Desde aquí os animamos a trabajar los siguientes cambios para que profundicéis un poco más en este lenguaje:

  • Modificar el tratamiento de csv a fichero de texto y validar mediante una única expresión regular
  • Modificar la expresión regular para que el correo sea de un dominio específico (p.e. @autentia.com)
  • Añadir un nuevo campo URL opcional al fichero y validarlo
  • Mejorar la expresión regular correspondiente al e-mail
  • Todo aquello que se os ocurra para “divertiros” con este lenguaje

6. Referencias

El tutorial se ha escrito desde cero, partiendo de la documentación oficial y las referencias ahí incluidas.

Modificaciones parciales de un documento de Elasticsearch 5 con Python

$
0
0

En este tutorial vamos a analizar la posibilidad de realizar modificaciones parciales en los documentos almacenados en Elasticsearch.

Índice de contenidos


1. Introducción

A modo de introducción previa al tutorial os dejo dos estupendos tutoriales sobre Elasticsearch y Kibana ya que el contenido está enfocado a desarrolladores con conocimientos previos sobre las tecnologías mencionadas.

Primero vamos a explicar cómo realizarlas a través de las herramientas de administración de Kibana para luego automatizarlas con un script en Python.

Para el tutorial vamos a suponer que necesitamos actualizar el campo “number” del documento “bill”. Solo queremos modificar aquellos documentos cuyo “number” tenga 8 dígitos y truncarlos a 7 por la derecha.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.5 GHz Intel Core i7, 16GB DDR3, 500GB Flash Storage).
  • Sistema Operativo: Mac OS Sierra 10.12.3
  • Entorno de desarrollo: Atom 1.14.2
  • Elasticsearch 5.0.1
  • Kibana 5.0.0
  • Python 2.7.13 con requests 2.13.0

3. Modificación parcial de un único registro en Elasticsearch

Para empezar analizaremos la llamada al API de Elasticsearch para actualizar un único registro a través de las herramientas de desarrollador de Kibana:

POST store/bill/20170124103200000000066666660012066361239/_update
{
  "doc": {
    "details": {
      "number": "1234567"
    }
  }
}

En la URL nos encontramos los siguientes parámetros: store/bill/20170124103200000000066666660012066361239/_update

  • store: índice del registro
  • bill: tipo de registro
  • 20170124103200000000066666660012066361239: identificador único del registro
  • _update: acción a realizar sobre el registro

Se puede observar que el documento contiene únicamente los campos que queremos actualizar, en este caso el número de la factura.

Con esta llamada, solo el “number” es actualizado pero la política de Elasticsearch es reemplazar los documentos completamente por lo que internamente borrará el registro, hará los cambios indicados e insertará de nuevo el documento.


4. Automatizar la modificación de registros con Python

Elasticsearch tiene un API para Python para realizar operaciones pero en este caso vamos a prescindir de él ya que con comandos básicos de Python podemos conseguir nuestro objetivo.

elasticsearch_query = '{"query":{"bool":{"must":{"match":{"type":"bill"}},
                      "filter":{"regexp":{"details.number":{"value":"[0-9]{8}"}}}}}}'

elasticsearch_query_response = requests.get(elasticsearch_url + '_search',
                                            data = elasticsearch_query)

Si Elasticsearch nos devuelve resultados, los almacenaremos en un array para más tarde actualizar los registros obtenidos. Podemos realizar el almacenaje de la siguiente manera:

for hit in elasticsearch_response.json()['hits']['hits']:
 elastic_search_records.append(
	entities.ElasticSearchRecord(hit['_index'], hit['_id'], hit['_source']['details']['number'])
 )

Por último y para realizar las modificaciones en Elasticsearch, recorreremos el array donde hemos almacenado la información de los registros obtenidos e invocaremos la siguiente llamada:

elasticsearch_url_update= elasticSearchRecord.index + '/bill/' + elasticSearchRecord.id

elasticsearch_data_update='{"doc":{"details":{"number":"'+elasticSearchRecord.number[:7]+'"}}}'

elasticsearch_update_response= requests.post(elasticsearch_url+elasticsearch_url_update+'/_update',
                                              data= elasticsearch_data_update)

Esto hará que se actualice la información en Elasticsearch y nos devolverá un error en caso de que no se pueda actualizar el registro o algún parámetro sea incorrecto.

Como he explicado anteriormente, aunque solo se actualiza el documento parcialmente, Elasticsearch reemplaza los documentos completamente por lo que si los parámetros o los datos enviados son erróneos podríamos llegar a sobreescribir el documento por completo.


5. Código fuente del tutorial

En mi repositorio de GitHub podéis encontrar todo el código del proyecto del tutorial. Para ejecutarlo solo hay que tener instalado el entorno que he indicado y ejecutar el siguiente comando en un terminal “python partials_modifications.py”.


6. Conclusiones

Esta solución es poco efectiva desde el punto de vista del número de llamadas que se realizan a Elasticsearch y se puede solucionar en una sola llamada con el método “Update by query” en el que se hace la query y la actualización en un solo paso.

Pero si el campo a actualizar tuviera una fuente de datos externa a Elasticsearch o el cambio no se pudiera realizar con el parámetro “script”, la solución analizada en este tutorial nos aportaría más flexibilidad.

Un saludo.

Alejandro

aalmazan@autentia.com

Análisis estático de accesibilidad web con GoogleChrome Accessibility Developer Tools

$
0
0

Parafraseando a Tim Berners-Lee, creador de la WWW y director del W3C,”la potencia de la web se basa en su universalidad y un aspecto esencial es que cualquiera pueda acceder a la misma sin importar sus capacidades”.
En este tutorial vamos a centrarnos en la accesibilidad web, por ello vamos a mostraros como poder analizar la accesibilidad de nuestras webs gracias a GoogleChrome Accessibility Developers Tools, y como automatizar el proceso con Jenkins.

Índice de contenidos


1. Introducción

Entendemos por Accesibilidad Web la habilidad de hacer que una web sea usable por el máximo número de personas posible con independencia de sus conocimientos o capacidades personales.

En este sentido la W3C (World Wide Web Consortium) crea en 1997 la Web Accessibility Initiative (WAI), iniciativa que busca como principal objetivo eliminar las barreras que impiden a las personas que tengan dificultades de cualquier índole encontrar o consumir información en la web.

Para alcanzar esta meta, el W3C definió una serie de pautas o directivas que deben tenerse en cuenta a la hora de diseñar las páginas web para conseguir que estas sean accesibles. Hasta un total de 14 pautas se ven recogidas en el WCAG (Web Content Accessibility Guidelines).

Desde proveer de alternativas equivalentes del contenido en formato audio o visual, hasta asegurarnos de que los documentos que ofrecemos son claros y sencillos, cada pauta se ve definida por una serie de checkpoints que nos informaran del grado de cumplimiento de la misma.
Dichos checkpoints se pueden categorizar según distintas prioridades de importancia, del 1 al 3, y el cumplimiento de la totalidad de cada una de las prioridades nos dará una medida de cuán accesible es nuestra web.

Es a través del cumplimiento de los checkpoints como se definen los grados o niveles de conformidad:

  • Nivel de conformidad “A”:Se cumple con la totalidad de los checkpoints de prioridad 1.
  • Nivel de conformidad “AA”:Se cumple con la totalidad de los checkpoints de prioridad 1 y 2.
  • Nivel de conformidad “AAA”:Se cumple con la totalidad de los checkpoints. Prioridades 1, 2 y 3 completas.

La propia WAI dispone de una herramienta que nos ayuda a generar informes, paso a paso, sobre el grado de accesibilidad de una determinada web, pero no realiza ningún análisis de manera automática.

Para este último proceso, los análisis automáticos, vamos a ayudarnos de GoogleChrome Accessibility Developer Tools, una suite de herramientas que, si bien no se basan en las 14 pautas de la W3C, si analizan el cumplimiento de algunos problemas más comunes en la accesibilidad web, como son:

  • El cálculo del ratio de contraste y la sugerencia de colores.
  • Obtención y validación de atributos y estados WAI-ARIA (Accesibile Rich Internet Applications).
  • El cálculo de la accesibilidad de un nombre usando el algoritmo propuesto por la W3C.

En este tutorial vamos a mostraros como poder analizar la accesibilidad de nuestras webs gracias a GoogleChrome Accessibility Developers Tools, y como automatizar el proceso con Jenkins.


2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro Retina 15′ (2.5 Ghz Intel Core I7, 16GB DDR3).
  • Sistema Operativo: macOS Sierra 10.12.3.
  • Git v2.6.0
  • NodeJS v6.3.0
  • Npm v3.10.3
  • Grunt v1.2.0
  • PhantomJs v2.1.1
  • Docker v1.12.2
  • Docker Kitematic v0.12.0

3. Instalación del entorno

El primer punto a tener en cuenta es que asumiremos que ya se dispone de las dependencias declaradas en la sección anterior.

Continuaremos clonando el repositorio de las herramientas como se muestra a continuación:

$ git clone --recursive https://github.com/GoogleChrome/accessibility-developer-tools.git

Tras la descarga del proyecto desde Github, procederemos colocandonos en la carpeta base del proyecto y resolviendo todas las dependencias del mismo mediante:

$ npm install

Para finalizar volveremos a compilar el proyecto con las dependencias descargadas mediante grunt:

$ grunt

Mediante estos sencillos pasos habremos instalado el entorno necesario para proceder con los análisis de accesibilidad de la herramienta Accessibility Developer Tools (ADT).


4. Ejecución análisis

Para proceder con los análisis de accesibilidad tan solo necesitaremos ayudarnos de la herramienta PhatomJS procediendo según el siguiente comando:

$ phantomjs {ruta-de-instalacion-ADT}/tools/runner/audit.js {ruta-del-fichero-html}

En ese instante, la herramienta comenzará con el análisis del fichero html objetivo, ofreciendo como resultado la escritura por pantalla de aquellas problemas de accesibilidad que haya detectado.

A continuación se puede ver un ejemplo de resultado de ejecución del análisis.

*** Begin accessibility audit results ***
An accessibility audit found
Errors:
Error: AX_TEXT_01 (Controls and media elements should have labels) failed on the following element:
#s
See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_text_01 for more information.


Warnings:
Warning: AX_FOCUS_01 (These elements are focusable but either invisible or obscured by another element) failed on the following elements (1 - 5 of 96):
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-twitter > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-facebook > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-linkedin > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-youtube > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-rss > A
See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_focus_01 for more information.

Warning: AX_TEXT_02 (Images should have a text alternative or presentational role) failed on the following elements (1 - 5 of 18):
#content > .post.clearfix > .image-member > A > IMG
#content > DIV:nth-of-type(3) > .image-member > A > IMG
#content > DIV:nth-of-type(4) > .image-member > A > IMG
#content > DIV:nth-of-type(5) > .image-member > A > IMG
#content > DIV:nth-of-type(6) > .image-member > A > IMG
See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_text_02 for more information.

Warning: AX_TEXT_04 (The purpose of each link should be clear from the link text) failed on the following elements (1 - 5 of 20):
#content > .post.clearfix > .image-member > A
#content > DIV:nth-of-type(3) > .image-member > A
#content > DIV:nth-of-type(4) > .image-member > A
#content > DIV:nth-of-type(6) > .image-member > A
#content > DIV:nth-of-type(7) > .image-member > A
See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_text_04 for more information.

Warning: AX_COLOR_01 (Text elements should have a reasonable contrast ratio) failed on the following elements (1 - 5 of 128):
#topbar > .container > .eight.columns > .callus
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-twitter > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-facebook > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-linkedin > A
#topbar > .container > DIV:nth-of-type(2) > .social-icons.clearfix > UL > .social-youtube > A
See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_color_01 for more information.

Warning: AX_IMAGE_01 (Meaningful images should not be used in element backgrounds) failed on the following elements (1 - 5 of 60):
#content > .post.clearfix > .post-meta > .meta-date > .icon-calendar
#content > .post.clearfix > .post-meta > .meta-author > .icon-user
#content > .post.clearfix > .post-meta > .meta-comment > .icon-comment
#content > .post.clearfix > .post-meta > SPAN:nth-of-type(5) > .icon-eye-open
#content > DIV:nth-of-type(3) > .post-meta > .meta-date > .icon-calendar
See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_image_01 for more information.


*** End accessibility audit results ***

Una característica bastante interesante es que, para cada uno de los problemas que detecte durante el análisis, nos ofrece una URL del tipo See https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#. Esta página nos proveerá de la información necesaria para entender el problema, porqué se produce y cómo solucionarlo.


5. Automatización de análisis mediante Jenkins y Dockerización.

Vamos a proceder con la automatización del proceso de análisis con Jenkins, pero, dado que el proceso de instalación es bastante sencillo, consideramos oportuno automatizarlo asociándolo a una imagen dockerizada de Jenkins.

Procedemos con la generación de un fichero Dockerfile como el que se puede ver a continuación:

FROM jenkins:latest

USER root
RUN apt-get update
RUN apt-get install -y nodejs
RUN apt-get install -y npm
RUN ln -s /usr/bin/nodejs /usr/bin/node

RUN mkdir -m 777 /opt/adt
RUN git clone https://github.com/jlrvilla/accessibility-developer-tools.git /opt/adt
RUN npm install -g grunt-cli
RUN npm install -g grunt
RUN cd /opt/adt && npm install
RUN cd /opt/adt && grunt --force
RUN npm install -g phantomjs

USER jenkins

Como vemos, no estamos haciendo uso del repositorio de GoogleChrome, sino que hemos realizado un fork del proyecto donde hemos modificado el runner audit.js adaptandolo para que nos devuelva códigos de error en caso de encontrar problemas de manera que Jenkins nos devuelva un mensaje adecuado dependiendo de la corrección o no del proceso de análisis.

A continuación procedemos con la construcción de la imagen mediante:

$ docker build -t "jenkins:adt" .

Y levantamos una instancia de la imagen:

$ docker run --name "ADT" jenkins:adt

Cuando el contenedor se encuentre en pleno funcionamiento procedemos con el mapeo de puertos al exterior, en nuestro caso lo hemos realizado a través de Kitematic, como se puede ver en la imagen a continuación:

Redirección de puertos

Una vez mapeados los puertos procedemos con la configuración de Jenkins mediante el sencillo asistente paso a paso que nos ofrece la imagen dockerizada.

Cuando hayamos acabado procedemos con la configuración de una nueva tarea de jenkins. En nuestro caso simplemente procederemos con la evaluación de un fichero html que vamos a alojar en la carpeta temporal.

Configuración del task de Jenkins

En las imágenes anteriores podemos ver cómo proceder con el análisis, simplemente estableciendo la ejecución de un nuevo paso de tipo “Ejecutar línea de comandos (Shell)” en el que estableceremos la ejecución del análisis:

phantomjs /opt/adt/tools/runner/audit.js /tmp/prueba.html

En este caso tan solo mantenemos un fichero html sobre el que realizar la prueba mientras en nuestras páginas web, probablemente, existan más páginas. ¿Qué hacer en estos casos? Tendremos que modificar este paso para introducir un poco de programación shell script realizando una búsqueda de aquellos fuentes html en la ruta donde se disponga de los ficheros a analizar y proceder con un bucle FOR.

El resultado que nos devuelve la ejecución de la tarea de Jenkins, en nuestro caso, será similar a la que se muestra a continuación:

Ejecución Jenkins Análisis

6. Conclusiones

La accesibilidad web debería ser uno de los pilares básicos en la generación de webs, tanto más si la web que estamos creando está pensada para el público en general.

En este tutorial hemos visto que existe un estándar WCAG que nos ayuda en la búsqueda de conseguir que nuestra web sea accesible, hemos visto que a través de herramientas como las Accessibility Developer Tools de GoogleChrome podemos realizar análisis que nos permitan verificar si vamos por el buen camino.

Por último hemos visto que es una tarea fácilmente automatizable dentro de procesos de integración continua, mediante la generación de una imagen de Docker de Jenkins un poco personalizada.

¡Espero que este tutorial os sirva de ayuda en la búsqueda de una web más accesible!


7. Referencias

Reglas básicas de encuadre y composición para vídeo y fotografía

$
0
0
En este tutorial aprenderemos algunas reglas básicas de encuadre a la hora de realizar tanto fotografías como vídeos.

Índice de contenidos

1. Introducción

Cuando nos disponemos a realizar una fotografía o grabar un vídeo, debemos tener en cuenta de qué manera encuadramos, esto nos ayudará tanto a que sea visualmente estético como a que el espectador centre su atención en aquello que nosotros queramos. En este tutorial aprenderemos las reglas básicas de composición.

2. Fotografía

A la hora de componer en fotografía, la regla principal que se utiliza es La Regla de los Tercios. Esta regla es un método simple de aproximación a la proporción áurea y consiste en distribuir el espacio de manera que fijemos un punto de interés y resulte atractivo. Para aplicar la regla de los tercios dividimos nuestro espacio en 3 tercios iguales tanto en horizontal como en vertical:



De este modo tendremos 9 cuadrados con 4 intersecciones, estas intersecciones son los lugares ideales para colocar el punto de interés, que es donde queremos centrar la atención de nuestra fotografía. Además, también utilizaremos las líneas verticales y horizontales para ayudarnos a encuadrar. Por ejemplo, en el caso de la fotografía de paisajes, es importante dónde colocamos la línea del horizonte, ya que si colocamos el horizonte en la línea superior le estaremos dando más importancia al suelo, y si lo colocamos en la línea inferior, tendrá más importancia el cielo.



3. Vídeo

Cuando hablamos de composición debemos hacer una distinción entre vídeo y fotografía. Aunque por lo general se le da más importancia en la fotografía, hay algunas reglas importantes que en el vídeo debemos tener en cuenta. En este caso nos centraremos principalmente en reglas a la hora de encuadrar personas, bien sea para una entrevista, una imagen en movimiento,etc.

3.1. Ley de la mirada y control de los aires

Aunque parece una cosa sencilla, el dónde dejamos el aire (espacio vacío entre nuestro sujeto y el fin del encuadre) puede hacer de nuestra imagen desde algo estético hasta un auténtico desastre. Por lo general, a no ser que tengamos algo importante que mostrar en la parte superior, el espacio por encima de la cabeza del sujeto debe ser pequeño.



Como podéis comprobar, la segunda imagen es mucho más estética haciendo simplemente una pequeña corrección. En el caso del aire lateral, si nuestro sujeto se encuentra mirando al frente, podemos colocarle centrado, o siguiendo la regla de los tercios como ya mencionamos anteriormente.



Si por el contrario nuestro sujeto se encuentra mirando hacia cualquier otro lado, se aplica lo que se llama “La ley de la mirada”, que consiste en dejar más aire hacia la dirección hacia la que se encuentra mirando nuestro sujeto.



3.2. Tipos de encuadres

Además de controlar dónde dejamos los espacios a la hora de encuadrar, debemos también tener en cuenta en qué zona cortamos a la persona que estamos encuadrando. Cuando no realizamos un plano entero (plano que encuadra justo la figura entera del sujeto), las zonas en las que debemos evitar cortar son:
  • Articulaciones (tobillos, rodillas,etc)
  • Cintura (ya que a nuestro sujeto no se le verán las manos)
  • Cabeza (debemos evitar cortarla por arriba)
  • Cuello (siempre debemos dejar algo de hombros, o solamente la cara)


Los tipos de planos más habituales son:



4. Conclusiones

Como veis, siguiendo unas sencillas reglas podemos mejorar de manera notable nuestros vídeos y fotografías, aunque siempre hemos de tener en cuenta que cada imagen es un mundo y las reglas están para romperlas, así que debemos ser nosotros mismos los que debemos considerar en cada caso si es mejor seguirlas o no.

5. Referencias

Configuración de DKIM y SPF con Mandrill

$
0
0

Si usáis Mandrill en vuestros desarrollos, es muy probable que el cliente pida que los correos se envíen desde un dominio suyo, DKIM y SPF nos permiten hacerlo.

Índice de contenidos

  1. DKIM y SPF con Mandrill
  2. Configuraciones necesarias en Mandrill y en nuestro DNS

1. DKIM y SPF con Mandrill

Si trabajáis dando servicio a clientes, y en vuestros desarrollos os encontráis con la necesidad de integrar Mandrill para el envío de correos electrónicos, es muy probable que el cliente os pida estos se envíen en su nombre, es decir desde una dirección de correo propia del cliente.

La combinación de DKIM y SPF permitirá que podamos hacerlo y reducir los problemas de entrega derivados de modificar la información del From Name y el Subject, para simular que el envío lo está haciendo nuestro cliente (sobra decir que lo que se busca es limitar las malas prácticas de esta funcionalidad en técnicas como el phising).

¿Qué es DKIM?

El sistema DKIM (DomainKeys Identified Mail) es un mecanismo de autenticación que se emplea para que un correo electrónico pueda ser verificado por un destinatario, comprobando que el origen de ese correo es realmente el que aparece en las cabeceras del correo electrónico.

Este mecanismo, permite que puedan limitarse los correos que envían los spammers y los atacantes que emplean técnicas de phising, haciendo más difícil la suplantación.

El funcionamiento es sencillo, básicamente se produce un diálogo entre el servidor de correo de destino y el DNS del dominio del remitente. La conversación sería:

  1. Este diálogo se inicia cuando se envía un correo, momento en el que se incluye una cabecera en el mensaje con una firma digital específica.
  2. Cuando el servidor de correo de destino recibe el mensaje anterior, hace una consulta al DNS del dominio del remitente.
  3. Si el campo de la firma DKIM está debidamente configurado, obtiene la clave pública del dominio y la utiliza para descifrar el valor de la firma y recalcular el valor de la firma, ambos valores deben coincidir.

¿Qué es SPF?

El sistema SPF (Sender Policy Framework) es otra protección contra las falsificaciones de correos electrónicos.

A diferencia de DKIM, no busca el firmar y verificar un correo electró enviado, sino que permite especificar mediante un TXT en nuestro DNS desde qué servidores de correo pueden enviarse correos electrónicos de nuestro dominio.

De nuevo se produce un diálogo entre el servidor de correo de destino y el DNS del dominio del remitente. La conversación sería:

  1. Cuando el servidor de correo de destino recibe un email anterior, hace una consulta al DNS del dominio del remitente para comprobar si está configurada una entrada SPF en la que aparezca indicado el dominio desde el que se está recibiendo el correo.
  2. Si el dominio origen está especificado en el SPF de nuestro DNS, se acepta y si no se rechaza el mensaje.

2. Configuraciones necesarias en Mandrill y en nuestro DNS

Ya que es necesario realizar configuraciones tanto en nuestros ajustes de Mandrill como en el DNS, voy a desarrollar a continuación un ejemplo de configuración empleando 1&1 como referencia.

Paso 1. Añdir nuestro Dominio en Madrill

Accedemos a Mandrill https://mandrillapp.com/ y navegamos a través de Domains → Sending Domains para llegar a la pantalla de configuración de los dominios de envío.

Introducimos nuestro dominio, en mi caso: jangulog.es y pulsamos + Add

DKIM-SPF-lista dominios

Una vez introducimos el dominio nos aparecerá la siguiente capa modal solicitando una dirección de correo perteneciente a dicho dominio para poder verificar que tenemos acceso a ella, ya que nos va a enviar un enlace de verificación a ella.

DKIM-SPF-modal email

Una vez pulsemosSend Verification Email se creará una nueva entrada en el panel con el dominio que hemos incluido que aparecerá con un estado Requires verification. Para verificarlo tenemos que acceder al correo que nos habrá llegado de Mandrill (ver imagen siguiente) .

NOTA: La verificación debe realizarla la persona que tenga acceso a Mandrill, por lo que si no somos nosotros mismo, deberemos facilitar a esa persona el enlace que nos haya llegado para que pueda verificarla. Y en el caso contrario, si la persona que reciba el correo si no tiene acceso a Mandrill, es necesario que nos facilite a nosotros el enlace para verificar.

DKIM-SPF-dominio no verificado

Recibiremos un correo del tipo:

DKIM-SPF-verification email

Una vez verificado, cambiará el estado en nuestro panel y quedarán pendiente la configuración y validación del DKIM y SPF propiamente dichos.

DKIM-SPF-dominio verificado

Para proceder a configurar el registro DKIM en el dominio que hemos especificado, desde Mandrill se genera una clave de forma específica para ese dominio. Desde el propio panel, hacemos clic en View DKIM Settings y aparecerá la siguiente capa modal que nos da las instrucciones para crear un registro de tipo TXT en nuestro Dominio.

DKIM-SPF-DKIMvalue

Quedaos con el prefijo: andrill._domainkey y con el value que podéis copiar directamente desde el campo de texto: v=DKIM1; k=ras; p=….

Os recomiendo que para evitar saltar entre Mandrill y la configuración del DNS, os copieis en este paso también los datos de configuración del SPF. Para ello pulsamos View SPF Settings para que Mandrill nos facilite la configuración al igual que con el DKIM:


En este caso únicamente necesitamos el value que podéis copiar directamente desde el campo de texto: v=spf1 include:spf.mandrillapp.com ?all

Paso 2. Configurar las entradas DKIM y SPF en nuestro dominio

Ahora es el momento de configurar nuestro DNS, no cerréis Mandrill aún porque tendremos que volver a tocar cosas en él un poco más adelante. Accedemos en este caso a nuestro panel de control en 1&1: https://clientes.1and1.es/

Accedemos a

Dominios desde el menú y buscamos el dominio que queremos configurar (si tenemos más de uno), en mi caso jangulog.es

DKIM-SPF-panel 1and1 dominios

Una vez en Mis dominios, para acceder directamente a la configuración de este dominio concreto hacemos clic en el menú de puntos a la derecha del dominio y seleccionamos en el desplegable Configuración avanzada (ver imagen siguiente). Si hacemos clic en el dominio, también podemos llegar a la configuración pero implica un par de pasos de navegación adicionales.

DKIM-SPF- 1and1 configuracion avanzada

Una vez en la Configuración avanzada hacemos scroll hasta la sección Configuración DNS y pulsamos Editar

DKIM-SPF-1and1 DNS

Aparecerán como editables las diferentes configuraciones posibles sobre el DNS del dominio:

DKIM-SPF-1and1 DNS Editar

De nuevo bajamos haciendo scroll hasta la sección

Registros TXT y SRV y pulsamos

A&ntildeadir registro


DKIM-SPF-1and1 Registro TXT

Se abrirá una capa modal para que podamos añadir un nuevo registro, vamos a empezar con el DKIM, hay que configurarlo de la siguiente manera:

DKIM-SPF-1and1 TXT DKIM

NOTA 2: Ojo con el prefijo, hay que especificar únicamente mandrill._domainkey, la explicación del tooltip puede llevar a error y ofuscaros intentando poner mandrill._domainkey. 🙂 Una vez lo tengamos relleno, pulsamos Guardar.

NOTA 3: Al guardar podremos ver que el registro se ha añadido pero OJO, realmente no se ha guardado aún, para ello hay que pulsar de nuevo el botón Guardar que ha aparecido abajo a la derecha, y entonces sí que serán efectivos los cambios. Nos pedirá confirmación a través de una nueva modal.

DKIM-SPF-1and1 TXT DKIM OK

Y en el caso del SPF, repetimos la operación anterior para crear un nuevo TXT pero con los siguientes datos:

DKIM-SPF-1and1 TXT SPF

NOTA 4: No hay que especificar Prefijo, es únicamente para el DKIM. Y no os olvidéis de pulsar de nuevo Guardar como en el caso anterior.

Paso 3. Comprobar las configuraciones del DNS en Mandrill

Volvemos a Mandrill para comprobar que nuestras configuraciones son correctas. Desde el panel Domains → Sending Domains hacemos clic en Test DNS Settings, si todo está correctamente configurado tanto el DKIM como el SPF aparecerán como Valid.

DKIM-SPF-1and1 dominio OK

Gestiona tus herramientas de desarrollo con SdkMan!!!

$
0
0

SdkMan (Software Development Kit Manager) es una utilidad que nos va a permitir instalar y gestionar, de forma simultánea, distintas versiones de nuestras herramientas de desarrollo, como por ejemplo: Gradle, Groovy, Java, Maven, Scala, …​

Índice de contenidos

SdkMan logo

1. Introducción

SdkMan nos va a facilitar la instalación de las distintas herramientas de desarrollo. Pero para mi lo importante, o donde más valor aporta, es “de forma simultánea“. Es decir podemos tener instaladas distintas versiones, por ejemplo de Java, y usar una u otra en función de nuestras necesidades.

Por ejemplo podemos tener Java 8 para nuestros desarrollos habituales y tener Java 9 para empezar a juguetear con las nuevas funcionalidades. SdkMan nos permitirá pasar de una a otra de forma muy sencilla y sin afectar al resto del sistema.

Ojo porque SdkMan no es lo mismo que jEnv. jEnv se limita a Java y no hace la instalación. De hecho podríamos usar ambos de forma combinada, por ejemplo SdkMan para instalar Java y jEnv para determinar la versión de Java en función del directorio en el que nos encontramos (SdkMan define uno por defecto y lo puedes cambiar para una Shell, pero no para un directorio concreto).



2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15” (2.5 GHz Intel i7, 16GB 1600 Mhz DDR3, 500GB Flash Storage).
  • AMD Radeon R9 M370X
  • Sistema Operativo: macOS Sierra 10.12.3
  • SdkMan 5.5.3+194


3. Instalación de SdkMan

La instalación es muy sencilla, basta con abrir un terminal y ejecutar:

$ curl -s "https://get.sdkman.io" | bash

Y luego:

$ source "$HOME/.sdkman/bin/sdkman-init.sh"

Podemos comprobar que todo ha ido bien ejecutando:

$ sdk version

SdkMan version

En nuestro caso hemos instalado la versión 5.5.3+194

Una de las cosas curiosas de este comando es que, tal como se ve en la imagen, no sólo nos dice la versión de SdkMan, sino las últimas versiones que tenemos disponibles para instalar. Lo podemos ver en el apartado BROADCAST.

Para ver todo lo que podemos hacer con SdkMan basta con ejecutar:

$ sdk help
SdkMan help

Tras la instalación se habrá modificado nuestro ~/.bash_profile y al final del todo se habrán añadido las siguientes líneas:

SdkMan change bash_profile

También se habrá creado el directorio ~/.sdkman donde se guarda todo lo relacionado con esta utilidad, incluidos los binarios que instalemos.



4. Instalando Java 8 y 9 con SdkMan

Para saber que cosas podemos instalar con SdkMan basta con ejecutar:

$ sdk list
SdkMan list

Así obtendremos la lista completa junto con la descripción de cada una de las herramientas de desarrollo que es capaz de gestionar SdkMan. Ahora mismo esta es la lista que es capaz de gestionar SdkMan recién instalado:

Vemos que Java está entre las herramientas que podemos instalar. Para ver que versiones están disponibles, usaremos ampliado el comando anterior:

$ sdk list java
SdkMan list java

Ahora instalamos Java 9 con:

$ sdk install java 9ea157
SdkMan install java 9

En esta imagen podemos destacar como al principio nos pide la confirmación de la licencia de Oracle, luego se hace la descarga del archivo, y finalmente la instalación. Al final vemos en verde la confirmación de la instalación y de como se ha fijado Java 9 como la versión por defecto. Esto es porque no hay ninguna otra versión instalada, de lo contrario nos preguntaría si la versión que estamos instalando queremos fijarla por defecto o no.

Ahora instalamos Java 8 con:

$ sdk install java 8u121
SdkMan install java 8

Igual que antes nos pide la aceptación de la licencia de Oracle, luego hace la descarga y luego la instalación. La diferencia es que ahora sí que nos pregunta si queremos hacer que esta sea la versión por defecto (le contestamos que sí).

Si volvemos a listar las versiones de Java deberíamos ver lo siguiente:

$ sdk install java 8u121
SdkMan installed java versions

Con un * vemos las versiones que están instaladas, y con > vemos la versión que está fijada en la Shell donde ejecutamos el comando.



5. Cambiando entre versiones de Java con SdkMan

Una vez fijada la versión por defecto, esta será la usada por todo el sistema. Si queremos usar otra de las versiones instaladas basta con ejecutar:

$ sdk use java 9ea157
SdkMan installed use java 9

A partir de aquí, en esta Shell la versión de Java activa será la 9. Lo podemos ver en la misma imagen como hacemos un mvn --version y vemos que la versión activa es ciertamente la 9-ea, y como el JAVA_HOME está fijado al directorio ~/.sdkman/candidates/java/9ea157

Esta magia se consigue básicamente manipulando la variable de entorno PATH y con algunos enlaces simbólicos.

SdkMan PATH environment variable

Esta parte del PATH es la que menos me gusta porque mientras más herramientas instalemos más “ensuciamos” la variable PATH (en la imagen vemos como está instalado maven, java y gradle). Pero es un pequeño precio a pagar por toda la funcionalidad que nos ofrece.



6. Conclusiones

SdkMan es un claro ejemplo de como se influencian unos lenguajes a otros. En este caso SdkMan, o como se conocía en sus inicios GVM (the Groovy enVironment Manager), se inspira en el RVM y rbenv de Ruby. Teniendo en cuenta la habitual lucha que hay entre lenguajes y plataformas, me parece un acierto y una alegría que en vez de trollear se aprovechen y se copien las buenas ideas.

Y desde luego ahora, gracias a SdkMan, no tenéis excusa para no instalar Java 9 y empezar a trastear ¡Buena caza y largas lunas!



7. Sobre el autor

Alejandro Pérez García (@alejandropgarci)
Ingeniero en Informática (especialidad de Ingeniería del Software) y Certified ScrumMaster

Socio fundador de Autentia Real Business Solutions S.L. – “Soporte a Desarrollo”

Socio fundador de ThE Audience Megaphone System, S.L. – TEAMS – “Todo el potencial de tus grupos de influencia a tu alcance”

Una guía de viaje por el Continente de los Microservicios: Building Microservices, de Sam Newman

$
0
0

Desde hace unos años, en el ámbito de arquitectura de software, hay una palabra que destaca sobre todas las demás: "microservicios". Se trata de una forma de estructurar una aplicación, dividiéndola en múltiples aplicaciones más o menos independientes llamadas microservicios. La finalidad es la de lograr una arquitectura más desacoplada que permita adaptarse a los cambios de una forma más veloz. Al menos en teoría, ya que hay muchas críticas al respecto.

Ténicamente no va mucho más allá de partir una aplicación en decenas de aplicaciones más utilizando las mismas herramientas que conoces hasta ahora. Es cierto que han surgido nuevas librerías y tecnologías para facilitar el desarrollo, sobre todo porque conlleva algún problema nuevo (descubrimiento de servicios, circuits breakers…). Pero en esencia no es un problema técnico, sino que es un problema de toma de decisiones ante nuevos escenarios: olvida lo que has hecho hasta ahora y enfréntate a una nueva forma de hacer las cosas.

Para que veas a qué me refiero, te propongo una serie de preguntas a las que nos hemos tenido que enfrentar los que hemos adoptado (o al menos intentado) una arquitectura de microservicios:

  • Tengo una sola base de datos y varios microservicos ¿Se comparte?¿Qué tabla va a cada microservicio?
  • Para este nuevo proyecto… ¿Tengo que usar microservicios o mejor un monolito?
  • ¿Cómo hago el despliegue? ¿ A la vez o por microservicio? Y el versionado… ¿es individual?
  • Esta nueva operación… ¿Va en un microservicio nuevo? Si lo dejo en el mismo ¿no es ya demasiado grande? ¿Cuándo lo divido?
  • Estoy repitiendo código en varios microservicios ¿Creo una librería común que importen todos?
  • Tengo dos microservicios que se comunican entre sí… ¿Comparto los DTO de cliente y servidor?¿Cómo sé que no estoy rompiendo nada al hacer un cambio en la API?
  • Cada vez tengo más dependencias y cada microservicio parecen micromonolitos… ¿Por qué?
  • Los microservicios nos hacen ir más lentos y negocio nos presiona ¿Qué hemos hecho mal?
  • …(y me dejo decenas)

El problema de entrar en un mundo nuevo, es que hay que hacer camino al andar, por lo que las dudas que surgen son muchísimas. Y sobre todo complicadas: no son dudas técnicas que cualquiera puede resolver en StackOverflow, sino que son dudas de arquitectura, de gestión de la configuración, de despliegue, de organización de equipos para trabajar con microservicios… Son dudas particulares de cada proyecto que no pueden ser resueltas salvo desde la experiencia.

Y el problema de la toma de decisiones es que llevan asociadas unas consecuencias, y seguramente esas consecuencias sean mayores de lo que esperas en un principio: si no conoces la importancia de una decisión, tampoco conocerás el calado de las consecuencias. Es posible que un par de meses después te encuentres en un lío tremendo que dificulte la evolución de la arquitectura por una decisión sobre un problema que considerabas trivial desde el punto de vista de monolitos, pero no sobre microservicios.

Espero que hayas tomado conciencia de la dificultad que supone aplicar el paradigma de los microservicios. Ahora tienes dos opciones: la mejor y primera es dejar asesorarte por alguien que haya pasado por ese trago y te enseñe el camino adecuado, pero esto no es siempre posible. La otra es que lo descubras por tí mismo, pero al menos lleves una guía de viaje, que no es otra que este Building Microservices de Sam Newman.

Sam Newman es un consultor de la empresa Thoughtworks, que si no te suena de nada, te diré que es la empresa de consultoría en la que lleva trabajando muchos años gente como Martin Fowler. Aunque los microservicios no llevan con nosotros mucho más allá que desde 2008, parece que han tenido la oportunidad de aplicarlos en unos cuantos proyectos, por lo que este libro representa una síntesis de la experiencia en esta tecnología a través de 12 capítulos.

También tengo que advertirte que no es un libro práctico que contenga extractos de código, así que no compres el libro esperando que vas a poder montar un sistema de microservicios desde 0 en Java o en otro lenguaje. No trata de cómo usar librerías ni servidores, ni habla de tecnologías en concreto, aunque sí que da valiosas citas sobre productos que pueden encajar dentro de sus consejos: Hystrix, Eureka, Spring Boot, Pact… Por tanto, espera un compendio de consejos y reflexiones generales sobre la experiencia de Sam Newman y su equipo en el mundo de microservicios. A mí me parece más valiosa la experiencia que soluciones concretas que sólo se pueden aplicar a casos concretos, pero cada uno tiene sus necesidades.

Vamos a ver un pequeño resumen de lo que se ve en cada uno de los 12 capítulos de los que consta para que te hagas una idea de lo que te vas a encontrar:

  1. Microservicios: una introducción a la arquitectura de microservicios, comentando sus características, especialmente la descomposición frente al monolito y comparándolo con la división en librerías compartidas y módulos; y sus beneficios como la posibilidad de usar diferentes lenguajes, el escalado, despliegues, independencia de desarrollo. También se comenta la necesidad de tener una organización alineada con los microservicios, aunque posteriormente se ahondará en este tema.

  2. La arquitectura evolutiva: introduce el símil del arquitecto de software con el arquitecto urbano, que diseña los principios de las ciudades, pero que deja libertad a lo que sucede en cada uno de los solares: trabajar para dejar que el desarrollo urbano surja con el tiempo y necesidades. Lo relaciona con la necesidad de la arquitectura de microservicios de dar satisfacción a objetivos no sólo técnicos sino estratégicos de la organización. Trata aspectos transversales como la necesidad de monitorización, desarrollo de interfaces, deuda técnica, gestión de excepciones, o la "gobernanza" a través de plantillas de generación de microservicios que incorporen todo lo que necesitamos para que estén alineados con lo que se espera.

  3. Cómo modelar microservicios: Debate sobre qué principios definen un buen microservicio, como el clásico "bajo acoplamiento y alta cohesión". Establece el Bounded Context del Domain Driven Design(DDD) como arma para la división correcta de un problema en microservicios, aplicándolo a un ejemplo clásico que pone de relieve algunas dificultades a la hora de aplicar esta teoría a la práctica: ¿Qué pasa cuando dividimos en dos bounded context pero en ambos está presente cierto "modelo"?¿A qué bounded context pertenece?¿Estamos haciendo una descomposición prematura?¿Y si descompongo por tecnología? En mi experiencia estas alternativas que expone y organiza en el libro son muy valiosas.

  4. Integración: aquí habla sobre cómo se van a comunicar los microservicios. No es algo sencillo cuando hablamos de microservicios porque podemos estar tomando decisiones que inconscientemente nos van a condicionar en el futuro… ¿usamos REST, SOAP, Protocol Buffers, Event-driven? Si tenemos una sola base de datos compartida por varios microservicios… ¿Y si lo que uno escribe en una tabla el otro lo lee, estableciendo un canal de comunicación a través de la base de datos o sistema de caché? También introduce el concepto de comunicación asíncrona (eventos), y de las diferencias entre orquestar microservicios(un único director manda sobre los demás), frente a coreografía (cada uno sabe qué hacer cuando sucede un evento). Otro aspecto importantísimo que trata es que no es lo mismo una llamada en la misma máquina y memoria que una llamada de red… tiene repercusiones que no verás hasta que comience a fallar en producción si no tienes cuidado :). El versionado de microservicios o si compartimos o no código a través de librerías son otros de los conceptos que se tratan, junto con el diseño de API y su composición.

  5. Dividiendo el Monolito: los microservicios suelen surgir frente a los problemas de los monolitos, así que el caso clásico suele ser crear una nueva arquitectura basada en microservicios que sustituya a un monolito que está funcionando con problemas. Comienza dando las razones por las que deberíamos (o no) dividir un monolito: ritmo de los cambios, para reflejar la estructura del equipo, seguridad, posibilidad de usar tecnologías apropiadas por dominio… Posteriormente nos presenta unos consejos generales sobre problemas que se han encontrado: ¿Cómo dividimos una única base de datos en múltiples base de datos, una por microservicio? También otro aspecto importante es el concepto de "transacción": en un monolito es sencillo hacer transacciones.. pero ¿qué pasa cuando atañen a varios microservicios comunicados por red? Pasan a ser transacciones distribuídas y ese tipo de cosas no suele acabar bien. Finalmente trata el impacto en los sistema de reporting, y cómo se debería tratar en el mundo microserviciado para reunir toda la información y que reflejase el comportamiento de un sólo sistema.

  6. Despliegue: ahora hay decenas y quizá centenas de microservicios que deben ser tratados de forma individual frente a un único despliegue del monolito. Tiene sus ventajas de adaptación al cambio, pero la complejidad es exponencial, por lo que es vital la automatización en la diferentes fases: integración continua, sistema de aprovisionamiento, Ansible, Puppet, AWS… También discute ciertas estrategias como varios microservicios por host, uno por host o el uso de contenedores como Docker, Kubernetes.

  7. Testing: las pruebas tampoco se quedan fuera de los cambios que se producen por los microservicios. Además de los clásicos test unitarios de los desarrolladores, y test de integración y end-to-end, se hace necesario testear la interacción de los microservicios con otros elementos (inlcuyendo otros microservicios) a través de Consumer-Driven test. Introduce la lógica detrás de este concepto y propone algunas herramientas como Pact.

  8. Monitorización: a día de hoy están apareciendo ligados a los microservicios nuevas tecnologías que están dando la importancia a la monitorización que se merece, tanto monitorización técnica como de conceptos de negocio. Desde el punto de vista técnico, con microservicios todo se hace mucho más complejo: encontrar un problema en un monolito es relativamente sencillo porque está concentrado en un sólo código en ejecución, pero con microservicios son múltiples los códigos en ejecución. Se hace necesario algo más “inteligente” que permita navegar en un mar de datos en búsqueda de los problemas o el estado del sistema. Así introduce técnicas para facilitar la relación de la información entre microservicios como dotar de un identificador único a las acciones del usuario y que sea compartido por cada microservicios, o introducir nuevos sistemas como la suite Elastic Search+Logstash+Kibana (ELK).

  9. Seguridad: donde explica las peculiaridades que hay ahora en microservicios, haciendo necesario centralizar en un sistema de single-sign-on todo el control de acceso: OAuth2, OpenId, SAML, Certificados roles… son algunos de los conceptos que introduce en este tema junto con nuevas visiones: cuando un microservicio llama a otro ¿cómo se autentica? También trata de temas generales de sentido común.

  10. Ley de Conway, que viene a decir algo así como que el diseño de los sistemas software al final acaban pareciéndose al diseño de los departamentos que los creó. Expone diferentes casos y cómo se deberían organizar los desarrolladores para desarrollar microservicios: ¿Cada microservicio debe ser responsabilidad de un equipo?¿Si tengo dos equipos saldrán dos microservicios? Como nota curiosa indica que Netflix organizó a sus desarrolladores del modo en que querían que su arquitectura se desarrollase.

  11. Microservicios a escala: otro capítulo en el que reflexiona sobre su experiencia con microservicios y que seguramente te sea muy valioso cuando trabajes en este tipo de entornos. Todo está muy bien en ejemplos teóricos, o cuando empiezas y tienes 5 microservicios… pero ¿qué sucede cuando las cosas empiezan a crecer mucho?¿Cuando tengas 80 microservicios y una carga alta? ¿Qué pasa con los tiempos de respuesta?¿Y de puesta en producción?… También trata aspectos como: ¿Cómo empiezan a fallar los sistemas de microservicios? En general suelen degradarse poco a poco, y es complicado de tratar y de detectar: recuerda que todo lo que antes estaba centralizado, ahora está distribuído. Introduce diferentes conceptos para hacer nuestro sistema "Antifragil" (es decir, que se adapte a los cambios), como por ejemplo Circuit Breakers y los Bulkheads, o las operaciones idempotentes para protegernos frente a circunstancias adversas. También trata de forma superficial cómo escalar un sistema de microservicios: clústers de microservicios, qué hacer con la base de datos o cuál elegir (NoSQL, autoescalado, teorema CAP), CQRS, sistemas de caché, o descubrimiento de servicios automático (Eureka, Consul…)

  12. Juntando todo: típico capítulo final de resumen con los principios básicos del libro.

Como puedes comprobar, si has tenido la paciencia de leer mi "resumen", se trata de una serie de directrices y de puntos clave en la arquitectura de microservicios. Es más un relato de la experiencia, una guía de viaje en el desarrollo de una aplicación, con un mapa que nos indica los peligros a los que nos enfrentamos y qué armas tenemos que utilizar. El cómo se utilizan en detalle quedan para libros y tutoriales específicos.

En definitiva, "nadie escarmienta en carne ajena", pero está bien que leas al menos una vez este libro para que sepas a lo que te enfrentas cuando comiences tu proyecto de microservicios, o al menos tengas argumentos para negarte a emplearlos cuando no proceda.

ps:¡Insisto! No esperes una guía para ponerte a desarrollar microservicios desde un proyecto de Eclipse nuevo. Para eso está Spring Boot y otros libros y tutoriales más prácticos y técnicos.

Aquí te dejo el enlace por si lo quieres comprar en Amazon:

Codelobster PHP Edition – Fully functional Joomla IDE

$
0
0

Professional version of the Codelobster PHP IDE has in its structure an excellent tool – the unit for working with the CMS Joomla.

Of course, we can edit PHP or HTML files by using any editor tool, but built-in unit of Joomla can extend the features of the program and transforms Codelobster into a fully functional Joomla IDE.

Usually development of the site by Joomla starts from a rather boring, but the necessary steps:

  • Installation of content management system, for example, on a locally running servers.
  • Creating and configuration the project in your IDE.

And only then, when a new empty version of CMS is installed, you can create themes for your site or develop a module.

Codelobster PHP IDE allows to combine all the stages of the project deployment and to make preparations for the work directly in the IDE.


Getting Started – the creation of the Joomla project


Run local HTTP Apache server and MySQL data base, for that I usually use the XAMPP package – very convenient set of programs for web-developers.

Everything is ok, then you can work Codelobster and nothing to worry about, we even will not have to download CMS – program will take care about everything.

So, run the IDE go to the main menu, and then move by the next sequence of items: “File” -> “New” -> “Project” to run a new project wizard.

Using the dialog box, which has appeared after previous step, specify the type of project, “Create an empty Joomla site”.

First of all, we write the project name “joomla-site” and choose the path to it. The project is located in the folder “htdocs” on the local server.


Note the item “Create a new project in the folder”. What was the result? Let’s examine the dialog box carefully.

The path to the project “D:\xampp-portable\htdocs\” – in this directory on the local server will be created a folder to place all the source files of the project.

The last line – URL of our new website “http://localhost/joomla-site/”, we will use it for testing and debugging.


Then click “Ok”. Next we will configure the new Joomla project.


Then the first dialog window of IDE immediately invites us to choose Joomla version. Make your choice and click “Next”.


In the next step the connection parameters to the database must be entered.

We enter the name of the database, what the IDE automatically creates for us, and also enter the user name and password to connect to MySQL.


Go to the next dialog. In this case, the advanced settings is no need to change, as the database is also running locally.


Follow further. Wizard prompts you for administrator account data of the new site.

Specify the user name, password, email address and press “Next”.


The next dialog lets us to configure FTP access. In this case, is not necessary to configure it, since we work with server on a desktop and have full access to the source files. Skip this step and go on.


At the final stage we will think up the name of our website and will enter it in the first string. Leave the other settings as default.

Click “Finish”.


Next, the wizard downloads the latest version of Joomla from the official site, makes installation and configuring of the project and opens it in a programming environment.


To begin editing the project, go to the folder “templates”, open the folder “beez3” -> “components.php”.

For quick tips on functions, you just should hover over the item of interest.


To use the code completion, you should move the cursor to the desired position and press Ctrl + Space.

Thus, we have quick access to all variables and functions of Joomla, it will speed up and make more comfortable the process of development.

Right in the progress, without distraction, and without leaving the program, you can use the contextual and dynamic help, if you need to clarify the Joomla API syntax.

So, let’s boldly proceed to the realization of our ideas, Codelobster PHP IDE provides wide opportunities for creating professional sites on Joomla.

Codelobster PHP IDE also has modules for other popular frameworks and content management systems: Drupal, WordPress, Smarty, Twig, JQuery, CodeIgniter, CakePHP, Laravel, Symfony, Yii, Laravel, AngularJS.

Los mejores libros para aprender UX

$
0
0

Cada vez que me he iniciado en una nueva disciplina, ha sido abrumadora la cantidad de documentación que había al respecto, y me ha resultado complicado encontrar un sitio por dónde empezar.

En el caso de la UX, quería tener una visión general antes de sumergirme en cada una de las fases, técnicas y metodologías de las que se nutre. Después de visitar decenas de blogs (UxDesign, UxMastery, Smashing Magazine, UxBooth…) y consultar a algunos expertos en la materia, terminé decidiéndome por los cinco libros que os muestro a continuación.




No me hagas pensar

Steve Krug

Este libro es una primera toma de contacto con la usabilidad web. A partir de ejemplos sencillos con sites tan conocidos como Amazon, y con un sentido aplastante de la lógica, Krug transmite la importancia de centrar el diseño web en el usuario.

El libro es sencillo y bastante ameno, ideal para empezar con conceptos fáciles y quedarnos con ganas de seguir aprendiendo.







La psicología de los objetos cotidianos

Donald Norman

Hay decenas de listas de libros de UX en internet y me sorprende que casi ninguna de ellas incluya algo de Donald Norman, el hombre que acuñó el término “User Experience” cuando todo el mundo hablaba de HCI (Human Computer Interaction). También fue el primero en hablar de la importancia del diseño centrado en el usuario.

En este libro describe, en clave de humor, la importancia del diseño y de la affordance de los objetos.







The elements of user experience

Jesse James Garrett

Después de las dos lecturas anteriores, ya estamos preparados para algo un poco más complejo. A nivel teórico, diría que este es el libro más importante de los cinco.

Contiene el famoso diagrama de los elementos de la experiencia de usuario, que van desde las necesidades del usuario hasta el diseño visual de la aplicación, y una detallada explicación de cada uno de esos elementos.







Measuring the User Experience: Collecting, Analyzing, and Presenting Usability Metrics (Interactive Technologies)

Thomas Tullis y William Albert

Estos dos autores recopilan decenas de métricas y muestran cuáles son los mejores métodos para analizar y presentar datos. Independientemente de la tecnología o del producto/servicio que queramos medir, proporcionan los criterios necesarios para seleccionar la métrica más apropiada. También ilustran el éxito de determinadas organizaciones que han sabido medir correctamente la UX.

Este libro permitirá que defendamos nuestro trabajo de manera cuantitativa ante los stakeholders, demostrando con números la importancia y el impacto de lo que hacemos.







Lean UX: Applying Lean Principles to Improve User Experience

Jeff Gothelf y Josh Seiden

Con Lean UX aprenderás a trabajar rápido, a prescindir de lo innecesario en tus proyectos y a mejorar la eficiencia del equipo. Contiene casos prácticos de Lean UX en agencias y empresas, con consejos para implementar los principios Lean en el diseño de la UX. Si quieres saber más sobre este libro, te recomiendo que leas la reseña que hizo Roberto Canales.

Hasta aquí mi lista de iniciación a la UX, con la que sin duda obtendrás una visión global de la disciplina, de los elementos que la forman y de la importancia de diseñar pensando en el usuario. Además aprenderás a medir y presentar tu trabajo, te acercarás a una metodología eficiente para tu equipo y tu proyecto.

Descubre el nuevo libro de Roberto Canales

$
0
0

El pasado 6 de abril, el tercer libro de Roberto Canales vio la luz “Conversaciones con CEOs y CIOs sobre Transformación Digital y Metodologías Ágiles”.

Las respuestas a estas preguntas son las que puedes encontrar en su libro:

  • ¿Sabes si estás definiendo proyectos de futuro o de pasado?
  • ¿Tu negocio demanda lanzar productos más rápidamente?
  • ¿Podrías hacer más proyectos en paralelo?
  • La imagen de calidad que proyectas, ¿te empieza a preocupar?

Roberto plasma en este libro muchas de las lecciones que ha ido aprendiendo a lo largo de los años en los procesos de implantación de técnicas de Agile junto con la Transformación Digital que estamos viviendo.

Este libro escrito como un manual, a través de una conversación, pretende mostrar cómo la Transformación Digital y la cultura Lean han venido para quedarse mucho tiempo y proporcionar nuevos modelos de trabajo que suponen un cambio muy brusco a nivel organizacional.

Ya puedes descargarte el libro en este enlace y empaparte de los tópicos como Design Thinking, UX, Lean, Agilismo, Coach y muchos más conceptos dentro de las organizaciones. #ConversacionesconCEOsyCIOs





También puedes descargarte gratis el libro “Las Reglas no escritas para triunfar en la empresa” aquí.

Viewing all 996 articles
Browse latest View live