Mi primer Palermo Valley (pv10)

El jueves se me dio por curiosear un poco en el evento que realizaba la gente de Palermo Valley sin mucha idea de qué se trataba.

Me pareció bueno el emprendimiento para conectarse con gente pero todavía falta un poco más de organización. Prometo que, cuando tenga yo un poco más de tiempo, voy a ofrecerme para colaborar, porque me interesa.

Interesante el anuncio que hicieron sobre el proyecto de unir a todos los valleys de Latinoamerica en un Latam Valley y también sobre la posibilidad de eventos dedicados a proyectos para móviles.

Hablando de móviles, algo que me sorprendió fue haber visto la mayor concentración de iPhones por metro cuadrado en toda mi vida!

Por otro lado, tuve la oportunidad de conocer emprendimientos muy interesantes. Entre las ideas que me parecieron más interesantes están:

  • MapiProp es un portal para búsqueda de propiedades basado en GoogleMaps. No es original la idea, pero lo probé y puedo afirmar que funciona muy bien.
  • WIMIT es un software para teleconferencias y e-learning que parece estar bastante bueno. Es uno de los productos que venimos considerando para nuestro proyecto de e-learning pero que por ahora está pospuesto.
  • Bibliotecasa es un sitio de intercambio de libros físicos. La idea está buena, pero no creo que le vaya a hacer mucha gracia a las editoriales. Me pregunto si será legal. Igual me parece buena la idea y en algún momento voy a probarlo.

En un futuro próximo, me gustaría alguna vez tener la oportunidad de dar un speech sobre algún proyecto propio. Claro que primero debería tener uno 🙂

Para los curiosos les dejo el link al video del evento en el blog de mi amigo Ariel Corgatelli, Infosertec, que hizo la cobertura:http://www.infosertec.com.ar/blog/?p=5803

Mis objetivos para el 2009

No quería adentrarme mucho en este nuevo año sin haber escrito un artículo con mis objetivos para el 2009. Y así llegué hoy a hacerlo, justo con un mes de atraso.

El 2009, como muchos lo pronostican, se avecina como un año muy dificil, debido a la crisis financiera internacional de la que seguramente todos hemos oido hablar. Por suerte, esa crisis todavía no se hace sentir mucho en Argentina, no porque estemos inmunes a ella, como quiere hacernos creer nuestra presidenta, si no porque nuestra situación económica con respecto al resto del mundo solo ha ralentizado los efectos.

Y creo que deberíamos tener un poco más de conciencia de eso y tratar de aprovechar esa ventaja a nuestro favor. La ventaja, concretamente, es que estamos un poco más avisados de que se viene una crisis. En EE.UU. y Europa la crisis los tomó por sorpresa. Aquí todavía no se presentó en la misma medida ni cerca, y podríamos aprovechar la advertencia para hacer algo al respecto.

Me preocupa un poco la aparente despreocupación de mis compatriotas ante este hecho. Parecería que todos están adormecidos. Pero es momento de despertarse, de hacer algo al respecto, de cambiar, de tener un PLAN B, C y D.

En mi caso, como muchos ya saben, trabajo de forma independiente, principalmente con clientes de EE.UU. y España. Por ahora tengo trabajo para entretenerme, aunque sí reconozco que el trabajo bajó. Sin embargo, todavía puedo mantenerme bien porque los años anteriores tenía más trabajo del que podía hacer, y este año tengo lo justo o un poco menos. Mi ventaja es que mi empresa es chica, trabajo desde mi casa y no tengo grandes costos ni empleados que mantener.

Con todo esto en mente es que pensé en la siguiente lista de objetivos para este año:

  • Lanzar alguna aplicación web para venta por suscripción: desde hace 2 años tengo la idea de desarrollar aplicaciones web para venta por suscripción. Tengo en desarrollo tres productos: un administrador de proyectos de software, un sistema de envío de e-mails y una aplicación financiera. Todas aplicaciones web; mi objetivo este año es lanzar al menos una de ellas y empezar a ganar algo de dinero.
  • Darle más importancia al blog: este año quiero darle más importancia a este blog, además de migrarlo a WordPress. Es un buen imán de visitas para mi sitio, aunque no atrae muchos clientes. Sin embargo me sirve para ganar algo de prestigio, escribiendo y dandome a conocer en temas que conozco. Ganar una comunidad de seguidores, aunque no sea un objetivo económico, me resulta tan tentador como ganar un buen proyecto. Es que al tener una comunidad de visitantes asiduos, generalmente programadores, también tengo a donde recurrir en caso de necesitar ayuda para algún proyecto extra grande.
  • Traducir el sitio a inglés: una tarea pendiente que me quedó colgada del año anterior. No puedo obtener clientes de habla inglesa si no tengo el sitio traducido al inglés.
  • Empezar a ofrecer cursos: yo empecé escribiendo en una revista de computación (USERS). Estuve trabajando como Redactor y posteriormente como Secretario de Redacción por 3 años. Por eso, escribir y enseñar es algo que siempre me gustó hacer y algo por lo que creo tener algo de facilidad. Por eso, a mediados del año pasado empecé a pensar en la idea de ofrecer cursos de programación a través de una plataforma de e-learning. No sé si llegaré a implementarlo este año, pero seguramente empezaré a escribir los cursos y a preparar la plataforma.
  • Desarrollar algún producto para vender: hasta ahora siempre vendí mis servicios, pero este año quiero, al menos, desarrollar un producto para vender. Tengo algo ya en mente que estoy por desarrollarlo con Federico y con soporte de un cliente. Espero que funcione.
  • Conseguir más clientes: de todas, esta es una de las más dificiles. Todavía no estoy muy seguro cómo voy a hacerlo, pero es uno de mis objetivos para este año tratar de conseguir más clientes importantes. Me gustaría conseguir, al menos, un cliente importante de Argentina y otro del exterior.

