Archivo para enero, 2011

Desde su blog personal, Ed Spencer (Software Architect at Sencha Inc) comparte el video del a presentación de ExtJS en la reciente SenchaCon 2010. En el video se habla de los objetivos que tuvieron a la hora de desarrollar, velocidad, flexibilidad y un api amigable.

Entre otras cosas se destaca la política de calidad, haciendo hincapié en los (numerosos) tests unitarios que realizan en cada build para asegurar la calidad y estabilidad del framework, QA visual, mejores defaults, etc.

Definitivamente, Sencha toma un gran riesgo en el golpe de timón que implica realizar todos estos cambios. Pero considerando la calidad del producto que proponen, es un costo sumamente accesible.

Actualización

Para los interesados, Sencha puso a disposición pública los videos y presentaciones de la SenchaCon, siendo ExtJS4 el principal tema de charla.

Generalmente, en el período de vida de un desarrollo es completamente normal que los requerimientos iniciales se vean modificados, así como tambien que la corrección de procedimientos o errores derive en un refactor de variada importancia.

Esta garantía de retrabajo supone estándares de desarrollo abierto a modificaciones (designing for change, open/closed principle). Dada su naturaleza dinámica, JavaScript carece de algunas herramientas básicas que poseen otros lenguajes (tipado, overloading, interfaces, etc).
En esta entrada se tratará de exponer puntualmente el uso de objetos como argumentos únicos, tanto para funciones y métodos como para los constructores.

Problematica

Como se mencionó previamente, las formas y objetivos de los desarrollos suelen mutar durante el período de vida del mismo. Las funciones y métodos puntualmente, suelen representar un punto crítico en este aspecto ya que su firma* dista de ser algo estatico.

*Se denomina firma (o signature en inglés) de un método a la identificación que puede realizarse en base a su nombre, número y calidad de argumentos.

Por ejemplo, supongamos que inicialmente el proyecto requiere de un método que realize un POST sobre una url dada. La implementación sería algo del tipo:

MyApp.request = function(url) {
	// .. implementacion
}

MyApp.request('/user/keep_alive');

Conforme mutan los requerimientos se le van agregando argumentos:

MyApp.request = function(url, method) {}
MyApp.request = function(url, method, params) {}
MyApp.request = function(url, method, params, headers) {}
MyApp.request = function(url, method, params, headers, successHandler) {}
MyApp.request = function(url, method, params, headers,
					successHandler, errorHandler) {}
MyApp.request = function(url, method, params, headers,
					successHandler, errorHandler, timeout) {}

Llegando al punto de que su uso se vuelve engorroso;

// Suponiendo que solo nos interesa completar la url y el timeout
MyApp.request('/user/status', null, null, null, null, null, 30);

Soluciones

Si usasemos algún lenguaje con overload, la implementación sería bastante sencilla (y digamos que engorrosa en casos donde varía demasiado la firma), en este caso Java:

public class Person {
	public Person() {
		// implementacion
	}

	public Person(String name) {
		// implementacion
	}

	public Person(String name, String lastname) {
		// implementacion
	}

	public Person(String name, String lastname, Integer age) {
		// implementacion
	}

	public Person(String name, Integer age) {
		// implementacion
	}

	// etc...
}

Volviendo a JavaScript, la solución, obvia para cualquier persona que haya utilizado alguna de las librerías más conocidas, consiste en agrupar todos o algunos de los argumentos en un objeto. Luego, para cada una de las propiedades, asignamos un valor por defecto.
Para asignar los valores por defecto, en primera instancia vamos a hacer uso de una pequeña utilidad

/**
 * Copia las propiedades del object source al object
 * target. No reemplaza valores existentes.
 *
 * @param {Object} target
 * @param {Object} source
 */
MyApp.extend = function(target, source) {
	// la lista es filtrada, para evitar copiar propiedades
	// inyectadas en los prototipos
	for(var prop in source) if(source.hasOwnProperty(prop)) {
		target[prop] = target[prop] || source[prop];
	}
}

Ya con nuestra navaja suiza, podemos definir facilmente nuestro kit de valores por defecto.

MyApp.request = function(config) {
	// definimos valores por defecto
	config = config || {};
	MyApp.extend(config, {
		url: 'user/status',
		method: 'POST',
		params: {},
		headers: {},
		successHandler: function(responseText) {},
		errorHandler: function(e) {},
		timeout: 30
	});

	// .. implementacion
}

