Mapas de calor o heat Maps

Posted on Actualizado enn

Unos de los puntos más importantes en cualquier estrategia de marketing es mejorar y optimizar nuestra plataforma de manera continua, ya sea una tienda online, una web de servicios o una landing page. Conocer qué zonas y elementos de nuestra web generan un mayor y mejor impacto sobre los usuarios es vital para tomar decisiones sobre lo que está funcionando, lo que no y lo que debe mejorarse.

Los distintos servicios de analítica nos aportan en este sentido datos valiosos en cuanto a cómo perciben los usuarios nuestra web y el uso que hacen de ella. Pero a menudo esta información es más difícil de interpretar a nivel de elementos de interfaz o su parametrización no resulta tan ágil como se desearía.

Los mapas de calor se utilizan, en este sentido, para identificar cuáles son los puntos que centran la atención del usuario a través de interacciones y pautas de comportamiento pre-estabecidas.

Percepción del cerebro sobre formatos digitales

El ojo humano sigue determinados patrones de lectura a la hora de “leer” una web. Estos patrones presentan una regularidad o esquemas en relación a cómo percibimos la realidad y sus formatos de lectura. De ahí que cuando leamos una web nos basemos en un patrón de lectura en Z o en F; es decir, realicemos una lectura según unos patrones que el cerebro tienen preconfigurados para tal fin. La Fondeu (Fundación del Español urgente) tiene algunos breves artículos que condensan este tema.

Los referentes en usabilidad han elaborado, con mayor o menor controversia, un canon propio sobre la forma de percibir la información sobre formatos digitales y cómo debería ser presentada. Éste es el caso, por ejemplo, del celebrado Jakob Nielsen y sus estudios sobre usabilidad web.

En este contextos, los de la percepción de la mente sobre información en soportes digitales, el Neuromarketing ha surgido como una técnica que aplica la neurociencia al terreno del marketing, con el fin de identificar los puntos claves que motivan la toma de decisiones en procesos concretos. Analizar los niveles de atención, emoción o memoria forman parte de los procesos que son tenidos en cuenta. La forma en cómo nos relacionamos con lo que nos agrada y nuestra respuesta ya estaba presente en obras capitales como Visión interior de Semir Zeki en la relación de la belleza con el cerebro a través de la neuroestética.

La raíz de la neurociencia es profunda frente al desarrollo de internet como forma de comunicación. Investigadores como Joseph E. LeDoux, Daniel Kahneman o Muhzarin Banaji han postulado teorizaciones propias que albergan desde las motivaciones de la toma de decisiones en zonas de incertidumbre a la percepción del sujeto frente a las valoraciones sociales y de grupo.

Cómo trabajan los mapas de calor

Los mapas de calor, a fin de presentar la información de la manera más gráfica y útil posible utilizan como forma de representación una termografía, estableciendo una jerarquía de dos polos; es decir, por una parte, se hace a través del empleo de colores cálidos (generalmente rojo, naranja y amarillo) para mostrar las zonas de acción de clics o interés de foco, frente a una gama de colores fríos (azul, verde) que semantizan las zonas que no reciben atención por parte del usuario.

Este tipo de técnicas se basa en el concepto de eye-tracking, tecnología que refiere a un conjunto de procedimientos técnicos que permiten monitorizar el modo en que una persona mira una imagen o registro visual. Si bien en algunos casos, como veremos, estas técnicas no tienen una fiabilidad del 100%, sí debería integrarse en cualquier plan analítico integral para la tomas de decisiones, especialmente sobre UI (interfaz de usuario) y UX (experiencia de usuarios).

Mapas de calor basados en clicks (Click Heatmaps)

Clic Heatmap

Este tipo de mapas registran las zonas donde se hacen clicks. Se trata de uno de los mapas más empleados, porque generan información a corto plazo sobre elementos que podemos definir con mucha claridad (por ejemplo, un banner con una promoción especial).

Se engloban dentro de los mapas de calor por la similitud en los resultados obtenidos, ya que comparten una matriz de datos fundamental. Al registrarse la interacción de los usuarios, el grado de verosimilitud de los datos obtenidos es muy relevante. La información que obtenemos se basa directamente sobre acciones concretas como clicks en enlaces, banners, y otros elementos afines. Permiten tomar decisiones a corto plazo (por ejemplo; si un banner para una determinada campaña está funcionando y qué CTR recibe).

Mapas basados en el movimiento del ratón (Mouse movement Heatmap)

Mouse Heatmap

