Noticias Weblogs Código

Blog Bitix

Listener de eventos de Hibernate con servicios de Spring

February 28, 2015 09:59 AM

Java

En el anterior artículo explicaba como crear un listener que reciba eventos de Hibernate pero quizá necesitemos en el listener hacer uso de un servicio de Spring si el proceso de la acción necesita aprovecharse de la funcionalidad proporcionada en los servicios. En este artículo mostraré como crear un listener de Hibernate que use un servicio de Spring, es decir, un listener de Hibernate con la posibilidad de inyectar servicios de Spring.

Para hacer la integración de los listeners con Spring debemos sustituir el Interceptor por un servicio que haga lo mismo pero al inicio del contenedor de Spring con la anotación @PostConstruct. Para ello creamos una clase con el siguiente contenido:

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate; import javax.annotation.PostConstruct; import org.hibernate.SessionFactory; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; import org.hibernate.internal.SessionFactoryImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class HibernateConfigurer { @Autowired private SessionFactory sessionFactory; @Autowired private ProductoEventAdapter productoEventAdapter; @PostConstruct public void registerListeners() { SessionFactoryImpl sfi = (SessionFactoryImpl) sessionFactory; EventListenerRegistry elr = sfi.getServiceRegistry().getService(EventListenerRegistry.class); elr.setListeners(EventType.PRE_INSERT, productoEventAdapter); elr.setListeners(EventType.PRE_UPDATE, productoEventAdapter); elr.setListeners(EventType.PRE_DELETE, productoEventAdapter); elr.setListeners(EventType.POST_INSERT, productoEventAdapter); elr.setListeners(EventType.POST_UPDATE, productoEventAdapter); elr.setListeners(EventType.POST_DELETE, productoEventAdapter); } }</code></pre></noscript>

