¿Por qué los desarrolladores y las empresas eligen Angular?

16/01/2018
Artículo original

Logo de Angular

Este artículo es una traducción del original Why Developers and Companies Choose Angular? escrito en Medium dentro del Grupo de Usuarios de Angular de Japón, el 25 diciembre de 2017.

Mi nombre es Stephen Fluin, y soy un promotor/evangelista en el equipo de Angular en Google. Tengo el privilegio de trabajar con miles de desarrolladores y empresas cada año, incluyendo una visita a Japón este año para participar en el increíble evento de ng-Japón.

Foto de familia de ng-Japan

De hablar con miles de desarrolladores, me queda claro que el hecho de desarrollar buenas aplicaciones de forma rápida es un desafío clave. Empoderar a los empleados y socios a través de aplicaciones empresariales, e interactuar con los clientes de manera exitosa, requiere de la capacidad de construir experiencias a escala en toda la organización.

Angular es una plataforma que entiende estos problemas, y por eso le pregunto a casi todo el mundo que conozco por qué usan Angular. Estas son las 5 razones principales que más se repiten.

1.- Angular es de ideas fijas

A lo largo de cualquier proceso de desarrollo, se deben tomar miles de decisiones, cosas que van desde las características del producto hasta las decisiones tecnológicas que afectarán la agilidad futura del código base.

Angular es dogmático, dando a los desarrolladores muchas decisiones tomadas por defecto como la conectividad de red, administración de estados, elección del idioma, las herramientas para compilar aplicaciones... Estos valores predeterminados se prueban y se validan continuamente entre sí para garantizar que la plataforma Angular avanza a un ritmo constante y fiable. Estas opciones por defecto se han diseñado teniendo en cuenta la libertad y la flexibilidad. Las empresas y las organizaciones tienen un montón de necesidades diferentes... Angular permite cambiar estos valores predeterminados cuando sea conveniente. Un buen ejemplo de esto es nuestra biblioteca de Material Design. Material Design fue construido para sacar el máximo provecho de lo último y lo mejor de Angular, pero Angular permite a las empresas elegir y utilizar cualquier biblioteca de interfaz de usuario que satisfaga sus necesidades.

2.- Angular escala

Angular fue creado en Google para resolver los problemas de escalabilidad de Google. Para Google, esto implica millones de líneas de código, miles de ingenieros, una amplia variedad de programas, de requerimientos y de flujos de trabajo. La plataforma está diseñada para permitirte construir y administrar código compartido y dividir el trabajo entre los roles apropiados. Muchos equipos tienen diseñadores, ingenieros, responsables de control de calidad independientes, y otros roles del estilo que trabajan de manera individual. El modelo basado en componentes utilizado en Angular fue diseñado para separar todos estos roles, y para permitir a un mayor número de desarrolladores participar en un desarrollo colaborativo.

El mantenimiento de aplicaciones es otra gran preocupación para la mayoría de los desarrolladores. Angular cubre esta necesidad fundamentalmente de dos maneras. En primer lugar, los desarrolladores en Angular utilizan TypeScript, que les permite encontrar bugs y errores muy pronto durante el proceso de desarrollo. TypeScript también permite a los desarrolladores que no están familiarizados con el código de una aplicación, ponerse muy rápidamente al día debido a su capacidad para ver de inmediato los tipos de datos que se mueven a través de la aplicación. El equipo de Angular construyó Angular con TypeScript por estos motivos. Finalmente, Angular se centra en la capacidad de hacer pruebas y ser testado. La Inyección de Dependencias es una parte fundamental de Angular que hace que las pruebas sean más fáciles de escribir. Angular también incluye soporte para pruebas de principio a fin con Protactor.

3.- Angular es fiable

El equipo de Angular está comprometido a hacer evolucionar Angular de una manera meticulosa y planificada, con un calendario de lanzamiento público que permite a las empresas aprender y planificarse para los futuros cambios en la plataforma.

Debido a que Angular es un producto de Google, es capaz de aprovechar la infraestructura de pruebas del gigante de Internet. Cada cambio que se hace en Angular se valida contra cada proyecto Angular dentro de Google. Esto significa que antes de que cualquier versión se libere públicamente, el framework ya está en uso en cientos de proyectos, maximizando la posibilidad de que no haya cambios de paradigma o regresiones no intencionadas.

4.- Angular no es un extraño

Los desarrolladores que utilizan Angular en su mayoría vienen de una de dos ramas.

La primera rama son desarrolladores con experiencia en AngularJS. Estos desarrolladores ya están familiarizados con ciertas cosas como las plantillas declarativas, la inyección de dependencias, las tuberías, los servicios y muchos de los conceptos que son fundamentales en Angular. Para estos desarrolladores, existe un nivel de familiaridad que hace que la curva de aprendizaje sea muy rápida. Permitir la migración de AngularJS a Angular fue uno de los objetivos del diseño, y ngUpgrade permite a las empresas migrar de forma lenta y progresiva de AngularJS a Angular.

La otra rama incluye a desarrolladores que vienen de Java o C#.NET. Ambos lenguajes utilizan tipado fuerte, y tienen un concepto centralizado de aplicación que es muy similar a la arquitectura requerida por Angular. Las aplicaciones Angular se dividen en módulos y componentes, y los desarrolladores tienen la capacidad de importar y exportar sub-partes de su aplicación según sea necesario. Este es un modelo mental muy conocido y también ayuda a los desarrolladores a ponerse al día más rápidamente, y les ayuda a diseñar sus aplicaciones con éxito.

5.- Angular tiene un Ecosistema potente

Imagen de Tokio

Existen miles de herramientas reutilizables, bibliotecas y ejemplos de código a través de Internet para Angular y AngularJS, y un gran número de estas herramientas, o han sido actualizadas para trabajar con Angular, o ya están en proceso de actualización. Los desarrolladores como VMware, Teradata, ag-Grid, NativeScript, y muchos otros son completamente compatibles con Angular.

20 backlinks de sitios de analitica gratis

15/01/2018
Artículo original

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

¿Buscas mejorar el posicionamiento web de tu sitio en Internet? Toda estrategia SEO requiere esfuerzo de linkbuilding, por supuesto una buena estrategia y campaña de construcción de enlaces, hoy mencionaremos 20 backlinks de sitios de analitica web para fortalecer y diversificar tu campaña SEO, por supuesto todos estos enlaces de calidad son gratis. ¿Qué son sitios web de analitica? Una buena estrategia SEO viene acompañada de diversificación de enlaces, los sitios de analitica web dan a nuestra campaña esta diversidad. Estos sitios web en su misión inicial darán algún tipo de analitica del sitio web ingresado en su programación, puede

Compartir en Facebook

Comparte en Google Plus

Compartir en Twitter

El post 20 backlinks de sitios de analitica gratis aparece en el Blog de Jonathan Melgoza

Los mejores plugins wordpress de botones sociales para compartir

11/01/2018
Artículo original

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

El día de hoy tenemos un articulo de invitado de Arturo de Jesús de Todopatuweb donde nos habla sobre los mejores plugins wordpress de botones sociales para compartir tu contenido y hacer crecer tu sitio web, tú ¿Ya estas utilizando un plugin social en tu sitio? Ya hemos hablado anteriormente como aumentar la seguridad de tu wordpress y hoy toca el turno de ver como aumentar la difusión de tu contenido a traves de permitir que los usuarios puedan compartir tu contenido en su red social favorita. De una vez por todas acompáñame a definir cuáles son los mejores plugins de

Compartir en Facebook

Comparte en Google Plus

Compartir en Twitter

El post Los mejores plugins wordpress de botones sociales para compartir aparece en el Blog de Jonathan Melgoza

Terraformando tu infraestructura en el Cloud

11/01/2018
Artículo original

Terraform

Terraform nos permite describir, evaluar y aplicar cambios en nuestra infraestructura mantenida como código (infrastructure as code).

Creado por Hashicorp y liberado como open source, Terraform se une a productazos de esta misma compañía como Vagrant, Vault, Nomad o Consul, los cuales destacan historicamente por su calidad y flexibilidad.

Introducción

Para iniciarte en Terraform, un buen punto de partida puede ser la charla "Terraform: Managing your Infrastructure as code" que los chicos de @FlyWireEng impartieron en la Software Craftsmanship de Barcelona:

En ella podemos ver en detalle el flujo básico de trabajo con Terraform utilizando uno de sus providers de Cloud como es el de AWS. En cualquier caso, la lista de los providers soportados es simplemente impresionante, ya que uno de sus objetivos principales es el de ser capaz de conectar y coordinar cualquier entorno ya sea Cloud como on-premises. En esta lista de providers, entre otros, podemos encontrar: Azure, Bitbucket, Cloudflare, DigitalOcean, DNSimple, Docker, Google Cloud, Kubernetes, OpenStack, OVH, PostgreSQL y muchos más.