Así que ya me quedan solo 11 meses para tratar de cumplir esos objetivos. Creo que son bastantes, aunque con un poco de esfuerzo y trabajo duro se pueden cumplir. Lamento no tener la ayuda que quisiera para poder llevar a cabo estos proyectos, pero no me voy a dar por vencido porque me gusta lo que hago y lo disfruto. Ya tendré tiempo para descansar cuando sea viejo.

Autenticación de usuarios con ASP.NET

ASP.NET ofrece tres mecanismos de autenticación: Windows, Passport y Formularios. De estos tres me voy a dedicar a explicar autenticación con formularios en detalle y voy a hacer un breve comentario sobre los otros dos.

La autenticación Windows permite al programador delegar la responsabilidad de la autenticación a Windows y al sistema de archivos NTFS, aprovechando también las funcionalidades de seguridad integradas en IIS. Normalmente, cuando un cliente realiza una petición a una páginas ASP.NET o a un web service, estas peticiones son manejadas por IIS. Si configuramos la autenticación en modo “Windows”, IIS delegará la tarea al sistema operativo. El usuario será autenticado en base a las credenciales que proveyó cuando inició sesión en Windows.

La autenticación Passport utiliza un servicio centralizado provisto por Microsoft. Para utilizar Passport hay que registrarse en el servicio, aceptar el acuerdo de licencia, pagar la cuota del servicio e instalar el SDK de Passport.

Finalmente, el método del cual vamos a hablar con mayor detalle es autenticación por formularios. Este método ofrece muchas ventajas al desarrollador, siendo la flexibilidad una de las más importantes. Será responsabilidad del desarrollador diseñar el formulario de inicio de sesión, implementar algún tipo de repositorio para las credenciales de usuario y realizar la validación contra ese repositorio, entre otras cosas.

Para utilizar autenticación por formularios es necesario realizar la siguiente configuración en el archivo web.config:

<configuration>
    <system.web>
        <authentication mode=”Forms”>
            <forms name=”auth_cookie” loginUrl=”Login.aspx” />
        </authentication>

        <authorization>
            <deny users=”?” />
        </authorization>
    </system.web>
</configuration>

Noten que puede haber, además de esto, otras configuraciones actualmente presentes en el web.config.

Con esto, lo que hemos hecho es:

  1. Definir el modo de autenticación.
  2. Denegar la autorización de acceso a los usuarios anónimos (simbolizados con el signo “?”).

Veamos ahora cómo realizar una autenticación sencilla siguiendo los siguientes pasos:

1.Creemos una página de inicio Default.aspx simple, para validar el ejemplo.

2.Creemos una página de login sencilla como la siguiente:

`<form id="form1" runat="server">
<h1>Login</h1>
<asp:Panel ID="panelMensaje" runat="server" Visible="true" EnableViewState="False" Width="100%">
  <asp:Label ID="lblMensaje" runat="server"></asp:Label>
</asp:Panel>
<p>Usuario:<br />
  <asp:TextBox ID="txtUsuario" runat="server"></asp:TextBox> </p>
<p>Password:<br />
 <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
</p>
<p><asp:Button ID="btnLogin" runat="server" Text="Login" OnClick="btnLogin_Click" /></p>
</form>`

3.Utilicemos el siguiente code-behind:

public partial class Login : System.Web.UI.Page
{
  private const string usuario = "jose";
  private const string password = "abc123";

  protected void Page_Load(object sender, EventArgs e)
  {

  }
  protected void btnLogin_Click(object sender, EventArgs e)
  {
      if (txtUsuario.Text == usuario && txtPassword.Text == password)
      {
          FormsAuthentication.RedirectFromLoginPage(txtUsuario.Text, false);
      }
      else
      {
          panelMensaje.Visible = true;
          lblMensaje.Text = string.Format(
            "Usuario {0} no autenticado. Intente nuevamente.", txtUsuario.Text);
      }
  }
}

Con este ejemplo, y el web.config que comentamos antes, debería redirigirnos a la página Login.aspx cuando quisiéramos acceder a nuestra Default.aspx.

Otros parámetros del tag <form> son los siguientes:

  • protection: indica si la aplicación debe usar validación, encriptación, ninguno o ambos métodos para proteger la cookie. El valor predeterminado es All, que utiliza ambos métodos. None indica ningún método. Encryption utiliza Triple-DES para encriptar la cookie y Validation especifica que se valide la información leída en la cookie antes de utilizarla.
  • timeout: expecifica el número de minutos de vida de la cookie.
  • slidingExpiration: valor true o false indicando si la cookie se refresca con cada petición. En ASP.NET 1.1, el valor predeterminad es true y en 2.0 es false.

Autenticación contra el web.config

El ejemplo anterior mostraba cómo autenticar usuarios contra datos fijos en el archivo de code-behind. Otra forma de guardar los datos de los usuarios es en el archivo code-behind.

<configuration>
    <system.web>
        <authentication mode=”Forms”>
            <forms name=”auth_cookie” loginUrl=”Login.aspx”>
                <credentials passwordFormat="Clear">
                    <user name="marcelo" password="abc123" />
                    <user name="pablo" password="zxy987" />
                </credentials>
            </forms>
        </authentication>

        <authorization>
            <deny users=”?” />
        </authorization>
    </system.web>
</configuration>

Guardar los passwords en formato “Clear” no es lo más recomendable. Es posible también guardarlos hasheados en MD5 y SHA1. Para hashear los passwords podemos utilizar el método HashPasswordForStoringInConfigFile() de la clase FormsAuthentication.

Para autenticar de este modo debemos utilizar el método Authenticate de la clase FormsAuthentication. Autenticación contra una base de datos

Este tal vez es el método más común. En este ejemplo se trabaja con una función Authenticate dentro de la página aunque lo más frecuente es tener toda la lógica de autenticación en algún componente separado.