Configurando Spring con anotaciones y código Java, como es recomendable en vez de xml, la configuración del ejemplo es la siguiente y un archivo xml casi testimonial de Spring. En esta configuraicón vemos el servicio ProductoEventAdapter que usaremos para recibir los eventos y el servicio DummyService que se inyectará en el anterior:

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.spring; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.hibernate.SessionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.orm.hibernate4.HibernateTransactionManager; import org.springframework.orm.hibernate4.LocalSessionFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.support.ResourceTransactionManager; import es.com.blogspot.elblogdepicodev.plugintapestry.services.dao.ProductoDAO; import es.com.blogspot.elblogdepicodev.plugintapestry.services.dao.ProductoDAOImpl; import es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate.ProductoEventAdapter; @Configuration @ComponentScan({ &quot;es.com.blogspot.elblogdepicodev.plugintapestry&quot; }) @EnableTransactionManagement public class AppConfiguration { @Bean(destroyMethod = &quot;close&quot;) public DataSource dataSource() { BasicDataSource ds = new BasicDataSource(); ds.setDriverClassName(&quot;org.h2.Driver&quot;); ds.setUrl(&quot;jdbc:h2:mem:test&quot;); ds.setUsername(&quot;sa&quot;); ds.setPassword(&quot;sa&quot;); //ds.setDriverClassName(&quot;com.mysql.jdbc.ReplicationDriver&quot;); //ds.setUrl(&quot;jdbc:mysql:replication://127.0.0.1:3316,127.0.0.1:3326/test&quot;); //ds.setUsername(&quot;root&quot;); //ds.setPassword(&quot;&quot;); return ds; } @Bean public LocalSessionFactoryBean sessionFactory(DataSource dataSource) { LocalSessionFactoryBean sf = new LocalSessionFactoryBean(); sf.setDataSource(dataSource); sf.setPackagesToScan(&quot;es.com.blogspot.elblogdepicodev.plugintapestry.entities&quot;); sf.setHibernateProperties(getHibernateProperties()); return sf; } @Bean public ResourceTransactionManager transactionManager(SessionFactory sessionFactory) { HibernateTransactionManager tm = new HibernateTransactionManager(); tm.setSessionFactory(sessionFactory); return tm; } @Bean public ProductoEventAdapter productoEventAdapter() { return new ProductoEventAdapter(); } @Bean public ProductoDAO productoDAO(SessionFactory sessionFactory) { return new ProductoDAOImpl(sessionFactory); } @Bean public DummyService dummyService() { return new DummyService(); } private Properties getHibernateProperties() { Map&lt;String, Object&gt; m = new HashMap&lt;&gt;(); m.put(&quot;hibernate.dialect&quot;, &quot;org.hibernate.dialect.HSQLDialect&quot;); //m.put(&quot;hibernate.dialect&quot;, &quot;org.hibernate.dialect.MySQLDialect&quot;); m.put(&quot;hibernate.hbm2ddl.auto&quot;, &quot;create&quot;); // Debug m.put(&quot;hibernate.generate_statistics&quot;, true); m.put(&quot;hibernate.show_sql&quot;, true); Properties properties = new Properties(); properties.putAll(m); return properties; } } </code></pre></noscript>
<noscript><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:context=&quot;http://www.springframework.org/schema/context&quot; xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot; xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot; xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd&quot;&gt; &lt;context:component-scan base-package=&quot;es.com.blogspot.elblogdepicodev.plugintapestry&quot; /&gt; &lt;/beans&gt;</code></pre></noscript>

En el listener haremos uso de un servicio de Spring que podemos inyectar usando la anotación @Autorwire tal y como hacemos normalmente usando el contenedor de depednecias de Spring. La implementación con respecto a usar un listener con solo Hibernate varía ligeramente para adaptarse a los cambios de usar un servicio.

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEventListener; import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreDeleteEventListener; import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.event.spi.PreUpdateEvent; import org.hibernate.event.spi.PreUpdateEventListener; import org.hibernate.persister.entity.EntityPersister; public class HibernateEventAdapter implements PreInsertEventListener, PostInsertEventListener, PreUpdateEventListener, PostUpdateEventListener, PreDeleteEventListener, PostDeleteEventListener { private static final long serialVersionUID = 1L; @Override public boolean requiresPostCommitHanding(EntityPersister persister) { return false; } @Override public void onPostDelete(PostDeleteEvent event) { } @Override public boolean onPreDelete(PreDeleteEvent event) { return false; } @Override public void onPostUpdate(PostUpdateEvent event) { } @Override public boolean onPreUpdate(PreUpdateEvent event) { return false; } @Override public void onPostInsert(PostInsertEvent event) { } @Override public boolean onPreInsert(PreInsertEvent event) { return false; } }</code></pre></noscript>
<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreUpdateEvent; import org.springframework.beans.factory.annotation.Autowired; import es.com.blogspot.elblogdepicodev.plugintapestry.services.spring.DummyService; public class ProductoEventAdapter extends HibernateEventAdapter { private static final long serialVersionUID = 1L; @Autowired private DummyService dummy; public void setDummy(DummyService dummy) { this.dummy = dummy; } @Override public void onPostDelete(PostDeleteEvent event) { dummy.process(&quot;postDelete&quot;, event.getEntity()); } @Override public boolean onPreDelete(PreDeleteEvent event) { dummy.process(&quot;preDelete&quot;, event.getEntity()); return false; } @Override public void onPostUpdate(PostUpdateEvent event) { dummy.process(&quot;postUpdate&quot;, event.getEntity()); } @Override public boolean onPreUpdate(PreUpdateEvent event) { dummy.process(&quot;preUpdate&quot;, event.getEntity()); return false; } @Override public void onPostInsert(PostInsertEvent event) { dummy.process(&quot;postInsert&quot;, event.getEntity()); } @Override public boolean onPreInsert(PreInsertEvent event) { dummy.process(&quot;preInsert&quot;, event.getEntity()); return false; } }</code></pre></noscript>
<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.spring; import es.com.blogspot.elblogdepicodev.plugintapestry.entities.Producto; public class DummyService { public void process(String action, Object entity) { if (entity instanceof Producto) { Producto p = (Producto) entity; System.out.println(String.format(&quot;Action: %s, Id: %d&quot;, action, p.getId())); } } }</code></pre></noscript>

En este ejemplo el resultado que veríamos en la consola sería el siguiente con las trazas Action: preInsert, Id: null y Action: postInsert, Id: 1 antes y después de ejecutarse la sentencia SQL:

Usando los listeners de Hibernate con Spring no necesitamos el archivo que creábamos antes en /META-INF/services/org.hibernate.integrator.spi.Integrator. Esto es un ejemplo de prueba de concepto pero perfectamente podría ser aplicado a una necesidad real. En el ejemplo PlugIn Tapestry que hice para un libro sobre el framework de desarrollo web Apache Tapestry puede verse el código completo y funcional de esta implementación.

Referencia:
Autowiring Spring beans in Hibernate/JPA entity listeners
Spring-injected Beans in JPA EntityListeners

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

Juanjo Navarro

Gestión de errores y liberación de recursos en Go

February 27, 2015 11:19 PM

Hace algo así como un mes dediqué unas cuantas noches a aprender Go (más sobre esto en un próximo artículo) y una de las cosas que más me llamó la atención fue que el lenguaje Go no tiene excepciones.

En Go todas las funciones pueden devolver más de un resultado. Esto es aprovechado en todo el lenguaje para señalar condiciones de error. Muchas funciones devuelven un objeto “Error” (en realidad es un interface, por lo que puede estar devolviendose bastante información). El código que llama a la función debe simplemente comprobar el error. Por ejemplo, la función strconv.Atoi que convierte una cadena en un entero:

i, err := strconv.Atoi("42")
if err != nil {
    fmt.Printf("No se puede convertir número %v\n", err)
}
fmt.Println("Entero convertido", i)

Como se puede ver, la función devuelve a la vez dos valores, que se asignan a las variables i y errr. No es necesario recuperar siempre todos los valores devueltos, por lo que perfectamente se podría escribir i := strconv.Atoi("42") para ignorar el error.

Esto nos evita tener que utilizar siempre excepciones, como ocurre en Java:

int i = 0;
try {
	i = Integer.parseInt("42");
	System.out.println("Entero convertido "+i);
} catch (NumberFormatException e) {
	System.out.println("No se puede convertir número "+e.getMessage());
}

Las excepciones también se usan en Java y otros lenguajes para hacer “limpieza”, para asegurarte de que se cierran las conexiones a la bbdd, se liberan recursos, etc. El típico ejemplo en Java para acceder a una base de datos:

Connection conn = null;
Statement stmt = null;
try {
	conn = DriverManager.getConnection(DB_URL,USER,PASS);
	stmt = conn.createStatement();
	ResultSet rs = stmt.executeQuery(sql);
	while (rs.next()) {
		// Código de gestión de cada fila
	}
	rs.close();
} catch (SQLException e) {
	// Gestión del error
} finally {
	if (stmt!=null) {
		stmt.close();
	}
 	if (conn!=null) {
		conn.close();
 	}
}

El código en el finally nos aseguramos que siempre se ejecuta. No obstante, como no sabemos hasta donde se llegó a ejecutar nuestro código, tenemos que hacer esas feas comprobaciones para saber si los objetos stmt y conn valen null.

Go tiene una forma muy ingeniosa de crear este código de liberación de recursos. Veamos esta función de ejemplo, que copia un fichero:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()
    return io.Copy(dst, src)
}

La palabra clave defer de Go retrasa la ejecución de un código hasta que termina la función actual. En el ejemplo se retrasa la ejecución de src.Close() y de dst.Close(), las dos instrucciones que cierran los ficheros previamente abiertos. Las llamadas defer se van añadiendo a una pila de llamadas (por lo tanto, las últimas añadidas son las que primero se ejecutan) y se ejecutan al finalizar la función de ejemplo CopyFile.

Yo a esto le veo un par de ventajas:

  • Hace innecesario comprobar si los ficheros están previamente abiertos antes de cerrarlos. Simplemente, cada vez que se abre un fichero, se hace un defer de su cierre. Si se produce un error al abrir el segundo fichero, por ejemplo, el defer dst.Close() no llega a ejecutarse y por lo tanto no es llamado al finalizar la función.
  • El código que libera el recurso está cerca del código que lo maneja y no todo junto al final de la función, lo cual facilita entender mejor el código de liberación de recursos.

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

El blog de Adrián Arroyo

Introducción a D-Bus

February 27, 2015 11:00 PM

Esta entrada la he realizado originalmente para el blog DesdeLinux

Esquema de D-Bus

Si llevas algún tiempo en Linux quizás te hayas llegado a preguntar que es D-Bus. D-Bus es un componente incorporado no hace mucho a las distribuciones de escritorio en Linux que previsiblemente jugará un papel muy importante para la programación en Linux.

¿Qué es D-Bus?

D-Bus es un sistema de comunicación entre aplicaciones de muy diverso origen. Con este sistema podremos llamar incluso a aplicaciones privativas (si estas implementan D-Bus). No juega el mismo papel que una librería pues una librería no es un programa independiente y la librería forma parte de tu ejecutable. La idea de D-Bus está inspirada en los objectos OLE, COM y ActiveX de Windows. Los objetos COM de Windows ofrecen una manera sencilla de llamar a cualquier programa desde otro programa llegando incluso a poder incrustarse visualmente uno dentro de otro sin necesidad de usar el mismo lenguaje de programación. D-Bus no llega tan lejos pero ofrece esa comunicación de la que UNIX carecía.

¿Por qué es importante D-Bus?

D-Bus es importante dada la gran diversidad de lenguajes que pueden funcionar en Linux y la gran diversidad también de librerías. Pongamos un ejemplo práctico. Yo quiero mandar una notificación al sistema notify-osd de Ubuntu desde mi aplicación en Node.js. Primero tendría que ver que librería ofrece esa funcionalidad en el sistema, libnotify en este caso, y después debería hacer unos bindings para poder llamar la librería programada en C desde JavaScript. Además imaginemos que queremos hacer funcionar nuestra aplicación con un escritorio que no usa libnotify para las notificaciones.

Usando D-Bus

Entonces hemos decidido que vamos a usar D-Bus para crear la notificación de nuestra aplicación en JavaScript.

Hay 2 tipos de buses en D-Bus, un D-Bus único al sistema y un D-Bus para cada sesión de usuario. Luego en D-Bus tenemos servicios que son “los nombres de los proveedores D-Bus”, algo así como las aplicaciones D-Bus. Después en una estructura como de carpeta están los objetos que puede tener ese servicio o instancias y finalmente la interfaz es la manera de interactuar con los objetos de ese servicio. En este caso es muy redundante pues el servidor de notificaciones es muy simple.

¿Quién usa D-Bus?

Más programas de los que imaginas usan D-Bus. Algunos servicios de D-Bus solo por nombrar ejemplos son:

  • com.Skype.API
  • com.canonical.Unity
  • org.freedesktop.PolicyKit1
  • org.gnome.Nautilus
  • org.debian.apt
  • com.ubuntu.Upstart

Para averiguar todos los servicios de D-Bus que tienes instalados puedes usar D-Feet

D-Feet

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

Poesía Binaria

En ocasiones, veo lenguajes de programación por la calle [ Frikada rápida ]

February 27, 2015 09:56 AM


Un día, paseando por Linares, me asomo a este escaparate y… veo tres llaveros cuidadosamente colocados formando las iniciales de uno de mis lenguajes de programación favoritos.
Vale, es CPP y no C++, pero también se dice C plus plus y una de las extensiones típicas para esos archivos es cpp.
Luego hay una B por ahí, que es otro lenguaje de programación… pero, ¿quién programa en B hoy en día?

¿Estoy obsesionado?

La entrada En ocasiones, veo lenguajes de programación por la calle [ Frikada rápida ] aparece primero en Poesía Binaria.

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

Juanjo Navarro

fernand0 @ GitHub.io y recursividad

February 26, 2015 07:00 AM

Quiero recomendaros un blog que creo que es muy interesante: fernand0 @ GitHub.io.

Su creador suele escribir sobre temas a medio camino entre la seguridad y la programación, esa zona intermedia que tan importante es que desarrollemos como programadores.

En estos tiempos de hiperespecialización hay quien piensa que son dos áreas completamente distintas, pero la seguridad es algo que abarca todas las fases de la tecnología, nunca puede ser algo añadido “a posteriori” y es por lo tanto crucial que como programadores tengamos unas bases sólidas que nos permitan aplicar a nuestro día a día las mejores prácticas de programación segura.

De paso me permito recomendaros el enlace que fernand0 enlaza en su útimo artículo, Are We Shooting Ourselves in the Foot with Stack Overflow?, donde se habla de cómo errores en la gestión de la pila han producido accidentes de tráfico en algunos vehículos Toyota. Entre lo mucho interesante del artículo, hay una diapositiva que dice:

On top of that… Toyota used dangerous recursion. […] absence of recursive procedures which is standar in safety critical embedded software

Lo que quiere decir que cuidado con el código recursivo. Puede llenar la pila antes de lo que imaginamos y la diapositiva que enlaza nos dice que lo estandar en sofware embebido crítico es no utilizarlo.

Así que ya sabeis, niños, si vuestro código es recursivo intentad convertirlo en tail recursive y aseguraros que vuestro lenguaje es capaz de manejarlo como tal, sin crear un nuevo stack frame en cada llamada.

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

carlosrovira.com

Un poco de Blender y el nuevo logo de Codeoscopic

February 25, 2015 06:25 PM

Nadie dijo que fuese fácil. Combinar las tareas de gestión como Director General con otras más creativas, es tan desafiante como estimulante, por la cantidad de dedicación y diversas disciplinas que hay que manejar a lo largo del día. Además el día tiene un número limitado de horas y los peques de la casa demandan.

En un blog como este, más informal, apetece exponer alguna imagen de esa parte más creativa (no creo que hablar de balances, cuentas de resultado y otros temas fuese tan divertido ¿no?).

En este apartado, siempre me ha encantado el 3D y ya desde la “época ochentera” con mi viejo Comodore Amiga y programas como Sculpt 4D o 3D Studio MAX era un placer crear imagenes con un software 3D.

Hoy en día hay un programa que destaca, para mi gusto, por su versatilidad y calidad profesional. Me refiero a Blender.

codeoscopic_blender

Como ya comenté, hace algunas entradas, mi intención con este blog era postear cosas muy diversas. Esta sin duda es una de ellas, con respecto a lo que he publicado en los últimos 10 años. Si tengo ocasión, y mis otras actividades me lo permiten, intentaré ir publicando algún que otro render de vez en cuando.

Si tenéis oportunidad, bajad Blender y dadle un “try”, el programa es una auténtica maravilla y lo más increíble es su naturaleza Open Source.

Happy Blending!

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

Poesía Binaria

31 Atajos de teclado que podemos usar en nuestra terminal o consola

February 25, 2015 09:32 AM

Foto: Thomas (Flickr CC)

¿ Qué podemos hacer en nuestra terminal a un golpe de teclado ? Muchas más cosas de las que nos imaginamos. Hay muchas teclas alternativas, algunas no funcionarán en versiones muy antiguas, otras son teclas algo más fáciles pero en ocasiones algunas terminales remotas no aceptan esos códigos y pueden no funcionar. Las teclas alternativas suelen estar especificadas en /etc/inputrc . También incluyo alguna palabra clave que suele ser el significado de la tecla, o alguna aclaración mnemotécnica para no olvidarnos (o eso pretende).

Posicionamiento

  • Control + E : Ir al final de la línea (Tecla Fin). End.
  • Control + A : Movernos al principio de la línea (Tecla Inicio). La A es la primera letra del abecedario, pues al principio.
  • Alt + F : Movernos al siguiente espacio (Teclas Control + derecha). Forward
  • Alt + B : Movernos al espacio anterior (Teclas Control + izquierda). Backwards
  • Control + B : Movernos a la letra anterior (Tecla izquierda). Backwards (la tecla control suele ser para movernos más rápido)
  • Control + F : Movernos a la siguiente letra (Tecla derecha), Forward
  • Control + XX : Cambiar entre la posición actual del cursor y el inicio de la línea

Edición

  • Control + H : Borra la letra anterior al cursor (Retroceso o Backspace)
  • Control + D : Borra la letra sobre el cursor (Suprimir). ¡ Cuidado ! Esta tecla cierra el terminal si no hay nada escrito (ver más adelante). Delete
  • Alt + Retroceso (Backspace) / Control + W : Borrar la palabra anterior a nuestro cursor. Básicamente borra desde el cursor hasta el espacio anterior por lo que si estamos en mitad de una palabra borraremos la fracción desde el cursor hacia atrás. Cut Word.
  • Control + U : Corta desde la posición actual hasta el principio
  • Control + K (Alt + D): Corta desde la posición actual hasta el fin de línea. Alt + D se supone que no guarda en el portapapeles, pero en muchos casos sí lo hace.
  • Control + Y : Pega algo que has cortado desde la posición actual. Yank.
  • Control + T : Invertir el orden de las dos letras anteriores al cursor. Typo
  • Escape + T : Invertir el orden de las dos últimas palabras (Alt + T funciona si no interfiere con nuestro gestor de ventanas). Transpose.
  • Escape + U : Pone en mayúsculas toda la palabra desde el cursor hasta el final (Alt + U si no interfiere con nuestro gestor de ventanas). Uppercase.
  • Escape + L : Pone en minúsculas toda la palabra desde el cursor hasta el final (Alt + L si no interfiere con nuestro gestor de ventanas). Lowercase
  • Alt + C : Pone la letra en el cursor en mayúscula y nos vamos al final de la palabra. Capitalize.
  • Control + L : Limpiar la pantalla (igual que el comando $ clear). CLear.
  • Control + _ (a veces Control+-): Deshacer. Funciona si escribimos una línea, luego borramos una parte, volvemos a escribir… entonces deshacemos. Si no hacemos mucho, directamente aparecerá la linea vacía. Es lógico, el estado inicial es así.

Historial

  • Control + R : Acceder al historial (escribimos parte de una línea que hayamos escrito antes y nos sugerirá un comando anterior. Si nos sugiere algo y queremos que siga buscando cosas parecidas, debemos pulsar de nuevo Control+R.). Reverse.
  • Control + G : Salir del como de búsqueda de Control + R
  • Control + O / Control + J : Enviar el comando (Tecla enter de toda la vida). Aunque Control+O es más para enviar un comando propuesto por Control +R.
  • Control + P : Buscar en los últimos comandos enviados (Tecla Arriba). Previous.
  • Control + N : Buscar el siguiente comando (Tecla Abajo). Si hemos pulsado Control + P antes, con esta tecla podemos navegar. Next.
  • Alt + . (Alt y punto): Escribir la última palabra del comando anterior

Procesos

  • Control + C : Cancelar un comando (envía una señal SIGINT para cerrar un programa. El programa internamente puede elegir cerrarse o no cerrarse.). Cancel.
  • Control + D : Cerrar la terminal. Puede ser útil cuando estemos ejecutando comandos que requieran texto de la entrada estándar y queremos dejar de escribir texto.
  • Control + Z : Pausar una aplicación (Señal SIGTSTP, que puede ser ignorada por el proceso). Una vez pausada la aplicación podemos utilizar las siguientes órdenes
    1. $ jobs : para ver todas las taras que tenemos en marcha
    2. $ fg : Para continuar en primer plano la última tarea (foreground)
    3. $ bg : Para continuar en segundo plano la última tarea (background)
    4. $ bg [número de tarea] : Para continuar en segundo plano la tarea con ese número
    5. $ %[número de tarea] : Para continuar en primer plano la tarea con ese número (o también $ fg [número de tarea]
    6. $ kill %[número de tarea] : Para finalizar la tarea y no continuarla jamás.
  • Tab : Autocompletar (si estamos en mitad de una palabra busca el comando que más se parece, si pulsamos varias veces nos da varias opciones, si las hay. En las últimas versiones, es posible que muchos programas tengan configurados sus posibles argumentos y también aparezcan).
  • Conrol + S : Parar la salida por pantalla. Si por ejemplo ejecutamos un comando que tiene una salida inmensa, podemos hacer que trabaje en silencio. Stop.
  • Control + Q: Volvemos a permitir la salida por pantalla.

Algunos sitios que podemos visitar:
Atajos de teclado para consola o terminal
Linux Terminal Command Reference

La entrada 31 Atajos de teclado que podemos usar en nuestra terminal o consola aparece primero en Poesía Binaria.

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

Arragonán

Semanas 349 y 350

February 24, 2015 11:32 PM

Otra ocasión en la que la retro semanal se convierte en bisemanal, que están siendo unas semanas con bastante carga de trabajo y cuesta aún más de lo habitual buscar un rato para escribir.

Estuve gran parte de la primera semana en Madrid, en las oficinas de Besepa para trabajar mano a mano. En esos días aproveché para quedar de cañas con un puñado de amigos del mundillo, y para pasarme por el primer Papers We Love Madrid. En el Papers We Love el ponente era Álvaro Videla y su presentación se titulaba Harmful GOTO’s, premature optimizations, and programming myths are The Root of all Evil. En su blog ha publicado su (estupenda) presentación y lo que dijo en ella.

Durante este par de semanas seguimos definiendo algunas cosas del Startup Open Space 2015. Por ejemplo confirmamos la fecha y yo, como responsable de patrocinios, ya tengo apalabrado alguno (si algún posible interesado lee esto, que contacte conmigo en danilat (ARROBA) cachirulovalley (PUNTO) com).

Tras que me aceptaran una nueva propuesta para un pequeño proyecto (nombre en clave one-stop), estuve tanteando un poco la Milestone 1 de Grails 3.0 para usarlo como base del desarrollo, pero finalmente lo descarté y decidí empezarlo con la 2.4.4 para ir sobre seguro.

También tuve primer contacto para un proyecto bastante interesante, con mucho trabajo en cuanto la parte de programación en frontend. En este momento es un proyecto que no puedo asumir por los compromisos actuales, pero pinta que es posible que lleguemos a un acuerdo para abordarlo dentro de unas semanas.

La semana pasada fue la primera de SenpaiDevs, donde finalmente tenemos 5 kōhais seleccionados. Empezamos a introducir algunas cuestiones relacionadas con metodologías ágiles, estuvimos viendo algunas cosas trabajando en el terminal, hicimos una introducción a ruby e hicimos una primera kata en la que vimos algo de testing unitario… y además estuvimos viendo herramientas que facilitan la colaboración como son slack y trello.

Y para terminar la semana asistí al Betabeers Zaragoza de Febrero.

En cuanto a proyectos, en resumen:

  • En Besepa empezamos el proceso de separación del core de funcionamiento (vía un API JSON) de la aplicación web de gestión, para que esta pase a ser consumidora del API.
  • Resolví un bug en Minchador relacionado con las notificaciones de confirmación de reserva y estuve dedicando algo de tiempo para hacer mailing.
  • Acabé el primer scraper de Brazil y tras dedicar un tiempo de investigación al segundo, comencé a hacer las primeras pruebas de extracción.
  • En one-stop, tras descartar Grails 3. Estuve integrando la maquetación de la zona pública que facilitó el cliente y empecé a desarrollar la zona del backoffice.

Buena semana.

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

Variable not found

Inyección de dependencias en ASPNET 5

February 24, 2015 12:51 PM

ASP.NET 5La inyección de dependencias ha sido un tema recurrente en Variable not found desde hace bastante tiempo, y dadas las grandes novedades incluidas en ASP.NET 5 al respecto estaba deseando hincarle el diente y publicar algo sobre el tema.

Para los perezosos, podríamos resumir el resto del post con una frase: ASP.NET 5 viene construido con inyección de dependencias desde su base, e incluye todas las herramientas necesarias para que los desarrolladores podamos usarla en nuestras aplicaciones de forma directa, en muchos casos sin necesidad de componentes de terceros (principalmente motores de IoC que usábamos hasta ahora). Simplemente, lo que veremos a continuación es cómo utilizar esta característica ;)

Pero permitidme una nota antes de empezar: si no sabes aún lo que es inyección de dependencias, el resto del post te sonará a arameo antiguo. Quizás podrías echar previamente un vistazo a este otro, Desacoplando controladores ASP.NET MVC, paso a paso, que aunque tiene ya algún tiempo, creo que todavía es válido para que podáis ver las ventajas que nos ofrece esta forma de diseñar componentes.

Ah, y recordad: todo lo que vamos a ver podría cambiar conforme vaya avanzando el desarrollo del producto, pero más o menos son cosas que parecen relativamente estables desde hace algún tiempo.

1. Cómo registrar dependencias

Como ya hemos visto por aquí en el post “La clase Startup en ASPNET 5”, completado por el gran Unai en este otro, en esta clase existe un método llamado ConfigureServices() que es invocado por el framework durante el arranque para darnos oportunidad de configurar los servicios o dependencias utilizadas por nuestra aplicación. Esta llamada se producirá antes que Configure(), lo que da la oportunidad de registrar las dependencias antes de comenzar a configurar el pipeline.

Bueno, en realidad, como se comenta en los posts citados, el framework intentará llamar primero al método Configure[EnvironmentName]Services(), y sólo si no existe usará ConfigureServices(). Por ejemplo, si ejecutamos nuestra aplicación en un entorno denominado “Development”, el framework intentará primero ejecutar el método ConfigureDevelopmentServices() de la clase Startup, y, si no existe, ejecutará ConfigureServices().

La firma del método de configuración es la siguiente:



El parámetro de tipo IServiceCollection que recibimos representa a la colección que almacenará los servicios o dependencias a usar por nuestro sistema. Este interfaz, definido en el espacio de nombres Microsoft.Framework.DependencyInjection, establece un contrato bastante simple donde sólo encontramos operaciones que permiten registrar servicios partiendo de descriptores (objetos IServiceDescriptor):



Normalmente no usaremos directamente los métodos definidos por este interfaz, sino métodos de extensión de IServiceCollection bastante más cómodos de utilizar, y que normalmente pertenecerán a uno de los siguientes tipos:
  • Extensores proporcionados por frameworks y middlewares que registran los servicios usados internamente por estos componentes.
     
  • Extensores para añadir servicios personalizados manualmente.

1.1. Extensores proporcionados por frameworks/middlewares

Dentro del primer grupo están los extensores que acompañan de serie a los frameworks o middlewares. Por ejemplo, ASP.NET MVC 6 está construido internamente haciendo uso extensivo de inyección de dependencias, pero obviamente, para que funcione, es necesario configurarlo y registrar sus dependencias antes de que el framework comience a funcionar. Lo mismo ocurre con Entity framework, SignalR, Identity, y muchos otros middlewares, frameworks o componentes.

Por esta razón es por lo que en la plantilla de proyectos ASP.NET 5 encontramos el siguiente código por defecto:



Los métodos AddEntityFramework(), AddIdentity() y AddMvc() son proporcionados respectivamente por Entity Framework, ASP.NET Identity y ASP.NET MVC para facilitarnos el registro de sus dependencias internas. De hecho, si pensamos utilizar alguno de estos frameworks es absolutamente imprescindible realizar estas llamadas antes de que éstos se utilicen desde nuestra aplicación.

Así, por ejemplo, si intentamos ejecutar una aplicación ASP.NET MVC sin haber añadido sus servicios mediante el extensor AddMvc(), el sistema reventará directamente al arrancar, mostrando una pantalla como la que vemos en el latera. Es lógico, porque sin los servicios internos de MVC correctamente registrados no funcionaría absolutamente nada.

Entity framework o Identity son menos exigentes en un principio, pero se producirán errores y excepciones en cuanto intentemos utilizarlos en algún punto de la aplicación.

Por tanto, tened siempre en cuenta que cuando añadamos nuevos frameworks, middlewares o componentes a nuestras aplicaciones es probable que necesitemos registrar también sus servicios para que funcionen correctamente.

Estos extensores siguen una convención en cuanto a su nombrado, por lo que serán fácilmente descubribles de forma intuitiva. Por ejemplo, ¿qué podríamos esperar si vamos a añadir SignalR a nuestra aplicación? Está claro, el registro de servicios se realizará mediante una llamada al extensor AddSignalR().

También, a pesar de que cada extensor está implementado en el ensamblado del framework o middleware que lo proporciona, se definen siempre en el espacio de nombres Microsoft.Framework.DependencyInjection para que intellisense pueda ayudarnos a descubrirlos con más facilidad.

Estos métodos suelen presentar además un interfaz fluido que facilita la configuración del servicio cuando es necesario. Ejemplos de ello son las cadenas de llamadas AddEntityFramework().AddSqlServer().AddDbContext() o AddIdentity().AddEntityFrameworkStores() que hemos visto anteriormente.

En definitiva, debemos tener en cuenta estas convenciones si pensamos a crear nuestros propios componentes y queremos proporcionar a los desarrolladores una experiencia de codificación similar a la que encuentran de serie.

1.2. Extensores para añadir servicios personalizados

El registro de servicios personalizados lo realizaremos de forma muy similar a como hemos hecho hasta ahora utilizando contenedores de inversión de control como Unity, Autofac, Ninject, StructureMap u otros, normalmente asignando interfaces a clases concretas. La única diferencia, en lugar de utilizar los contenedores proporcionados por estos componentes utilizaremos el propio IServiceCollection que nos llega como parámetro.

Aunque hay algunas opciones adicionales, normalmente utilizaremos una de las siguientes fórmulas para registrar servicios:
  • Registro de interfaz a instancia concreta:



    Lo que le estamos diciendo al contenedor, más o menos es lo siguiente: “hey, cuando algún componente requiera una instancia del tipo IMyService, suminístrale directamente este objeto myService que te estoy proporcionando”. Obviamente, en memoria sólo existirá una copia del objeto, la que hemos instanciado para configurar el servicio.
         
  • Registro de interfaz a objeto volátil:


    Aquí, cada vez que un componente solicite una instancia de IMyService, el contenedor instanciará un nuevo objeto de tipo MyService. Por tanto, en memoria podrán existir múltiples instancias de objetos de tipo MyService, una por cada componente que lo haya requerido.
     
  • Registro de interfaz a objeto Singleton:



    En este caso, cuando un componente solicita una instancia de IMyService, el contenedor instanciará un objeto de tipo MyService, pero a diferencia del caso anterior, si otro componente requiere una instancia del mismo tipo se le suministrará la creada anteriormente, por lo que en memoria sólo existirá una copia de ella. Vaya, un Singleton de los de toda la vida.
  • Registro de interfaz a objeto Singleton con vida “per-request”: 


    Es similar al caso anterior, es decir, un objeto Singleton creado cuando lo solicite el primer componente y compartido con todos los componentes que lo requieran a continuación, pero con la particularidad de que será liberado explícitamente por el framework al terminar el ámbito de ejecución, o scope, en el que se enmarca el proceso actual. En la práctica, esto significa que el framework recordará los objetos que ha ido creando según este modelo de funcionamiento y cuando finalice el proceso de la petición invocará a sus respectivos métodos Dispose() para liberar recursos.

    Este escenario es muy frecuente en el mundo web, donde queremos que al finalizar la petición se liberen de forma automática los recursos que hayamos podido utilizar, como conexiones a bases de datos.
Al final, nuestro método ConfigureServices() podría tener una pinta como la siguiente:



Obviamente, en aplicaciones de cierto tamaño este método podría extenderse demasiado y probablemente sería más conveniente el estructurar este código de una forma apropiada, por ejemplo moviendo el registro a archivos y clases independientes, creando extensores, o de la forma que veamos más conveniente.

Por último, comentar que además de los extensores comentados anteriormente hay otros mecanismos que nos permiten un mayor grado de control sobre los procesos y gestión del ciclo de vida de las dependencias, pero con lo visto hasta el momento tenemos lo suficiente como para poder desarrollar aplicaciones con ciertas garantías.

2. Cómo usar dependencias

Vamos a ver ahora el proceso desde el otro lado, es decir, desde el punto de vista de los componentes que requieren para poder funcionar de los servicios prestados por otros componentes.

ASP.NET 5 ofrece de momento las siguientes fórmulas para inyectar dependencias en componentes:
  • Inyección de dependencias en parámetros de constructor, que para mi gusto es la forma más limpia y recomendable de utilizar inyección de dependencias.  Consiste simplemente en indicar en el constructor de una clase qué servicios necesita ésta para funcionar, realizar una copia local de éstos, y ponerlos a disposición de los miembros internos para que éstos puedan realizar sus tareas.

    La siguiente porción de código muestra un controlador MVC cuyo constructor muestra que esta clase depende de servicios de pago y notificaciones para realizar su cometido, con un ejemplo de uso en el método Cancel(). ASP.NET 5 se encargará de suministrarle las instancias indicadas a partir de los registros realizados en el IServiceCollection descrito anteriormente.


     
  • Inyección de propiedades, es otro mecanismo frecuente para indicar al framework qué dependencias presenta un componente. En este caso, lo que hacemos es crear propiedades del tipo deseado y decorarlas con el atributo [Activate]. ASP.NET 5 las detectará y les cargará automáticamente instancias atendiendo al registro de servicios.



    Obviamente la carga de dependencias la realizará el framework después de haber creado la instancia del objeto, por lo que en el constructor no podremos utilizar estos miembros.
            
  • Inyección de dependencias en parámetros de métodos, aunque sólo funcionarán en contadas ocasiones. De hecho, hasta el momento sólo he visto que se pueda hacer en un caso concreto, que es el método Invoke() de los middlewares personalizados, como en el siguiente ejemplo, donde indicamos en este método  que vamos a usar una instancia de INotificationServices:



    No sé si en el futuro se ampliará su ámbito de utilización, pero de momento es así de cortito. En cualquier caso, a priori no me parecería excesivamente complicado hacerlo por ejemplo con acciones de controlador, como ya vimos para versiones anteriores del framework en el post “Inyección de parámetros en acciones ASP.NET MVC (I)” de hace algún tiempo.
     
  • Y, aunque muchos lo consideran un antipatrón, para los escenarios en los que otros mecanismos no son posibles podríamos utilizar el patrón Service Locator con el contenedor de servicios. En este caso, lo único que tendríamos que hacer es obtener una instancia de IServiceProvider, el proveedor de servicios interno de ASP.NET, e invocar a su método GetService() para obtener los servicios que necesitemos:

Bueno, pues creo que podemos dejarlo aquí por hoy, pues ya hemos visto todo lo importante, detalle arriba o detalle abajo, sobre la inyección de dependencias en ASP.NET 5: cómo y dónde registrar los servicios, y cómo usarlos desde otros componentes.

Pero antes de cerrar, una última reflexión. Visto este nuevo panorama, en el que el propio framework ofrece muchas de las funcionalidades que hasta ahora se delegaban a contenedores de inversión de control, ¿tiene sentido utilizar contenedores IoC “tradicionales” como Unity, Ninject o Autofac en aplicaciones ASP.NET 5? Pues como dijo mi amigo gallego, sí… o igual no ;)

