Traducciones y tripas

Hola chavales,

He sacado un rato para contaros una cosilla que me ha tocado investigar últimamente. Las herramientas de traducción de aplicaciones, también conocidas i18n y l10n por internationalization y localization (contad las letras de las palabras y entenderéis el número).

Esas herramientas de traducción no son simplemente eso. Sirven para traducir y localizar las aplicaciones, no sólo traducen las frases o palabras, también saben elegir si usar un singular o un plural dependiendo de un número o utilizar cuantificadores como “mucho” o “poco” (pluralization). A parte de eso, valen para transformar la moneda, el formato de las fechas, las unidades de medida y ese tipo de cosas. El l10n hace unas cosas y el i18n otras leed al respecto que no es malo informarse (y se escapa a lo que quiero contar aquí).

Hoy quiero contarlos cómo hacer una aplicación de éstas de forma simple, y sólo me centraré en las traducciones. Además, tendréis mi solución de i18n de la que hablé en la entrada anterior, que es rematadamente simple y trata exactamente de lo que hablaré aquí hoy1. Ni más ni menos.

Esta entrada pretende que se os ocurra a vosotros cómo hacer una librería de este estilo, tirando a lo simple, para que los más novatos entendáis cómo es posible hacer cosas útiles con el conocimiento que ya tenéis. No es tan difícil programar. Pensadlo: si lo fuera, posiblemente el que os escribe no sería capaz de hacerlo.

Vale, vamos a por ello.

¿Cómo creéis que funciona una mierda de éstas?

Pensad un poco conmigo. Recordad que sólo queremos que pueda traducir, no queremos pluralización ni extras (luego hablaremos también de eso).

A nivel de usuario de la librería, normalmente funcionan de la siguiente manera:

  1. Importamos la librería en el programa que queramos que pueda ser traducido.
  2. En todo el texto que vaya a ser visto por el usuario se pone un identificador de algún tipo. Normalmente se trata de una llamada a una función entregada por la librería. Ejemplos:

i18n("texto a traducir")

_("texto a traducir")

gettext("texto a traducir")

  1. De alguna manera, se vuelcan todos los strings para traducir en unos ficheros con espacio para su traducción. Uno por idioma.
  2. Se traducen.
  3. Durante la ejecución, la aplicación busca los strings a traducir antes de mostrarlos en los ficheros. Cuando los encuentra, usa la traducción adecuada al lenguaje configurado por el usuario.

Así es como funciona. Simple ¿No?

Si no me he explicado bien podéis buscar algunas librerías de i18n y mirar su documentación para ver qué formato de ficheros utiliza o qué herramienta extrae los strings para la traducción.

Una vez entendido esto, hay que empezar a darle vueltas a cómo lo implementaríamos. Durante el desarrollo de esto haré algunas trampitas, porque soy un poco malévolo, pero os prometo que tendrán sentido.

¿Cómo lo haríais vosotros? ¿Por dónde empezaríais? ¿Qué os parece lo más difícil?

Nosotros vamos empezar por el paso 2 de la lista anterior. La forma de indicar que los strings son traducibles. La mejor forma es meterlos en una función, aunque habría otras. Si le damos al usuario de la librería una función a utilizar será simple: la aplicará a los strings que le interesen y todo irá bien.

Hay muchas formas de hacer esto, la más fácil es que los ficheros de traducción sean simples ficheros tipo JSON donde las claves sean el texto a traducir y los valores la traducción:

{
"hola": "hi",
"adiós": "bye"
}

Con esto así, cuando se llame a la función con un string sería suficiente con buscar en ese JSON la clave y devolver el valor. En JavaScript y simplificando muchísimo:

var traduccion = carga_traduccion();
function _( string ){
  return( traduccion[string] );
}

Lo que no hemos hecho todavía es esa función carga_traduccion que vemos en el snippet y tampoco gestionamos cuál es el idioma actual del usuario.

Eso tampoco es taaan difícil ¿no?

Podemos tirar por un formato orientado a objetos para hacerlo sencillo. Suponemos que el sistema de traducciones es una clase/prototipo (no me voy a poner a discutir esto ahora) con un campo de “idioma actual” y lo de arriba se transformaría en algo un poco más complejo si suponemos que el JSON de idiomas tiene un nivel de más que referencia al idioma. En JavaScript de nuevo:

{
"hola": { "english": "hi", "euskera": "kaixo" }
"adiós": { "english": "bye", "euskera": "agur" }
}

//...
function _( string ){
  this.traduccion[string][this.idioma];
}
//...

Una vez tenemos esto, sólo necesitamos hacer que nuestra clase/prototipo pueda configurar el idioma, leer los ficheros de idioma automáticamente al iniciar (el carga_traduccion de antes) y que le entregue la función _ al hilo principal.

Cargar los ficheros en principio es sólo leer una carpeta y ya. Aunque si estamos en el browser tenemos que hacer alguna magia añadida que no debería costaros mucho. Tema resuelto.

¡Coño! ¿Y ya está?

En realidad no, porque el traductor tendría un desastre de ficheros de idioma, y encima tendría que rellenarlos a mano. Lo suyo es que haya una herramienta tipo gettext que extraiga todos los strings a traducir y los vuelque en ficheros que luego tengan que traducirse. Ésta es la trampa, este punto es el más difícil. En mi solución de i18n, como sé que el lugar donde va a usarse lo permite, la propia clase vuelca los ficheros (también llamados catalog) al terminar la traducción, pero esto no es factible siempre porque si la ejecución del programa nunca pasa por mostrar el string nunca aparecería en los catálogos. (¿Quizás con unos tests brutos podríamos hacerlo?)

Otro punto interesante es tener en cuenta que hemos usado strings con texto, en nuestro caso en castellano. Entonces si el usuario tuviese seleccionado el idioma en castellano no tendrían que traducirse y tendrían que entregarse igual. Hay dos formas sencillas de atacar este problema:

  1. Obligar al usuario a no poner el string en concreto y poner un identificador para siempre se busque en la traducción.
  2. Tener configurado un idioma por defecto que sea el original.

Otra cosa que quiero tocar es que esos ficheros de JSON pueden ser la muerte (a pesar de que algunas herramientas los usan) porque si el texto a traducir es muy largo nos quedaríamos con unas claves gigantescas en una única línea (JSON no permite claves multilínea). El contenido también sería difícil de traducir porque también sería en una sola línea y tendría también caracteres especiales por el medio para marcar los saltos. ¡Quizás sea mejor usar otro tipo de fichero! Mi solución i18n usa YAML1 pero también tiene un sistema diferente para gestionar las claves. Si os interesa me preguntáis o lo miráis.

Otro punto extra es que no tenemos ninguna información sobre lo que estamos traduciendo. La misma palabra puede tener que traducirse de dos maneras diferentes dependiendo de dónde esté porque el contexto puede ser distinto. En este caso, nuestro programa sólo contempla que textos iguales se traducen igual.

Tampoco nos dice en qué línea del programa original está lo que traducimos así que es difícil encontrarlo.

