Tag Archive: Programación


Hoy, he visto este post “Fuga de información en Symfony al chequear requisitos” y me he preocupado al instante, ya que trabajo con este framework y tengo varios proyectos con él. Así que tras realizar un breve análisis de lo que explica en el post, he llegado a la conclusión que esas fugas son producidas por una mala configuración al subirlo al servidor web. Voy a explicar un poco mejor la organización de carpetas y los posibles fallos de configuración que podrían crear este error.

Estructura de carpetas en symfony

Estructura de carpetas en symfony

La imagen anterior muestra la estructura básica de un proyecto en Symfony2. Nos vamos a centrar en dos carpetas. La carpeta web es la parte pública del proyecto, donde tenemos el controlador principal (punto de entrada de todas las peticiones) y todos los recursos estátitcos que utilizarán nuestras páginas (imágenes, css, javascript, etc). Por otra parte, tenemos la carpeta app ,donde se guarda toda la configuración de nuestro proyecto y el archivo check.php. Según la filosofía de seguridad de Symfony la carpeta app NUNCA debería de ser accesible a través del servidor web, si eso ocurriera, sería desastroso ya que ahí tenemos contraseñas en texto plano, logs, etc. Así que cuando publiquemos un proyecto, siempre debermos dejar visible SOLO la carpeta web, para evitar problemas.  Si por desgracia hay que publicar la carpeta principal del proyecto, en principio no habría problema ya que por defecto todos los directorios críticos vienen protegidos mediante un archivo .htaccess con el siguiente contenido:


<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>

Por lo que es importante que nuestro apache permita sobreescribir estas directivas. Por que si no, tendríamos un problema mucho mayor que acceder al fichero check.php.

Fuga de Parameters.yml

Un ejemplo de lo que puede ocurrir con una mala configuración de Apache

Visto todo esto, he caido de que hay otro punto de fuga de información importante. Dentro del directorio web, tenemos el archivo app.php que es el controlador principal, el punto de entrada de todas las peticiones. También tenemos el fichero app_dev.php, también controlador principal pero que nos permite utilizar el entorno de desarrollo. Este entorno nos suele dar varias ventajas cuando estamos desarrollando y depurando un proyecto, una de ellas es la barra de “profiler” que nos permite hacer un seguimiendo de muchisima información.

El profiler en accion

El profiler en accion

Si se accede a esta información en producción, podrian obtener todas las rutas de nuestro proyecto, acceso al phpinfo, las consultas sql que utilicen nuestras páginas, bundles usados por el proyecto y la versión de symfony. Supongo que me dejaré alguna cosa en el tintero, pero cómo veis no es moco de pavo. Así que es importante no subir este fichero a los servidores de producción o modificarlos para incluir alguna restricción. Por defecto, incluyen este pedazo de código:


if (isset($_SERVER['HTTP_CLIENT_IP'])
|| isset($_SERVER['HTTP_X_FORWARDED_FOR'])
|| !in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', 'fe80::1', '::1'))
) {
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}

