Nov 30

Sobre angularjs

Porque me ha tocado en el curro, me he leído AngularJS by Brad Green and Shyam Seshadri y he empezado a trabajar con AngularJS. Aprovecho aquí para comentar el libro y los conceptos básicos de angularJS.

El libro

El libro no me ha hecho demasiada gracia. Los dos primeros capítulos sí me gustaron, muy sencillos y explican lo más básico de angularJS. Sin embargo, a partir del tercer capítulo, la cosa se complica y no me parece que hayan conseguido explicarla bien. Empiezan a meter mucho código con muchas de las cosas propias de angular sin dar una explicación detallada de ellas, por lo que aunque intuyes de que va el tema o cómo funciona, los detalles no te quedan claros. Imagino que con la práctica me acabe enterando.

Los conceptos

angularJS es un framework MVC javascript mantenido por google. Ya estamos con los "palabros", vamos a intentar explicarlo.

En angularJS el modelo no es más que una variable javascript llamada $scope en la que se añaden de la forma normal en javascript todos los datos (modelo) que queramos.

La vista es un HTML en el que podemos poner algunas cosas específicas de angularJS. Por ejemplo, un párrafo podría ser <p>{{unaVariable}}</p> done unaVariable es un atributo de $scope, es decir, $scope.unaVariable existe y es lo que se pondrá dentro de los tags <p> y </p>. Una característica interesante de angularJS es que esta relación es bidireccional y automática, de forma que si nuestro código javascript modifica el valor de la variable, automáticamente se refleja en la vista HTML, y si ese {{unaVariable}} lo pusieramos en un input text así <input type="text" ng-model="unaVariable">, en cuanto el usuario teclea se actualiza la variable.

El controlador es una clase javascript en cuyo constructor recibe el $scope (y otras cosas) y se encarga de rellenarlo con los datos que necesitemos. También a esa clase le ponemos todas las funciones javascript que necesitemos y a las que se podrán llamar desde los distintos eventos de HTML (onclick, submit, etc).

Por supuesto, podemos hacer un HTML principal (index.html o similar) que dentro llevará varias vistas, cada una con su propio modelo y controlador. La idea es hacer una aplicación de simple página e ir reemplazando dentro de ella los distintos trozos HTML según el usuario vaya pulsando botones y menús.

Aparte de este concepto MVC, angularJS tiene una serie de utilidades que resuelven los problemas típicos en cualquier aplicación web. Veamos los más importantes

Directivas : En angularJS podemos definir nuestras propias directivas, aparte de las que angularJS ya nos proporciona. Una directiva es una forma de reemplazar un tag a atributo de un tag html por lo que  nosotros queramos. Un ejemplo tonto sería hacer una directiva "horaActual", de forma que si en nuestro HTML ponemos <horaActual></horaActual> se reemplazará por la fecha/hora actual (porque así lo hemos codificado en la directiva). Por supuesto, el código que hay que hacer es más sencillo que andar buscando los tags HTML y reemplazándolos a pelo.

Servicios : En angularJS podemos definir servicios, que simplemente son código javascript realizado por nosotros con determinadas funciones que queremos poder aprovechar en varios sitios. Si creamos un servicio con una serie de funciones javascript, angularJS podrá pasarlo como parámetro en el constructor de los controladores, de forma que estos tendrán disponible y podrán usar las funciones javascript de nuestro servicio. Es bastante habitual hacer que los servicios sean los que hablen con el servidor, para pedirle información que debe presentarse al usuario, o enviarle las operaciones que ha realizado el usuario.

Promises : Relacionado con los Servicios cuando estos hacen llamadas al servidor, tenemos las Promises. Si el usuario hace una operación que necesita enviar y esperar respuesta del servidor, normalmente llamará a una función javascript de uno de nuestros servicios. Este función hará la llamada al servidor (con AJAX por ejemplo) y recibirá de forma asíncrona el resultado. Pues bien, nuestra función javascript del servicio puede devolver inmediatamente, sin esperar el resultado, una de estas Promises, que no es más que una instancia de una clase javascript de angularJS, y cuando tenga el resultado llamar al método resolve() de la Promise. angularJS se enterará y actualizará el modelo cuando el resultado esté disponible (hayamos llamado a resolve). Un pequeño ejemplo lo dejará más claro

