Weblogs Código

Picando Código

Rust Latam Montevideo 2019 – Conferencia latinoamericana de Rust

noviembre 20, 2018 09:32

Santiago Pastorino anunció en el foro de Rust información sobre la conferencia Rust Latam 2019.

Rust Latam Montevideo

La primera edición de Rust Latam se va a realizar los días 29 y 30 de marzo de 2019 en Montevideo, Uruguay. Del sitio (traducción mía):

La Conferencia Rust Latam es el evento líder en América Latina por y para el movimiento Rust, y una de las oportunidades anuales más grandes de networking en la comunidad local de Rust. Esta conferencia de un fin de semana con sesiones interactivas, actividades prácticas, y charlas junta a más de 200 defensores, desarrolladores y entusiastas del lenguaje de programación Rust de todo el mundo. En marzo, la gente e ideas de la comunidad latinoamericana de Rust saltará de la pantalla para aprender, discutir, debatir y enfrentar Rust en persona. Nuestra primera conferencia Rust Latam anual empieza en Montevideo.

En marzo, tendremos ese mismo espíritu vehemente de las reuniones locales. También tendremos a la gente y recursos para hacer un gran impacto. Una conferncia de dos días – series de talleres y oradores – con decenas de expertos y principiantes significa que podemos avanzar el pensamiento en cómo tú y tu equipo pueden usar características de Rust para crear una amplia gama de nuevas aplicaicones de software, como motores de juegos, sistemas operativos, sistemas de archivos, componentes de navegadores web y motores de simulación para realidad virtual. Podemos forjar nuevas sociedades, podemos entrenar a los programadores del mañana y podemos alimentar a la construcción comunitaria a través de los países de América Latina.

Ya se confirmaron dos oradores: Niko Matsakis del Rust Core Team y Boats de Rust Language Team para las charlas keynote. Pero está abierto el llamado a charlas. Hay 7 espacios disponibles para charlas de hasta 35 minutos. La organización de la conferencia cubre gastos de viaje (vuelos + hotel) por hasta USD 1.300, pero también aceptan que una compañía cubra los costos como espónsor bronce, y hay más opciones según cada caso.

Las entradas “supporter” cuestan USD 100, un tipo de entrada para ayudar a que la conferencia se lleve a cabo de la mejor manera posible. Pero también van a haber entradas regulares a USD 50.

Pueden visitar el sitio web por más información, comprar su entrada, o seguir a la conferencia en Twitter para enterarse de las novedades.

 

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

Fixed Buffer

Como instalar .NetCore en Linux

noviembre 20, 2018 09:00

.NetCore en Linux

Después de tantas entradas hablando sobre .NetCore y lo que me gusta utilizar una herramienta multiplataforma, hoy vamos a ver como instalar el SDK de .NetCore en Linux. Esto puede parecer algo complicado, pero en realidad es muy muy fácil. En este caso, vamos a utilizar para el ejemplo una distribución Debian 9.5.

Instalando .NetCore en Linux

En primer lugar, es necesario instalar el paquete “apt-transport-https”, el cual nos permite conectar con los repositorios de Microsoft:

sudo apt-get install apt-transport-https

Después, vamos a registrar las claves de Microsoft dentro de el registro de claves seguras, de cara a que no nos de advertencias al utilizar HTTPS los repositorios, para ello, escribimos las siguientes instrucciones en la terminal (En caso de no utilizar Debian sino otra distro, puedes consultar las instrucciones aquí):

sudo wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg –dearmor > microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
sudo wget -q https://packages.microsoft.com/config/debian/9/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list

Una vez hecho esto, ya hemos añadido los repositorios a nuestra maquina, con lo cual, solo nos queda actualizar la lista e instalar el SDK:

sudo apt-get update
sudo apt-get install dotnet-sdk-2.1

Con estos sencillos pasos, ya tenemos el SDK instalado en nuestro equipo con Debian, y podemos empezar a trabajar con el.

Probando el SDK

Una vez tenemos el SDK instalado, vamos a comprobar que todo funciona correctamente, para ello, vamos a descargar el proyecto de las entrada Venciendo el miedo a las pruebas unitarias en .Net , pero también podríamos hacerla con Rompiendo limites: Mocking en las Pruebas Unitarias .Net o cualquiera de las que usan .NetCore.

*NOTA: Voy a asumir que no todo el mundo usa git, por los que utilizaré los enlaces https, en caso de hacerlo con git, simplemente seria necesario hacer un “git clone” en vez de una descarga wget.

Vamos con el primero, lo primero, es descargarlo, descomprimirlo y entrar al proyecto:

wget https://github.com/JorTurFer/PostPruebasUnitariasCore/archive/master.zip
unzip -a master.zip
cd PostPruebasUnitariasCore-master

Una vez dentro de la carpeta, vamos a construir el proyecto con:

dotnet build *.sln

Si todo va bien, veremos algo como esto:

build result

Y vamos a comprobar que las pruebas unitarias se ejecutan correctamente accediendo a ellas y ejecutándolas con:

cd PruebasUnitarias
dotnet test *.csproj

Lo que nos debería mostrar algo como esto:

test result

En próximas entradas, mostraré como instalar nuestros programas como servicio .NetCore en Linux.

**La entrada Como instalar .NetCore en Linux se publicó primero en Fixed Buffer.**

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

Variable not found

Vistas precompiladas y editables en ASP.NET Core MVC 2.1: lo mejor de los dos mundos

noviembre 20, 2018 07:55

ASP.NET Core MVCSeguramente habréis notado que en proyectos ASP.NET Core 2.1 la compilación de las vistas funciona de forma ligeramente diferente a como se hacía en versiones anteriores. Hasta ahora las vistas sólo eran compiladas al publicar el proyecto pero, a partir de esta versión, este proceso se realizará antes, durante la compilación.

En este artículo vamos a ver cómo aprovechar las ventajas de la precompilación, y al mismo tiempo mantener la flexibilidad que nos ofrece la compilación en tiempo de ejecución que tradicionalmente hemos disfrutado en proyectos ASP.NET y ASP.NET Core.

A partir de ASP.NET Core 2.1, cuando compilemos normalmente nuestros proyectos, en el directorio /bin encontraremos, además de los archivos habituales, un ensamblado llamado [MyProjectName].Views.dll con el resultado de la compilación de las vistas Razor:

Directorio de salida de compilación, con el ensamblado de las vistas

Esto es buena cosa, sin duda. El hecho de compilar las vistas hará posible que detectemos antes los errores de programación, y normalmente los tiempos de compilación son tan rápidos que ni notaremos que ese proceso se está realizando. Además, al desplegar el proyecto, el arranque de la aplicación será más rápido porque no será necesario compilar las vistas al vuelo cuando los usuarios accedan a ellas.

Por otra parte, dado que el resultado de la compilación de las vistas ya lo tenemos en la carpeta de binarios en forma de ensamblado, al publicar el proyecto no se incluirá el contenido de las carpetas /Views, /Pages u otras relacionadas con las plantillas Razor:
Carpeta de resultados de publicación, donde no aparecen las carpetas de vistas

Gracias a esto, el paquete de publicación será más pequeño y las operaciones de despliegue serán más rápidas, porque hay muchos menos archivos que gestionar.

Sin embargo, al no existir en el servidor, dejaremos de tener la capacidad de actualizar directamente los archivos Razor, una característica muy socorrida cuando detectamos pequeños fallos o queremos introducir mejoras que no requieren un despliegue completo.

Lo que realmente estaría bien sería tener lo mejor de estos dos mundos, es decir, disponer al mismo tiempo de las ventajas de publicar las vistas precompiladas, pero a la vez mantener la posibilidad de actualizarlas directamente en el servidor y que éstas fueran recompiladas automáticamente sin necesidad de subir todo el proyecto.

Para conseguirlo, necesitaremos dos cosas:
  • Primero, hacer que en el paquete de publicación se incluyan los archivos de vista.
     
  • Segundo, hacer que las vistas puedan sean recompiladas de forma automática cuando se detecten cambios en ellas.

1. Incluir las vistas en el paquete de publicación

Para que las vistas de una aplicación MVC sean incluidas a la hora de generar el paquete de publicación, basta con añadir el siguiente código al archivo .csproj de nuestro proyecto:
<ItemGroup>
<_CustomFiles Include="$(MSBuildProjectDirectory)/views/**/*" />
<DotnetPublishFiles Include="@(_CustomFiles)">
<DestinationRelativePath>views/%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</DotnetPublishFiles>
</ItemGroup>
Si además queremos incluir las páginas Razor, presentes por convención en la carpeta "/Pages" del proyecto, debemos añadir adicionalmente el siguiente bloque:
<ItemGroup>
<_CustomFiles Include="$(MSBuildProjectDirectory)/pages/**/*" />
<DotnetPublishFiles Include="@(_CustomFiles)">
<DestinationRelativePath>pages/%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
</DotnetPublishFiles>
</ItemGroup>
Obviamente, deberíamos incluir de la misma forma otras carpetas de archivos Razor de nuestro proyecto, por ejemplo si estamos utilizando áreas, o si hemos modificado las convenciones de ubicación.
Al completar la publicación, si ejecutamos nuestro proyecto veremos que todo funciona correctamente, pero si intentamos modificar alguno de los archivos Razor incluidos en el despliegue y accedemos a él desde el navegador, obtendremos un error como el siguiente:

Error de compilación al modificar una vista en tiempo de ejecución

Este error indica que el servidor no dispone de los componentes necesarios para compilar las vistas, veamos cómo solucionarlo.

2. Hacer que las vistas puedan ser compiladas al vuelo

De nuevo, la forma de solucionarlo va a ser muy sencilla. Basta con añadir la propiedad <CopyRefAssembliesToPublishDirectory> con el valor "true" en el archivo del proyecto:
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
</PropertyGroup>
Al hacerlo veremos que nuestro paquete de publicación crece considerablemente (en una aplicación casi vacía con la que estoy probando, pasa de 6 a 33MB), pero ya se estarán incluyendo todos esos componentes que faltaban para hacer la compilación posible.

¡Y esto es todo! Espero que el truco os resulte útil para aplicarlo en vuestros proyectos.

Publicado en Variable not found.

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

Variable not found

Enlaces interesantes 338