// Ejecutamos el método con la url y el timeout:
MyApp.request({
	url: '/user/get_profile/',
	timeout: 0
});

Otra forma de asignar valores por defecto a una funcion o metodo (no constructor) consiste en utilizar un decorator, que complete con los valores dados toda configuración que reciba el método.

/**
 * Decora una funcion que espera un objeto de configuracion con valores
 * por defecto.
 *
 * @param {Function} fn
 * @param {Object} defaultConfig
 * @param {Object} context
 * @return {Function} La funcion con valores por defecto
 */
function defaultConfigDecorator(fn, defaultConfig, context) {
	defaultConfig = defaultConfig || {};
	return function(config) {
		config = config || {};	

		// Contexto o scope en el cual se aplica la funcion
		context = context || this;

		// Filtramos la lista
		for(var p in defaultConfig) if(defaultConfig.hasOwnProperty(p)) {
			config[p] = typeof(config[p]) != 'undefined' ?
							config[p] : defaultConfig[p];
		}

		// Devolvemos la funcion decorada
		return fn.call(context, config);
	}
}

/**
 * Crea un slideshow con la configuracion dada
 *
 * @param {Object} config
 */
function slideShow(config) {
	// implementacion
	console.log(config);
}

slideShow(); // undefined

/**
 * Crea un slideshow con la configuracion dada con
 * sus respectivos valores por defecto
 */
var defaultSlideShow = defaultConfigDecorator(slideShow, {
	target: document.getElementsByTagName('body')[0],
	title: 'Slideshow',
	fade: true,
	interval: 5
});

// Ejecutamos sin configuracion
defaultSlideShow(); // Object { target=, title="Slideshow", more...}
// Con configuracion parcial
defaultSlideShow({ title: 'MySlideShow' });
// Object { title="MySlideShow", target=, more...}

SenchaSencha no duerme, a la espera de la beta de ExtJS4 siguen exponiendo funcionalidad parcial del mismo a través de su blog oficial. Esta vez, Ed Spencer nos revela los cambios en el manejo de datos, con grandes novedades en cuanto al mappeado de entidades en el cliente.

Para empezar, luego de todo el refactor general que recibió, el data package se compone de 43 clases (sí, cuarenta y tres).
En principio se supone (según posteos en el foro oficial) que la lógica de los data stores no debería diferir en demasía con lo que ofrece Sencha Touch en la misma área.

Dentro de estas (por ahora) 43 clases, se destacan tres, Model, Store y Proxy. Como puede observarse en el gráfico siguiente, estas tres clases estan auxiliadas por clases específicas cuyo rol está acotado a un único propósito.

ExtJS Data Package

De los cambios, es sin duda el de mayor interés la introducción de la clase Model en el manejo de datos, donde ahora se va a poder manejar las entidades como tales sin necesidad de desparramar su definición (campos, url, validaciones, etc).

/*
 * Registarmos el modelo User
 */
Ext.regModel('User', {
    fields: ['id', 'name', 'age'],
    proxy: {
        type: 'rest',
        url : '/users',
        reader: {
            type: 'json',
            root: 'users'
        }
    }
});

/*
 * Creamos un store directamente
 * contra el modelo
 * Notese que se puede referenciar
 * el modelo registrado por su nombre
 */
new Ext.data.Store({
    model: 'User'
});

Otra ventaja que presenta el uso de modelos, es que permite manejar los registros de una manera más atómica e independiente, donde cada registro conoce como debe almacenarse, lo cual simplica MUCHO la inserción de registros desde el cliente.
La lógica y datos requeridos corren a cuenta del proxy asociado, abstrayendo al modelo de dicha responsabilidad. Aparentemente, además de los datasources tradicionales (en memoria y remoto o Ajax) se le suma el local storage definido en la especificación de HTML5.

// Se obtiene una referencia al modelo
var User = Ext.getModel('User');

// Se instancia un registro..
var ed = new User({
    name: 'Ed Spencer',
    age : 25
});

// Y se persiste...
ed.save({
    success: function(ed) {
        console.log("Saved Ed! His ID is "+ ed.getId());
    }
});

// De igual manera puede obtenerse un registro por id
User.load(123, {
    success: function(user) {
        console.log("Loaded user 123: " + user.get('name'));
    }
});

Otro punto fundamental en la implementación de la clase Model, es la capacidad de relacionar los modelos entre sí, permitendo manejar las coleccionas al mejor estilo MongoDB / CouchDB.
Estas relaciones, permiten manejar los request de datos recursivamente según lo definido en los modelos que contenga.