Esta tipología registra el movimiento del ratón y, aunque los datos derivados no tienen una fiabilidad completa, sí aportan valor a la analítica general, especialmente si los cruzamos con datos de otros mapas de calor. Los movimientos de ratón tienden a alinearse con el movimiento de nuestros ojos; existe así una pauta regular e intuitiva que rige el movimiento del ratón semi-inconscientemente hacia los focos de atención. En la Carnegie Mellon University en Pensilvania descubrieron que esta alineación llega hasta unos porcentajes que alejan cualquier duda de estos datos.

…84% of the times that a region was visited by a mouse cursor, it was also visited by (users’) eye gaze. In addition, 88% of regions that were not gazed by the eye were also not visited by a mouse cursor.

Mapas de Scrool (Scroll Heatmaps)

Scroll Heatmap

Se utilizan en páginas que desarrollan mucho scroll a fin de poder detectar el nivel de profundidad del scroll. Es decir, hasta qué punto llega el usuario a hacer scroll y cuál es el límite bajo el cual no existe navegación. Esto resulta especialmente útil para identificar las zonas que mayor atención reciben por parte de los usuarios, de modo que seamos conscientes sobre qué zonas debemos prestar más atención frente a otras zonas grises. Esto nos ayudará a identificar la línea de atención de nuestros clientes o usuarios más allá del famoso above the fold. Un enlace interesante respecto a esto último lo encuentras aquí.

Qué utilidad real tienen los mapas de calor

La utilidad principal, como hemos visto, es trackear zonas concretas de la web frente a la actividad media del usuario genérico en nuestra web. Entre las aplicaciones más destacadas podemos destacar las siguientes:

  • Analizar nuestros procesos de conversión: Nos ayudará a detectar si existen problemas en las fases de conversión, y en su caso a identificarlos de manera directa y proponer soluciones. Por ejemplo, si en una página de proceso de compra de una tienda online registramos problemas en un paso de compra, o si un formulario de una web de servicios recibe problemas por algún elemento.
  • Verificar prácticas de CTAs: Cada vez que se desarrolla una página existe una jerarquía de elementos, y dentro de esa jerarquía hay un ordenación de CTAs (Call to actions). Los CTAs son las llamadas a la acción sobre acciones específicas (“Compra ahora”, “solicita presupuesto”, “clica aquí”, etc), y se organizan según el plan de objetivos de cada página. Hay un CTA principal (por ejemplo, “Compra ahora”) y una jerarquía de CTAs si no se cumple el CTA principal (por ejemplo, “Suscríbete a nuestra newsletter”). Los mapas de calor nos ayudan en este sentido a medir si el usuario está clicando en los elementos que se han desarrollado para tal acción o si, en cambio, debemos realizar una mejora. (modificar CTA principal, cambiar de ubicación, color, acompañar de imagen transaccional, etc.)
  • Comprobar la efectividad de los elementos de información: Resulta vital conocer si determinados elementos están cumpliendo correctamente su función a nivel global. Por ejemplo, si hay elementos en el menú principal que no reciben clicks y pueden cambiarse por otros elementos más transaccionales, o si hay algún elemento pasivo que cobra protagonismo frente al usuario (por ejemplo, icono de gastos de envío gratis que no está enlazado pero que recibe un gran número de clics porque el cliente necesita ampliar esta información). Esto nos ayudará a mejorar nuestra interfaz (UI) y hacer una experiencia cliente (UX) más fuerte y consecuente.
  • Tener un feedback sobre nuestra interfaz: La interfaz supone la forma mediante la cual interactuamos con la página web y tiene un valor crucial. Aunque este punto se encuentra presente de manera transversal en los 3 apartados anteriores, los mapas de calor nos ayudarán a identificar el grado de satisfacción de nuestra interfaz y el grado en que cumple con el cometido para el que fue diseñado. Esto resulta especialmente útil en webs que presentan un diseño fuera de los cánones (firmas de moda, webs de tendencias, etc.)
  • Usos particulares: Bajo usos particulares englobamos todos aquellos programas de objetivos que escapan a los usos generales. Por ejemplo, en webs que monetizan a partir de redes publicitarias, la ubicación de los banners frente al comportamiento del usuario establecerá una política de tarifas publicitarias, gracias al scroll map sabremos hasta qué profundidad nuestros anuncios son vistos por los visitantes.

Recursos disponibles para implementar un mapa de calor

