Soldat's log

Simplemente otro blog personal

Archivo para la categoría "Desarrollo Software"

PHP, PEAR y PHPUnit para Yii en Mac OS X Lion

con 9 comentarios

He tenido que montar un entorno de desarrollo para Yii, un framework de PHP, en mi Mac OS X Lion. Como he tenido que hacer varios apaños he recopilado todos los ajustes en esta entrada.

Antes de nada enumero las versiones del software utilizadas:

  • Mac OS X 10.7.2
  • PHP 5.3.6
  • PEAR 1.9.4
  • PHPUnit 3.5.15
  • Selenium Server 2.8.0
  • Yii 1.1.8
  • Firefox 7.0.1

La mayoría de las acciones que comento han de realizarse con permisos de administrador.

Activar el servidor web con PHP

Mac OS X Lion incluye Apache con soporte para PHP. Sin embargo es necesario activarlo. Para ello en /etc/apache2/httpd.conf descomentamos:

LoadModule php5_module libexec/apache2/libphp5.so

Y en /etc copiamos el fichero de configuración de fábrica:

# cp /etc/php.ini.default /etc/php.ini

Nota: personalmente prefiero que mis entornos de desarrollo y pruebas sean lo más privados posibles. Por ello modifico el Listen de /etc/apache2/httpd.conf a

Listen 127.0.0.1:80

Solamente resta activar el servidor web desde System Preferences / Sharing / Web Sharing.

El directorio que se publica como página principal está ubicado en /Library/WebServer/Documents/ podemos utilizar directamente esta carpeta o bien utilizar carpetas personales ( http://localhost/~usuario/ ). La opción de carpetas personales busca el directorio Sites en el home del usuario, que se puede crear a partir del cuadro de configuración anterior.

Para probar que todo está funcionando lo más recomendable es publicar un fichero .php con un getinfo():

<?php
phpinfo();
?>

Instalar PEAR y PHPUnit

En las versiones anteriores de Mac OS X PEAR está directamente instalado, en Lion es necesario hacer un paso previo:

# php /usr/lib/php/install-pear-nozlib.phar 

Con esto ya tenemos PEAR instalado. Actualizamos:

# pear upgrade-all

configuramos el autodiscover (para hacernos las cosas más fáciles) e instalamos PHPUnit:

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

Solamente nos falta un detalle más, añadir el software instalado por PEAR a la configuración de PHP. Añadimos al /etc/php.ini:

include_path = "/usr/lib/php/pear/"

Para comprobar que todo va bien comprobamos la versión de PHPUnit

# phpunit --version

Montar y ejecutar las pruebas de un proyecto Yii

A partir de aquí lidiaremos con los problemas que surgen con Yii. Primero nos descargamos Yii y creamos un proyecto de prueba:

$ yii/framework/yiic webapp demo

Problema con date.timezone

Al acceder a la demo puede que nos muestre un mensaje de error como el siguiente:

It is not safe to rely on the system’s timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function.

La solución más sencilla es configurar en /etc/php.ini el timezone, por ejemplo:

date.timezone = "Europe/Madrid"

Problema con Selenium, arrancar Firefox

Configuramos la URL para las pruebas en el fichero protected/tests/WebTestCase.php:

define('TEST_BASE_URL','http://localhost/~chernando/demo/index-test.php/');

Descargamos Selenium y lo dejamos ejecutando:

$ java -jar selenium-server-standalone-2.8.0.jar

e intentamos lanzar las pruebas funcionales del proyecto yii:

$ cd demo/protected/tests/
$ phpunit functional/SiteTest.php

Lo más probable es que falle el lanzamiento de iexplorer (bastante razonable en Mac OS X ;) ) o que no encuentre firefox-bin ya que no está en el path. Para solucionarlos editamos protected/tests/phpunit.xml, eliminamos:

<browser name="Internet Explorer" browser="*iexplore" />

y modificamos la entrada del Firefox a nuestro, en mi caso es el siguiente:

<browser name="Firefox" browser="*firefox /Users/chernando/Applications/Firefox.app/Contents/MacOS/firefox-bin" />

Con esto podremos ejecutar las pruebas funcionales.

Problema con Selenium, fallan los test

Después de llegar hasta aquí me sorprendió bastante que de los tres tests que vienen de fábrica fallaran dos ;) Concretamente estos dos fallos:

Body cannot be blank.
Failed asserting that <boolean:false> is true.

Password cannot be blank.
Failed asserting that <boolean:false> is true.

Por suerte es un problema conocido y ya está corregido en el repositorio.

El problema es que no se espera a que se ejecuten las validaciones con JavaScript. El parche publicado corrige un par de líneas en el fichero protected/tests/functional/SiteTest.php que cambia los dos assert que fallan por waitFor, consiguiendo el comportamiento esperado.

Escrito por chernando

2011/10/16 a 17:22

Escrito en Desarrollo Software, Mac OS X

Etiquetado con , , ,

Python generators

dejar un comentario »

Todos conocemos las listas de Python mientras que sus hermanos los generadores son menos conocidos. Este breve artículo es un recordatorio :-)

El concepto de generador

La idea de los Generators se basa en dos conceptos: los iteradores y la evaluación perezosa.

Python utiliza el concepto de iterador de una forma muy natural, por ejemplo:

for i in elementos:
    print i

Por debajo el intérprete lo que hace es crear un iterador mediante el método iter() y avanzar posiciones con el método next() del iterador devuelto. Cualquier objeto que cubra estos métodos puede ser tratado como un iterador y por tanto trabajar de forma transparente con el resto de la API.

El segundo concepto es la evaluación perezosa (lazy evaluation), que en pocas palabras significa que una expresión se resuelve únicamente cuando se la necesita, no antes.

Uniendo ambos conceptos tenemos el generador: una estructura, sobre la que podemos iterar, cuyos componentes se van obteniendo según se va avanzando.

La diferencia fundamental con la lista es que la lista tiene una evaluación ansiosa/codiciosa (eager evaluation). Esto implica que cuando queremos manejar una lista de 10 elementos la lista necesita disponer de esos 10 elementos en el momento de su declaración. Sin embargo el generador solamente necesita saber cómo generar el siguiente valor por lo que no necesita ni reservar espacio ni conocer a priori ningún elemento.

Declaración de un método generador

Un método que utilice la palabra reservada yield se trata como un generador. yield puede devolver un valor, como si fuera un return (cuidado, return se utiliza para terminar el generador).

Por ejemplo definimos la función naturales() que devuelve un iterador de todos los números naturales:

def naturales():
    n = 1
    while True:
        yield n
        n = n + 1

i = naturales()
print i         # <generator object naturales at 0x10f1a3050>
print i.next()  # 1
print i.next()  # 2

Vemos que la primera ventaja de la evaluación perezosa es que podemos tratar estructuras infinitas.

Emulemos el método enumerate(list) nativo de Python utilizando el generador como una lista (o mejor dicho su iterador):

ls = ['a', 'b', 'c']
print zip(naturales(), ls)  # [(1, 'a'), (2, 'b'), (3, 'c')]

Declaración de una expresión generadora

Otra forma de declarar un generador es exactamente igual que una expresión de lista pero con paréntesis. Por ejemplo los números pares:

print [p for p in range(10) if p % 2 == 0]  # [0, 2, 4, 6, 8]
print (p for p in range(10) if p % 2 == 0)  # <generator object <genexpr> at 0x10f1a3190>

En este caso vemos que la lista está completa mientras que el generador se queda a la espera. A partir de este ejemplo podemos definir otro generador de números naturales pares:

i = (p for p in naturales() if p % 2 == 0)
print i.next()  # 2
print i.next()  # 4

Es importante encadenar generadores con generadores, en caso contrario la evaluación de la lista obligaría el recorrido completo del generador. Si la estructura fuera infinita, como el ejemplo, provocaría la muerte del intérprete:

[p for p in naturales() if p % 2 == 0]

Conclusiones

Evidentemente utilizar los generadores no es la solución para todos los problemas.

Hay unos escenarios concretos en los que merece la pena considerados:

  • Estructuras infinitas.
  • Estructuras que ocupen mucha memoria, reducimos así el espacio de memoria a reservar.
  • La generación de los elementos es costosa, podemos retrasar su cálculo hasta el último momento.
  • Sabemos que la estructura no se va a consumir completamente, una mezcla de las dos anteriores.
  • Cuando trabajamos con otros generadores.

Más información

Escrito por chernando

2011/09/01 a 15:15

Escrito en Desarrollo Software

Etiquetado con ,

Servidor de proyectos con Subversion, Trac, Apache (y LDAP)

con 19 comentarios

En esta entrada vamos a montar un servidor para la gestión de proyectos. Para ello utilizaremos un sistema de control de versiones (Subversion), un sistema de gestión de incidencias (Trac) y un sistema de autenticación compartido, para ello utilizaremos Apache y alguno de sus métodos de autenticación como por ejemplo LDAP (válido si es necesario utilizar las cuentas de un Directorio Activo).

Aunque existen soluciones más elaboradas y más integradas, como el software de SourceForge.net, este planteamiento permite montar un servidor a medida, pudiendo alterar cualquiera de sus elementos, y en mi opinión más sencillo de mantener.

Nos basaremos en una instalación mínima de Ubuntu Server 8.04 (por lo que no disponemos ni de Subversion 1.5 ni de Trac 0.11) con la idea de montar un servidor preparado para mantener varios proyectos.

Instalación y configuración de Subversion

Para empezar instalaremos subversion, también es recomendable subversion-tools por los scripts adicionales que incorpora, y preparamos un repositorio de prueba:

# apt-get install subversion subversion-tools
# mkdir /srv/svn
# svnadmin create /srv/svn/proyecto

Aprovechamos ahora para crear una estructura básica dentro del repositorio, esto nos servirá en las pruebas para ver si realmente podemos acceder al repositorio:

# svn co file:///srv/svn/proyecto
# svn mkdir proyecto/{branches,tags,trunk}
# svn ci -m 'Estructura Inicial' proyecto

Instalación y configuración de Trac

Instalaremos y configuraremos mínimamente un proyecto de Trac para el repositorio que acabamos de crear:

# apt-get install trac
# mkdir /srv/trac
# trac-admin /srv/trac/proyecto initenv
(Opciones sugeridas)
Path to repository [/path/to/repos]> /srv/svn/proyecto

Es el momento de comprobar que trac y su unión con el repositorio de subversion funcionan correctamente, para ello lanzaremos el servidor incluido en trac:

# tracd -p 80 /srv/trac/proyecto

Abriendo la URL http://localhost:80/ deberíamos ver disponible nuestro proyecto, “My Project“, y comprobamos que la función de “Browse Source” funciona correctamente.

Por el momento nada nuevo, paremos tracd y sigamos.

Instalación y configuración de Apache

Optamos por enganchar Trac con mod_python así que lo más sencillo es instalar el paquete de mod_python y que instale apache por sus dependencias:

# apt-get install libapache2-mod-python

Bien, ahora editamos la configuración para que Apache pase las peticiones que vayan a /trac a nuestro conjunto de proyectos en /srv/trac. Editando el fichero /etc/apache2/sites-available/default añadimos antes del cierre de </VirtualHost> lo siguiente:

<Location /trac>
  SetHandler mod_python
  PythonInterpreter main_interpreter
  PythonHandler trac.web.modpython_frontend
  PythonOption TracEnvParentDir /srv/trac
  PythonOption TracUriRoot /trac
</Location>

Forzamos la recarga de la configuración de Apache:

# /etc/init.d/apache2 reload