function pideDatosAlServidor (…, $q) {     // $q es un objeto específico que nos pasará angularJS para trabajar con Promises
   var defer = $q.defer();            // Obtenemos un objeto deferred para manejar nuestra Promise
   $.get ("url", function (data) {          // Nuestra llamada a ajax. AngularJS tiene parte de jQuery embebido.
       // hacemos algo con los datos recibidos
      defer.resolve();                      // Indicamos que ya se dispone de los resultados.
   }
   return defer.promise;             // Devolvemos la Promise
}

Fíjate que la llamada a $.get() es una llamada AJAX que llamará a function(data) cuando se tengan los resultados. El código pideDatos() hace esta llamada y directamente, sin esperar el resultado, devuelve el defer.promise. Esto le bastará a angularJS para saber que tiene que esperar por los datos antes de refrescar la página.

Test : AngularJS está pensado para que se puedan hacer test unitarios y de integración, por lo que viene con herramientas como e2e (end to end), facilitando, con herramientas adicionales, incluso cosas como levantar un servidor para los test y ejecutarlos.

Por supuesto, angularJS tiene otro montón de cosillas típicas para ayudarnos en nuestro desarrollo, como facilidades para la internacionalización, validación de formularios, bucles en las plantillas HTML, filtros, etc, etc.

Bueno, me he quejado de que el libro no explica bien, y escribiendo esto veo que es difícil explicarlo bien, pero al menos no  he necesitado 175 páginas.

Nov 23

Herramientas frikis vs amigables

friki sheldom big bang theoryCuando busco herramientas para trabajar o aplicaciones para instalar, gratuitas, me he encontrado con frecuencia que hay dos herramientas con éxito, pero que difieren entre sí en un detalle, su facilidad de instalación/administración y sus posibilidades de extensión. Una de las herramientas es lo que yo llamo "para frikis", suele ser una herramienta muy potente y configurable, pero que requiere muchos conocimientos para administrarla. La otra es la herramienta "amigable", que un administrador sin profundos conocimientos informáticos puede instalar y poner en marcha sin demasiados problemas, pero que hace lo que hace y no es tan fácilmente salirse de eso. Veamos algunos ejemplos

Eclipse vs Netbeans . Ambas muy aceptadas en el mundo de desarrollo java. Eclipse es la herramienta de frikis. Puedes bajarte un eclipse "pelado" que apenas hace nada y luego liarte a instalarle plugins según el tipo de desarrollo que vayas a hacer (C++, java escritorio, java ee, plugins, etc, etc). Netbeans, por el contrario, es una herramienta más monolítica. Sí, también tiene plugins que puedes instalar/desinstalar, pero basta compara la página de descarga de eclipse y la de netbeans, para ver que eclipse requiere más conocimientos para saber qué quieres bajar. De hecho, hay más entornos de programación basados en eclipse, como el de Android o el de Spring, o el Aptana, que sobre netbeans. Suele ser normal también encontrarse que se usa eclipse en los entornos de desarrolladores profesionales, mientras que usan más netbeans los programadores que están empezando.

drupal vs joomla . Ambos para la construcción de sitios web. Drupal es la herramienta de frikis, aunque tiene una administración bastante intuitiva, es más espartana, muchos plugins desarrollados y posibilidades realmenten sorprendentes. Joomla, sin embargo, tiene una administración más vistosa (aunque a mi me resulta menos intuitiva) y tiene bastantes y más vistosos temas que drupal. De alguna forma, drupal es adecuada para administradores con conocimientos que en un momento dado requiran instalar o desarrollar plugins que hagan cosas que se salgan de lo normal en un sitio web, mientras que joomla es adecuado para obtener rápidamente un sitio web vistoso. Basta mirar el número de plugins de drupal (más de 14000) y el de joomla (más de 7000).

Geoserver vs mapserver Ambos servidores web de mapas. Mapserver es el de frikis, ejecutable nativo, mucho más eficiente sirviendo mapas, pero sin una interfaz web de administración (salvo instalando plugins) y un horror para configurar los mapas, a base de editar ficheros con sintaxis específicas. Geoserver, sin embargo, hecho en java, algo más lento sirviendo mapas, tiene una interfaz web de administración muy intuitiva y sin muchos conocimientos de geoserver, en pocos minutos se puede tener funcionando y sirviendo mapas. Mapserver es adecuado para un servidor de mapas que no vaya a tocarse mucho y que se requiera que sea eficiente, se configura una vez y listo. Geoserver más adecuado si se va a andar cambiando con frecuencia los mapas servidos o va a tener que administrarlo alguien sin muchos conocimientos.

redmine vs trac. Ambos gestores de tareas/incidencias. Redmine es el amigable, con una interfaz web de administración muy intuitiva, mientras que trac venía sin interfaz web de administración cuando lo estuve mirando, aunque ahora ya la lleva. La administración se hace por línea de comandos con un comando "trac-admin". Eso sí, al menos cuando los instalé, ninguno de los dos es fácil de instalar, aunque redmine me dio bastante menos guerra.

Git vs Mercurial. Ambos sistemas de control de versiones distribuidos. Git, diseñado por linus torvalds, cuenta aparentemente con más comandos avanzados y de bajo nivel, dándonos más posibilidades de hacer más cosas. Mercurial, sin embargo, tiene los comandos necesarios para hacer lo que se necesita en este tipo de herramientas. Sus características avanzadas están deshabilitadas por defecto. Git gusta a los frikis, Mercurial tiende a ser más la herramienta de los que necesitan un control de versiones distribuidos y no quieren pelearse con ella, sino dedicarse a desarrollar su propio proyecto. Es curiosa la comparativa en que se dice que Git es como Wesley Snipes y Mercurial como Denzel Washington. Denzel Washington es todo un actor, pero tiene una vida real normal, nada interesante. Mientras que Wesley Snipes, quizás no siendo tan buen actor, es mucho más divertido por tener una vida real más agitada. Por cierto, en el artículo dicen que Subversion sería Morgan Freeman.

Aunque una de ellas no es gratuita, por supuesto, Windows vs Linux. No hay mucho que comentar, creo que todos conocemos las virtudes y defectos de ambos sistemas operativos. Todos sabemos cual es el amigable y cual el de frikis. Y de la misma forma, Android vs iOS, que creo que tampoco es necesario comentar

Y seguramente hay más, aunque en este momento, con mi mujer al lado dándole a la ruidosa aspiradora, no se me ocurren más.

Jan 28

El vi, esa gran maravilla

Ando liado con menús y chorradas varias en el proyecto. El "rollo" es que a veces los menús son muy largo (fíjate en esta tabla de códigos ISO de paises). En base de datos va el código numérico o el de dos/tres letras, mientras que al usuario se le deben presntar los textos legibles con el nombre completo del país. A veces es símplemente mostrar un país resultado de una consulta, otras veces es mostrar un menú (unos select/option de HTML).

Pues bien, a partir de una de esas tablas hay que conseguir bien una tabla en base de datos con dos columnas: código – texto legible, bien el menú select/option de HTML, bien un fichero properties de java con muchas líneas codigo=país, etc. Y muchas veces la única entrada que tenemos es copiar de una página web como esa.

Habitualmente, un "truco" que hago es seleccionar con el ratón la tabla de la página web en cuestión, esto me copia en el portapapeles el código de la tabla HTML y afortunadamente, si se pega eso en un Excel lo hace de forma correcta (fíjate en la imagen)

excel importando paises

Ahora el proceso es más fácil, se puede exportar esto a un CSV, que no es más que un fichero de texto en el que cada fila corresponde a una línea y los valores de las columnas van separados por comas, encerrados en comillas o como queramos. Eliminamos antes las columnas que no nos interesen y obtenemos un fichero de texto como este (abierto ya con el editor vi)

CSV en el vi

 

Bien, pues con un fichero como este, lo habitual sería cogerle algún lenguaje de script, como phyton, perl, awk o cualquiera de nuestro gusto y hacer un pequeño programita que lea eso línea a línea y genere lo que  nosotros queramos, estilo

<option value="AF">Afganistán</option>

si queremos, por ejemplo, un select/option de HTML. Pero no es necesario uno de estos lenguajes, el mismo editor vi (gvim en mi caso) tiene un potente buscar y reemplazar con expresiones regulares similares a las de cualquiera de esos lenguajes. Si en el editor anterior ejecutamos el comando (lo explicamos al final)

:%s/^\(\S\+\); *\(.*\)$/<option value="\1">\2<\/option>/

nos da el resultado esperado

gvim modificado

El comando no es más que un comando de vi que permite sustituir unas cadenas por otras.

El : es para entrar en el modo que nos permite escribir comando de este tipo.

El %s indica la s un "sustituir" y el % que se haga en cada línea. El comando s lleva este formato s/cadena a buscar/cadena nueva/.

La cadena a buscar es una expresión regular, en la que ^ indica inicio de línea y $ fin de línea, como en cualquier lenguaje.

\S+ indica caracteres no espacios una o más veces, como en las expresiones regulares de cualquier otro lenguaje. La diferencia aquí es que el + hay que escaparlo, por eso \S\+. Esto ira casando con los códigos de dos letras. El .* indica cualquier cadena de caracteres, que iran casando con las descripciones de los paises.

Como nos interesa guardarnos ambas cosas (el código y la descripción), las encerramos entre paréntesis, pero a diferencia de otros lenguajes, aquí también hay que escaparlos, así que quedan cosas tan feas como \(\S\+\) y \(.*\). Entre ambas está el ; y el espacio que por lo que vemos es opcional, es decir <espacio>*

En la parte de la nueva cadena, tenemos disponibles en \1 y \2 lo que habíamos colocado entre paréntesis, es decir código de país y descripción. Así que la cadena nueva no es más que el <option> completo, poniendo \1 donde va el código de país y \2 donde va la descripción.

Nada que no se pueda hacer desde un script en otro lenguaje, como hemos dicho, pero .. ¿qué pasa en mi caso que posiblemente hiciera el script usando el vi? ¿para qué hacer el script, salvo que piense reutilizarlo más veces?

Oct 25

He leído “OpenLayers 2.10 Beginner’s Guide”

http://blog.sonxurxo.com/wp-content/uploads/2011/03/openlayers-beginners-guide.pngAcabo de leer OpenLayers 2.10 Beginner’s Guide 

OpenLayers es una librería javascript que nos permite hacer aplicaciones con mapas en nuestra página web. Los mapas pueden ser los de Google Maps, Bing, OpenStreetMap o servidor por cualquier servidor que cumpla los estándares OGC, como Geoserver. OpenLayers "traga" además otro montón de formatos para dibujar mapas directamente de ficheros.

El libro es muy básico, empieza desde el principio, tan desde el principio que incluso nos explica lo básico de javascript y qué son las clases, la herencia, los métodos y atributos. Por supuesto, no se entretiene demasiado en ello, una simple explicación para no pillarse los dedos según va avanzando en OpenLayers.

Si, como yo, has empezado con OpenLayers a base de ensayo y error, copy-paste de código en google y ya tienes algo de experiencia, este libro es muy básico, pero siempre acalara algún concepto que puedes no haber "pillado" en tus pruebas, ensayos, errores, copies y pastes.

Es ideal sin embargo para leer antes de empezar con OpenLayers, el libro va despacio, no es muy complejo lo que explica y va asentando bases antes de seguir. Cada apartado resulta un poco repetitivo porque primero te explica cómo funciona, luego te lo vuelve a explicar mientras te dice cómo codificarlo y finalmente te lo vuelve a explicar añadiendo un título "¿Qué acaba de pasar?" después de que hayas ejecutado el ejemplo que acabas de codificar. Lo dicho, ideal para quien no tiene idea del tema y le gusta asentar bien las cosas.

 

Sep 20

He leído “Test Driven: TDD and Acceptance TDD for Java Developers”

tdd acceptance testBueno, realmente exagero un poco cuando digo "he leído…", me he dejado sin leer los últimos capítulos.

La parte central del libro me ha encantado, pero la primera parte y la última me han resultado muy pesadas e inútiles, hasta el punto de dejar de leerlo en esa última parte.

Los primeros capítulos nos cuenta principalmente las ventajas de TDD, no se extiende mucho en qué es o cómo se hace TDD, sino que se extiende mucho (muchísimos) en sus ventajas. Estas ventajas son más o menos conocidas por todos (código con menos fallos, confianza en que no estropeamos nada a la hora de hacer refactoring por lo que nos cuesta menos hacerlo, etc, etc). Por ello, varios capítulos dedicados a las ventajas me parece excesivo e incluso repetitivo, ya que una y otra vez comenta las mismas ventajas.

Afortunadamente el grueso de capítulos centrales me ha parecido una maravilla. No por TDD en sí mismo, sino porque se dedica para cada tipo de proyecto diffícilmente testeable (base de datos, jsp, swing, etc) a mostrarnos los distintos frameworks con los que podemos trabajar (jdbc, hibernate, spring, jsf, tapestry, wicket, …) como las librerías útiles para hacer test automáticos en esos frameworks (mockito, clases de spring que son mock objects de interfaces java complejas, jspunit, fit, etc, etc). Lo mejor de todo esto es que no da por supuesto que conocemos cada framework o librería, sino que nos da un resumen de cada uno de ellos, qué es, para qué sirve y cómo se usa. Así que esta parte, más que para hacer test automáticos, sirve realmente para conocer de qué van todas esas siglas que oímos de continuo y a veces no sabemos qué son (jsf, spring mvc, tapestry, jsf, wicket …) y es una primera guía para saber por dónde empezar a usarlas.

Dentro de este grupo de capítulos nos habla de los test de aceptación, que son test automáticos de más alto nivel donde idealmente se considera el sistema como caja negra y se testea desde fuera automáticamente. Idealmente estos test deben estar escritos por el cliente más que por los desarrolladores, puesto que el cliente es el que sabe lo que quiere y si el test es o no suficiente. Así que, aparte de discutir en qué casos se puede/debe testear desde fuera el sistema como caja negra, o cuando se puede/debe testear justo por debajo de la interfaz de usuario, nos introduce en herramientas como fit o fitnesse.

En la última parte, la que he dejado de leer, nos muestra los problemas que podemos tener con nuestros compañeros de trabajo si intentamos convencerlos de que usen TDD, y cómo identificar esos problemas y cómo abordarlos. Pero para mí, programador principalmente, lo de las relaciones humanas no es un libro que me entretenga. Y para mí, cabeza cuadriculada, semejante texto me parece demasiado "etéreo" y evidente. Oír por enésima vez las ya consabidas frases estilo "para que tus compañeros hagan TDD, dales ejemplo haciéndolo tú" o "si te dicen que sí sin entusiasmo igual te están diciendo que no", no me parece que ayuden demasiado a pelearte con los problemas día a día. Este tipo de problemas son problemas que puedes resolver si tu forma de ser es la adecuada para ello, y los resolverás o no independientemente de que hayas leído este libro. Hay quien de forma innata es un lider y que yo sepa, no existe quien de forma innata es un anti-lider y se convierte en lider con un cursillo.

Hablando de cursillos, me ha llamado la atención (creo que tiene toda la razón), este post sobre el peligro de las certificaciones, tan de moda hoy en día http://www.javiergarzas.com/2012/09/problemas-testing.html

Y volviendo al libro otra vez, una frase traducida más o menos libremente que me ha llamado la atención "Los buenos programadores sufren un tipo especial del síndrome de déficit de atención, consistente en poner todo su empeño en usar una herramienta nueva, para abandonarla pocos meses después  y poner nuevamente todo su empeño en otra herramienta más nueva". Real como la vida misma.

Aug 13

Configurando Hibernate para test unitarios.

Cuando hacemos test de JUnit o la herramienta que sea, una de las cosas difíciles de probar son las transacciones con base de datos. En nuestro entorno de desarrollo necesitaríamos una base de datos igual que la de producción y deberíamos borrar todos los datos, meter los datos previos al test para tener la base de datos en un estado conocido, hacer el test y luego revisar el contenido de la base de datos. Esto, aparte de complejo, puede ser muy lento con una conexión a una base de datos real.

Una de las soluciones es utilizar una base de datos en memoria (como HSQLDB, H2, etc). Estas bases de datos suelen ser un jar que no necesita instalación, así que basta con añadirlo a nuestro classpath en los test. Al ser en memoria, los test serán también muy rápidos. Sin embargo, sigue habiendo dos problemas:

  • En cada test habría que crear todas las tablas desde cero.
  • Las SQL de estas bases de datos pueden no ser las mismas que la de nuestra base de datos de producción (MySQL, Oracle, etc).

Para ayudarnos a solucionar estos problemas podemos usar Hibernate (u otra herramienta similar). Si en el fichero de configuración de Hibernate, los de mapeo de clases (o anotaciones) y nuestro código no usamos nada específico de una base de datos (debería ser lo normal salvo casos excepcionales), Hibernate nos hará independientes de la base de datos que usemos (de hecho, ese es uno de sus objetivos). Por otro lado, hibernate tiene un parámetro de configuración hibernate.hbm2ddl.auto en el que le podemos indicar que cree desde cero todas las tablas de la base de datos. Así que Hibernate nos resuelve, en principio, los dos problemas mencionados.

La primera idea que se nos ocurre es tener dos ficheros de configuración de Hibernate (hibernate.cfg.xml), uno para producción con la base de datos real y otro para test. Pero somos buenos programadores y no nos gusta tener cosas repetidas (principio DRY), posiblemente esos ficheros sean prácticamente iguales salvo los parámetros de conexión a la base de datos y el mencionado parámetro hibernate.hbm2ddl.auto, que en nuestro base de datos de test valdrá "create" y en la de producción puede valer algo como "verify".

La solución es simple, basta tener un único fichero de configuración para la base de datos de producción, con toda la parámetrica real. Para los test cargamos ese fichero … y modificamos en código sólo lo que nos interese. Se puede hacer de esta forma

Configuration configuration = new Configuration().configure();
configuration.setProperty("hibernate.connection.url", "jdbc:h2:mem:databaseName");

configuration.setProperty("hibernate.hbm2ddl.auto","create");
SessionFactory sessionFactory = configuration.buildSessionFactory();
 

El método configure() de Configuration leerá el fichero por defecto de configuración (suponemos que es el de producción) y luego, con el método setProperty() podemos "machacar" todas las propiedades que queramos de ese fichero. En el ejemplo sólo hemos cambiado la url de la base de datos y hibernate.hbm2dll.auto, aunque posiblemente deberíamos cambiar también el nombre de la clase del Driver de base de datos, usuario, password, etc.

Ahora sólo nos quedaría poner este código en algún sitio al que nuestras clases de test puedan acceder. Nuestras clases de test obtendrían el sessionFactory usando este código, mientras que el código de producción lo obtendría de la forma habitual, posiblemente a través de un UtilHibernate.java.

Por supuesto, no nos libra nadie de llenar la base de datos en cada test con unos datos conocidos y analizar luego los resultados.

Nos queda un detalle, que es borrar los datos después de cada test. Utilizando la base de datos en memoria como hemos hecho en el código anterior, quedará "viva" y con datos mientras esté arrancada la máquina virtual java. Al ejecutar una batería de test (con mvn test, por ejemplo), la máquina virtual no muere hasta que acaban todos los test, por lo que los datos que unos test van dejando en base de datos quedan para los siguientes test, aunque estén en memoria. Debemos vaciar esos datos después de cada test.

Una opción "pesada" es ir borrando los datos o hacer un "drop" de las tablas, en el orden adecuado para evitar problemas de "contraints". Pero otro método rápido y sencillo consiste en simplemente cerrar el SessionFactory. El siguiente test tendrá que crear un SessionFactory nuevo (con el código de arriba) y así se volverán a crear todas las tablas desde cero. Es decir, nuestras clases de test tendrán los métodos setUp() y tearDown() (o @Before y @After en JUnit 4) de la siguiente forma

public class TestUno extends TestCase {
   private SessionFactory = sessionFactory;

   @Override
   public void setUp() {
      sessionFactory = // Obtener session factory con el codigo anterior
   }

   @Override
   public void tearDown() {
      sessionFactory.close();
   }
   …
}

 

Simplemente un detalle. De esta manera realmente no se borran los datos al terminar en test. En realidad se borran al empezar el siguiente test, ya que el "create" de hibernate.hbm2dll.auto hace exactamente eso, borrar para crear desde cero.

 

Aug 09

Se me han pasado las ganas de jugar con Git submodules

Sigo jugando con Git y lo siguiente que me apetecía probar era el tema de git submodule. Con ellos puedes construir un proyecto/repositorio de Git en el que algunos de los directorios son otros proyectos/repositorios de git. De esta forma tienes un "super repositorio" compuesto de varios pequeños repositorios (aparte de tener su propio código).

Me pongo a ello, a mirar en google cómo se hace y veo que es un poco liado. Finalmente este tutorial me quita las ganas de probarlo. Git tiene unas pequeñas restricciones que hace que la creación de módulos sea un poco laboriosa y "pesadita"

  • En un repositorio bare no se pueden hacer commits.
  • No se puede clonar como submodulo un repositorio vacío.
  • No se puede añadir un submodulo en un repositorio bare.
  • No se puede hacer push si el origin es un repositorio no bare

Con todo esto el proceso de creación es tan complicado como lo siguiente (puedo estar equivocado en algún detalle, sólo lo he leído, intentado probarlo rápidamente y desistido por ser algo pesado)

  • Creas los repositorios que harán de submodulos como repositorios normales (no bare) y haces algún commit en ellos (si es bare no puedes  hacer el commit)
  • Los clonas para convertirlos en bares, pero con git config tienes que tocar manualmente las referencias en la copia, para que el bare no tenga como origen el repositorio normal.
  • Creas el super repositorio como repositorio normal y haces algún commit en él.
  • Convertimos el super repositorio en bare haciendo otro clone y tocando nuevamente las referencias a mano con git config.
  • Hacemos otro clone más del super repositorio bare y entonces añadimos los submodulos en la copia. Hacemos commit y push al repositorio central.

y tras este "simple" proceso está más o menos listo, salvo el detalle de que si un segundo desarrollador clona el super repositorio, tiene que acordarse de ejecutar git submodule init y git submodule update para que los submodulos realmente se clonen … ¿cómo hacemos estos comandos en un hudson por ejemplo? ¿O hudson lo tiene controlado?

Y bueno, si sólo fuera el proceso de crearlos, aun se podía plantear hacerlo (eso se hace sólo una vez en la vida de cada proyecto y lo hace el que crea el repositorio, supuestamente una persona avezada en git). El problema son los "gotchas" que vienen en el tutorial anterior. Indica que siempre hay que acordarse de publicar los cambios en los submodulos ANTES que los cambios en el superproyecto. Me hace a mí que en un grupo relativamente grande de desarrolladores, alguno se va a equivocar con bastante frecuencia. Aunque todo es cuestión de aprender el truco y convertirlo en hábito : "siempre commit y push de los submódulos primero".

Está bien saber que existen los submodulos de git por si algún día hacen falta. De hecho, trabajando en java es algo que echo de menos cuando tenemos muchos proyectos independientes pero que comparten juegos de ficheros no java comunes. Por ejemplo, juegos de iconos, dlls de windows, ficheros de script o ficheros de configuración (xml, properties, etc). La solución de "copiar" todo ese juego de ficheros en cada proyecto es un poco pesada, pero si no se copia es dificil hacer automáticamente un zip de instalación que tenga todos esos ficheros. La solución que se me ocurre es hacer un repositorio git con todo ese juego de ficheros y añadirlo como submodulo en los proyectos que lo necesiten.

En cualquier caso, queda pendiente para más adelante. Ahora me he puesto a jugar con Spring Framework WEB/MVC ;-)