noviembre 19, 2018 07:55

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

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

    Web / HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Xamarin

    Otros

    Publicado en: www.variablenotfound.com.

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

    Picando Código

    R-Ladies Madrid y NoLegalTech: Legal Analytics para democratizar el acceso a las leyes

    noviembre 17, 2018 08:10

    Les dejo la información del próximo evento de R-Ladies Madrid y NoLegalTech en Madrid, a realizarse el martes 20 de noviembre. Todo lo que ayude a democratizar el acceso a información del estado y le de poder a la gente sobre sus gobernantes es bueno:

     R-Ladies Madrid y NoLegalTech: Legal Analytics para democratizar el acceso a las leyes

    A la democracia le ha llegado la hora tecnológica

    El Boletín Oficial del Estado (BOE) es el diario oficial para la publicación de las normas y las leyes en el territorio español. Recogida en el artículo 9 de la Constitución, la publicidad de las normas es un principio básico de seguridad jurídica de los ciudadanos. Solo, pues, si los ciudadanos conocen las normas pueden ejercer sus derechos y deberes dentro de la sociedad.

    Si bien este principio nace como esencial en una sociedad democrática, hoy en día la cantidad de normas aplicables en tiempo y lugar se ha multiplicado hasta tal punto que ni la profesión del abogado puede llegar a alcanzarlas en su plenitud. Desde las directivas y reglamentos que dicta la Unión Europea, hasta las diversas normas que pueden dictar tanto las Cortes Generales como el propio Gobierno (Leyes Orgánicas, Leyes Ordinarias, Reales Decretos…) el puzzle del sistema legal español llega a ser incomprensible bajo los sistemas tradicionales de ejercer la profesión.

    En los últimos años el Estado de Derecho, empleando una estructura burocrática alejada del ciudadano y un lenguaje ininteligible e incluso enigmático, se ha ido alejando cada vez más del objetivo principal de la sociedad: la convivencia normativa y la sujeción de los poderes públicos a la norma.

    Sin embargo, con la irrupción de la tecnología en nuestras vidas y la generación de datos a lo largo y ancho del globo, el estudio y el aterrizaje de las normas al ciudadano ya es posible.

    Habiéndose generado en los dos últimos años más datos que en toda la historia de la humanidad, a través del machine learning y el entrenamiento de textos jurídicos masivos (leyes, normas, disposiciones…), se puede crear lo que por primera vez en España puede traer poder legislativo: conocimiento real de las leyes a través de la ciencia y al margen de los intereses políticos. Hoy en día podemos alcanzar a conocer la normativa vigente española (tan solo conocer qué normas y qué preceptos están en vigor en todas las ramas del Derecho ya es un paso adelante), detectar incompatibilidades normativas… e incluso comprobar, con un análisis social, si una ley es efectiva en el tiempo y lugar en que se ha aplicado.

    El próximo 20 de noviembre, bajo el paraguas de la comunidad R-Ladies Madrid y NoLegalTech, celebramos el primer evento Legal Analytics, donde tendrán cabida todas estas cuestiones.

    Programa

    18:00 Presentación
    18.30 Bárbara Román (Nolegaltech): charla “Panorama legal y datos”
    19.30 Mesa redonda moderada por Bárbara Román: Gloria Sánchez (Banco Santander), Sara Molina (Marketingnize) y Elen Irazábal (R-Law Geeks)
    20.30 Presentación del grupo R-Law Geeks. Será una presentación tanto jurídica como técnica, como un ejemplo de legal analytics.
    21.15 Preguntas
    21.30 Despedida

    Más información e inscripciones

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

    Blog Bitix

    El agotamiento de direcciones IPv4 y el protocolo de internet IPv6

    noviembre 16, 2018 06:00

    Las direcciones IP de IPv4 que identifican a las computadoras de forma uniquivoca para comunicarse entre si son limitadas y ya se han agotado. Aún siendo casi 4300 millones con el auge de internet en la última década y la que se espera en los próximos años con los dispositivos móviles y los elementos conectados del internet de las cosas la presión sobre el número de direcciones IP será aún mayor. Hasta ahora varias medidas paliativas han permitido mitigar la presión y extender la disponibilidad de direcciones IP. Uno de los principales motivos del desarrollo del protocolo IPv6 es aumentar en varios órdenes de magnitud el número de las direcciones IP disponibles, siendo en este protocolo direcciones de 128 bits en vez de 32 como IPv4. Sin embargo, hay varios motivos por los que aún IPv6 no se está usando ya de forma masiva.

    El protocolo mediante el cual las computadoras intercambian datos mayoritariamente es el protocolo IPv4. Este protocolo identifica a cada máquina conectada a internet mediante una dirección IP que es un número de 32 bits normalmente escrito en cuatro grupos decimales para ser más fácilmente recordados por los humanos, por ejemplo, 192.169.1.56. La dirección IP pública que tenemos asignada la podemos obtener de páginas como cual-es-mi-ip. Un número binario de 32 bits da un máximo teórico de 4.294.967.296 posibles direcciones IP. En la época en que fué desarrollado las primeras redes de computadoras este número parecía adecuado y suficiente, sin embargo, con el éxito de internet y el crecimiento del número de dispositivos conectados el número de direcciones IP libres ya se ha agotado.

    11111111.11111111.11111111.11111111
    255.255.255.255
    127.0.0.1 (dirección loopback)

    El problema de agotamiento de direcciones IPv4 no es nuevo y ya desde hace alguna década se estaba advirtiendo e implementando medidas en el protocolo para retrasar durante algún tiempo el llegar a agotar las direcciones. Algunas de estas medidas son CIDR y NAT. CIDR permite aprovechar mejor o no desperdiciar tantas direcciones IP en los grupos de direcciones estableciendo una máscara para determinar cuál es la parte de dirección de red y cual es la dirección del host dentro de esa red, con CIDR las direcciones IP se identifican con el siguiente formato 255.255.255.255 /8 donde el último número determina que número de bits corresponden a la dirección de red y que parte a los hosts de esa red, en este caso 24 bits corresponden a la red y 8 bits a identificar al host hasta llegar a 32 bits. NAT permite a los dispositivos de una red compartir la misma dirección IP al conectarse a otras redes o internet, de esta forma en vez de requerirse una dirección pública por cada dispositivo de la red solo es necesaria una para todos los equipos, en los hogares es el enrutador o router el dispositivo el que hace NAT y el que tiene la dirección IP pública asignada. NAT no está exento de inconvenientes ya que los equipos externos a una red no pueden contactar directamente a los equipos internos de una red en la que se hace NAT, para ello hay que usar activación o reenvío de puertos que los routers ofrecen en su panel de administración. Incluso algunos proveedores de internet están implementado un NAT a nivel de proveedor conocido como CG-NAT para usar una o unas pocas direcciones IP para todos los usuarios del proveedor. A este nivel está llegando la escasez de direcciones IP del protocolo IPv4.

    Panel de un router para hacer NAT

    La solución a largo plazo para la escasez de direcciones IP del protocolo IPv4 es usar una nueva versión de protocolo conocida como IPv6. En IPv6 las direcciones IP son números de 128 bits dando lugar a 2128, aproximadamente 3.4×1038 o 340.282.366.920.938.463.463.374.607.431.768.211.456 combinaciones teóricas posibles, esto son unas 7.9×1028 veces más direcciones IP posibles que en IPv4. Las direcciones IPv6 se escriben en ocho grupos de cuatro números hexadecimales, pudiéndose hacer una contracción omitiendo grupos de ceros contiguos con ::. El enorme número de direcciones de IPv6 hace innecesaria el NAT y CG-NAT y los problemas que estos ocasionan. Estas son unas comparaciones:

    • La tierra tiene 4500 millones de años, si se asignan direcciones IPv6 al ritmo de 1000 millones por segundo desde que se formó la tierra hast ahora se usado una trillonésima parte del espacio de direcciones.
    • Se puede asignar 4 trillones de direcciones por cada uno de los 510 billones de metros cuadrados de la tierra.

    11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111:11111111 (no es un error de maquetación, son los 128 bits de una dirección IPv6)
    FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
    ::1 (dirección loopback)

    IPv6 ha sido desarrollado hace varios lustros sin embargo su adopción está siendo tan lenta ya que los proveedores de internet y servidores de internet necesitan soportar el nuevo protocolo, los sistemas operativos y equipos de red ya hace tiempo que fueron adaptados y cualquiera que se compre actualmente ya lo soportará. Usar IPv6 puede ocasionar que algunos servidores que no soporten este protocolo aún no puedan ser accedidos. El servicio de nombres de internet ya fue adaptado a IPv6 y seguramente los servidores de los servicios de empresas importantes como Google, Amazon, Facebook, Microsoft o Apple también lo soportan.

    Algunas otras ventajas de IPv6 sobre IPv4 son:

    • Procesado simplificado en los enrutadores: la cabecera de los paquetes de IPv6 aunque son más grandes son más simples. Los paquetes no son fragmentados en el tránsito por los routers sino por el equipo emisor. Se ha eliminado el checksum de la cabecera que ahora se considera garantizado a nivel de capa de enlace de modo que los routers no deben recalcular cuando los campos de la cabecera cambian.
    • Opciones extensibles: la cabecera de los paquetes tienen una extensión de 40 octetos con opciones adicionales implementadas como extensiones. Esto posibilita extender el protocolo en el futuro sin afectar a el núcleo de la estructura del paquete.
    • Jumbograms: IPv4 limita el tamaño de los paquetes a una carga de 65.535 (2^16−1) octetos (64 KiB). IPv6 opcionalmente puede manejar paquetes de hasta 4.294,967.295 (2^32−1) octetos (4 GiB).
    • Privacidad: ofrece mecanismos para que la dirección IP que permite a cualesquiera dos equipos comunicarse no sea usado potencialmente para el rastreo.
    • Multicast: transmitir un paquete a múltiples destinos en una única operación de envío, en IPv4 era opcional pero en IPv6 está dentro de la especificación.

    Algunos motivos por los que IPv6 no se ha adoptado aún son:

    • Es necesario tiempo para implementar el estándar: IPv6 fué publicado en 1998 pero llevó tiempo implementarlo y probarlo pasando varios años hasta que fué incorporado en los equipos. Los operadores necesitan tiempo para aprender cómo trabajar con esta nueva tecnología.
    • Falta de claro beneficio: los usuarios no experimentarán una mejora de rendimiento o redes más fiables. Implementarlo requiere esfuerzo económico y humano. Por ejemplo, si hay software o hardware que no soporta IPv6 es necesario reemplazarlo.
    • Falta de incentivo: sin demanda de los usuarios y sin un beneficio claro los proveedores de software no han realizado el cambio.
    • Dependencia sobre IPv4: la transición a IPv6 es un esfuerzo distribuido en la que intervienen múltiples organizaciones. Por un tiempo IPv4 y IPv6 coexistirán. Algunos programas no soportan el nuevo protocolo.

    Para añadir el soporte de IPv6 en un servidor web hay que usar la siguiente dirección IP en Apache y Nginx para escuchar peticiones en todas las interfaces de red.

    # Apache
    Listen [::]:80

    # Nginx
    listen [::]:80 default_server;

    En una máquina con GNU/Linux se puede ver la dirección IPv6 asignada a cada interfaz de red con el comando ip -6 addr show.

    Amazon soporta en algunas regiones IPv6 con lo que si usamos este servicio de computación en la nube se puede usar el nuevo protocolo. Otros actores importantes de la nube también lo soportan como Digital Ocean (1) o Linode (2). En las entidades de registro de dominios basta con añadir un registro AAAA a las zonas DNS.

    Los siguientes dos artículos son interesantes, hablan de la evolución de las direcciones de internet y del agotamiento de direcciones, el estado de despliegue y por qué aún no se ha adoptado de forma masiva:

    Aún queda tiempo hasta que IPv6 sea el protocolo mayoritariamente utilizado, cuanta más escasez de direcciones IPv4 haya más necesidad de migrar habrá pero para ello el soporte en los servidores, hardware y software es necesario. En algunos países el tráfico sobre IPv6 se ha doblado respecto a otros años, en otros países aún no ha sido desplegado de forma significativa. ¿Llegará un momento en que Google tenga en cuenta para el SEO aquellos sitios que soporten IPv6 como ya hace con HTTPS en vez de HTTP?

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

    Variable not found

    Localizar errores de validación del binding en ASP.NET Core MVC

    noviembre 16, 2018 03:27

    ASP.NET Core MVCEn el framework ASP.NET Core MVC es muy sencillo establecer los mensajes de error de validación de campos utilizando propiedades de las data annotations, como en el siguiente ejemplo:
    namespace LocalizationDemo.ViewModels
    {
    public class PersonViewModel
    {
    [Required(ErrorMessage ="The name is required")]
    public string Name { get; set; }
    }
    }
    Incluso es bastante fácil hacer que este texto aparezca traducido atendiendo a la cultura del hilo actual. Para ello, simplemente debemos configurar los servicios de localización apropiadamente, e indicar en la propiedad ErrorMessage la clave del mensaje de error en el archivo RESX asociado a la clase:
    // En Startup.cs:
    public void ConfigureServices()
    {
    ...
    services.AddLocalization(opt=>opt.ResourcesPath="Resources");
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
    .AddViewLocalization()
    .AddDataAnnotationsLocalization(); // Esta línea añade la localización
    // de data annotations
    }

    // En el view model:
    namespace LocalizationDemo.ViewModels
    {
    public class PersonViewModel
    {
    // El mensaje de error está definido
    // en Resources/ViewModels.PersonViewModel.resx
    [Required(ErrorMessage ="NameIsRequired")]
    public string Name { get; set; }
    }
    }
    Esto es así de fácil para las validaciones que declaramos de forma explícita en nuestro código, mediante atributos de anotaciones de datos. Sin embargo, existen otro tipo de validaciones que se realizan de forma implícita (por ejemplo, cuando en un campo entero introducimos un valor no numérico) y cuyos mensajes de error no son tan sencillos de establecer.

    Por ejemplo, observad el siguiente view model:
    public class PersonViewModel
    {
    [Required(ErrorMessage = "NameIsRequired")]
    [Display(Name="Name")]
    public string Name { get; set; }

    [Display(Name="Height")]
    public int Height { get; set; }

    [Display(Name="Birthdate")]
    public DateTime Birthdate { get; set; }
    }
    Las propiedades Height y Birthdate son de tipo valor y por tanto intrínsecamente obligatorias, por lo que el framework mostrará un texto de error por defecto si se dejan en blanco. Sin embargo, podemos cambiar fácilmente este mensaje de error utilizando la anotación [Required] y estableciendo en ella el parámetro ErrorMessage, tal y como hacemos con el campo Name.
    Ojo, que esto también se puede automatizar, es decir, podemos asignarle automáticamente a las propiedades de tipo valor un atributo [Required] con los mensajes ya preconfigurados, o incluso definir los textos por defecto para ese tipo de validadores. Pero para no extendernos mucho, lo veremos en este otro post: https://www.variablenotfound.com/2018/11/establecer-textos-por-defecto-y.html.
    Pero además, existen otros problemas de validación que pueden darse cuando el binder intente establecer el valor de estas propiedades a partir de los datos que llegan en la petición, por ejemplo, si el usuario introduce "abc" en el campo numérico o de fecha.

    En estos casos, por mucho que hayamos configurado el sistema de localización y todo funcione aparentemente bien, podremos encontrarnos con pantallas como la siguiente, donde los mensajes de error generados durante el binding están en el idioma por defecto (inglés), mientras que los que establecemos nosotros mediante anotaciones han sido traducidos al idioma actual:

    Formulario donde aparecen traducidos algunos errores de validación, pero otros aparecen en el idioma por defecto

    Para solucionarlo tendremos que cambiar la forma en que el binder busca los textos de estos errores :)

    ¿De dónde obtiene el binder esos mensajes de error?

    Como otros muchos mecanismos del framework, la obtención de estos mensajes se basa en proveedores, es decir, existe un componente especializado en proveer al binder de estos mensajes.

    Por defecto, el proveedor mensajes de validación para el binder se implementa en la clase DefaultModelBindingMessageProvider, cuya única instancia es depositada durante el arranque de la aplicación en la propiedad ModelBindingMessageProvider del objeto MvcOptions que configura el comportamiento del framework MVC.
    services.AddMvc(opt=>
    {
    // opt.ModelBindingMessageProvider tiene una instancia
    // de DefaultModelBindingMessageProvider
    })
    DefaultModelBindingMessageProvider dispone de métodos para establecer y obtener las factorías que generarán los textos de cada uno de los errores que el binder es capaz de detectar, que son los siguientes:
    • MissingBindRequiredValue
    • MissingKeyOrValue
    • MissingRequestBodyRequiredValue
    • ValueMustNotBeNull
    • AttemptedValueIsInvalid
    • NonPropertyAttemptedValueIsInvalid
    • UnknownValueIsInvalid
    • NonPropertyUnknownValueIsInvalid
    • ValueIsInvalid
    • ValueMustBeANumber
    • NonPropertyValueMustBeANumber
    Cada uno de estos mensajes de error dispone de un método en la clase DefaultModelBindingMessageProvider donde es posible establecer su factoría que, en la práctica, es un delegado que se encargará de retornar el texto de error para cada caso. Por ejemplo, el siguiente método de esta clase permitiría establecer el mensaje a mostrar cuando el binder encuentre un valor no válido para la propiedad que intenta establecer:
    services.AddMvc(opt=>
    {
    opt.ModelBindingMessageProvider.SetValueIsInvalidAccessor(
    value => $"El valor '{value}' no es válido"
    );
    [...] // Establecer el texto para el resto de errores
    });
    Observad que cada tipo de error soportado dispone de un método para establecer su factoría, de la forma Set{ErrorType}Accessor(), como en SetValueIsInvalidAccessor() o SetValueMustNotBeNullAccessor().

    Establecer los textos desde recursos localizados

    Visto lo anterior, si hacemos eso mismo con todos los mensajes de error soportados, y aprovechamos para personalizar o localizar el texto en cada caso, tendremos el trabajo hecho.

    Pero vamos a dar una vuelta de tuerca más: ¿qué ocurre si queremos que los textos a emplear en cada caso provengan de archivos RESX? Pues no es mucho más complicado que lo que hemos visto antes: bastará con obtener acceso al IStringLocalizer apropiado y utilizarlo para tener acceso a los recursos.

    En el siguiente bloque de código vemos cómo podríamos obtenerlos, por ejemplo, del archivo de recursos localizados "ModelBindingDefaultMessages.resx" presente el proyecto "LocalizationDemo":
    services.AddMvc(opt=>
    {
    var stringLocalizerFactory = mvcBuilder.Services
    .BuildServiceProvider().GetService<IStringLocalizerFactory>();
    var loc = stringLocalizerFactory
    .Create("ModelBindingDefaultMessages", "LocalizationDemo");

    opt.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor(
    prop => loc["MissingBindRequired", prop]);
    opt.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(
    () => loc["MissingKeyOrValue"]);
    opt.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(
    () => loc["MissingRequestBodyRequired"]);
    opt.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
    prop => loc["ValueMustNotBeNull"]);
    opt.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor(
    (value, prop) => loc["AttemptedValueIsInvalid", value, prop]);
    opt.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(
    value => loc["NonPropertyAttemptedValue", value]);
    opt.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor(
    prop => loc["UnknownValueIsInvalid", prop]);
    opt.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(
    () => loc["NonPropertyUnknownValueIsInvalid"]);
    opt.ModelBindingMessageProvider.SetValueIsInvalidAccessor(
    value => loc["ValueIsInvalid", value]);
    opt.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(
    prop => loc["ValueMustBeANumber", prop]);
    opt.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(
    ()=> loc["NonPropertyValueMustBeNumber"]);
    });
    Tras esto, podemos crear los archivos de recursos "Resources/ModelBindingDefaultMessages.resx" con los textos para cada uno de estos errores:

    Contenido del archivo de recursos
    Obviamente, para que esto funcione debemos tener registrados y configurados los servicios de localización de .NET Core para que los recursos sean tomados de la carpeta /Resources.

    Pero, ¿y no es esto demasiado código para Startup?

    Pues sí, y para mejorarlo podríamos usar llevarnos el código a un extensor de IMvcBuilder como el siguiente:
    public static class MvcOptionsExtensions
    {
    public static void ConfigureModelBindingMessages(this IMvcBuilder mvcBuilder,
    string resourceName = null, string resourceLocation = null)
    {
    mvcBuilder.Services.Configure<MvcOptions>(opt =>
    {
    // By default, the Resx file name is ModelBindingDefaultMessages.resx:
    resourceName = resourceName ?? "ModelBindingDefaultMessages";

    // By default, resources live in same assembly that the Startup class does:
    resourceLocation = resourceLocation
    ?? Assembly.GetExecutingAssembly().GetName().Name;

    var stringLocalizerFactory = mvcBuilder.Services
    .BuildServiceProvider().GetService<IStringLocalizerFactory>();
    var loc = stringLocalizerFactory.Create(resourceName, resourceLocation);

    opt.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor(
    prop => loc["MissingBindRequired", prop]);
    opt.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(
    () => loc["MissingKeyOrValue"]);
    opt.ModelBindingMessageProvider.SetMissingRequestBodyRequiredValueAccessor(
    () => loc["MissingRequestBodyRequired"]);
    opt.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
    prop => loc["ValueMustNotBeNull"]);
    opt.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor(
    (value, prop) => loc["AttemptedValueIsInvalid", value, prop]);
    opt.ModelBindingMessageProvider.SetNonPropertyAttemptedValueIsInvalidAccessor(
    value => loc["NonPropertyAttemptedValue", value]);
    opt.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor(
    prop => loc["UnknownValueIsInvalid", prop]);
    opt.ModelBindingMessageProvider.SetNonPropertyUnknownValueIsInvalidAccessor(
    () => loc["NonPropertyUnknownValueIsInvalid"]);
    opt.ModelBindingMessageProvider.SetValueIsInvalidAccessor(
    value => loc["ValueIsInvalid", value]);
    opt.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(
    prop => loc["ValueMustBeANumber", prop]);
    opt.ModelBindingMessageProvider.SetNonPropertyValueMustBeANumberAccessor(
    ()=> loc["NonPropertyValueMustBeNumber"]);
    });
    }
    }
    De esta forma ya podríamos usarlo desde ConfigureServices() como sigue, dejando el código de inicialización de la aplicación mucho más limpio:
    services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
    .AddViewLocalization()
    .AddDataAnnotationsLocalization()
    .ConfigureModelBindingMessages(); // Por defecto los recursos estarán en el archivo
    // de recursos ModelBindingDefaultMessages.resx,
    // en el ensamblado actual.

    En resumen

    En este post hemos visto cómo podemos modificar los mensajes de error generados por el sistema de binding, normalmente debido a problemas encontrados a la hora de asignar los valores presentes en la petición a los parámetros de entrada de nuestras acciones.

    Para ello, sólo hemos tenido que acceder al proveedor de mensajes de este componente, DefaultModelBindingMessageProvider y modificar el delegado de generación de dichos mensajes. En este caso, dado que queríamos conseguir una versión localizada de los mensajes, hemos hecho uso de un IStringLocalizerFactory para obtener acceso a los recursos RESX y poder mostrar en cada caso la traducción correspondiente.

    En un artículo posterior continuaremos profundizando en este tema y veremos cómo modificar los mensajes por defecto de las anotaciones estándar (Required, Range, etc.), evitando así el tener que personalizar los textos de error en cada una de dichas anotaciones, y aprovechando para ver cómo localizar estos mensajes.

    Publicado en: www.variablenotfound.com.

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

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

    Casio fx-CP400 (Operaciones)

    noviembre 14, 2018 11:07

    En la anterior entrega sobre la calculadora CAS y gráfica fx-CP400, habíamos dado un repaso superficial a sus características. En el artículo de hoy, trabajaremos en serio con ella, al igual que hicimos con en Casio fx-CG50 (Operaciones). Si te lo perdiste, ahora es el momento de que leas el artículo anterior: Casio fx-CP400 (Introducción […]

    La entrada Casio fx-CP400 (Operaciones) aparece primero en Bitácora de Javier Gutiérrez Chamorro (Guti).

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

    Picando Código

    Cerveza de DATA – Donde Pinta 2.0

    noviembre 13, 2018 10:00

    ¿Se acuerdan de “Dónde Pinta” Aplicación sobre cerveza artesanal basada en datos abiertos? ¡Volvió! En forma de Cerveza de DATA.

    Los “Café de DATA” (o en este caso “cerveza”) son una excusa para acercarse a la comunidad de DATA, y la más amplia comunidad de Datos Abiertos de Uruguay y América Latina. La oportunidad para conocer gente y limpiar un poco el karma negativo obtenido del trabajo que hacemos a diario alimentando este sucio sistema capitalista con trabajo voluntario para hacer del mundo un lugar mejor. Porque de entre todas las cosas que aprendimos gracias a Stan Lee, una fue que el super poder no hace al héroe, sino cómo lo usa. Así que si tenés ganas de aportar a un proyecto con un fin tan solemne y honrado como Dónde Pinta de forma voluntaria (o de hecho cualquiera de los proyectos de DATA), el primer paso es acercarte.

    Si -como yo- sos de esas personas a las que les cuesta esa interacción inicial con desconocidos, una buena cerveza Índica, Davok, o tantas de las nuevas marcas que vienen surgiendo en el mundillo uruguayo de la cerveza artesanal pueden servir como catalizador social. Y si no tomás alcohol (o la cerveza no es lo tuyo), no hay problema. Mandate igual, DATA es un excelente grupo humano que te va a recibir de Brazos Abiertos™. Dejo la invitación:

    Cerveza de Data - Dónde Pinta 2.0

    DondePinta.uy Es una aplicación web que permite encontrar productores, bares, restaurantes y tiendas donde se pueda conseguir cerveza artesanal uruguaya.

    El proyecto fue gestado junto a Underground Beer Club y Mirá Mamá y fue llevado adelante por la comunidad de los Café de DATA. Es un proyecto 100% voluntario y comunitario y su intención es que sea adoptado por los cerveceros como una forma de dar a conocer su oferta y compartir la información como Datos Abiertos.

    La plataforma ya tiene un par de años, por eso creemos que ya es hora de volver a revisarlo ¡y hacerle algunas mejoras!
    Para eso nos vamos a juntar en Shelter Patio Cervecero (Andresito Guacarí 1806) a las 17:00 a empezar el trabajo y discutir ideas. No dejes de sumar tus ideas en Meetup, o de llevarlas al evento.

    Algunas ideas para discutir:

    • Calendario de eventos cerveceros
    • Mejoras de interfaz
    • Herramientas para colaboración de usuarios (sumar locales, correcciones, etc.).
    • Pasar a Ionic para publicar en appstores
    • Nuevas categorías de información (ej. café de especialidad)
    • Categoría “insumos para cerveza casera” en el mapa
    • Hacer al sitio accesible (estándar W3C)
    • Cambiar mapa por uno abierto y agregar geolocalización y búsqueda de direcciones

    Se busca:

    Este proyecto necesita gente con ganas de sumarse y destinar algo de su tiempo libre durante el sábado 1º y algunos ratos extra en las semanas siguientes, que seguramente seguiremos puliendo detalles vía web.
    Todo por la excelente causa de facilitarnos a todos encontrar más y mejor cerveza artesanal, allí donde se encuentre en Uruguay 😁🍻

    Precisamos gente con muchos talentos, como:

    • Desarrollo web/HTML/Javascript
    • Trabajo con mapas
    • Diseño web/maquetado/frontend
    • Ionic/Desarrollo app móviles
    • Recolección y subida de datos

    ¿Qué va a pasar el sábado?

    Vamos obviamente a trabajar en la app, aunque es probable que no terminemos el mismo sábado. El objetivo es acordar el plan, dar los primeros pasos y marcar cómo avanzamos de ahí en adelante. Quienes quieran y puedan comprometerse para seguir, seguiremos en contacto mediante Slack para terminar el proyecto y lanzarlo.

    La pregunta que todos se están haciendo:

    ¡Claro que va a haber cerveza! Shelter nos va a invitar la primera ronda 👏 y DATA la segunda 😎

    ¿Te divierte?

    Ya podés empezar a participar entrando a dondepinta.uy, probándola, pensando qué más te gustaría que tuviera y proponiendo ideas en los comentarios 😉

    Inscribite Ya

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

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

    Si llevo Kanban, también llevo los principios ágiles

    noviembre 13, 2018 07:11

    Recientemente se está hablando de si kanban es una metodología ágil o no. Creo que la primera vez que me lo plantee fue al leer una serie de magníficos posts de Michael Sahota sobre la cultura de las organizaciones. NOTA: Es realmente curioso que un tema tan específico haya llegado a una revista como Forbes. Steve Denning, al que debeis seguir atentamente si no lo haceis ya, está "fusilando" las

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

    Variable not found

    Establecer textos por defecto y localizados en validaciones de ASP.NET Core MVC

    noviembre 13, 2018 07:55

    ASP.NET Core MVCEn el post anterior veíamos cómo personalizar los mensajes de error generados durante el proceso de binding, y cómo aprovechar las posibilidades que nos brinda el framework para introducir textos localizados en esos puntos.

    Hoy seguiremos profundizando en este tema, pero esta vez nos centraremos en modificar los textos por defecto de las anotaciones de datos y hacer que simplemente decorando una propiedad con un atributo como Required consigamos obtener mensajes de error localizados y a nuestro gusto, sin necesidad de establecer el ErrorMessage de forma explícita y, en algunos casos, ni siquiera tener que indicar dicho atributo.

    Personalizando los proveedores de metadatos de validación

    Los proveedores de metadatos de validación son componentes que se registran durante la configuración de los servicios de MVC, y son invocados por el framework para obtener las anotaciones o atributos de validación de propiedades.

    Estos componentes, que implementan IValidationMetadataProvider, tienen la siguiente pinta:
    public class CustomValidationMetadataProvider : IValidationMetadataProvider
    {
    public void CreateValidationMetadata(ValidationMetadataProviderContext context)
    {
    // Configurar metadatos de validación aquí
    }
    }
    El método CreateValidationMetadata() es invocado una única vez para cada objeto, parámetro o propiedad (el resultado es cacheado) del que se desee obtener información de validación. A través del argumento context podremos conocer información sobre el elemento evaluado y establecer sus metadatos de validación.

    Esto podemos usarlo, por ejemplo, para añadir una anotación [Required] preconfigurada a todas las propiedades de tipo valor (int, long, DateTime, etc.) que no hayan sido previamente decoradas con dicha anotación:
    public class CustomValidationMetadataProvider : IValidationMetadataProvider
    {
    public void CreateValidationMetadata(ValidationMetadataProviderContext context)
    {
    // Lets add a [Required] annotation to all value types
    if (context.Key.ModelType.GetTypeInfo().IsValueType
    && !context.ValidationMetadata.ValidatorMetadata.OfType<RequiredAttribute>().Any())
    {
    context.ValidationMetadata.ValidatorMetadata.Add(
    new RequiredAttribute() { ErrorMessage = "Hey, este campo es obligatorio" }
    );
    }
    }
    }
    Fijaos que de esta forma tan sencilla estamos modificando el mensaje por defecto del framework para obligatoriedad implícita en este tipo de campos. Si quisiéramos aplicar los cambios, simplemente deberíamos añadir el siguiente código al ConfigureServices() de nuestra aplicación:
    services.AddMvc(opt =>
    {
    opt.ModelMetadataDetailsProviders.Add(new CustomValidationMetadataProvider());
    });

    Estableciendo mensajes de error por defecto para las anotaciones

    Jugando con el ejemplo anterior un poco, podemos ver que establecer un mensaje por defecto para las validaciones más habituales no es nada complicado. En la colección _context.ValidationMetadata.ValidatorMetadata podemos encontrar las anotaciones de validación asociadas al elemento evaluado, por lo que simplemente tendremos que recorrerlas y establecer sus propiedades como más nos convenga:
    public void CreateValidationMetadata(ValidationMetadataProviderContext context)
    {
    // First, lets add a [Required] annotation to all value types
    if (context.Key.ModelType.GetTypeInfo().IsValueType
    && !context.ValidationMetadata.ValidatorMetadata.OfType<RequiredAttribute>().Any())

    // Now, set the ErrorMessage for all validation attributes
    var validationAttributes = context.ValidationMetadata.ValidatorMetadata.OfType<ValidationAttribute>();
    foreach (var validationAttribute in validationAttributes)
    {
    if(validationAttribute is StringLengthAttribute)
    {
    validationAttribute.ErrorMessage = "Hey, este campo es obligatorio";
    }
    else if(validationAttribute is RangeAttribute)
    {
    validationAttribute.ErrorMessage = "Introduce valores entre {1} y {2}";
    }
    [...] // Set ErrorMessage for other validations
    }
    }
    Y obviamente, si le damos una vuelta de tuerca más podemos utilizar este mismo enfoque para establecer los mensajes de texto localizados, aunque esto nos costará un poco más de trabajo.

    Localizando los mensajes de validación por defecto

    Lo primero que vamos a hacer es crear un archivo RESX con los mensajes de validación al que, por ejemplo, llamamos SharedResources.Resx:

    El archivo de recursos

    Hay varias cosas a tener en cuenta:
    • La ubicación del archivo puede ser cualquiera. En nuestro caso, lo introduciremos en una carpeta llamada "Resources" en el raíz del proyecto.
       
    • En las propiedades del archivo archivo RESX debemos establecer su custom tool al valor "PublicResXFileCodeGenerator". Esto hará que se genere automáticamente una clase que facilita el acceso a los recursos localizados en el archivo {NombreDeTuResx}.Designer.cs; en nuestro ejemplo, el nombre de la clase es SharedResources.
       
    • Por convención, las claves de los recursos coincidirán con el nombre del atributo de validación, pero eliminando el sufijo "Attribute". Así, para la anotación [Required], el texto localizado lo encontraremos bajo la clave "Required" en el RESX, "Range" para la anotación [Range] y así con todos.
    Obviamente, podemos añadir traducciones en archivos RESX adicionales, como SharedResources.en.resx, SharedResources.fr.resx, etc. En estos casos, no será necesario modificar su custom tool.
    A continuación, vamos a hacer que el proveedor de metadatos de validación configure correctamente los mensajes por defecto atendiendo a las convenciones y acciones anteriores.

    Como puede verse en el siguiente código, estamos modificando nuestro proveedor de metadatos para que reciba en el constructor el tipo de la clase asociada al archivo RESX donde hemos depositado nuestros mensajes de error, y usamos esta información para configurar el mensaje por defecto del validador:
    public class CustomValidationMetadataProvider : IValidationMetadataProvider
    {
    private readonly ResourceManager _resourceManager;
    private readonly Type _resourceType;

    public CustomValidationMetadataProvider(Type type)

    {
    _resourceType = type;
    _resourceManager = new ResourceManager(type.FullName, type.GetTypeInfo().Assembly);
    }

    public void CreateValidationMetadata(ValidationMetadataProviderContext context)
    {
    if (context.Key.ModelType.GetTypeInfo().IsValueType
    && !context.ValidationMetadata.ValidatorMetadata.OfType<RequiredAttribute>().Any())
    {
    context.ValidationMetadata.ValidatorMetadata.Add(new RequiredAttribute());
    }

    var validationAttributes = context.ValidationMetadata
    .ValidatorMetadata.OfType<ValidationAttribute>()
    foreach (var validationAttribute in validationAttributes)
    {
    if (validationAttribute.ErrorMessageResourceName == null
    && validationAttribute.ErrorMessageResourceType == null)
    {
    // By convention, the resource key will coincide with the attribute
    // name, removing the suffix "Attribute" when needed
    var resourceKey = validationAttribute.GetType().Name;
    if (resourceKey.EndsWith("Attribute"))
    {
    resourceKey = resourceKey.Substring(0, resourceKey.Length - 9);
    }

    // Patch the "StringLength with minimum value" case
    if (validationAttribute is StringLengthAttribute stringLength
    && stringLength.MinimumLength > 0)
    {
    resourceKey = "StringLengthIncludingMinimum";
    }

    // Setup the message if the key exists
    if (_resourceManager.GetString(resourceKey) != null)
    {
    validationAttribute.ErrorMessage = null;
    validationAttribute.ErrorMessageResourceType = _resourceType;
    validationAttribute.ErrorMessageResourceName = resourceKey;
    }
    }
    }
    }
    }
    Hecho esto, ya sólo nos queda suministrar la clase apropiada durante el registro de servicios:
    services.AddMvc(opt =>
    {
    opt.ModelMetadataDetailsProviders.Add(
    new CustomValidationMetadataProvider(typeof(SharedResources)));
    // "SharedResource" is the class generated from the RESX file
    })

    Por último

    Por si queréis ver todo esto en funcionamiento, o incluso copiar y pegar algo de código para vuestros proyectos, he dejado en Github una demo funcional de lo descrito en este artículo y el anterior, por lo que tenéis una solución con la localización y personalización de mensajes resuelta por completo :)

    https://github.com/VariableNotFound/i18n-validations

    Publicado en Variable not found.

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

    Variable not found

    Enlaces interesantes 337

    noviembre 12, 2018 08:05

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

    Por si te lo perdiste...

    .NET / .NET Core

    ASP.NET / ASP.NET Core

    Azure / Cloud

    Conceptos / Patrones / Buenas prácticas

    Data

    Web / HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Otros

    Publicado en: www.variablenotfound.com.

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

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

    Scrum master a tiempo completo: 42 Tareas

    noviembre 11, 2018 09:18

    Uno de los artículos que más referencio en mi curso de Scrum cuando hablo de las labores del Scrum Master es: 42-tasks-for-a-scrum-masters-job. Por alguna razón, todo el mundo parece entender que el Product Owner es un trabajo a tiempo completo, o ser miembro de un equipo también, pero que probablemente el rol del Scrum Master puede ser realizado a media jornada o incluso menos. El scrum master

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

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

    Agile Fluency Model en castellano

    noviembre 11, 2018 09:17

    He publicado el modelo de fluidez ágil en castellano. Este modelo desarrollado por James Shore y Diana Larsen proporciona una interesante perspectiva sobre el camino al agilismo por los equipos. Modelo de fluidez ágil.

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

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

    Marco Scrum

    noviembre 11, 2018 09:17

    Marco de Scrum Curso de Scrum: Contactanos si estás interesado.

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

    Navegapolis

    Motivación laboral, cultura empresarial y estilo de liderazgo

    noviembre 11, 2018 03:01

    motivadoLa motivación laboral es el estímulo que anima a la persona a producir el resultado esperado de su trabajo. Es un factor clave —quizá el más importante— del éxito de las empresas del conocimiento. 

    En el siguiente esquema intento resumir en un vistazo relaciones para tener en cuenta, entre estilos de liderazgo y cultura empresarial y la división habitual de motivación extrínseca e intrínseca.  

    motivacion laboral curltura de empres y liderazgo 

     

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

    Blog Bitix

    Desempaquetado y análisis de monitor Benq PD2700Q

    noviembre 10, 2018 09:00

    La siguiente pieza que he adquirido para completar lo que será mi ordenador personal en casa es el monitor que usaré en mayor medida para tareas ofimáticas, navegar por internet, quizá ver alguna película y en menor medida quizá también algún juego. De entre las opciones que buscaba inicialmente era que fuese IPS por los colores, de 27 pulgadas y con ese tamaño de pantalla con al menos resolución QHD. Hay muchos modelos entre los que elegir y con variaciones de precios notables en aparentemente las mismas características, de 32 pulgadas, ultrapanorámicos, UHD, orientados más a gaming, …

    Benq

    Aún no se está comercializando en España el ordenador que he decidido comprar, un Intel NUC entre las opciones entre portátil, ITX o NUC que evalué hace ya más de seis meses. Espero que no quede mucho hasta que haya disponibilidad en Amazon del Intel NUC8i5BEK Bean Canyon, al menos está listado y en Amazon EEUU ya hay disponibilidad. A pesar de esto ya estoy comprando las otras piezas que en cualquier caso necesito como teclado y ratón inalámbrico, alfombrilla para la mesa y ahora otra pieza más importante, el monitor.

    Ya con unas semanas habiendo usado el teclado y ratón V7 CKW200 acepto que el ratón no sea totalmente fiable y un peligro hacer cosas importantes con él al arrastrar y soltar ya que falla en ocasiones, quizá compre un ratón tradicional con cable si me da muchos problemas. He comprobado que se pueden tener dos ratones conectados simultáneamente, funcionado ambos. El teclado funciona perfectamente y estoy encantado con él, solo por el teclado no los he devuelto. Sin embargo, en el monitor con algún defecto de píxeles muertos o retención de imagen si haría la devolución, por fortuna una vez me ha llegado y lo he comprobado con unas imágenes de prueba para detectar píxeles muertos o vagos no he visto ninguno así que el panel me ha llegado perfecto estado.

    He mirado varias opciones y modelos de monitor y aún así tampoco estoy seguro de que la que finalmente he elegido sea la mejor en el mercado. Leyendo comentarios y opiniones de otros usuarios en todos los modelos siempre hay alguno que ha tenido problemas, en el caso de los monitores de píxeles muertos, vagos, fugas de luz, hasta retención de imágenes. Las fugas de luz es algo normal en la tecnología actual de monitores y televisores con retroiluminación led en los bordes de la pantalla, apreciable en condiciones de poca luz e imágenes oscuras.

    Opciones

    Partía de unos requerimientos de que fuera IPS por su calidad de colores, de 27 pulgadas y con resolución 2K o 2560x1440 en un rango de precio de hasta unos 300€. En otro orden de cosas menos importantes que valoraba era que tuviese conexión HDMI y DisplayPort, marcos finos y compatibilidad con soporte VESA. Hay opciones en formato ultrapanorámico de 29 pulgadas y resolución 2560x1080. He visto alguno de estos pero finalmente he preferido que tenga más resolución vertical y no sea tan ancho para no tener que girar la cabeza.

    Con estos requerimientos iniciales me quedé con unos pocos candidatos. Básicamente monitores 2K en formato de 27 pulgadas e IPS muy similares en cuanto a características, son IPS de resolución QHD 2560x1440, tienen entrada HDMI 1.4 y DisplayPort 1.2, soporte VESA 100x100, con algunas pequeñas diferencias en cuanto a especificaciones. El Benq PD2500Q es de 25 pulgadas, panel de 8 bits y tiene sensor para adaptar automáticamente el brillo a la luz ambiental, posee un hub USB 3.1 de cuatro puertos, el Benq PD2700Q posee un hub USB 2.0 de dos puertos y un panel de 10 bits. Los dos modelos de Benq son muy parecidos sino iguales en cuanto al panel de monitor, varía en cuanto al marco que en el caso del GW s brillante y en el PD mate, también cambian las conexiones que tienen el GW tiene D-Sub, DVI-DL (Dual Link), DisplayPort, HDMI y el PD DisplayPort, mini DisplayPort y HDMI. Ambos soportan 100% Rec.709 y sRGB, sin parpadeos, son regulables en altura, gira, inclinación y pivotable para ponerlo en modo vertical. Tienen un precio similar.

    De las opciones con las que me quedé todos con panel de 10 bits (u 8 + 2, salvo el PD2500Q que es de 8), función de no parpadeo, filtro de luz azul y altavoces sencillos. El PD2700Q es un modelo más antiguo, los PD2500Q y PD2700U son más modernos y se notan en los biseles más reducidos y el hub con una versión mayor de USB.

    Tanto el Viewsonic VG2719-2K como el V7 L27HAS2K-2E tienen un precio más económico pero en cuanto a especificaciones técnicas no eran malas. Sin embargo, de estos modelos no hay mucha información ni de análisis en páginas web, desempaquetados en Youtube, ni opiniones en Amazon. Del V7 hay dos opiniones en Amazon alemania y ambas no son buenas y coinciden en que el texto y la imagen se ve borrosa. La falta de información me ha hecho tener reticencias con estos modelos.

    El Viewsonic es un modelo del 2015 con dos HDMI 1.3 y DisplayPort 1.2. El Benq PD2700Q es un modelo del año 2016, tiene HDMI 1.4 y DisplayPort 1.2, no es muy relevante ya que de HDMI 1.3 a 1.4 varía la resolución soportada pero en estos monitores en ambos casos es suficiente cualquiera de ellas en esta resolución y a 60 Hz. El Viewsonic es una opción algo menor pero considerando su precio y el del Benq hubiese sido otra buena elección. El Viewsonic en contra tiene la falta de información aún habiendo un comentario positivo en Amazon.

    Los otros candidatos destacables que he visto han sido alguno en formato panorámico.

    Los monitores UHD para un tamaño de 27 pulgadas a máxima resolución el texto y elementos gráficos se ven muy pequeños con lo que para uso ofimático hay que escalar a una más pequeña. Hay en un rango de precios muy amplio, desde 200€ a 700+€. Dos modelos en 4K de los anteriores son el Benq PD2700U que es el que hubiese elegido en caso de haber optado por UHD, el Benq BL2711U, el LG 27UK600-W o el LG 27UK650-W, nótese la U en estos modelos. Estos monitores aún siendo UHD no llegan a los 192 dpi, se quedan en 163, para considerarse HiDPI ni tienen una pantalla retina. Otras opciones son optar por 32” UHD y en formato panorámico.

    En monitores de 27 pulgadas la recomendación en varios foros es de 2K. Menos con 1K o full hd es ver píxeles como centollos, más es ver las cosas pequeñas a máxima resolución para tareas ofimáticas y es habitual tener que escalar a una resolución más baja aunque tenga mayor definición aprovechando la mayor densidad de píxeles o DPI. Estos son unos hilos en foros y dos artículos que me han parecido interesantes y que me han resuelto alguna duda o confirmado lo que ya suponía.

    En PcComponentes los precios no varían tanto pero en Amazon en algunos productos si hay notables diferencias entre pocos días o alguna semana, sobre todo en productos de tecnología. Para conocer si el precio actual de un producto es alto, bajo o está bajando la página CamelCamelCamel permite introduciendo la URL del producto de Amazon mostrar una gráfica con la evolución del precio.

    Evolución del precio del monitor Benq PD2700Q

    Benq PD2700Q

    Teniendo en cuenta todo finalmente me he decidido por el Benq PD2700Q. Tiene buena conectividad, sin conexiones obsoletas como en el caso del Benq GW. Es un monitor orientado a diseñadores aunque mi uso es principalmente en tareas ofimáticas y posiblemente algún juego ocasional. Los monitores ultra anchos no los he usado pero se me hacen demasiado anchos obligando a mover la cabeza para cubrir su área de visualización. Ya con 27” el monitor se me hace grande aunque me acostumbraré a este tamaño pasado un tiempo, para la gente que aún quiere un monitor más grande los hay de 32” aunque en estos ya hay que ir a una resolución de UHD 3840x2160.

    Sus especificaciones más destacables son:

    • Tamaño de la pantalla, 27
    • Tipo de panel, IPS
    • Brillo, 350 cd/㎡
    • Colores de la pantalla, 1.07 billion colors
    • Área de la pantalla (mm), 596.74(H) x 335.66(V)
    • Distancia entre píxeles (mm), 0.2331 y PPP, 108.79
    • Profundidad de color, 10bits
    • HDMI, HDMI (v1.4) x 1
    • DisplayPort, DisplayPort (v1.2) x 1
    • Mini DisplayPort, Mini DisplayPort (ver1.2) x 1
    • Concentrador USB 2.0, upstream x 1 y downstream x 2
    • Fuente de alimentación, built-in
    • Dimensiones (altura × anchura × profundidad, mm) (sin base), 379.5 x 641.0 x 56.5. Con base (posición alta): 547.3 x 641.0 x 239.6. Con base (posición baja): 417.3 x 641.0 x 239.6

    Con 27 pulgadas y una resolución de 2560x1440 su área de visualización sus PPI son:

    • Display size (27”, QHD): 23.53” × 13.24” = 311.5in² (59.77cm × 33.62cm = 2009.68cm²) at 108.79 PPI, 0.2335mm dot pitch, 11834 PPI²

    Comprando con un monitor de 23 pulgadas y una resolución de 1920x1080 y con un monitor de 3840x2160 en 27 pulgadas estos números son:

    • Display size (23”, FHD): 20.05” × 11.28” = 226.04in² (50.92cm × 28.64cm = 1458.33cm²) at 95.78 PPI, 0.2652mm dot pitch, 9174 PPI²
    • Display size (27”, UHD): 23.53” × 13.24” = 311.5in² (59.77cm × 33.62cm = 2009.68cm²) at 163.18 PPI, 0.1557mm dot pitch, 26627 PPI²

    Hay algo de confusión cuando se menciona 2K en monitores, en realidad el término más apropiado es QHD (quad hd), que es 4 veces la resolución HD (1280x720), dos en alto y dos en ancho. Lo mismo ocurre con los monitores 4K que en realidad la denominación más acertada es UHD (ultra hd) y son cuatro veces la resolución FHD, dos en cada medida, 3840x2160.

    En YouTube hay unos cuantos vídeos analizando este mismo monitor u otros parecidos que me han servidor para aclarar dudas.

    Desempaquetado

    El monitor está perfectamente embalado en una caja impresa con las principales características del monitor, dentro lleva sus respectivos protectores e incluye los siguientes elementos. Que incluya una cable DisplayPort, HDMI y USB es de agradecer para no tener que comprarlos aparte y poder usar el monitor desde el primer momento.

    • Monitor PD2700Q.
    • Guía de inicio y CD con software.
    • Cable de alimentación.
    • Cable HDMI.
    • Cable Mini DisplayPort a DisplayPort.
    • Cable USB para conectar al monitor y ordenador.
    • Gancho para colgar cascos en la parte trasera.
    Desempaquetado
    Contenido de la caja

    El monitor posee unos biseles bastante amplios de 2 cm sin embargo al ser el marco negro con acabado mate no distraen mucho ni son molestos estéticamente. Aún así la tendencia es que estos biseles sean reducidos pero este modelo de monitor ya tiene algunos años.

    Pantalla
    Pantalla parte trasera

    Este modelo de monitor incluye dos puertos USB 2.0 en la parte trasera, tiene utilidad en un ordenador de escritorio que esté debajo de la mesa para tener unos puertos USB más cerca del escritorio con un concentrador USB.

    Conexiones

    Los botones para ajustar los parámetros del monitor y OSD están detrás del monitor y el piloto indicador de encendido en un lateral de modo que en un ambiente de luz oscuro no molesta.

    Configuración en pantalla (OSD)

    Habitualmente estoy trabajando con un portátil MacBook Pro con pantalla retina y anteriormente he usado un Sony VAIO E 14P con panel TN de resolución 1600x900 y un monitor LG 2351 también TN de resolución 1920x1080. Con respecto a los dos paneles TN que he usado hasta el momento la mejora es apreciable en sus ángulos de visión, colores más intensos, este es el motivo de requerir un panel tipo IPS. Si tengo que destacar algo del MacBook es la pantalla, la pantalla del Benq cumple pero al no ser retina la diferencia es clara, aún conservando buena vista la definición del texto no es mala pero no llega a la calidad de la pantalla del MacBook, eso si en cuanto a colores no tiene nada que envidiar a la pantalla del Mac.

    Análisis

    Después de un tiempo de uso ya veré como son las impresiones, las primeras impresiones son buenas aunque viniendo de una pantalla retina no tanto. Si el texto resultase demasiado pequeño según las preferencias el MacBook permite reducir la resolución a 2048x1152, 1600x900, 1080p o 720p. Si se desea un monitor UHD que tenga HDMI 2.0 a 60 Hz y en esa resolución casi pensando en las 32”. Con el monitor externo no tengo que tener la pantalla tan cerca ni tener la cabeza agachada como con el portátil con lo que la postura corporal y de espalda es mejor para evitar cansancios y dolores que con el uso continuado pueden surgir, la verdad es que lo he notado ya desde el primer momento. Otra cosa que me gusta es que su fuente de alimentación está integrada con lo que no hay un transformador encima de la mesa y sus respectivos cables.

    Con la resolución de 2560x1440 se puede visualizar más información al mismo tiempo y es más cómodo trabajar en una misma aplicación a pantalla completa o poder ver dos aplicaciones en primer plano. Se nota y mucho el espacio extra en la pantalla al trabajar con varias de las aplicaciones que uso habitualmente como editor de textos, navegador web y terminal.

    La primera impresión es que un monitor de 27” es grande pero ya me han comentado que es cuestión de acostumbrase y que dentro de poco me parecerá lo normal o incluso pequeño, de hecho ya hay monitores de 32” para esa gente que aún lo quiere más grande. A la espera del Intel NUC tendré monitor que me parece grande pero un equipo que cabe en la palma de la mano. Comparado con la pantalla retina del MacBook las cosas no se ven tan definidas, un monitor de 27” con pantalla retina no he visto salvo los Mac. Con 27” en UHD tampoco se considera HiDPI y se queda en 163 dpi contra los 226 del Mac. Para que un monitor de 27” tenga similar definición que con una pantalla retina la resolución debería ser del doble, 5120x2880, con esto alcanzaría unos 216 dpi y ya se consideraría HiDPI aunque habría que escalar la resolución en la configuración del ordenador ya que en la resolución nativa el texto se vería muy pequeño.

    Imagen en la pantalla

    Este monitor tiene los botones de ajustes detrás del monitor marcados con unos indicadores en el frontal para encontrar su posición, en algún sitio se comenta que esto es algo incómodo para acertar a pulsar el botón adecuado. En lo que probado no me ha resultado nada incómodo y los botones me han parecido perfectos. Dado que el monitor tiene altavoces se le puede enviar sonido por el mismo cable HDMI que envía la señal de vídeo y luego con un jack de 3.5mm desde el monitor enviarlo a unos altavoces que den mejor calidad de sonido, esto me permite conectar un cable menos al ordenador y que la mesa no quede cubierta de cables. Aunque con esta opción al menos un Mac no permite ajustar el volumen hay que hacerlo desde los altavoces, por fortuna tengo unos altavoces con regulador de volumen.

    Las fugas de luz o bleeding es otro de los posibles problemas de un monitor, por la tecnología de retoiluminación led hace que en condiciones oscuras los negros aparezcan como grises y que en los bordes de la pantalla se noten zonas un poco iluminadas al no bloquearse por completo la retoiluminación. Ya era consciente de este problema y no me sorprende que el que me ha llegado a mi tenga, no se si será mucho o poco porque no puedo comparar pero no me ha parecido excesivo. Sin píxeles muertos o vagos sin mucha fuga de luz me doy por contento. Por el momento tampoco he notado retención de imagen.

    Bleeding en ambiente oscuro

    Con una resolución QHD hay más espacio que en una Full HD que agradecen los programadores, diseñadores y gente que pasa mucho tiempo delante de una pantalla trabajando en tareas relacionadas con múltiples documentos o aplicaciones. Por ejemplo, con un IDE es posible visualizar el código fuente de dos archivos o visualizar en primer plano el IDE, navegador y terminal al mismo tiempo. Las personas que trabajan con gráficos pueden visualizar imágenes más grandes ya sea en Photoshop, GIMP, Inkscape, OpenShot y herramientas similares.

    Espacio en resolución QHD

    En un futuro probablemente aparezcan nuevos monitores que soporten HDR y con resolución retina que supondrá otro salto en la calidad de la imagen. Me va a quedar la duda de si un monitor con resolución UHD de 27 pulgadas bajando la resolución sería una buena opción en cuanto a definición de texto, se suele comentar que no, usar una resolución escalada del panel no es lo mismo que en la resolución nativa pero me gustaría experimentarlo. Al menos en el Mac de resolución nativa 2560x1440 cambiar a 1600x900 se sigue viendo perfectamente el texto y en este Benq de 27” bajando la resolución no aprecio que el texto se vea peor. En cualquier caso los monitores UHD por el momento tienen un precio mayor, más del doble o al menos unos cientos de euros más según el modelo comparado, y este Benq PD2700Q es de buena calidad acorde a su precio mayor que algunos otros QHD y que muchos Full HD pero de todas formas asequible para alguien que le da buen uso.

    Detección de píxeles muertos o vagos

    Es recomendable realizar una primera prueba al monitor para buscar píxeles muertos, que no se encienden, o vagos, que permanecen encendidos todo el tiempo con el color de un componente. El siguiente test se compone de varias imágenes en los colores básicos (negro, blanco, azul, rojo y verde) que visualizadas individualmente a pantalla completa muestran si algún pixel es defectuoso. Que haya un pixel defectuoso es molesto ya que basta para que haya solo uno y sepas donde está entre los casi 3 millones que tiene el monitor para que estés fijándote constantemente en él, más si se encuentra en el área central de la pantalla. También conviene informarse antes de comprar acerca de la garantía que ofrece cada fabricante respecto a los píxeles muertos, algunos toleran y no aceptan la devolución con cierto número de píxles que permanecen encendidos, apagados o algún subpixel estropeado.

    Vídeos de prueba

    Para probar la calidad del monitor (o televisor) en cuanto a colores o resolución en YouTube hay varios, uno de ellos de ejemplo es este pudiendo seleccionar la calidad desde 720p (HD), 1080p (FHD, 1K), 1440p (QHD, 2K) y 2160p (UHD, 4K). Lógicamente para apreciar las diferencias de calidad con respecto a una resolución más baja hay que tener un monitor o televisor que soporte la resolución más alta. Esto da una idea de como se ve una película, vídeo o imágenes. Las tareas ofimáticas no es lo mismo que ver vídeos, en UHD y usando escalado el texto gracias a su mayor PPI en el mismo tamaño que un QHD se ve más definido y claro. Sin embargo, el escalado aún presenta problemas ya que no todas las aplicaciones está preparadas.

    Soporte VESA

    En todos los modelos he buscado que sea compatible con la norma VESA 100x100 por si en un futuro lo pongo en uno soporte de modo que el pie no ocupe sitio en la mesa. La peana de algunos monitores no soportan ajustes en altura, inclinación, ajuste laterales o modo vertical pero con un soporte compatible con VESA se puede suplir la carencia además de evitar que la peana ocupe espacio. Con un soporte tienen la ventaja de que se puede ajustar la posición del monitor muy fácilmente. Estos soportes funcionan con una mordaza para anclarlos a la mesa.

    Algunos soportes compatibles con VESA que he mirado son los siguientes.

    Hub de puertos USB

    Teniendo el monitor un hub USB 2.0 no merece la pena un hub externo USB 3.0 o 3.1, sin embargo, con otros monitores puede ser una opción como con el PD2500Q o el PD2700U. Los discos duros externos que tengo son USB 2.0 y por limitaciones del disco duro si fuesen 3.0 el rendimiento adicional no se si sería mucho mayor. Hay hubs de 4, 7 y 10 puertos pero con esta candidad de puertos es recomendable que sea alimentado sobre todo si se conectan discos duros externos sin alimentación propia, también hay algunos con interruptor individual de encendido por puerto.

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

    Blog Bitix

    Novedades de Java EE 8

    noviembre 09, 2018 04:00

    Java EE
    Java

    La publicación de Java 9 en agosto de 2017 con la importante novedad de los módulos ha hecho que la publicación de Java EE 8 haya pasado desapercibida. También ha contribuido el hecho de que Oracle haya entregado el desarrollo de futuras nuevas especificaciones de Java EE a la fundación Eclipse con el objetivo de que sea más abierto a otras empresas, Java EE a partir de ahora bajo la fundación Eclipse se denominará Jakarta EE. Pasados algunos meses los servidores de aplicaciones ya están implementando las nuevas novedades de Java EE 8. Algunos de los servidores que ya soportan estas nuevas especificaciones y novedades son Wildfly, Payara Kumuluz o Tomcat 9.

    Hay algunas novedades de Java EE 8 entre ellas:

    • Java Servlet 4.0 API con soporte para el protocolo HTTP/2, incluido el soporte para enviar recursos desde el servidor sin que el cliente los haya pedido aún a través del método newPushBuilder() de la clase HttpServletRequest.
    • Soporte para JSON mejorado incluyendo una nueva API de binding.
    • Eventos CDI asíncronos.
    • Una nueva API de seguridad simple, estandarizada y modernizada.
    • Soporte para las nuevas capacidades de Java 8 (pe. Date & Time API, Streams API, mejoras en las anotaciones).

    El soporte para HTTP/2 y sus importantes novedades hace que desde Java se puedan aprovechar las mejoras en la segunda versión del protocolo HTTP y se aprovechan las novedades de Java 8. También en Java EE 8 se han actualizado de versión algunas de las especificaciones e incluido alguna nueva como la de la nueva API de seguridad que han de soportar los servidores de aplicaciones para ser compatibles:

    • JSR 366 – Java EE 8 Platform
    • JSR 365 – Contexts and Dependency Injection (CDI) 2.0
    • JSR 367 – The Java API for JSON Binding (JSON-B) 1.0
    • JSR 369 – Java Servlet 4.0
    • JSR 370 – Java API for RESTful Web Services (JAX-RS) 2.1
    • JSR 372 – JavaServer Faces (JSF) 2.3
    • JSR 374 – Java API for JSON Processing (JSON-P)1.1
    • JSR 375 – Java EE Security API 1.0
    • JSR 380 – Bean Validation 2.0
    • JSR 250 – Common Annotations 1.3
    • JSR 338 – Java Persistence 2.2
    • JSR 356 – Java API for WebSocket 1.1
    • JSR 919 – JavaMail 1.6

    Hay un tutorial de Java EE 8 y con el ejemplo FirstCup se puede adquirir un buen conocimiento para desarrollar aplicaciones con el lenguaje Java. En el artículo What’s new in Java EE 8 hay unos pocos ejemplos de código con varias de estas novedades. Finalmente, con el traspaso de Java EE a la fundación Eclipse y por motivos de marca registrada el proyecto ha sido renombrado a Jakarta EE. Java EE o ahora Jakarta EE, Microprofile y Spring junto con algunos frameworks especializados son las opciones más utilizadas para realizar aplicaciones en Java en el lado del servidor.

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

    Fixed Buffer

    Rompiendo los limites: Mocking en las Pruebas Unitarias .Net

    noviembre 06, 2018 09:00

    MockingRompiendo los limites: Mocking en las Pruebas Unitarias .Net

    Hoy por fin os traigo la unión entre las Pruebas Unitarias y la Inyección de Dependencias, el “Mocking“. ¿Que es el “Mocking” te preguntarás?, pues es la técnica utilizada para simular objetos en memoria con la finalidad de poder ejecutar pruebas unitarias.

    Esto, es especialmente útil cuando utilizamos recursos externos como bases de datos o servicios de mensajería, o cualquier cosa en general que no queramos o no podemos ejecutar durante las pruebas unitarias.

    Sin mas preámbulos, ¡vamos con ello! En primer lugar, he reutilizado el proyecto Entity Framework Core “Code First” para partir de tener el contexto de datos creado. Ademas, he añadido una clase “GeneradorInformes” (la cual cumple el patrón de Inyección de Dependencias en el constructor) y una clase “EmailSender” que implementa la interfaz “IEmailSender”:

    Proyecto

    //IEmailSender.cs
    namespace PostMocking.Model
    {
        public interface IEmailSender
        {
            bool Enviar(string Destinatario, string Mensaje);
        }
    }
    
    //EmailSender.cs
    namespace PostMocking.Model
    {
        public class EmailSender : IEmailSender
        {
            public bool Enviar(string Destinatario, string Mensaje)
            {
                //{...}
                return true;
            }
        }
    }
    
    //GeneradorInformes.cs
    using Microsoft.EntityFrameworkCore;
    using PostMocking.Data;
    using System.Linq;
    using System.Text;
    
    namespace PostMocking.Model
    {
        public class GeneradorInformes
        {
            //Propiedad con la dependencia
            private IEmailSender emailSender { get; set; }
            private PostMockingDbContext context { get; set; }
    
            public GeneradorInformes(PostMockingDbContext context, IEmailSender emailSender)
            {
                this.context = context;
                this.emailSender = emailSender;
            }
    
            public bool GenerarInforme(string NombreProfesor, string Email)
            {
                //Obtenemos mediante LinQ los datos del profesor
                var Profesor = context.Profesores.Where(x => string.Compare(x.Nombre, NombreProfesor, true) == 0)
                                                    .Include(x => x.Cursos)
                                                    .ThenInclude(x => x.Alumnos)
                                                    .FirstOrDefault();
                //En casode no encontrar nada, salimos
                if (Profesor is null)
                    return false;
    
                //Generamos el informe de alumnos y cursos
                StringBuilder sb = new StringBuilder();
                sb.AppendLine($"El profesor {Profesor.Nombre} imparte los siguientes cursos:");
                foreach (var curso in Profesor.Cursos)
                {
                    sb.AppendLine($"\t*{curso.Nombre} con los alumnos:");
                    foreach (var alumno in curso.Alumnos)
                    {
                        sb.AppendLine($"\t\t*{alumno.Nombre}");
                    }
                }
    
                emailSender.Enviar(Email, sb.ToString());
                return true;
            }
        }
    }
    
    //Program.cs
    using PostMocking.Data;
    using PostMocking.Model;
    using System;
    
    namespace PostMocking
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (PostMockingDbContext context = new PostMockingDbContext())
                {
                    EmailSender emailSender = new EmailSender();
                    GeneradorInformes generador = new GeneradorInformes(context, emailSender);
                    if (generador.GenerarInforme("FixedBuffer", "jorge_turrado@hotmail.es"))
                        Console.WriteLine("Informe enviado con éxito");
                    else
                        Console.WriteLine("Problema al enviar el informe");
                }
            }
        }
    }

    El resumen del funcionamiento básico, es que GeneradorInformes recibe las dependencias del contexto de datos y el servicio de correo, y al llamar al método “GenerarInforme(string,string)” se obtiene el informe del cursos y alumnos del profesor indicado, y se envía al correo indicado. En caso de que el informe se genere correctamente y se envíe, retornamos un true, en caso contrario un false.

    Pruebas Unitarias y Mocking

    Generaremos un proyecto de pruebas unitarias en nuestra solución y añadimos a nuestro proyecto de pruebas el siguiente paquete:

    Moq.Net

    O por consola:

    PM-> Install-Package Moq -ProjectName “NombreProyectoPruebas”

    Este paquete esta alineado con .NetStandard, en concreto .NetStandard 1.3, por lo que se puede utilizar indistintamente en .Net Framework o en .Net Core. Una vez que lo tenemos todo listo, vamos a crear nuestra prueba unitaria para GeneradorInformes.

    using Microsoft.EntityFrameworkCore;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    using PostMocking.Data;
    using PostMocking.Model;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace PruebasUnitarias
    {
        [TestClass]
        public class GeneradorInformesTests
        {
            // Como queremos reutilizar los Mock, los declaramos a nivel de clase
            Mock<IEmailSender> emailSender;
            Mock<PostMockingDbContext> DbContext;
    
            // Creamos los objetos de Mock en el constructor de la clase, para reutilizarlos
            public GeneradorInformesTests()
            {
                // Creamos el mock sobre nuestra interfazde envio de mensajes
                emailSender = new Mock<IEmailSender>();
                // Ante la llamada a su metodo enviar, retornamos un true, pero ademas serializamos al output del test el informe
                emailSender.Setup(m => m.Enviar(It.IsAny<string>(), It.IsAny<string>()))
                           .Returns((string destinatario, string mensaje) =>
                           {
                               Console.WriteLine(mensaje);
                               return true;
                           });
    
                // Creamos la coleccion que devolverá nuestra base de datos Mockeada
                var profesor = new Profesor { Nombre = "Jorge Turrado", IdProfesor = 1 };
                var curso = new Curso { IdProfesor = profesor.IdProfesor, Ciudad = "Vitoria", Nombre = "Mocking", Profesor = profesor };
                var alumno = new Alumno { IdCurso = curso.IdCurso, Curso = curso, Nombre = "Andres Garcia" };
                curso.Alumnos.Add(alumno);
                profesor.Cursos.Add(curso);
                var Profesores = new List<Profesor>()
                {
                     profesor
                }.AsQueryable();
    
                // Creamos el mock para la base de datos
                var mockSet = new Mock<DbSet<Profesor>>();
                mockSet.As<IQueryable<Profesor>>().Setup(m => m.Provider).Returns(Profesores.Provider);
                mockSet.As<IQueryable<Profesor>>().Setup(m => m.Expression).Returns(Profesores.Expression);
                mockSet.As<IQueryable<Profesor>>().Setup(m => m.ElementType).Returns(Profesores.ElementType);
                mockSet.As<IQueryable<Profesor>>().Setup(m => m.GetEnumerator()).Returns(Profesores.GetEnumerator());
    
                // Asignamos el mock de la base de datos al contexto
                DbContext = new Mock<PostMockingDbContext>();
                DbContext.Setup(c => c.Profesores).Returns(mockSet.Object);
            }
    
            [TestMethod]
            public void GenerarInformeValido()
            {
                // Creamos nuestra clase a testear y le pasamos los objetos mock
                GeneradorInformes generador = new GeneradorInformes(DbContext.Object, emailSender.Object);
                var result = generador.GenerarInforme("Jorge Turrado", "");
    
                //Comprobamos el resultado
                Assert.AreEqual(true, result, "No se ha podido generar el informe");
            }
    
            [TestMethod]
            public void GenerarInformeInvalido()
            {
                // Creamos nuestra clase a testear y le pasamos los objetos mock
                GeneradorInformes generador = new GeneradorInformes(DbContext.Object, emailSender.Object);
                var result = generador.GenerarInforme("Pedro Mayo", "");
    
                //Comprobamos el resultado
                Assert.AreEqual(false, result, "Se ha podido generar el informe");
            }
        }
    }

    Analicemos la clase de pruebas. En primer lugar, tenemos 2 objetos de tipo “Mock<T>” , declarados a nivel de clase, esto es debido a que vamos a utilizarlos en las 2 pruebas, y así nos ahorramos tener que construirlos 2 veces, aligerando así la carga.

    Mock<IEmailSender>

    Lo siguiente que tenemos, es el constructor. En él, se inicializan los objetos Mock, vamos de uno en uno:

    // Creamos el mock sobre nuestra interfazde envio de mensajes
    emailSender = new Mock<IEmailSender>();
    // Ante la llamada a su metodo enviar, retornamos un true, pero ademas serializamos al output del test el informe
    emailSender.Setup(m => m.Enviar(It.IsAny<string>(), It.IsAny<string>()))
         .Returns((string destinatario, string mensaje) =>
         {
             Console.WriteLine(mensaje);
             return true;
         });

    Mediante el método “Setup”, le estamos indicando el comportamiento que debe tener cuando llamemos el método Enviar(string,string) de la interfaz. Cabe destacar que “It.IsAny<string>()” esta indicando que este Mock se aplicara ante cualquier entrada de tipo string, pero podríamos indicarle un string concreto, por ejemplo:

    // Creamos el mock sobre nuestra interfazde envio de mensajes
    emailSender = new Mock<IEmailSender>();
    // Ante la llamada a su metodo enviar, retornamos un true, pero ademas serializamos al output del test el informe
    emailSender.Setup(m => m.Enviar("FixedBuffer", It.IsAny<string>()))
         .Returns((string destinatario, string mensaje) =>
         {
             Console.WriteLine(mensaje);
             return true;
         });

    Con este segundo código, solo aplicaría el Mock si el primer string es “FixedBuffer”, pudiendo definir así diferentes comportamientos ante diferentes entradas ya que podemos llamar tantas veces al método Setup como queramos.

    Mock<PostMockingDbContext>

    En este caso, no vamos a generar generar un comportamiento en concreto, sino que vamos a generar un contexto de datos falso. Lo primero para eso, es crear una colección de datos:

    var profesor = new Profesor { Nombre = "Jorge Turrado", IdProfesor = 1 };
    var curso = new Curso { IdProfesor = profesor.IdProfesor, Ciudad = "Vitoria", Nombre = "Mocking", Profesor = profesor };
    var alumno = new Alumno { IdCurso = curso.IdCurso, Curso = curso, Nombre = "Andres Garcia" };
    curso.Alumnos.Add(alumno);
    profesor.Cursos.Add(curso);
    var Profesores = new List<Profesor>()
    {
        profesor
    }.AsQueryable();

    Como se puede ver, simplemente estamos creando la colección que luego convertiremos en el contexto de datos. Una vez que tenemos los datos, vamos a crear el Mocking del DbSet, que como se puede ver, simplemente consiste en relacionar la colección que acabamos de crear con el objeto Mock<DbSet<Profesor>>.

    var mockSet = new Mock<DbSet<Profesor>>();
    mockSet.As<IQueryable<Profesor>>().Setup(m => m.Provider).Returns(Profesores.Provider);
    mockSet.As<IQueryable<Profesor>>().Setup(m => m.Expression).Returns(Profesores.Expression);
    mockSet.As<IQueryable<Profesor>>().Setup(m => m.ElementType).Returns(Profesores.ElementType);
    mockSet.As<IQueryable<Profesor>>().Setup(m => m.GetEnumerator()).Returns(Profesores.GetEnumerator());

    En el caso de necesitar mockear más tablas, solo tendríamos que repetir el proceso con todas las tablas que nos interese. Una vez que hemos acabado de generar todos los datos que tendrá nuestro contexto, asignamos los DbSet mokeados, vamos a crear por fin nuestro “Mock<PostMockingDbContext>”:

    DbContext = new Mock<PostMockingDbContext>();
    DbContext.Setup(c => c.Profesores).Returns(mockSet.Object);

    Esto lo conseguimos diciéndole en el Setup que ante un acceso a la tabla “Profesores”, devuelva el objeto mock que acabamos de crear.

    Una vez que tenemos creados nuestros dos objetos “Mock<T>” para las pruebas, veamos las pruebas:

    [TestMethod]
    public void GenerarInformeValido()
    {
        // Creamos nuestra clase a testear y le pasamos los objetos mock
        GeneradorInformes generador = new GeneradorInformes(DbContext.Object, emailSender.Object);
        var result = generador.GenerarInforme("Jorge Turrado", "");
    
        //Comprobamos el resultado
        Assert.AreEqual(true, result, "No se ha podido generar el informe");
    }
    
    [TestMethod]
    public void GenerarInformeInvalido()
    {
        // Creamos nuestra clase a testear y le pasamos los objetos mock
        GeneradorInformes generador = new GeneradorInformes(DbContext.Object, emailSender.Object);
        var result = generador.GenerarInforme("Pedro Mayo", "");
    
        //Comprobamos el resultado
        Assert.AreEqual(false, result, "Se ha podido generar el informe");
    }

    Como se puede ver, el funcionamiento es exactamente igual que sería en nuestro proyecto en producción, pero en vez de pasarle el PostMockingDbContext y EmailSender, las cuales pueden no estar disponibles para las pruebas, le pasamos sus respectivos Mock, de modo que siempre podemos prever el comportamiento, pudiendo hacer pruebas que sean fiables, sin necesidad de que se tenga acceso a recursos externos. Esto es especialmente útil si se emplean herramientas de CI como Travis o AppVeyor, ya que no van a tener acceso a esos recursos. De hecho, os dejo el enlace a una colaboración que hice hace unos meses hablando sobre AppVeyor y la integración continua.

    Ademas, como dato adicional, podemos ver la salida de consola del test unitario, donde ademas de saber que se ha ejecutado correctamente, podríamos ver el reporte:

    report

    Como habitualmente, dejo el enlace de GitHub para descargar el proyecto y poder probarlo, en este caso, desarrollado en .Net Core. Para ampliar información, dejo también la documentación de Moq.Net.

    **La entrada Rompiendo los limites: Mocking en las Pruebas Unitarias .Net se publicó primero en Fixed Buffer.**

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

    Variable not found

    Enlaces interesantes 336

    noviembre 05, 2018 08:34

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

    Por si te lo perdiste...

    .NET / .NET Core

    ASP.NET / ASP.NET Core

    Azure / Cloud

    Conceptos / Patrones / Buenas prácticas

    HTML / CSS / Javascript

    Visual Studio / Complementos / Herramientas

    Xamarin

    Otros

    Publicado en: www.variablenotfound.com.

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

    Blog Bitix

    Formatear con color sentencias SQL o código fuente en la terminal con Java y Jansi

    noviembre 03, 2018 10:30

    Java

    Usando la librería Jansi es posible hacer que un programa Java emita texto en color en la terminal. Emitir color en la terminal sirve para identificar más fácilmente y visualmente ciertas partes del texto, por ejemplo, emitiendo en rojo mensajes importantes o con color amarillo de fondo algún dato. Una utilidad práctica es formatear con color una sentencia SQL que se va a ejecutar en una aplicación a modo de traza o con colores el código fuente de un archivo Java u otro tipo de archivo de texto.

    Una forma sencilla para formatear con colores un archivo de código fuente Java o una sentencia SQL sin llegar a hacer un procesador de sintaxis de ese lenguaje o formato es utilizar expresiones regulares y grupos de captura. En ambos casos hay partes que son palabras claves, números o cadenas en definitiva elementos que se deseen destacar. Con las clases Pattern y Matcher de Java se van obteniendo los diferentes grupos de captura de forma secuencial. Según sea el grupo capturado del elemento actual se utiliza Jansi para formatearlo con el color que le corresponde.

    Una formateador simple para cada uno de estos casos serían los siguientes donde se utiliza una expresión regular con diferentes grupos de captura con nombre. El primer caso es para formatear con color una sentencia SQL sencilla en el método printSql() y el segundo el programa Hola mundo de Java en el metodo printJava().

    El resultado en la terminal es el siguiente.

    Formateado de sentencia SQL y código Java

    Este ejemplo es una aplicación útil de las expresiones regulares. En este caso he usado Jansi para emitir en la terminal texto con color pero de forma similar esto se puede usar para formatear en una web el mismo texto transformándolo y generando el HTML con las clases CSS adecuadas.

    El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew installDist && ./build/install/JavaRegexFormatter/bin/JavaRegexFormatter.

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

    Fixed Buffer

    ¡Reconocimiento MVP Award 2018!

    noviembre 01, 2018 07:12

    MVP Award

    Hoy por la tarde he recibido un correo de Microsoft que decía:

    mvp

    Estoy muy ilusionado de recibir este reconocimiento a la labor de este tiempo en MSDN y estas ultimas semanas con el blog, solo espero poder seguir aprendiendo y estar a la altura de este galardón.

    Es todo un honor para mi poder entrar a formar parte de este grupo de grandes profesionales, entre los que están muchas personas que admiro por su gran labor, como el gran Leandro Tuttini, el señor Williams Morales o don José M. Aguilar, cuyo blog Variable not found (de obligada lectura, mis dieces) me incito a abrir el mio.

    Muchas gracias al programa MVP de Microsoft por darme este premio.

    Muchas gracias a los compañeros que me aguantáis día a día me aguantáis hablando sobre proyectos interesantes, algunos incluso sin poder huir durante la ida y vuelta al trabajo (Eso lo sabe bien Rubén de MascandoBits, la otra persona que me incitó a abrir un blog, incluso haciendo algunas colaboraciones en el suyo, mis dieces también).

    Pero sobre todo, millones de gracias a mi familia que me ha apoyado, y en especial a mi novia que es la que mas tiene que aguantar los días pegado a la pantalla del ordenador y la que más me apoya, sin ella, nada de esto sería posible.

     

    **La entrada ¡Reconocimiento MVP Award 2018! se publicó primero en Fixed Buffer.**

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

    Picando Código

    Hack2Progress – Hackathon en la Universidad de Cantabria, Santander

    octubre 31, 2018 11:00

    Los días 16 y 17 de noviembre se celebra en Santander la IV Edición de Hack2Progress. La cita en es la Casa del Estudiante – Torre A – Universidad de Cantabria.

    Hack2Progress IV

    ¿Qué es Hack2Progress?

    Se trata de un Hackathon social que te permite impulsar tu carrera profesional, conocer tus límites, compartir conocimientos y desarrollar una idea. En esta edición la idea debe estar relacionada con el sector energía (eficiencia, consumo, ….) y cómo ámbito tecnológico conductor el uso del servicio cloud de Azure (cuentas gratuitas disponibles) o si dispone de una cuenta de otro proveedor (AWS, Google Cloud, …). Todo en un ambiente muy creativo y amigable.

    Hace mucho que no voy a una hackathon, pero son muy divertidas y enriquecedoras. En esta ocasión la asistencia es gratuita, y se ofrece comida, bebida, snacks, material de oficina y lo necesario para desarrollar la idea. El jurado estará para enseñar, aconsejar y valorar a los participantes y habrá gente de la organización disponible para que todos se sientan cómodos. Serán 24 horas de hackathon, con 3 premios que suman 4.800 €. Es un hackathon colaborativo y no competitivo, de mis preferidos. ¡Suena muy interesante!

    Edición pasada

    La III Edición de Hack2Progress congregó a 80 personas repartidas en 21 equipos de 2 a 5 personas, con diferentes perfiles: estudiantes universitarios y de FP, trabajadores en activo y en paro.

    Pueden visitar el sitio web para ver más información o directamente registrarse.

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

    proyectos Ágiles

    Refactorización organizativa Agile – Parte 1

    octubre 30, 2018 07:00

    La refactorización “obligatoria” de la organización, necesaria para ser más ágil, tiene muchos beneficios pero, como cualquier diseño organizativo, también tiene problemas típicos que se deben esperar (por ejemplo, efecto silo en equipos, no tener una base tecnológica lista, cómo tratar con los mandos intermedios). A continuación se muestran algunas opciones para lidiar con estas situaciones antes de que se vuelvan difíciles. Otro tema que también hay que considerar es que no tiene sentido el concepto de “transformación corporativa” y qué hacer si te encuentras en esta situación.

    Versión en español

    English version

    Presentaciones relacionadas

    Artículos relacionados:

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

    Meta-Info

    ¿Que es?

    Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

    rss subscripción

    Sponsors

    Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
    planetacodigo

    planetacodigo

    Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

    Idea: Juanjo Navarro

    Diseño: Albin