Terraform y su provider de AWS

Para que te sea más sencillo iniciarte en Terraform, vamos a crear un proyecto nuevo basado en el provider de AWS. Crearemos así un directorio hello-terraform-aws y dentro, generaremos la descripción inicial de la infraestructura en el fichero main.tf con el siguiente contenido:

resource "aws_s3_bucket" "mycompany-database-dumps" {
    bucket = "mycompany-database-dumps"
    acl = "private"
}

Como podéis ver, estas lineas describen un bucket en el servicio de S3 de AWS. Ahora que ya tenemos alguna descripción que hace uso de uno de los providers de Terraform, es buen momento para inicializar nuestro proyecto y que Terraform descargue los providers necesarios ejecutando el comando terraform init:

$ terraform init

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "aws" (1.6.0)...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 1.6"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Como podemos ver por el output del comando terraform init, el siguiente paso es ver el impacto de nuestra configuración con el comando terraform plan. El comando plan permite que Terraform evalue la infraestructura descrita en main.tf y la compare con el estado actual de nuestra cuenta en AWS, mostrando un resumen de aquellas acciones que se aplicarían si finalmente ejecutamos la definición.

Si lo ejecutamos en nuestro caso, al no existir todavía ningún bucket con el nombre mycompany-database-dumps, Terraform nos mostrará el siguiente resultado:

$ terraform plan
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Default: us-east-1
  Enter a value: eu-west-1

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_s3_bucket.mycompany-database-dumps
      id:                  
      acceleration_status: 
      acl:                 "private"
      arn:                 
      bucket:              "mycompany-database-dumps"
      bucket_domain_name:  
      force_destroy:       "false"
      hosted_zone_id:      
      region:              
      request_payer:       
      versioning.#:        
      website_domain:      
      website_endpoint:    

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Como podemos ver al principio del comando, Terraform nos va a preguntar todo aquello que desconoce al no haber sido especificado en el main.tf. Es el caso por ejemplo de la región de AWS. Si no queremos estar poniendola todo el rato, lo mejor es añadir esta definición al principio del main.tf:

provider "aws" {
  access_key = "XXX"
  secret_key = "XXX"
  region = "eu-central-1"
}

Lo siguiente es fijarnos en el resumen del final del comando, el cual nos indica que al ser un bucket nuevo, el plan será crear un recurso, cambiar 0 y destruir 0. Vamos pues a ello!!

Para aplicar los cambios descritos, utilizaremos el comando terraform apply:

$ terraform apply
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Default: us-east-1
  Enter a value: eu-west-1

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + aws_s3_bucket.mycompany-database-dumps
      id:                  
      acceleration_status: 
      acl:                 "private"
      arn:                 
      bucket:              "mycompany-database-dumps"
      bucket_domain_name:  
      force_destroy:       "false"
      hosted_zone_id:      
      region:              
      request_payer:       
      versioning.#:        
      website_domain:      
      website_endpoint:    

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_s3_bucket.mycompany-database-dumps: Creating...
  acceleration_status: "" => ""
  acl:                 "" => "private"
  arn:                 "" => ""
  bucket:              "" => "mycompany-database-dumps"
  bucket_domain_name:  "" => ""
  force_destroy:       "" => "false"
  hosted_zone_id:      "" => ""
  region:              "" => ""
  request_payer:       "" => ""
  versioning.#:        "" => ""
  website_domain:      "" => ""
  website_endpoint:    "" => ""
aws_s3_bucket.mycompany-database-dumps: Creation complete after 5s (ID: mycompany-database-dumps)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Y listo!! Ya tenemos nuestros recursos definidos y sincronizados con nuestra infraestructura. Si nos queda alguna duda, sólo tenemos que volver a ejectuar terraform plan para ver que no hay cambios a realizar y que todo está sincronizado:

$ terraform plan
provider.aws.region
  The region where AWS operations will take place. Examples
  are us-east-1, us-west-2, etc.

  Default: us-east-1
  Enter a value: eu-west-1

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aws_s3_bucket.mycompany-database-dumps: Refreshing state... (ID: mycompany-database-dumps)

------------------------------------------------------------------------

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.

Ingeniería inversa de nuestra infraestructura

Para completar esta introducción y si ya tienes una infraestructura existente, como es nuestro caso, una buena opción para aprender con ejemplos es terraformar tu infraestructura y ver el resultado. Con esto quiero decir generar el fichero de descripción de Terraform a partir de la infraestructura ya existe, de forma que al intentar ejectuar terraform plan, nos diga que no hay cambios a aplicar.

Para ello, en la parte de Terraform debemos contar tanto con el script de definición main.tf como con el estado actual o descripción completa de todos los elementos de la infraestructura que ya se han consolidado. Esta definición detallada la guarda Terraform en el fichero terraform.tfstate y se conoce como state (estado actual de sincronización).

Así pues y para conseguir este objetivo, el primer impulso es examinar el comando terraform import, pero como podemos ver, todavía no está listo para ser utilizado según la documentación oficial, ya que aunque permite importar configuración al state, no nos genera su descripción de recursos:

The current implementation of Terraform import can only import resources into the state. It does not generate configuration. A future version of Terraform will fully generate configuration, significantly simplifying this process.

Una alternativa pues para generar ambos ficheros es utilizar Terraforming, el cual se encuentra disponible como contenedor Docker.

Suponiendo que ya tenemos una cuenta en AWS con varias instancias corriendo en EC2, una forma de recuperar el estado actual y "terraformar" así nuestra infraestructura como código sería producir antes de nada el fichero de definición de recursos (en esta caso lo generaremos automáticamente examinando la infraestructura existente en AWS):

docker run --rm  --name terraforming \
    -e AWS_ACCESS_KEY_ID=XXX \
    -e AWS_SECRET_ACCESS_KEY=XXX \
    -e AWS_DEFAULT_REGION=eu-west-1 \
    quay.io/dtan4/terraforming:latest terraforming ec2 > main.tf

Como podemos ver, pasar las credenciales de acceso a AWS en este caso es necesario, ya que estas no se encuentran definidas en el contenedor Docker. Por otra parte, lo único que queda pues es ejecutar el comando "terraforming" pasándole el servicio sobre el que lo aplicaremos de entre todos los soportados en AWS (asg, dbpg, dbsg, dbsn, ec2, ecc, ecsn, elb, iamg, iamgm, iamgp, iamip, iamp iamr, iamrp, iamu, iamup, nacl, r53r, r53z, rds, rt, rta, s3, sg, sn y vpc).

Finalmente, para dejar a Terraform con el state correcto, debemos generar también el terraform.tfstate. En caso contrario, nos estaría pidiendo aplicar cambios que ya tenemos consolidados en nuestra infraestura al desconocer que ya se aplicaron en su momento. Para ello podemos volver a invocar a terraforming, esta vez con la opción --tfstate:

docker run --rm  --name terraforming 
    -e AWS_ACCESS_KEY_ID=XXX \
    -e AWS_SECRET_ACCESS_KEY=XXX \
    -e AWS_DEFAULT_REGION=eu-west-1 \
    quay.io/dtan4/terraforming:latest terraforming ec2 --tfstate > terraform.tfstate

Si ahora ejecutamos terraform plan, nos debería decir que todo está aplicado y correcto como en el caso del ejemplo del bucket :)

Conclusión

Terraform nos permite de una forma declarativa construir y mantener la definición de nuestra infraestructura como si de código se tratase. Su potencia y capacidad de adaptación a múltiples entornos y necesidades hace que se especialmente interesante en entornos muy dinámicos, cambiantes y con distintos entornos de ejecución (on-premises, cloud o híbrido).

También te recomendamos

El día en que Netflix salió de nuestra televisión

Google anuncia que Cloud SQL ya permite conexiones nativas MySQL

Amazon Simple Notification Service (SNS): notificaciones push móviles para Android e iOS

-
La noticia Terraformando tu infraestructura en el Cloud fue publicada originalmente en Genbeta Dev por Ricardo Borillo .

Gestión de errores con Node.js - Cambios en la versión 9 y cómo proceder

09/01/2018
Artículo original

En la versión 9 de Node.js llegaron cambios a la forma de lanzar y gestionar errores en tiempo ejecución. Estos cambios empezaron a aparecer ya en versiones tardías de Node.js 8 y se continuaron y terminaron en las versiones 9.x.