Desde mi punto de vista, probablemente la mayoría de aplicaciones convencionales no tendrán necesidad real de usarlos, puesto que ASP.NET ofrece todo lo que necesitaremos para cubrir los escenarios más habituales. A falta de lo que la experiencia vaya enseñándonos por el camino, creo que la tendencia natural será comenzar los proyectos usando las herramientas proporcionadas por el framework, y migrar a contenedores más versátiles cuando realmente sean necesarios, por ejemplo si queremos usar convenciones, mapeos basados en archivos de configuración, parametrización o selección dinámica de constructores, ciclos de vida no incluidos (por ejemplo, el scope por thread),  u otras características más avanzadas.

Publicado en Variable not found.

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

El blog de Adrián Arroyo

Usando Node con XULRunner y Gecko

February 23, 2015 11:00 PM

Las aplicaciones basadas en HTML5 para el escritorio han ido en aumento. Soluciones como node-webkit, recientemente renombrado a NW.js, han crecido en popularidad. Juegos como Game Dev Tycoon o aplicaciones como Atraci o Popcorn Time son solo unos ejemplos de apliaciones que usan node-webkit. Sin embargo las personas que preferimos tecnología de Mozilla nos encontrabas con que no había nada que sirviese.

node-xulrunner

Así que tenía que hacer algo. De momento es más que nada una prueba de concepto sin nada reseñable y con miles de bugs potenciales. Lo he diseñado para ser llamado desde una API de CommonJS. Esto permitirá tener un cliente de línea de comandos de manera sencilla, pero de momento no lo voy a hacer. Os enseñaré a usarlo con otro método