Ni nos da ningún tipo de detalle sobre la traducción, cosa que en algunas soluciones i18n existe: Te permiten poner palabras clave para definir el contexto mejor como guías para los traductores que se ignoran en el programa pero que se vuelcan en los ficheros de traducción. Ejemplo:

i18n("Abrir", "Abre un fichero"); // siendo Abrir el string a traducir

Ya os he hablado de la pluralización antes, y de la internacionalización también. Eso también complica las cosas…

Joder…

Bueno eso, que era fácil…

¿No?

😉

Un abrazo.

De sitios web, traducciones y vacas peludas

Hola chavalada,

Hace mucho que no os traigo nada fresco y me parece muy mal y más vale que a vosotros también porque si no no entiendo por qué os leéis esta mierda de sitio.

Con eso de la empresa necesito una página web. ¿Quién no necesita una hoy en día? ¡Si hasta las panaderías tienen!

Como soy un afeitador de yaks profesional me he dedicado un par de semanas a hacerlo. No sé parar. Os cuento cómo fue el rollo.

Necesitaba una página web para la empresa. Esto me llevó a querer escribirla, pero quería que fuera simple y estática y no quería tener que gestionar todo en HTML a pelo y repetir pedazos así que tenía que ser modular. Para esto necesitaba un generador de páginas web. Lo malo es que el creador de webs tenía que ser capaz de traducir todo de forma automática sin tener que reescribir toda la web.

Y esto no es muy extremo, la verdad. No tardé mucho en conseguirlo (¿un par de días en ratos sueltos?). Aquí la prueba.

Pero ya os he dicho que no sé parar y como el tipo de fichero de traducción generado por gettext no me gustaba, no podía automatizar todo dentro del mismo script porque tenía que llamar a los métodos internos de pyBabel que no estaban muy claros en la documentación y ahora me estoy saturando de python y quiero cambiar de aires pues… Se me fue la olla y decidí hacerlo en Node.js.

No en CoffeeScript, como he hecho otras cosas. Esta vez tenía que ser JavaScript. Porque sí. Además quería forzar un poco de programación funcional y ya que me ponía quería jugar con Underscore porque la quiero usar en otro proyecto y ya mato dos pájaros de un tiro.

Cuál fue mi sorpresa cuando me puse a investigar diferentes librerías de templating y de i18n (internacionalización, para las traducciones) y no me quedaba satisfecho.

Para el templating probé cosas parecidas a Jinja, que es lo que conocía y el proyecto que hice en Python que te he linkado antes. Fueron:

  • Jinjs: Una implementación en de Jinja en Node.js. Casi compatibles al 100% pero no tiene muy buena documentación. Te referencia a la de Jinja y me daba miedo basarme en cosas que luego no funcionaran bien.
  • Plate: No probé mucho, hice un ejemplo y funcionó bien. Son plantillas compatibles con las de Django (un framework web de Python), que al mismo tiempo es el projecto en el que se basan las de Jinja que usé en la prueba de concepto de antes.
  • Nunjucks: Un proyecto basado en Jinja. Muy similar y compatible en todo lo que necesitaba. Me quedé con estas porque tienen una docu bastante decente. Aunque luego una cosa que quise hacer no estaba bien documentada y la tuve que hacer de otra manera. 🙂
  • Y algunas otras diferentes por probar, pero no me gustaron para esta aplicación. Mustache y similares. Interesantes también para otras cosas.

Pero eso sólo solucionaba medio problema.

La mayor parte de soluciones de i18n que encontré tienen muchas cosas que no necesito como que te capturen automáticamente las locales y encima usan un fichero JSON para las traducciones que es muy difícil de gestionar con strings largos o multilínea. Este último problema en mi caso era grave porque traducir una web conlleva mucho contenido, no es lo mismo que una aplicación. Así que tenía que hacer algo.

Pensé que usar un YAML molaba mucho más porque gestiona los strings largos de una forma mucho más bonita. Para empezar, puede ajustarse el ancho del fichero a un límite de caracteres por defecto y, con eso, marca de forma distinta los strings que son muy largos y han sido ajustados al ancho del fichero añadiendo saltos de línea o los strings que contenían saltos de línea y deben ser leídos de forma literal. Esto para mí es oro puro y lo necesitaba.

Así que. Lo hice. Me dediqué unos días a crear mi propia librería de i18n (que tengo que documentar mejor, por cierto). Está aquí:

https://github.com/ekaitz-zarraga/i18n_yaml

Y la podéis instalar con:

npm install i18n_yaml

Y eso ya me habilitó el poder hacer el constructor de sitios webs estáticos con i18n á la Ekaitz y lo pude hacer y poner aquí:

https://gitlab.com/ElenQ/schiumato

Ahora mismo está un poco en bragas (tenéis una lista de cosas pendientes al final del README.md), pero tiene buena pinta y funciona. No os voy a contar de donde viene el nombre porque entonces tendría que hablar de otro gestor de sitios que… Ejem.

Todavía me queda el último salto de la recursión, claro, me queda hacer la web. Pero eso os lo cuento otro día.

Tampoco he entrado en profundidad a contar cómo funcionan estas cosas, como me suele gustar hacer. Igual lo hago pronto y aprovecho para documentarlas un poco mejor.

Un abrazo.

Seguid afeitando yaks y cambiando el mundo por el camino.

Futuro presente

Hola,

Ya estamos por aquí otra vez.

Hoy me pondré más serio de lo que acostumbro porque me toca hablar del futuro más que del pasado, como me suele gustar hacer.

Ya sabéis que dejé mi trabajo y otras movidas así que ahora estoy en mi sofá a las 9:45 de la mañana escribiendo esto, sin cobrar un duro.

Que no cobre ahora no significa que no tenga intención de hacerlo y tampoco significa que quiera un trabajo. Tal y como ya dije, creo que no encajo bien en el modo de trabajo de una empresa estándar y tampoco quiero forzarme a hacerlo porque para mí no tiene ningún sentido.

Como todavía tengo energía y he tenido la suerte de haber podido ahorrar un poco, estoy dedicando este tiempo a aprender y a preparar mi futuro trabajo. Sí, voy a generar un puesto de trabajo en el que encajo perfectamente, diseñado explícitamente para mí. Si hay suerte, puede que en el futuro pueda generar otros puestos de trabajo especiales, quizás uno sea para ti.

Esto sólo es una forma retorcida de decir que estoy creando mi propia empresa.

Ahora tendréis miles de preguntas pero tampoco voy a poder responder a todas porque, como he dicho, la estoy creando. La idea lleva mucho tiempo viva pero es posible que la realidad la haga cambiar y mutar hasta que yo pueda comer de esto o hasta que demuestre definitivamente que no puedo.

Os cuento un poco de qué va, aunque algunos ya lo habéis leído por otras fuentes.

 

 

ElenQ Technology es el nombre de la empresa que estoy creando.