protected void btnLogin_Click(object sender, EventArgs e)
{
    if (Authenticate(txtUsuario.Text, txtPassword.Text))
    {
        FormsAuthentication.RedirectFromLoginPage(txtUsuario.Text, false);
    }
    else
    {
        panelMensaje.Visible = true;
        lblMensaje.Text = string.Format(
            "Usuario {0} no autenticado. Intente nuevamente.", txtUsuario.Text);
    }
}

public bool Authenticate(string usuario, string password)
{
    // defino la consulta (uso parámetros para evitar sql injection
    string query = "select count(*) from usuarios where usuario=@usuario and password=@password";

    // defino la conexión
    SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["ConnectionString"]);

    // defino el command y le asigno los parámetros a la consulta.
    SqlCommand cmd = new SqlCommand(query, conn);
    cmd.Parameters.AddWithValue("@usuario", usuario);
    cmd.Parameters.AddWithValue("@password", password);

    // ejecuto la consulta.
    conn.Open();
    int count = (int)cmd.ExecuteScalar();
    conn.Close();

    // devuelvo true solo si obtuve 1 resultado
    return count == 1;
}

Redirigir a una página específica

Para redirigir a una página específica, distinta de la que podría venir en el parámetro RedirectUrl, primero enviamos la cookie y luego hacemos el redireccionamiento:

FormsAuthentication.SetAuthCookie(txtUsuario.Text, false);
Response.Redirect("OtraPagina.aspx");

Cerrar la sesión

FormsAuthentication.SignOut();

Locations

Por defecto, todos los directorios y subdirectorios, incluyendo archivos, donde se encuentre el web.config estarán protegidos al activar el mecanismo de autenticación. Si necesitamos que alguna ubicación específica tenga permisos diferentes, podemos (1) recurrir a un web.config por cada directorio o (2), mucho mejor, definir elementos en el web.config principal.

<location path="imagenes">
    <system.web>
        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</location>

<location path="estilos.css">
    <system.web>
        <authorization>
            <allow users="*" />
        </authorization>
    </system.web>
</location>

 

 

Haciéndolo Realidad

La razón por la cual este artículo está acá es porque una gran mayoría de los proyectos de software nunca se concluye correctamente. Ya sea por demoras, gastos extra o requerimientos cambiantes, los proyectos de software son muy suceptibles al fracaso, tanto por culpa de los programadores, clientes, o ambos. Por ello vamos a hablar sobre pautas básicas para evitar esta clase de problemas.

“Haciéndolo Realidad” es la traducción del título del libro “Getting Real” publicado por 37signals. El libro describe una excelente metodología para “desarrollar aplicaciones web de manera inteligente, rápida y fácil” y es tan interesante que nos parece que ningún diseñador web debería dejar de leerlo.

Por un lado, los invitamos a leerlo aquí, cuando tengan algo de tiempo libre, pero a continuación vamos a destacar algunos de los ensayos que nos parecieron más interesantes.

Básicamente la filosofía de Getting Real se basa en el desarrollo de software tan sencillo como sea posible. Software elegante e inteligente que haga justo lo que las personas necesitan y nada más. Compartimos la idea de que la mayoría del software es muy complejo. Demasiadas funcionalidades, demasiados botones, demasiada confusión. Las interfases son inentendibles e inusables. Entonces, ¿por qué no concentrarnos en hacer un producto básico excelente en lugar de uno súpercompleto y mediocre?

Si bien existen otras metodologías de desarrollo de software que ya hablan de “construir menos”, como eXtremme Programming, no lo proponen desde la conveniencia del usuario, sino del proyecto y del desarrollador.

Las siguientes son algunas de las propuestas de Getting Real que más destacamos:

Construir menos

Contraimente al pensamiento tradicional, GR propone construir menos que la competencia: menos funcionalidades, menos opciones/preferencias, menos gente y estructura corporativa, menos reuniones y abstracciones y menos promesas. Creemos en esto ya que al construir menos tenemos una puerta abierta para focalizarnos en la escencia del producto que deseamos construir. Y queremos destacar que captar esa escencia no es nada fácil; los programadores tradicionalmente tienden a analizar demasiado y a preveer situaciones futuras que sólo serán necesarias en el 1% de los casos.

Fijar tiempo y presupuesto, Flexibilizar el alcance

Todo proyecto de software comparte estas tres variables: tiempo, presupuesto y alcance. Y la recomendación es que, si tenemos problemas, no invirtamos más tiempo o dinero en él; simplemente reduzcamos el alcance de nuestra aplicación. Lanzar en tiempo y en presupuesto, con alguna funcionalidad menor que lo originalmente planeado siempre es mejor que lanzar un producto mediocre y lleno de bugs tan solo por el hecho de necesitar cumplir con tiempos y presupuestos ridículos.

Que no sea una carga

Este punto nos pareció importantísimo. Nuestra pasión, o la falta de ella, se traslucirá a través de nuestra aplicación. Mientras menos sea una carga, mejor será el producto final.

“Si la aplicación no nos exita, algo anda mal. Si solo trabajamos por el dinero, se notará”.

Menos masa

En cinemática (una rama de la física) aprendemos que cuanto mayor es la masa de un cuerpo en movimiento, mayor energía necesitamos para detenerlo o cambiar su dirección. Esto también es cierto en el mundo del desarrollo de software.

Los cambios son todo un tema para la ingeniería del software y son una de las tareas que más disgusta a los integrantes de un proyecto. Y es cierto, a nadie le gusta cambiar lo que hizo, sobre todo si invirtió un tiempo y esfuerzo considerables en realizar la tarea original.

Para que esto no se convierta en un problema, los cambios simples y baratos. Si un cambio no resulta simple y barato significa que no estamos llevando el proyecto por el camino correcto, y tal caso sería bueno revisar qué estamos haciendo mal. Si no podemos hacer cambios de esta forma, perderemos terreno ante un competidor que sí puede.