Hasta su versión 8, la mayoría de los errores lanzados por Node.js consistían tan solo en un mensaje de texto asociado a los mismos. Si querías que tu código se comportarse de una forma determinada en función de cuál fuese el error, lo que tenías que hacer era comparar la cadena de texto del mensaje de error con algún valor conocido. El resultado era código de este estilo:

 try {     
     //hacer una llamada a la API de Node.js aquí
     } catch (error)  {
        if (error.message === 'Este es l mensaje' {
           // haz algo 
        }  else { 
           // haz otra cosa
        }
   }

Es evidente que esto es una fuente de problemas. Para empezar ese mensaje puede cambiar con el tiempo. Bastaría con que cambiase una coma para que ya no se detectase correctamente. Por otro lado si te equivocas y metes un error tipográfico (como de hecho hemos puesto en el fragmento anterior, a propósito), tampoco te funcionará.

Además para el equipo que está desarrollando Node.js también era un problema grave. Cuando aparecía un error y lo querían arreglar, aunque solo fuera un pequeño fallo tipográfico en un mensaje de error, debían marcar el cambio como un cambio "mayor" de SemVer (versionamiento semántico); ya que el cambio podía romper la compatibilidad hacia atrás de todos los desarrolladores que estuvieran usando la cadena en una comparación y así detectar el error. Esto implica que el cambio solo se puede incluir en el siguiente lanzamiento "mayor" (la siguiente versión grande que cambia el primer número) y que no se puede portar hacia atrás a las versiones anteriores. En el caso de un error tipográfico quizás no sea nada muy importante, pero si se tratase de un mensaje que está mal o que lleva a conclusiones erróneas puede que sea una preocupación más grave.

La fuerte dependencia en la cadena de texto del mensaje también supone un reto para la internacionalización. Node.js ha hecho grandes avances en este frente a través de la incorporación de ICU y APIs para poder internacionalizar código de aplicaciones. Para dar un paso más y permitir la internacionalización de los mensajes de error que devuelve Node.js, se debía permitir que el error en concreto pueda identificarse sin utilizar en absoluto la propia cadena del mensaje de error. Si no se hiciese así no se podrían internacionalizar los mensajes, ya que según el idioma de cada servidor podrían cambiar y no habría forma de detectarlos comparando las cadenas de texto.

Los cambios que se han llevado a cabo consisten en añadir un "código" a todos los objetos de error lanzados por las APIs de Node.js, los cuales se pueden consultar en la documentación de la API de Node.js.

Esto permite que el ejemplo de código anterior se pueda volver a escribir de esta manera:

 try {     
     // hacer una llamada a la API de Node.js aquí
     } catch (error)  {
       if (error.message === 'ERR_INVALID_URL_SCHEME' {
           // haz algo 
        }  else { 
           // haz otra cosa
        }
   }

Manera mucho más clara, eficiente y que, si el mensaje cambia en el futuro, el código no se vería afectado dado que el código del error no habrá cambiado. Además de poder detectarlo independientemente de los ajustes de localización en los que se ejecute la aplicación de Node.js.

Ahora que los códigos de error están incorporados a Node.js, es posible que actualicen los mensajes de error sin tener que esperar a nuevas versiones "grandes", y sin romper la compatibilidad de tu código..

¿Qué debes hacer?

Si usas el estilo antiguo de "parsear" las cadenas de mensajes de error para gestionar tus errores lo que debes hacer es:

  1. Localiza todos los fragmentos de código en los que estés utilizando la cadena del mensaje de error. Debería ser muy fácil con una simple búsqueda de tu editor de código favorito.
  2. Una vez hecho esto, salvo en los casos en los que no sea absolutamente necesario (y no debería serlo), elimina el código que haga uso de esas cadenas de texto de mensajes de error.
  3. Actualiza el código para emplear el código de error correspondiente en lugar del mensaje, usando la lista de errores enlazada anteriormente para localizar el más apropiado.

Si necesitases dar soporte simultáneo a más de una versión de Node.js (una antigua y una nueva), de modo que se soporten ambos estilos de gestión de errores, puedes hacer algo como esto:

 try {     
       // hacer una llamada a la API de Node.js
  } catch (error)  {
               if ((!error.code && (error.message === 'Este es l mensaje'))  | |
                   (error.code === 'ERR_INVALID_URL_SCHEME')
                     )  {
                   // haz algo 
                }  else { 
                   // haz otra cosa
  }

De este modo podrás mantener el código antiguo si en tu servidor o el de un cliente no puedes actualizar la versión de Node.js, y tendrás el código preparado para funcionar sin cambios en cuanto actualices a una versión reciente del runtime de Node.js.

Creación de aplicaciones React "The Agile Way"

08/01/2018
Artículo original

React

React se ha convertido en los últimos tiempos en una de las librerías más utilizadas a la hora de afrontar el problema de la comunicación con el DOM. Gracias al trabajo de muchos desarrolladores y divulgadores, existen multitud de recursos tanto gratuitos como de pago que hacen que aprender React sea una tarea relativamente sencilla, así que no nos vamos a centrar tanto en el manejo de propiedades o en la gestión del estado en React, sino más en su uso como solución general dentro de un contexto Agile.

Este artículo no va de porqué React y no Angular 4 o VueJS. Si esperabas un flame al respecto siento decepcionarte. Este artículo se centra en como hacer que una herramienta como React se alinee con nuestra forma de enfocar el desarrollo de software.

La iteración 0

Cada vez que iniciamos un nuevo proyecto debemos crear la estructura del mismo y preparar las herramientas necesarias para ir añadiendo funcionalidad de forma progresiva haciéndola llegar a producción de la forma más rápida y efectiva. Esta construcción incial en la "iteración 0", se conoce como Walking Skeleton y está descrita en detalle en el Growing Object-Oriented Software, Guided by Tests, siendo además de vital importancia para garantizar una correcta evolución del proyecto. Si no estáis familiarizados con el concepto, la idea principal es intentar que la primera historia de usuario que implementemos en el sistema, por muy mínima que sea, actue como una bala trazadora obligándonos a definir todo nuestro sistema, construcción, despliegue y puesta en producción desde el principio. De esta forma, cada vez que terminemos una historia de usuario, esta podrá acabar en producción de una forma sencilla y predecible al haber implementado desde el principio todo el ciclo completo de construcción y despliegue.

En el caso de React y comenzando por la creación del proyecto, podemos generar esta estructura inicial mediante una utilidad que se llama create-react-app. Con create-react-app podemos gestionar un proyecto React sin tener que lidiar de forma directa con las complejidades del tooling de nueva generación que envuelve a la mayoría de proyectos JavaScripts actuales. Un pequeño infierno que debes aprender a controlar dominando Babel, NPM, Webpack y algunos otros amigos con los que mantener una relación de amor/odio.

Con create-react-app tenemos gratis:

  • Babel: React, JSX y ES6.
  • Chequeo de tipos con Flow.
  • Linting con ESLint.
  • Webpack: Dev server + soporte para los loaders de CSS e imágenes.
  • Testing con Jest.
  • Autoprefixer.
  • Build script para generación de versiones de producción, con imágenes optimizadas y soporte para Source Maps.
  • Offline-first service worker y webapp manifest para Progressive Web Apps.

Si no te gusta tanta magia negra, siempre puedes ejecutar el comando npm run eject y hacer explícita la configuración utilizada (toda la config se guarda en la raíz de tu proyecto), pudiendo así adaptar tu aplicación a cualquier configuración no soportada inicialmente por create-react-app.

Por supuesto, a la parte cliente tenemos que añadir las llamadas a nuestro backend que expondrá la funcionalidad deseada como servicio. En cualquier caso, simplemente recordaros que para poder conectar con la parte del servidor sin tener problemas de CORS, podemos utilizar la opción de proxy de nuestro servidor de desarrollo.

Empezando la casa por las historias de usuario

Cualquier funcionalidad en un proyecto Agile nace gracias a su formalización como historia de usuario. Aunque todo esto lo explica Mike Cohn muchísimo mejor que yo, me gustaría centrarme en un aspecto que para mi es clave en su escritura y que cada vez veo a más equipos obviar a la hora de crear una buena historia de usuario: El Definition of Done o DoD para los amigos.

Dilbert Almost Done

Sin el DoD, no tenemos una forma clara de verificar que las expectativas de la historia de usuario se han satisfecho una vez finalizada. Es la descripción formal, sistematizada y reproducible del incremento de valor aportado y, como tal, tiene que ayudarnos a guiar nuestro desarrollo dirigido por pruebas.

"No se que tengo que testear" es una de la afirmaciones que más sueles escuchar cuando alguien está comenzando a introducirse a XP (ojo que XP no es un sistema operativo obsoleto que aun vaga por el mundo, sino un conjunto de prácticas técnicas sintetizadas por Kent Bech en Extreme Programming Explained).

¿Como comenzar entonces mis primeras pruebas? Pues si seguimos una aproximación de fuera hacia dentro (outside-in TDD), el primer test del que partiremos será un test de aceptación construido a partir de la definición del DoD que nuestro Product Owner ha escrito para nosotros. Si todo esto te suena un poco a chino y quieres saber más acerca de los tests de aceptación o de testing en general, puedes echarle un ojo a Test-Driven Development by Example de Kent Beck o Growing Object-Oriented Software, Guided by Tests de Steve Freeman.

Centrando un poco más el tema, nuestro objetivo es que cada funcionalidad que se defina en mi backlog en forma de historia de usuario tenga su DoD y pueda dar lugar a un test de aceptación que guíe el desarrollo. Para este primer test de aceptación, nosotros no usamos el concepto de historia de usuario "ejecutable" que podemos ver en Cucumber en su definición más BDD y/o ATDD, sino que es el equipo técnico el que escribe estos tests a partir del DoD y los automatiza para que se ejecuten a través del navegador (aunque comenzamos hace un tiempo con PhantomJS, ahora estamos pasando a Chrome headless y desde hace nada siguiendo muy de cerca el trabajo detrás de Chrome Puppeteer).

Si ya conoces bien la comunidad JavaScript, sabrás que existen mil millones de frameworks para cada cosa, y el tema del testing no es una excepción. Desde la base definida por Selenium y WebDriver podemos encontrar una amplísima gama de opciones con la que tendremos que lidiar antes de poder poner a funcionar nuestro primer test de aceptación, así que si las opciones elegidas para este ejemplo no te convencen, no te preocupes, hay mucho donde elegir!!!

Como curiosidad, comentar que nosotros usamos NightWatch para lanzar este tipo de pruebas y que, para evitar la fragilidad de los tests por cambios en el interfaz, los puntos del UI son accionados mediante una abstracción que definimos sobre los componentes siguiendo el patrón Page Object.

Siguiendo nuestro enfoque dirigido por las pruebas de aceptación, deberíamos de tener el test fallando y en rojo y el Page Object correspondiente definido pero sin posibilidad de interaccionar con ningún componente del interfaz. Estamos en el buen camino!! Sigamos pues adelante :)

Salida de ejecución de Nightwatch

Tests de integración de componentes

Con un test de aceptación en rojo que mantendremos durante el tiempo que tardemos en satisfacer el DoD de la historia de usuario, es hora de comenzar a implementar la funcionalidad deseada mediante la interacción de distintos componentes dentro del interfaz.

Es en este momento momento cuando herramientas como enzyme, gracias al shallow rendering que permite React, nos permitirán ir definiendo como se deben comportar en cuanto a interacción y en cuanto a definición nuestros componentes.

Antes de nada, añadimos las dependencias necesarias a nuestro proyecto, teniendo en cuenta que, si hemos usado create-react-app tal y como hemos comentado anteriormente, Jest será el framework de test que tendremos como base para la ejecución de los tests.


npm install --save-dev chai enzyme react-test-renderer

Una vez todo instalado, ya podemos crear un test en nuestro proyecto.

Aunque se puede hacer de muchas formas, Jest soporta la definición de directorios __tests__ en los distintos niveles de nuestra estructura de código, de forma que podemos dejar en estos directorios las clases de test y los recursos que utilicen en ellas.

Como ejemplo, podemos ver este test que prueba un componente que lista películas de StarWars haciendo uso de un Page Object:


jest.mock('../Repository');

import React from 'react';
import { mount } from 'enzyme';

import App from '../../App';
import Films from '../Films';

const FILM_TITLES = [
  'The Phantom Menace', 
  'Attack of the Clones', 
  'Revenge of the Sith', 
  'A New Hope', 
  'The Empire Strikes Back', 
  'Return of the Jedi', 
  'The Force Awakens'
];

const FILM_EPISODES = FILM_TITLES.map((title, index) => index+1);
const NUMBER_OF_FILMS = FILM_TITLES.length;

describe('Films', () => {
  let wrapper;
  let films;

  beforeEach(() => {
    wrapper = mount();
    films = new Films(wrapper);
  });

  it('should be listed', async () => {
    expect(films.obtainFilms()).toHaveLength(NUMBER_OF_FILMS);   
  });

  it('should show name and episode number', async () => {
    const filmsTitles = films.obtainFilmsTitles();
    expect(filmsTitles).toEqual(FILM_TITLES);

    const filmsEpisodes = films.obtainFilmsEpisodes();
    expect(filmsEpisodes).toEqual(FILM_EPISODES);
  });

  it('should be ordered by episode number', async () => {
    films.obtainFilmsEpisodes().forEach((episode, index) => {
      expect(episode).toEqual(index + 1);
    });
  });
});

Como podemos ver en la primera linea de código del ejemplo, se hace uso de un doble de test o mock, de forma que los datos no se cargan realmente del origen HTTP, sino que se definen para servir ciertas fixtures dentro de un directorio especial que en Jest se llama "__mocks__".

Así pues, sólo nos quedaría echar un vistazo al Page Object utilizado para el acceso a la estructura de películas:


export default class Films {
  constructor(wrapper) {
    this.wrapper = wrapper;
  }
 
  obtainFilms() {
    return this.wrapper.find('.film');
  }
  
  obtainFilmsTitles() {
    return this.wrapper.find('.film .title').map((film) => film.text());
  }

  obtainFilmsEpisodes() {
    return this.wrapper.find('.film .episode').map((film) => parseInt(film.text(), 10));
  }
}

Y, como no, aquí tenemos el componente principal:


import React, { Component } from 'react';

import repository from './Repository';

export default class FilmsPanel extends Component {
  constructor(props) {
    super(props);

    this.state = {
      films: []
    }
  }

  componentWillMount() {
    repository.retrieveFilms()
      .then((response) => {
        this.setState({
          films: response.results
        });
      });
  }

  render() {
    return (
      <div>
        {
          this.state.films.map((film) => {
            return (
              <div className="film" key={film.episode_id}>
                <div className="title">{film.title}</div>
                <div className="episode">{film.episode_id}</div>
              </div>
            );
          })
        }
      </div>
    );
  }
}

Una vez añadido todo el código de test necesario a nuestro proyecto, tendremos que lanzar el comando npm test para ver como la ejecución de las pruebas se queda en modo "watch", esperando cambios por nuestra parte:

Captura De Pantalla De 2017 09 26 16 05 10

Con este ciclo que hemos abierto, sólo debemos continuar iterando en estas definiciones hasta que todos los requisitos del DoD se hayan satisfecho y el test de aceptación general pase por fin de rojo a verde. Well done!!! :)

Conclusión

El framework principal de elección es sólo una parte ínfima del conjunto de decisiones que debemos llegar a tomar para diseñar un proceso de construcción escalable y de calidad. Prácticas como el testing, la integración continua y la correcta definición de historias de usuario tienen un peso intrínseco infinitamente mayor que ser fan y/o gurú de React o de VueJS. Comencemos hoy a interiorizar prácticas independientes de la tecnología empleada y nos convertiremos con el tiempo en verdaderos Agile Developers.

¿Qué practicas estás incorporando a tu ciclo de desarrollo con el fin de mejorarlo día a día?

También te recomendamos

Receta de Flan de turrón en Crock-Pot

Conociendo la API REST de WordPress

Angular.js módulos y arquitectura

-
La noticia Creación de aplicaciones React "The Agile Way" fue publicada originalmente en Genbeta Dev por Ricardo Borillo .

¿Como agregar una imagen a un ActionBar en Android?

08/01/2018
Artículo original

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

El dia de hoy vamos a ver como agregar una imagen a un actionbar en nuestras aplicaciones moviles para este sistema operativo, esto lo haremos gracias a la libreria de compatbilidad de android support v7 y nos permitirá darle mayor branding a nuestra app. Si tu aplicación no tiene actionbar te decimos como agregar una y además por supuesto agregar una imagen a un actionbar en lugar de que muestre el clasico texto o titulo de nuestra aplicación. ¿Como agregar un actionbar a nuestra aplicación android? En caso de que por alguna razón no tengas un actionbar en tu proyecto

Compartir en Facebook

Comparte en Google Plus

Compartir en Twitter

El post ¿Como agregar una imagen a un ActionBar en Android? aparece en el Blog de Jonathan Melgoza

FRIKADAS: Un nuevo tipo de computadora que acabará con los agujeros de seguridad

05/01/2018
Artículo original

Hace poco la Agencia de Proyectos de Investigación Avanzados para la Defensa de EEUU (la famosa DARPA, creadora entre otras cosas, de Internet) anunció que va a invertir 3.6 millones de dólares  (unos 3 millones de euros) en el desarrollo de un nuevo tipo de hardware para computadoras liderado por la Universidad de Michigan. Sus creadores lo definen como "un puzle sin solución". Este proyecto se llama MORPHEUS y está orientado a solucionar un problema común de los ordenadores actuales: la facilidad para explotar fallos de seguridad relacionados con el uso de la memoria.

Alrededor del 40% de los exploits existentes utilizan un puñado conocido de debilidades que se basan en cómo se utiliza el hardware actual. Se trata de cuestiones relacionadas con el uso de memoria, la inyección de código en memoria, errores criptográficos... Solo hay que remitirnos al grave problema de seguridad descubierto anteayer en los procesadores Intel, AMD y ARM.

Por regla general, en cada programa o sistema, la ubicación de la información no cambia de lugar respecto al origen de su espacio de memoria. Esto implica que si un atacante logra encontrar un bug que le permita acceder a la memoria e identifica en dónde está guardada la información relevante puede hacer cosas como inyectar código en posiciones concretas para cambiar el comportamiento del programa o robar claves, por ejemplo. Existen infinidad de exploits y ataques que se aprovechan de esto, como por ejemplo Heartbleed, uno de los más graves de los últimos años que permitía acceder a claves y datos críticos de millones de servidores web conectados a Internet debido a un fallo de este tipo descubierto en OpenSSL.

La solución propuesta por MORPHEUS consiste en un nuevo diseño de hardware que utilizará circuitos diseñados para mover aleatoriamente los datos almacenados en la memoria de la computadora, en lugar de usar posiciones fijas como ocurre ahora.

De este modo, cuando un software malicioso encuentre un bug que se aproveche de esto o que localice la ubicación de memoria en la que se almacenan datos relevantes (como claves, por ejemplo), se encontrará con que la información ya no está ahí y que se ha movido a otro lado, imposible de determinar. El reto está en hacer que los programas lícitos puedan seguir funcionando y creen que han dado con la solución.

Se trata de un concepto muy interesante.

DARPA se ha propuesto como reto (nada sencillo, desde luego) hacer que la mayor parte de las vulnerabilidades actuales sean obsoletas en los próximos 5 años. Para ello está invirtiendo 50 millones de dólares en investigación de ciber-seguridad basada en hardware, en proyectos como MORPHEUS.

¿Lo conseguirán? Hagan sus apuestas...

Nota: Imagen de Toni Blay.

Diseño y simplicidad

29/12/2017
Artículo original

Complejidad

Desarrollar software no es una tarea fácil. Aunque estemos desarrollando algo muy sencillo, es posible que nos compliquemos tanto la vida, que acabemos haciendo algo complejo. Muchas veces con la idea de meter tecnologías o técnicas supuestamente mejores, acabamos aumentando la complejidad del desarrollo. Y esto puede ser algo que a la larga nos cueste muy caro. Tanto en tiempo como en dinero. Así que nunca es importante que nuestros desarrollos tiendan a la sencillez. ¿Pero es eso fácil de conseguir?

¿Por qué desarrollamos software? A veces se nos olvida, pero el propósito de todo lo que desarrollamos es ayudar a la gente. Si estamos desarrollando un navegador web, lo que queremos es que la gente pueda ver sus páginas web favoritas. Si estamos desarrollando un programa de hojas de cálculo, lo que queremos es que la gente pueda introducir datos y hacer cálculos. Y esto es así siempre. A veces desarrollamos para parecer muy listos o desarrollamos para hacernos ricos. Pero eso es perder el foco del objetivo final.

Nuestro fin último tiene que ser el de ayudar a la gente con nuestros desarrollos. Pensando de esta manera nos daremos cuenta de qué funcionalidades son interesantes para las personas que van a usar nuestro desarrollo. Podremos priorizar, e incluso eliminar, tareas.

El valor y el esfuerzo

Supongamos que tenemos 50 funcionalidades para implementar en nuestra aplicación ¿Cómo las priorizamos? Primero podemos decir que toda funcionalidad pendiente de desarrollo tiene un valor y necesita de un determinado esfuerzo para finalizarse.

Valor

El valor, de una posible funcionalidad a desarrollar, es más alto cuanto más ayude a la gente. Pero medir el valor de una posible funcionalidad no siempre es fácil. Aunque no podamos pensar en números exactos, siempre podemos hacer aproximaciones. Cuando pensamos en el valor de una funcionalidad debemos pensar en su valor potencial y en su valor probable. Su valor potencial es el cuánto ayudará a los usuarios o clientes. El valor probable será con cuánta frecuencia los ayudará.

Una funcionalidad que se ejecute pocas veces, pero que tenga un alto valor potencial, es una funcionalidad deseable. Más aun, si además de tener un potencial muy alto, se ejecuta frecuentemente. Si su valor potencial no es tan alto, pero se ejecuta muchas veces, también puede ser interesante ejecutarla, aunque quizá algo menos. En el último escalón estarían esas funcionalidades que ni son ejecutadas a menudo (su valor probable es bajo), ni son demasiado útiles (su valor potencial es bajo).

Esfuerzo

El esfuerzo, es el tiempo que nos costará desarrollar esa funcionalidad, y es más fácilmente representable en números. Al final desarrollar algo le lleva X tiempo a una persona determinada. Lo que no es tan fácil es medirlo, ya que depende de muchos factores. Por ejemplo no todos los programadores trabajan igual de rápido. Además podemos encontrar bastantes costes ocultos que incrementen el esfuerzo, como la mantenibilidad del código, tiempo de análisis del problema, comunicación con otros departamentos o desarrolladores etc.

Mantenimiento

Hay un factor importante que también hay que tener en cuenta en todo desarrollo: el mantenimiento. No basta con desarrollar una funcionalidad, si no que hay que mantenerla a lo largo del tiempo. Y dependiendo de las decisiones que tomemos, esto puede ser más sencillo o más complicado. Debemos pensar también, en que una funcionalidad debe seguir ayudando a usuarios en el futuro, por lo que nuestra funcionalidad tendrá también un esfuerzo y un valor en el futuro.

Calidad y futuro

Todas estas variables nos sirven para explicar porque es deseable un software bien diseñado y con calidad. En realidad es relativamente desarrollar software que ayude a una persona ahora mismo, pero es muchísimo más complicado diseñar un software que ayude a muchas personas durante muchos años. Y es que si lo piensas, si tu aplicación va bien, en un futuro tendrás mucho más trabajo que ahora, ya que tendrás muchos más usuarios según pase el tiempo. Si ignoramos el diseño, y no pensamos en el mantenimiento futuro, acabaremos haciendo un software, que no ayude a sus usuarios o que no lo haga a tiempo.

En un futuro tendrás mucho más trabajo que ahora, ya que tendrás muchos más usuarios según pase el tiempo

El problema es que aunque pensemos en el futuro, hay cosas que serán difíciles de predecir. Esto nos lleva a que es importante tener en cuenta el futuro de nuestro desarrollo, pero es una pérdida de tiempo intentar adivinarlo. ¿Habéis estado involucrados en algún proyecto que se haya pasado de previsor? Seguro que sí. Al final se acaban tomando decisiones equivocadas en base a una predicción que no debía haberse hecho, haciendo el desarrollo futuro mucho más lento y complicado.

Probabilidad de cambio

El tema aquí es claro: cuanto más tiempo esté funcionando nuestro software, más probable es que haya que cambiarlo. Es difícil predecir en qué punto y cuándo cambiará nuestro software, pero es bastante probable que si pasa 20 años funcionando, haya cambiado hasta el 100% del mismo.

Esta ley de probabilidad nos hace cometer tres típicos errores:

Escribir código que no es necesario

Hay muchas veces que estamos desarrollando y nos surge una duda: "¿y si el usuario quisiera hacer esto?". La idea en ese momento nos parece buena y decidimos implementarla para no tener que hacerlo en el futuro. El problema es que eso no es algo que el usuario nos haya pedido, si no algo que nosotros hemos pensado que sería buena idea. Trabajo que nos quitamos en el futuro. Lo malo de este enfoque es que cabe la posibilidad de que esa funcionalidad nunca se utilice, habiendo trabajado para nada. Además, cuando intentamos hacer desarrollos abiertos, tendemos a escribir un código más sucio y feo, lo que todavía complica más las cosas. Hay que ser prácticos y aplicar YAGNI (You Ain’t Gonna Need it ).

No hacer el código sencillo de modificar

Si nuestros desarrollos son demasiado rígidos, serán difíciles de mantener. A veces el código es rígido porque hemos asumido demasiadas cosas. Si los requisitos no están claros, y tomamos decisiones con mucha antelación, nuestro código va a ser difícil de modificar, ya que el diseño no será lo suficientemente versátil. Otras veces, el problema está en el diseño del software (o en su ausencia). Si no dedicamos tiempo suficiente a pensar en el diseño, tendremos problemas en el futuro.

En definitiva, tenemos que pensar en el diseño, sin asumir nada, y haciéndolo en base a la información que conocemos ahora y no en lo que podría pasar en el futuro.

Pasarnos de genéricos

Esto nos ha pasado a todos. Como tenemos dudas en cuanto al futuro comportamiento del software, decidimos desarrollarlo intentando controlar todas las posibles situaciones. Obviamente esto es muy difícil, y más en un entorno de incertidumbre. Debido a esto acabamos haciendo un diseño demasiado complejo. Este ejercicio de sobreingeniería, nos complica tanto que el desarrollo, e incluso el inicial, se ve muy penalizado.

Al igual que antes, hay que ser tan genérico como necesitemos en el momento actual, y con la información que tenemos.

Desarrollo iterativo e incremental

Como os habréis fijado, estos problemas pueden paliarse en gran medida evitando los desarrollos en cascada y tomando un enfoque iterativo e incremental. Decidimos cual es la funcionalidad que queremos implementar, la desarrollamos en base a lo que sabemos ahora, y corregimos los problemas que podamos encontrar. Una vez hayamos hecho esto, volvemos a empezar, con otra funcionalidad. Y luego otra, y luego otra.

Es una buena manera de conseguir un código sencillo y mantenible, así que por eso, desarrollar y diseñar de forma incremental está demostrando ser una técnica tan útil.

Bugs y errores

Da igual lo bueno que seas programando, que al final siempre vas a añadir errores en tú código. Es algo que debemos asumir. Cuanto más grande sea el cambio que tenemos que implementar, más fácil es que cometamos un error que provoque bugs que afecten al sistema o al usuario. Esto nos lleva a pensar que es mejor no modificar el software para evitar problemas futuros, pero como ya hemos dicho antes, todo software tiende a cambiar con el paso del tiempo. Así que nuestra misión como programadores es la de crear diseños que nos permitan modificar el entorno con los menos cambios posibles en el software. Y esto no es nada fácil.

El trabajo ya es lo bastante complicado, como para encima perder el tiempo con errores que no existen.

Una cosa importante aquí, es que solo tenemos que arreglar cosas que no funcionen. Tiene que haber evidencias que demuestren que algo está fallando. A veces los usuarios reportan como error algo que en realidad no lo es, bien porque no está fallando realmente, o bien porque desean una funcionalidad que está ya implementada, pero que desconocen. Como programadores también tendemos a arreglar problemas que todavía no existen, por ejemplo cuando caemos en la trampa de la optimización prematura.

El trabajo ya es lo bastante complicado, como para encima perder el tiempo con errores que no existen.

Simplicidad

Si no modificamos nuestro código, no provocamos errores. Pero como es imposible que no modifiquemos en algún momento una aplicación que esté funcionando a lo largo del tiempo, hay que pensar en la mejor manera de hacerlo.

La mejor manera es intentar hacer cambios pequeños. Pero esto no es suficiente, ya que también tenemos que pensar en la facilidad de mantenimiento en el futuro. Y aquí es donde podemos decir que la facilidad de mantenimiento de cualquier software, es proporcional a la simplicidad de sus partes.

Cuanto más sencillas son las partes de nuestro desarrollo, más fácil es el mantenimiento del sistema en conjunto. Además es mucho más fácil hablar de piezas o partes, ya que es muy difícil manejar la complejidad de todo un sistema. Aunque sea un sistema sencillo, es muy difícil comprender todas las partes del mismo, pero es mucho más asequible conocer cada una de sus partes y más si estas son sencillas.

Aquí debemos pensar otra vez en el esfuerzo que nos lleva un desarrollo. Es cierto que hacer un diseño simple y fácilmente mantenible (ahora, sin pensar en el futuro), puede llevar un poco más de esfuerzo. Pero la experiencia nos dice que es mucho más fácil hacer cambios e introducir nuevas funcionalidades en software sencillo y bien diseñado.

Cuanto más sencillas son las partes de nuestro desarrollo, más fácil es el mantenimiento del sistema en conjunto

El problema de la simplicidad, es que no es igual para todos. Algo que a un programador le parece simple, puede parecerle muy complicado a otro. Aquí debemos pensar en alguien que no conoce nada sobre el código que escribimos, pero tiene que intentar entenderlo. Es algo a lo que nos hemos enfrentado todos. Que levante la mano el que no se ha visto frustrado intentando descifrar el código desarrollado por otra persona. Así que desarrollar pensando en alguien que en un futuro tendrá que descifrar nuestro código (quizá nuestro yo futuro), es siempre un acierto.

Como la simplicidad conlleva un esfuerzo, es lógico pensar en cómo de simples tenemos que ser. Aquí la regla es sencilla: cuanto más sencillo sea nuestro código, mejor. Para tontos. Lo contrario significa que posibles cambios en el futuro no se hagan bien, ya que no se entiende bien el diseño, y esto provoque errores y pérdidas de tiempo.

Hay además unas cuantas reglas que podemos utilizar para hacer nuestro código más sencillo:

  • Hay que ser consistente en el código. Si las variables las nombramos utilizando camel case hazlo así durante todo el desarrollo. Si los nombres de los métodos están en inglés, que sea así a lo largo de todo el código. La consistencia ayuda mucho a la hora de leer y entender código fuente.
  • Formateo del código. Con los IDE's y editores es más fácil hoy en día, pero no está de más recordarlo. El código bien formateado, con sus espacios y sus saltos de línea para separar conceptos, ayuda mucho a mejorar la legibilidad.
  • Nombres de variables y métodos. Un clásico del código limpio. Haz que los nombres de variables, métodos y funciones sean explicativos de lo que hacen o representan. Es mucho mejor un CalculadorIVAReducido, que un Calculador. Es mucho mejor un IsVIPCustomer que un b_vip_customer. Los nombres son de gran ayuda a la hora de entender el código así que hay que tomarse un tiempo para pensarlos.
  • Comentarios. Los comentarios ayudan, o entorpecen. Si el código necesita un comentario, quizá es que no es lo suficientemente sencillo. Pero si no se puede simplificar más, deja un comentario de por qué lo haces así. Y siempre, siempre evitar los comentarios obvios que solo ensucian el código. En GenbetaDev ya hablamos sobre esto.

Complejidad

Controlado el tema de la simplicidad, debemos hablar de su antagonista, la complejidad. Lo primero a tener en cuenta es que la complejidad, unida a más complejidad, hace las cosas exponencialmente difícil. Si añadimos una nueva funcionalidad, es probable que esta tenga que coordinarse con otras funcionalidades que ya existían en el sistema. Si la nueva funcionalidad es compleja, esto será complicado, pero es que si además las otras funcionalidades que ya existen también son complicadas, la complejidad se dispara. Seguro que alguna vez has ido a añadir un cambio supuestamente sencillo y has acabado perdiendo horas por intentar integrarlo con el código ya existente.

Aunque normalmente añadimos complejidad al añadir funcionalidades, también se puede añadir de otras maneras: como añadiendo más programadores, ampliando el alcance del proyecto, por malentendidos, por malas tecnologías, por un mal diseño etc.

Manejar la complejidad que ya existe no es tarea sencilla. Lo que mejor suele funcionar es ir cambiando partes pequeñas de nuestro código, para hacerlo más simple de forma progresiva. No es algo rápido, y a veces es frustrante, pero es ideal para mejorar el código sin tener que tirar abajo el sistema existente.

Como esto lleva tiempo, lo podemos entender como parte de añadir funcionalidades. Cuando tengamos que añadir una funcionalidad, podemos definir un tiempo para la tarea de simplificar la instauración de esa funcionalidad en el código existente.

También podemos encontrarnos el problema de que no podamos esquivar la complejidad (por ejemplo cuando trabajamos directamente con hardware). En este caso lo que deberemos hacer es esconder la complejidad. Creamos un wrapper para llamar a esas funciones que interactúan con el hardware, y así desacoplamos una parte de la otra, haciendo las cosas mucho más sencillas.

Testing

Para estar seguros de que nuestros programas siguen funcionando en el futuro, tenemos que hacer tests. Y punto. Cuanto más testees un sistema, mayor seguridad tendremos de que ese sistema funciona correctamente. No quiero profundizar mucho más en los tests, pero por muy listo que seas, hay que probar el código que escribes, porque no hay nadie infalible y siempre se cometen errores.

Conclusión

Que nuestro código sea sencillo y mantenible es garantía de éxito. Conseguirlo no es tarea fácil, pero es algo que con la suficiente experiencia y con un buen diseño, es factible de conseguir.

Imagen | Mark Skippe

También te recomendamos

Weinre: Inspector Web Remoto

Muéstrame tus herramientas y te diré quién eres

Tu foto favorita puede ser dos veces mejor

-
La noticia Diseño y simplicidad fue publicada originalmente en Genbeta Dev por rubenfa .

Cómo exponer una DLL de .NET para usarla con Visual Basic 6, VBScript y ASP Clásico

28/12/2017
Artículo original

Logo de ActiveX: hasta el logo era horribleAunque la plataforma .NET lleva ya más de 16 años entre nosotros, la realidad es que hay muchas ocasiones en las que debemos hacer uso de otro tipo de tecnologías más antiguas y menos potentes, basadas en el vetusto estándar COM (también conocido como ActiveX). Por ejemplo, si queremos programar Office debemos utilizar VBA, para automatizar Windows es muy sencillo utilizar scripts escritos con Windows Scripting Host (que por debajo utiliza VBScript o JScript), y por supuesto, muchos tenemos que mantener todavía aplicaciones escritas con Visual Basic 6 o con ASP Clásico (ASP 3.0).

Creo que a la mayoría de los que llevamos muchos (pero que muchos) años en este mundillo, estas tecnologías antiguas nos siguen gustando mucho a pesar de sus obvias limitaciones. Sí, sin duda eran más limitadas, pero eran mucho más sencillas de aprender y utilizar y con poco trabajo podías hacer muchas cosas. Y si no me crees pregúntale a alguien que haya trabajado con Visual FoxPro: aún hoy en día los hay a miles que beben los vientos por esas herramientas.

El caso es que una gran parte de las limitaciones de estas herramientas tienen que ver con que se quedaron congeladas en el tiempo, sin evolucionar para dar soporte a muchos estándares que aparecieron más adelante, como SOAP (ya en decadencia), JSON, servicios REST y cosas por el estilo. Tampoco permiten hacer ciertas "virguerías" que son muy sencillas en plataformas modernas como .NET: multi-subproceso, async/await, manejo de datos en memoria... Y por supuesto la gran mayoría de proyectos Open Source modernos quedan fuera de su alcance.

Para estos casos en los que una aplicación antigua debe soportar características modernas, una estupenda opción consiste en escribir la funcionalidad con una herramienta actual y exponerla hacia estas tecnologías antiguas a través de COM/ActiveX.

Sí, COM era lo peor, y si no sabes lo que es el "DLL Hell" suerte para ti que no lo viviste. Pero sigue siendo una solución universal para componentes bajo Windows, y todas las tecnologías antiguas lo soportan, ¡incluso las de hace más de 20 o 25 años!

La web de Microsoft tiene una detallada documentación sobre cómo hacerlo, pero buena suerte para conseguirlo usando esos contenidos. Son liosos, tediosos y sin ningún ejemplo real. Realmente es complicado sacar algo en limpio de ellos. En Google hay mucha información, pero la inmensa mayoría está anticuada o es incompleta, por lo que buena suerte con eso también.

En realidad, conseguirlo es bastante sencillo si se sabe cómo, e implica unos pocos conceptos y pasos a seguir. En este artículo voy a explicar cómo crear un componente COM/ActiveX sencillo a partir de código .NET escrito con C#, y luego veremos cómo utilizarlo apropiadamente.

Vamos a ello...

Definir un componente COM o ActiveX con C

Abre Visual Studio (yo usaré VS2017) y crea un nuevo proyecto C# de tipo "Biblioteca de clases":

Diálogo de nuevo proyecto

.NET dispone de un espacio de nombres especializado en la compatibilidad con COM y ActiveX llamado InteropServices.

Por lo tanto, lo primero que debes hacer es añadir en la parte de arriba de tu archivo de clases la línea:

using System.Runtime.InteropServices;

para que reconozca automáticamente todas las clases de este espacio de nombres y las puedas usar con facilidad, sin escribir su nombre completo.

Definir una interfaz

Debido al funcionamiento interno de COM.aspx), toda la funcionalidad que vayas a exponer hacia el exterior deberás hacerlo a través de interfaces de programación. Por ello, antes de nada debes pensar bien qué métodos quieres exponer hacia el exterior a través de COM y crear una interfaz que los defina. Por ejemplo, supongamos que quieres exponer un método llamado HacerAlgoComplejo que no devuelve nada y que toma como parámetros una cadena y un número entero. Deberías definir una interfaz (con el nombre que quieras) de forma análoga a esta:

public interface IMiObjeto
{
    void HacerAlgoComplejo(string a, int b);
}

Deberás definir tantas interfaces como objetos quieras exponer desde tu componente final.

Definir la clase correspondiente

Ahora que ya tienes la interfaz con todos los métodos que quieras exponer, debes crear una clase que la implemente. Para ello usarás la sintaxis convencional que consiste en indicar la interfaz a continuación del nombre de clase, separándola con dos puntos (esto es como se hace en C# siempre, no es nada particular de COM).

Es decir, tu clase quedaría similar a:

public class MiObjeto : IMiObjeto
{
    public MiObjeto()  {}

    public void HacerAlgoComplejo(string a, int b)
    {
        //El código que sea va aquí dentro
    }
}

Fíjate en dos cosas:

  1. He definido explícitamente un constructor sin parámetros, que en realidad no hace nada pues su código está vacío. Esto es necesario porque para instanciar un objeto COM necesitas ese constructor y aparentemente el que crea implícitamente el compilador no nos sirve. No cuesta nada: es una línea.
  2. Al contrario de lo que verás escrito por ahí en muchos sitios, no es necesario implementar la interfaz explícitamente. En este fragmento yo no lo he hecho. Puedes hacerlo poniendo IMiObjeto.HacerAlgoComplejo, pero entonces no le podrás poner el modificador de ámbito public delante lo cual te dificulta poder usar la misma clase desde .NET también. Con esto la podrás usar en .NET y con COM simultáneamente.

Además deberás tener en cuenta algunas reglas mínimas que debe cumplir tu clase para que sea compatible:

  • Deberá ser pública, al igual que todos los miembros que quieras exponer
  • No podrá ser abstracta ni estática
  • Todos los tipos que se devuelvan o se usen como parámetros deberán ser públicos
  • Si la clase es derivada de otra que a su vez es también derivada de otra y así sucesivamente, debes saber que al exponerla a COM todo esto se elimina y queda una única herencia, "aplanada" de las demás.
  • Mucho ojo con usar tipos muy especializados, como clases genéricas o cosas específicas de .NET, ya que no se soportarán.
  • No podrás usar parámetros opcionales ni valores por defecto

Mientras lo que expongas sea algo "normal" (un mínimo común denominador) no tendrás problemas.

Adornar la interfaz y la clase con atributos

Vale, ahora bien lo importante y específico para COM/ActiveX. Debes indicar al compilador cómo exponer la clase hacia el exterior. Para conseguirlo debes utilizar ciertos atributos especiales que vienen en el espacio de nombres que colocamos en el primer paso.

Generar y asignar GUIDs

Empecemos por la identificación. Todas las clases e interfaces de COM deben identificarse de manera única para evitar conflictos. Para ello se utilian GUIDs, que son identificadores universales. Debes generar uno para cada interfaz y clase y asignárselos.

Para generar un GUID Visual Studio nos proporciona una utilidad llamada newguid.exe que puedes encontrar en la ruta C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\ o equivalente según la versión de Visual Studio que utilices.

Nota: en anteriores versiones de Visual Studio disponíamos de acceso directo a esta herramienta desde el menú "Herramientas" del entorno. Ahora ya no. De todos modos es muy fácil de solucionar usando el menú Tools>External Tools... y agregando ese comando:

Añadir herramienta

A partir de ese momento dispondrás de un acceso directo en el menú de herramientas (este es el mío):

Nueva herramienta

Una vez que lances la herramienta deberás generar al menos dos nuevos GUID diferentes, uno par ala interfaz y otro para la clase, usando el tipo "Registry Format"

Generador de GUIDs

Con la tecla Copy ya los puedes copiar a Visual Studio listos para usar. Debes asignar uno a la Interfaz y otro a la clase usando el atributo Guid, así:

[Guid({A8E38597-0E0B-45F3-A264-6E0D9CC49598})]
public interface IMiObjeto

....
[Guid({0E234FAE-F5D2-4DA5-A539-628864F28471})]
public class MiObjeto : IMiObjeto

....

Vale, ahora que ya tenemos los GUID debemos asignarle el resto de atributos mínimos que necesitamos. Aunque algunos tienen un valor por defecto yo prefiero asignarlos explícitamente para no dejar lugar a dudas sobre lo que estoy haciendo. Serían en concreto estos:

[Guid({A8E38597-0E0B-45F3-A264-6E0D9CC49598})]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IMiObjeto

....
[Guid({0E234FAE-F5D2-4DA5-A539-628864F28471})]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[ProgId("MiLib.MiObjeto")]
public class MiObjeto : IMiObjeto

....

En el caso de la interfaz le estamos diciendo que implemente una interfaz dual de modo que puede ser utilizada tanto a través de early binding (mediante IUnknown añadiendo una referencia antes de usarla, como lo hace VB6 o VBA de Office) como late binding (que es lo que usan VBScript o ASP clásico, por ejemplo). De todos modos aquí tienes todos los valores posibles.aspx), por si te resultaran útiles.