Se dedicará a hacer, principalmente, Investigación y Desarrollo en las áreas en las que me muevo. Estas áreas incluyen todo lo que hablo en este blog y algunas cosas más que me guardo para mí como la electrónica, las redes, y otras, casi todas relacionadas con lo que conocemos como la Ingeniería de Telecomunicaciones con el pequeño añadido de que soy una persona muy curiosa que ha indagado en otros mundos.

Sé que a los que me leéis os van a gustar lo que diré a continuación:

ElenQ Technology tiene un fondo social obligatorio, heredado de mis desencantos con la empresa y de mis principios, que he dejado demasiado de lado durante un tiempo y ya va siendo hora de recuperar. Así de fácil. Los puntos críticos son los siguientes:

  • Los proyectos que se hagan deben hacer del mundo un lugar mejor. Respetaran los derechos humanos y el diseño ético. Serán Free Software/Hardware siempre. Los clientes tendrán acceso a todo lo que hago y participaran de forma activa en el desarrollo.
  • En los casos que sea aplicable, el conocimiento obtenido con el proyecto será liberado, con una buena documentación, para todo el mundo, para que cualquiera tenga más la información ya mascada sirviendo la realización del proyecto como una excusa para concentrar el conocimiento en una documentación fácil de leer.
  • Se apoyarán los proyectos que se utilicen durante la actividad de la empresa, en la medida de lo posible, con tiempo de desarrollo, dinero o lo que se pueda.

Sobre este sustento, la empresa investigará y desarrollará cosas para terceros, que siempre serán pequeñas y medianas empresas porque son esas empresas que aún conservan cierta ética (en algunos casos) y no pueden permitirse tener un departamento de I+D continuo. Con esto se fomenta que las empresas pequeñas crezcan porque tendrán la oportunidad de investigar y mejorar sus procesos e ideas para así competir un poco mejor con los grandes. Como los proyecto serán éticos, fomentará que las empresas crezcan en una dirección ética, provocando, en cierta manera, que el mundo sea un lugar mejor dentro de un tiempo, aunque sólo sea un poquito.

También ayudaré a que particulares consigan avanzar en esa idea de proyecto que tienen y que no tienen a nadie que les ayude a crear. Esa idea que siempre te quita el sueño pero como tienes un curro no tienes fuerza suficiente para sentarte a investigar y terminarla.

Y, por último, también se desarrollarán ideas propias y se mantendrán proyectos propios. Algunos serán herramientas que utilice para mí (ejemplo), como hago siempre, y otros serán ideas que querré desarrollar.

Como un pequeño extra importante, aunque no quiero que sea la primera actividad de la empresa, se darán formaciones sobre tecnología y sus usos que servirán para aprovechar lo que aprendo en los proyectos y el extraño y profundo conocimiento que llevo años atesorando en lugar de hacer otras cosas. Intentaré tener un hueco para hablar de las razones de abandonar mi curro anterior y animar a otros a plantearse qué está ocurriendo con los datos, la privacidad, la ética y otras cosas también. Siempre desde la humildad de un tío de 26 años que no se ve con un traje ni hablando de usted, pero siempre tiene una carcajada y un apretón de manos para cualquiera.

Durante el texto le he llamado empresa a lo que estoy montando aunque podría ser perfectamente una organización sin ánimo de lucro. Sólo quiero poder comer, y vivir sin preocuparme demasiado, no necesito crecer y crecer como el concepto (capitalista) de empresa propone. La idea es hacerle la vida más fácil a los demás a costa de que hagan mi vida un poco más fácil.

Eso es a lo que me voy a dedicar. Innovación Ética.

Crecimiento en la dirección adecuada.

Seguiré informando. Para bien o para mal.

 

PD: Ya tengo un nombre de dominio y movidas pero no me quiero poner con eso aún a poner links y cosas. Prefiero dejaros aquí la filosofía y ya contaros cosas en el futuro o que me preguntéis. No me gusta el spam. No me gusta una mierda.

 


Como siempre un par de agradecimientos:

A los que estáis ahí y escucháis mis mierdas, a ti, sí, y a ti, el del fondo, también.

A los que respetan lo que hago. A los que me confirman que un mundo mejor es posible. A los que no pierden la confianza en mí. A los que me piden colaborar conmigo a 700km de aquí. A los que me preguntan cosas. A los que tienen ideas para mí. A los que tienen cerveza y me sacan de casa cuando las cosas van lento.

A mi hermana por ayudarme con el LaTeX.

A mi amada por ayudarme en general.

Llegó el momento de saltar.

Si hay agua abajo, os aseguro que voy a caer tan fuerte que os voy a salpicar a todos.

Vergüenza inocente

Me cansé de “ser rico” y ahora me toca “ser pobre”. Entrecomillo porque ambas son inciertas hasta cierto punto, y ciertas hasta otro. La magia de la contradicción.

Hoy toca hablar de vergüenza, más que de inocencia, y de por qué una persona “brillante” “con muy buenas ideas” se convierte en un “fracasado” por iniciativa propia. Esta vez entrecomillo porque son citas textuales, algunas mías y algunas no. Seguro que puedes adivinar cuales digo yo, y seguro que los que no me siguen a menudo se equivocan, posiblemente a propósito.

En una entrada anterior te cuento cómo lo pasas mal cuando no tienes curro, hoy te cuento cómo lo pasas mal cuando tienes uno que casi todo el mundo querría y no lo quieres.

Sí, tú. Dejé ese curro hace un mes. Ese del que te hablo, el del I+D.

Era un curro de puta madre, no te líes, pero no quiero trabajar allí y posiblemente en el que me propongas tampoco. No valgo para eso. Soy una mierda de persona. Lo sé. Puedes dejar de leer aquí si te da la gana, o puedes leer más y llorar de la risa por lo tonto que soy. Quizás sólo llores. Sólo lo sabrás si sigues leyendo.

Hace mucho tiempo lo conté en la entrada a la que enlazo en la entrada enlazada aquí: me duele. Me duele mucho. A veces me duele tanto que quiero arrancarme los brazos, o esa puta rodilla que empezó a darme problemas. La espalda y con ella el cuello, la mandíbula y la cabeza. Hasta detrás de los ojos.

Es difícil vivir así.

Es difícil no poder levantarte de la cama y al mismo tiempo pasarte días sin dormir. Muy difícil. Casi tan difícil como contarlo sin hacerte la víctima o quedar como un mierda que se queja por todo.

Los que me leéis aquí sabéis lo que me gusta lo que hago, o lo podéis intuir al menos y sabéis que sólo lo que os acabo de contar no iba a ser una razón suficiente para dejar un curro que me guste. Soy un poco más duro que todo eso, al menos todavía. Así que empiezo por la razón menos importante para deciros además que ya me encuentro mejor, aunque sea un poco mejor y me esté costando, que todo lo que estoy haciendo está sirviendo para algo por ahora.

Ahora añadidle a ese dolor el trabajo en el que estaba. Un curro de puta madre para muchos, repito, en el que creo que siguen buscando gente.