La masa aumenta con:

  • Contratos de largo plazo
  • Staff numeroso
  • Decisiones permanentes
  • Reuniones acerca de otras reuniones
  • Un proceso denso
  • Inventario (físico o mental)
  • Estar limitado un hardware, software, o tecnología concretos
  • Formatos de datos propietarios
  • El pasado rigiendo el futuro
  • Planes de acción a largo plazo
  • Políticos de oficina

La masa la reducen…

  • Forma de pensar “Just-in-time”
  • Miembros del equipo multitarea
  • Aceptar las limitaciones, no tratar de aumentarlas
  • Menos software, menos código
  • Menos funcionalidades
  • Tamaño de equipo pequeño
  • Simplicidad
  • Interfaces simplificados
  • Productos de código libre
  • Formatos de datos abiertos
  • Una cultura accesible que haga fácil admitir errores

Las ventajas de tener menos masa son importantísimas: nos permite cambiar de dirección rápidamente, focalizarnos en en las buenas ideas y descartar las malas. En fin, nos permite estar siempre un paso adelante de nuestros competidores.

Reducir el costo del cambio

Como venimos diciendo, la posibilidad de implementar cambios rápidamente nos permite diferenciarnos de nuestros competidores y estar un paso delante de ellos. Por esto es importante reducir todos los obstáculos que puedan impedir cambiar rápidamente. Rápido y barato es un arma secreta que las grandes empresas, con docenas de empleados jamás van a poder tener.

Ignorar los detalles tempranos

Los detalles son importantísimos, pero cuando estamos en la etapa inicial de un proyecto, lo importante es concentrarse en lo escencial, disponer los elementos que van en la página y hacerlo andar.

Si empezamos por preocuparnos por qué colores, qué fuente y qué tamaño tendrán los títulos de nuestras páginas, empezaremos a “desgastarnos”. Los detalles llevan tiempo y no son los escencial del proyecto.

Sin embargo, sí queremos decir que consideramos los detalles importantísimos para la calidad final de un proyecto. Un producto en donde todo funciona perfectamente, pero en el cual no se tuvieron en cuenta los detalles, es un fracaso. Si no es un problema, no te hagas problema

Es interesante esta filosofía. Muchos dirían que es poco profesional no adelantarse a los problemas. Pero es cierto que si tenemos “poca masa” que manejar, solucionar los problemas cuando aparezcan será fácil y barato.

Y creo que eso es lo que debemos tener en cuenta. Si bien no se habla de esto en Getting Real, es bueno tener una idea de los riesgos y problemas que podemos encontrar en el camino y construir de forma tal que, llegada la necesidad, podamos implementar la solución correcta rápidamente. Pero ciertamente no es válido construir un castillo cuando solo necesitamos un departamento de tres ambientes.

Simplemente, “¡NO IMPORTA!”

Cuando debemos indicar la fecha y hora de creación de un mensaje en un foro, ¿es importante indicar toda la información, por ejemplo, “11-10-07 14:54”? Simplemente, toda esa información no importa, y sería más útil para los usuarios indicar, en su lugar, “ayer” o “hace 3 días” o bien “hace 2 horas”, por más que no sea tan preciso como el caso original. Solo preocupémonos por la información que es realmente importante. Empezar con el “No”

Getting Real afirma, y con razón, que cada vez que decimos “sí” a una nueva funcionalidad estamos “adoptando a un niño”. Y tenemos que llevar a ese niño por toda una cadena de eventos hasta su madurez (análisis, diseño, implementación, pruebas, etc.).

Una de las cosas más importantes que aprendimos es que nunca debemos “regalar” funcionalidades no solicitadas en nuestros productos. Si un cliente no pidió la posibilidad de editar la lista de países que aparecen en los menúes desplegables, por más que nos parezca súper simple hacerlo, no lo hagamos. Una vez que le dimos esa funcionalidad al cliente, tendremos que mantenerla.

Cada funcionalidad debería poder probar por sí misma que es valiosa. Por ello, seamos selectivos, pongamos filtros, si una funcionalidad es realmente importante, podrá atravesarlos.

¿Podemos sostenerlo?

Este ensayo me encanta, personalmente porque lo he comprobado y aplicado en muchísimos aspectos de mi vida también. Sólo hagamos aquello que podemos sostener. Esto implica descartar muchísimas cosas, pero al hacerlo concientemente, y sabiendo por qué, no nos sentiremos mal. Por el contrario, sabremos que estamos haciendo lo correcto.

Muchas veces en nuestra vida empezamos algo con muchísimo entusiasmo y luego lo abandonamos. Puede ser cualquier cosa: clases de música, el gimnasio, llevar una lista de los gastos diarios, etc. Mi consejo es no empezar ninguna de estas actividades si no estamos seguros que vamos a poder continuarlas. Abandonar algo que se inició con entusiasmo es mucho más deprimente que nunca haberlo empezado, porque no ibamos a poder mantenerlo.

Entregas rápidas y frecuentes

En las metodologías de desarrollo iterativo, como RUP o XP, se conoce con el nombre de iteraciones a las partes en que se divide un proyecto de software para tener porciones más pequeñas y fáciles de manejar. Las iteraciones son una excelente herramienta, y es importantísimo que las iteraciones sean cortas y que entreguen un subproducto que haga algo importante y que funcione tan bien como un producto final.

Las iteraciones nos permiten concentrarnos en una funcionalidad (o en un conjunto de ellas) por vez. Por ejemplo, si tenemos que hacer un sistema de facturación muy grande, la primera iteración sería la creación de un módulo de clientes. De esta forma sólo deberíamos construir un subproducto muchísimo más reducido el cual podríamos entregar al cliente en no más de dos semanas para validar la dirección del proyecto.

De la idea a la implementación