Que en principio, parece que nos protege. Pero según creo, de esto por ahora sólo son conjeturas, si nuestro servidor está detrás de un proxy, permite saltarse la restricción debido a que una de las condiciones (isset($_SERVER[‘HTTP_X_FORWARED_FOR’))  permite bypasearlo. Por lo que habría que editarlo.

Cómo recomendación final, es importante revisar todos estos puntos cuando tengamos el proyecto en producción para evitar dar demasiadas pistas a posibles atacantes y ahorrarno un disgusto en el futuro.

A estas alturas del partido, podemos encontrar por la red numerosos artículos y opiniones contándonos las bondades de utilizar control de versiones, especialmente Git. Así que he decidido aportar mi granito de arena dando mi opinión de porqué hay que utilizar este fabuloso sistema de control de versiones. Voy a resumirlo en 10 puntos, que creo que cubre  la mayoría de ventajas, por supuesto no son todas, pero en mi opinión creo que sí las más importantes.

  1. Nos proporciona organización: esto es común a cualquier sistema de control de versiones, eso de tener 30 carpetas para diferenciar las versiones sólo nos suele traer dolores de cabeza, pérdida de tiempo y problemas a la hora de entregar un trabajo o ponerlo en producción.
  2. Podemos probar nuestro código de forma más ordenada sin necesidad de crear copias de los ficheros o el proyecto. Codeamos, probamos, que funciona, bien, que no funciona, hacemos un revert y no ha pasado nada.
  3. Nuestro código estará más limpio, ya que con Git no es necesario ir comentando código “por si luego lo uso”. Esto, aparte de ser una mala práctica, luego nos costará más mantener el código, mientras con git siempre podremos ver las versiones antiguas de nuestros archivos y rescatar código eliminado.
  4. Más sencillo controlar los cambios, proyectos y ver el historia. Si instalamos un visor web en nuestro servidor de repositorios, cómo GitLab, o utilizamos GitHub, podremos ver todos nuestros proyectos y navegar de forma sencilla por todas las versiones, ramas, etc.
  5. Las puestas en producción son más sencillas y rápidas, sobre todo hablando de entornos web. Hay gente que todavía utiliza el FTP cómo medio de subir los proyectos, y muchas veces sin comprimir, tardando bastante en subir un proyecto. Aparte de que si has cambiados varios ficheros y se te olvida subir alguno, puedes ocasionar errores en el funcionamiento de la aplicación. Con Git, basta con hacer un pull del proyecto para que suba los cambios casi de forma instantánea y sin dolores de cabeza.
  6. Sirve cómo sistema de copia de seguridad, siempre que lo tengamos en un servidor. Git trabaja de forma no destructiva, así que salvo que utilicemos uno o dos comandos especiales. Nuestra información siempre estará ahí. También donde tengamos un repositorio (servidor y diferentes máquinas), tendremos la historia completa del repositorio. Hay quien piensa que guardandolo en Dropbox o similares tenemos una copia de seguridad. Pero cómo accidentalmente borremos el proyecto de la carpeta, se borarrá entodos los equipos sincronizados. Esto me ha pasado y no fué agradable.
  7. Nos permite fiscalizar mejor nuestro trabajo. Hay veces que necesitamos un historial de los cambios de una aplicación para facturarlos o por otras necesidades. Esto, normalmente conlleva un tiempo buscando correos y papeles con las especificaciones o a veces ni eso ya que las peticiones se hicieron telefónicamente. Si usamos Git podremos ver de un vistazo todos los cambios y cuando se hicieron. Si a esto le añadimos una buena metodología en los mensajes de los commits, aparte del cuándo, podremos saber el qué se cambió.
  8. Compartir nuestro código, Git es ideal para hacer trabajo colaborativo, permitiendo desarrollos en paralelos y si utilizamos GitHub, podemos publicar nuestro código para que la comunidad lo aproveche y nos ayude a mejorarlo.
  9. Trabaja desde cualquier sitio, teniendo un servidor de Git (propio o GitHub) es tremendamente sencillo poder descargarte una versión de tu proyecto en cualquier sitio y trabajar fuera de tu ámbito normal. Esto viene genial para arreglar errores críticos que te pillen fuera de la oficina o por si pierdes o rompes tu equipo.
  10. Estaréis mejor visto laboralmente, actualmente en la gran mayoría de las ofertas de trabajo de programador, están pidiendo que se sepa utilizar un sistema de control de versiones, especialmente Git. Así que dedicándole un par de mañanas, podréis añadir a vuestro curriculum una nueva habilidad que os facilitará encontrar un empleo.

Espero haberos convencido, a aquellos que aún no lo utilicen, para probar este sistema y ver cómo vuestra calidad de vida de programadores.

Llevo unos días cacharreando para montar una pequeña aplicación en Android. Así que tras un par de pruebas con el emulador, llega el momento de utilizar un dispositivo real, así que he cogido un Samsung Galaxy S (GT-I9000) que liberé hace algún tiempo y que tenía la ROM CreedROM_v11 puesta.

El caso esque al conectar  y lanzar la aplicación, no podía ver nada en el LogCat, sólo un error:

Unable to open log device '/dev/log/main': No such file or directory

Así que tras unas cuantas horas buscando por internet y probando cosas, conseguí dar con la solución. Para variar, es algo bastante simple, resulta que el loggin viene desactivado con esta ROM, así que para activarlo, hay que ir a la aplicación SemaphoreManager y activar el módulo Loggin, luego le damos un reinicio al teléfono y ya podremos ver los logs en LogCat y así depurar nuestra aplicación.  Espero que este pequeño tip sea de ayuda para alguién.

Este es el primero de una serie de tutoriales que pretendo publicar acerca de ir creando aplicaciones utilizando JSF 2.1.Intentaré centrarme en cómo se hacen ciertas tareas e ir creando material nuevo que o bien sea difícil de encontrar en nuestro idioma o no se haya publicado nada al respecto en ningún otro idioma. Por ello, pondré enlaces de recomendada lectura que vean que pueden aclarar conceptos, ya que es una tontería volver a publicar algo que esté bien explicado en otro lado.

Cómo primer objetivo, será entender el ciclo de vida y el proceso de creación de una aplicación sencilla en JSF, pero antes de nada, os recomiendo este enlace de adictos al trabajo  en el que se explican todos los conceptos de JSF sin entrar en mucho detalle.

Vamos a comenzar por crear un proyecto, para esta serie de tutoriales usaremos Netbeans 7.2 que ya viene incluido con soporte para JSF 2.1, un contenedor Tomcat 7.0.22 y soporte para Java EE6. Así que creamos un proyecto del tipo web application

Después hacemos las configuraciones del proyecto y pulsando Siguiente, hasta llegar a la elección de que frameworks queremos utilizar, entonces seleccionamos Java Server Faces

Ahora que tenemos el proyecto creado, vamos a crear un pequeño formulario para crear las estadísticas de un personaje de D&D. Necesitaremos  un backend bean, su diseño en facelet (XHTML) y una clase POJO para representar al personaje. Al final del articulo, encontrareis el proyecto que tiene la siguiente estructura:

Podréis observar las impresiones en consola que se hacen en los diferentes métodos, para que veamos y comprendamos mejor el ciclo de vida JSF aplicado al funcionamiento de una aplicación.

Así que vamos a echarle un ojo a la siguiente imagen del ciclo de vida:

ciclo de vida

Cómo podemos ver, tenemos diferentes fases, que en el enlace de adictos al trabajo se explica muy bien:

  1. Restaurar los conponentes de la vista (restore view). En esta etapa el controlador construye en memoria la estructura de componentes de la página.
  2. Aplicar los valores de la petición.. (apply request values). En esta etapa se recuperan los valores de la request y se asignan a los beans de la página.
  3. Procesamiento de las validaciones (process validations). Se verifican los parámetros de entrada según un conjunto de reglas definidas en un fichero de configuración.
  4. Actualizar los valores del modelo (update model values). Los valores leídos y validados son cargados en los beans.
  5. Invocacion a la aplicación (invoke application). Se ejecutan las acciones y eventos solicitados para la página. Si es necesario se realiza la navegación.
  6. Generación de la página (render response). En esta fase se genera la página que será enviada al usuario con todos sus elementos y valores actualizados.

Entonces, cómo afecta esto a nuestro proyecto?  Al  llamar por primera vez a la página, sólo se ejecutan las dos primeras fases, debido a que no hay información de la petición para relllenar el modelo.  Ahora el usuario rellena los datos y pueden ocurrir dos cosas, que haya algún error de validación, en cuyo caso no pasará del paso de validación y no se rellenará el modelo (nunca se ejecuta el setter) ni se ejecutará ninguna acción (no se ejecuta el método save() ). O bien que todo esté correcto, entonces ejecutará el siguiente paso de actualizar los valores del modelo y si no hay ningún error en este paso, se pasarían a ejecutar las invocaciones de los eventos, en este caso, se ejecutaría el método save().

También se puede ver, que el getter en el backend bean, se llama una vez por cada propiedad de la clase POJO, ya que a través de este getter obtenemos la clase POJO, por lo que si lo estuviéramos obteniendo con una consulta a una base de datos o algún otro método costoso, habrá que guardarlo en una variable del bean.

Por supuesto, esto se puede complicar más, porque habrá que tener en cuenta que ocurre cuando hay listeners  de por medio. Pero eso lo dejamos para otra entrada.

El proyecto entero, podeis descargarlo desde aquí.

Después de mucho sin actualizar, ya iba tocando.

Llevo algo más de un mes dandole duro a Symfony 2 otra vez. Esta vez con la versión 2.1, y estoy descubriendo un nuevo mundo utilizando composer para gestionar las dependencias.

Todo aquél que haya comenzado a utilizarlo, sabrá que el repositorio oficial de composer es https://packagist.org/, y aunque tiene un buscador muy bueno, no proporciona suficiente información acerca de los bundles que puedes encontrar. Aparte, de que no es un repositorio exclusivo de bundles de Symfony 2, aunque es lo que más hay.

Así que buscando esta mañana un Bundle para retocar imágenes, he dado con la página http://knpbundles.com/ , que contiene una gran cantidad de bundles (2003 actualmente). Nos da bastante información acerca de cada uno, así como el nombre y enlace para pacakgist (e incluirlo directamente en el composer.json) y la dirección en github. Desde luego , es lo suficientemente interesante cómo para dedicarle unos minutos de nuestro tiempo.

Desde hace unos meses que comencé a enredar con Icefaces 3, me topé con un problema al utilizar uno de los componentes más potentes para formularios. Se trata del componente  ace:dateTimeEntry,  para tener una bonita presentación a la hora de introducir fechas y horas en los formularios. El componente, dispone de dos formatos de presentación, el primero establece el calendario en bruto en la página y el segundo, y más útil, nos muestra un campo de texto que al recibir el foco, muestra un popup con el calendario para introducir la fecha. Un ejemplo de uso de esta esta segunda forma de renderización es la siguiente:

<ace:dateTimeEntry id="cal"
value="#{bean.persona.fechaNacimiento}"
timeZone="Canada/Mountain"
pattern="dd/MM/yyyy"
navigator="true"
yearRange="1940:1980"
renderAsPopup="true"/>

Con el atributo renderAsPopup , conseguimos que se renderice como un popup. Para el resto de opciones, aunque se explican por sí solas, podeis ver cómo funcionan en el showcase de icefaces. Una vez presentado el componente, vamos a ver el problema que me surgía y hasta la semana pasada no he conseguido dar con el origen. Cuando renderizaba el componente de esta forma,  el popup se mostraba, pero al realizar una navegación y volver al formulario, el popup con el calendario dejaba de mostrarse.  Después de mucho darle vueltas al porqué y preguntar en el foro de icesoft(sin mucho éxito), conseguí dar con el problema. Lo primero que hay que tener en cuenta, esque en Icefaces trabaja completamente con Ajax, con lo cual, cuando realizamos una actualización de sección, solo refresca la partede la página que ha cambiado. Para ello, el servidor envia como respuesta un XML con los cambios que ha habido. Entonces, en algunas páginas, esta respuesta, hacía cambiar el body entero, con una respuesta parecida a esta:

<div><partial-response></div>
<div><changes></div>
<update id="javax.faces.ViewBody"><body title=""><div id="header">...

En el momento que aparece <update id=”javax.faces.ViewBody”>, se nos fastidia el popup de dateTimeEntry, debería de aparecer un id de los que hayamos puesto nosotros. Tras revisar y revisar el código, el fallo está en algo bastante tonto, pero no trivial. Resulta que en las plantillas, para organizar las secciones de cada página, lo tenía dividido utilizando divs y algún que otro table, sin asignarle un id.  Y JSF, para detectar los cambios que hay en el DOM cuando cambiamos de navegación, utiliza los id para localizar que parte tiene que sustituir, así que asignandole un id, debería bastar, pero aún así, se dan comportamientos extraños. Por lo que lo mejor, es utilizar las etiquetas propias de JSF para organizar el código, que luego se sustituira en el cliente por un div.

De forma que para arreglar este error y evitar otros posibles funcionamientos erráticos, habría que sustituir (si tenemos algo parecido) esto:


<div id="content">

<ui:insert name="content"></ui:insert>

</div>

&nbsp;

&nbsp;

Por esto otro:


<h:panelGroup layout="block" id="content">

<ui:insert name="content"></ui:insert>

</h:panelGroup>

Y siempre, siempre, siempre, asignarle un id a los panelGroup, para que JSF encuentre correctamente donde tiene que sustituir al hacer cambios en el DOM.

Antes de nada, Feliz año a mis escasos lectores. Últimamente ando enfangado de nuevo con Java y por supuesto, con JasperReport para sacar informes. Con la aplicación que estoy desarrollando actualmente, estoy utilizando Icefaces 3 para JSF, que da muchas facilidades para crear aplicaciones rápidamente y atractivas, tanto visual como funcionalmente. Una de las cualidades de esta versión del framework, esque todo va por AJAX. Esto, al llegar al momento de presentar al usuario los informes creados con JasperReport, supone un problema. Ya que normalmente, nos inmiscuimos en el ciclo de vida de JSF para capturar la respuesta e imprimir nuestro informe, y esto no le sienta muy bien al framework.  Así que tras unas cuantas peleas y cabezazos contra el teclado, he conseguido llegar a una solución. Dicha solución consiste en sacar el informe en otra ventana, fuera del flujo de Icefaces. Y para presentar el informe, utilizo directamente un Servlet para generar la respuesta. Paso a explicar paso a paso.

Lo primero, tenemos en nuestro archivo .xhtml lo siguiente:

<h:commandButton id="pushButtonId12"  actionListener="#{informes.pdfEdificios}"  />

Utilizamos actionListener, para que no haga ningún refresco del contenido. Ahora, el código del método encargado de manejar dicho evento es el siguiente:


public String pdfEdificios(ActionEvent event) throws IOException{
try {
Connection con = edificiosDao.getConnection();
HashMap parameters = new HashMap();
String reportsDirectory = "c:/Users/Usuario/Documents/NetBeansProjects/Proyecto/build/web/";
String jasperName = "report1.jasper";
JasperPrint jasperPrint = UtilReports.generateReport(con, reportsDirectory, jasperName, parameters);
getSessionMap().put("report",jasperPrint);
JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), "window.open('Report', 'myWindow');");
} catch (NamingException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (JRException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException ex) {
Logger.getLogger(Informes.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}

En este método, simplemente generamos el informe mediante UtilReports.generateReport(con, reportsDirectory, jasperName, parameters), una vez hecho, guardamosel objeto JasperPrint a la sesión, para poder recuperarlo después en el Servlet.
Hasta este punto, ya tenemos el informe creado y guardado en la sesión, ahora necesitamos abrir el Servlet automáticamente para llevar el informe al usuario. Para ello, Icefaces nos proporciona JavascriptContext, desde el cual podemos añadir código javascript enviado desde el servidor.
Así que mediante JavascriptContext.addJavascriptCall(FacesContext.getCurrentInstance(), “window.open(‘Report’, ‘myWindow’);”); enviamos el código para que se abra una ventana nueva que lleva al servlet Report. Que habremos creado previamente con el siguiente código para manejar la respuesta:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
JasperPrint report =(JasperPrint) request.getSession().getAttribute("report");
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + "report" + ".pdf;");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, report);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, response.getOutputStream());
exporter.exportReport();
} catch (JRException ex) {
Logger.getLogger(Report.class.getName()).log(Level.SEVERE, null, ex);
}finally{
request.getSession().removeAttribute("report");
}

}