Currar de 8:30 a 14:00, parar una hora obligatoriamente para comer y volver al tajo de 15:00 a 17:45. Ocho horas y cuarto para poder salir a las 17:00 el viernes porque había que entrar obligatoriamente a las 9:00 (el resto de días se podía entrar hasta las 10:00 si se compensaba por la tarde). Muchos trabajáis con horarios peores, lo sé, pero yo no puedo con eso. Os cuento por qué.

Primero porque el dolor me está arruinando la vida y la única forma de hacerle frente es hacer deporte, bastante deporte. Con estar finito (como estoy) y cuidarse no es suficiente. No es un tema de salud, es que mis musculitos se vuelven locos con el estrés y están todo el rato haciendo fuerza sin control así que me duele mucho porque tampoco estoy como Hulk y mi cuerpecillo no lo soporta. Eso significa, parar más, estirar a menudo y hacer deporte. Para, estira, haz puto deporte y controla ese estrés. No quieres que te pase esto. Créeme.

Segundo porque ese mierda de curro no era suficiente (acabo de decir que era guay ¿no?). Bromas de nuevo. No, no era suficiente. Era un curro técnico, que tenía sus cosas pero no estaba cambiando el mundo, al menos no para bien. Los retos técnicos eran escasos porque todo estaba relativamente masticado ya y siempre se hacía lo mismo. Dejé de aprender, de aprender mucho, y pasé a aprender poco a escondidas aprovechando la pequeña libertad de implementación que tenía para hacer siempre cosas que nunca había hecho porque me estaba aburriendo ya y no podía soportarlo.

Esto no era suficiente, cuando iba a casa mis ansias de aprendizaje me hacían seguir investigando en otros ámbitos (me obligaba a que no fuera laboral). Seguía aprendiendo cosas, moviéndome, recordándome todo el rato lo poco que sé. Esto lo hacía, claro, en lugar de hacer deporte. Uno, porque estaba agotado y el dolor no ayuda y, dos, porque el tiempo es limitado y todo no se puede hacer.

Tampoco era todo aprender, a veces también programaba cosas para aprender y diseñaba cosas sin las trabas que los altos cargos narcisistas, que creen que su análisis de dos minutos vale más que seis meses de implementación de cuatro ingenieros a tiempo completo, imponen. Me he currado la frase, vuélvetela a leer si hace falta.

Sin trabas, como cuando estuve en paro, aprendía rápido. Joder. ¡Hasta empecé a robar tiempo a la empresa haciéndolo allí! No es un secreto esto, empecé a procrastinar para investigar y aprender por culpa de la desmotivación.

La gente dice que la motivación tiene que salir de uno pero no es suficiente. Intenté mantenerme motivado de las siguientes maneras. Orden cronológico aunque algunas se mantienen vivas siempre o aparecen antes en menor medida.

  1. Aprender del entorno. Esto es cuando eres novato y tienes que ponerte al día. La oficina, el ritmo, etc.
  2. Aprender de proyectos. Cuando te empiezan a asignar cosas y tienes que acostumbrarte a las herramientas nuevas.
  3. Aprender de compañeros. Siempre tienen cosas para ti. Esto es cuando ya controlas tu mundo y aprendes del de los demás pidiéndoles ayuda o preguntándoles cosas que ellos saben y tú no.
  4. Aprender de retos. Esto es cuando ahora no buscas sólo hacer, si no que buscas hacer bonito lo que haces y entender cómo funcionan tus herramientas por dentro para poder explotarlas de forma elegante.
  5. Aprender de la incomodidad. Aquí es donde sales de tu zona de confort apropósito porque no vas a aprender dentro de lo de siempre y cambias detalles (como el lenguaje en el que programas) para tener una excusa para seguir aprendiendo.
  6. Aprender del intrusismo y la transferencia. Aquí tratas de ayudar a tus compañeros a realizar su labor mejor, aportándoles información sobre aquella cosa que investigaste en tu tiempo libre que puede que les venga bien. Intentas prestar atención a cuando tienen un problema o explican sus cosas para sacar información y poder preguntarles o participar en sus retos porque los tuyos te aburren y te has quedado sin ideas para hacerlos divertidos.
  7. Aprender del suicidio. Aquí ya se te acaban todas las ideas y procrastinas porque sabes cuánto tardas en hacer las cosas. Trabajas más rápido porque tienes más experiencia así que te permites decir que tardas lo mismo que antes para poder robar ese tiempo en cosas tuyas en las que sí te quedan ideas o para leerte esa librería que usas que te ha parecido interesante. Aprendes lo que sea porque eres un yonki y haces cualquier cosa con no trabajar porque te aburre demasiado y te deprime. Esto intentas hacerlo poco pero cuando estás mal simplemente te supera y te come.

Esas son las etapas.

Traté de hacer un poco de transferencia de conocimiento interna, mandé emails técnicos de cosas que había estado investigando (la entrada de las single page apps que hice es uno de esos emails reciclado) y obtuve muy poca respuesta así que me desanimé bastante.

Otras cosas que no me gustaban dentro de este apartado de la satisfacción personal son el product-driven-engineering o hacer las cosas para productizar o vender. Tío, es un departamento de I+D. Hay que innovar. Primero innova, el producto vendrá después.

Lo que acabó ocurriendo es que muchas cosas las hacíamos buscando aumentar la tasa de aceptación de los proyectos. Es decir, se buscaba justificar la existencia del departamento haciendo que la empresa viese que los proyectos que se hacían en nuestra unidad llegaban a producirse, a implantarse en producción. Casi ningún jefe está de acuerdo conmigo en que eso era un error. Puede que me equivoque, pero, independientemente de si me equivoco o no, a mí y a algún otro compañero eso nos parecía un error y una incomodidad suficiente como para dejar la empresa. Supongo que eso quiere decir algo.

Todas esas cosas me estaban creando un perfil de cómo me gustaría trabajar.

Tercero un tema laboral, que muy de puta madre pero no. Ya os dije que mi salario casi se duplicó desde el trabajo anterior, pero eso no quiere decir que estuviese bien pagado. Estaba pagado ligeramente por debajo de convenio, cosas que sólo se puede conseguir falseando tu puesto de trabajo, no quiero entrar en muchos más detalles porque es una historia que los que seguiréis este blog desde España seguro que ya conocéis porque habréis estado en una situación similar.

Sin salir de ese tema laboral, chorradas que te cambian la forma de entender la empresa: Le dices a tu jefe que necesitas otro tipo de teclado en lugar de esa puta mierda de logitech de teclas de feedback nulo o algún tipo de ayuda ergonómica y te dice que desde recursos humanos dicen que no pueden ni un euro cuando después de un tiempo se compran teclados de Mac a 50€ la pieza a cualquiera que tenga un Mac en la empresa, ya sean desarrolladores de iOS o jefes que sólo usan el ordenador para hacer putas presentaciones y responder emails mientras que tú lo pasas puto para navegar porque tienes un i3 del año de maricastaña (y no te digo compilando). For example.

Repito, son chorradas, pero chorradas que te abren los ojos. Tienes que estar agradecido de lo que tienes, pero no hay nada para ti más que lo que te pagan a fin de mes que además es menos de lo que te mereces. Es gracioso.