HeatmapsExisten decenas de soluciones para instalar mapas de calor en nuestra web, pero sólo te vamos a reseñar 3: La mejor (y de pago), la que utilizaríamos en cualquier proyecto para iniciarnos (gratuita) y la de Google (que tienen que ir mejorando con el tiempo).

  • Crazyegg: Crazyegg es el rey de los mapas de calor. Bajo esta firma se encuentra Neil Patel. Se trata de una solución de pago, dispone de una suscripción gratuita para los primeros 30 días. Es una solución recomendada para proyectos de una cierta envergadura.
  • Sumome: Se trata de un formato gratuito basado en el concepto de plugin, se implementa sobre la plataforma. Tiene una más que correcta integración con WordPress, tiene plugin propio, aunque recomendamos su integración vía Google Tag Manager. No se trata de la opción más potente pero Sumome es una buena opción para una toma de datos inicial ya que cumple con creces su cometido.
  • Page Analytics (Google): Teníamos que incluir esta opción porque es la oficial de Google, pero dista mucho de lo que debería ser un mapa de calor aún. Para poder utilizarla hay que instalar una extensión en Chrome, que os dejo para instalar pulsando aquí.

En resumen

  • De manera conjunta a nuestro servicio de analítica (Google Analytics, Yandex o sistema elegido externo o interno) los mapas de calor deberían ser nuestra siguiente opción.
  • Suponen una fuente de información valiosa que no necesita de grandes conocimientos técnicos para poner en práctica e interpretar. Si además conseguimos cruzar esta información con valores analíticos (porcentaje de rebote, tiempos de estancia, salidas, embudos de conversión, etc.) conseguiremos perfilar el modo en que nuestros usuarios navegan por la web.
  • Esta información nos ayudará a mejorar nuestro CRO y en consecuencia nuestra campaña global de marketing.
Anuncios

Understand ‘+’, ‘>’ and ‘~’ symbols in CSS Selector

Posted on Actualizado enn

It explains How to use different signs (+,> and ~) in CSS selector and their differences. Before starting, let us take a sample code to understand the signs.

<div id="container">           
   <p>First</p>
    <div>
        <p>Child Paragraph</p>
    </div>
   <p>Second</p>
   <p>Third</p>     
</div>

Space:

div#container p{
font-weight:bold;
}

It is the descendant selector. It will target all p tags within container div.

> Sign:

It will target elements which are DIRECT children of a particular element.

div#container > p {
  border: 1px solid black;
}

css selector

It will target all P element which are direct children of container div, not children of child div.

+ Sign:

It is Adjacent sibling combinator. It combines two sequences of simple selectors having the same parent and the second one must come IMMEDIATELY after the first.

div + p { 
   color: green
}

css selector

It will only select the first element that is immediately preceded by the former selector. In our example, it will target to Second ONLY because the owner P element comes just after Div tag.

~ Sign:

It is general sibling combinator and similar to Adjacent sibling combinator. the difference is that the second selector does NOT have to immediately follow the first one means It will select all elements that is preceded by the former selector.

div ~ p{
background-color:blue;
}

css selector

It will target both second and third.

Hope, you enjoyed this.

¿Cómo me conecto a una cámara con su IP y con Java?

Posted on

Hola amigos!!

Os voy a comentar algo bastante curioso, me han regalado una cámara y decidí averiguar cómo conectarme a dicha cámara empleando un lenguaje de programación, en este caso empleé uno de código libre como es Java. Así que tras comprobar que era una cámara IP y traía consigo Applets y Controles ActiveX para poder conectarnos a estas, sin embargo un gran problema es que este tipo de controles no soportan trabajar fuera del contexto de algún navegador web.

Como Java nos permite conectárnos al módulo CGI que poseen estas cámaras y obtener las imágenes de forma continua para realizar el procesamiento necesario sobre estas. Lo único que tenemos que hacer es acceder a este módulo cgi de forma continua e ir mostrando las imágenes a modo de secuencia lo cuál dará una apariencia de ser el video en sí.

Lo primero que tenemos que hacer es sobreescribir el método paintComponent de un JPanel del siguiente modo:

Captura

 

Luego agregamos este panel a un JFrame y voilà, tenemos nuestra aplicación funcionando, podemos acceder a la secuencia de imágenes cuando se quiera, procesarlas, enviarlas por correo con información adicional, almacenarlas en una base de datos… en fin, un sin número de posibilidades!!!

Es importante saber la dirección correcta del módulo cgi de nuestras cámaras ip para lo cuál debemos acceder a su página de configuración. En mi caso la dirección es la
siguiente: http://192.168.1.150/image/jpeg.cgi. De todos modos cualquier duda, la comentan.

Además si han tenido problemas con la ventana de autenticación de usuario aquí les dejo una solución:

Captura.JPG

Multi-module project in Maven

Posted on Actualizado enn