En este ensayo Getting Real aconseja seguir el flujo ideas-bosquejos-interfaces-programación. Y ciertamente, funciona. Dibujar bosquejos en papel a partir del análisis de las ideas, luego diseñar las interfaces pensando en las necesidades de las personas y recién después pasar a implementar la programación, lo que incluye también el desarrollo de la base de datos.

La mayoría de los programadores suele empezar diseñando e implementando la base de datos o el modelo de dominio, dejando la interfaz para el final. Creemos que esto está mal ya que se le suele restar importancia a la parte más importante del programa: la interfaz. Tengamos en cuenta que nuestro producto no va a ser utilizado por expertos en bases de datos o software. Va a ser utilizado por vendedores, contadores, ingenieros industriales, arquitectos, en fin, personas. Y el punto de entrada de estas personas con nuestra aplicación será la interfaz. La interfaz es el “cuello de botella” de nuestra aplicación.

De nada sirve haber optimizado una consulta para que en lugar de traer los resultados en 3 segundos lo haga en 1 segundo. Si el usuario tarda más de 2 segundos en entender o acordarse cómo había que hacer para obtener la información que necesitaba, no servirá de nada. Y si los usuarios no se sienten exitados utilizando nuestro producto, podemos considerarnos fracasados.

Evitar las preferencias/opciones

Tan simple como eso. Limitar las preferencias en un producto al máximo lo harán más simple de utilizar.

Tiempo a solas

Getting Real resalta que es muy importante darnos cuenta cuál es el momento del día en que podemos ser más productivos. Muchas veces, durante el día sufrimos interrupciones (el teléfono, alguien que toca el tiembre, nuestra familia, etc.). Ciertamente, para ser más productivos necesitamos de un período de tiempo continuo sin interrupciones. Puede ser después de cenar, a la mañana muy temprano, después de medianoche, etc. Lo importante es encontrar un período de al menos cuatro horas en donde podamos programar sin interrupciones. Y personalmente confieso que da mayores resultados trabajar cuatro horas así que siete u ocho en una oficina rodeado de distracciones.

Buscar y celebrar pequeñas victorias

Sobre este ensayo puedo decir que, personalmente, suelo “premiarme” con un café o una salida cada vez que obtengo una pequeña victoria, como ser una nueva funcionalidad implementada, una mejora a una funcionalidad ya existente, una simplificación en el programa, etc.

La interfaz en primer lugar

Ya hablamos de esto antes, pero Getting Real dedica un ensayo especial sobre su forma de diseñar las interfaces en primer lugar. Nosotros también trabajamos del mismo modo ya que la interfaz es lo que el usuario verá en primer lugar y, si la dejamos para el final, ciertamente se notará en la calidad del producto terminado.

Diseño desde el epicentro

El objetivo de esto es centrarnos en la escencia de la interfaz. Sabemos que toda página web, por ejemplo, tiene un encabezado, barra de navegación, pie de página, etc. Obviamente, también deberemos definir los colores, las tipografías, la disposición del los elementos, etc.

Pero empecemos por el epicentro. Si estamos diseñando una página para registro de nuevos usuarios, hagamos sólo eso: un formulario para registrar nuevos usuarios. Y esto no implica modificar la fuente ni los colores predeterminados del navegador ni la necesidad de meter toda la página dentro de una tabla (créanme, lo he visto).

Solución para los tres estados

Algo que muy pocos saben o tienen en cuenta es que la mayoría de las aplicaciones, sobre todo hablando de aplicaciones con bases de datos, tienen tres estados:

  • Vacío: Este es el estado inicial de toda aplicación. Cuando los usuarios utilicen nuestro programa o sitio web por primera vez, lo verán vacío, sin datos, sin información. Y este estado es tan importante que es el que les dará la primera impresión a los usuarios. Por ello, debemos pensar cuidadosamente qué se mostrará cuando el programa se inicie por primera vez o cuando alguna pantalla no tenga datos.

    Por lo general se suele dejar una tabla vacía o colocar un mensaje de “0 productos”, pero esto no es muy estimulante para los nuevos usuarios. Muchas aplicaciones web, por ejemplo, han elegido resolver este estado inteligentemente mostrando una imagen, con una marca de agua que dice “EJEMPLO”, de cómo se vería esa página si tuviera datos.

    Otra alternativa sería no intentar mostrar una pantalla de listado de “cero productos” sino que, directamente, mostrar el formulario para agregar el primero a la lista.

  • Regular: Este es el estado para el que solemos diseñar generalmente. Es el estado en el cual se encuentra una pantalla cuando ya se ha empezado a trabajar en ella y ya tiene información.
  • Error: Finalmente, es importantísimo diseñar para cuando las cosas anden mal. ¿Qué tipo de errores vamos a informar? ¿Dónde vamos a mostrar los mensajes de error? ¿De qué forma? ¿Qué pasará con el resto de la página si hay un error? ¿Y los datos que cargó el usuario en el formulario?

Ciertamente todavía hay mucho más Getting Real para leer, pero quisimos solo destacar lo escencial para lo que nos compete. No dejen de leer este excelente trabajo.

Primeras impresiones de CakePHP

Uno de los primeros artículo de este blog hablaba de mis primeras impresiones sobre Ruby on Rails. Ahora voy a hablar sobre otro framework MVC que estuve investigando y utilizando en los últimos días. Se trata de CakePHP, como podrán intuirlo, para PHP.

Llegué a CakePHP porque me gustó mucho la filosofía detrás de Ruby on Rails, pero no tenía tiempo de ponerme a aprender un lenguaje nuevo, como es Ruby. Me pregunté entonces si no existiría algo similar en un lenguaje que conociera, como PHP. Así fue como encontré CakePHP. También encontré otros frameworks MVC para PHP, que no tuve tiempo de investigar, como por ejemplo, el francés symfony.