Luego te dicen que valores también que hay café, cocacola y galletas en la oficina. Y eso también es gracioso porque no hay fruta fresca y fisioterapia. Hay azúcar y cafeína. La gasolina del programador. La gasolina del infarto.

Otro tema es el horario que tampoco llevo muy bien. El trabajo era bastante creativo y me cuesta encajar mi creatividad dentro de lo que el horario me permite. Si estoy con el flow a las seis de la tarde me tengo que ir y si no me sale nada a las tres de la tarde me tengo que quedar tres horas a cagarla y tener que arreglarlo mañana. Eso es calentar la silla, presentismo laboral y es muy triste.

Fuera de todo lo malo a nivel laboral hay que decir que se cobraba al día (es muy cutre que haya que celebrar esto ¡Hola Irontec!) y que visto a grandes rasgos había bastante flexibilidad en horas de entrada (pero no en horas trabajadas al día), que estaba cerca de donde vivo y el ambiente laboral era muy bueno (luego vuelvo a este punto).

Cuarto punto. La ética. Este es el punto en el que más hincapié quiero hacer. Me fui porque lo que hacía no me parecía ético y porque me daba vergüenza. No puedo contar mucho porque firme unos papeles que blablablá. Cruzamos la línea roja, mi línea roja (la única que vale) y para evitar una situación desagradable en la que me piden algo y digo que no lo voy a hacer me fui. Ni siquiera iba a soportar que me lo pidieran. Ya me cuesta bastante dormir por las noches (dijo a las 2:04 de la mañana).

Es el punto más corto porque no puedo contar nada, pero no podía contribuir a eso.

No lo voy a marcar como un quinto punto pero la mierda de actitud de winner de la empresa, sus valores copiados de los de Amazon (todo un ejemplo a seguir), sus reuniones motivacionales con el superjefe niño rico sociópata que se las da de guay y sus putas cenas de empresa tipo cocktail me tocaban mucho los cojones y no las echo nada en falta. Nada de nada.

Prefiero ser pobre y eso es lo que he decidido hacer. Ahora mismo tengo 0 ingresos. Cero. Cobro cero patatero y mi dinero tampoco va a durar mucho tiempo.

Llevo un mes sin trabajar en la empresa y aunque estoy todavía en fase de negación, como si estuviese de vacaciones, me siento bien. Estoy tranquilo. Me sigue costando dormir por otras movidas pero ahora me puedo dedicar a mis cosas, cocino, descanso más, hago deporte, etc. Mi cuerpo está recuperándose.

Sí que os voy a decir que tengo ideas y cosas en marcha y os iré contando, que no me fui a ciegas, que no soy taaan tonto y que estamos en ello. Perdonadme si por el camino pongo algún link de donación pero necesito comer y toda ayuda es buena.

Os diré que todo lo que voy a hacer a partir de ahora es cambiar el mundo. Intentaré usar lo poco que sé para hacer de este mundo un lugar un poco mejor. Espero que esta aventura dure para siempre y contaminar a todos los que pueda con este germen que me ha convertido en un fracasado pero me ha llevado al lugar al que creo que tengo que estar.

Lo siento, lo siento mucho, pero no sirvo para lo que todo el mundo quería de mi. Ojalá no fuera así.

Ojalá no me diera vergüenza.


Como siempre, mi espacio para las cosas buenas.

Soy una persona emocional, no soy un robot como muchos creen, por eso escribo siempre este tipo de entradas.

He conocido a personas rematadamente imbéciles en esta etapa de mi vida, sí. Pero muy pocas y eran fácilmente eclipsadas por el resto de personas maravillosas.

Nunca os haréis una idea de lo que me duele no haber podido seguir allí con vosotros. No es un discurso de mierda de “todo lo que he aprendido”. Realmente es imposible explicar lo cómodo que llegué a sentirme y lo fáciles que hicisteis las cosas. Si fue más de un año y medio, creed lo que os digo, fue en su mayoría por vosotros. Sin vosotros no hubiese durado ni la mitad.

Me duele mucho pensar que mi egoísmo me ha hecho alejarme de teneros tan cerca.

Es lo único que echo de menos de trabajar ahí. Lo que estoy haciendo no es tan bonito si no tengo a nadie a con quien compartirlo. Me gustaría que estuvieseis aquí conmigo. Ojalá lo estemos de nuevo algún día. De verdad lo espero.

Menos mal que me quedas tú, reina. Escuchas mis mierdas y me animas en todo lo que se me pasa por la cabeza. Siempre incansable. El amor de mi vida. Ojalá el mundo fuera más como tú y menos como lo que es.

Me ha dolido esta entrada. Me ha dolido mucho. Os quiero a todos. Espero no defraudar en esto, o en lo otro. Pero sólo tenía vergüenza encima y no podía seguir viviendo así.

Ojalá esto despierte a otros.

Osakidetza y cómo cagarla con un Bash-script (2/2)

Bueno pues.

Después de toda la espera toca ponerse a destripar el script del infierno que ya presenté aquí.

Aviso de que se me va a ir de longitud este post. Nada raro en mí.

Antes de nada deciros muy seriamente un par de cosas:

No está bien meterse con el trabajo de los demás. Este script lo ha hecho una persona. Por lo que veremos, seguramente no tendrá mucho conocimiento de GNU/Linux pero programa bastante limpio así que seguramente sea programador, posiblemente de Java (el programa está hecho en Java), cosa que hará bastante mejor que yo.

El objetivo de esto no es la burla, es el aprendizaje. Tanto el mío como el vuestro. No voy a clavar perfecto todo lo que comente aquí, igual que ocurre con todo lo que escribo en el blog, pero este es un lugar para la reflexión y el análisis positivo.

Escribo en clave de humor y vacilo a Osakidetza y a otros medios gubernamentales porque su labor es hacer las cosas bien (como la de todos) pero tiran de subcontrataciones chapuceras y no tienen un control real de lo que reciben a cambio.

Ni media broma al currela que se parte el lomo por una miseria en una consultora carnicera para hacer un script en un lenguaje que no forma parte de su curro.

Todo mi desprecio a las consultoras carniceras que sólo quieren que parezca que han hecho algo y ponen a cualquiera a hacer el trabajo y le presionan para que funcione y a todos los que contratan a este tipo de esclavistas y estafadores modernos.

Dicho esto, vamos a analizar el código. No sólo lo que esté mal (según mi criterio, ojo) si no que vamos a intentar fijarnos en el estilo y otras cosas. Veremos si lo consigo.

Aquí os dejo el script, pulsad para expandirlo. Lo dejo recogidito porque iré sacando pedazos más abajo, sólo está como referencia.

#!/bin/bash
# This script attempts to find an existing installation of Java that meets a minimum version
# requirement on a Linux machine.  If it is successful, it will export a JAVA_HOME environment
# variable that can be used by another calling script.
#
# To specify the required version, set the REQUIRED_VERSION to the major version required, 
# e.g. 1.3, but not 1.3.1.
REQUIRED_TEXT_VERSION=1.6

