miércoles, 28 de marzo de 2012

Symfony 2: múltiples formularios en una página

Al momento de usar dos formularios no relacionados en la misma pagina, surgieron mas complicaciones de las que me esperaba. Aunque cada formulario estuviera definido en su propio controlador y funcionaran correctamente cuando estaban aislados, al mostrarlos en la misma pagina obtuve dos errores:
  • This form should not contain extra fields
  • The CSRF token is invalid
La manera de solucionarlo fue asignarle un nombre a cada formulario para comprobar que la solicitud esta asociada a dicho formulario. Para poder darle nombre a un formulario hay que acceder al servicio brindado por FormFactory usando el metodo createNamedBuilder. Un ejemplo con la estructura básica seria el siguiente:


Un par de aclaraciones, Acme es una entidad, AcmeType es un formulario definido en dicha clase.

Como se puede ver, el nombre del formulario es acme_form, por el cual se puede identificar cual fue el formulario que genero la petición POST.  

viernes, 23 de marzo de 2012

Symfony 2: Aislamiento de pruebas

La base de datos es parte de nuestra aplicación por lo que también debe formar parte de las pruebas. Ya que sino las pruebas estarán incompletas y dejaran un agujero en la calidad de nuestro software.

Las pruebas a una base de datos deben hacerse de manera aislada. ¿Que pasaría si una prueba corrompe la base de datos? Nuestro entorno de desarrollo se vería afectado y las siguientes ejecuciones de otras pruebas podrían fallar. Por lo que habría que solucionar el problema restaurando la base de datos. Seguro que nadie quiere molestarse en perder tiempo volviendo a un estado fiable de la base de datos.
Un par de cosas a tener en mente a la hora de realizar las pruebas, que sean cortas y concisas; y que la base de datos no tenga gran cantidad de datos (salvo que sea necesario) para poder ejecutar las pruebas rápidamente. 

Para lograr iniciar las pruebas siempre en el mismo estado y por ende que los cambios no se reflejen en la base de datos, tenemos que usar Doctrine Fixtures. Los fixtures permiten definir los datos que necesitamos  para una ejecución correcta de las pruebas. Estos deben ir definidos en la ruta AcmeBundle/DataFixtures/ORM, y su definición es bastante sencilla.



Y por ultimo hice un pequeño shell script con las ordenas para borrar el esquema actual, crear uno nuevo y cargar los datos.



Con eso ya tenemos listo nuestro entorno de pruebas.

jueves, 15 de marzo de 2012

Autentificación con Symfony2 y PHPUnit

Al momento de hacer pruebas es bastante mas comodo hacer la autentificación mediante HTTP basic que manejando los formularios con el crawler de Symfony2 (que por cierto me da bastantes problemas). Para poder iniciar sesion con los datos de un usuario en las pruebas, tenemos que agregar al fichero config_test.yml lo siguiente:

 security:  
   firewalls:  
     main:  
       pattern:  /.*  
       http_basic:  
         realm: "Secured Area"  
         provider: fos_userbundle  
       logout:   true  
       security:  true  
       stateless: true  
       anonymous: true  

En mi caso uso FOSUB como proveedor de usuarios, si se usa otro proveedor obviamente habria que cambiarlo.

Y finalmente en las las pruebas hay que crear el cliente como se indica en la documentación de Symfony.

 $client = static::createClient(array(), array(  
   'PHP_AUTH_USER' => 'username',  
   'PHP_AUTH_PW'  => 'pa$$word',  
 ));  

Algo bastante sencillo para los dolores de cabeza que me dio.

miércoles, 14 de marzo de 2012

Latex: instalar moderncv

Los paquetes de Latex que vienen empaquetados en Ubuntu son bastante viejos, pero por un tema de pereza y espacio (unos 2GB, para instalar manualmente la ultima versión), sigo usando lo que viene por defecto con el sistema ya que no tengo que hacer nada muy avanzado ni complejo. Lo uso para escribir documentación y para hacer mi CV.

