Si quieres suscribirte al feed RSS del blog, este es el canal por defecto: http://www.nohaycuchara.com/feed/
Not safe for work

Tag "Desarrollo web":

PythonSoy un maniático, lo reconozco. Mis amigos se pasan el día descolocándome las cosas para echarse unas risas mientras ven que no puedo soportar dejarlas así. Podrían hacerlo 50 veces seguidas, que yo iría otras 50 a dejarlo a mi gusto. Todo tiene que estar alineado, ordenado por tamaños, colores o cualquier otra estúpida característica que me saque de la manga. Qué se le va a hacer.

Esto, lejos de ser una jodienda (que a veces lo es, pero para los demás :D ), se ha convertido en una gran ventaja. A la hora de retocar algo, me encuentro con ficheros perfectamente estructurados, con el mismo formato e indentados de la misma manera. De mejor o peor manera, sí, pero de la misma. Y eso es una suerte cuando manejas proyectos de bastante calado.

El problema de esto es que no soy él único con manías. Cuando tengo que compartir código con otra persona que también las tiene, pero distintas, es un infierno. O lo que es peor, que no las tiene. En ese momento, emergen de la nada un ángel y un demonio, cada uno encima de uno de mis hombros. Uno me dice que lo modifique todo, y otro que pase del tema (y ya no sé cuál es cuál). Es entonces cuando pienso: ¿no hay forma de estandarizar esto? Leer más…

DetectiveEn el tiempo que llevo trabajando en este sector he conocido muchos tipos de clientes. Desde los más normales e incluso comprensivos, hasta el que se cree que tienes un botón en el teclado que pone “hacer web”, el que te pide colores “frescos pero serios” o el clásico “no sé qué ha pasado, yo no he tocado nada”. Este último será el que reciba hoy nuestra atención.

Prueba a hacer un CMS con opciones críticas, de esas que sirven para borrar algo importante o modificar un campo que utilizan muchos servicios relacionados. Pon un texto informativo al lado en rojo chillón y una confirmación con JS seguida de otra de lado del servidor. Da igual. Al final, algo se romperá y un agujero negro engullirá los pocos indicios que podían ayudarte a deshacer el entuerto.

Cuando la información es tan sensible que perderla significa perder dinero e incluso puestos de trabajo, saber qué ha pasado es fundamental. Por ello, y a falta de un buen sistema de backup, conviene dejar constancia en el log de todo lo ocurrido.

Para conseguirlo, y ya que mi framework de trabajo es RoR, he preparado un initializer que cumple con esto que os comento. Haciendo uso de varios callbacks, informa de los cambios que ha sufrido un objeto o de los atributos que tenía antes de ser borrado. Todo ello suponiendo que el modelo herede de AR e indicando la fecha y hora del suceso.

Como no son muchas líneas, lo pego directamente. Espero que os sea de utilidad :) : Leer más…

CucumberVa, os dejo unos segundos para que os riáis a gusto… ¿ya? No, ¿que si me instalo el pepino dónde? xD :D
En serio, dejémonos de coñas (ahora viene el contenido de verdad):

Algunas -acertadísimas- decisiones estratégicas del departamento en el que curro me han llevado a dar un paso más en mis desarrollos con Rails: empezar con el BDD. Para ello, me he puesto manos a la obra con Cucumber, mejorando la experiencia al combinarlo con Mundo Pepino. Pero claro, no todo iba a ser de color de rosa, y es que es imposible instalar la gema si trabajamos bajo una distro basada en Debian (como Ubuntu, el cual es mi caso).

La solución a esto es hacernos con un par de paquetes de los que depende, así que ejecutamos el siguiente comando como superusuario:

  1. apt-get install libxslt1-dev libxml2-dev

Y ya está, con esta tontería nos acabamos de librar del error que escupe justo después de “Building native extensions“.