Hi guys!! There is a lot of time since my last post, I have been very busy ultimatelty so now I am going to tell you something new.

Most of the people when start to code, they usually make only one project and they put all their code in it, so I am going to tell you how to import code from another project using Maven.

Usually, when we design a web application, we do not implement all our java classes and config files in it, but we develop several jar projects that later they are included in our war file. In order to do that, we use  the multi-module projects, it means that we have a project that it is the “father”, that it contains the others, I will show you in an example.

In our example, we are going to use a xml file, called “pom.xml” which contains two projects, a web project and a jar project. The objetive is to create a war file that contains the jar project and the web project.

For starting, we create a file named “pom.xml”, over the other projects, as we can see below:

Captura
Let’s see the main pom file:

Captura

As we can see, it is a project that contains two modules. Inmediately, what we have to do is to type the dependencies to the project.
Captura
Finally, we have to add in the other two pom files, the reference to its “father” pom file:
 Captura
Multi-module and Eclipse
Now, I am going to explain the same but using the IDE Eclipse.
We select in Eclipse “File>>New>>Other>>Maven proyect”.
https://i0.wp.com/www.notodocodigo.com/wp-content/uploads/2013/04/nuevomaven.jpg

We choose the arquetipe “pom-root” to create the main project:

 

https://i2.wp.com/www.notodocodigo.com/wp-content/uploads/2013/04/pomroot.jpg

We type a name and push “Finish” button. Now, we can create the other projects.

Indicamos el proyecto padre y le damos un nombre:

 

https://i2.wp.com/www.notodocodigo.com/wp-content/uploads/2013/04/newmodule.jpg

Select the “maven-archetype-webapp” arquetipe and crete it. You can repeat the same process to create a normal java. Finally, we select the web project , “right click >>Maven>>Add Dependency”, we search for the library, typing “Free” to filter and show us the project

 

Dependencia al módulo Librería

Well, just now we have created the multi-module project, so we are going to stop a little bit in order to analyze how the projects are shown in Eclipse:

Proyecto multimódulo en eclipse

Well, in the image shown on these lines, it has been marked with a red rectangle, all three projects, images in Eclipse as normal projects and can work with them as always. If you look at “ProjectPadre”, we see that it contains two folders that have been surrounded with a green rectangle and that correspond to the files of the two children projects. So, it’s as if the projects were duplicated. In fact, the App.java file that is shown in the image with an arrow is the same, it does not matter to open it by double clicking on one site and another.

Well, this is so, because Maven needs the projects to be physically arranged hierarchically in the directory and Eclipse works with all projects on the same level.

 

Inheriting settings

The configuration is inherited from the parent pom. For example, to compile all projects with java 6, just add this in the parent pom:

 

Captura
Package the multi-module project


We select Run As … >> Run Configuration and we create a “Maven Build” configuration, we make sure to put the “Base directory” correctly and launch the next execution.

https://i1.wp.com/www.notodocodigo.com/wp-content/uploads/2013/04/ejecmulti.jpg

This will create the Site of the project father and the two children. And it will create a file “ProjectWeb.war”. If we open that war to see its content, we check that it contains the project “Libreria” in its “lib” folder. Below these lines we can see a detail of the war file.

https://i0.wp.com/www.notodocodigo.com/wp-content/uploads/2013/04/detallewar.jpg

How to store data in your web page

Posted on Actualizado enn

Hi everybody!!, I was here studying how to code an arduino board, but as you all know I love coding, and I stated to make a website as a simple exercise of entertaiment.

One of most used javascripts frameworks is AngularJs, and I started to learnt it. I made a post about these frameworks some months ago, if you want you can look it up in the “Publicaciones” tab.

I was doing this tutorial and I thought, how could I store data in the web page? And I started to investigate it, and I was very surprised when I read this web page, so it is possible, and now I am going to show you all how to do that:

Get Started

(1) You can install angular-local-storage using 3 different ways:
Git: clone & build this repository
Bower:

$ bower install angular-local-storage –save

npm:

$ npm install angular-local-storage

(2) Include angular-local-storage.js (or angular-local-storage.min.js) from the dist directory in your index.html, after including Angular itself.

(3) Add 'LocalStorageModule' to your main module’s list of dependencies.

When you’re done, your setup should look similar to the following:

<!doctype html>
<html ng-app=myApp>
<head>
 
</head>
<body>
    …
    <script src=//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js></script> 
    <script src=bower_components/js/angular-local-storage.min.js></script> 
    …
    <script>
        var myApp = angular.module(myApp, [LocalStorageModule]);
 
    </script> 
    …