El paquete que uso para mi CV es moderncv, hay que instalarlo manualmente y siempre me olvido como hacerlo, así que ahí va el mini tutorial:

Descargamos el zip desde la pagina oficial y desde la carpeta se descomprimió ejecutamos lo siguiente:
 sudo rm -rf /usr/share/texmf-texlive/tex/latex/moderncv/  
 sudo mv moderncv/ /usr/share/texmf-texlive/tex/latex/  
 sudo texhash  

La primer linea es para eliminar el paquete obsoleto de Ubuntu, la segunda para instalar el paquete nuevo, y la ultima para actualizar las referencias internas de Latex. Y listo, nada mas.

lunes, 12 de marzo de 2012

Android + Symfony2: Autentificación

Hoy en día una tarea bastante común es validar las credenciales de un usuario remotamente. En mi caso necesito que una aplicación ejecutándose en Android se valide en el sistema principal que esta construido con Symfony2 y con FOSUserBundle como proveedor de usuarios.

Ya que FOSUB se encarga de la validación en el sitio web, lo ideal y mas lógico seria que también se encarga de la validación remota, evitando duplicar trabajo y cometer errores. En esencia es el mismo escenario que cuando el usuario quiere iniciar sesión desde un navegador tradicional. Pero con algunas diferencias, así que manos a la obra.

Symfony2
En nuestro proyecto de Symfony tenemos que definir un firewall para hacer accesible la autentificacion via HTTP:

security.yml
 api:  
  pattern: ^/api/.*  
  stateless: true  
  security: true  
  http_basic:  
    realm: "API"  

access_control:
  - { path: /api/, role: ROLE_USER }

Con estas lineas presentes en el firewall, cada vez que se quiera acceder una ruta del siguiente tipo <nuestro-domino>/api/ se generara una petición para iniciar sesión mediante HTTP.


routing.yml
 api_login:  
   pattern: /api/login  
   defaults: { _controller: AcmeUserBundle:Security:apiLogin }  


En el rounting es necesario indicar el controlador y la acción a realizar.


SecurityController
 public function apiLoginAction()  
 {  
   $response = new Response();  
   $response->setContent('<html><body>OK</body></html>');  
   $response->setStatusCode(200);  
   $response->headers->set('Content-Type', 'text/html');  
   
   return $response;  
 }  


Y finalmente en el controlador generamos la respuesta, es muy importante indicar el código html de la respuesta para poder saber en Android si el login fue correcto o no.



Android
En Android hay que implementar la siguiente funcion, creando un cliente HTML y dandole una peticion GET para acceder a la URL.

  1 public int login(String username, String password)
  2 { 
  3     HttpClient httpClient = new DefaultHttpClient();
  4      
  5     //construir peticion get 
  6     HttpGet httpGet = new HttpGet("url");
  7     httpGet.addHeader(BasicScheme.authenticate( 
  8             new UsernamePasswordCredentials(username,password), "UTF-8", false)); 
  9      
 10     //ejecutar get y recibir respuesta 
 11     HttpResponse response = null; 
 12     Boolean error = false; 
 13     try { 
 14         response = httpClient.execute(httpGet); 
 15     } catch (ClientProtocolException e) { 
 16         error = true; 
 17         e.printStackTrace(); 
 18     } catch (IOException e) { 
 19         error = true; 
 20         e.printStackTrace(); 
 21     } finally{ 
 22         //cerrar conexion htl y liberar recursos
 23         httpClient.getConnectionManager().shutdown(); 
 24     } 
 25      
 26     //devolver algun codigo (exito, fallo, error, etc) 
 27 }

Este es el caso mas básico de autentificación, ya que al ser HTTP Basic, el usuario y el password van en texto plano. Pero para la etapa de desarrollo y depuración es bastante útil.


Y con esto estaría todo funcionando. Mas adelante me tocara implementar un inicio de sesión para producción (así que tendrá que ser seguro), así que eso ya vendrá en otro post.

jueves, 8 de marzo de 2012

Instalar PHPUnit en XAMPP