Además le estamos diciendo que esa interfaz debe ser visible para COM. Si no le ponemos explícitamente este atributo ConVisible no se exportará y tendremos un problema.

En el caso de la clase además se usan dos atributos adicionales:

  • ClassInterface: que indica el tipo de interfaz de clase que se debe generar para COM. En este caso indicamos el valor None porque la interfaz necesaria la hemos generado nosotros ya. Los valores posibles y sus explicaciones los tienes en la MSDN.aspx).
  • ProgID: este es un atributo clave ya que determina la manera en la que vamos a instanciar esta clase desde COM cuando hacemos early binding, es decir, desde un entorno de scripting como VBS o ASP 3.0. Como ves se le pasa una cadena con al menos dos partes separadas por puntos: el nombre de la biblioteca y el nombre de la clase, en nuestro ejemplo: MiLib.MiObjeto.

Una última cosa que debemos hacer es asignarle identificadores en la interfaz a los diferentes métodos que tengamos (no así a las propiedades) para que se identifiquen de manera única y podamos utilizarlas, para lo cual se usa el atributo DispId (de "Dispatch Id"). Este atributo toma un número único que puedes er cualquiera. Yo suelo numerarlas desde el 1 en adelante. Con esto nuestro código de ejemplo quedaría:

[Guid({A8E38597-0E0B-45F3-A264-6E0D9CC49598})]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[ComVisible(true)]
public interface IMiObjeto
{
     [DispId(1)]
    void HacerAlgoComplejo(string a, int b);
}