Aquí, recuperamos de la sesión el objeto JasperPrint de nuestro informes y preparamos las cabeceras de la respuesta para indicar el tipo, y utilizamos un exporter para exportar el informe en el formato deseado (normalmente pdf). Por último, es importante poner en el bloque finally, la liberación del objeto de la sesión, para no sobrecargar la memoria del servidor.
Esta es la única forma que he encontrado, de momento, para poder mostrar los informes sin entrar en conflicto con Icefaces. Si alguién tiene una solución mejor, que no dude en comentarlo.

Instalando gitolite

Una vez que tenemos nuestro servidor de git funcionando de forma básica. Pero si queremos darle un uso serio y va a ser utilizado por más de una persona, lo mejor es añadirle un control de seguridad por usuarios. Para eso, podemos utilizar gitolite.
Los requisitos que hay que tener en el servidor:

  •  git 1.6.6+
  • perl 5.8.8+
  • openssh 5.0+
  • Un usuario dedicado para almacenar los repositorios. Como usuario ,vamos a utilizar el que usuario git creado anteriormente.

Cumpliendo estos requisitos, la instalación de gitolite es muy sencilla siguiendo los siguientes pasos:

  1. Nos logueamos con el usuario git.
    $ sudo su - git
    
  2. Hay que asegurarse que el archivo ~/.ssh/authorized_key no existe o está vacio.
    $ rm .ssh -rf
    
  3. Descargamos y configuramos gitolite:
    $ cd /home/git
    $ git clone git://github.com/sitaramc/gitolite
    $ mkdir bin -p
    $ gitolite/install -to ./bin
    $ bin/gitolite setup -pk git.pub
    

    Lo que estamos haciendo es lo siguiente, nos descargamos el proyecto de github, creamos una carpeta bin, donde se van a guardar los ejecutables de gitolite. Yo prefiero hacerlo dentro del home de git, para no guarrear mucho el servidor. Con el comando gitolite/install -to ./bin le indicamos que la instalación la haga en la carpeta que acabamos de crear. Por último, al hacer setup, configura automáticamente gitolite, con -pk git.pub le estamos diciendo que utilice el archivo git.pub, que contiene una clave pública, para hacer el logueo de usuario administrador. Este archivo con la clave pública, deberemos crearlo y pasarselo nosotros. Con esto ya tenemos gitolite configurado para empezar a trabajar.