Aug 02

Instalando el servidor de Git en Windows

Jugando toda esta semana, al final he conseguido instalar un servidor de Git en Windows.

La instalación de Git es ahora fácil al haber un instalador de Windows. Eso sí, para ser usado a través de bash para Git o de Cygwin, puesto que la opción de meterlo en el PATH de Windows no está aconsejada.

El git daemon quedó totalmente descartado al no tener autentificación de usuarios y quedarse colgado en Windows cuando se intenta un pull.

Así que me puse a intentar la siguiente opción de servidor, el ssh. Windows no lleva servidor de ssh, así que hay que buscarse uno. Primero probé con servidores específicos de Windows, gratuitos, en concreto FreeSSHd, La instalación muy sencilla y además coge los usuarios de Windows, por lo que lo tengo funcionando rápidamente. Un cliente ssh puede conectarse y obtiene un cmd de Windows remoto. El problema viene al intentar integrarlo con Git. Al ser un cmd de Windows, no tiene en el PATH los comandos de Git, Es más, no necesita los comandos de git que vienen en el directorio bin, sino algunos de los que vienen en el directorio libexec/git-core y alguna dll de bin. Tras copiar los ejecutables concretos y dlls concretas que hacen falta en C:\Windows, ya intenta funcionar el git clone ssh://… … pero sólo lo intenta, da error. Buscando en google, me encuentro con que mucha gente tiene el mismo problema, que nadie da solución y que aparentemente FreeSSHd devuelve algún caracter que no le gusta al protocolo de git y va mal, posiblemente los famosos retornos de carro /r/n o sólo /n, por lo que descarto el servidor FreeSSHd para usar con Git. Busqué otros servidores SSH de Windows, pero no encontré ninguno gratuito o que me convenciera.