Nuestra aplicación HTML5, Node.js, XULRunner

El primer paso será clonar el proyecto node-xulrunner de GitHub.

git clone http://github.com/AdrianArroyoCalle/node-xulrunner html5-app
cd html5-app
npm install

La estructura del proyecto ya es una aplicación empaquetable, de manera que solo tendremos que modificar ciertos ficheros. Necesitaremos editar el archivo test.js para indicar ciertas preferencias sobre la aplicación. Algunos parámetros interesantes son os que puede ser: win32, mac, linux-i686 o linux-x86_64 y version que debe coincidir con una versión de XUL Runner disponible y publicada. El resto de opciones son autoexplicativas cuando las veais. En mi caso test.js queda así:

var xul=require("./index.js");

var options={
	os: "linux-i686",
	version: "35.0.1",
	name: "HTML5 App",
	vendor: "Adrián Arroyo",
	appVersion: "0.1.0",
	buildId: "00000001",
	id: "test@node-xulrunner",
	copyright: "2015 Adrián Arroyo Calle",
	width: 800,
	height: 800
};

xul.packageApp(options);

Ahora debemos crear nuestra aplicación propiamente dicha. Se encuentra bajo el directorio app/ y concretamente el fichero main.js será el ejecutado nada más arrancar la aplicación. main.js puede usar todas las APIs de Node. De hecho si usamos npm en esa carpeta funcionaría correctamente. En mi caso:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(4200, '127.0.0.1');
console.log('Server running at http://127.0.0.1:4200/');

Y ahora los pasos mágicos. Vamos a la terminal y ejecutamos test.js con Node

node test.js

Nos saldrá una advertencia y empezará a descargar XULRunner y Node.js. Este ejecución no creará ninguna aplicación. Para, ahora sí, crear la aplicación debemos llamar otra vez a test.js

node test.js

Y en la carpeta build/ tendremos la aplicación lista para ser probada.Simplemente tendremos que ejecutar app

HTML5 App con XULRunner

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

Poesía Binaria

Magento: Saber si estamos en la página principal (home page)

February 23, 2015 09:57 AM

Cuando estamos trabajando con Magento, en ocasiones necesitamos conocer si actualmente nos encontramos en la home de nuestro sitio o en cualquier otra página. Normalmente, la home, tendrá un diseño diferente y por eso debemos diferenciarla de las demás.

Para ello, desde Magento 1.5+ tenemos una función que nos puede ayudar mucho en esta misión. Si nos encontramos en el header.phtml podemos comprobarlo llamando a:

1
2
3
4
if ($this->getIsHomePage())
  echo "Estoy en la HOME";
else
  echo "No estoy en la HOME";

Pero si nos encontramos en cualquier otra plantilla, tenemos que llamar a esa misma función que se encuentra en el header, para ello obtenemos el singleton del header y se lo preguntamos:

1
2
3
4
if (Mage::getBlockSingleton('page/html_header')->getIsHomePage())
  echo "Estoy en la HOME";
else
  echo "No estoy en la HOME";

O también podemos asignar el valor de Mage::getBlockSingleton(‘page/html_header’)->getIsHomePage() a una variable local y utilizar su valor en nuestra plantilla.

Actualización 23/02/2015 15:25 : Arreglado el segundo código que no salia bien.

La entrada Magento: Saber si estamos en la página principal (home page) aparece primero en Poesía Binaria.

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

Variable not found

Enlaces interesantes 188

February 23, 2015 08:05 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes :-)

.Net

ASP.NET

Azure / Cloud

Conceptos/Patrones/Buenas prácticas

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Publicado en Variable not found

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

Koalite

Monitores de rendimiento para Javascript

February 23, 2015 05:06 AM

Desde hace ya bastante tiempo todos los navegadores (incluso Internet Explorer) incluyen herramientas de desarrollo muy completas para trabajar con Javascript, entre las cuales existen profilers que nos permiten identificar cuellos de botella en nuestras aplicaciones web, tanto a nivel de rendimiento como de consumo de memoria.

Estas herramientas están muy bien, pero a veces necesitamos algo que nos proporcione una información más inmediata de lo rápido que se está ejecutando nuestra aplicación o de la memoria que estamos ocupando en cada momento. En este post vamos a ver un par de herramientas muy sencillas que nos pueden venir bien para obtener estos datos.

memory-stats.js

Como su nombre indica, memory-stats.js nos permite mostrar estadísticas del consumo de memoria de una aplicación Javascript.

Para utilizarlo, podemos instalarlo desde bower o incluir directamente en nuestra página el fichero js necesario. Después sólo necesitamos crear un objeto MemoryStats que se encargará de mostrar un pequeño gráfico con las estadísticas de consumo de memoria:

memory-stats.js

Podemos configurar la posición del gráfico y la frecuencia de actualización. Lo mínimo que necesitaríamos para usarlo (asumiendo que queremos refrescar el gráfico tan rápido como podamos) es:

// Creamos el objeto MemoryStats
var stats = new MemoryStats();

// Colocamos el gráfico en la esquina inferior derecha
stats.domElement.style.position = 'fixed';
stats.domElement.style.right = '0px';
stats.domElement.style.bottom = '0px';

document.body.appendChild( stats.domElement );

// Actualizamos con requestAnimationFrame
requestAnimationFrame(function rAFloop(){
    stats.update();
    requestAnimationFrame(rAFloop);
});

Internamente, memory-stats.js se basa en la API window.performance.memory, que es una extensión propietaria de Chrome y, por tanto, sólo está disponible en ese navegador. Además, para obtener resultados más precisos deberemos ejecutar Chrome con el parámetro --enable-precise-memory-info.

stats.js

stats.js nos permite añadir también un pequeño gráfico que nos indique el número de frames por segundo (o los milisegundos entre frames, lo que más nos guste) a los que está funcionando nuestra aplicación web.

Al igual que memory-stats.js, podemos instalarlo desde bower o referenciar directamente el fichero javascript de stats.js, y al emplearlo mostrará un gráfico con las estadísticas en nuestra página:

stats-js

El modo de uso es muy similar al de memory-stats.js (que, de hecho, está inspirado en esta librería). Sólo necesitamos crear un objeto Stats, configurar el gráfico para añadirlo al DOM y empezar tomar tiempos:

// Creamos el objeto Stats
var stats = new Stats();
stats.setMode(1); // 0: fps, 1: ms

// Lo colocamos en la esquina superior derecha
stats.domElement.style.position = 'fixed';
stats.domElement.style.right = '0px';
stats.domElement.style.top = '0px';

document.body.appendChild( stats.domElement );

// Monitorizamos algún proceso
var update = function () {
    stats.begin();

    // aquí va el código a monitorizar

    stats.end();
    requestAnimationFrame( update );
};

requestAnimationFrame( update );

La parte más complicada de esto es ver en qué parte de nuestra aplicación colocar los stats.begin/stats.end, ya que no siempre es fácil encontrar un punto bueno para hacer la medición (ni siquiera siempre tiene sentido hacerlo, la verdad).

Los tiempos se miden utilizando Date.now(), por lo que la precisión no va a ser muy elevada. Si necesitas una precisión mayor, seguramente puedas conseguirla modificando la librería para utilizar la API de window.performance.

Resumen

Tanto memory-stats.js como stats-js son dos librerías muy simples que nos pueden ayudar a monitorizar en tiempo real el rendimiento de una aplicación web.

Ninguna de ellas sustituye a los profilers que se incluyen con las herramientas de desarrollo de los navegadores, pero la posibilidad de ver en tiempo real el comportamiento de la aplicación resulta útil en determinadas circunstancias, y no siempre es fácil o cómodo conectar las herramientas de desarrollo, especialmente cuando trabajamos con algunos dispositivos móviles.

Posts relacionados:

  1. Swiftcore.js: un contenedor IoC para Javascript
  2. Usando C# para entender los prototipos de Javascript
  3. Logs para aplicaciones javascript con persistent-log

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

Blog Bitix

Ejemplo de listener de eventos de Hibernate

February 21, 2015 10:30 PM

Java

En alguna ocasión puede que tengamos la necesidad de realizar alguna acción cuando una entidad es guardada en base de datos, actualizada, eliminada, cargada, … . Algunos casos de uso pueden ser:

  • Establecer la fecha de creación o de actualización del objeto cuando es persistido en base de datos.
  • Cifrar un dato del objeto al ser persistido en base de datos.
  • Proporcionar seguridad de modo que un usuario solo pueda modificar o acceder a los objetos a los que tenga acceso.
  • Al persistir el objeto guardar en un campo el valor calculado resultado de una función.
  • Cualquier otra restricción, cálculos o acciones.

Para realizar estas operaciones podemos hacerlas de varias formas, una puede ser usando triggers de la base de datos disponibles en Oracle y PostgreSQL. Si usamos Hibernate como librería ORM de persistencia en una aplicación el equivalente a los triggers de BBDD es mediante un listener que sea llamado al ocurrir ciertos eventos. En la clase EventType está la lista completa de los eventos que podemos recibir y la clase listener que debemos implementar para recibir cada uno de los eventos. Esta es la lista de los eventos disponibles según los conceptos que usa Hibernate:

  • AUTO_FLUSH
  • CLEAR
  • DELETE
  • DIRTY_CHECK
  • EVICT
  • FLUSH
  • FLUSH_ENTITY
  • INIT_COLLECTION
  • LOAD
  • LOCK
  • MERGE
  • PERSIST
  • PERSIST_ONFLUSH
  • POST_COLLECTION_RECREATE
  • POST_COLLECTION_REMOVE
  • POST_COLLECTION_UPDATE
  • POST_COMMIT_DELETE
  • POST_COMMIT_INSERT
  • POST_COMMIT_UPDATE
  • POST_DELETE
  • POST_INSERT
  • POST_LOAD
  • POST_UPDATE
  • PRE_COLLECTION_RECREATE
  • PRE_COLLECTION_REMOVE
  • PRE_COLLECTION_UPDATE
  • PRE_DELETE
  • PRE_INSERT
  • PRE_LOAD
  • PRE_UPDATE
  • REFRESH
  • REPLICATE
  • RESOLVE_NATURAL_ID
  • SAVE
  • SAVE_UPDATE
  • UPDATE

Con estos eventos podemos ser notificados de muchas cosas que suceden internamente en Hibernate en algunos casos antes y/o después del evento. En JPA se dispone de varias anotaciones (@PreInsert, @PostInsert, …) con las que podemos marcar un determinado método como listener de un evento pero no funcionan si usamos únicamente Hibernate.

En este artículo explicaré como implementar un listener de ejemplo que reciba parte de estos eventos usando solo Hibernate. Primeramente e importante, debemos tener en cuenta que el proceso como reacción a uno de estos eventos ha de ser muy ligero y tardar poco tiempo ya que algunos eventos son lanzados por cada instancia de entidad como consecuencia de operaciones muy frecuentes en una aplicación, si tardasen mucho o consumiesen mucha memoria o tiempo de procesador probablemente el rendimiento de la aplicación disminuiría notablemente.

Como se ve en la clase EventType cada evento tiene un listener distinto, para evitar crear una clase diferente por cada listener podemos emplear el patrón de diseño Adapter de forma que implemente las diferentes interfaces en las que estamos interesados. La implementación de la clase Adapter y una implementación de esta clase Adapter si nos interesasen los eventos PRE_INSERT, PRE_UPDATE, PRE_DELETE, POST_INSERT, POST_UPDATE, POST_DELETE sería la siguiente:

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEventListener; import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreDeleteEventListener; import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreInsertEventListener; import org.hibernate.event.spi.PreUpdateEvent; import org.hibernate.event.spi.PreUpdateEventListener; import org.hibernate.persister.entity.EntityPersister; public class HibernateEventAdapter implements PreInsertEventListener, PostInsertEventListener, PreUpdateEventListener, PostUpdateEventListener, PreDeleteEventListener, PostDeleteEventListener { private static final long serialVersionUID = 1L; @Override public boolean requiresPostCommitHanding(EntityPersister persister) { return false; } @Override public void onPostDelete(PostDeleteEvent event) { } @Override public boolean onPreDelete(PreDeleteEvent event) { return false; } @Override public void onPostUpdate(PostUpdateEvent event) { } @Override public boolean onPreUpdate(PreUpdateEvent event) { return false; } @Override public void onPostInsert(PostInsertEvent event) { } @Override public boolean onPreInsert(PreInsertEvent event) { return false; } }</code></pre></noscript>
<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreInsertEvent; import org.hibernate.event.spi.PreUpdateEvent; import org.springframework.beans.factory.annotation.Autowired; import es.com.blogspot.elblogdepicodev.plugintapestry.services.spring.DummyService; public class ProductoEventAdapter extends HibernateEventAdapter { private static final long serialVersionUID = 1L; @Autowired private DummyService dummy; public void setDummy(DummyService dummy) { this.dummy = dummy; } @Override public void onPostDelete(PostDeleteEvent event) { dummy.process(&quot;postDelete&quot;, event.getEntity()); } @Override public boolean onPreDelete(PreDeleteEvent event) { dummy.process(&quot;preDelete&quot;, event.getEntity()); return false; } @Override public void onPostUpdate(PostUpdateEvent event) { dummy.process(&quot;postUpdate&quot;, event.getEntity()); } @Override public boolean onPreUpdate(PreUpdateEvent event) { dummy.process(&quot;preUpdate&quot;, event.getEntity()); return false; } @Override public void onPostInsert(PostInsertEvent event) { dummy.process(&quot;postInsert&quot;, event.getEntity()); } @Override public boolean onPreInsert(PreInsertEvent event) { dummy.process(&quot;preInsert&quot;, event.getEntity()); return false; } }</code></pre></noscript>