Se da el caso en que a veces, cuando utilizamos Paperclip, necesitamos que el path de un mismo adjunto sea diferente según su origen. Esto viene bien para guardar su información relacionada en el mismo campo (MIME type, tamaño y nombre), pero flexibilizando al máximo su ubicación. Para ello, esta popular gema de Rails pone a nuestra disposición una característica muy útil: las interpolaciones.

Con ellas podemos mandar un parámetro extra (mediante un campo oculto, por ejemplo) al modelo para setear cualquier dato que necesitemos utilizar después.

PaperlipsPrimero, creamos un “initializer” con el siguiente contenido:

  1. Paperclip::Interpolations[:folder] = proc do |attachment,style|
  2.   attachment.instance.folder
  3. end

De esta manera estamos habilitando el uso de la palabra reservada “:folder” en la instrucción “has_attached_file“, así:

  1. has_attached_file :att, :path => ":folder/:id.:extension"

Hace unos meses tenía entre manos una aplicación que consumía Web Services de una API que servía la información en varios formatos (XML, YAML, texto plano y PHP serializado -entre otros-). Para ello se ideó un proceso que con el tiempo se vio que era bastante chapucero, pues se basaba en varias peticiones HTTP y la escritura de su resultado en archivos temporales. Debido a ello, en ocasiones el rendimiento caía en picado, cosa que pasaba hasta que descubrí la forma correcta de manejar este flujo de datos:

Web Services

  1. def get_data(data_url)
  2.   begin
  3.     url = URI.parse(data_url)
  4.     res = Net::HTTP.get_response(url)
  5.     YAML.load(res.body)
  6.   rescue Exception => e
  7.     Rails.logger.error "ERROR: #{e}"
  8.     false
  9.   end
  10. end

Nueve sencillas líneas de código es lo único que necesitamos para lograr este objetivo. Primero, lo encapsulamos todo en un bloque que pueda capturar una posible excepción (“begin [...] rescue“). En la primera parte del mismo obtenemos el contenido y lo parseamos con la herramienta correspondiente al formato de salida que elijamos (YAML en este caso). En la segunda, guardamos en el log el nombre del problema ocurrido y devolvemos “false“.

Phusion PassengerParece ser que una de las últimas versiones de Passenger (“mod_rails” para los amigos), la 2.2.4, tiene un bug bastante conocido pero sin solución aparente: la carpeta temporal se crea sin permisos de escritura.
Como en todos los lenguajes de servidor, a la hora de subir un archivo se copia el mismo a un directorio donde permanece hasta que la transferencia ha llegado al 100%; pero en este caso es antes cuando sucede el error, ya que dicha transferencia no llega ni a comenzar.

Como hemos dicho antes, se trata de un tema de permisos. Podríamos pensar en dárselos sin más, pero esto sería sólo una solución temporal, pues este path se renueva cada vez que reiniciamos el server. Para arreglar esto de forma definitiva, deberemos abrir el archivo “Utils.cpp” y buscar la siguiente línea:

  1. makeDirTree(tmpDir + "/webserver_private", "u=wxs,g=,o=", workerUid, workerGid);

Una vez la hayamos encontrado, la sustituimos por esta y reiniciamos el proceso:

  1. makeDirTree(tmpDir + "/webserver_private", "u=wxs,g=wx,o=wx", workerUid, workerGid);

Fuente: Fermín, mi compañero de Jet. :D Yo estaba a su lado, pero en realidad el cerebrito de todo esto es él, ¡gracias tío! ;)

PostgreSQLRoR tiene muy buena integración con todos los tipos de BBDD existentes, aunque a veces su compatibilidad con algunas características deja mucho que desear. Este es el caso de los “schemas” de PostgreSQL, que para ser usados en la sentencia “set_table_name” de un modelo necesitan que pongamos el siguiente código en el fichero “environment.rb“:

  1. ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
  2.   def quote_table_name(name)
  3.     name
  4.   end
  5. end