[Guid({0E234FAE-F5D2-4DA5-A539-628864F28471})]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[ProgId("MiLib.MiObjeto")]
public class MiObjeto : IMiObjeto
{
    public MiObjeto()  {}

    public void HacerAlgoComplejo(string a, int b)
    {
        //El código que sea va aquí dentro
    }
}

dentro del espacio de nombres que le corresponda.

¡Listo! Con esto tenemos ya lo que necesitamos para generar el objeto y podemos compilar nuestra biblioteca. Hazlo y continuamos en un minuto viendo cómo registrarla y usarla. pero antes...

¿Por qué no has usado la opción "Register for COM interop"?

En la propiedades de un proyecto de biblioteca de clases de Visual Studio existe una opción específica llamada de este modo:

Opción para COM en compilación

Lo que hace esta opción es que se registra automáticamente el componente en el registro de Windows en cada compilación (des-registrándolo antes si había una versión previa). Puede ser útil si quieres probarlo desde un script externo (un .vbs por ejemplo) mientras desarrollas, pero teniendo en cuenta que los tests los harás seguramente en .NET y que el funcionamiento es exactamente el mismo, personalmente creo que no merece la pena en absoluto. A la hora de registrarlo en cualquier otra máquina que no sea la tuya tienes que hacerlo a mano, por lo que mejor que aprendas a hacerlo así de todos modos. Y es lo que vamos a ver ahora mismo.