</body>
</html>

Configuration

setPrefix

You could set a prefix to avoid overwriting any local storage variables from the rest of your app
Default prefix: ls.<your-key>

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setPrefix(yourAppName);
});

setStorageType

You could change web storage type to localStorage or sessionStorage
Default storage: localStorage

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setStorageType(sessionStorage);
});

setDefaultToCookie

If localStorage is not supported, the library will default to cookies instead. This behavior can be disabled.
Default: true

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setDefaultToCookie(false);
});

setStorageCookie

Set cookie options (usually in case of fallback)
expiry: number of days before cookies expire (0 = session cookie). default: 30
path: the web path the cookie represents. default: '/'
secure: whether to store cookies as secure. default: false

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setStorageCookie(45, <path>, false);
});

setStorageCookieDomain

Set the cookie domain, since this runs inside a the config() block, only providers and constants can be injected. As a result, $location service can’t be used here, use a hardcoded string or window.location.
No default value

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setStorageCookieDomain(<domain>);
});

For local testing (when you are testing on localhost) set the domain to an empty string ”. Setting the domain to ‘localhost’ will not work on all browsers (eg. Chrome) since some browsers only allow you to set domain cookies for registry controlled domains, i.e. something ending in .com or so, but not IPs or intranet hostnames like localhost.

setNotify

Configure whether events should be broadcasted on $rootScope for each of the following actions:
setItem , default: true, event “LocalStorageModule.notification.setitem”
removeItem , default: false, event “LocalStorageModule.notification.removeitem”

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setNotify(true, true);
});

Configuration Example

Using all together

myApp.config(function (localStorageServiceProvider) {
  localStorageServiceProvider
    .setPrefix(myApp)
    .setStorageType(sessionStorage)
    .setNotify(true, true)
});

API Documentation

isSupported

Checks if the browser support the current storage type(e.g: localStorage, sessionStorage). Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  if(localStorageService.isSupported) {
    //… 
  }
  //… 
});

getStorageType

Returns: String

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  var storageType = localStorageService.getStorageType(); //e.g localStorage 
  //… 
});

You can also dynamically change storage type by passing the storage type as the last parameter for any of the API calls. For example: localStorageService.set(key, val, "sessionStorage"); ###set Directly adds a value to local storage.
If local storage is not supported, use cookies instead.
Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function submit(key, val) {
   return localStorageService.set(key, val);
  }
  //… 
});

get

Directly get a value from local storage.
If local storage is not supported, use cookies instead.
Returns: value from local storage

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function getItem(key) {
   return localStorageService.get(key);
  }
  //… 
});

keys

Return array of keys for local storage, ignore keys that not owned.
Returns: value from local storage

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  var lsKeys = localStorageService.keys();
  //… 
});

remove

Remove an item(s) from local storage by key.
If local storage is not supported, use cookies instead.
Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function removeItem(key) {
   return localStorageService.remove(key);
  }
  //… 
  function removeItems(key1, key2, key3) {
   return localStorageService.remove(key1, key2, key3);
  }
});

clearAll

Remove all data for this app from local storage.
If local storage is not supported, use cookies instead.
Note: Optionally takes a regular expression string and removes matching.
Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function clearNumbers(key) {
   return localStorageService.clearAll(/^\d+$/);
  }
  //… 
  function clearAll() {
   return localStorageService.clearAll();
  }
});

bind

Bind $scope key to localStorageService. Usage: localStorageService.bind(scope, property, value[optional], key[optional]) key: The corresponding key used in local storage Returns: deregistration function for this listener.

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  localStorageService.set(property, oldValue);
  $scope.unbind = localStorageService.bind($scope, property);
 
  //Test Changes 
  $scope.update = function(val) {
    $scope.property = val;
    $timeout(function() {
      alert(localStorage value:  + localStorageService.get(property));
    });
  }
  //… 
});
<div ng-controller=MainCtrl>
  <p>{{property}}</p>
  <input type=text ng-model=lsValue/>
  <button ng-click=update(lsValue)>update</button>
  <button ng-click=unbind()>unbind</button>
</div>

deriveKey

Return the derive key Returns String

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  localStorageService.set(property, oldValue);
  //Test Result 
  console.log(localStorageService.deriveKey(property)); // ls.property 
  //… 
});

length

Return localStorageService.length, ignore keys that not owned. Returns Number

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  var lsLength = localStorageService.length(); // e.g: 7 
  //… 
});

Cookie

Deal with browser’s cookies directly. ##cookie.isSupported Checks if cookies are enabled in the browser. Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  if(localStorageService.cookie.isSupported) {
    //… 
  }
  //… 
});