Una vez que tenemos la clase que va a recibir los eventos para que Hibernate la use debemos crear un Integrator que lo instanciará y la dará a conocer a Hibernate. En el siguiente código puede verse una implementación de un Integrator de Hibernate, en el se instancia el listener y se asocia a los diferentes eventos. En este caso solo se crea un listener pero perfectamente podríamos asociar varios listeners al mismo evento:

<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate; import org.hibernate.cfg.Configuration; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; import org.hibernate.integrator.spi.Integrator; import org.hibernate.metamodel.source.MetadataImplementor; import org.hibernate.service.spi.SessionFactoryServiceRegistry; import es.com.blogspot.elblogdepicodev.plugintapestry.services.spring.DummyService; public class HibernateIntegrator implements Integrator { @Override public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { // As you might expect, an EventListenerRegistry is the place with which event listeners are registered. It is a service // so we look it up using the service registry final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService(EventListenerRegistry.class); ProductoEventAdapter pea = new ProductoEventAdapter(); pea.setDummy(new DummyService()); eventListenerRegistry.setListeners(EventType.PRE_INSERT, pea); eventListenerRegistry.setListeners(EventType.PRE_UPDATE, pea); eventListenerRegistry.setListeners(EventType.PRE_DELETE, pea); eventListenerRegistry.setListeners(EventType.POST_INSERT, pea); eventListenerRegistry.setListeners(EventType.POST_UPDATE, pea); eventListenerRegistry.setListeners(EventType.POST_INSERT, pea); } @Override public void integrate(MetadataImplementor metadata, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { } @Override public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { } }</code></pre></noscript>
<noscript><pre><code>package es.com.blogspot.elblogdepicodev.plugintapestry.services.spring; import es.com.blogspot.elblogdepicodev.plugintapestry.entities.Producto; public class DummyService { public void process(String action, Object entity) { if (entity instanceof Producto) { Producto p = (Producto) entity; System.out.println(String.format(&quot;Action: %s, Id: %d&quot;, action, p.getId())); } } }</code></pre></noscript>

Finalmente, para que Hibernate conozca la existencia de este Integrator debemos crear un archivo que contenga el nombre completo de la clase Integrator. El archivo ha de estar en de un librería .jar en la ubicación /META-INF/services/org.hibernate.integrator.spi.Integrator y disponible en el classpath. El contenido de este archivo para el ejemplo es:

<noscript><pre><code>es.com.blogspot.elblogdepicodev.plugintapestry.services.hibernate.HibernateIntegrator</code></pre></noscript>

Con esto ya recibiremos los eventos cuando ocurran. En el ejemplo aparecerá en la consola los mensajes cuando se inserte, actualice o elimine una fila de base de datos. En las capturas de imagen se muestran las trazas de una inserción, una traza para la preinseción Action: preInsert, Id: null donde se ve que la entidad no tienen identificativo asignado y otra traza después de la inserción Action: postInsert, Id: 1 donde la entidad ya tiene identificativo asignado y la sentencia SQL se ha ejecutado, como se ve en la captura los mensajes salen antes y después de ejecutarse la sentencia SQL que se envía a la base de datos.

Otra implementación distinta a la expuesta en este artículo es con anotaciones tal y como hace JPA, podríamos hacer una implementación de listener que busque una anotación en la entidad y llame a ese método cuando se produzca el evento. Depende de como prefiramos organizar el código, si preferimos tener el código del listener separado de la entidad o todo el código en la propia entidad.

Esto así puede servirnos pero si el listeners es más complejo debamos hacer uso de un servicio de Spring, en el ejemplo mostrado se usa la clase DummyService.java que es instanciada por HibernateIntegrator.java e inyectada en la clase adaptador ProductoEventAdapter.java. En el siguiente artículo explicaré lo que debemos hacer para crear un listener de Hibernate que use servicios de Spring e inyecte dependencias de otros servicios, de esta forma el listener o adaptador podrá usar todas las funcionalidades de los servicios disponibles en el contenedor IoC de Spring.

El código fuente completo del ejemplo lo puedes encontrar en mi repositorio de GitHub.

Referencia:
Documentación hibernate
Interceptores y eventos
Special service registries
Event Listener Registration
Listeners y reglas de negocio[alfresco]: http://www.alfresco.com/

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

Picando Código

La canción Code Monkey de Jonathan Coulton en forma de código

February 20, 2015 02:00 PM

Code Monkey de Jonathan Coulton es un tema que ya tiene algunos años y está basado en la experiencia como desarrollador de software del autor. Recientemente llegó a tener su propio cómic llamada Code Monkey Save World a cargo de Greg Pak y Takeshi Miyazawa.

La licencia Creative Commons de la canción permite que se hagan obras derivadas a partir de ella, y una vez más una persona creativa en internet se tomó el trabajo de hacer un video con la canción.

El programador Jim McKeeth escribió la letra de la canción como código fuente en Delphi. Al ejecutar el código, también se muestra la letra.  Con todo esto, publicó el siguiente video:

El video también tiene una licencia Creative Commons, y se pueden descargar el código fuente desde el sitio del autor.

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

Poesía Binaria

Intercambiar datos entre hojas de cálculo Calc o Excel usando una columna de referencia

February 19, 2015 09:20 AM

Imaginemos que tenemos en una hoja de cálculo (una Excel de toda la vida, aunque no me gusta llamarla por un nombre comercial, es como llamar a los yogures Danone o Gillette a las cuchillas de afeitar, en fin), una serie de datos (es muy importante el hecho de tener un identificador o referencia en cada fila). En esta misma hoja, queremos insertar datos que se encuentran en otro archivo de hoja de cálculo haciendo que los nuevos datos se correspondan con el autor de la primera hoja. Queremos cruzar o unificar los datos de las dos tablas en una sola.

Los escenarios que se nos presentan son muy variados, desde un simple listado como el que muestro en el ejemplo como una tienda donde tenemos que introducir información adicional de cientos de productos o un proyecto bibliográfico, etc.

En el ejemplo, se nos hace imposible copiar y pegar, ya que en la otra hoja, por un lado, aparecen los elementos desordenados, y por otra parte, hay elementos que no están en las dos tablas (puede que en una haya elementos que no estén en la segunda y viceversa). Los datos que vamos a utilizar para la segunda hoja son los siguientes:

Por un lado, es cierto que podemos ordenar los datos, pero lo malo es que hay elementos no coincidentes (incluso el número de elementos puede ser distinto).

En este ejemplo he creado una nueva hoja para el primer archivo y he copiado en esa hoja los datos de las fechas. Ahora, creamos la columna Años dentro de la primera hoja y en la segunda fila introducimos la siguiente fórmula:

=BUSCARV(A2;Hoja2.$A$2:$B$100;2;0)

Y extendemos la fórmula a todas las líneas (podemos hacer esto de forma rápida pulsando en la esquina inferior derecha del cuadro que aparece seleccionando dicha celda (cuando hay cientos o miles de filas esto es muy útil).

En la fórmula anterior, BUSCARV, buscará una fila con un criterio especificado y nos devolverá el valor de una celda:

  • A2 nos dice qué tenemos que buscar. Como estamos en la segunda fila y la columna de interés es la primera (A), buscaremos ahí (cuando extendamos la fórmula, este valor irá cambiando: A3, A4, …
  • Hoja2.$A$2:$B$100 es dónde buscar. Es decir, buscamos en la Hoja2 (en mi caso se llama así, debemos poner el nombre de la hoja) en la matriz especificada: desde la primera columna segunda fila, hasta la segunda columna, fila 100. Por supuesto podemos poner muchas más columnas (sólo he usado dos en el ejemplo) y muchas más filas (puse un 100 como un número muy grande). Los signos de dólar son para que al extender la fórmula, ésta no cambie (y no cambien los números de fila). En esta matriz debemos incluir la columna que contiene los valores que queremos devolver.
  • 2 indica la columna cuyo valor queremos mostrar (como queremos la segunda columna, B, ponemos un 2). Tenemos que tener cuidado porque si en la matriz empezamos por una columna distinta de la A, un 1 aquí indicaría esa columna.
  • 0: Los valores no están ordenados, y por tanto se realizará una búsqueda exhaustiva. Si ordenamos previamente los valores en la Hoja2, las búsquedas serán mucho más rápidas.

Como primera aproximación está bien, aunque cuando un ID no se encuentra en la Hoja2, en la columna Años dentro de la primera hoja vemos un horrible #N/D. Para solucionar esto podemos hacer lo siguiente:

=SI.ND(BUSCARV(A2;Hoja2.$A$2:$B$100;2;0);”No hay datos”)

En este caso, si no se encuentra el valor en la Hoja2 veremos un mensaje que dice: “No hay datos“.

Ahora, compliquemos un poco más esto e incorporemos el dato de la fecha al nombre del autor:

=SI.ND(CONCATENAR(B2;” (“;BUSCARV(A2;Hoja2.$A$2:$B$100;2;0);”)”);B2)

Esto, colocará en una celda el nombre del autor (columna B) junto con la fecha entre paréntesis siempre que esté presente, si no lo está, mostrará el nombre del autor simplemente.

Dejo por aquí el archivo de ejemplo (formato ods, 49Kb) por si os resulta de ayuda.

 

La entrada Intercambiar datos entre hojas de cálculo Calc o Excel usando una columna de referencia aparece primero en Poesía Binaria.

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

Picando Código

She Makes Comics

February 18, 2015 02:06 PM

She Makes Comics es un documental que recorre la historia de las mujeres en la industria del cómic. A pesar de una creencia general de que es un arte por y para hombres, siempre han habido mujeres en el mundo de los cómics. Lectoras, artistas, escritoras han sido protagonistas de las historietas desde su origen, a pesar de no siempre haber tenido correcta representación.

She Makes Comics

El documental cuenta de distintas épocas del medio mediante testimonios de varias personas involucradas. El origen de las tiras estuvo en los diarios. En ese momento habían muchas mujeres, tanto creando como consumiendo el material, y esto era algo normal. Varias eran consideradas celebridades. Una de las primeras creadoras mencionadas es Jackie Ormes, la primera mujer afroamericana en dibujar cómics que fueran publicados masivamente.

Particularmente en la década de los 40 mientras los hombres se encontraban peleando alguna guerra, muchas mujeres estaban creando cómics. Y no se trataba exclusivamente de historias románticas, habían mujeres detectives y superheroínas. Eran mujeres fuertes que no necesitaban ser rescatadas por hombres. Las historias transitaban por la ciencia ficción, los western, romance, crímen y demás.

Uno de los cambios fue la introducción de la censura mediante el Comics Code Authority. También tras la Segunda Guerra Mundial con una tendencia de Estados Unidos de “recuperar ciertos valores”, la cosa fue un poco para atrás empezando a retratar a las mujeres de formas particulares.

AquamanRamona Fradon, una de las protagonistas que da su testimonio, fue dibujante de cómics desde 1950 hasta 1995 cuando se retiró, aunque siguió haciendo alguna contribucion esporádica. Dibujó a Aquaman por un tiempo, y es responsable de co-crear a Aqualad y Metamorpho.

A lo largo de los años y en distintos contextos históricos, se fue marginalizando a las mujeres en el medio. Hubo mucha falta de representación con pensamientos misóginos como “las mujeres no pueden dibujar”, “las mujeres no pueden escribir cómics”, entre otras idioteces.

La falta de diversidad en la creación hizo que la oferta para las lectoras estuviera limitadas a historias que los hombres creían que las mujeres querían leer, y se fuera perdiendo público. El mainstream también hizo que mucha gente se sintiera incómoda objetificando a las mujeres en los cómics. El estereotipo del vendedor de cómics de Los Simpsons y la percepción que difunden series de televisión como The Big Bang Theory como que una mujer no puede entrar a una tienda de cómics sin parecer “un bicho raro”, no ayudan.

Gail Simone - She Makes ComicsGail Simone se hizo conocida por fans de los cómics en Internet en Women in Refrigerators, un sitio dedicado a identificar superheroínas que fueran asesinadas, violadas, o sufrieran alguna otra miseria como trama para un personaje hombre. Hoy es una de las escritoras de cómics más influyentes en la industria, habiendo escrito a Superman, Wonder Woman y Batgirl, entre otros personajes.

Se mencionan y entrevistan a varias mujeres célebres más en el mundo de los cómics como Jenette Kahn, editora y presidente de DC durante muchos años. Bajo su tutela, DC abrió nuevos caminos publicando novelas gráficas como Watchmen y The Dark Knight Returns además de defender los derechos de los creadores. Karen Berger tuvo un papel importantísimo como editora de DC y Vertigo y el éxito de varias de sus series.

Además del cómic en sí, se cuenta un poco cómo incidieron temas aledaños com el Cosplay, las conferencias, los webcomics, el manga e Internet en nivelar un poco las cosas y cómo ayudaron a que más mujeres se identificaran y fueran más fuertes en la comunidad.

El documental no se concentra tanto en los aspectos negativos que llevaron al cómic a ser un mercado dominado por los hombres, y celebra las formas en que se fue recuperando y cuenta hoy en día con un panorama más sano. Por suerte en los últimos años la percepción está cambiando. Tanto del lado de los autores como de los consumidores el medio es cada vez más inclusivo y diverso y el documental afirma que hay más mujeres en el mundo de los cómics hoy en día de lo que nunca hubo.

Kelly Sue DeConnickShe Makes Comics es un documental importante que todo amante del cómic debería ver. Además de enseñarnos más sobre la historia de este medio, y señalar los problemas por los que se han pasado, deja un mensaje de la pasión que transmiten las personas involucradas por hacer lo que les gusta.

Como crítica podría decir que a veces como que falta un hilo conductor general. Se va apoyando en las entrevistas para ir contando las cosas, pero no llega a aburrir o distraer.

Les recomiendo mirarlo. Pueden comprarlo en DVD o descarga en el siguiente enlace:
http://sequart.org/movies/6/she-makes-comics/

Termino la nota con algo que dice Kelly Sue DeConnick (actual escritora de Captain Marvel, entre otras cosas) en una de las entrevistas:

Si te gusta leer, si te gustan las historias, si tenés corazón, hay un cómic para vos

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

Variable not found

ASPNET 5, ¿y dónde está mi Web.config?

February 17, 2015 06:36 PM

ASP.NET 5
Hace sólo unos días comentábamos la próxima desaparición del Global.asax a partir de ASP.NET 5 y su sustitución por la clase Startup, pero no es esto lo único que va a dejar de acompañarnos durante nuestras andanzas digitales. Como vimos también hace algún tiempo, el nuevo enfoque de ASP.NET hace que se prescindan de muchos elementos que ya no tienen sentido.

Y en esta ocasión hablaremos sobre la desaparición del Web.config, el célebre archivo de configuración que lleva con nosotros desde los inicios de ASP.NET. Nacido en momentos en que se pensaba que XML era la panacea y muy ligado al denostado System.Web, ha sido útil durante años para configurar aspectos relativos a la compilación y ejecución de las aplicaciones web en el servidor, así como el “sitio oficial” para introducir de settings como cadenas de conexión y otros valores configurables de nuestros sistemas.

Pero, antes de continuar, un disclaimer: ASP.NET 5 está aún en desarrollo, y parte de lo que se diga aquí puede no ser del todo cierto mañana mismo, aunque posiblemente los conceptos fundamentales sí lo serán. O eso espero ;)


Está claro que los tiempos han cambiado: ASP.NET es multiplataforma (tanto en tiempo de ejecución como a la hora de desarrollar sobre él), tenemos a Roslyn proporcionando servicios de compilación sobre la marcha, hay un nuevo modelo de ejecución de peticiones, existen nuevas tendencias, queremos librarnos de System.Web ;) … son demasiados cambios para poder mantener al viejo Web.config y por eso era hora de jubilarlo, o al menos casi totalmente.

Poco más o menos, en las configuraciones que usábamos en un proyecto (y usamos aún a día de hoy) encontramos las siguientes tipologías:
  • Settings personalizados o de aplicación, como una cadena de conexión, la dirección de correo a la que vamos a enviar mensajes de notificación, o la clave del API de un servicio externo. Las encontrábamos en la sección <appSettings> o en custom settings del Web.config.
     
  • Opciones de compilación que indicaban cómo había de ser compilado el código desplegado al servidor (vistas, código en App_Code, y similares).
     
  • Opciones de servidor y runtime, como los handlers o módulos que procesan las peticiones, opciones de seguridad o de comportamiento del framework.
     
  • Configuraciones destinadas a tiempo de diseño e IDEs, como las referencias del proyecto, los archivos incluidos en el mismo, o el modo de compilación a utilizar durante la build.
En versiones de ASP.NET anteriores a la 5, los tres primeros aspectos se definían en ese batiburrillo en el que se había convertido el Web.config. Para el último punto, en cambio, se utilizaban los archivos .csproj o .vbproj que entendían entornos como Visual Studio.
image

Como se puede observar en la captura de pantalla de la derecha, nuestro viejo conocido Web.config ya no existirá en la carpeta raíz de nuestros proyectos, aunque tendremos nuevas fórmulas, mucho más potentes, para introducir las configuraciones:
  • Existirán configuraciones de las herramientas, como los archivos de configuración de Bower, Grunt o Npm, aunque cada una de ellas estará normalmente en su propio archivo. En la captura anterior podéis verlas en bower.json, gruntfile.js y package.json respectivamente.
     
  • Todo lo relativo a la compilación del proyecto, lenguajes, referencias, metadatos, frameworks usados, etc., se mueve al archivo project.json, de existencia obligatoria en toda aplicación ASP.NET 5.
     
  • Todo lo relativo al runtime, como los módulos que procesarán las peticiones o configuración de seguridad se desplaza, como ya sabemos, a la clase Startup y los middlewares que en ella establezcamos. Es decir, este tipo de configuración se realizará totalmente mediante código en la clase de inicialización.
     
  • Los settings personalizados propios de nuestra aplicación pasarán a definirse en uno o varios archivos de configuración, que podrán encontrarse escritos usando prácticamente cualquier origen, formato o sintaxis, desde archivos locales .json, .ini, .xml u otros, hasta orígenes remotos, bases de datos, o incluso las variables de entorno del equipo en que se ejecute la aplicación.
Vamos a detenernos en algunos de estos puntos.

Project.json

Este archivo, obligatorio en toda aplicación ASP.NET a partir de la versión 5, contiene la configuración necesaria para construir, empaquetar y ejecutar el proyecto. En esta dirección de Github podéis encontrar mucho más detalle sobre su contenido, pero a grandes rasgos, encontraremos en él:
  • Las dependencias con otros paquetes que presenta nuestro proyecto.
  • Las configuraciones de compilación disponible (por ejemplo, Debug o Release), con opciones de compilación para cada una de ellas.
  • Los frameworks que emplearemos en el proyecto y las referencias a ensamblados incluidas para cada uno de ellos.
  • Las carpetas y archivos que contienen código a compilar.
  • Dónde se encuentra la carpeta que actuará como “raíz” en el servidor web, normalmente wwwroot.
  • Opciones de compilación (defines, versión de lenguaje, etc.) para Roslyn.
  • Comandos disponibles en la aplicación.
  • Scripts a ejecutar antes o después de la build del sistema.
  • Algunos metadatos, como el nombre del proyecto, versión o autor.
Este archivo es exactamente igual para cualquier IDE o incluso sistema operativo. No hay dependencias de ningún tipo con Visual Studio ni similares; partiendo de la información contenida aquí, podemos lanzar la aplicación desde línea de comandos o empaquetar el resultado para enviarlo a un sitio web. Recordad que una de las ideas de fondo es que podamos desarrollar aplicaciones ASP.NET desde cualquier plataforma.

Definición de settings personalizados

En los settings personalizados encontramos otras de las grandes novedades de ASP.NET 5, la capacidad de tener múltiples archivos u orígenes de configuración, y prácticamente en cualquier formato.

En primer lugar, el hecho de tener más de un archivo de configuración, además de permitirnos “trocear” las configuraciones extensas de forma lógica, va a simplificar y facilitar enormemente la utilización de componentes de terceros. Ya no será necesario, como hasta ahora, retocar nuestro archivo de configuración principal cada vez que instalamos un componente externo, sino que cada componente podrá traer su propio archivo.

La carga de los distintos archivos de configuración se realiza, como no podía ser de otra forma, en la clase  Startup. La siguiente porción código carga en el constructor de la clase de inicialización los settings contenidos en varios archivos JSON en el que se encuentran los settings de la aplicación:



Más adelante veremos un ejemplo de archivo de settings escrito en JSON. De momento, es interesante destacar que si el mismo setting se encuentra en varios archivos de configuración, el último valor será el que prevalezca, en el orden en que los orígenes de datos hayan sido configurados. Podemos usar esta característica para crear una estructura de valores por defecto que puedan ser sobrescritos de forma sencilla sin modificar las configuraciones iniciales.

En segundo lugar, el hecho de que podamos utilizar prácticamente cualquier formato se debe a que el sistema de configuración es muy extensible, lo que abre la puerta a posibilidades que anteriormente eran bastante más difíciles de implementar y, sobre todo, utilizando un interfaz de programación común. En el siguiente código vemos cómo podríamos añadir varios orígenes más de configuración, como un archivo .ini adicional o las variables de entorno del sistema:



Aunque aún están trabajando en ello y podría variar un poco, ASP.NET 5 incorpora de momento los siguientes orígenes de configuración:
  • Archivos .ini
  • Archivos .json
  • Archivos .xml
  • Parámetros de línea de comandos, si arrancamos la aplicación usando esta opción. Serían parámetros que pasamos en la llamada como “/key=value” o “--key=value”
  • Variables de entorno del sistema.
Y lo mejor es que se trata de un sistema extensible, podemos crear nuestro propio proveedor implementando el interfaz Microsoft.Framework.ConfigurationModel.IConfigurationSource y añadiéndolo en la clase de inicialización :)

¿Y cómo accedo a los settings desde mi código?

Desde el punto de vista del consumidor, es decir, de los procesos que deben obtener los valores de configuración, por supuesto que no utilizaremos ConfigurationManager ni otras clases ligadas a las versiones anteriores de ASP.NET. El framework nos ofrece un mecanismo genérico mediante el cual podremos acceder a los valores de los settings independientemente del lugar o formato desde donde se hayan obtenido.

Para ello, sólo tenemos que usar el método Get() de una instancia de IConfiguration, definido en el espacio de nombres Microsoft.Framework.ConfigurationModel, que podríamos recibir, por ejemplo, inyectado en el constructor del controlador:




El código anterior serviría para obtener el valor del correo electrónico en un archivo de settings creado en formato JSON o INI como se muestra en los cuadros. Observad que para acceder a una propiedad que se encuentra en una sección (como MySettings/Email) se ha utilizado como separador los dos puntos.

Obviamente, para que esto funcione antes tendremos que haber registrado en el contenedor de inyección de dependencias de ASP.NET que la instancia de Configuration sobre la que hemos añadido los distintos orígenes será la ofrecida cuando se solicite un objeto IConfiguration, pero eso es harina de otro costal ;)