Así que intenté OpenSSH para Windows, una instalación mínima de cygwin con OpenSSH para Windows. No conseguí hacerlo funcionar rápidamente, posiblemente debido a algún problema en el PC donde hice las pruebas, así que pasé a la siguiente opción, instalar cygwin con git y openssh. La guía del enlace anterior es una maravilla, exactamente lo que necesitaba y funcionó a la primera, incluso instalando OpenSSH como servicio de Windows e importando los usuarios propios de Windows. Un git clone ssh://  también funcionó bien sin problemas …. pero ya sabemos que esto de la programación no es tan maravilloso como parece y siempre hay algún "pero". El único problema, que mucha gente tiene en google y que nadie parece haber resuelto, es que no se pueden clonar repositorios de otros discos que no sean el C:\. Cosas como ssh://host/D:/repo, ssh://host/d/repo o ssh://host/cygdrive/d/repo no funcionan. Como todos sabemos, es buena costumbre en Windows dejar C:\ para el sistema operativo y meter nuestros datos en otro disco (D:\ por ejemplo), así que esta opción, aunque va con repositorios en C:\, no me convence.

ACTUALIZACIÓN: Lo de usar distinto drive está funcionando. Desinstalé el git de windows e instalé el de cygwin a través del setup.exe de cygwin. Ya lo había hecho al instalar el OpenSSH. Ahora git clone ssh://localhost/cygdrive/e/repo funciona correctamente. Quizás el fallo se debiera a lguna incompatibilidad entre los paths del git de Windows que te bajas de http://git-scm.com/ y el de cygwin. 

