Desarrollo multiplataforma

17-09-2009 | Categoría: Desarrollo 9 comentarios »

Los clientes que tenemos en Arcadina, fotógrafos profesionales en su mayoría, trabajan con dos plataformas Windows y Mac, aunque últimamente, sobre todo desde que Apple se pasó a los procesadores Intel y durante el último año más todavía (no sabemos porqué), Mac parece que esté ganando la “batalla”.

Nuestro software PhotoGestión está desarrollado con Delphi y funciona sobre Windows, yprácticamente a diario se produce una conversación así (más o menos):

- ¿Funciona en Mac? – No, todavía no funciona en Mac – ¿Para cuando estará en Mac? – Pues no sabemos decirle, no tenemos definida una fecha.

Llevo bastante tiempo, a ratos no a full-time, buscando una solución que permita a una empresa pequeña desarrollar para Windows y para Mac, he probado varios entornos, pero ninguno me ha terminado de gustar (debe ser que los comparo con mi estimado Delphi). He estado evaluando Mono+C#, RealBasic, FreePascal+Lazarus, Java, Mono+Delphi Prism y Velneo V7

Mono+C# Es un lenguaje avanzado, con una gran comunidad, con miles de componentes y librerías, es un lenguaje estándar ISO. Mono está apoyado por grandes empresas, es OpenSource y está demostrando

RealBasic casi lo consigue, pero no me decidí por la falta de comunidad, porque no será sencillo encontrar a desarrolladores con experiencia, y porque no encontré algunos componentes que necesitamos si o si.

FreePascal+Lazarus, no llega al nivel, tuve muchos problemas al intentar hacer algo, posiblemente por falta de experiencia, no lo niego. Era el primer candidato, para aprovechar parte del código que tenemos. Espero que alguna empresa (¿Embarcadero?) lo respalde y puedan dedicarle más recursos.

Java, el entorno multiplataforma por excelencia. Muchos puntos a favor: millones de programadores con experiencia, gran comunidad, miles de librerías y componentes, rendimiento más que suficiente para el tipo de aplicación que realizamos, abrumado por la cantidad de documentación y código que hay. Para mí el que más posibilidades tiene.

Delphi Prism he estado leyendo sobre él. Es el Pascal para .NET y Mono. Pero me parece que todavía está verde, hablo con la probabilidad de equivocarme porque no lo he probado.

Velneo V7, con éste entorno me pasa lo mismo que con Delphi Prism, sólo he leído acerca de él. No encontré demasiadas referencias, ni componentes y supongo que no será sencillo encontrar personal con experiencia.

Lo que he sacado en claro durante este tiempo es que:


  1. Es un tema complejo, que igual se escapa a nuestras posibilidades, dados nuestros recursos.

  2. El tema de la apariencia/interfaz: o no se parece a ningún entorno, o si queremos que tenga el look&feel de cada uno se ha de separar por capas y la de presentación hacerla ex-profeso para cada GUI con los widgets que correspondan, con las normas de cada entorno, etc. cosa que complica más el asunto.

  3. Las pruebas y verificaciones se multiplican por mucho.

  4. Temas de instaladores, bases de datos, sistemas de informes, mecanismos de impresión (necesitamos imprimir en una impresora de tickets), etc. Es algo que se multiplica por dos.

  5. Que posiblemente el futuro vaya por SaaS (alquiler de aplicaciones que se encuentran alojadas en servidores ajenos a la empresa que los utiliza), y que por lo tanto este perdiendo el tiempo con ésto. Claro que desarrollar una aplicación vía web tampoco está exenta de retos.


En fin, un mundo apasionante éste del desarrollo de aplicaciones informáticas ;)

mod_rewrite de Apache: una profunda explicación

15-09-2009 | Categoría: Desarrollo No hay comentarios »

Los que hemos tenido que trastear alguna vez con el mod_rewrite del servidor web Apache sabemos que puede llegar a ser un quebradero de cabeza si se intenta hacer algo complejo. No me considero un experto del tema, apenas he realizado unos pocos, por eso quiero compartir un artículo que he encontrado que creo puede ser de mucha utilidad: http://bit.ly/1f7Oas ¡Que lo disfrutéis!

Amigos

23-08-2009 | Categoría: Personal 5 comentarios »

En esta semana que termina he cumplido 40 años, he recibido felicitaciones de mucha gente, agradezco todas y cada una.