Eh, pero… ¡aún estoy viendo un Web.config al publicar el proyecto!

Efectivamente, así es, y por eso antes hablaba de “jubilación casi total”: al publicar un el proyecto con la herramienta Publish de Visual Studio, o simplemente al empaquetarlo mediante línea de comandos (kpm pack), se puede ver un bonito Web.config en la carpeta wwwroot. Esto es así porque hay configuraciones que deben seguir siendo desplegadas y son usadas por servidores como IIS para poder ejecutar correctamente la aplicación. De hecho, si publicamos o empaquetamos un proyecto MVC creado directamente desde la plantilla por defecto, en la carpeta wwwroot encontraremos el Web.config con el siguiente contenido a día de hoy:


image

Son las opciones básicas para que el host pueda ejecutar la aplicación, y es generado automáticamente cada vez que hacemos un despliegue o empaquetamos el proyecto para enviarlo a un servidor.

Aún así, si quisiéramos añadir alguna configuración adicional a este archivo, podemos hacerlo creando un Web.config en la carpeta wwwroot de nuestro proyecto e introduciendo en él únicamente lo que necesitemos. Los procesos de empaquetado lo tendrán en cuenta y lo utilizarán como base a la hora de generar el Web.config que irá finalmente al servidor.

Pero observad que en cualquier caso, este archivo no forma parte del raíz de nuestro proyecto, sino que se encuentra en wwwroot, que, como sabemos, es la carpeta donde se encuentra el contenido estático que subiremos al servidor en ASP.NET 5.

Bueno, y vamos a dejarlo aquí. Espero que este vuelo a vista de pájaro sobre la configuración de aplicaciones ASP.NET 5 os sea de utilidad para iros orientando en este nuevo entorno que se avecina.

Publicado en Variable not found.

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

Picando Código

Café de DATA en Montevideo: Especial mes de la mujer/día de los datos abiertos

February 17, 2015 01:00 PM

El sábado 21 de febrero es el Día de los Datos Abiertos. En Montevideo, DATA lo festeja con un evento especial aprovechando el mes de la mujer.

Café de DATA - Mes de la mujer/Día de los Datos Abiertos