# Transform the required version string into a number that can be used in comparisons
REQUIRED_VERSION=`echo $REQUIRED_TEXT_VERSION | sed -e 's;\.;0;g'`
# Check JAVA_HOME directory to see if Java version is adequate
if [ $JAVA_HOME ]
then
    JAVA_EXE=$JAVA_HOME/bin/java
    VERSION=`$JAVA_EXE -version 2>&1 | head -1`
    VERSION=`echo $VERSION | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'`
    VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'`
    if [ $VERSION ]
    then
        if [ $VERSION -ge $REQUIRED_VERSION ]
        then
            JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'`
        else
            JAVA_HOME=
        fi
    else
        JAVA_HOME=
    fi
fi

# If the existing JAVA_HOME directory is adequate, then leave it alone
# otherwise, use 'locate' to search for other possible java candidates and
# check their versions.
if [ $JAVA_HOME ]
then
    :
else
    for JAVA_EXE in `locate bin/java | grep java$ | xargs echo`
    do
        if [ $JAVA_HOME ] 
        then
            :
        else
            VERSION=`$JAVA_EXE -version 2>&1 | head -1`
            VERSION=`echo $VERSION | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'`
            VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'`
            if [ $VERSION ]
            then
                if [ $VERSION -ge $REQUIRED_VERSION ]
                then
                    JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'`
                fi
            fi
        fi
    done
fi

# Get additional weasis arguments
userParameters=()
for var in "$@"
do
if  [[ $var == \$* ]]
then
    userParameters+=("$var")
fi
done
echo user arguments: ${userParameters[@]}

# If the correct Java version is detected, then export the JAVA_HOME environment variable
if [ $JAVA_HOME ]
then
    export JAVA_HOME
    echo Java Home: $JAVA_HOME/bin/java
    curPath=$(dirname "`readlink -f "$0"`")
    echo Weasis launcher directory: $curPath
    $JAVA_HOME/bin/java -Xms64m -Xmx512m -Dgosh.args="-sc telnetd -p 17179 start" -Dweasis.portable.dir="$curPath" -classpath "$curPath/weasis/weasis-launcher.jar:$curPath/weasis/felix.jar:$curPath/weasis/substance.jar" org.weasis.launcher.WeasisLauncher \$dicom:get --portable ${userParameters[@]}
else echo 'Weasis requires Java Runtime '$REQUIRED_TEXT_VERSION' or higher, please install it'
fi

Empezamos resumiendo un poco la labor del script. Básicamente, se encarga de arrancar un programa en Java con un conjunto de argumentos de entrada, toda la lógica sirve para buscar lo que veréis que se llama JAVA_HOME y comprobar la versión del Java que el usuario tenga instalado (VERSION dentro del script).

Los dos if principales (lineas 13 y 35) son los que aseguran esto. Ahora entramos en detalle pero antes una cuestión de estilo.

Podéis ver que el código está bien indentado (con tabs, pero bien indentado) y comentado. Con comentarios no muy anchos para que sean fáciles de leer. Por eso comentaba antes que me da la sensación de que el que lo ha escrito es programador, pero, por las cosas que veremos luego, no controla demasiado de la herramienta que está usando (que, por otro lado, es bastante cabrona y difícil).

Vamos a por esos if:

El primero, que comienza en la línea 13 es el if con lógica. Parte de la variable de entorno JAVA_HOME que suele indicar dónde está nuestra instalación de Java para ejecutar java -version (línea 16) y comprobar si la versión es más grande que la indicada en la línea 8.

# Check JAVA_HOME directory to see if Java version is adequate
if [ $JAVA_HOME ]
then
    JAVA_EXE=$JAVA_HOME/bin/java
    VERSION=`$JAVA_EXE -version 2>&1 | head -1`
    VERSION=`echo $VERSION | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'`
    VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'`
    if [ $VERSION ]
    then
        if [ $VERSION -ge $REQUIRED_VERSION ]
        then
            JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'`
        else
            JAVA_HOME=
        fi
    else
        JAVA_HOME=
    fi
fi

Hasta aquí todo bastante bien. En detalle vemos como necesita diseccionar el resultado de java -version (lineas 16-18) pero tampoco sabría hacerlo mucho más bonito si fuera necesario.

En el if anidado vemos que para recuperar el JAVA_HOME nuevo corta el JAVA_EXE que él mismo ha creado antes (linea 23). No tiene mucho sentido, creo yo, recuperando el JAVA_HOME de más arriba era suficiente. Además lo recorta eliminándole los últimos 9 caracteres, que es exactamente lo que mide /bin/java, que es la parte que le ha añadido a JAVA_HOME antes para crear JAVA_EXE.

Yo ese if lo dejaría directamente así (líneas 19-30):

        if [ $VERSION -lt $REQUIRED_VERSION ] # -lt es less than
            JAVA_HOME=
        fi

Muchos ya os habéis dado cuenta de un par de detalles pero lo iré aclarando al final, no desesperéis. Si luego me olvido pues lo comentáis y listo.

Ese if no tiene mucha más chicha que eso, vamos al siguiente que es donde está la movida guapa.

# If the existing JAVA_HOME directory is adequate, then leave it alone
# otherwise, use 'locate' to search for other possible java candidates and
# check their versions.
if [ $JAVA_HOME ]
then
    :
else
    for JAVA_EXE in `locate bin/java | grep java$ | xargs echo`
    do
        if [ $JAVA_HOME ] 
        then
            :
        else
            VERSION=`$JAVA_EXE -version 2>&1 | head -1`
            VERSION=`echo $VERSION | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'`
            VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'`
            if [ $VERSION ]
            then
                if [ $VERSION -ge $REQUIRED_VERSION ]
                then
                    JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'`
                fi
            fi
        fi
    done
fi

El comentario ya lo dice todo.

Para empezar, usa locate, una herramienta que no viene por defecto normalmente. Cagada. Mejor usar find aunque sea más lento.

O mejor no usar, porque lo que hace es aún más cremas. Mirad la línea 39:

    for JAVA_EXE in `locate bin/java | grep java$ | xargs echo`

Ese for itera por todos ficheros del sistema que cumplen lo siguiente:

  • Contienen bin/java en su nombre
  • Terminan en java

El developer esperaba encontrar /usr/local/bin/java por ejemplo, pero con esa búsqueda también puede encontrar /home/user/projects/example_virus/bin/java_binaries/destroy_all_java, por ejemplo.

Lo divertido es que poco más abajo lo ejecuta, así porque él lo vale. Linea 45:

            VERSION=`$JAVA_EXE -version 2>&1 | head -1`

Repito más claro:

Este programa busca cosas casi aleatorias en tu sistema y las ejecuta para ver si su versión es correcta. Luego, si cumple, lo vuelve a ejecutar con un churro gigante de parámetros de entrada.

Si te encuentra algo que no es, lo va a disparar, sea malicioso o no.

Eso es lo más divertido de ese if, el resto es como el de arriba.