El viernes fuí de cena con los amigos, mis hermanas, cuñadas, cuñados, etc. un rato de diversión y risas, durante el cual me regalaron varias cosas, un álbum de fotos de “mi vida”, una camiseta con un diseño de mi hermana Elisa (http://www.elisatalens.blogspot.com/), este es el diseño:

Fa vint anys que tinc vint anys

También me regalaron un colgante, con un nº 1. Otro regalo fue un pulsómetro/cronómetro, algo que quería tener para poder controlar mejor cuando salgo a correr y las clases de spinning. Pero lo que realmente me emocionó fue algo que llevaba tiempo queriendo disfrutar, algo que puede que parezca una tontería, pero que me hacía mucha ilusión: practicar rafting, pero no yo solo, sino con todos mis amigos, mis hermanas… me puse a llorar, sentimental que es uno.

Ayer sábado nos pusimos en ruta a las 7:30 de la mañana hacia Venta del Moro, donde nos unimos a otros grupos para acercarnos al rio Cabriel, a Tamayo. No voy a detallar todo, solo decir que fue muy divertido y refrescante.

Gracias a todos los amigos que me acompañaron, y a los que no pudieron también, gracias por hacer un día inolvidable, con amigos como vosotros poco más se puede pedir.

Gracias a Jessica de AvenSport, nuestra monitora del rafting, por hacer tan ameno el trayecto y por la canción.

Pero sobre todo estaré eternamente agradecido a mi mujer Mar, por organizarlo todo, por cuidar cada detalle, por ser mi media naranja.

Gràcies amics!!!

Utilizar AmazonS3 para descargas

20-08-2009 | Categoría: Internet 2 comentarios »

Un cliente nos pidió que pusiéramos en su web un apartado de descargas de ficheros, nada fuera de lo común si no hubiera sido por el tamaño de los ficheros a descargar: uno de 700MB y otro de 1,5GB

Lo hubiéramos podido poner en su web, por espacio de disco no era, pero para evitar posibles saturaciones de transferencia del servidor y que afectara a otras webs alojadas en el mismo optamos por otra solución: Amazon S3

datacenterPara quien no lo sepa: Amazon S3 es un servicio de almacenamiento de archivos de la empresa Amazon que tiene un coste normal, pero la ventaja que cobra según el espacio consumido y la transferencia (además de otros pequeños conceptos), teniendo la gran ventaja su escalabilidad, es decir, podemos almacenar todo lo que queramos (y podamos pagar) sin preocuparnos de ir añadiendo discos ni contratando más espacio.

Volviendo al problema inicial, optamos también por proporcionar una dirección que no implicara el nombre de amazon S3 (s3.amazonaws.com) para poder cambiar de proveedor o realizar otros cambios sin tener que ir revisando todos los enlaces. Para ello creamos en las entradas de nuestro dominio arcadina.net un CNAME (un alias) llamado “cdn” que apuntara a cdn.arcadina.net.s3.amazonaws.com. De esa forma un fichero que tenga como dirección http://cdn.arcadina.net/descargas/fichero.zip realmente apuntará a http://cdn.arcadina.net.s3.amazonaws.com/descargas/fichero.zip

Pero para que ésto funcione hemos de crear en nuestra cuenta de Amazon S3 un bucket (un contenedor) que se llame exactamente cdn.arcadina.net. Para los puristas, se que S3 no es un CDN como tal, que para eso Amazon tiene otro servicio: CloudFront, pero no se me ocurrió otro nombre ;)

Para subir los ficheros a S3 hemos utilizado un script en PHP que utiliza una clase para gestionar la conexión con AmazonS3. La clase se puede encontrar aqui: http://undesigned.org.za/2007/10/22/amazon-s3-php-class

De esta forma cuando alguien se descargue el fichero de 1,5GB el servidor donde se encuentra la web no se verá afectado de ninguna forma.

Tratamiento del AJAX en un solo fichero

18-08-2009 | Categoría: Desarrollo No hay comentarios »

Últimamente estoy desarrollando sobre todo con PHP+JavaScript (jQuery). En un proyecto interno de Arcadina, que hace un uso intensivo del AJAX, decidí probar un planteamiento diferente del utilizado hasta ahora.

Normalmente las llamadas a la parte AJAX del servidor se hacen a diferentes scripts PHP en función de lo que deseamos obtener (query) o guardar (submit). Por ejemplo, si estamos tratando una lista de clientes podemos llamar a get_cliente.php para solicitar los datos de un cliente pasando su identificador, podemos llamar a delete_cliente.php para eliminarlo o a save_cliente.php para guardar cambios en la ficha, etc. (tengo la manía de utilizar spanglish en el nombre de funciones, procedimientos, etc.). Ejemplo de llamada según esté método:

?Descargar form1.php
1
2
3
4
5
<form name="ejemplo1" action="/api/save_cliente.php" method="post">
<input type="hidden" name="id" value="<?= $cliente->id ?>">
Nombre: <input type="text" name="nombre" value="<?= $cliente->nombre ?>"><br/>
EMail: <input type="text" name="email" value="<?= $cliente->email ?>"><br/>
</form>

Según el proyecto va avanzando nos podemos encontrar con decenas de scripts: get_<em>loquesea</em>, delete_<em>loquesea</em>.... En cada uno de ellos añadir el código de validación, los includes, etc. Se me ocurrió centralizarlo en dos ficheros ajaxsubmit.php y ajaxquery.php, y utilizar un parametro para indicar la acción a realizar. Ejemplo de un formulario utilizando este sistema:

?Descargar form2.php
1
2
3
4
5
6
<form name="ejemplo2" action="/api/ajaxsubmit.php" method="post">
<input type="hidden" name="ajaxsubmit" value="save_cliente">
<input type="hidden" name="id" value="<?= $cliente->id ?>">
Nombre: <input type="text" name="nombre" value="<?= $cliente->nombre ?>"><br/>
EMail: <input type="text" name="email" value="<?= $cliente->email ?>"><br/>
</form>

Como se ve tampoco es que varíe mucho en la parte que se envía al navegador, se añade un nuevo campo oculto “ajaxsubmit” que indica la acción que queremos realizar. Veamos el ajaxsubmit.php:

?Descargar ajaxsubmit.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?php
 
// Incluimos código mínimo de autenticación y librerías
include('includes.inc.php');
 
// Si no está logeado el usuario, devolvemos error
if (!isset($usuario_login) || ($usuario_login == '')) {
 
	$respuesta['resultado'] = 'nok';
	$respuesta['mensaje'] = 'Error: primero debe identificarse';
 
} else {
 
	$respuesta['resultado'] = 'nok';
	$respuesta['mensaje'] = 'Error no identificado';
 
	$ajaxsubmit = $_POST['ajaxsubmit'];
 
	// "AS_" viene de AjaxSubmit ... ;)	
	$funcion = 'AS_' . $ajaxsubmit;
	// Comprobamos que tenemos una función con ese nombre
	if (function_exists($funcion)) {
		$funcion(); // Llamamos a la función AS_ que indique el ajaxsubmit
	} else {
		$respuesta['resultado'] = 'nok';
		if ($ajaxsubmit == '') {
			$respuesta['mensaje'] = 'No se ha indicado "ajaxsubmit"';
		} else {
			$respuesta['mensaje'] = 'AjaxSubmit: orden "' . $ajaxsubmit . '" no reconocida.';
		}		
	}
 
}
 
// Si el resultado es NOK registramos en el log de errores
if ($respuesta['resultado'] == 'nok') {
	error_log('*** AJAXSUBMIT - error - ' . $respuesta['mensaje']);
}
 
// ---------------------------------------------------------------------------------------
// Devolvemos respuesta en formato JSON
$json = new Services_JSON;
echo $json->encode($respuesta);
 
exit;
 
// =======================================================================================
// Procesos de guardar, eliminar, actualizar, etc...
// =======================================================================================
 
function AS_save_cfg() {
 
	global $respuesta;
	$cfg = array();
 
	foreach($_POST as $clave => $valor) {
		// Comprobar si $clave empieza por 'cfg_'
		if (ereg('^cfg_', $clave)) {
			$k = ereg_replace("^cfg_", "", $clave);
			$cfg[$k] = $valor;
		}
	}
	$errormsg = SaveConfig($cfg);
	if ($errormsg == 'ok') {
		$respuesta['resultado'] = 'ok';
		$respuesta['mensaje'] = 'Configuración guardada';
	} else {
		$respuesta['resultado'] = 'nok';
		$respuesta['mensaje'] = $errormsg;
	}
 
}
 
function AS_save_cliente() {
 
	global $respuesta;
 
	// Código simplificado para el ejemplo: se debería
	// añadir validación y protección contra SQL injection
	$cliente = new Cliente($_POST['id']);
	$cliente->nombre = $_POST['nombre'];
	$cliente->email = $_POST['email'];
 
	$errormsg = $cliente->Save();
	if ($errormsg == 'ok') {
		$respuesta['resultado'] = 'ok';
		$respuesta['mensaje'] = 'Cliente guardado';
	} else {
		$respuesta['resultado'] = 'nok';
		$respuesta['mensaje'] = $errormsg;
	}
}
 
?>

La “gracia” del código está en ésta parte:

19
20
21
22
23
24
25
	$ajaxsubmit = $_POST['ajaxsubmit'];
 
	// "AS_" viene de AjaxSubmit ... ;)	
	$funcion = 'AS_' . $ajaxsubmit;
	// Comprobamos que tenemos una función con ese nombre
	if (function_exists($funcion)) {
		$funcion(); // Llamamos a la función AS_ que indique el ajaxsubmit

Lo que se envia en la variable “ajaxsubmit” es el nombre de la función a llamar, pero la precedemos de “AS_” para evitar conflictos con otros nombres de funciones. Entonces gracias a la magia de PHP, porque es un lenguaje de script, aprovechamos la variable $funcion para realizar una llamada a una funcion: $funcion()

¿Qué ventajas tiene este sistema? Tenerlo centralizado, de esa forma, añadir una capa de autenticación o variar el formato de salida de JSON a XML, por ejemplo, es mucho más sencillo. Evidentemente si alguna función es compleja se puede separar en un fichero aparte y hacer el correspondiente include, que además lo recomiendo.

Para facilitar la labor de serializar los inputs de un formulario y enviarlo por ajax estoy utilizando el plugin ajaxForm para jQuery.