Creo que no me equivoco si digo que CakePHP es una copia de Ruby on Rails. Las funcionalidades son idénticas, así como el nombre de muchos de los métodos. Pero bueno, por lo menos, podemos programar con PHP al estilo Rails. De hecho, este mismo blog lo desarrollé con CakePHP en tan solo un par de horas.

Sin embargo, CakePHP tiene un problema grave: la falta de buena documentación y ejemplos. Creo que por más bien que funcione, si falta eso, es una gran contra. CakePHP tiene un manual muy básico y, a mi parecer, muy mal organizado. El manual no llega a cubrir con un nivel aceptable todas las características del framework y remite casi todo el tiempo a la documentación de la “API”. Pero la documentación de la API es todavía peor, por no decir casi inservible. Dificil y oscura para navegar, y sin ejemplos que ilustren el uso de los métodos, se hace imposible entender cómo hacer ciertas cosas.

Sin dudas, CakePHP es una buena alternativa a Rails, para programadores de PHP, pero una documentación tan pobre y un manual casi muerto hacen que realmente pueda llegar a ser más fácil aprender un muy buen lenguaje nuevo, como es Ruby y un framework mucho mejor documentado, y con mayor soporte de la comunidad, como esRuby on Rails.

 

Referencia de LLBLGen Pro

Esta es una referencia rápida de LLBLGen Pro a la que podrán acceder para consultar la forma de realizar tareas básicas con este framework.

La documentación la pueden bajar acá. Está disponible en varios formatos.

La clave para empezar a usar la documentación esta rápido es leer el capítulo “Tutorials and Examples -> How do I … ?”

Luego, para ahondar más en conceptos teóricos pueden leer ” Using the generated code”. Cuando habla de los dos modelos de código soportados, SelfServicing y Adapter , el que nos interesa a nosotros es el primero, ya que es el que vamos a usar.

El trabajo con LLBLGen Pro se divide en dos procesos:

  1. Generación de código: LLBLGen Pro es una aplicación de escritorio para generar código. Resumiento, este proceso es así: se crea un proyecto, se le dice con qué base de datos se va a trabajar, el programa lee la estructura de la base de datos y genera un proyecto de tipo “librería de clases” en .NET. Este paso ustedes NO lo van a necesitar hacer ni aprender, ya que la base de datos y el código lo voy a generar yo (obviamente, si les interesa aprenderlo o necesitan modificarlo, no hay problema; yo siempre subo el proyecto de LLBLGen Pro al SVN).
  2. Uso del código generado: Una vez generado el proyecto de acceso a datos y agregado a la Solución de VS en la que van a trabajar, ya pueden utilizar el código generado. El código generado contiene varias clases, pero las que más van a usar ustedes con las “EntityClasses” y las “CollectionClasses”.

El uso del código generado es muy sencillo. Les paso algunos ejemplos (también se los adjunto).

NOTA: Para diferenciar mejor cuáles son palabras que vienen del framework de LLBLGen Pro, las que creo yo son en castellano. Por ejemplo, con “PersonaEntity” se pueden dar cuenta que yo creé una tabla llamada Persona y al generar código, me creó la clase con el nombre de la tabla + el sufijo “Entity”.

Guardar un nuevo registro en la base de datos:

PersonaEntity persona = new PersonaEntity();

persona.Nombre = "Marcelo";
persona.Apellido = "Ruiz";

persona.Save();

Traer un registro de la base de datos

int idPersona = 1;
PersonaEntity persona = new PersonaEntity();
persona.FetchUsingPK(idPersona);

Traer un registro de la base de datos (otra forma)

PersonaEntity persona = new PersonaEntity(IdPersona);

// No estoy seguro qué pasa si el IdPersona no existe,
// pero eso verifico que no sea nuevo.
if (!persona.IsNew)
{
    Console.WriteLine("Nombre = "+ persona.Nombre);
}
else
{
    Console.WriteLine("Persona no encontrada");
}

Actualizo un registro

PersonaEntity persona = new PersonaEntity();
if (persona.FetchUsingPK(1))
{
    persona.SegundoNombre = "Hernan";
    persona.Save();
}
else
{
    Console.WriteLine ("Persona no encontrada");
}

Borrar un registro

PersonaEntity persona = new PersonaEntity(IdPersona);
persona.Delete();

Filtros

PersonaCollection personas = new PersonaCollection();

// quiero un filtro equivalente al SQL "WHERE Edad > 18 AND Edad < 40"
IPredicateExpression filter = new PredicateExpression();
filter.Add(PersonaFields.Edad > 18);
filter.AddWithAnd(PersonaFields.Edad < 40);

personas.GetMulti(filter);

// recorro la colección:
foreach (PersonaEntity persona in personas)
{
    Console.WriteLine (persona.Nombre +" "+ persona.Apellido);
    foreach (TelefonoEntity telefono in persona.Telefonos)
    {
        Console.WriteLine(telefono);
    }
}

Filtro y ordenamiento

PersonaCollection personas = new PersonaCollection();

IPredicateExpression filter = new PredicateExpression();
filter.Add (PersonaFields.Edad > 18);
filter.AddWithAnd(PersonaFields.Edad < 40);

ISortExpression sorter = new SortExpression();
sorter.Add(PersonaFields.Name | SortOperator.Ascending);
sorter.Add(PersonaFields.Apellido | SortOperator.Ascending);

personas.GetMulti(filter, 0, sorter);

// recorro la colección:
foreach (PersonaEntity persona in personas)
{
    Console.WriteLine(persona.Nombre +" "+ persona.Apellido);
    foreach (TelefonoEntity telefono in persona.Telefonos )
    {
        Console.WriteLine(telefono);
    }
}

Otro filtro más elaborado

PersonaCollection personas = new PersonaCollection();

// quiero un filtro equivalente al SQL "WHERE ((Edad < 18 OR Edad > 40) AND Nacionalidad = 'Argentino')"
IPredicateExpression filtroCompleto = new PredicateExpression();