El bloque final es mucho más aburrido. Añade los parámetros de entrada que le metamos en la ejecución a unos que permite que le metas por defecto hardcoded (lineas 59-68) y ejecuta el programa haciendo mil jueguecitos que tiene pinta que sean muy necesarios.

Un par de cosas generales:

No entiendo para qué se necesita volverse loco con la puta variable JAVA_HOME. Si el usuario no es idiota y tiene todo bien instalado con llamar directamente a java es suficiente porque estára en el PATH y listo. Si realmente se necesitase setear la variable para algo interno del programa, con buscar dónde está ese binario sería suficiente, de nuevo, tirando del sistema se puede hacer con un whereis.

Es absurdo porque tras nosecuantísimo procesamiento para buscar la versión y la localización de Java en mi sistema, fue incapaz de localizar el OpenJDK, que sí que es capaz de lanzar el programa con éxito. Esto ocurrió porque en el parseo de las versiones utiliza un formato que es específicamente el que da Java, OpenJDK pone otra cosa diferente aunque parecida:

openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-0ubuntu1.16.04.2-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)

A parte de todo esto, cuando decía que el programador no controla mucho de Bash es porque le veo muchos detalles en los que se nota que no tiene mucha experiencia con la herramienta. Por ejemplo, tiene ifs vacíos porque necesita el else cuando con negar la condición sería suficiente. El uso del locate en lugar de otras herramientas también puede dar una pista.

En general, el programa parece que está copy-pasteado de Stack Overflow. Cosa que todos hacemos, pero a algunos se les nota más que a otros.

🙂