La reunión tiene un fin especial, empezar a trabajar en un proyecto a ser lanzado el 8 de marzo, día internacional de la mujer:

El objetivo es crear un visualizador que permita ver todas las calles de Montevideo que llevan nombres de mujeres, en base a los datos abiertos del nomenclator de Montevideo. Ya comenzamos a trabajar y queremos lanzarlo el 8 de marzo (día internacional de la mujer). Este Café de DATA es una oportunidad de acercarse y colaborar en este proyecto.

Ya tenemos la información de todas las calles que llevan nombre de mujer, incluso divididas entre aquellas que reconocen a mujeres por mérito propio y aquellas que se tratan de esposas de otros personajes.

La idea es poder visualizar eso en un mapa, mostrando que incluso desde las personas que reconocemos tenemos un sesgo importante a nivel de género.

Y tratándose de datos del nomenclator, ya tenemos la “historia” detrás de cada calle, por lo que podrías hacer click y saber quién era esa mujer. A esto se suma una recolección ya hecha de información histórica y fotografías.

Nuestra idea es armar un grupo base (coordinación, desarrollo y diseño para el que ya contamos con coordinación) y convocar a quien quiera acercarse a trabajar en el proyecto a este Café de DATA.

Si te interesa sumarte al equipo base que ya está trabajando, por favor escribimos a contacto@datauy.org.

Pueden unirse al grupo de Café de DATA en Meetup para estar al tanto de más eventos e inscribirse a este evento en particular.

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

Picando Código

Videos de RubyConf Argentina 2014

February 16, 2015 01:30 PM

Videos RubyConf Argentina 2014En el post sobre RubyConf Argentina 2014 escribí que publicaría un post una vez que estuvieran los videos subidos

Los videos en Vimeo:

Naveguen la lista de y seguro encuentran alguno que les interese.

Si quieren saber cómo estuvo, dénse una vuelta por mi resumen, para tener una idea de mis impresiones. Como comenté ahí, la conferencia estuvo genial.

Síganlos en Twitter para estar al tanto de novedades. Espero atento anuncios sobre RubyConf Argentina 2015…

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

Poesía Binaria

20 Cosas chulas que se pueden hacer en una terminal de Linux

February 16, 2015 09:58 AM

Un poco como continuación de un post de hace casi 2 años, en el que comentaba ejemplos de cosas que se pueden hacer con Bash en una o dos líneas de código.

Ahora quiero hacer cosas un poco más complejas (y alguna que otra sencilla, pero curiosa), en una nueva sección de códigos para copiar y pegar en vuestros terminales:

Servidores

Montar un servidor FTP rápido

Ésta es una solución para un apaño, para el caso en que necesitemos activar un servidor FTP para pasar un fichero a alguien, o para que nos pasen algún fichero… o por ejemplo por si queremos instalar un plugin en WordPress rápidamente y somos unos paranoicos de la seguridad (y por tanto no queremos dejar un servidor FTP corriendo siempre).
Dependencias: python y pyftpdlib

1
$ sudo python -m pyftpdlib

Si queremos lanzar el servidor en un puerto diferente al 21:

1
$ sudo python -m pyftpdlib -p2121

Si además queremos dar permisos de escritura en la carpeta actual:

1
$ sudo python -m pyftpdlib -p2121 -w

Sacar listado de las ips que más han visitado una web servida por Apache

Dependencias: Apache
Si queremos conocer las direcciones que más han entrado hoy nuestro servidor.

1
cat /path/to/access.log |awk '{print $1}' |sort |uniq -c |sort -n |tail

Podríamos cambiar el cat por tail -n X, así vemos sólo las últimas visitas.
Fuente: Count IP Addresses in Access Log File

Descargar una URL cambiando el User-agent

Para ver con los ojos de un robot, o para hacer pruebas con nuestros scripts:

1
$ curl -A "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" http://mi-web.com

Así me disfrazo del robot de rastreo de Microsoft…

Borrar todas las bases de datos de un servidor MySQL excepto mysql, test e information_schema

A veces, necesitamos limpiar un servidor de base de datos, o quitar todas las tablas menos algunas:
Dependencias: MySQL

1
mysql -uroot -p[password] -e "SHOW DATABASES" grep -v 'Database\|mysql\|test\|information_schema' | awk '{print "DROP DATABASE " $1 ";select sleep(0.1);"}' | mysql -uroot -p[password]

Si no queremos poner el password de la base de datos en la línea de comandos, nos lo pedirá dos veces.

Barra de progreso al importar datos a MySQL

Cuando tenemos gran cantidad de datos para importar, muchas veces nos ponemos de los nervios al ver que no acaba, y como no sabemos cuánto queda, nos frustramos y desesperamos. Para ello, tengo varias soluciones, una sin entorno gráfico (para servidores) y otra con él (para escritorio)

Dependencias: mysql, pv

1
$ pv -n backup.sql | mysql -uroot -ppassword base

Y ahora con entorno gráfico, vamos con barra de progreso y notificación al final:
Dependencias: mysql, pv, zenity, notify-send

1
$ (pv -n backup.sql | mysql -uroot -ppassword base) 2&gt;&amp;1 | zenity --progress --auto-close --title "Importando..." ; notify-send "La importación de base de datos ha terminado"

Adminsitración

Módulos del kernel instalados en el sistema

En muchas instalaciones podemos pedir el listado de módulos instalados en el sistema con:

1
$ modprobe -l

Pero en otros sitios no funciona. De todas formas, es un listado de archivos con extensión .ko a partir de una ruta específica, así que:

1
$ find /lib/modules/$(uname -r)/ -iname "*$1*.ko*" | cut -d/ -f5-

Dibujar un árbol jerárquico de archivos y directorios (tree)

Bueno, el caso es hacer algo parecido al comando tree sin tener el comando tree.

1
$ ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'

Fuente: Tree

Borrar una estructura de archivos y directorios en una carpeta a parte

No sé cómo explicar esto, es un poco difícil, imaginemos que estamos dentro de un directorio X que tiene unos archivos y unos directorios dentro, luego tenemos un directorio Y que tiene lo mismo que X junto con más cosas.
El caso típico es la instalación a mano de plugins en ciertos programas. El plugin tiene muchos archivos que se copian junto con otros archivos del programa, por lo que tendremos los archivos del programa y el plugin mezclados.
Lo que queremos hacer es borrar el plugin:

1
function deltwin() { FILE=${1:2}; DPATH=$3; COMMAND=$2; $COMMAND $DPATH$FILE; }; find . | while read file; do deltwin "$file" rm "/directorio/de/programa"; done

Para usar esto, nos ponemos en el directorio que contiene la estructura que queremos borrar (pero la queremos borrar de /directorio/de/programa).

Cambiar stack size sin ser root (o cualquier cosa de ulimit)

Tal vez queremos cambiar algún parámetro de ulimit dentro del usuario actual y nos deniega el permiso. Puesto que ulimit no tiene ejecutable asociado, no podemos usarlo directamente con sudo. Y si nos hacemos root, no somos el usuario que queremos ser…

Dependencias: sudo configurado y usuario en sudoers.

1
$ sudo sh -c "ulimit -s 8192000 &amp;&amp; exec su user"

Sacar un listado de directorios ordenados por tamaño

Muy útil cuando nos estamos quedando sin sitio en el disco y no sabemos dónde se nos ha ido. Aunque tarda un montón si nuestro disco es grande.

1
du -kx | egrep -v "\./.+/" | sort -n

Listar los archivos más grandes dentro de todas las carpetas

Encuentra todos los archivos a partir de la ruta actual y los ordena por tamaño. Alguna vez he encontrado un .iso perdido en mi hdd que no hacía más que comer espacio.

1
find ~ -type f -exec ls -s {} \; | sort -n

Reiniciar un dispositivo de entrada en Linux

En ocasiones, puede que un ratón / touchpad u otro dispositivo de entrada deje de funcionar correctamente en nuestro servidor X. Para ello, primero averiguamos el ID del dispositivo con:

$ xinput

Luego hacemos:

$ xinput disable ID
$ xinput enable ID

Nos podemos ahorrar muchos reinicios innecesarios con esto.

Cambiar el título de una ventana bajo el ratón

Escribimos el nombre, hacemos click en la ventana y tendrá otro nombre. Muy útil para organizar nuestro escritorio.
Dependencias: zenity, wmctrl

1
$ zenity --entry "Titulo de la ventana" | xargs -i /usr/bin/wmctrl -r :SELECT: -N "{}"

Comprimir un tar.gz en multi-hilo

Cuando creamos un archivo tar.gz con

1
$ tar cvzf archivo.tar.gz archivo_a_comprimir1 archivo_a_comprimir1 ...

la compresión se realiza en un sólo núcleo del procesador por lo que, cuando hay gran cantidad de datos, la compresión puede ser muy lenta. Pero podemos utilizar pigz para comprimir, si tenemos varios procesadores o varios núcleos, podremos aprovecharlos todos para realizar la compresión. Y es tan fácil como:

1
$ tar -I pigz -vcf archivo.tar.gz [archivos a comprimir]

Es lo mismo pero sustituyendo el argumento z por el comando que queremos utilizar para realizar la compresión. Si por ejemplo quiero comprimir mi carpeta de documentos y descargas:

1
$ tar -I pigz -vcf documentos_y_descargas.tar.gz Documentos/ Descargas/

Para descomprimir, como siempre, en lugar de usar c, usamos x:

1
$ tar -I pigz -vxf documentos_y_descargas.tar.gz

Cifras y letras

Búsquedas en un archivo con contexto

Con la siguiente instrucción podemos buscar un patrón dentro de un fichero y veremos 3 líneas por arriba y 3 líneas por abajo. Muy útil para no perdernos cuando buscamos en textos muy extensos.

1
$ grep -C3 patron fichero

Por cierto, el patrón puede ser un texto sencillo o una expresión regular.

Sacar N dígitos del número PI

Una frikada en toda regla…

1
$ echo "scale=N; a(1)*4" | bc -l

Donde N=10, 100, 1000, 12345… no lo hagáis muy grande porque puede tardar mucho tiempo…

Obtener un número aleatorio

Es nuestro dado para la terminal:

1
head -c 1 /dev/random | od -An -td | sed 's/\s*\(.*\)\s/\1/g'

Ya que sólo sacamos un byte, el número más grande será 255, pero podemos sacar números más grandes aumentando el valor. Si pedimos más de 4 bytes, el resultado se dividirá en dos números.
Fuente: Código para llevar

Extraer una cadena de texto aleatoria

1
$ echo "$(dd if=/dev/urandom bs=30 count=1 2&gt; /dev/null)"

Si te parece demasiado aleatoria, démosle caracteres más normales…

1
$ echo "$(dd if=/dev/urandom bs=64 count=1 2&gt; /dev/null)" | tr -dc 'a-zA-Z0-9'

También lo podemos hacer con openssl:

1
$ openssl rand -base64 32

Esta forma puede dar el siguiente error: unable to write ‘random state’, normalmente se debe a que el archivo $HOME/.rnd es propiedad de otro usuario, como puede ser root.

Crear un comando rot13 para consola

Diversión sin límites codificando mensajes con el algoritmo rot13:

1
alias rot13="tr a-zA-Z n-za-mN-ZA-M"

Así podremos hacer lo siguiente:

$ echo “Hola mundo!” | rot13
Ubyn zhaqb!
$ echo “Ubyn zhaqb!” | rot13
Hola mundo!

Reemplazar espacios por guiones bajos sólo en el texto entrecomillado

1
$ echo "<a href="\&quot;http://miweb.com/una">Una ruta con espacios</a>" | awk 'BEGIN { FS = "\""; OFS="\"" } { gsub(" ","_",$2); print }'

En el ejemplo se ve muy claro, sólo cambiamos los espacios de la URL que está entre comillas.

¿ Alguna curiosidad interesante o algún mini-script que te haya salvado la vida ? Me encantaría leer vuestros comentarios.

Actualización 16-02-2015 : Añadido un truco más para que quede un número redondo.

Foto: Robert Couse-Baker (Flickr CC-by)

La entrada 20 Cosas chulas que se pueden hacer en una terminal de Linux aparece primero en Poesía Binaria.

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

Variable not found

Enlaces interesantes 187

February 16, 2015 08:25 AM

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada, espero que os resulten interesantes ;-)

.Net

ASP.NET

Data access

Html/Css/Javascript

Visual Studio/Complementos/Herramientas

Cross-platform

Otros

Publicado en Variable not found

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

Koalite

Cohesión y Acoplamiento

February 16, 2015 05:06 AM

Junto con ponerle nombres a las cosas, uno de los aspectos más complicados a la hora de desarrollar software es decidir cómo asignar las distintas responsabilidades de la aplicación, en qué componentes repartirlas y cómo agruparlas. Existe mucha literatura al respecto y si queréis empezar por algunos clásicos, al menos en lo que a diseño orientado a objeto se refiere, podéis echarle un vistazo a los patrones GASP.

En este post vamos a centrarnos en dos aspectos que, aunque a primera vista pueden parecer ortogonales, en ocasiones están más ligados de lo que nos gustaría: la cohesión y el acoplamiento.