IPredicateExpression filtroPorEdades = new PredicateExpression();
filtroPorEdades.Add(PersonaFields.Edad < 18);
filtroPorEdades.AddWithOr(PersonaFields.Edad > 40);

filtroCompleto.Add(filtroPorEdades);
filtroCompleto.AddWithAnd(PersonaFields.Nacionalidad == "Argentino");

personas.GetMulti(filtroCompleto);

// recorro la colección:
foreach (PersonaEntity persona in personas)
{
    Console.WriteLine(persona.Nombre +" "+ persona.Apellido);
    foreach (TelefonoEntity telefono in persona.Telefonos)
    {
        Console.WriteLine(telefono);
    }
}

Guardar datos asociados de un solo paso:

PersonaEntity persona = new PersonaEntity();
persona.Nombre = "Marcelo";
persona.Apellido = "Ruiz";

TelefonoEntity telefono1 = new TelefonoEntity();
telefono1.Numero = "4567-1234";
telefono1.Descripcion = "Casa";

TelefonoEntity telefono2 = new TelefonoEntity();
telefono2.Numero = "4000-7896";
telefono2.Descripcion = "Trabajo";

persona.Telefonos.Add(telefono1);
persona.Telefonos.Add(telefono2);

// Guardo con "true", para indicar que guarde tmb los
// datos relacionados (telefonos).
bool resultado = persona.Save(true);
if (resultado)
  Console.WriteLine("OK");
else
  Console.WriteLine("Error");

Limpiar la cache de CakePHP al actualizar Modelos

Uno de los errores más frecuentes que se suelen ver al trabajar con CakePHP es que, por más que actualicemos nuestra base de datos (por ejemplo, agregando nuevos campos), los modelos no se ven actualizados.

Esto se debe al hecho de que CakePHP cachea los modelos en la carpeta app/tmp/cache/models.

Cuando estamos en modo development, este no es el caso. El problema aparece en producción. En estos casos lo que tenemos que recordar siempre es borrar el contenido de esa carpeta para forzar el refresco de los modelos.

Es importante recordar también que toda la carpeta tmp y sus subcarpetas necesitan permisos de escritura.

Impresiones de Ruby on Rails

En este artículo voy a comentar las primeras impresiones que me dio Ruby on Rails, visto desde la óptica de alguien que viene de programar aplicaciones web en ASP, PHP y ASP.NET.

Ruby on Rails es un framework para el desarrollo de aplicaciones web con basadas en bases de datos. Se trata de un framework “RAD” (Rapid Application Development) que nos permitirá crear aplicaciones web totalmente funcionales en un tiempo mucho menor que utilizando otras tecnologías, por ejemplo, ASP.NET.

Ruby on Rails (RoR) me sorprendió de entrada con características muy ventajosas que me hicieron preguntar por qué en ASP.NET o en PHP tengo que escribir hasta 10 veces más código para lograr lo mismo.

La respuesta no es “porque RoR es mejor”. El tema es que RoR está muy enfocado en una dirección, y es “el desarrollo de aplicaciones web basadas en bases de datos”. RoR también se maneja mucho con ciertas convenciones que de alguna forma nos limitan en esa dirección, pero nos permiten lograr resultados más rápido.

Un ejemplo de estas limitaciones es que RoR espera que cada una de las tablas de nuestra base de datos tenga un campo autonumérico definido como la primary key llamadoid. Si cumplimos con esa convención, RoR promete hacernos las cosas más fácil.

Por supuesto, RoR permite ciertos deslices de esas convenciones, pero pagando el costo de mayor cantidad de código.

Sinceramente no me puse a investigar qué pasa si queremos trabajar con modelos de datos un poco más complejos. Por ejemplo, si queremos modelar herencia en la base de datos, algo que suelo utilizar bastante. Otra cosa que noté es que las tablas de asociación de muchos-a-muchos no se modelan en RoR, es decir, no son representadas como objetos en el modelo; con esto, no sé qué pasaría si yo quiero agregar algún campo extra a estas tablas para calificar una asociación. Sinceramente son cosas que tengo que investigar.

Patrón MVC

Ruby on Rails implementa el patrón MVC. Por alguna razón este patrón está de moda. Aunque, pensandolo bien, es un patrón que funciona MUY bien para aplicaciones web basadas en datos.

No voy a explicar el patrón MVC aquí, solo voy a explicarles cómo lo implementa RoR, ya que me costó un poquito entenderlo.

El Modelo (la M de MVC), en Ruby on Rails contiene clases que representan a las tablas de nuestra base de datos. Una característica destacable es que nosotros creamos estas clases pero no es necesario definir los atributos para acceder a los campos de las tablas que representan. Rails infiere esta información automáticamente a partir del nombre de la clase (para esto sirven las convenciones).

En las clases del Modelo podemos, además, agregar otra lógica propia de cada clase. Por ejemplo, si tenemos una clase Product, en principio, se vería así:

class Product < ActiveRecord::Base

end

Supongamos que queremos agregar código para calcular el precio con impuestos. En ese caso, la clase Product es el lugar indicado para hacerlo.

class Product < ActiveRecord::Base

  def price_plus_taxes
    price * 1.21 
  end

end

También podemos definir métodos estáticos:

class Product < ActiveRecord::Base

  def price_plus_taxes
    price * 1.21 
  end

  def self.find_available_products
    Product.find_by_available(1)
  end

end

Una de las confusiones que tuve fue con el tema de los Controllers y las Views (C y V en MVC). Creí erróneamente que también debe existir un Controller por cada tabla y una carpeta dentro de View por cada tabla. Si bien es bastante común esto no tiene por qué ser así.

Los Controllers son clases que agrupan un conjunto lógico de páginas. Por ejemplo, páginas para listar y visualizar artículos. En ese caso tendríamos un Controller llamadoArticlesController con métodos list (para listar) y show (para visualizar). A su vez, tendríamos una carpeta articles con plantillas que llevarán los mismos nombres que los métodos: list y show.