Conclusiones:

  • No ejecutéis todos los ficheros del sistema. Si el usuario no tiene las cosas bien configuradas que le den por el culo. Es mejor eso que ponerse a ejecutar cosas aleatorias que a saber lo que hacen.

  • No seáis tan locos con las versiones porque eso os puede llevar al caso anterior.

  • Si sólo tenéis código para meter en el else quizás negar el if sea la solución más elegante. man [ para que os cuenten un poco de qué va el rollo.

  • Intentad evitar usar programas que no estén por defecto en el sistema. locate vs find. Y, a poder ser, no busquéis porque puede que caigáis en el caso 1.

  • Todos hemos trabajado en una herramienta en la que estamos un poco cojos, está bien buscar en internet, pero no está de más investigar un poco más si las fechas de entrega te lo permiten.

  • Bash es más feo que una nevera por detrás, pero es útil a veces.

  • No trabajes en una consultora cárnica si lo puedes evitar. Intenta cambiar el mundo en otro tipo de trabajos más sociales o éticos. Esos cabrones no te merecen. Es difícil pero lo puedes conseguir.

En fin.

Ya comenté en la primera parte la solución rápida que le metí al script, dejo en vuestras manos una más elaborada.

Un abrazo. Como siempre.

Osakidetza y cómo cagarla con un Bash-script (1/2)

Hola,

Son la una y media de la mañana y no puedo dormir así que os voy a contar una linda historia con moraleja en la que trataré de poneros en mi piel.

Resulta que hace varias semanitas, debido a unos temas, acaba llegando a tus manos una radiografía de la espalda de alguien. Esta radiografía fue pedida a Osakidetza, el servicio Vasco de Salud (comunidad autónoma en la que resides).

El sistema es sencillo. Te hacen una radiografía y, si la quieres, te permiten que rellenes un escrito para pedirla. Poco después te llega en un CD.

Tiene todo el sentido del mundo porque así tienes oportunidad de tener una segunda opinión de un conocido, acudir a otro médico o verla por ti mismo.

Ahora esperaréis que la historia se tuerza porque no se puede ver en Linux pero va a molar mucho más.

Resulta que el CD tiene un Autorun (que en Linux, evidentemente, no se dispara) que despierta una aplicación para poder ver las radiografías. Piensas que con una imagen sería suficiente, pero, como descubrirás después, el programa es mucho más potente y es capaz de tomar medidas, navegar y otras cosas que a los médicos les vienen bien.

Como la aplicación no se dispara, piensas que en Linux vas a estar jodido, pero te motivas y abres el contenido del CD a ver qué se cuece. Resulta que es un programa en Java. ¡Bien! ¡Eso es portable!

Tiene un par de carpetas de Windows y Mac así que esperas que haya para Linux. Efectivamente. Te encuentras con un script de Bash que esperas que levante la aplicación de Java.

Lo lanzas y falla, te dice que no tienes Java instalado. Lo compruebas y tienes el OpenJDK. Por un momento piensas en instalar el Java oficial pero acabas rechazando la idea y abriendo ese script para buscar por qué no te localiza lo que tienes. Te encuentras con esto:

#!/bin/bash
# This script attempts to find an existing installation of Java that meets a minimum version
# requirement on a Linux machine.  If it is successful, it will export a JAVA_HOME environment
# variable that can be used by another calling script.
#
# To specify the required version, set the REQUIRED_VERSION to the major version required, 
# e.g. 1.3, but not 1.3.1.
REQUIRED_TEXT_VERSION=1.6

# Transform the required version string into a number that can be used in comparisons
REQUIRED_VERSION=`echo $REQUIRED_TEXT_VERSION | sed -e 's;\.;0;g'`
# Check JAVA_HOME directory to see if Java version is adequate
if [ $JAVA_HOME ]
then
	JAVA_EXE=$JAVA_HOME/bin/java
	VERSION=`$JAVA_EXE -version 2>&1 | head -1`
	VERSION=`echo $VERSION | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'`
	VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'`
	if [ $VERSION ]
	then
		if [ $VERSION -ge $REQUIRED_VERSION ]
		then
			JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'`
		else
			JAVA_HOME=
		fi
	else
		JAVA_HOME=
	fi
fi

# If the existing JAVA_HOME directory is adequate, then leave it alone
# otherwise, use 'locate' to search for other possible java candidates and
# check their versions.
if [ $JAVA_HOME ]
then
	:
else
	for JAVA_EXE in `locate bin/java | grep java$ | xargs echo`
	do
		if [ $JAVA_HOME ] 
		then
			:
		else
			VERSION=`$JAVA_EXE -version 2>&1 | head -1`
			VERSION=`echo $VERSION | grep "java version" | awk '{ print substr($3, 2, length($3)-2); }'`
			VERSION=`echo $VERSION | awk '{ print substr($1, 1, 3); }' | sed -e 's;\.;0;g'`
			if [ $VERSION ]
			then
				if [ $VERSION -ge $REQUIRED_VERSION ]
				then
					JAVA_HOME=`echo $JAVA_EXE | awk '{ print substr($1, 1, length($1)-9); }'`
				fi
			fi
		fi
	done
fi

# Get additional weasis arguments
userParameters=()
for var in "$@"
do
if  [[ $var == \$* ]]
then
    userParameters+=("$var")
fi
done
echo user arguments: ${userParameters[@]}

# If the correct Java version is detected, then export the JAVA_HOME environment variable
if [ $JAVA_HOME ]
then
	export JAVA_HOME
	echo Java Home: $JAVA_HOME/bin/java
	curPath=$(dirname "`readlink -f "$0"`")
	echo Weasis launcher directory: $curPath
	$JAVA_HOME/bin/java -Xms64m -Xmx512m -Dgosh.args="-sc telnetd -p 17179 start" -Dweasis.portable.dir="$curPath" -classpath "$curPath/weasis/weasis-launcher.jar:$curPath/weasis/felix.jar:$curPath/weasis/substance.jar" org.weasis.launcher.WeasisLauncher \$dicom:get --portable ${userParameters[@]}
else echo 'Weasis requires Java Runtime '$REQUIRED_TEXT_VERSION' or higher, please install it'
fi

Es gracioso porque en menos de 10 minutos lo haces funcionar convirtiéndolo en lo siguiente sin pensar mucho:

#!/bin/bash
# This script attempts to find an existing installation of Java that meets a minimum version
# requirement on a Linux machine.  If it is successful, it will export a JAVA_HOME environment
# variable that can be used by another calling script.
#
# To specify the required version, set the REQUIRED_VERSION to the major version required, 
# e.g. 1.3, but not 1.3.1.
REQUIRED_TEXT_VERSION=1.6



# Get additional weasis arguments
userParameters=()
for var in "$@"
do
if  [[ $var == \$* ]]
then
    userParameters+=("$var")
fi
done
echo user arguments: ${userParameters[@]}

# If the correct Java version is detected, then export the JAVA_HOME environment variable
if [ 0 -eq 0 ]
then
	export JAVA_HOME=/usr/
	echo Java Home: $JAVA_HOME/bin/java
	curPath=$(dirname "`readlink -f "$0"`")
	echo Weasis launcher directory: $curPath
	$JAVA_HOME/bin/java -Xms64m -Xmx512m -Dgosh.args="-sc telnetd -p 17179 start" -Dweasis.portable.dir="$curPath" -classpath "$curPath/weasis/weasis-launcher.jar:$curPath/weasis/felix.jar:$curPath/weasis/substance.jar" org.weasis.launcher.WeasisLauncher \$dicom:get --portable ${userParameters[@]}
else echo 'Weasis requires Java Runtime '$REQUIRED_TEXT_VERSION' or higher, please install it'
fi

Por el camino, te encuentras con unos detalles increíbles que, viendo lo graves que son, decides contar en tu blog.

La única diferencia con el script de arriba es que has borrado todo el bloque de comprobaciones y has hardcodeado JAVA_HOME. Una solución muy sucia que no funcionaría en todos los casos, pero te vale por el momento.

En la parte borrada hay unas joyas estelares. La próxima entrada explicará cuales, por qué y por qué este código a parte de no ser muy bueno es peligroso.

Seguimos otro día. Comentad con lo que encontréis y lo vamos discutiendo. Dentro de unos días pongo mi análisis del código.

¡Un abrazo!

droWMark, postea en WordPress desde Vim

Hola hijos,

Hoy vengo a contaros una cosa loca que he estado desarrollando para mí durante el último año en ratos libres.

Hace tiempo que salió su primera versión pero no lo publiqué porque no saqué tiempo y porque me daba un poco de vergüenza porque era muy cutre, pero ahora que tengo más tiempo (ya os contaré por qué) me he decidido a añadirle unas funcionalidades nuevas y estoy contento.

Vengo a presentaros droWMark, un plugin para Vim que sirve para postear a WordPress desde el editor, escribiendo en Markdown (con sabor a Pandoc). Por si os lo preguntabais, sí, llevo como un año posteando en este foro directamente desde Vim.

Hasta hace muy poquito el plugin no soportaba la subida de imágenes, pero tampoco lo necesitaba porque pocas veces añado imágenes a los posts. 🙂

Os cuento un poco cómo funciona por debajo y si queréis más información os leeis la documentación del plugin, que es bastante clara. Para leerla con entrar en la carpeta doc en el repo es suficiente pero mola más que os lo instaléis (usad Vundle o algo por favor) y hagáis :help drowmark.

El plugin se separa en dos partes, una en VimL (o VimScript) y otra en Python.

La parte en VimL se encarga de gestionar todo lo relacionado con Vim y llamar a la parte de Python. Hace las siguientes cosas:

  • Syntax highlighting de los ficheros especiales que utilizo. Son una mezcla de Markdown con un poco de INI1 en la cabecera (aunque no por mucho tiempo).
  • La documentación del plugin, para poder hacer :help drowmark y esas cosas.
  • Define el nuevo tipo de fichero WP. Para esto necesita detectar el tipo de fichero y marcarlo bien. De esto se encarga ftdetect.
  • Gestionar el post de plantilla para que se pueda hacer :NewWordPress y te prepare el archivo. Simplemente se guarda un fichero de referencia que se escribe en el búfer actual al llamar al comando. Fácil.
  • Llama al script de python de forma ordenada pidiéndole los credenciales al usuario. Mientras escribes la contraseña no muestra nada en la pantalla, pero no está muy fino y si te equivocas y borras no funciona. Si leéis el código entenderéis por qué.

La parte de python es todo lo relacionado con WordPress y la funcionalidad que se quería ofrecer. Así puede separarse y usarse sola o migrarse a otros editores. Tengo pendiente migrar a Emacs pero necesito la ayuda de mi divertida hermana Emacsera para eso. La parte de Python hace lo siguiente por el momento.

  • Captura las opciones puestas en la cabecera INI y construye un blogpost con ellas.
  • Convierte el MarkDown a HTML usando panflute. En las versiones anteriores lo hacía con pypandoc.
  • Durante la conversión (esto es la novedad de la versión 1.1) busca todas las imágenes en el texto y las sube si son ficheros locales cambiando la URL para que referencia al recién subido archivo. Lo mismo ocurre con el campo thumbnail de la cabecera.
  • Postea el documento convertido.

Y eso es todo.

Fijaos que esta entrada tiene un thumbnail (o imagen destacada) y una imagen aquí debajo. Están únicamente puestas para demostrar que puedo.

Imagen puesta para fardar

No os toméis este Plugin como la mejor manera de hacer las cosas, sólo quiero que veáis que cuando nos apetece cambiar nuestro workflow con un poco de esfuerzo podemos conseguirlo. No hace falta volverse muy loco.

Si leéis el código fuente veréis que es un script de Python muy corto y bastante cutre. A veces merece la pena programar agresivo si el programa cumple con lo que debe.

Ahora mismo estoy en ese proceso de ir convirtiéndolo en un programa razonable, pero, de momento, es un poco mierda porque funciona y para mí es suficiente. Si empezamos grande a veces nos aburrimos antes de terminar. Mejor empezar pequeño y cuando la cosa dé unos frutos recuperar la motivación para poder crecer.

¡Espero que os mole y lo probéis!

Comentarios y críticas, en la sección de abajo.

Un abrazo.


  1. INI son los ficheros como la configuración de Git, hice un parser de esto en C hace un tiempo