Quantcast
Channel: Planeta Código
Viewing all 2708 articles
Browse latest View live

proyectos Ágiles: Refactorización organizativa Agile – Parte 1

$
0
0

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:


Picando Código: Hack2Progress – Hackathon en la Universidad de Cantabria, Santander

$
0
0

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.

Fixed Buffer: ¡Reconocimiento MVP Award 2018!

$
0
0

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

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

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

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

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

Variable not found: Enlaces interesantes 336

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

Metodologías ágiles. De lo racional a la inspiración.: Agile Fluency Model en castellano

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

Fixed Buffer: Rompiendo los limites: Mocking en las Pruebas Unitarias .Net

$
0
0

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


Variable not found: Localizar errores de validación del binding en ASP.NET Core MVC

$
0
0
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 otro post.
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.

Israel Perales: Instalar nano en SLES

$
0
0
Instalar nano en SLES

Para instalar en nano es necesario tener la contraseña del usuario root y ejecutar los siguientes comandos

Si estas en SLES 12 SP 3

zypper addrepo https://download.opensuse.org/repositories/editors/SLE_12_SP3/editors.repo

zypper refresh

zypper install nano

Si estas en SLES 15

zypper addrepo https://download.opensuse.org/repositories/editors/SLE_15/editors.repo

zypper refresh

zypper install nano

Aunque siempre debemos conocer un poco como funciona vi, no sabemos cuando nos puede ser útil.

Israel Perales: Habilitar el completado de comandos de docker en el bash de SLES

$
0
0
Habilitar el completado de comandos de docker en el bash de SLES

Siempre que trabajamos con el bash nunca esta de mas una ayuda, en este caso el completado de los comandos nos sera útil, después de escribir docker im presionamos tab y nos sales algunas opciones.

Como súper usuario ejecutamos el siguiente comando, salimos y volvemos a entrar a la consola.

curl https://raw.githubusercontent.com/docker/docker-ce/master/components/cli/contrib/completion/bash/docker -o /etc/bash_completion.d/docker.sh

Blog Bitix: Novedades de Java EE 8

$
0
0
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, PayaraKumuluz 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.

Navegapolis: Motivación laboral, cultura empresarial y estilo de liderazgo

$
0
0

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 

 

Metodologías ágiles. De lo racional a la inspiración.: Scrum master a tiempo completo: 42 Tareas

$
0
0
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

Variable not found: Enlaces interesantes 337

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

Variable not found: Establecer textos por defecto y localizados en validaciones de ASP.NET Core MVC

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

Metodologías ágiles. De lo racional a la inspiración.: Si llevo Kanban, también llevo los principios ágiles

$
0
0
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

Picando Código: Cerveza de DATA – Donde Pinta 2.0

$
0
0

¿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

Bitácora de Javier Gutiérrez Chamorro (Guti): Casio fx-CP400 (Operaciones)

$
0
0

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

Blog Bitix: El agotamiento de direcciones IPv4 y el protocolo de internet IPv6

$
0
0

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

Viewing all 2708 articles
Browse latest View live