Sin embargo, la forma en que manejamos las entidades de nuestro modelo en nuestros controllers es totalmente libre. Solo tenemos que tener en cuenta la relación que hay entre Controllers y Views, como mencioné arriba.

Características principales de Ruby on Rails

Hasta aquí hice una breve presentación de RoR y expliqué cómo está implementado el patrón MVC. Voy a hablar ahora de las características que más destaco de MVC, sobre todo porque me encantaría tenerlas en otros lenguajes.

En general, encuentro que las características que más me atraen de Rails son aquellas que facilitan el trabajo del programador. Creo que Rails sigue la misma filosofía de Ruby, de programar para seres humanos y no para máquinas. Las características de Rails están pensadas para el mundo real; se pensó en cuáles son las necesidades cotidianas más comunes de cualquier programador de aplicaciones web y se implementaron soluciones realmente inteligentes.

Mapeo automático

Al crear una clase de tipo Model, Rails automáticamente infiere las propiedades de la misma, mapeandolas a los campos de la tabla de la base de datos. Rails sabe a qué tabla corresponde cada clase del Model gracias a las convenciones de nombres.

Lo mejor de esto es que, al agregar un nuevo campo a una tabla, no hace falta tocar nada en el Model; la nueva propiedad estará disponible automáticamente.

Hasta el momento no se me ocurre si esto es posible de hacer con .NET. Tal vez se pueda utilizando un poco de Reflection, pero lo dudo; y si se puede, debe ser difícil. Claro, una diferencia fundamental entre Ruby y el CLR de .NET es que Ruby tiene un soporte nativo para meta programación y los lenguajes de .NET son compilados, lo que hace que esto sea un poco dificil.

Validación

Otro ejemplo de una solución inteligente. Mientras que en frameworks como ASP.NET o incluso en CakePHP (otro framework MVC, pero para PHP) las validaciones se realizan a nivel de interfaz de usuario, en Rails se definen las reglas de validación en el Modelo.

Validar que el nombre y precio de un producto no estén vacíos, y que el precio sea un número válido, es tan fácil como agregar esta línea al modelo:

class Product < ActiveRecord::Base
  validates_presence_of :title, :price
  validates_numericality_of :price
end

Automáticamente Rails va a impedir que guardemos datos que no cumplan con dichas validaciones, y nos va a mostrar los errores correspondientes.

ASP.NET ofrece los controles de validación. Pero configurarlos y usarlos no es tan simple. Además, debemos implementarlos cada vez que trabajemos con la entidad.

Funciones find

Leer registros de la base de datos para llenar objetos o colecciones es por lejos una de las tareas más comunes. Para facilitarnos esta tarea, Rails tiene muchas variantes de la función find. Por ejemplo, todas las clases del Modelo tienen una función find para traer todos los registros de su tipo. Por ejemplo:

Product->find(:all)

Devolverá un array con todos los objetos Product de la base de datos.

Product->find(:first)

Devolverá el primer objecto Product.

Esta función acepta también más argumentos para definir un poco mejor los resultados que queremos y cómo los queremos ordenados. Por ejemplo:

Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)

Traerá todas las Person que cumplan con la condición del segundo argumento.

Pero donde esto se pone realmente interesante es cuando llamamos a funciones como:

Product.findByPrice(9.99)

Para traer todos los productos cuyo precio sea 9.99.

Wine.findByType('Malbec')

Para traer todos los vinos cuyo tipo sea Malbec.

Todas las funciones findBy<campo> son automáticamente generadas por Rails en tiempo de ejecución, gracias a las características de meta-programación del lenguaje Ruby.

Esto nos puede ahorrar realmente muchísimo trabajo. ¡Piensen en la cantidad de veces que necesitaron recurrir a funciones de este tipo! Si bien en otros lenguajes podemos construir funciones del tipo findBy<campo>, para evitar la repetición del código, de todas formas tenemos que escribirlas al menos una vez. En Rails, ya están todas “escritas”.

Formularios

Otra de las características destacables de Rails es el manejo de formularios asociados a entidades del Modelo. Mediante el método form_for, creamos un formulario HTML que estará asociado a una entidad determinada:

<% form_for :person, @person, :url => { :action => "update" } do |f| %>
First name: <%= f.text_field :first_name %>
Last name : <%= f.text_field :last_name %>
Biography : <%= f.text_area :biography %>
Admin?    : <%= f.check_box :admin %>
<% end %>

En el ejemplo, creamos un formulario para la entidad Person (esto lo definimos mediante el símbolo :person. La data se la pasamos en la variable @person. Y en el tercer argumento definimos qué acción ejecutar (en este caso, update).

Vean que luego utilizamos métodos especiales para crear los campos del formulario. Al utilizar los mismos nombres de los campos de nuestras tablas para los campos del formulario, Rails automáticamente sabrá cómo completarlos y sabrá luego cómo y dónde guardarlos cuando enviemos este formulario.

Sin dudas, esto simplifica muchísimo las cosas. Fíjense cómo controlamos este formulario desde la clase PeopleController:

@person = Person.findById(1);

¡Listo! No hizo falta ni siquera ir campo por campo asignando la propiedad correspondiente… form_for lo hace por nosotros.

Guardar

Lo bueno de guardar en Rails es que, cuando recibimos un form, recibimos también un array params, con todos los valores del formulario enviado. Entonces, con pasar este array params al método update o create, automáticamente Rails sabrá cómo guardar la entidad.

El método flash

El método flash puede parecer algo tonto, pero es muy útil. Su funcionalidad principal es pasar info entre peticiones diferentes. Uno de los principales usos es para mostrar notificaciones o mensajes de error.

Conclusión

Como ven, Ruby on Rails tiene muchísimas cosas interesantes. Cosas que tal vez no vamos a encontrar en otros frameworks y que se las vamos a envidiar.