CSVSiguiendo con lo que os contaba en el post anterior, en otra ocasión tuve que importar un CSV, para lo cual disponemos del un módulo cojonudo con ese mismo nombre. Además de otras muchas cosas, nos permite leer y parsear un archivo con sólo escribir esto:

  1. CSV::Reader.parse(file, ';') do |row|
  2.   # Cálculos, operaciones, mandangas varias…
  3. end

Como veis, se trata de un bucle que saca una línea cada vez, permitiéndonos operar con ella como si de un array se tratase. Si en una de estas operaciones nos decidimos a insertar los datos directamente en la BBDD, podemos encontrarnos con el siguiente fallo:

  1. RuntimeError: ERROR    C22021    Minvalid byte sequence for encoding "UTF8": 0xe17271
  2. HThis error can also happen if the byte sequence does not match the encoding expected by the server, which is controlled by "client_encoding".
  3. F.\src\backend\utils\mb\wchar.c    L1578
  4. Rreport_invalid_encoding: INSERT INTO personas (nombre, apellido) VALUES('Pepito', 'Garc?a') RETURNING id

Si nos fijamos en los valores del “insert” que intentamos ejecutar, vemos que hay una interrogación (que en este caso se corresponde con una “í“). Esto se debe a que, al ser de texto plano, el archivo no cuenta con ninguna indicación que le permita al parser saber qué codificación tiene, por lo que debemos decírselo nosotros con una simple instrucción que nos brinda “Iconv“:

  1. Iconv.conv('UTF-8', 'ISO-8859-15', campo)

Si conocemos el charset de entrada se lo podemos poner directamente (tal y como hemos hecho ahora). Sino, nos será de gran ayuda una librería como CharGuess, que se encarga de hacer diferentes comprobaciones para decirnos, finalmente, qué encoding cree que se ha encontrado. Es importante remarcar esto último, pues el resultado siempre será una mera conjetura de la aplicación, nunca una verdad absoluta.

File transferTrabajando con Ruby te das cuenta de que en Internet está todo lo que puedas imaginar. No necesitas cursos, profesores ni ejemplos. Nada. Cualquier ayuda extra es bien recibida, aunque sin ella también puedes llegar a la resolución de ese problema que te lleva comiendo la moral una semana.

Eso sí, ser autodidacta tiene dos grandes pegas:

  • Sólo aprendes lo que te exige la experiencia, el día a día.
  • Generalmente, inviertes más tiempo de lo deseable en ello.

Esta pequeña introducción viene a que el otro día, intentando hacer algo tan trivial como copiar un archivo de una ubicación a otra, me encontré con varias supuestas soluciones bastante confusas o erróneas. Pese a esto, que probablemente sea uno de los detonantes del segundo punto, conseguí llegar al final de la historia. Todo ello me llevó a querer postear más a menudo sobre temas breves, sencillos y que no deberían ser problemáticos, pero que a veces nos consumen más tiempo del que deberían…

Al grano

Según un montón de webs, basta con hacer un simple “File.copy“… Nada más lejos de la realidad, pues si lo intentamos sólo conseguiremos ver el siguiente error:

  1. undefined method 'copy' for File:Class

Como no queremos crear un nuevo objeto de la clase “File” ni nada parecido (que es lo que intuyo que necesita el comando anterior), debemos echar mano del módulo “FileUtils“. Este es el más indicado cuando queremos ejecutar métodos de bajo nivel, de entre los cuales elegiremos “copy_file“:

  1. FileUtils.copy_file origen, destino

Fácil, ¿no? :)

RubyGemsEste bonito mensaje nos aparecerá si acabamos de instalar Ruby y al intentar meter Rails nos damos cuenta de que necesitamos actualizar nuestro ya anticuado RubyGems (< 0.8.5, 1.1 o 1.2). A pesar de ser lo contrario, dichas versiones se empeñan en pensar que son las más modernas, lo que nos obligará a sacarlas como sea del sistema. Esto lo podemos hacer manualmente o ejecutando estos dos sencillos comandos:

  • gem install rubygems-update
  • update_rubygems
Página 1 de 3123