cookie.set

Directly adds a value to cookies.
Note: Typically used as a fallback if local storage is not supported.
Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function submit(key, val) {
   return localStorageService.cookie.set(key, val);
  }
  //… 
});

Cookie Expiry Pass a third argument to specify number of days to expiry

    localStorageService.cookie.set(key,val,10)

sets a cookie that expires in 10 days. Secure Cookie Pass a fourth argument to set the cookie as secure W3C

    localStorageService.cookie.set(key,val,null,false)

sets a cookie that is secure. ###cookie.get Directly get a value from a cookie.
Returns: value from local storage

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function getItem(key) {
   return localStorageService.cookie.get(key);
  }
  //… 
});

cookie.remove

Remove directly value from a cookie.
Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function removeItem(key) {
   return localStorageService.cookie.remove(key);
  }
  //… 
});

cookie.clearAll

Remove all data for this app from cookie.
Returns: Boolean

myApp.controller(MainCtrl, function($scope, localStorageService) {
  //… 
  function clearAll() {
   return localStorageService.cookie.clearAll();
  }
});

Check out the full demo at http://gregpike.net/demos/angular-local-storage/demo.html

Development:

  • Don’t forget about tests.
  • If you’re planning to add some feature please create an issue before.

Clone the project:

$ git clone https://github.com/<your-repo>/angular-local-storage.git
$ npm install
$ bower install

Run the tests:

$ grunt test

Deploy:
Run the build task, update version before(bower,package)

$ npm version patch|minor|major
$ grunt dist
$ git commit [message]
$ git push origin master –tags

Diagramas de clases. Relaciones

Posted on Actualizado enn

UML, Agregacion y Composicion
He visto demasiadas discusiones vicentinas sobre las diferencias entre la agregación y la composición en los diagramas de clases de UML. Es más, cada cierto tiempo, alguien surge y me pregunta cual es la diferencia en el código y me explica sus propias teorías sobre esta cuestión, este debate es casi tan extenso como el de los extends y los includes de los casos de uso.

UML: Diferencia Agregacion, Asociacion y Composición. Codigo
Empecemos, debemos recordar siempre que una de las mayores criticas que recibe UML, es que ha logrado salvar muchas ambigüedades… Pero no todas!!! aun quedan conceptos que se prestan a dobles interpretaciones, no se si este será uno de ellos, pero por lo discutido parece que sí. Por otro lado, podemos hacer un poco de historia, recordando que primero existió la asociación, después surge la Agregación para representar un relacion estructural contenedor/contenido y luego como una “extensión” de esta ultima nace la Composición.
Para explicar mi punto de vista voy a echar mano, al diagrama de clases que ya he utilizado en otro post y después voy a poner el código de la clase Persona, que es la que se lleva todala carga de la discusiónEl código hace referencia, solo a este modelo, y es bien detallado, hasta con cosas innecesarias, o meramente teóricas, pero lo que busco es fijar una posición, concreta y definitiva, en el tema de las relaciones.

Algo importante a tener en cuenta, es que un objeto existe (digamos que esta vivo, pero esto no es técnicamente correcto por que no es un hilo) mientras existe una variable de referencia que “apunte” (tampoco correcto, por que java no tiene punteros, je) a dicho objeto en memoria. Es decir que se convertirá en elegible para ser borrado por el garbage collector, cuando no exista una variable que “apunte” a dicho objeto.


import java.util.LinkedList;
import java.util.List;

