La importancia del ‘estilo’ al programar
Codifica como si el que fuera a mantener tu código fuese un manÃaco asesino que sabe donde vives.
software development as an artistic expression
Codifica como si el que fuera a mantener tu código fuese un manÃaco asesino que sabe donde vives.
En uno de los ‘pet-projects‘ en los que ando he intentado construir todo un poco académicamente rollo RESTfull (o casi full :) ) y tal.
Pero lo que nos acomete ahora es que he intentado soportar en todas las acciones de controlador en formato .xml.. ¿para qué?.. pues no sé.. pero ahà que lo he dejado.. sin testear ni nada, por si acaso.
Resulta que el otro dÃa me levanté inquieto por la mañana con el pensamiento de que las si se hacÃa una petición a una acción con el formato .xml la respuesta que le iba a llegar al cliente no me iba a gustar nada.. pero luego se me pasaba y me olvidaba.
Bueno pues la acabo de hacer, acabo de hacer la petición con el formato .xml y, confirmando mis miedos, la respuesta no me ha gustado nada.
http://127.0.0.1:3000/people/
<user> <activated-at type="datetime">2009-02-02T01:01:22+01:00</activated-at> <activation-code nil="true"></activation-code> <company-name>Mi company</company-name> <company-url>http://micompany.com</company-url> <created-at type="datetime">2009-02-02T01:01:10+01:00</created-at> <crypted-password>8e2434d3e574c95f4cfd0f457b20c8959ecbf04b</crypted-password> <iail>xxxx@xxx.com</iail> <id type="integer">103</id> <login>fguillen</login> <name>Fernando Guillen</name> <password-reset-code>fcc786cd2137679f1055976989f5469607c9d663</password-reset-code> <permalink>fernando-guillen</permalink> <personal-web-name>mi web</personal-web-name> <personal-web-url>http://google.es</personal-web-url> <public-profile type="boolean">true</public-profile> <remember-token nil="true"> <remember-token-expires-at type="datetime" nil="true"> <role>User</role> <salt>5c26f52452d00dbd997f8c6998f5c3de1e4f53f3</salt> − <text> Vaya texto guapo que pongo aquÃ: Con varias lÃneas y alert("hola"); </text> <updated-at type="datetime">2009-02-04T18:52:21+01:00</updated-at> </remember-token-expires-at> </remember-token></user>
En la respuesta están todos los datos del usuario 103.. ¡todos! hasta la password, aunque está cifrada.. y ¡¡el email!!.. y si hacemos:
http://127.0.0.1:3000/people.
Entonces es cuando casi me desmayo..
Alguien podrÃa descargarse toda la base de datos de mi aplicación a base de peticiones .xml o usando ActiveResource.. ¿qué hago ahora?…
Antes de liarme a postear de manera alarmista en éste mi blog, sin solución alguna que ofrecer, comenté estas inquietudes en la sagrada lista ror-es y aparecieron el señor Blat y Pablo Formoso al rescate con un buen par de links y sugerencias:
Estos dos links te enseñan a pasarle parámetros a las invocaciones del método .to_xml de tus modelos de tal modo que puedes poner en tus controladores cosas como esta:
format.xml { render :xml => user.to_xml( :except => [:email, :crypted_password] ) }
Y asà empecé pero no me acabó de convencer pues tengo miedo de olvidarme algún controlador y decido pasar la seguridad al modelo y sobrescribir el método .to_xml de la clase User. Pero los métodos para hacer esto que se sugieren por ahà son un poco tediosas con llamadas a Builder::XmlMarkup y vamos.. un rollo. Lo suyo serÃa trasladar la facilidad del :except y el :only a la sobrescritura del .to_xml y en los comentarios de este post tenemos una solución bien ingeniosa:
alias_method :ar_to_xml, :to_xml def to_xml(options = {}) default_except = [ :activated_at, :activation_code, :email, :login, :password_reset_code, :permalink, :public_profile, :role, :crypted_password, :salt, :remember_token, :remember_token_expires_at, :created_at, :updated_at ] options[:except] = (options[:except] ? options[:except] + default_except : default_except) self.ar_to_xml( options ) end
Sobre todo fÃjate en el truqui del alias_method para seguir permitiendo llamadas al comportamiento original del to_xml.
Si sigues los comentarios del hilo que te he indicado verás que las propuestas continúan hasta formar un solución polivalente que bien se puede convertir en un buen parche para Rails.
Me he picado una extensión de Paperclip para porveer de validaciones de dimensiones para las imágenes adjuntas aquà la comparto contigo por si te resulta útil.
No he hecho un parche para Paperclip pues eso supone un gran esfuerzo en tests y demás y eso tendrá que esperar un poco.
Lo he organizado todo para que copies y pegues el código en un fichero en /lib y lo requieras desde el environment.rb con eso deberÃa valer pero úsalo bajo tu entera responsabilidad :)
La extensión provee de dos nuevos validates:
Que se pueden usar con toda la convinación de opciones que el nativo ‘validates_attachment_size‘.
Por ejemplo:
validates_attachment_width :photo, :greater_than => 576, :less_than => 1000 validates_attachment_height :photo, :greater_than => 150, :less_than => 300 validates_attachment_width :photo, :in => 576..1000 validates_attachment_height :photo, :in => 150..300, :message => "file height must be between :min and :max pixels."
Tenemos por ejemplo la validación del ancho:
def validates_attachment_width name, options = {} min = options[:greater_than] || (options[:in] && options[:in].first) || 0 max = options[:less_than] || (options[:in] && options[:in].last) || (1.0/0) range = (min..max) message = options[:message] || "file width must be between :min and :max pixels." attachment_definitions[name][:validations][:width] = lambda do |attachment, instance| if attachment.file? && !range.include?( Geometry.from_file(attachment.queued_for_write[:original]).width.to_i ) message.gsub(/:min/, min.to_s).gsub(/:max/, max.to_s) end end end
El código completo lo tienes aquÃ: paperclip_validations_extended.rb.
Un ejemplo de tests y usos lo tienes aquÃ: paperclip_validations_extended_test.rb.
A mi me está funcionando.. y a t�
En un hilo de ror-es con tÃtulo: ‘Problema con carga de rubygem en App rails al hacer rake db:create && rake db:migrate‘, Andrés Gutierrez se debatÃa con las versiones de las gemas solicitadas por un proyecto Rails que esta intentando arrancar.
La configuración del proyecto le requerÃa tener instalada una gema y para indicarle la versión se utilizaba el operador ‘~>‘.. no el ‘==’, el ‘>’, el ‘>=’.. no, tenÃa el simbolito raro ese de la ñ.
En concreto la especificación de la gema requerida es tal que asÃ:
config.gem 'mislav-will_paginate', :version => '~> 2.2.3', :lib => 'will_paginate', :source => 'http://gems.github.com'
Su duda venÃa de que él tenÃa instalada la versión 2.3.6 de la gema en concreto y no entendÃa porque no cumplÃa los requisitos.
El caso es que me piqué y al más puro ‘estilo Daniel R. Troitiño‘ ahà que fui a las tripas del código a ver que demonios hacÃa el operador ése..
Si miramos el fichero:
$ mate rubygems/requirement.rb
Tenemos la lista de operadores y lo que se hace con cada uno:
OPS = { "=" => lambda { |v, r| v == r }, "!=" => lambda { |v, r| v != r }, ">" => lambda { |v, r| v > r }, "<" => lambda { |v, r| v < r }, ">=" => lambda { |v, r| v >= r }, "<=" => lambda { |v, r| v <= r }, "~>" => lambda { |v, r| v >= r && v < r.bump } }
Donde ‘v‘ parece ser la versión disponible y ‘r‘ la requerida
Lo raro del operador ~> es que a la versión requerida le hace un bump
y el código de esto está en otro fichero:
$ mate rubygems/version.rb
Aquà lo pego:
# Return a new version object where the next to the last revision # number is one greater. (e.g. 5.3.1 => 5.4) def bump ints = build_array_from_version_string ints.pop if ints.size > 1 ints[-1] += 1 self.class.new(ints.join(".")) end
Al parecer a la versión requerida en la aplicación, en este caso ‘2.2.6‘, le hace un bump y la deja en ‘2.3‘ y por lo tanto la
condición:
'2.3.6' < '2.3'
No se cumple .. como podemos comprobar aquÃ:
irb> >> req = Gem::Requirement.create("~> 2.2.3") >> req.satisfied_by?( Gem::Version.new('2.3.6') ) => false >> req.satisfied_by?( Gem::Version.new('2.2.6') ) => true >> req.satisfied_by?( Gem::Version.new('2.1.6') ) => false
Lo que parece que quiere el operador ~> es que la gema disponible esté
en la misma familia X.X que la gema requerida.
Hasta aquà he llegado. Si por favor conoces la documentación oficial donde se explica pásame el link para darme bien de tollejas.
Sino conoces github no sigas leyendo.. y échale un vistazo, sino es el caso igual te interese este truquito que he descubierto trasteando.
Resulta que si quieres compartir un link de un fichero de un proyecto en github puedes navegar por los ficheros llegar al que quieres compartir y copiar el link que muestra la página, algo como esto:
http://github.com/fguillen/euruko_app/blob/b94f2458b54fdea82e970810de5e9bbe9f63a385/doc/TODO.md
Y bien, funciona, o eso parece.. pero no, porque entonces estarás compartiendo el estado congelado del fichero en un commit determinado osea que cuando tus amigos sigan el link dentro de unos diás, después de unos commits, seguirán viendo la misma versión ya anticuada.
El truco para compartir un link que siempre tenga la última versión disponible es sustituir el identificador del commit por el alias de ‘último commit‘ que es ‘HEAD‘, tal que asÃ:
http://github.com/fguillen/euruko_app/blob/HEAD/doc/TODO.md
Ahà queda :)
Estoy instalando Passenger (aka mod_rails) en el servidor de staging porque es lo que está ahora de moda para poner aplicaciones Rails en producción y la verdad que empiezo a entender porque.
La instalación es super sencilla, se instala como una gema:
$ sudo gem install passenger
Que en realidad lo que instala es el instalador en sÃ, que se encarga de buscar tu apache, compilar los módulos, buscar posibles dependencias que falten, decirte como arreglar estas dependencias y finalmente explicarte como activar el módulo y como configurar el apache para activar tu primera aplicación rails.
Osea que más que un instalador es un tutorial super amigable y fácil de seguir.
Una vez todo instalado y configurado mi primer virtual host apuntando a mi aplicación Rails intento acceder a ella y salta un error, pero no es un error de esos feos, de casque total que no sabes lo que es y tienes que empezar a navegar por los logs para detectarlo, no.. el error aparece en forma de página web dispensada por el propio Passenger que te explica amable y elegantemente que no tienes Rails 2.2.2 instalado y el comando que debes ejecutar para arreglarlo.. super simpático.
Instalado rails 2.2.2 me salta otro error, debidamente capturado otra vez por el módulo y con varias explicaciones, posibles causas y consejos para resolverlo.. en este caso me falta el database.yml.. normal que no funcione.. pero lo increÃble es lo majo que está siendo conmigo Passenger.
Arreglado esto intentamos volver a acceder a la aplicación.. pum! nuevo error, falta una gema.. no pasa nada Passenger también me sugiere “A required library may not installed. Please install all libraries that this application requires.”.
Error de acceso a BD, ups! Passenger no me ha dado feedback de esto, una simple pantalla blanca con un poco sutil “500 Internal Server Error“. Ha habido que acudir a los logs para detectar la causa.. un punto menos Passenger :)
Toma otro 500 en frÃo, no habÃa corrido las migraciones, ya lo sabÃa, era para ver como se portaba Passenger, parece ser que una vez arrancada la aplicación ya deja de capturar los errores, una pena, con lo bonitos que los muestra.
La balanza se inclina muy favorablemente y estoy encantado con Passenger.
Estaba yo viendo el screencast éste sobre notificación de excepciones del grandÃsimo Ryan Bates y me puse a instalarlo, que son, calculando por lo alto, un par de minutos. Y cuando intento acceder al listado de excepciones veo que no funcionan los links ni las acciones ajax de borrar y tal… ouch¡.. resulta que está pensado para trabajar con Prototype y yo tengo todo por defecto con jQuery… asà que nada, gracias al gran poder forkeador de github me puse a modificar el plugin para que soporte jQuery y aquà tenéis el resultado:
Instalarlo tal que asÃ:
$ git clone git://github.com/fguillen/exception_logger_jquery.git vendor/plugins/exception_logger
Si queréis paginación hay que instalar la gema will_paginate y tocar el init.rb:
$ sudo gem install will_paginate
# init.rb require 'will_paginate' $PAGINATION_TYPE = 'will_paginate'
No estaban todos los que eran ni eran todos los que estábamos pero si es cierto que se logró reunir a un buen grupo de Monstruos del Ruby.
Sábado 13 de Diciembre de 2008, 14horas, una convocatoria vÃa email logró reunir a rubistas de todo el estado en una sola habitación para enfrentarse a un reto por equipos.
Se trataba de enfrentarse a un fast-code de una aplicación web usando para el desarrollo de cada una de las capas herramientas que empiezan a emerger y de las que carecÃamos de ningún dominio.
Nos mostraron como especificación una aplicación hecha en Rails llamada Frankenstein, para cuyo desarrollo emplearon un par de horas y otro poco para otorgarle un sugerente diseño.
Nos dividimos por equipos atendiendo a la suerta y también por sorteo se fueron repartiendo las herramientas (librerÃas) que cada equipo podÃa emplear.
A nuestro monstruoso equipo (los nosferatu) nos tocó:
Pero hubo combinaciones mucho más dolorosas.. :) que ahora no logro recordar.
Nuestro resultado lo podéis encontrar aquÃ: Nosferatu, pero está totalmente incompleto y dudo que logréis hacerlo funcionar sin nuestras explicaciones. El resto de grupos y resultados los tenéis en el grupo monstersofruby de github.
A mi parecer no se puede entender lo logrado en esta reunión viendo el código generado. Lo que se ha conseguido va mucho más lejos del código, para mà ha sido una apertura de mente hacia las tecnologÃas que desconozco pues me da una pereza horrible enfrentarme a la primera lÃnea de código, pero esta experiencia me ha demostrado que no es tan complicado y que hay muchas estrellas en el cielo por conocer.
Además también ha sido otra demostración más de la amigabilidad, sociabilidad, apertura y deseo de compartir que embriaga a toda esta peña entusiasmada con la programación.
Muchas gracias a todos.. ¡hasta la próxima! .. qué por cierto ya se está cociendo.
Tengo la mala costumbre de borrar los ficheros desde mi editor de texto asà que cuando vuelvo a consola y hago un ‘git status‘ me salen un montón de ficheros borrados y no encuentro el comando de ‘git‘ para decirle: cárgatelos todos en el próximo commit.
Asà que me he hecho este script para decirle exactamente eso:
$ git rm `git status | grep "deleted" | cut -f 2 -d ":" | cut -d " " -f 5`
CuidadÃn con él eh :).. a ver si no va a estar bien..
Actualización: como dice Juanma en los comentarios podemos hacerlo mucho más elegantemente con:
git add -u
El nuevo generador de scaffold de RoR 2.2 no se lleva bien con Texmate, la nueva sintaxis para tests impide que usemos el utilÃsimo atajo de teclado ‘ctrl + shift + R‘ para ejecutar solamente uno de los tests de todo el fichero.
Este script convierte las lÃneas tipo:
test "should get new" do
en:
def test_should_get_new
Seguro que esto le espanta a más de uno, pero mientras Texmate no soporte la nueva sintaxis yo tampoco la soporto :). El script es la primera versión que me ha salido, se aceptan mejoras.
El script:
if ARGV[0].nil? puts "Indica la ruta al fichero" exit end if not File.exists?(ARGV[0]) puts "Fichero no encontrado: #{ARGV[0]}" exit end result = "" File.open(ARGV[0]).read.each do |line| if line =~ /^\s*test ".*" do$/ line.gsub!( /\"\sdo\s*/, "" ) line.gsub!( /"/, "" ) line.gsub!( /^\s*/, "" ) line.gsub!( /\s/, "_" ) line.gsub!( /^/, "def ") line = " " + line + "\n" end result << line end File.new( ARGV[0], "w+" ).puts( result )
a Freelance Web Developer is proudly powered by
WordPress
Entries (RSS)
and Comments (RSS).
Fernando Guillen's blog by Fernando Guillen is licensed under a Creative Commons Attribution-NoDerivs 3.0 Unported License.