Como contribución a la fabulosa y creciente comunidad Ruby, y dado que no soy un Ruby's Skilled Guru como para hacer artículos realmente extraordinarios, les dejo esta traducción del original Ruby on Rails Rake Tutorial (AKA How Rake turned me into an alcoholic) via RailsEnvy que he realizado en mis tiempos libres en estos días.
Como desarrollador Rails probablemente estás familiarizado con correr "rake" en tus test porque has usado "rake db:migrate" para correr tus migraciones. Pero ¿Realmente entiendes que es lo que sucede dentro de las tareas de Rake? ¿Sabías que puedes escribir tus propias tareas o crear tu propia biblioteca de útiles archivos Rake?
Aquí hay unos pocos ejemplos de como he usado tareas Rake
En este artículo vamos a descubir la razón por la que Rake fue creado, y como esto nos puede ayudar en nuestras aplicaciones Rails. Al final, deberías poder escribir tus propias tareas, y aprender como mearte estando borracho en no menos de tres pasos.
Este artículo se encuentra disponible también en Francés, Ruso, Chino, y Polaco.
Para entender porqué tenemos Rake, necesitamos primero dar una mirada a Make, el viejo abuelo de Rake.
Viaja conmigo por un momento hacia atrás, a los viejos días en los que cada pieza de código tenía que ser compilada, antes que los lenguajes interpretados y los iPhones llenasen la Tierra.
Volviendo entoces cuando descargas programas grandes ellos vienen como un montón de lineas de código fuente y un script shell. Este script shell debería contener cada línea de código necesitada por tu ordenador para poder compilar/enlazar/construir la aplicación. Deberías correr "install_me.sh" (el script shell), entonces cada línea de código correría (tipicamente compilando el código fuente), y deberías poder obtener un ejecutable que podrías ejecutar.
Esto ha funcionado bien para la mayoría de la gente, excepto que seas uno de los pocos desafortunados desarrolladores del programa. Cada vez que debías hacer un pequeño cambio al fuente y deseabas testearlo, debías tener que volver a ejecutar el script shell el cual recompilaría todo de nuevo. Obviamente esto trajo muchos problemas grandes por un largo tiempo.
En 1977 (el año en que nací) Stuart Feldman de Bell Labs inventó "Make", el cual resolvió el problema de esos largas compilaciones. Make se usa para compilar programa del mismo modo, pero con dos grandes avances:
También debería explicarse que "Make" es simplemente un programa ejecutable como "dir" o "ls". Para que make entienda como compilar un programa, se necesita crear un archivo "makefile" el cual receferencia a todo el fuente y las dependencias. los "makefiles" tienen su propia sintaxis críptica de la cual no necesitamos saber nada.
Make ha evolucionado a traves de los años y comenzaron a usarlos otros lenguajes de programación. De hecho, los programadores Ruby lo usaron antes que Rake llegase.
"Pero Ruby no es un lenguaje compilado, ¿Para qué lo usarían los programadores Ruby?" Escucho que exclamas.
Si, Ruby es un lenguaje interpretado y no necesitamos compilar nuestro códido.
¿Por qué razón hubieron programadores de Ruby usando archivos Make?
Bien, por dos razones:
¿Cómo obtuvimos Rake?
Bien, hace unos años atrás Jim Weirich se encontraba trabajando en un proyecto Java en el que estaba usando Make. Mientras trabajaba con su Makefile se dio cuenta de cuan conveniente podía ser si pudiese escribir pequeños snippets de Ruby dentro de su makefile. Por lo que el creó Rake. Tuvimos la suficiente suerte de encontrarnos con Jim en RailsConf el mes pasado, y realmente es un tipo agradable.
Jim incorporó la habilidad de crear Tareas, hacer tracking de dependencias de tareas, y tambien incorporar reconociento de timestamp (reconstrucción unicamente de archivos modificados desde la última compilación). Obviamente esta característica no es usada frecuentemente, dado que no compilamos.
Siempre me soprendí de lo que "Jim Weirich" hizo, y ahora lo conozco también! Jim nunca tuvo intenciones de escribir este código, supongo que nació de el.
Inicialmente quise titular a esta sección "Como quedar tirado con Rake", pero claro, esto no es muy intuitivo.
Digamos que quiero emborracharme, ¿Qué pasos necesitarían involucrarse?
Si yo quiero usar Rake para llamar a cada una de estas tareas, debería crear un archivo llamado "Rakefile" el que contendría algo así:
task :comprarAlcohol do puts "Vodka comprado" end task :mezclarTrago do puts "Caipiroska mezclada" end task :estarDescerebrado do puts "Uhh loco..., veo elefantes rosadoooossss" end
Entonces puedo correr estas tareas desde el mismo directorio que mi archivo rake, algo como esto:
$ rake comprarAlcohol Vodka comprado $ rake mezclarTrago Caipiroska mezclada $ rake estarDescerebrado Uhh loco..., veo elefantes rosadoooossss
Muy bien! Sin embargo, desde el punto de vista de las dependencias, yo podría correr estas tareas en cualquier orden. Algunas veces yo podria "estarDescerebrado" antes que yo "mezclarTrago" o "comprarAlcohol", esto es humanamente imposible.
task :comprarAlcohol do puts "Vodka comprado" end task :mezclarTrago => :comprarAlcohol do puts "Caipiroska mezclada" end task :estarDescerebrado => :mezclarTrago do puts "Uhh loco..., veo elefantes rosadoooossss" end
Ahora estoy diciendo que "para mezclarTrago, primero debo comprarAlcohol", y "para estarDescerebrado debo mezclarTrago". Como puedes esperar, las dependecias se apilan. Veamos:
$ rake comprarAlcohol Vodka comprado $ rake mezclarTrago Vodka comprado Caipiroska mezclada $ rake estarDescerebrado Vodka comprado Caipiroska mezclada Uhh loco..., veo elefantes rosadossss
Como puedes ver, cuando ahora voy a "estarDescerebrado", son llamadas las tareas dependientes "comprarAlcohol" y "mezclarTragos". Después de un tiempo puedes ser tentado a expandir tus adicciones, y por lo tanto expandir tu Rakefile. También puedes tentarte a convertir en adictos a tus amigos. Como en un proyecto de software real, cuando agregas gente a tu equipo, necesitarás asegurarte que tienen una buena documentación. La pregunta consiguiente es:
Aquí está como lo usarías.
desc "Esta tarea comprará tu Vodka" task :comprarAlcohol do puts "Vodka comprado" end desc "Esta tarea mezclará un buen cocktail" task :mezclarTrago => :comprarAlcohol do puts "Caipiroska mezclada" end desc "Esta tarea beberá demasiado" task :estarDescerebrado => :mezclarTrago do puts "Uhh loco..., veo elefantes rosadossss" end
$rake --tasks rake estarDescerebrado # Esta tarea beberá demasiado rake mezclarTrago # Esta tarea mezclará un buen cocktail rake comprarAlcohol # Esta tarea comprará tu Vodka
Bastante fácil, no?
Una vez que te hayas vuelto un alcoholico y que estés usando un montón de tareas Rake, puedes necesitar una mejor manera de categorizarlas. Aquí es donde los espacios de nombres entran en acción. Si yo usase espacios de nombres en el ejemplo de arriba, puede lucir algo así.
namespace :alcoholic do desc "Esta tarea comprará tu Vodka" task :comprarAlcohol do puts "Vodka comprado" end desc "Esta tarea mezclará un buen cocktail" task :mezclarTrago => :comprarAlcohol do puts "Caipiroska mezclada" end desc "Esta tarea beberá demasiado" task :estarDescerebrado => :mezclarTrago do puts "Uhh loco..., veo elefantes rosadossss" end end
Los espacios de nombres te permiten agrupar tareas de acuerdo a una categoría, y SI, puedes tener más de un espacio de nombres dentro de un Rakefile. Ahora si yo hago un "rake --tasks" esto es lo que vería.
rake alcoholic:estarDescerebrado # Esta tarea beberá demasiado rake alcoholic:mezclarTrago # Esta tarea mezclará un buen cocktail rake alcoholic:comprarAlcohol # Esta tarea comprará tu Vodka
Por lo tanto ahora para correr estas tareas obviamente correrías "rake alcoholico:estarDescerebrado".
¿Cómo puedo escribir Tareas Ruby útiles?
Bien, solo escribe Ruby! No es broma. Recientemente necesité escribir un script que crease un par de directorios, por lo que terminé escribiendo una tarea Rake que luce algo como esto:
desc "Crea directorios vacíos si no existen" task(:create_directories) do # Los directorios que necesito crear shared_folders = ["icons","images","groups"] for folder in shared_folders # Chequea para ver si existen if File.exists?(folder) puts "#{folder} existe" else puts "#{folder} no existe por lo que se esta creando" Dir.mkdir "#{folder}" end end end
Las aplicaciones Rails vienen con un montón de tareas rake preexistentes, la cuales puedes listar yendo al directorio de tu aplicación y escribir "rake --tasks". Si aún no lo has intentado, hazlo ahora, estaré esperando....
Para crear nuevas tareas rake para tu app Rails, necesitas abrir el directorio /lib/tasks (el cual deberías tener). Si creas tu propio Rakefile en este directorio, y lo denominas "algo.rake", la tarea será levantada automáticamente. Estas tareas serán agregadas a la lista de tareas Rake de la aplicación, y podrás correrlas desde el directorio raíz de la misma. Tomemos el ejemplo de arriba y brindémoselo a nuestra aplicación rails.
utils.rake
namespace :utils do desc "Crea directorios vacíos si no existen" task(:create_directories) do # Los directorios que necesito crear shared_folders = ["icons","images","groups"] for folder in shared_folders # Chequea para ver si existen if File.exists?("#{RAILS_ROOT}/public/#{folder}") puts "#{RAILS_ROOT}/public/#{folder} existe" else puts "#{RAILS_ROOT}/public/#{folder} no existe por lo que se esta creando" Dir.mkdir "#{RAILS_ROOT}/public/#{folder}" end end end end
Note en este snippet como usé #{RAILS_ROOT} para obtener el path completo. SI ahora corro "rake --tasks" en el directorio base de mi aplicación, podré ver a esta nueva función entremezclada con todas las otras tareas rake de Tails:
... rake tmp:pids:clear # Clears all files in tmp/pids rake tmp:sessions:clear # Clears all files in tmp/sessions rake tmp:sockets:clear # Clears all files in tmp/sockets rake utils:create_directories # Crea directorios vacíos si no existen ...
Muy bien! Ahora aquí es donde se vuelve realmente útil..
SI, SI, SI!!! De hecho esto es para lo que uso rake mayormente: Escribiendo tareas que necesito correr manualmente en una ocasión, u otras que agendaré para correr automáticamente(usando cronjobs). Como dije al inicio del artículo, uso las taras Rake para las siguientes cosas:
Realmente util, pero además de eso es fácil. Aquí hay una tarea rake que encuentra a los usuarios cuyas suscripciones están por expirar, y les envía un email:
utils.rake
namespace :utils do desc "Encuentra y envia emails a suscripciones prontas a expirar" task(:send_expire_soon_emails => :environment) do # Find users to email for user in User.members_soon_to_expire puts "Emailing #{user.name}" UserNotifier.deliver_expire_soon_notification(user) end end end
Como puedes ver, hay un solo paso para obtener acceso a tus modelos, la cosa "=>:environment"
task(:send_expire_soon_emails => :environment) do
Para correr esta tarea en mi db de desarrollo yo correría "rake utils:send_expire_soon_emails". Si yo quiero correr esto en mi base de datos de producción correría "rake RAILS_ENV=production utils:send_expire_soon_emails".
Si yo entonces quiero correrlo a última hora a medianoche en mi base de datos de producción, podría escribir un cronjob que luciría algo así.
0 0 * * * cd /var/www/apps/rails_app/ && /usr/local/bin/rake RAILS_ENV=production utils:send_expire_soon_emails
Muy conveniente!
Ahora que conoces bastante como para empezar a escribir útiles tareas rake, supongo que te dejarñe con algunos recursos mas. La mejor manera de mejorar tu programación es leer el código de otras personas, por lo que algunos de esos existen porque la gente ha creado tareas rake útiles.
Ya tienes todo junto. Si encuentras otros, siéntete libre de postearlos en los comentarios.
¿Aún leyendo? Si estas ahí, quiero hacerte saber que estamos buscando más gente para escribir para RailsEnvy. Si tienes alguna idea para algún buen tutorial de Rails queremos escucharte!. Básicamente trabajariamos contigo para corregir el tutorial y ayudar en el pulido (actuando como editor). Definitivamente podría ser una buena forma de tener tu nombre ahí fuera, y de comenzar a obtener algunos hits (para tu blog o compañía). Envía un email a Gregg at RailsEnvy si estás interesado.
Continuación
Como continuación, he recibido un email de Jim hace unos pocos minutos, explicando como puedo simplificar mi script de creación de directorios:
# This is needed because the existing version of directory in Rake is slightly broken, but Jim says it'll be fixed in the next version. alias :original_directory :directory def directory(dir) original_directory dir Rake::Task[dir] end # Do the directory creation namespace :utils do task :create_directories => [ directory('public/icons'), directory('public/images'), directory('public/groups'), ] end
Comentarios recientes
18 weeks 20 hours ago
19 weeks 5 days ago
1 año 11 hours ago
1 año 2 weeks ago
1 año 14 weeks ago
1 año 19 weeks ago
1 año 25 weeks ago
1 año 25 weeks ago
1 año 25 weeks ago
1 año 26 weeks ago