public class Persona {
private String nombre;
private String apellido;
private List perfiles = new LinkedList();
private List lugaresFrecuentes = new LinkedList();

//Setters and Getters
public String getNombre() {return nombre;}
public void setNombre(String nombre) {this.nombre = nombre;}
public String getApellido() {return apellido;}
public void setApellido(String apellido) {this.apellido = apellido;}

// OJO no confundir estos son solo setters y getters de las propiedades
public List getPerfiles() {return perfiles;}
public void setPerfiles(List perfiles) {this.perfiles = perfiles;}
public List getLugaresFrecuentes() {return lugaresFrecuentes;}
public void setLugaresFrecuentes(List lugaresFrecuentes) {this.lugaresFrecuentes = lugaresFrecuentes;}

La clase comienza normalmente con la declaración de las variables de instancia. Donde perfiles y lugaresFrecuentes

son dos colecciones, pero tranquilamente podrían ser arrays. que se transformarán en contenedores de elementos. Al ser propiedades tienen getters y setters (accessors y mutators), que nada tienen que ver con la agregación y la composición.
Ahora veamos cuales son los métodos que caracterizan a la relación de AGREGACIÓN


public void agregarLugarFrecuenta(Lugar lugar){
if(!lugaresFrecuentes.contains(lugar)){
lugaresFrecuentes.add(lugar);
}
}
public void removerLugarFrecuenta(Lugar lugar){
if(lugaresFrecuentes.contains(lugar)){
lugaresFrecuentes.remove(lugar);
}
}

La primera característica es que la clase contiene dos métodos uno que agrega elementos a la coleccion y otro que los elimina de ella. He acá algo importante… los objetos son pasados por parametro, no han sido instanciados dentro del método, es decir no hemos realizado el new del objeto. Ha “nacido” en cualquier otra parte y se lo hemos pasado por parámetro al método para ser agregado a la lista lugaresFrecuentes. En otras palabras, el objeto Persona podria morir, y el objeto ahun podría mantener una referencia activa en alguna otra parte de nuestro codigo por lo tanto sus ciclos de vida no estrían atados. No nace ni muere, dentro de la Persona.

¿Cual es la Diferencia con la COMPOSICIÓN?


public void agregarPerfil(){
Perfil p = new Perfil();
perfiles.add(p);
}
//sobrecarga
public void agregarPerfil(String nombre){
Perfil p = new Perfil(nombre);
perfiles.add(p);
}
public void removerPerfil(int index){

perfiles.remove(index); // aca lo quitamos de la lista

}
Bueno… la composición también tiene los métodos para agregar y borrar. Pero…

“el new del objeto se realiza dentro del método agregar”

la instanciación del objeto p se realiza dentro del método agregar y la referencia no se devuelve (es void o boolean), la variable de referencia local va a dejar de existir una vez que el método se termine de ejecutar, y el ciclo de vida de esa instancia en particular va a quedar atada a la lista, y por ende a la Persona. Una vez que el objeto Persona no se referencie más, (o sea muera, aunque técnicamente esto no es así) el objeto lista, quedará sin referencia, y por lo tantos sus elementos también. Además como el método no es estático, se deberá crear primero una instancia de Persona, para después poder agregar un Perfil. Empezando así a “atar” el ciclo de vida de un Perfil, al de una Persona.
En cuanto al método remover, no existe nada de extraordinario, simplemente quitamos un elemento de la lista.

Volviéndonos Paranoicos de la Teoría

Profundizando la paranoia y jugando con la teoría; para que atemos definitivamente los ciclos de vida, la variable lugaresFrecuentes no debería ser una propiedad, y la clase Perfil debería ser una Inner Class.
En el caso de la Inner class, hacemos esto para que se tenga que utilizar una instancia de la clase “Outer” para luego obtener una instancia de la clase Inner. Por ejemplo si en nuestro codigo, la clase Perfil, fuera una Inner class de la clase publica Persona (Se entiende no?, es decir que esta dentro del archivo Persona.java), para obtener una instancia de Perfil fuera de la clase persona tendríamos que hacer:
Persona persona = new Persona();
Persona.Perfil perfil = persona.new Perfil();

Y la clase Perfil existiría mientras exista la instancia de persona. (ciclos de vida atados)
En el caso de que la variable de instancia lugaresFrecuentes no debería tener getters y setters públicos, esto se debe a que ningún otra clase, con excepción de la clase Persona, debería tener la oportunidad de mantener una referencia viva a un objeto del contenedor. Y mucho menos obtener toda la Lista desde afuera! Un objeto Perfil, vive y muere con la Persona!
También de esto se pueden desprender otros delirios, como cuestiones de herencia y cosas así.
Pero de nuevo, y no me voy a cansar de decirlo… esto es un extremo!!, es solo para conversar entre amigos, o vanagloriarse con algún profesor, no tiene nada de practico, ni de real, salvo para casos específicos.

Paranoia de la Paranoia

¿Creían que ya habíamos terminado? aun se puede ser más paranoico!!!! mucho se ha discutido sobre estos temas, y mucho fue paranoia teórica. Lo siguiente, es algo que les llevará a sus amigos o profesor a decir, “…bueno pero eso ya es una locura”:
Sobreescribiremos el método finalize() de la Clase Persona, que es un método que todas las clases heredan de Object, y que se invoca justo antes de que un objeto sea borrado de la memoria por el garbage collector de java.

public void finalize(){
for(Perfil p : perfiles){
p = null;
}
}

En el desreferenciamos cada uno de los elementos de la lista un segundo antes de que el objeto de tipo Persona desaparezca de la memoria, una milésima de segundo, o algo asi!!! atando definitivamente el nacimiento y muerte, el ciclo de vida, de un elemento contenido con su contenedor.
Pero de nuevo!!! LA PARANOIA EN EL CÓDIGO NO ES BUENA!!! solo sirve en aquellas noches de borrachera entre programadores, en las cuales el boliche cerró y pinta quedarse en casa con amigos.

Yo rescataría de todo este biri-biri aquello de “el new se realiza dentro del método” y nada más!!!

UML, Asociacion y Agregacion
El post de la Agregación y Composición despertó otra pregunta más (Como siempre suele suceder, ya dije que el debate siempre dá para algo más). La duda viene por el lado de la diferencia entre asociación y Agregación en código. Se sostiene que es algo conceptual, que no se representa en código. Mi respuesta es que esto es 50% correcto. Ya que, como mencione antes (la etiqueta Diseño tiene mas sobre esto), la Asociación surgió primero, y la Agregación vendría a ser un tipo particular de Asociación. Ergo …Agregacion “IS-A” Asociación ¡punto para ud! . Primero veamos el diagrama UML

composicionAhora veamos el codigo de la clase Persona (Nota: imagínense los generics por que blogspot los toma como tags, así que no aparecen)

import java.util.List;

public class Persona {

private String nombre;
private String apellido;

private Foto foto;
private List lugaresFrecuentes;
private List comunicaciones;

public String getNombre() {return nombre;}
public void setNombre(String nombre) {this.nombre = nombre;}
public String getApellido() {return apellido;}
public void setApellido(String apellido) {this.apellido = apellido;}

//Asociacion Foto
public Foto getFoto() {return foto;}
public void setFoto(Foto foto) {this.foto = foto;}

//public List getLugaresFrecuentes() {return lugaresFrecuentes;}
//public void setLugaresFrecuentes(List lugaresFrecuentes) {this.lugaresFrecuentes = lugaresFrecuentes;}

//Agregacion
public void agregarLugar(Lugar lugar){
lugaresFrecuentes.add(lugar);
}
public boolean quitarLugar(Lugar lugar){
return lugaresFrecuentes.remove(lugar);
}
//Asociación
public void setComunicaciones(List comunicaciones) {
this.comunicaciones = comunicaciones;
}
public List getComunicaciones() {
return comunicaciones;
}

}

Las diferencias principales son que:
¡La Agregación son siempre colecciones, o arrays! O algo que sirva de contenedor para “agregar” más de un objeto, aunque agreguemos uno solo. (si no sería settear y no agregar, add)La Agregación cuenta con dos métodos: uno para “agregar” un solo objeto a la lista, y el otro para quitarlo de la misma.La agregación puede, como no, tener los metodos setter y getter, mientras que la Asociación siempre los tiene, que ponen y obtienen una variable de referencia del mismo tipo de la variable de instancia o de clase, en este caso List.

 

Habilitar ver filas y columnas en el bloc de notas de Windows

Posted on

Una de las mejores herramientas de Windows 7 y XP es y será siempre el Bloc de Notas  para la edición de texto plano.

En esta herramienta la barra de estado viene deshabilitada por defecto y no es fácil activarla, pues si vas a la opción “Ver” de la barra de menú, verás que no puedes hacer clic sin más, pero con unos simples pasos puedes tener esta opción accesible.

Actualmente, mi Bloc de Notas no tiene la barra de estado, se muestra de la siguiente forma:

dibujo

PAra habilitarlo debes abrir el registro de Windows de la siguiente forma:

  1. Clic en el menú Inicio
  2. En caso de que tengas Windows 7, escribe “regedit” y dale a la tecla Intro; en caso de que tengas Windows XP, escoge la opción “Ejecutar” y luego escribe “regedit”

Una vez que estamos en el registro del sistema, nos dirigimos a la siguiente clave:

HKEY_CURRENT_USER\Software\Microsoft\Notepad

Notepad - Regedit

En la opción “StatusBar”, pulsamos clic derecho y seleccionamos la opción Modificar, y le cambiamos el número a 1, con lo que activaremos automáticamente la barra de estado. El resultado se puede ver en la siguiente imagen:

Barra de estado en Notepad

Así,  la barra de estado estará activada de manera automática. Siendo el comportamiento y aspecto de mi Bloc de notas con la barra de estado habilitada.

Bloc de notas con barra de estado

Esto es muy útil  en muchas ocasiones, por ejemplo cuando desarrollo algún código en PHP a mano, no podía ver la línea donde estaba el error; con esto ya podré hacerlo sin problema.

Un truco fácil de aplicar que te da mucha potencialidad.

(Fuente: Aquí)