Registrar el componente para poder usarlo

Para poder utilizar un componente COM/ActiveX es necesario registrarlo en el sistema. No llega con copiarlo y listo, como pasa con .NET. Hoy en día no nos damos cuenta de la gran ventaja que supone, pero en su momento fue algo revolucionario ;-)

Para registrar el componente debemos utilizar una herramienta de línea de comandos que viene con la plataforma .NET, así que la tendrás ya disponible en cualquier máquina donde lo vayas a instalar. No es necesario descargarse nada, ya que .NET viene con el sistema operativo desde hace más de una década (eso sí, actualízalo a la última versión si es la que has utilizado para compilar). La herramienta se llama RegAsm.exe y la encontrarás en la carpeta C:\Windows\Microsoft.NET\Framework64\v4.0.30319 o similar.

Nota: si tu sistema es de 32bits deberás usar la carpeta C:\Windows\Microsoft.NET\Framework\v4.0.30319.

Abre una línea de comandos como administrador y escribe:

RegAsm.exe "C:\Utilidades\MiLib.dll" /tlb:MiLib.tlb /codebase

poniendo primero la ruta a donde hayas copiado la DLL que vas a exponer e indicando en el parámetro /tlb un nombre cualquiera para el archivo .tlb de definición de interfaces COM que se genera y que es el que se utiliza para registrar la biblioteca.

