Opinión: De las palabras de un iluminado y de madurez de comunidad…

Buenas! Este post surge a raíz de una interesante conversación que mantenido con Jorge Serrano, Eugenio EstradaPablo Núñez y Pablo Iglesias.

A ver empecemos… todo viene a raíz de las palabras de un iluminado que se pueden encontrar en http://blog.expensify.com/2011/03/25/ceo-friday-why-we-dont-hire-net-programmers. Resumiendo este tio viene a decir que no contrata nunca a desarrolladores en .NET porque en definitiva no los considera buenos. Según él los desarrolladores en .NET sólo saben arrastrar cajas y poca cosa más. Bueno, es su opinión (no la comparto pero la respeto).

Eugenio publicó el siguiente tweet al respecto (http://twitter.com/eugenioestrada/status/52396715441000448):

image

A lo que yo respondí con el tweet que originó la conversación posterior (http://twitter.com/eiximenis/status/52397065011077121):

image

Luego ya empezamos a intervenir todos sobre el tema, que si sí, que si no, que si tal… La verdad es que en 140 carácteres cuesta expresar según que ideas, así que al final me he decidido escribir el post. En concreto yo decía que parte de culpa tenemos en la imagen que tiene de .NET, porque considero que como comunidad (de desarrolladores se entiende) nos falta cierta madurez.

Lo importante, como digo en mi tweet, no es lo que pueda opinar alguien, ya se sabe que las opiniones son como el culo: todo el mundo tiene la suya. Lo importante es si esa imagen de que los desarrolladores de .NET no somos “buenos” está extendida. Y si lo está, mirarnos un poco nosotros, como comunidad y ver si podríamos hacer algo mejor. Ahí iba. Y para empezar voy a dejarlo claro: No estoy de acuerdo con el tipo ese. Los desarrolladores de .NET somos tan buenos o tan malos como los de Java, PHP o cualquier otra comunidad (extendida, eh? que os conozco y me sacaréis algún ejemplo de algún lenguaje, como Brainfuck, donde haya que tener un Nobel para desarrollar). Además el propio autor considera a los de PHP y Java como buenos desarrolladores en contraposición a los de .NET.

Bueno, que me lío, como digo lo importante no es si eso lo piensa un iluminado o no. Lo importante es si lo piensan muchos. Y, mi percepción, es que hay bastante gente que opina como este tio. En general, creo, que la imagen de .NET en general y de los que desarrollamos bajo ella está por debajo de lo que se merece la plataforma y los que la usamos. Y es ahí donde honestamente creo que algo de culpa que tenemos. Recordad que este tio, sólo expresa su opinión, y no se le ve un talibán anti microsoft, se le vé un anti-desarrolladores-en-.NET que no es lo mismo.

Cuando digo que algo de culpa tenemos, lo resumo en que nos falta madurez como comunidad, en respecto a otras comunidades. Eso por supuesto, es una opinión personal, basada en mi experiencia. Es evidente que Microsoft pretende que desarrollar en .NET sea fácil. Eso se resume en muchas cosas: muchos recursos para aprender, mucha documentación y también muchos asistentes que realizan tareas. Eso no es malo, estoy de acuerdo con Jorge cuando dice literalmente que “Hay gente que por hablar de temas frikis se creen que saben más, pero a veces los problemas cotidianos requieren soluciones cotidianas”. Eso es lo que me gusta de .NET, que es fácil iniciarse en ella. Y entonces… ¿donde está el problema?

Bien, voy a generalizar a partir de ahora. Por lo tanto, por favor, que nadie se ofenda. El problema está en que al ser tan fácil desarrollar en .NET no profundizamos mucho. Nos quedamos en la superficie. Además MS nos facilita tanto las cosas que nos permite p.ej. desarrollar aplicaciones web sin conocer html o http. Un ejemplo concreto: he visto varias veces gente que desarrolla en asp.net poner un msgbox y dejar un iis “colgado”. Eso denota una falta total de conocimientos de como funciona la web en general (ojo, no estoy diciendo que tengamos que nacer enseñados). Y esa gente está haciendo aplicaciones porque .NET lo permite, por su facilidad. No he visto jamás a nadie desarrollando en jsp o php que se le ocurra hacer esto. Todas las decisiones tienen sus dos caras, y la que MS tomó de hacer .NET fácil acarrea estas consecuencias. Por supuesto no estoy diciendo que todos los que programan jsps o phps sean los putos masters del universo desarrollando aplicaciones web. Evidentemente que no. Habrá auténticos patanes desarrollando en estas plataformas. Seguro. Y harán aplicaciones. Seguro. Pero nosotros tenemos esta imagen y ellos no. Quizá, en proporción, seamos más. Quizá no y es todo un tema de imagen.

En general creo que somos una comunidad que nos cuesta innovar y buscar soluciones fuera del paragua de Microsoft. De nuevo voy a generalizar y seré un poco “duro”, que nadie se ofenda, por favor.

En general no sabíamos hacer unit test hasta que salió VS2005, y eso que nUnit ya llevaba su tiempo. El tema de builds automáticas e integración continúa era desconocida para nosotros, hasta que salió TFS. Y eso que nAnt y Cruise Control.NET ya existían. Por supuesto usar un modelo MVP para desarrollar aplicaciones de escritorio no sabíamos lo que era hasta que salió CAB (y honestamente después seguimos sin saberlo porque CAB era como era y tampoco llegó a pasar nunca de algo un poco freak dentro de .NET). Por supuesto hasta que Micrtosoft no sacó Entity Framework, no conocíamos que era un ORM, a pesar de que NHibernate ya tenía sus añitos. Y que decir de usar MVC para aplicaciones web? Nada, hasta que Microsoft sacó asp.net mvc. Y podría seguir con más ejemplos…

A lo mejor ahora parece que nos estemos despertando un poco, pero yo veo que seguimos danzando al ritmo que marca Microsoft. Si VS2011 integra una herramienta tipo SpecFlow empezaremos a hablar todos de las maravillas de BDD y como sin BDD no se puede desarrollar. Somos así.

Y eso creo que nos ocurre porque nos hemos acostumbrado a que Microsoft nos proporcione siempre el stack completo: base de datos, lenguajes, plataforma, APIs, buenas prácticas y nos diga que se puede hacer y que no. Lo que no esté en este stack de Microsoft, ni lo miramos. Pero cuando se incorpora algo nuevo, aunque sea peor que algo que ya existía pero no era de Microsoft, lo abrazamos sin dudarlo. Es por eso que creo que nos falta madurez como comunidad. En otras comunidades (sobre todo Java) nos llevan mucha ventaja en esto. No quiero entrar en el porqué, porque eso sería otro debate.

Y creo que todo esto contribuye a la mala imagen que podamos tener como desarrolladores de .NET. Y esto debería preocuparnos, porque si esa mala imágen se extiende entre los que al final nos deben contratar, entonces el problema lo vamos a tener nosotros!

Y por último respondiendo a una última pregunta de Jorge, sobre que se puede hacer para solucionar esto: Pues en general no lo sé. Intentando ser un buen desarrollador, en intentar aprender continuamente, en investigar que hay más allá y sobretodo no quedarse en .NET. Mirar en otros sitios que están haciendo y como lo hacen. Alguien me dijo un dia que todo desarrollador debería aprender (aprender como funciona y su filosofía, no convertirse en un crack) un lenguaje nuevo cada año. Me parece una visión muy acertada.

Un saludo a todos!

PD: Y para terminar, insisto en lo que he dicho antes. He generalizado. Ya lo he dicho pero lo digo de nuevo: No creo que los que desarrollamos en .NET seamos peores que los que lo hacemos en otras plataformas. Hay gente muy, muy, muy buena desarrollando en .NET, al igual que la hay en todas partes. Y al revés también, por supuesto.

[HTML/JS] Module pattern

Muy buenas! Cuando creas un sitio web, es normal que vayas añadiendo cada vez más código javascript en él. Al final, seguramente terminaréis desarollando una mini-api, propia que muchas veces reside en un archivo .js, lleno de funciones. Algo como:

function HazAlgo(id, params, callback)
{
}

function _HazAlgoHelper(domObj, params)
{
}

En este caso tenemos dos funciones, HazAlgo y _HazAlgoHelper. La función _HazAlgoHelper es realmente una función privada, es decir está pensada para ser llamada únicamente dentro de HazAlgo. Pero en Javascript no existe directamente el concepto de función privada así que le ponemos un idicador (que empieze por _) y asumimos que todas las funciones que empiecen por _, son privadas.

Como podemos ver, el código javascript no está muy “organizado”: tenemos todas las funciones juntas y además podemos ver aquellas que no deberíamos. El patrón de módulo existe para solventar esos problemas.

El patrón de módulo

La idea del patrón de módulo (module pattern) en Javascript es simular el concepto de una clase estática con métodos públicos y métodos privados. Lo de clase estática viene porque no quiero ir creando objetos de mi módulo, simplemente quiero tener ahí, las funciones públicas. Pero sin ver las privadas. Para los que no lo sepáis: en javascript no existe el concepto de variable estática.

La idea que hay detrás del patrón de módulo es muy simple, y como casi siempre pasa simple significa también brillante: Se trata de crear un objeto anónimo, cuyos campos públicos sean las funciones públicas. Finalmente se crea una instancia de ese objeto y se asigna al namespace global (eso es, al objeto window).

El código de base sería algo como:

(function(wnd) {
var miModulo = function() {
var _HazAlgoHelper = function(domObj, params) {alert("método privado");};
var _HazAlgo = function(id, params, callback) { _HazAlgoHelper(); alert("método público"}; };

return {
HazAlgo : _HazAlgo
};
}

wnd.m$ = miModulo();
})(window);

Bien… se que el código puede parecer lioso, la primera vez, pero vamos a analizarlo.

Podemos ver que estamos definiendo una función anónima que acepta un parámetro (llamado wnd).

¿Y que hace esa función anónima)? Pues para empezar define una variable miModulo que resulta ser… una función (var miModulo = funcion() { …}).

Y que hará esa función cuando se invoque? Pues tres cosas:

  1. Definir una variable llamada _HazAlgoHelper… que es otra función.
  2. Definir una variable llamada _HazAlgo… que es otra función.
  3. Devolver un objeto anónimo con un campo, llamado HazAlgo. El valor de HazAlgo es el mismo que _HazAlgo (es decir una función).

Con eso tenemos una función (miModulo) que cuando la invoquemos nos devolverá un objeto con un solo campo (llamado HazAlgo). Así, teoricamente:

miModulo.HazAlgo();    // Valido
miModulo._HazAlgo(); // No válido
miModulo._HazAlgoHelper(); // NO válido

Fijaos que la gracia está en que HazAlgo() realmente es lo mismo que _HazAlgo()… y desde _HazAlgo() podemos llamar sin ningún problema a _HazAlgoHelper() que es nuestra función privada. Así la clave es mapear las funciones públicas como campos del objeto anónimo devuelto.

¡Con eso hemos simulado el concepto de funciones privadas!

Pero, miModulo es una variable local, es local a la función anónima que estamos definiendo. Así que todavía nos queda un paso más: Guardar miModulo en alguna propiedad del parámetro wnd. Eso es lo que hacemos con la línea:

wnd.m$ = miModulo();

Invocamos miModulo (con parentesis, pues es una función) y guardamos el resultado (el objeto anónimo con el campo HazAlgo) en la propiedad m$ del parámetro wnd.

Ya casi estamos… Hasta este punto hemos definido simplemente una función anónima. Ahora toca invocarla. Fijaos en un detalle importante: la primera línea empieza con un paréntesis abierto. Con eso, de hecho, estamos invocando nuestra función anónima. Pero nuestra función anónima espera un parámetro (wnd) así que debemos pasárselo. Eso es lo que hacemos en la última línea: le pasamos window. Y porque window? Pues porque window es un namespace global: todo lo que esté en window es global.

Por lo tanto ahora, desde cualquier parte de mi página puedo hacer:

<script>m$.HazAlgo(1,2,3);</script>

Y eso es correcto, mientras que hacer.

<script>m$._HazAlgoHelper(1,2);</script>

Nos dará un error.

Para tener nuestro módulo listo para ser usado, basta con tenerlo en un .js propio e incluirlo en aquellas páginas donde lo necesitemos. ¡Y listos!

Espero que esto os parezca interesante y os ayude a organizar vuestro código javascript!

Un saludo!

ASP.NET MVC: Previsualizar imágenes subidas (2)

Buenas! Donde dije digo, digo Diego… Sí, ya sé que dije que el segundo post sería como hacerlo con Ajax, pero bueno… la culpa es de twitter, concretamente de @pablonete con el que hemos empezado a hablar sobre si es posible evitar el guardar la imágen físicamente en el servidor. Hay un mecanismo obvio, que es usar la sesión (guardar el array de bytes que conforman la imágen en la sesión). Pero… hay otra? Pues sí: usar data urls!

Data urls

Lo que mucha gente no conoce es que el formato de URL permite incrustar datos que son interpretados por el navegador como si se los hubiese descargado externamente. P.ej. la siguiente url es válida:

data:image/jpeg;base64,xxxxxxx

donde xxxxxxx es la codificación en base64 de la imágen.

P.ej. eso es totalmente correcto:

<img src=”data:image/jpeg;base64,xxxxxx/>

Veamos como podríamos modificar nuestro proyecto anterior, para usar data urls en lugar de un fichero temporal en el servidor… Fijaos que eso sólo evita guardar el fichero, la imagen debe ser subida al servidor.

Las modificaciones son muy simples, por un lado primero vamos a modificar la acción del controlador, para que quede así:

[HttpPost]
public ActionResult SendImage(HttpPostedFileBase img, string base64, string contenttype)
{
if (base64 != null && contenttype != null && img==null)
{
// Aquí podríamos guardar la imagen (en base64 tenemos los datos)
return View("Step2");
}
var data = new byte[img.ContentLength];
img.InputStream.Read(data, 0, img.ContentLength);
var base64Data = Convert.ToBase64String(data);
ViewBag.ImageData = base64Data;
ViewBag.ImageContentType = img.ContentType;
return View("Index");
}

La acción recibe tres parámetros:

  1. img: El fichero seleccionado (contenido del <input type=”file”).
  2. base64: Codificación en base64 de la imagen que se está previsualizándo.
  3. contenttype: Content-type de la imagen que se está previsualizando.

Por supuesto base64 y contenttype sólo se envían si se está previsualizando una imagen. En caso contrario valen null.

Fijémonos lo que hace la acción, si base64 y contenttype valen null, o bien el usuario ha seleccionado una imagen nueva (img != null):

  1. Obtenemos los datos en base64 de la imagen enviada por el usuario (base64Data).
  2. Pasamos esos datos (junto con el content-type) a la vista, usando el ViewBag.

Y la vista que hace? Pues poca cosa:

<form enctype="multipart/form-data" method="post" action="@Url.Action("SendImage")">
<label for="img">Seleccionar imagen:</label>
<input type="file" name="img" /> <br />
<input type="submit" />

@if (ViewBag.ImageData != null)
{
<img src="data:@ViewBag.ImageContentType;base64,@ViewBag.ImageData" />
<input type="hidden" value="@ViewBag.ImageData" name="base64" />
<input type="hidden" value="@ViewBag.ImageContentType" name="contenttype" />
}
</form>

Tenemos el formulario con el input type=”submit”, y luego si en el ViewBag vienen los datos de la imagen que se debe previsualizar, genera 3 campos más:

  1. Una imagen, cuyo src es una data url (formato data:content-type;base64,[datos en base64])
  2. Dos campos hidden (para guardar el content-type y los datos para mandarlos de vuelta al servidor).

Y listos! Con eso tenemos la previsualización de las imágenes sin necesidad de generar fichero temporal alguno.

Finalmente, tres aclaraciones:

  1. Fijaos que la codificación en base64 se incrusta en la página (en este caso se incrusta dos veces, aunque con un poco de javascript podría incrustarse solo una), por lo que esto puede generar páginas muy grandes si la imagen ocupa mucho.
  2. Si no voy errado, los navegadores sólo están obligados a soportar URLs de hasta 1024 bytes. Todo lo que exceda de aquí… depende del navegador. Firefox p.ej. acepta URLs muy largas, pero recordad que Base64 genera fácilmente URLs no muy largas, sinó descomunalmente largas (si la imágen ocupa 100Ks, la URL en Base64 ocupará más de 100Ks). Así que para imágenes pequeñas igual tiene sentido, pero para imágenes largas, honestamente no creo que sea una buena solución.
  3. La mayoría de navegadores modernos soportan data urls (IE lo empezó a hacer con su versión 8).

En fin… bueno, como curiosidad no está mal, eh? 😉

Gracias a Pablo Nuñez por la divertida e interesante conversación en twitter!

Saludos!

PD: Enlace al post anterior de esa serie: http://geeks.ms/blogs/etomas/archive/2011/03/15/asp-net-mvc-previsualizar-im-225-genes-subidas-1.aspx

ASP.NET MVC: Previsualizar imágenes subidas (1)

Buenas! Una pregunta que últimamente parece que se pregunta varias veces en los foros de ASP.NET MVC es como previsualizar una imagen que se quiere subir al servidor.

Antes que nada aclarar que, técnicamente, la pregunta está mal hecha: no es posible previsualizar la imagen antes de que sea subida. Antiguamente en algunos navegadores, y con un poco de javascript, eso era posible, pero ahora por suerte eso ya no funciona 🙂

Básicamente previsualizar una imagen consiste en:

  1. Recibir los datos de la imagen
  2. Guardarla en algún sitio “temporal”
  3. Mandarla de vuelta al navegador
  4. Borrar la imagen del sitio “temporal” si el usuario no la acepta

En este primer post vamos a ver como hacerlo sin usar Ajax. Luego habrá un segundo post y veremos como hacerlo usando Ajax (no es tan sencillo porque XMLHttpRequest no soporta el formato de datos multipart/form-data que es el que se usa cuando se mandan ficheros).

Bien, vamos a verlo rápidamente, ya veréis cuan sencillo es 🙂

Lo primero es tener la vista que tenga el formulario para enviar los datos. La siguiente servirá (si queréis más detalles de como hacer upload de ficheros en MVC mirad mi post al respecto):

<h2>Index</h2>

<form enctype="multipart/form-data" method="post" action="@Url.Action("SendImage")">
<label for="img">Seleccionar imagen:</label>
<input type="file" name="img" /> <br />
<input type="submit" />
</form>

Trivial: un form con multipart/form-data que enviará sus datos a la acción SendImage. La acción podría ser algo como:

[HttpPost]
public ActionResult SendImage(HttpPostedFileBase img)
{
var data = new byte[img.ContentLength];
img.InputStream.Read(data, 0, img.ContentLength);
var path = ControllerContext.HttpContext.Server.MapPath("/");
var filename = Path.Combine(path, Path.GetFileName(img.FileName));
System.IO.File.WriteAllBytes(Path.Combine(path, filename), data);
ViewBag.ImageUploaded = filename;
return View("Index");
}

Vale, fijaos que recibimos el HttpPostedFileBase (llamado img como el atributo name del input type=”file”). Lo que hacemos en la acción es muy simple:

  1. Guardamos la imagen en disco (en este caso por pura pereza uso el directorio raíz de la aplicación web. Por supuesto lo suyo es usar un directorio configurado en web.config y con los permisos NTFS correspondientes).
  2. Coloco en el ViewBag el nombre de la imagen que se ha guardado (luego vemos porque).
  3. Devuelvo la vista “Index” (la misma de la cual venimos)

Ok, tal y como lo tenemos ahora, si lo probáis veréis que podéis hacer un upload de la imagen, y la imagen se graba en el disco (en el directorio raíz de la aplicación web), pero no se previsualiza nada. Vamos a modificar la vista Index para que haya esa previsualización.

Para previsualizar la imagen basta con añadir el siguiente código, despues del </form> en la vista:

@if (!string.IsNullOrEmpty(ViewBag.ImageUploaded))
{
<img src="@Url.Action("Preview", new {file=ViewBag.ImageUploaded})" />
}

Simple, no? Si en el ViewBag existe la entrada ImageUploaded generamos un tag <img> cuya dirección es la acción “Preview” y le pasamos el parámetro file con el nombre de la imagen. Y como es la acción Preview? Pues super sencilla:

public ActionResult Preview(string file)
{
var path = ControllerContext.HttpContext.Server.MapPath("/");
if (System.IO.File.Exists(Path.Combine(path, file)))
{
return File(Path.Combine(path, file), "image/jpeg");
}
return new HttpNotFoundResult();
}

Simplemente leemos la imagen guardada y la devolvemos usando un FilePathResult. Simple, eh? 😉

Con eso, si subís una imagen, cuando le deis a submit, aparecerá de nuevo la vista, pero ahora previsualizando la imagen.

Ahora sólo nos queda el punto final: Que el usuario pueda aceptar esa imagen como correcta. Para ello lo más simple es hacer lo siguiente:

  1. Si el usuario está previsualizando una imagen y NO ha seleccionado otra, cuando hace el submit se entiende que acepta dicha imagen.
  2. Si el usuario NO está previsualizando una imagen, o bien está previsualizando una pero selecciona otra, al hacer el submit se entiende que descarta la imagen anterior y quiere previsualizar la nueva.

Para ello, tenemos que hacer que la vista le indique al controlador si se está previsualizando una imagen, y cual és. Por suerte eso es muy sencillo. Basta con modificar el <form> de la vista para que su atributo action quede como:

<form enctype="multipart/form-data" method="post" 
action="@Url.Action("SendImage", new {previewed=ViewBag.ImageUploaded})">

Fijaos que añadimos un parámetro previewed que la vista mandará a la acción y que será el nombre de la imagen que se está previsualizando.

Vamos a modificar la acción para que reciba ese parámetro y actúe en consecuencia:

[HttpPost]
public ActionResult SendImage(HttpPostedFileBase img, string previewed)
{
if (!string.IsNullOrEmpty(previewed) && img == null)
{
ViewBag.ImageName = previewed;
return View("Step2");
}

// Código tal cual estaba
}

Añadimos ese if al inicio: Si el usuario está previsualizando una imagen y no ha seleccionado otra, entendemos que acepta esta imagen. Entonces guardamos el nombre en el ViewBag y lo mandamos a la siguiente vista (Step2).

La vista es muy sencilla:

<h2>Step2</h2>

Siguiente paso. El usuario ha aceptado la imagen: <br />

<img src="@Url.Action("Preview", new {file=ViewBag.ImageName})" />

Simplemente mostramos la imagen. Fijaos que de nuevo usamos la acción Preview, puesto que la imagen ya la tenemos guardada en el servidor.

Notas finales

Faltarían, al menos, dos cosas para que dicho proyecto funcionase “de forma aceptable”:

  1. Borrar las imagenes descartadas por el usuario (en la acción SendImage si se recibe una imagen nueva y se estaba previsualizando una, borrar esta ya que el usuario la ha descartado).
  2. La acción  Preview siempre devuelve content-type a image/jpeg, eso debería hacerse según la extensión de la imagen, o guardarlo en algún sitio.

Ambas cosas son triviales y no las añado porque lo único que consiguiría es liar el código un poco más 😉

En el próximo post… lo mismo pero usando Ajax.

Saludos!