PHPUnit es un framework para la realización de pruebas unitarias en PHP. Mi entorno de desarrollo normalmente es XAMPP, asi que vamos a ver como se instala PHPUnit en XAMPP.

cd /opt/lampp/bin

sudo ./pear config-set auto_discover 1 
sudo ./pear install pear.phpunit.de/PHPUnit

Y por ultimo habria que indicadarle a nuestro IDE donde encontrar el ejecutable de PHPUnit, su ruta es /opt/lampp/bin/phpunit

¿Complicado no?

miércoles, 7 de marzo de 2012

Android: pasando datos entre Actividades

En muchas ocasiones al iniciar una actividad necesitamos enviar información extra. Se podría asimilar con un parámetro de una función, pero no disponemos de ellos.

Objetivo
El usuario indica la ruta de un fichero en una actividad y se pasa la ruta a la actividad siguiente.

Pasando los datos
Para poder enviar los datos a otra actividad debemos hacerlo mediante un Bundle. A grandes rasgos un  Bundle es un contenedor de tipos primitivos, y su uso es bastante sencillo.

1:  Intent player = new Intent(this, Player.class);  
2:    
3:  String path = "/sdcard/clip.avi"  
4:              
5:  Bundle bundle = new Bundle();  
6:  bundle.putString("filePath", path);  
7:    
8:  player.putExtras(bundle);  

Con un par de lineas esta todo hecho. Se crea un Bundle se le agregan todos los datos que queramos (en mi caso solo preciso un String) y finalmente se agrega el Bundle al Intent mediante putExtras.


Recibiendo los datos
Y para ir finalizando solo queda recuperar los datos desde la actividad recién iniciada.

 Bundle bundle = getIntent().getExtras();  
 String filePath = bundle.getString("filePath");  

Y colorín colorado este cuento se ha acabado.

martes, 6 de marzo de 2012

Android: Emular tarjeta SD con Eclipse

Muchas veces no tenes un dispositivo a mano para poder desplegar nuestra aplicación, por lo que tenemos que usar el emular para ir depurando. Y en alguno de esos caso necesitamos depurar el acceso a una tarjeta SD. Para poder emular una tarjeta SD hay que crear una imagen, montarla, copiar el contenido y finalmente usarla con el emulador.

Creando la imagen
El SDK de Android contiene varias herramientas una de ellas es mksdcard, de la ayuda del ejecutable podemos ver:

mksdcard: create a blank FAT32 image to be used with the Android emulator.
usage: mksdcard [-l label] <size> <file>
if <size> is a simple integer, it specifies a size in bytes
if <size> is an integer followed by 'K', it specifies a size in KiB
if <size> is an integer followed by 'M', it specifies a size in MiB
if <size> is an integer followed by 'G', it specifies a size in GiB
Minimum size is 9M. The Android emulator cannot use smaller images.
Maximum size is 1099511627264 bytes, 1073741823K, 1048575M or 1023G

Sino tenemos el SDK de android como variable de entorno nos desplazamos a android-sdk-linux/tools y ejecutamos

./mksdcard 512M droidcard

Montando la tarjeta en Linux
Una de las maneras de pasar ficheros a la tarjeta es haciendo un push mediante adb pero para mi gusto es mas fácil montar la tarjeta y copiar todo lo queramos como si fuera una carpeta mas. Para eso es necesario crear un punto de montaje para luego poder montar la tarjeta.


sudo mkdir /media/droidcard
sudo mount -o loop droidcard /media/droidcard


Nota: el nombre del fichero de la tarjeta no tiene porque coincidir con el punto de montaje.

Agregando ficheros a la tarjeta
A estas alturas ya podríamos estar copiando (como root) cosas a nuestra tarjeta sin ningún problema. Una vez terminado hay que desmontarla para poder usarla con el emulador.

sudo umount /media/droidcard

Usando la tarjeta en el emulador

En Eclipse dentro de Run configuration, vamos a la opción target, y en Additional configuration command line options agregamos el siguiente comando:


-sdcard /ruta/a/tu/tarjeta

Y listo, eso es todo.