El parámetro /codebase se utiliza para indicarle al programa que debe registrar la DLL incluso aunque no esté firmada digitalmente. Se quejará pero lo hará igualmente. El hecho de que te diga ue es mejor firmarla se debe a que si por cualquier motivo debes instalar varias versiones de la misma DLL, tenerla firmada te permite introducirla en la GAC del sistema (la caché global de ensamblados .NET) y facilita la gstión de versiones, algo que ayuda mucho con el "DLL Hell" del que hablábamos al principio. Si tu biblioteca está bajo tu control y no la vas a distribuir como parte de un producto no es necesario. Simplemente desinstálala antes de copiar e instalar una nueva versión y listo. Aquí te dejo instrucciones para firmarla digitalmente y para meterla en la GAC.

Bien, lo anterior habrá registrado correctamente tu ensamblado y podrás utilizarlo desde cualquier entorno compatible con COM/ActiveX, como veremos a continuación.

Para desinstalarlo en el futuro solo debes hacer:

RegAsm.exe /unregister "C:\Utilidades\MiLib.dll"

Usarlo desde VBA o VB6

Para poder usarlo desde estos entornos lo más habitual es agregar una referencia explícita en las propiedades del proyecto (early binding), que es más eficiente. Para ello debemos usar el diálogo de agregar referencia que tienen estos entornos. Vamos a verlo en Office (concretamente yo he usado Word, pero valdría cualquier otra aplicación de la Suite).

Abre el editor de Visual Basic for Applications pulsando ALT+F11 o yendo a la pestaña de "Developer". Ahora vete al menú Tools y elige la primera opción para gestionar las referencias del proyecto:

Diálogo de referencias de VBA

En VB6 es idéntico. En el diálogo localiza tu ensamblado en la lista y marca el check que tiene a la izquierda. Ya está listo para usar como cualquier otro objeto. Puedes comprobarlo sacando el examinador de objetos (tecla F2) y buscando tu clase en la lista. Podrás ver sus métodos y propiedades.

Usarlo desde WSH o ASP Clásico

Desde entornos de scripting es incluso más fácil (aunque menos eficiente) ya que lo único que tienes que hacer es instanciar un nuevo objeto usando CreateObject. Por ejemplo en WSH crea un nuevo archivo de texto con extensión .vbs y escribe:

Dim miObj
Set miObj = WScript.CreateObject("MiLib.MiObjeto")

miObj.HacerAlgoComplejo "Primer parámetro", 1 

WScript.Echo "¡¡La llamada ha funcionado!!"

En ASP clásico sería lo mismo solo que usarías Server.CreateObject.

Inciso: si quieres aprender a sacarle todo el jugo a .NET nada mejor que empezar con el curso de "Fundamentos de Programación con C# y la plataforma .NET" de campusMVP.

En resumen

Mantener aplicaciones antiguas que tienen limitaciones a la hora de utilizar tecnologías modernas es algo que todos, tarde o temprano, nos vemos obligados a hacer. Por suerte cuando Microsoft lanzó .NET en 2001 pensó (por la cuenta que le traía) en cómo facilitar la interoperabilidad entre lo viejo y lo nuevo.

En este largo artículo hemos repasado cómo podemos crear bibliotecas de clases en C# que además de poder ser reutilizadas en .NET podremos exponer también como objetos de tipo COM/ActiveX susceptibles de ser utilizados en lenguajes y entornos antiguos. De este modo podremos traer funcionalidades complejas y/o modernas a nuestras aplicaciones "Legacy", por lo que hemos abierto un mundo de nuevas posibilidades.

La mayor parte de la información que existe sobre estas técnicas es antigua y poco actualizada por lo que resulta complicado ponerlo en marcha. Pero en el fondo es muy sencillo si se sabe exactamente cómo proceder, que es lo que hemos visto en este documento.

¡Espero que te resulte útil!

Página Siguiente