Hacemos una prueba con el navegador en http://localhost/trac/ que debería mostrarnos un error por falta de permisos de escritura. Como vamos a dejar a Apache como gestor de los proyectos es necesario darle los permisos que necesita:

# chown -R www-data.www-data /srv/trac/proyecto

Con esto todo debería funcionar exactamente igual que con la prueba realizada con tracd. Vamos ahora a mostrar el repositorio desde Apache.

Subversion trabaja con Apache haciendo uso de WebDAV así que instalamos el módulo necesario:

# apt-get install libapache2-svn

Añadimos la configuración necesaria en el fichero /etc/apache2/mods-available/dav_svn.conf, podéis descomentar las opciones si os resulta más cómodo. En cualquier caso la configuración debe quedar de la siguiente manera:

<Location /svn>
  DAV svn
  SVNParentPath /srv/svn
</Location>

De nuevo, forzamos la recarga de la configuración de Apache y comprobamos que http://localhost/svn/proyecto muestra el proyecto y que podemos navegar dentro de él. Si probáis http://localhost/svn/ os dará un error, ya que en este caso no existe un listado de proyectos disponibles como hacía Trac.

Igualmente que en Trac, si Apache es el gestor del repositorio es necesario que tenga permisos de escritura. En este caso vamos a ceder completamente el control a Apache:

# chown -R www-data.www-data /srv/svn/proyecto

Ahora mismo disponemos de un sistema completamente funcional en el que no se exige ningún tipo de autenticación. En el caso de Trac no se puede hacer login y en el caso de Subversion ni siquiera se pide. Si queréis verlo en podéis hacer la siguiente prueba:

# svn co http://localhost/svn/proyecto/trunk
# touch trunk/README.txt
# svn add trunk/README.txt
# svn ci -m "Fichero leame" trunk

Si comprobamos el historial, svn log trunk/README.txt, podremos ver que no hay ningún usuario responsable del commit. En ningún momento se nos ha pedido identificarnos, ya que hay permisos de lectura y escritura para todo el mundo, así que podemos bajarnos el contenido del repositorio y los commit son anónimos.

Autenticando usuarios

Empecemos con lo más sencillo, usuarios válidos de un fichero htpasswd, podéis leer algo más en otro de mis artículos sobre ficheros .htpasswd.

# htpasswd -c /etc/apache2/users.conf chernando

Editamos Trac para soportar un login centralizado añadiendo un nuevo location a default:

<Location /trac/*/login>
  AuthType Basic
  AuthName "Trac Projects"
  AuthUserFile /etc/apache2/users.conf
  Require valid-user
</Location>

Forzando la recarga de Apache ya disponemos de la función “login” en Trac. Para el repositorio vamos a dejar el acceso de lectura para todo el mundo y limitar el acceso de escritura a los usuarios registrados añadiendo a la configuración de WebDAV:

AuthType Basic AuthName "Subversion Repository"
AuthUserFile /etc/apache2/users.conf
<LimitExcept GET PROPFIND OPTIONS REPORT>
  Require valid-user
</LimitExcept>

Una vez más recargando Apache ahora podemos bajar y actualizar un repositorio pero necesitaremos identificarnos para subir cambios al repositorio. Probad a añadir un nuevo fichero y comprobaréis que ahora se exige un usuario y password válidos.

Rizando el rizo, autenticando contra un LDAP

En el caso de disponer de un sistema de autenticación centralizada, por ejemplo LDAP o un Directorio Activo con el servicio LDAP activo, podemos delegar toda la carga de la gestión de usuarios dejando nuestro servidor de proyectos completamente “inhabitado”.

Para ello lo único que necesitamos es cambiar ambas configuraciones. En primer lugar habilitamos los módulos necesarios:

# a2enmod authnz_ldap
(esto debería habilitar el módulo ldap por dependencias)

Y configuramos ambas secciones de autenticación. Primero eliminamos AuthUserFile que ya no es necesaria y después añadimos:

AuthBasicProvider "ldap"
AuthLDAPURL "ldap://127.0.0.1/dc=chernando,dc=eu?uid?sub?(objectClass=inetOrgPerson)"
authzldapauthoritative Off

Podéis ver más detalles en http://trac.edgewall.org/wiki/TracModPython.

Ampliaciones que pueden hacerse a partir de aquí

En esta entrada he intentado introducir el menor ruido posible, tanto en comandos como software a instalar, por lo que hay ciertas mejoras que se han quedado en el tintero. Por ejemplo:

  • Configurar Apache para hacer uso de SSL, muy necesario ya que hasta el momento todas las negociaciones con Apache van en texto claro.
  • Establecer limitaciones en el acceso de los repositorios (y en secciones de los mismos) haciendo uso de authz.
  • Configurar un sistema de correo, que permita notificar todo tipo de eventos: nuevos tickets, cambios en el repositorio…
  • Integrar Subversion con Trac, por ejemplo permitir que un commit cierre o añada información a un ticket de Trac.
  • Utilizar la última versión de Subversion, 1.5, por su mejora en la gestión de merge de ramas.
  • Utilizar la última versión de Trac, 0.11, por las mejoras en el interfaz y en la gestión del flujo de trabajo asociado a un ticket.
  • Ampliar el sistema incluyendo otros servicios: listas de correo, servidor de integración continua…

Escrito por chernando

2008/07/20 a 13:29

Apache Ant, primeros pasos creando un build.xml

con 3 comentarios

Uno de los inconvenientes en la construcción de software con más de dos ficheros (cualquier cosa que se salga de un “Hello world!”) es el proceso de compilación: orden de compilación de ficheros, dependencias, rutas de librerías internas, etc… En esta entrada vamos a dar los primeros pasos en una de las soluciones más comunes para proyectos Java: Apache Ant.

Para los impacientes: al final de la entrada encontraréis el build.xml con todo lo explicado que seguramente os sirva tal cuál.

En primer lugar creamos un fichero build.xml en el raíz de nuestro proyecto y definimos su nombre:

<project name="Proyecto">
</project>

Ant, al igual que otras herramientas de construcción, se basa en el concepto de objetivos o targets cuya definición engloba tanto las dependencias previas como los pasos a seguir para conseguirlo.

Vamos a comenzar definiendo un objetivo de preparación llamado init que será el encargado de crear un directorio classes donde guardaremos los ficheros .class resultantes de la compilación y el directorio build para el .jar final. Para ello basta incluir dentro de <project> las siguientes líneas:

<target name="init">
	<mkdir dir="classes" />
 	<mkdir dir="build" />
</target>

Como podemos ver los objetivos se delimitan con etiquetas <target> y un nombre. Dentro de ellos se enumeran los pasos que se han de seguir para alcanzar el objetivo, en este caso ha de crear directorios.

Si queremos alcanzar el objetivo init basta con realizar:

$ ant init
Buildfile: build.xml

init:
    [mkdir] Created dir: /home/chernando/proyecto/classes
    [mkdir] Created dir: /home/chernando/proyecto/build

 BUILD SUCCESSFUL
Total time: 0 seconds

Es hora de compilar nuestro proyecto, vamos a definir el objetivo compile. Ahora bien, la compilación depende de la creación del directorio classes que se realiza objetivo anterior. Con esto en cuenta basta con incluir:

<target name="compile" depends="init">
	<javac srcdir="src" destdir="classes" />
</target>

La dependencia se fija en la declaración del target de tal manera que se garantiza su cumplimiento antes de comenzarla. Nuestro código está en el directorio src y el resultado de la compilación se lleva al directorio classes.

Importante notar que esta vez estamos usando <javac> esto es lo que Ant se denomina tarea. Hay muchas tareas predefinidas, consultad el manual de ant.

Con nuestro proyecto compilado vamos a generar el .jar que distribuiremos haciendo uso de un nuevo objetivo llamado build.

<target name="build" depends="compile">
  	<jar destfile="build/proyecto.jar" basedir="classes" />
</target>

En este caso dependemos de los frutos de compile y utilizamos la tarea jar que se encarga de empaquetar todo el contenido del directorio classes en el fichero proyecto.jar.

Finalmente incluiremos un nuevo objetivo para limpiar todo el entorno, el objetivo clean:

<target name="clean">
  	<delete dir="classes" />
  	<delete dir="build" />
</target>

A estas alturas es fácil entender que lo único que realiza es eliminar los directorios de trabajo dejando el entorno limpio del proceso de compilación.

Resumiendo nuestro fichero build.xml es:

 <project name="Proyecto">
        <target name="init">
                <mkdir dir="classes" />
                <mkdir dir="build" />
        </target>
        <target name="compile" depends="init">
                <javac srcdir="src" destdir="classes" />
        </target>
        <target name="build" depends="compile">
                <jar destfile="build/proyecto.jar" basedir="classes" />
        </target>
        <target name="clean">
                <delete dir="classes" />
                <delete dir="build" />
        </target>
</project>

Y hasta aquí este minitutorial sobre el uso de ant :-)

Escrito por chernando

2008/03/02 a 18:34

Escrito en Desarrollo Software

Etiquetado con , ,

Metodología de desarrollo CADT

con un comentario

Leyendo Building Scalable Web Sites de Cal Henderson, un libro bastante curioso por cierto, encontré una referencia a la metodología de desarrollo CADT o “Cascade of Attention-Deficit Teenagers” de Jamie Zawinski.

La idea fundamental radica en “ignorar los bugs abiertos contra una funcionalidad”, es decir, los errores prácticamente no se resuelven.

El artículo del señor Zawinski se basa en un caso real ocurrido con los errores reportados contra los programas de GNOME 1.4 que fueron completamente ignorados hasta la versión 2.0 en la que se consideró que los bugs habían sido solucionados automáticamente.

El objetivo básico de esta metodología es mantener al programador lo más “contento” posible. Esto se traduce en que el programador se dedica a implementar nuevas funcionalidades y no peleándose con extraños errores.

Las ventajas son evidentes:

  1. Es mucho más sencillo planificar el tiempo necesario para implementar cierta funcionalidad que estimar la cantidad de horas que se van a invertir en solucionar un error.
  2. “Crear” nuevas funcionalidades es una actividad creativa más reconfortante que la depuración, que tiende a ser repetitiva y poco gratificante.
  3. No hay gasto en el cambio de contexto. No es necesario sacar al programador de su contexto de trabajo para llevarlo a un trabajo que realizó hace unos meses para solucionar un problema.

Las desventajas: evidentemente esto no es serio. No podemos aplicar esto a un desarrollo de aplicaciones críticas o a clientes que exijan parámetros de calidad estrictos.

A primera vista podría pensarse que es más una broma que una metodología, y no sería un pensamiento desacertado… Ahora bien, conociendo la vida real en la que los programadores con tal de eliminar un bug asignado son capaces de recurrir al “if (error) return 0;” o relanzar la excepción hacia arriba no está tan mal. Al menos ya sabes que la calidad va a dejar mucho que desear :-)

Llevar a cabo esta metodología es muy sencillo, simplemente se habilita un issue-tracking que contabilice el número de bugs abiertos contra cierta funcionalidad. Cuando se alcanza el valor umbral la funcionalidad se reimplementa desde cero.

El resultado es que el programador siempre está pasando un buen rato creando software y los usuarios son sorprendidos con nuevas funcionalidades cada cierto tiempo.

Como menciona el señor Henderson es la estrategia que muchos sitios “Web 2.0″ están siguiendo más o menos oficialmente. Esta metodología merece un poco de reflexión, nunca se sabe.

Escrito por chernando

2007/12/02 a 13:58

Escrito en Desarrollo Software

Etiquetado con

Seguir

Get every new post delivered to your Inbox.