// Registramos un modelo
Ext.regModel('User', {
    fields: ['id', 'name'],
    hasMany: 'Posts'
});

Ext.regModel('Post', {
    fields: ['id', 'user_id', 'title', 'body'],
    belongsTo: 'User',
    hasMany: 'Comments'
});

Ext.regModel('Comment', {
    fields: ['id', 'post_id', 'name', 'message'],
    belongsTo: 'Post'
});

// Cargamos el usuario e iteramos las colecciones
User.load(123, {
    success: function(user) {
        console.log("User: " + user.get('name'));

        user.posts().each(function(post) {
            console.log("Comments for post: " + post.get('title'));

            post.comments().each(function(comment) {
                console.log(comment.get('message'));
            });
        });
    }
});

Sin duda alguna, el Data Package promete mucho, será cuestión de tiempo ver si la implementación de widgets (Grids y Forms) logra exprimir todo el potencial que presenta.

Leer más:

Hoy Tomas Fusch anunció en su blog que Zepto alcanzó una nueva release (0.4), con numerosos aportes de la comunidad (al momento de escribir esto el repositorio acusa 89 forks y 1077 watchers).
El peso final de esta versión (minimizada y gzippeada por supuesto) es de 3.7kb, con un limite autoimpuesto de 5kb, por lo cual todavía queda hilo en el carretel hasta llegar a la versión 1.0.

Changelog

  • JSONP Ajax requests
  • Optimized and cleaned up .each & .find
  • .append, .prepend, .before, .after now accept Zepto objects
  • Function arguments to .html and .attr
  • .val (rudimentary support)
  • Blackberry detection
  • .size, .parent, .parent, .eq, .removeAttr

Además los cambios realizados son visibles en el repositorio en GitHub.

Leer más:

SenchaSegún una entrada en el blog oficial de Sencha, ExtJS4 está a la vuelta de la esquina con grandes novedades, siendo la que se anuncia en dicha entrada el nuevo sistema de clases.

Historicamente, ExtJS utilizó un esquema de clases basado en herencia simple (un objeto tiene un único padre) que se ajusta más a lenguajes oop típicos (Java y C# por nombrar algunos) que a JavaScript, donde la flexibilidad del lenguaje permite implementar soluciones mucho más potentes y sencillas.

Con ExtJS4 parece haber un gran giro de timón al respecto, en el que complementan la herencia simple con un sistema de mixins en el que las “clases” bases toman características de múltiples fuentes.

/*
 * Se define una composición de objetos, heredando directamente de
 * Sample.Person e implementando distintos mixins
 */
Ext.define('Sample.Musician', {
    extend: 'Sample.Person',

    mixins: {
        guitar: 'Sample.ability.CanPlayGuitar',
        compose: 'Sample.ability.CanComposeSongs',
        sing: 'Sample.ability.CanSing'
    }
});

Este tipo de implementaciones permiten al desarrollador generar sus propios componentes con la libertad de definirlo de acuerdo a sus necesidades y no a la aproximación de un objeto (como suele darse con la herencia simple).

Mixins en accion

Mixins en accion

Aprovechando esta nueva característica, se incluye un sistema de dependencias que cubre un gran vacío existente actualmente en ExtJS: la modularidad.
Hoy por hoy, ExtJS se sirve en un gran archivo concatenado al cual se le suman unas pocas dependencias externas que suelen ser plugins o archivos muy específicos (caso de los archivos locale). Con el nuevo sistema, se realizará la carga bajo demanda para los modulos requeridos (incluso recursivamente para los que requiera el modulo en cuestión).

// requerimos el archivo
Ext.require('Ext.Window', function() {
    // y asincronicamente realizamos la tarea
    new Ext.Window({
        title : 'Loaded Dynamically!',
        width : 200,
        height: 100
    }).show();
});

A estos cambios se le suma la no menos importante reducción significativa del markup de los widgets (que hoy por hoy puede alcanzar niveles de anidamiento alarmantes) lo cual impactará directamente en el rendimiento, permitiendo tener grillas de alta performance, uno de los puntos más flacos de ExtJS en relación a otras librerías (SlickGrid por ejemplo).

Leer más:

  • Buscar

  • Ultimos Twits

  • Por fecha

    • 2012 (11)
    • 2011 (63)
    • 2010 (13)
  • Tags

  • Documentacion Javascript

    JavaScript Reference, JavaScript Guide, JavaScript API, JS API, JS Guide, JS Reference, Learn JS, JS Documentation