Creación de la llave pública y privada

Para crear el par llave pública/privada, es tan sencillo como ejecutar:

$ ssh-keygen -t rsa

Nos preguntará donde queremos guardar la clave privada. En nuestro le ponemos el nombre git, para que lo haga en el directorio actual. Nos pedirá una passphrase para proteger la clave privada, en mi caso prefiero dejarla vacia. Nos creará dos archivos nuevo, uno llamado git a secas, que es la clave privada y otro llamado git.pub que es la clave pública. Esta clave pública es la que utilizamos en el apartado anterior. La clave privada, la tendremos que poner en el cliente para poder acceder a la configuración administrativa de gitolite.

Configuración de usuarios y repositorios

Ahora en el cliente, con nuestra clave privada, normalmente en ~/.ssh/id_rsa, hacemos un clone del panel administrativo de gitolite:

$ git clone git@host:gitolite-admin

Si todo está correctamente, hará el pull del proyecto directamente, sin pedir ninguna contraseña. En caso de que nos pida una contraseña, algo estamos haciendo mal con las claves privada/publica.

Dentro del proyecto gitolite-admin hay dos carpetas, config y keydir. En config tenemos el fichero de configuración de gitolite y en keydir, tendremos que poner las claves públicas de los usuarios.

Para configurar gitolite, os dejo el enlace de  la documentación oficial y de un libro de git que a mi me ha servido de mucha ayuda.
Una vez hecho todos los cambios necesarios, haremos un push al servidor y gitolite se encargará automaticamente de actualizar la configuración y el acceso a través de las claves.
Ahora desde nuestro cliente,  podremos ver los repositorios a los que tenemos acceso, así como los permisos:

ssh git@host info

Si pide contraseña, esque hay algún problema con las claves. Si muestra la informaci´no, todo ha ido perfecto y ya podremos trabajar tranquilamente, teniendo el control de seguridad extra, en nuestros repositorios.

Para nosotros los programadores, hay una cosa que puede resultar algo complejo: Crearse un portfolio. Para los artistas y diseñadores, es suficiente con crearse una página simple y unas cuantas capturas de sus trabajos. Pero para un programador, es más complejo, si has hecho una aplicación web accesible al público, puedes poner el enlace. Pero, ¿Y si solo has creado un módulo de una aplicación? ¿ Y sí has colaborado en un proyecto con una pequeña parte? ¿Y si quieres enseñar una aplicación de la universidad?. Incluso a veces, a  un posible contratador, le puede interesar más ver la calidad de nuestro código. Para este tipo de casos, tenemos una opción, que además nos permite recibir feedback de otros usuarios. Para que nos entendamos, una red social de desarrolladores.

Estoy hablando de Github, que aparte de todo esto, nos proporciona un control de versiones, que debería de ser algo obligado para todos nuestros proyectos.
A pesar de sus bondades, tiene una cosa muy buena y una cosa muy mala. La cosa muy buena esque te puedes crear una cuenta gratis, la mala cosa, esque los proyectos que subamos han de ser públicos, salvo que nos hagamos una cuenta de pago. En este último caso, el número de proyectos privados dependerá de la cantidad que paguemos. Aún así, sigue siendo una opción bastante interesante, sobre todo si tenemos algún proyecto interesante en mente, ya que nos abrimos a colaborar con más desarrolladores y eso siempre es bueno.

Os dejo una lista con los enlaces para configurar git de forma sencilla :

Después de un largo tiempo de inactividad, vuelvo a postear. Cambio de trabajo, estudios y directamente, falta de ganas por actualizar. Pero vuelta a la carga, como novedades, he subido mi primera aplicación para iOS al apple store, ya pondré más información en cuanto haya pasado el proceso de aprobación de apple. Por otro lado, he creado un twitter de la página @projectlonginus. Quizás haya alguna novedad más próximamente.

Y como consejo del día, me encontré con el siguiente problema a la hora de crear el distribution de la aplicación de iOS. La solución es bastante sencilla, si sois como yo y no habeis podido esperar y estais trabajando con la beta del ios5 y el xcode con la beta, este no permite firmar aplicaciones para distribución, así que habrá que desinstalar completamente el xcode e instalar la ultima versión no beta. Para desinstalar el xcode es suficiente con la siguiente línea de comando:

<pre>sudo /Developer/Library/uninstall-devtools --mode=all