Podríamos definir la cohesión como lo estrecha que es la relación entre los componentes de algo. Si hablamos de clases, una clase tendrá una cohesión alta si sus métodos están relacionados entre sí, tienen una “temática” común, trabajan con tipos similares, etc. Si pasamos a componentes de mayor tamaño, como paquetes o librerías, tendríamos una cohesión alta cuando las clases que lo forman están muy relacionadas entre sí, con un objetivo claro y focalizado.

El acoplamiento es la manera que se relacionan varios componentes entre ellos. Si existen muchas relaciones entre los componentes, con muchas dependencias entre ellos, tendremos un grado de acoplamiento alto. Si los componentes son independientes unos de otros, el acoplamiento será bajo. Al igual que con la cohesión, podemos ver el acoplamiento a distintos niveles y existe acoplamiento entre los métodos de una misma clase (o las funciones de un módulo), entre distintas clases o entre distintos paquetes. Además, existen varios tipos de acoplamiento, desde acoplamiento a través de datos comunes, acoplamiento temporal (es necesario utilizar los componentes en un orden concreto), etc.

Como decía al principio del post, a priori ambas dimensiones son independientes, pero en la práctica no siempre es así. Algo parecido veíamos al hablar sobre la Ley de Demeter; Don’t Ask y God Object: si queremos aumentar la cohesión podemos acabar creando un alto acoplamiento entre distintas funcionalidades del sistema.

Podemos mostrar ambas dimensiones en un gráfico:

cohesión y acoplamiento

Está claro que lo ideal es situarnos en el cuadrante de alta cohesión y bajo acoplamiento (eso es Bueno™), y lo peor que nos puede pasar es acabar en la situación opuesta, con baja cohesión y alto acoplamiento (eso es Malo™), pero si tenemos que perder uno u otro, ¿qué sacrificios estamos haciendo? ¿Qué es lo que nos perdemos y lo que ganamos en cada caso?

Favorecer la alta cohesión

Supongamos que nos movemos hacia el cuadrante de la alta cohesión a costa de aumentar también el acoplamiento.

Un código muy cohesionado tiende a ser más autocontenido y, precisamente por eso, suele ser más fácil de entender como un todo porque tiene menos dependencias externas y un API más definida. Podemos tratarlo como una caja negra que encapsula de forma férrea toda la lógica e información que contiene y eso hace que sea más sencillo olvidarse de su implementación interna.

Además, si aumentamos la cohesión podemos tener menos componentes, lo que nos ayuda a descubrir las funcionalidades del sistema y a tener más claro dónde asignar cada responsabilidad.

En un componente con una alta cohesión podemos llegar a mezclar responsabilidades que, en algunos casos, podríamos ver como independientes. Por ejemplo, en una aplicación podríamos tener un componente que encapsula a la vez DOM, CSS y Javascript. Esto facilita su uso porque sólo hay que considerar un componente a la hora de utilizarlo, pero limita la flexibilidad porque estamos acoplando responsabilidades que, tal vez, deberíamos separar (el aspecto con el CSS y la funcionalidad con el Javascript).

El riesgo claro de esto es acabar construyendo un supercomponente que hace mil cosas en aras de la cohesión. Lo curioso es que en ese escenario, la búsqueda de la cohesión nos lleva justamente a todo lo contrario, acabar con un componente poco cohesionado en el que existen operaciones que realizan tareas que no tienen nada que ven entre ellas.

También hay que tener en cuenta la pérdida de libertad que mencionábamos antes: al acoplar más la funcionalidad, será más complicado reemplazar partes de la misma para adecuarla a nuevos escenarios.

Favorecer el bajo acoplamiento

Si preferimos situarnos en el cuadrante del bajo acoplamiento a costa de perder cohesión, tendremos un escenario diferente.

Al favorecer el bajo acoplamiento lo normal es que tengamos componentes más pequeños, con responsabilidad más definida y, por tanto más fáciles de entender por separado.

Estos componentes serán, en general, más reutilizables, precisamente porque al ser independientes unos de otros e incluir menos funcionalidades habrá más escenarios en los que tengan cabida.

Ambos factores ayudan a tener un sistema más flexible, en el que podemos reconfigurar las relaciones entre las funcionalidades que ya tenemos implementadas para añadir otras nuevas.

Un riesgo asociado a un diseño en el que todos los componentes están muy desacoplados unos de otros es que al tender a ser componentes más pequeños y reutilizables, se acaban reutilizando en muchos contextos, por lo que aquello que inicialmente era una ventaja porque nos permitía desacoplar aspectos dentro de una funcionalidad, acaba por acoplar unas funcionalidades con otras a través del uso de componentes comunes.

Además, comprender la estructura global de un sistema en el que todo está muy desacoplado puede ser más difícil porque necesitamos comprender no sólo los componentes individuales sino las relaciones entre ellos, y éstas suelen ser menos explícitas y directas para poder reducir el acoplamiento.

Distintos niveles, distintas prioridades

Al definir la cohesión y el acoplamiento veíamos que ambos conceptos pueden aplicarse a distintos niveles y, en mi experiencia, dependiendo del nivel en el que nos estemos moviendo, merece la pena preocuparse más de una cosa u otra.

Dentro de una funcionalidad concreta, prefiero primar la alta cohesión.

Si tengo un componente para mostrar en pantalla resultados deportivos, prefiero que el componente sea lo más autocontenido y cohesivo posible para facilitar su reutilización, aunque ello implique un mayor acoplamiento entre sus distintas facetas y tenga en un mismo “paquete” el código CSS, la obtención del datos del servidor y el HTML necesario para renderizarlo.

Igualmente, si tengo un componente (una clase o un grupo de clases) para representar una factura, prefiero primar la cohesión aunque eso me haga perder flexibilidad, por ejemplo a la hora de cambiar la forma en que se calculan los impuestos. A veces esto se manifiesta en el uso de clases privadas, en no utilizar inyección de dependencias o en minimizar el API externa.

Cuando se trata de coordinar las distintas funcionalidades de una aplicación, le doy más importancia a conseguir un bajo acoplamiento.

Intento evitar dependencias rígida entre las distintas funcionalidades, introduciendo niveles de indirección entre ellas mediante interfaces, estructuras de datos básicas para el intercambio de datos y, en general, cualquier cosa que me permita minimizar el conocimiento de unas con respecto a las otras.

Si tengo un módulo de facturación y otro de gestión de clientes, probablemente intentaré que el módulo de facturación no tenga dependencias directas sobre el módulo de clientes y limitaré el acceso al mismo al mínimo, tratando de no compartir estructuras de datos complejas (clases) para facilitar la evolución por separado de cada módulo. Algo parecido a lo que comentaba al hablar sobre diseño de modelos.

Conclusiones

No hay (o la menos yo no la tengo) una respuesta clara a qué debemos primar en caso de que no podamos conseguir una alta cohesión y un bajo acoplamiento, en función de cada proyecto (y cada equipo de desarrollo) puede ser más importante dar prioridad a una u otra cosa.

Como siempre, lo importante es conocer las implicaciones de las decisiones que tomamos y ser capaces de valorar lo que estamos consiguiendo y lo que estamos perdiendo por el camino.

No hay posts relacionados.

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

Blog Bitix

La palabra clave assert de Java y un ejemplo

February 14, 2015 10:30 PM

Java

La palabra clave o reservada assert sirve para aseverar que en un determinado momento del código una determinada condición debe ser cierta. Está disponible en Java desde la versión 1.4 pero al menos yo con bastantes años de experiencia en programación en este lenguaje aún no he usado de forma amplia y posiblemente le pase a mucha de la gente y aún así hemos sobrevivido durante todo este tiempo.

Sin embargo, puede resultarnos bastante útil. Una de las situaciones en que puede ayudarnos es para descubrir una condición no válida en el momento del assert y no donde se produce una excepción en otro punto del código que puede no ser la causa real del error. Por ejemplo, supongamos que un método privado no acepta un parámetro con valor null, una variable no puede ser null o una colección no ha de estar vacía por poner solo unos pocos ejemplos de condiciones, si en un punto del código estamos seguros que es un error que esa condición sea falsa podemos hacer que el programa falle con una excepción ahí y no más tarde a consecuencia de que las condiciones no se cumplían. Otra forma en la que nos ayudan los assert es como documentación, en vez de poner un comentario o en el javadoc indicando una condición que se ha de cumplir podemos ponerlo con un assert. Normalmente se usan en:

  • Precondiciones: en métodos privados que el llamador ha de cumplir.
  • Postcondiciones: para verificar el resultado prometido por el método.
  • Class invariants: para validar el estado de una clase según está definido en su contrato, siempre se debe cumplir independientemente de las operaciones que se realicen.
  • Código no alcanzable en tiempo de ejecución: partes del programa que se espera que no sea alcanzable, como cláusulas else o default en sentencias switch.

Y no deben usarse para:

  • No se deben usar para comprobar argumentos en métodos públicos: los asserts pueden habilitarse o deshabilitarse, comprobar los argumentos se considera parte de las responsabilidades del método y su especificación.
  • No se deben usar para realizar tareas: ya que los asserts pueden deshabilitarse las tareas dejarían de ejecutarse y de proporcionar la funcionalidad del programa.

Nos pueden entrar dudas de cuando emplear un assert y cuando un if o una excepción. Las excepciones se encargan de hacer que el programa sea robusto controlando las situaciones inesperadas pero posibles, los assert se encargan de que el programa sea correcto. Los assert deberían ser usados para asegurar algo, mientras que las excepciones deberían usarse para comprobar algo que podría ocurrir. Un assert termina la ejecución (ya que no se suele capturar la excepción que se produce) mientras que una excepción permite al programa continuar con la ejecución. Los asserts no deben ser sustitutos de condiciones de validación que debería hacer el programa en métodos públicos de una clase. Los assert son una herramienta en tiempo de desarrollo, las excepciones además son una herramienta para la ejecución en producción.

Un pequeño ejemplo de los asserts podría ser el siguiente en la que en el método nextNumber hay una postcondición según la cual el método debe devolver un número entero entre 0 y 9 (incluidos):

<noscript><pre><code>package io.github.picodotdev.blogbitix.asserts; import java.util.Random; public class Main { private Random random; public Main() { random = new Random(); } /** * Devuelve un número entero entre 0 y 9. */ public int nextNumber() { int i = random.nextInt(40); // Si el cálculo del número fuese más complejo incluyendo un assert // podemos asegurar el valor generado por este método. assert i &gt;= 0 &amp;&amp; i &lt; 10: String.format(&quot;El número aleatorio devuelto no cumple la postcondición (%d)&quot;, i); return i; } public static void main(String[] args) { Main main = new Main(); System.out.println(main.nextNumber()); } }</code></pre></noscript>

Un assert cuya expresión se evalúa como falso produce una excepción del tipo java.lang.AssertionError pero para ello se han de habilitar en tiempo de ejecución como el parámetro -ea de la máquina virtual. En eclipse podemos cambiarlo en la configuración de ejecución del programa en la pestaña Arguments y VM arguments tal como se ven en la siguiente captura de pantalla:

En la primera de las siguentes capturas de pantalla puede verse como el programa se ejecuta sin producir una excepción a pesar de no cumplirse el assert del método nextNumber ya que los asserts no fueron activados, en la segunda captura activando los assert se lanza una excepción al no cumplirse la postcondición.

En el recomendable artículo Programming With Assertions se comenta de forma más detallada y amplia el funcionamiento y uso adecuado de la palabra clave assert de Java.

El funcionamiento de los assert en Groovy es distinto. En groovy los assert no pueden deshabilitarse, están siempre habilitados y por tanto no hace falta usar el parámetro -ea de la máquina virtual que empleamos en Java, no es un bug es una feature. Por el contrario, en Java los asserts se consideran una herramienta en tiempo de desarrollo o depuración y por tanto podemos habilitarlos mientras desarrollamos y no habilitarlos en producción, una de las razones es que los asserts pueden suponer una penalización de rendimiento si las comprobaciones son costosas en tiempo o carga de CPU cosa que no queremos en producción donde el código ya se considera correcto.

Referencia:
Programming With Assertions
Correct use Java “assert” keyword
When to use an assertion and when to use an exception
Is Groovy’s assert a good idea for production code, unlike Java’s assert?
Java: Should I assert or throw AssertionError?[alfresco]: http://www.alfresco.com/

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

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

¿Cómo va FileOptimizer?

February 13, 2015 04:24 PM

Estamos ya en 2015, lo que quiere decir que FileOptimizer, tiene ya casi 3 años de edad. Si hace 2 años hablaba de la marca de las 30.000 descargas, a día de hoy se superan las 200.000 sólo desde Sourceforge, es decir, sin contar con otros mirrors. Lo que hace una media aproximada de 1.500 descargas semanales. Sin lugar a dudas FileOptimizer era mi proyecto personal más exitoso, y a la vez el menos ambicioso. Con estas cifras, ahora se demuestra que probablemente no haya ningún otro que logre tanta popularidad. No hay más que ver la cantidad de webs y redes sociales en donde lo analizan, lo evalúan, lo prueban, y lo recomiendan. La versión 6, sentaba las bases evolutivas de FileOptimizer. Con la versión 7, seguí apostando por el soporte de nuevos formatos: FLAC, MP4, SVG, TAR. Se mejoró además la eficacia en la optimización de muchos otros, [...]

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

Meta-Info

¿Que es?

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

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

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

Idea: Juanjo Navarro

Diseño: Albin