Así que me cogí los servidores de Git de este comentario y me puse a ello. Una rápida búsqueda en Google para comparar y ver cómo se instalan en Windows me decidió rápidamente a probar gitblit. Entre esta guía de instalación de gitosis en windows y esta otra de gitblit (apartado GO: Single stack solution, con todas las dependencias integradas y 5 minutos para tenerlo funcionando), está claro. Viendo rápidamente el fichero de configuración de gitblit, veo que soporta LDAP, por lo que puedo intentar ponerlo en el Active Directory de Windows. Dicho y hecho, lo prometido por gitblit es cierto, en cinco minutos está funcionando bien y sin problemas. Sólo desempaquetar el zip, indicarle en el fichero de configuración el path donde están tus repositorios y arrancarlo con doble click. La integración con Active Directory me costó un poco más, pero más por culpa de Microsoft que de gitblit, Active Directory no es exactamente igual que LDAP y me dio algún problema hasta encontrar la solución. (principalmente, si necesitas un usuario para consultar LDAP, el nombre de usuario es estilo "cn=jsmith,ou=usuarios,dc=compania,dc=com", mientras que en Active Directory el usuario es "John Smith", es decir, el nombre "expandido" y sin cn=, ou= ni dc=. El segundo problema el campo email. gitblit espera un campo email y Active Directory (al menos el nuestro) tiene uno mail (sin la e delante). Aguí gitblit lanza una excepción y no va. Afortunadamente, el fichero de propiedades de gitblit permite indicar cómo se llama este campo.

Me he quedado con ganas de hacer alguna prueba más con lo del disco no C:\ de OpenSSH y de probar el servidor scm-manager, pero creo que he jugado bastante, tengo servidor git "oficial" con gitblit funcionando y todavía me queda actualizar Hudson o instalarle el plugin correspondiente para que entienda de git.

Resumiendo, si tienes un servidor en Windows, quieres un servidor Git con los usuarios de Active Directory y no quieres "jugar" mucho tiempo, gitblit es una buena opción.

Jul 27

Jugando con GIT

Tras el post anterior, me quedé con las ganas de ponerme a jugar con Git un poco más en serio, y eso he estado haciendo, a ratos, a lo largo de esta semana.

La primera sorpresa agradable es que ahora, desde la página oficial, existe descarga para windows, con un instalador fácil estilo windows. Me lo descargo e instalo. El instalador te da tres opciones, instalar un bash específico para Git, hacer una instalación paa cygwin o bien, con serias advertencias por parte del instalador, poner los comandos de git accesibles desde una ventana de comandos de ms-dos. Esto último requiere que ciertos comandos de windows, como find.exe, se cambien por otros al estilo unix. Vemos que windows, aunque ya hay instalador fácil oficial, sigue sin estar totalmente soportado, haciéndose necesario el uso de una shelll de unix. Supongo que esto no tiene especial importancia si estás acostumbrado a unix (como es mi caso), vas a usar un TortoiseGIT o directamente el IDE que sea.

Siguiente parte a probar, el servidor. Según la documentación hay tres opciones:

  • git daemon. Es lo más simple, se arranca con un comando y ya está compartido cualquier repositorio git en el servidor. El acceso es de solo lectura, haca falta configurar el arranque si queremos también acceso de escritura. La pega es que tiene ningún tipo de autentificación de usuario, así que cualquier anónimo sería capaz de escribir y borrar en nuestro repositorio sin ninguna restricción. Es ese el motivo por el que no se aconseja esta opción en redes que no sean de confianza. Este servidor se aconseja sólo en modo lectura, puesto que es el más rápido a la hora de hacer descargas (git clone).
  • ssh. Acceso a través de ssh, con una configuración de ssh más o menos estándar. No la he probado y es la opción aconsejada, con la pega de que todos los usuarios deben tener usuario en el servidor.
  • http. Publicar un repositorio en modo lectura desde un servidor http es casi inmediato. La parte de escritura requiere habilitar WebDAV en el servidor apache y la autentificación de usuarios recae totalmente en el servidor apache. Esta es la opción que he usado yo.

Con respecto a la primera opción, git daemon, simplemente comentar que git push se queda colgado (llevar algo al repositorio remoto) haciéndola totalmente inútil. Parece ser un bug del cliente windows que lleva ya un tiempo ahí. La parte de servidor ssh en windows tampoco parece que se pueda poner fácilmente en un windows que no sea "server" (creo que si no es un windows "server" ni siquiera tiene servicio de ssh) y por eso me he tenido que ir a la parte de apache http.

En cuanto a las ramas, me he puesto a jugar con ello y es una pequeña maravilla. La gran mejora respecto a svn no es que haga mejor los "merge" en si mismos, supongo que el algoritmo de merge deben ser parecidos y en ambos casos, si hay conflictos, hay conflictos y se deben resolver a mano. La gran ventajas de Git sobre SVN es que Git lleva automáticamente toda la cuenta de qué ramas se han hecho de qué ramas, qué merges se han hecho y cuando hacemos un nuevo merge, el sabe automáticamente qué ficheros son los que tiene que juntar y desde dónde. SVN no lleva toda esta cuenta, por lo que el primer merge que se hace suele funcionar bien (se hace desde las revisiones adecuadas), pero si más adelante intentamos hacer un segundo merge con las mismas ramas, o de una de estas con otra, svn no sabe exactamente desde qué revisiones partir y debemos decírselo manualmente en el comando. Eso nos obliga a llevar a nosotros, manualmente, un histórico de merges que se han hecho, desde qué ramas, etc, etc. Claramente, si quieres trabajar con muchas ramas y vas a hacer muchos merges, Git es muchísimo mejor opción que Subversion.

En cuanto a facilidad de comandos, sobre todo si se está acostumbrado s Subversion, cuesta acostumbrarse un poco. El principal motivo, aparte de saber o no la sintaxis de los comandos, es que venimos de Subversion con la idea de un único repositorio central. Nuestra copia de trabajo está siempre ligada al repositorio central y los comando svn se hacen siempre contra ese repositorio central. En Git, nuestra copia del repositorio "central" es exactamente igual de importante para Git que la "central". Y pueden evolucionar por separado, yo puedo tener ramas que no están en la central y evolucionan de forma distinta, el "central" puede tener sus propias ramas, llamarse incluso igual que las nuestras, y no tener nada que ver.

El tema va incluso más allá, lo de repositorio "central" es cosa nuestra, realmente no lo hay. Git permite incluso que traigamos cosas de repositorios distintos. Puedo traerme una rama del "central", otra rama del repositorio de mi compañero de trabajo Federico y otra de internet, teniendo así un repositorio local con ramas traídas de N fuentes distintas.

Y las ramas mías y de la central, aunque se llamen igual (o no) y tengan el mismo contenido, pueden no estar ligadas para Git o pueden si estarlo. Si no están ligadas, git las considera independientes y los comandos git push o git pull no funcionarán si no les damos la paramétrica adecuada indicando ambas ramas. Si están ligadas, aunque tengan nombres distintos, git lo sabe y es capaz de ejecutar push y pull sin problemas.

Todo esto añade una complejidad excesiva de conceptos y variedad de comandos, sobre todo para los que venimos de Subversion con un respositorio central en el que están todas las ramas que existen y no hay más. Para empezar a trabajar con Git y sacarle partido a todo esto de las ramas, es necesario hacer un montón de pequeñas pruebas con los comandos para entender exactamente qué hacen, o buscar tutoriales detallados, puesto que la documentación es más bien pobre (es más una documentación de referencia que un tutorial de conceptos).

Ahora probaré el tema de servidor y autentificación con LDAP. Si consigo algo decente, montaré un servidor Git en el curro para alguno de los proyectos pequeños y empezaremos a hacer pruebas para usarlo. Me queda también ver si redmine es capaz de sacar fuentes de Git y si Hudson/Jenkins entiende de Git.

Jul 20

Sobre Git y Subversion

Este post de javahispano me ha hecho pensar de nuevo en Git. Leyendo los comentarios, llegué también a este otro donde cuentan las ventajas de Git sobre Subversion.

Hay un problema que creo que es generalizado y es que cuando se comparan dos herramientas, dos lenguajes dos entornos de desarrollo o dos lo que sea en este mundo de la programación siempre se tiende a decir que esto es mejor que aquello y que todos deberíamos usar esta herramienta. No hay nada más lejos de la realidad, cada herramienta suele tener su propósito y razón de ser, no hay cosas buenas y mejores en términos absolutos, siempre depende de cuales sean nuestras necesidades.

Mi opinión puede estar sesgada puesto que trabajo y me encuentro cómodo con Subversion, mientras que con Git sólo he hecho algunas incursiones (tengo un par de proyectos en GitHub ) y he intentado instalarme algún servidor Git en algún momento. Veamos las ventajas de Git.

Imagina, como es nuestro caso, que somos un grupo de desarrolladores (alrededor de 30) trabajando conjuntamente en proyectos, dentro de una empresa, en una sala todos juntos y con un servidor corporativo en la red interna para alojar el control de versiones (Subversion en nuestro caso). Los proyectos no son proyectos vivos, en el sentido de que el cliente pone unos requisitos, paga un precio, hay un plazo y acabado ese tiempo, el proyecto entra en mantenimiento para corregir errores, pero deja de evolucionar.

Ventajas de Git:

Es más rápido que svn en los checkouts/clone y demás comandos con accesos masivos al servidor, el repositorio ocupa menos tamaño. De acuerdo, no he hecho pruebas, pero no tengo motivos de duda.

Una vez que has hecho tu clone, puedes hacer tus commit en local, aunque no tengas acceso al servidor. Esto es una ventaja si quieres mantener tu histórico de cambios personales y que luego vaya al servidor más adelante. En nuestro caso es poco probable que no tengamos acceso al servidor, salvo que estemos trabajando desplazados. En cualquier caso, sí es una ventaja, aunque le veo una utilidad relativa en nuestro caso donde el 99% del trabajo se hace en la empresa.

Git hace mucho mejor los merges. No he probado, seguramente es cierto porque los de subversion dejan bastante que desear. En cualquier caso, nosotros no solemos hacer demasiados merges, así que en nuestro caso, nuevamente la ventaja sería relativa. Aunque claro, quizás no hagamos merges porque los merges de subversion son un infierno.

¿Y cuales son las pegas?

La primera es que nuestro servidor es windows y tiene un directorio LDAP de usuarios. He buscado tutoriales para configurar un servidor Git en Windows con LDAP y parece que es posible, pero no evidente. Git está pensado para linux y su soporte windows no es muy allá. Las ventajas que pienses conseguir con Git deberían compensar las horas de tu administrador para instalar todo eso correctamente.

Y la segunda pega es que me hace la impresión de que en general hay algo en Git que no gusta a la comunidad. El motivo es la aparición de herramientas como la mencionada en javahispano ( subgit ) o herramientas como git-svn que pretenden hacer una "mezcolanza" entre ambos repositorios. Esto hace pensar que los desarrolladores tienden a no cambiar a git (no ven suficientes ventajas o lo ven demasiado complejo) y es necesario este tipo de herramientas para animarles.

Otras pegas que me he encontrado con git es que no soporta fácilmente subproyectos dentro de un mismo repositorio ni es fácil extraer sólo la parte del repositorio que te interesa. Esta es una  molestia importante en proyectos grandes con muchos desarrolladores donde cada uno está especializado en alguna parte del proyecto. El desarrollador se ve obligado a sacar/hacer clone de todo el proyecto. Por supuesto, es una duda mía, no conozco git y es lo que he encontrado en rápidas buscas en google cuando he hecho pruebas, seguramente alguien con experiencia en git pueda decir que esto es posible y fácil.

Y finalmente, mis busquedas en google tampoco me han dejado claro si git soporta fácilmente el tema de ramas y etiquetas más allá de hacer clones del repositorio. Hacer clones no me parece una solución adecuada para ramas oficiales (por ejemplo, para tener una versión que se ha entregado al cliente y corregir sobre ella los errores que encuentre el cliente). Me obligaría a pensar en el servidor una estructura de directorios para hacer los clones y a ponerlos accesibles de alguna manera a los desarrolladores (directorios compartidos, añadir más directorios al servidor git si es que se puede, etc). Una búsqueda rápida en google sobre ramas en subversion lo deja bastante claro, dentro del repositorio se pone trunk, tags y branches y se hace svn copy. El mismo servidor subversion sin tocar nada tiene todo esto público, incluidas nuevas ramas. Insisto, no conozco git, quizás lo soporta bien pero da la impresión de que los tutoriales que digan cómo no son tan fáciles de encontrar.

Y hay otro artículo que me ha llamado la atención. De alguna forma da a entender que si lo que realmente quieres hacer es trabajar en tu proyecto y usar un repositorio distribuido como una herramienta, casi es mejor usar Mercurial. Git es más interesante, de alguna forma, se convierte más en el fin que en la herramienta, haciendo quizás  que el proyecto en el que estás trabajando sea sólo la excusa para usar Git. Git es adecuado para proyectos de gran envergadura, con múltiples ramas, con procesos de trabajo complejos y que necesitan ser configurados fuera de los estándar que ofrece Mercurial.

Soy bastante friki y me gusta/quiero pasarme a git, pero veo que tendría que tener una, dos o tres semanas libres para aprender a montar todo y organizarlo lo suficientemente bien como para hacer que todo nuestro grupo migre a git de forma cómoda, teniendo acceso a las ramas actuales con el código instalado en los clientes, la rama principal de desarrollo, etc, etc. Y la pega es que no sabría explicar claramente a los no frikis qué ventajas reales van a tener con una única excepción. Si varios desarrolladores se van un período largo a las instalaciones del cliente a desarrollar, pueden montar fácilmente su propia red de repositorios git entre ellos para corregir bugs, hacer merges entre ellos y cuando estén de vuelta, hacer un merge en el repositorio oficial.

Pero resumiendo, posiblemente Git sea bueno para proyectos opensource con muchos colaboradores, donde se hacen muchas ramas experimentales, muchos desarrolladores cada uno a su bola intenta hacer mejoras, se quiere juntar todo con cierta frecuencia, etc. Es decir, proyectos vivos, complejos y con muchos desarrolladores más o menos independientes. Para un proyecto de empresa que requiere un repositorio central con la versión oficial del cliente, donde los desarrolladores están todos ubicados en la misma sala y no tienen tanta libertad para sus desarrollos porque deben limitarse a requisitos concretos en plazos de tiempo concretos, quizás Subversion es más que suficiente.