Una de las novedades de Rails edge es la inclusión de filtros y silenciadores en las trazas de nuestra aplicación. Los filtros permiten cambiar al vuelo contenidos de las líneas de traza en base a expresiones regulares y los silenciadores, más radicales, se limitan a tragarse las líneas de log que lo activen.
En Rails < = 2.2, si hacemos lo siguiente
$ rails test-exception $ cd test-exception $ script/generate scaffold blog $ script/serverY lanzamos un navegador hacia http://localhost:3000/blogs obtendremos una bonita traza de error como esta.
Sin embargo, en las versiones posteriores a Rails 2.2 la traza de error es mucho más escueta y por tanto más fácil de leer porque omite información que por lo general es poco necesaria. He aquí la nueva traza.
Como era de imaginar la cosa no acaba aquí sino que podremos configurar los filtros y silenciadores a nuestro gusto si editamos el fichero config/initializers/backtrace_silencers. Por ejemplo, con el siguiente cambio y con nuestra configuración de Rails
Rails.backtrace_cleaner.add_filter {|line| line.sub(/(\/opt\/local\/lib\/ruby\/gems\/1.8\/gems\/)([a-z]+)-([0-9.]+)\/(.*)/) {|match| " #{$2} (#{$3}) :#{$4}"} }conseguiremos que las trazas generadas por el código de las gemas aparezca mostrando el nombre de la gema y su versión. Puede verse aquí la traza que se generaría.
La semana pasada los desarrolladores norteamericanos celebraron el Día de Acción de Gracias, pero esto no se ha notado en el desarrollo de Rails. Las cosas se encaminan hacia la versión 2.3 de Rails.
Integración con RackLa base de script/server se ha reescrito y simplificado. Por ejemplo, la lista explícita de servidores soportados ha desaparecido y en su lugar Rails ahora depende de la instalación de Rack, y script/server hace esto – lo que quiere decir que ahora Rails soportará cualquier servidor siempre que Rack lo haga también.
Rutas más eficientesLas rutas han visto un par de cambios importantes esta semana. Los helpers de rutas que empiezan por formatted_ han desaparecido, y en su lugar ahora hay que pasar la opción :format al helper normal. De esta maanera se ahorra hasta el 50% de las rutas generadas para un recurso y esto puede ahorar una cantidad sustancial de memoria (hasta el 100MB en aplicaciones grandes según este ticket en Lighthouse) Si tu código utiliza los helpers formatted_ por el momento seguirá funcionando, pero debes tener en cuenta que estos helpers son «a extinguir» y que tu aplicación será más eficiente si adaptas tu código para usar el nuevo estándar. Otro cambio importante es que Rails ahora soporte múltiples archivos de rutas, en lugar de usar únicamente routes.rb Se puede usar RouteSet#add_configuration_file para incorporar más rutas en cualquier momentot – y sin borrar las rutas ya cargadas. commit commit
Mejor soporte de plugins de EnginesEl segundo cambio en las rutas permite soportar mejor los Engines: ahora se cargan y recargan automáticamente los archivos de rutas de los Engines (como en el resto de plugins) Pero los Engines están recibiendo más atención, además del enrutado: si tu plugin tiene un directorio app, entonces los directorios app/[models|controllers|helpers] se añadirán automáticamente al load path de Rails. Hay una discusión en curso acerca de cómo debería funcionar este mecanismo y cuánto reutilizar del plugin de Engines actual así que es posible que aún no hayamos visto los últimos commits en este aspecto. Los Engines ahora soportan la adición de paths para las vistas.commit commit commit commit
Volcados de los tests más sensatosSi usamos el plugin Quiet Backtrace de Thoughtbot, que nos permite eliminar selectivamente las líneas de los volcados generados por Test::Unit, nos agradará saber que han aparecido ActiveSupport::BacktraceCleaner y Rails::BacktraceCleaner en el core de Rails. Se soportan tanto filtros (para hacer sustituciones en las líneas de volcado usando expresiones regulares) como silenciadores (para eliminar líneas enteras). En las aplicaciones nuevas Rails añadirá automáticamente silenciadores para eliminar la mayor parte del ruido en los volcados de tests y construirá un archivo config/backtrace_silencers.rb donde poder configurar los nuestros. commit
Integración con Ruby 1.9Varios commits continúan avanzando en la compatibilidad con Ruby 1.9 y minitest para asegurar que Rails 2.3 está listo para funcionar con las nuevas mejoras en Ruby cuando sean lanzadas. Los que sigan el edge, sin embargo, deben tener en cuenta que uno de los últimos cambios en edge Rails depende de un parche al core de Ruby que aún no ha sido aplicado. También será necesario aplicar (temporalmente) el fork de Mocha de Jeremy Kemper para ser compatibles con MiniTest tal y como requiere este parche .
Tiempo de arranque más rápido en desarrolloJeremy Kemper y Josh Peek han estado trabajando para garantizar que haya partes de Rails y sus dependencias se carguen en memoria sólo cuando sea realmente necesario. Comprobad los commits del 23 de Noviembre para ver muchos cambios relacionados con esta recarga perezosa. Los frameworks Active Support, Active Record, Action Controller, Action Mailer y Action View ahora utilizan autoload para cargar sus clases individuales, y así mantener la carga de memoria lo más baja posible mejorando el rendimiento de Rails. commit commit commit commit commit
OtrosCon la nueva opción preload_frameworks se puede especificar si queremos que las librerías del core se autocarguen en el arranque. Por defecto es false de forma que Rails se autocarga pieza por pieza, pero en algunas circunstancias podríamos querer traerlo todo a la vez – por ejemplo Passenger y JRuby necesitan que Rails se cargue en su totalidad. commit
Los asset hosts son más flexibles, con las poisiblidad de declarar un host como un objeto específico que responde a cierta llamada. DHH ha proporcionado un proyecto de ejemplo, asset-hosting-with-minimum-ssl, que demuestra un buen uso de esta funcionalidad. commit
La ubicación de la carpeta de helpers es ahora configurable en una aplicación Rails ajustando ActionController::Base.helpers_dir, lo cual será una bendición para algunas circunstancias poco habituales – el caso de uso original es la construcción de una aplicación Rails que facilita la extensión mediante plugins en lugar de mediante la alteración de la propia aplicación. commit
La generación de tokens de protección contra CSRF se ha simplificado, y ahora Rails utiliza una cadena aleatoria generada por ActiveSupport::SecureRandom en lugar de trastear con los IDs de sesión. Como resultado de esto las opciones :digest y :secret de protect_from_forgery están obsoletas y no surtirán ningún efecto a partir de ahora. commit
Y ya que hablamos de secretos, seguro que algunos encontrarán algún uso original y útil de ActiveSupport::MessageEncryptor, que proporciona una forma sencillo de cifrar información para su almacenamiento en lugares no seguros (como las cookies). commit
La función de Active Support from_xml ya no depende de XmlSimple. En su lugar, Rails ahora incluye un su propia implementación de XmlMini con sólo la funcionalidad necesaria, de forma que Rails puede dejar de incluir la copia de XmlSimple que venía arrastrando. commit commit
Como se recordará, las mejoras de la semana pasada incluyeron el cambio de nombre de application.rb a application_controller.rb. Esta semana hay una nueva tarea de Rake, rake rails:update:application_controller que hace esto automáticamente y que forma parte de la tarea rake rails:update. commit
Hay buenas noticias si usamos ActiveSupport::OrderedHash: por fin implementa each_key y each_value. commit
Se ha internacionalidado un poquito más del core de Rails: las unidades usadas por number_to_human_size. Si mantienes un archivo de traducciones, ahora tienes que añadir las traducciones de las unidades de almacenamiento: bytes, KB, MB, GB, TB. commit
Por fin ha desaparecido el soporte de los componentes en Rails, que fueron en su momento según «Agile Web Development wih Rails» un brillante ejemplo de lo que ocurre cuando la ambición se impone a la prudencia. Por si un par de años de advertencia sobre lo que iba a ocurrir no fue suficiente, ahora es el momento de encontrar una solución alternativa. commit
Han dejado de generarse por defecto un par de archivos en /public que tenían que ver con los dispatchers de CGI y FCGI. De todas formas, si te hace falta, puedes obtenerlos añadiendo la opción --with-dispatches al ejecutar el comando rails, o bien generarlos después usando rake rails:generate_dispatchers). commit commit
No ha habido mucha actividad, en términos de grandes cambios o nuevas funcionalidades en el core de Rails. Así que lo mejor es dirigirse a los logs
de los commits en busca de cambios menores o correcciones. Ha habido algunos avances en la refactorización y sorporte de threads en ActionPack, así como alguna actividad en ActiveModel, pero nada realmente concreto.
script/server ahora comprueba si Thin está instalado y lo utiliza. Esto es bastante cómodo si usamos Thin como nuestro servidor en producción y queremos usarlo también en desarrollo. Hay que añadir config.gem ‘thin’ al fichero environment.rb.
Este parche ha sido contribuido por uno de los chicos de fluxin, y estos son los cambios
Personalización de String#humanize mediante reglas de inflexiónLa extensión String#humanize del core se utiliza para convertir cadenas con guiones bajos, por ejemplo nombres de tablas, en texto legible. Por ejemplo:
"Actor salary" "anime_id".humanize => "Anime"Pero a veces esto no funciona tan bien, cuando tenemos tablas heredadas de otra aplicación o simplemente con nombres inhumanos como “act_sal_money” (que sería humanizada como “Act Sal Money”)
Ahora podemos especificar nuestras propias reglas de inflexión (igual que puede hacerse para personalizar las reglas de inflexión para plurales, singulares, etc.)
Inflector.inflections do |inflect| inflect.human /_cnt$/, '\1_count' inflect.human 'act_sal_money', 'Actor Salary' endObsérvese que podemos usar expresiones regulares para convertir por ejemplo columnas como “click_cnt” en “Click Count”
Demos las gracias a Dan Manges y Pascal Ehlert por este cambio.
Condiciones sobre múltiples tablas utilizando hashesPratik ha entregado un pequeño (pero útil) cambio en ActiveRecord que permite especificar las condiciones de una tabla sobre la que se efectúa un JOIN en su propio hash. Por ejemplo:
Anime.all( :joins => :character, :conditions => { :active => true, :characters => { :gender => 'female' } } )La sentencia anterior encontrará todos los animes “activos” con personajes femeninos. Cambios
Lo más importante esta semana en el desarrollo de Rails es el inminente lanzamiento de Rails 2.2.1 -también conocido como Rails 2.2 RC2. Debido a eso ha habido algunos cambios significativos en el repositorio de Rails.
En primer lugar, es probable que sea necesario actualizar rubygems para ejecutar RC2: ahora requiere la versión 1.3.1 de rubygems, que fue publicada exactamente ayer. Esto hará más util y estable la posibilidad de poner gemas en el directorio vendor, aunque a costa de una actualización que puede resultar algo costosa. En este artículo se pueden encontrar algunas soluciones. commit
También se ha trabajado bastante en el motor de rutas durante la semana pasada. Jeremy Kemper entregó bastantes correcciones que minimizan la creación de objetos y expresiones regulares, recortando así el uso de memoria. commit commit Ahora tenemos las las opciones :only y :except para invocar a map.resources, que también harán descender el uso de memoria si usamos muchas rutas de recursos -véanse estos artículos para entrar en detalle (aunque también ha habido algunos retoques a la manera en que funcionan los recursos anidados después de que se publicase). commit commit commit
El nuevo código de pooling de conexiones de ActiveRecord ha sido afinado un poco también, evitando algunos problemillas con el adaptador de Oracle y siendo un poco más eficiente en modo desarrollo. commit
Las URLs polimórficas son ahora más intuitivas si uno de sus parámetros es nil. Por ejemplo, llamar a polymorphic_path([@project, @filter, @issue]) con un filtro nulo ahora devuelve project_issue_url en lugar de NoMethodError. commit
Se ha limitado la funcionalidad de protección contra request forgery de forma que sólo se aplica a peticiones que solicitan contenido formateado en HTML. Ha habido una gran discusión en el ticket de Lighthouse que llevó a este cambio, pero se resume en que la implementación anterior tenía fallos que hacían que las acciones destroy fueran inaccesibles cuando se usaba XML. Además, los otros tipos de peticionese pueden proteger por otros medios. commit
Antes que nada esta semana, una advertencia para aquellos que no sigan de cerca el estado del repositorio de Rails – “edge” ahora significa el filo de la navaja. Ya se está preparando el lanzamiento de la release 2.2 y el repositorio se ha bifurcado; por el momento la versión edge de Rails se identifica como 2.3 , aunque el número de versión podría cambiar más adelante. Si queremos instalar la versión 2.2 en nuestra máquina, deberíamos asegurarnos de que estamos usando la rama 2-2-stable, y no edge, que está sufriendo cambios profundos.
El código en 2-2-stable aún sigue viendo cambios, pero son pequeños ajustes. Esta semana, se trata de corregir un bug en la asignación en asociaciones has_one :through , más ajustes a la protección CSRF , y una correción a la manipulación de checkboxes para valores booleanos, así como la actualización de las copias incluidas de TZInfo, Prototype, script.aculo.us. También se ha avanzado en la compatibilidad con Ruby 1.9 (aunque el objetivo de soportar Ruby 1.9 en sus totatlidad está planteado para Rails 2.3).
Las mayores modificaciones en la rama 2.2 es la adición de soporte explícito de internacionalización en los proyectos Rails recién generados, incluyendo un archivo de locale de ejemplo, la autocarga de todos los locales que existan en config/locales y configuración de ejemplo en config/environment.rb. commit
También es destacable que en 2.2 se ha eliminado un buen trozo de código, desapareciendo de los casos de tests de ActiveRecorod todo un embrollo de casos especiales para el adaptador de SQL Server. Ken Collins ha hecho un tremendo trabajo consiguiendo que el adaptador de SQL Server funcione de la manera que Rails espera que lo hagan los adaptadores, lo cual es un gran pasado en el área de la portabilidad de los adaptadores. commit
En cuanto a la rama edge, ahí tenemos todavía más acción dado que en esta rama recién abierta acaba de entrar un montón de código que estaba esperando su momento. Corren tiempos interesantes y merece mucho la pena descargarse edge. Pasemos a ver algunos de los cambios más notables de la última semana.
Muchos cambios son obras de Jeremy Kemper, que ha estado mejorando los tests internos de Rails para cambiar de Test::Unit::TestCase a ActiveSupport::TestCase. Esto incluye también el requisito de usar Mocha para testar Rails (en el código 2.2, si no tenemos Mocha instalados, algunos tests serán ignorados), así como hacer que la estrategia de testing en Rails (tanto en el core como en las aplicaciones generadas) sea más consistente de ahora en adelante.
Aquellos a los que molestaba el caso especial del nombre de archivo application.rb se alegrarán de saber que se ha cambiado a application_controller.rb. Hay más información aquí y aquí . commit
Con Rails 2.3 se introducirá la noción del default scopes : similares a los named scopes, pero que se aplican a todos los scopes o métodos find dentro de la clase. Por ejemplo, podemos escribir default_scope :order => 'name ASC' y en culauqie rmomento en que se accedan a registro de esa clase, serán recuperados ordenados por nombre (a no ser, claro está, que se indique otra opción) commit
Muchos programadores han adoptado la noción de usar try() a la hora de lanzar operaciones en objetos – he aquí el post en el blog de Chris Wanstrath que lo presenta. Esto es especialmente útil en vistas donde uno puede evitar los chequeos de nil escribiendo código como< %= @person.try(:name) %>. Pues bien, ahora está incluido dentro de Rails. commit
También en el terreno del azúcar sintáctico aparece Enumerable#none? para comprobar que ningno de los elementos cumplen con el bloque proporcionado. commit
El método render se ha ido haciendo más listo conforme pasan los años, y lo será aún más en la 2.3. Si tenemos un objeto o una colección y los nombres conciden, ahora podemos hacer tan sólo < % render @article %> o < % render @articles %> y todo funcionará. Ryan Daigle ha escrito algunos ejemples de esto. commit
En el mismo orden de cosas, en 2.3 render_component ha pasado de “a extinguir” a “inexistente”. Si es necesario, aún podemos instalar el plugin . commit
El helper autolink se ha refactorizado para hacerlo un poco menos complicado y más intuitivo. commit commit
Ha habido un arreglo para una fuga de memoria relacionada con el soporte de multithreading y los tags de assets, que podría perjudicar a sites que estuvieran referenciando muchas imágenes externas. Aaron Batalion ha contribuido la solución a este problema, así como un blog explicando el problema. commit and commit
Por último, merece la pena mencionar que ha surgido algo de controversia alrededor de un cambio hecho al código 2.2 hace cinco meses – la adición de Array#second a Array#tenth como aliases de Array#[1] a Array#[9]. Sin tomar partido en la controversia, notaremos que el cambio más reciente en edge ha reducido el soporte sólo de Array#second a Array#fifth – y con el ahorro ahora se ha podido implementar Array#forty-two. commit
Lo más importante esta semana en el desarrollo de Rails es el inminente lanzamiento de Rails 2.2.1 -también conocido como Rails 2.2 RC2. Debido a eso ha habido algunos cambios significativos en el repositorio de Rails.
En primer lugar, es probable que sea necesario actualizar rubygems para ejecutar RC2: ahora requiere la versión 1.3.1 de rubygems, que fue publicada exactamente ayer. Esto hará más util y estable la posibilidad de poner gemas en el directorio vendor, aunque a costa de una actualización que puede resultar algo costosa. En este artículo se pueden encontrar algunas soluciones. commit
También se ha trabajado bastante en el motor de rutas durante la semana pasada. Jeremy Kemper entregó bastantes correcciones que minimizan la creación de objetos y expresiones regulares, recortando así el uso de memoria. commit commit Ahora tenemos las las opciones :only y :except para invocar a map.resources, que también harán descender el uso de memoria si usamos muchas rutas de recursos -véanse estos artículos para entrar en detalle (aunque también ha habido algunos retoques a la manera en que funcionan los recursos anidados después de que se publicase). commit commit commit
El nuevo código de pooling de conexiones de ActiveRecord ha sido afinado un poco también, evitando algunos problemillas con el adaptador de Oracle y siendo un poco más eficiente en modo desarrollo. commit
Las URLs polimórficas son ahora más intuitivas si uno de sus parámetros es nil. Por ejemplo, llamar a polymorphic_path([@project, @filter, @issue]) con un filtro nulo ahora devuelve project_issue_url en lugar de NoMethodError. commit
Se ha limitado la funcionalidad de protección contra request forgery de forma que sólo se aplica a peticiones que solicitan contenido formateado en HTML. Ha habido una gran discusión en el ticket de Lighthouse que llevó a este cambio, pero se resume en que la implementación anterior tenía fallos que hacían que las acciones destroy fueran inaccesibles cuando se usaba XML. Además, los otros tipos de peticionese pueden proteger por otros medios. commit
(Esta entrada a es traducción de This week in edge Rails, por Mark Gunderloy)
Para aquellos que no tengais tiempo de seguir todos los commits al código de Rails, hemos resucitado esta sección del blog. Esta vez vamos a cubrir tres semanas de commits, el tiempo desde el lanzamiento de Rails 2.2 RC1 (también conocido como Rails 2.2) Aunque no se han añadido nuevas funcionalidades importantes a Rails, esto no quiere decir que el repositorio haya estado muerto: ha habido unos 75 commits en este lapso de tres semanas.
En el arreón final hasta Rails 2.2 hemos podido ver una gran cantidad de pequeñas correcciones conforme los desarrolladores van comprobando la calidad de la release
Algunas de las funcionalidades más importantes de la versión 2.2 han recibido también ajustes. Se ha trabajado en atar algunos cabos sueltos en el apartado del threading y cambios para que el backend de i18n recargue sus traducciones en modo de desarrollo. La librería Prototype se actualizó a la última versión (1.6.0.3). El código para configuar, cargar y poner en vendor las gemas también ha tenido algo de atención, así como el código que mantiene pools de conexiones a la base de datos.
Y aunque la rama esté congelada, esto no quiere decir que no se sigan colando nuevas funcionalidades:
(Esta entrada a es traducción de This week in edge Rails, por Mark Gunderloy)
Para aquellos que no tengais tiempo de seguir todos los commits al código de Rails, hemos resucitado esta sección del blog. Esta vez vamos a cubrir tres semanas de commits, el tiempo desde el lanzamiento de Rails 2.2 RC1 (también conocido como Rails 2.2) Aunque no se han añadido nuevas funcionalidades importantes a Rails, esto no quiere decir que el repositorio haya estado muerto: ha habido unos 75 commits en este lapso de tres semanas.
En el arreón final hasta Rails 2.2 hemos podido ver una gran cantidad de pequeñas correcciones conforme los desarrolladores van comprobando la calidad de la release
Algunas de las funcionalidades más importantes de la versión 2.2 han recibido también ajustes. Se ha trabajado en atar algunos cabos sueltos en el apartado del threading y cambios para que el backend de i18n recargue sus traducciones en modo de desarrollo. La librería Prototype se actualizó a la última versión (1.6.0.3). El código para configuar, cargar y poner en vendor las gemas también ha tenido algo de atención, así como el código que mantiene pools de conexiones a la base de datos.
Y aunque la rama esté congelada, esto no quiere decir que no se sigan colando nuevas funcionalidades:
No ha habido mucha actividad, en términos de grandes cambios o nuevas funcionalidades en el core de Rails. Así que lo mejor es dirigirse a los logs de los commits en busca de cambios menores o correcciones. Ha habido algunos avances en la refactorización y sorporte de threads en ActionPack, así como alguna actividad en ActiveModel, pero nada realmente concreto.
Soporte de thin en script/serverscript/server ahora comprueba si Thin está instalado y lo utiliza. Esto es bastante cómodo si usamos Thin como nuestro servidor en producción y queremos usarlo también en desarrollo. Hay que añadir config.gem ‘thin’ al fichero environment.rb.
Este parche ha sido contribuido por uno de los chicos de fluxin, y estos son los cambios
Personalización de String#humanize mediante reglas de inflexiónLa extensión String#humanize del core se utiliza para convertir cadenas con guiones bajos, por ejemplo nombres de tablas, en texto legible. Por ejemplo:
"actor_salary".humanize => "Actor salary" "anime_id".humanize => "Anime"Pero a veces esto no funciona tan bien, cuando tenemos tablas heredadas de otra aplicación o simplemente con nombres inhumanos como “act_sal_money” (que sería humanizada como “Act Sal Money”)
Ahora podemos especificar nuestras propias reglas de inflexión (igual que puede hacerse para personalizar las reglas de inflexión para plurales, singulares, etc.)
Inflector.inflections do |inflect| inflect.human /_cnt$/, '\1_count' inflect.human 'act_sal_money', 'Actor Salary' endObsérvese que podemos usar expresiones regulares para convertir por ejemplo columnas como “click_cnt” en “Click Count”
Demos las gracias a Dan Manges y Pascal Ehlert por este cambio.
Condiciones sobre múltiples tablas utilizando hashesPratik ha entregado un pequeño (pero útil) cambio en ActiveRecord que permite especificar las condiciones de una tabla sobre la que se efectúa un JOIN en su propio hash. Por ejemplo:
Anime.all( :joins => :character, :conditions => { :active => true, :characters => { :gender => 'female' } } )La sentencia anterior encontrará todos los animes “activos” con personajes femeninos. Cambios
Aclaración: la columna semanal que publicaba Chu Yeow e su blog ha pasado al blog oficial de Ruby on Rails. Con este cambio la numeración de las entregas vuelve a empezar por el número 1.
Ha habido un buen puñado de cambios y mejoras de rendimiento en las últimas semanas desde la aparición de Rails 2.1, así que en lugar de volcarlas todas en un post enorme, serán repartidas en dos. En esta entrega hablaremos de las nuevas featuras y cambios en la API.
Cambios menores en la API link_to ahora recibe un bloqueEl helper link_to ahora recibe un bloque como argumento para aquellas ocasiones en las que tenemos textos muy largos con variable para nuestros enlaces
<% link_to(@profile) do %> <strong><%= @profile.name %></strong> - <span>Status: <%= @profile.status %></span> <% end %>Es bastante más claro que
<%= link_to "<strong>#{@profile.name}</strong> " + "<span>Status: #{@profile.status}</span>", @profile %>Los autores de este cambio son David Heinemeier y Sam Stephenson (de Prototype).
ActiveRecord::Base#merge_conditions es ahora parte de la API públicaJeremy Kemper ha hecho público el método ActiveRecord::Base#merge_conditions.
Esto es bastante útil si en nuestras consultas a la base de datos tenemos condiciones de múltiples fuentes o querríamos, por alguna razón, combinar cualquier condición:
Post.merge_conditions( {:title => 'Lucky ☆ Star'}, ['rating IN (?)', 1..5] ) => "(`posts`.`title` = 'Lucky ☆ Star') AND (rating IN (1,2,3,4,5))"Debemos tener en cuenta que las uniones se hacen siempre con un AND lógico de SQL, nunca con un OR.
Detalles de este parche
Las asociaciones reciben una opción :validateLas macros de las asociaciones ahora aceptan la opción :validate así:
class Anime > ActiveRecord::Base has_many :characters, :validate => true endCon esto le indicamos a ActiveRecord que valide las asociacion characters cuando almacene el modelo Anime, exactamente de la manera en que funciona :validates_associated. Por defecto el valor es falso, que es el funcionamiento por defecto de Rails 2.1 y anteriores, así que no es necesario perder la tranquilidad. Este mecanismo funciona también con el resto de macros (has_one, belongs_to, has_and_belongs_to_many)
Demos las gracias a Jan de Poorter y Pratik Naik por este cambio, que también resuelve un bug recalcitrante. Parche.
ActiveSupport::StringInquirer y los métodos de conveniencia Rails.env.development?DHH ha añadido recientemente una subclase ActiveSupport::StringInquirer que permite hacer lo siguiente:
s = ActiveSupport::StringInquirer.new('awesome') => "awesome" s.awesome? => true s.sucks? => falseUn uso inmediato de esto es cuando estamos comprobando el entorno en que se está ejecutando nuestra aplicación: Rails.env está recubierto por un StringInquirer, de forma que se pueden usar métodos de consuta como Rails.env.development? y Rails.env.production?
Detalles
Extensiones al core: Object#present? y Enumerable#many?DHH también ha añadido algunas extensiones que, si bien son triviales, pueden contribuir a hacer nuestro código más legible. El primero es Object#present?, que equivale a !Object#blank?
[].present? => false [1, 2].present? => true "".present? => false "heme aquí".present? => trueTambién se ha añadido una extensión Enumerable#many? que es simplemente un valor lógico que comprueba si enumerable.size > 1:
[].many? => false [:solo_yo].many? => false [:solo_yo, 'mi_amigo'].many? => trueParche para Object#present? y parche para Enumerable#many?
Sintaxis declarativa de bloques para escribir testsDHH debía de estar inspirándose en Jay Fields cuando escribió este terrón de azúcar sintáctico: ahora podemos escribir nuestros tests (Test::Unit) con forma de bloques declarativos:
test "Un anime debería ser no válido si cualquiera de sus personajes no es valido" do # Codigo normal de pruebas endTodos los stubs de tests generados por Rails utilizan ya esta nueva sintaxis.
Detalles del parche
Pruebas de rendimientoJeremy Kemper ha estado haciendo un gran trabajo en la optimización y mejora del rendimiento de Rails, así que no es de extrañar que haya introducido un nuevo tiempo de tests de integración: el test de rendimiento.
Ahora podemos usar el generador de tests de rendimiento (añadido por Pratik en el cambio 23232a) para generar un test de rendimiento que rellenar.
script/generate performance_test LoginStoriesPara correr los tests de rendimiento es necesario tener instalado ruby-prof (>= 0.6.1), que aún no ha sido publicado pero se puede obtener la versión de desarrollo haciendo un checkout del código ei nstalando la gem uno mismo. (Es mejor utilizar el fork de Jeremy por el momento) Es interesante hacer notar que con la versión 0.6.1 ruby-prof soporta casos de pruebas escrtos usando Test::Unit.
A continuación basta con poner código de pruebas (pedir unas cuantas acciones de algún controlador – cualquier historia de usuario de la que queramos probar el rendimiento) y ejecutar el test. Obtendremos un resutlado como este (además de toda la salida habitual de ruby-prof en test/tmp/performance ):
> ruby performance/login_stories_test.rb Loaded suite performance/login_stories_test Started LoginStoriesTest#test_homepage (32 ms warmup) process_time: 11 ms memory: unsupported objects: unsupported . Finished in 0.870842 seconds.Los resultados de memory y objects están no soportados porque aún no he parcheado mi intérprete de Ruby con el soporte para profiling de la memoria y el recolector de basura. Hay información de cómo hacerlo aquí. Dejaremos que lo explique gente más sabia.
Detalles del parche
(Este artículo es una traducción de Living on the edge of Rails (22), publicado por Chu Yeow en su blog redemption in a blog)
No ha habido cambios alucinantes en esta semana previa a la RailsConf -tal y como mencionó Gregg en el podcast de RailsEnvy estamos en la antesala de Rails 2.1 (probablemente será lanzado durante la misma RailsConf), así que es comprensible esta tranquilidad. Ah, y ahora todos los tests pasan con Ruby 1.9 después de que un recalcitrante error en module_eval haya sido corregido en el trunk de Ruby 1.9. (Véase este hilo para más información)
El informe de esta semana cubre los cambios efectuados en Rails entre el 19 y el 25 de Mayo de 2008.
Métodos first y last en asociaciones y ámbitos con nombre¿Recordais cómo la integración de la gema has_finder nos permitía hacer cosas como Post.first y Post.last?
Ahora podemos ir un paso más allá y utilizar los mismo métodos en las asociaciones ActiveRecord. Por ejemplo:
post = Post.find(1) first_comment = post.comments.firstY si hemos definido un named scope llamado recent, podemos incluso hacer:
post.comments.recent.lastEsta pequeña mejora es cortesía de Ryan Bates (sí, el Ryan Bates de RailsCasts)
Métodos exist? y fragment_exist? para la cachéLos almacenes de caché en Rails (memcache, fichero, etc) ahora disponen de un método exist? que comprueba si la caché dispone de un valor guardado para una clave dada. Esto permite que los controladores Rails dispongan de un método fragment_exist? que nos permite preguntar si existe un fragmento de caché.
fragment_exist?('example.com/foo/bar')El cambio correspondiente es http://github.com/rails/rails/commit/99860b72aebe0348f41e82d4710343498d89a84b#diff-2
Creación de registros en asociaciones con bloques como argumentoAhora se pueden crear registros para asociaciones así
post.comments.create!(:title => 'Techcrunch') do |c| c.body = "Rails no escala!" endTodo esto va parejo con el reciente cambio en ActiveRecord::Base.create del que ya hemos hablado por aquí
El autor de este parche es (otra vez) Ryan Bates
El cambio es este
¡Hasta la semana que viene!
(Este artículo es una traducción de Living on the edge of Rails (21), publicado por Chu Yeow en su blog redemption in a blog)
Esta ha sido otra semana tranquila (sólo ha habido dos cambios de importancia, en mi opnión) tras la liberación de la primera Release Candidate de Rails 2.1 Sigue el enlace donde están las instrucciones de instlaación -aunque si estás leyendo esta serie de posts probablemente no te importe (porque estas, ya sabes, viviendo en el filo) Bromas aparte, no te olvides de informar de cualquier fallo con el que te puedas topar cuando te actualices a 2.1 RC1 en el bug tracker de Rails – ¡es una versión candidata, así que cualquier informe de fallos será bien recibido!
El informe de esta semana cubre los cambios efectuados en Rails entre el 12 y el 18 de Mayo de 2008.
caches_action condicionalescaches_action ahora recibe una opción :if exactamente igual que caches_pages. Por ejemplo:
caches_action :index, :if => Proc.new { |c| !c.request.format.json? }Esta pequeña mejora es obra de José Valim. El cambio relacionado está en http://github.com/rails/rails/commit/7708650f73ddb4db300ea2059c60c1d907a4384e
Fallo corregido: los finders de ActiveRecord siguen la opción select para garantizar que se incluyen las tablas necesarias en el SQL generadoLa siguiente sentencia
Post.find(:all, :include => :author, :select => 'posts.*, authors.id as "author_id"', :limit => 2)Generaría el código
SELECT posts.*, authors.id as "author_id" FROM "posts" LIMIT 2Obsérvese que no se hace un JOIN sobre la tabla de autores. Este fallo ha sido corregido por John Devine, y el cambio correspondiente es http://github.com/rails/rails/commit/b28b54cab090bed8f099ef375b419a8f92390dd4
¡Hasta la semana que viene!
Este artículo es una traducción de Living on the edge of Rails (20), publicado por Chu Yeow en su blog redemption in a blog.
El informe de esta semana cubre los cambios entre el 5 y el 11 de Mayo de 2008 en Rails.
script/dbconsoleSe ha añadido un script en scripts/dbconsole que nos permite conectarnos a la base de datos usando el cliente de consola de la misma. Si por ejemplo necesitásemos conectarnos al servidor de base de datos de producción (¡más vale que sepamos lo que estamos haciendo!) podríamos ejecutar RAILS_ENV=production script/dbconsole y nos conectará a la base de datos utilizando el cliente de comandos de MySQL. También funcionará con las bases de datos SQLite y PostgreSQL.
Como siempre, para disponer de este script en las aplicaciones que ya tengamos funcionando debemos recordar ejecutar rake rails:update:scripts después de actualizarnos a la última versión edge de Rails
Esta pequeña mejora es obra de Steve Purcell, que ya tenía un plugin similar.
Los cambios correspondientes son http://github.com/rails/rails/commit/4a07103687084496b773e18a03b1f2f5e686f7ad
flash.now en testsEsto era algo con lo que posiblemente muchos desarrolladores de Rails nos hemos topado a la hora de escribir tests de los mensajes flash que envía nuestra aplicación mediante flash.now. Básicamente: uno no puede hacer pruebas sobre los contenidos de flash.now porque siempre se vaciaban antes de que el código del test se ejecutase sobre ellos.
# En el controlador flash.now[:notice] = 'Debes de estar de broma!' # En el test assert_equal 'Debes de estar de broma!', flash.now[:notice] # FALLA porque flash.now es nuloAndreas Neuhaus le echó un buen vistazo al funcionamiento y se aseguró de que se puedan hacer tests sobre flash.now sin tener que recurrir a assert_select
El cambio en cuestión está en http://github.com/rails/rails/commit/74eed6290e63111d1aad2b181692a84f4f040aea
No hay mucho más que reseñar pero, como siempre, si queremos conocer hasta los más minimos detalles, lo mejor es repasar los logs de commit de Rails.
Este artículo es una traducción de Living on the edge of Rails (19), publicado por Chu Yeow en su blog redemption in a blog.
Esta entrada cubre los cambios realizados en Rails entre el 29 de Abril y el 4 de Mayo de 2008.
change_table para las migraciones ActiveRecordGracias a Jeff Dean, que también ha escrito sobre la nueva funcionalidad podemos modificar una tabla con un bloque de la siguiente manera:
change_table :videos do |t| t.add_timestamps t.add_belongs_to :goat t.add_string :name, :email, :limit => 20 t.remove_column :name, :email # puede recibir varios argumentos t.rename :new_name t.string :new_string_column # se ejecuta contra el nombre cambiado end Las funcionalidades clave son:Una excelente mejora, de nuevo gracias a Jeff Dean. El grupo de cambios correspondiente a este parche es http://github.com/rails/rails/commit/96980bd561d79824b6cb6efbcbecdcbf8785d452
ActiveRecord::Base recibe un bloque, al igual que ActiveRecord::Base.newEn efecto, ahora podemos crear objectos ActiveRecord pasando un bloque como argumento exactamente igual que con ActiveRecord::Base.new:
@person = Person.create(params[:person]) do |p| p.name = 'Konata Izumi' p.age = 17 endEl autor es Adam Meehan, y el grupo de cambios correspondiente a este parche es http://github.com/rails/rails/commit/dd120ede53eaf71dee76894998a81626b7a689fc
Arreglo: change_column debería poder usar :null => true en un campo que anteriormente estaba marcado como falseAhora podemos usar change_column en nuestras migraciones para alterar una columan como nulificable si anteriormente estaba marcada como NOT NULL
Esta corrección es cortesía de Nate Wiger y el grupo de cambios relacionado es http://github.com/rails/rails/commit/10ef65a3b054270ed3d458ec8eb7c2b9a3e638f7
Ahí va una rápida que me ha costado trabajo encontrar (gracias a kithpom en #rubyonrails): cuando tenemos varias versiones de la gema de Rails instaladas en nuestro sistema (por ejemplo, la 2.0.2 y la 1.2.6), si ejecutamos rails en la consola se creará un esqueleto de aplicación 2.0 ¿Qué pasa si, por desdicha, necesitamos crear una aplicación 1.2.x?
rails _1.2.6_ myappLa explicación, en cat `which rails`
Ya están disponibles las transparencias de mi presentación en la II Conferencia Rails Hispana.
Lamentablemente, el grueso de la presentación consistió en una demo en la que, con mayor o menor fortuna, traté de mostrar parte de la funcionalidad que Slingshot nos ofrece con muy poco esfuerzo por nuestra parte. Esa parte os la teneis que imaginar :)
Actualización: también están disponibles en Slideshare:
<object height='355' width='425' style='margin:0px'><param name='movie' value='http://static.slideshare.net/swf/ssplayer2.swf?doc=integracin-de-rails-en-el-escritorio-con-slingshot-1195921427627259-5' /><param name='allowFullScreen' value='true' /><param name='allowScriptAccess' value='always' /><embed allowfullscreen='true' type='application/x-shockwave-flash' src='http://static.slideshare.net/swf/ssplayer2.swf?doc=integracin-de-rails-en-el-escritorio-con-slingshot-1195921427627259-5' allowscriptaccess='always' height='355' width='425'></embed></object>
| <a href='http://www.slideshare.net/guest2e577a/integracin-de-rails-en-el-escritorio-con-slingshot' title='View \'Integración de Rails en el Escritorio con Slingshot\' on SlideShare'>View | Upload your ownEn este artículo haremos una breve introducción al servicio Amazon Elastic Computing Cloud (EC2) y describiremos los pasos necesarios para ejecutar nuestra propia instancia y lanzar una aplicación Rails.
¿Qué es Amazon EC2?EC2, o Elastic Computing Cloud es un servicio de Amazon que nos proporciona máquinas bajo demanda. La idea de negocio de Amazon en este caso es vender potencia de CPU, y nada más. Este servicio está ligado a S3, el sistema de almacenamiento flexible de bajo coste que también nos ofrece, cómo no, la compañía de Bezos, que se posiciona así como líder a la hora de ofrecer un nuevo tipo de hosting a precios relativamente moderados: una vez que nuestra máquina en EC2 esté operativa tendremos a todos los efectos acceso como usuario root a un PC de mediana potencia conectado a Internet corriendo el sistema operativo que hayamos escogido, con la particularidad de que esta máquina no es real, sino que corre bajo un hipervisor Xen instalado en una máquina (ahora sí) real gestonada por Amazon. Este detalle más que un inconveniente es una ventaja porque aporta el factor de flexibilidad que hasta hace poco ningún otro proveedor de hosting puede dar: podemos clonar máquinas en EC2 a partir de la imagen de disco bajo demanda, y su funcionamiento se facturará por horas o fracciones lo que nos permite ajustar con bastante exactitud el gasto en CPU a la carga de trabajo necesitada en cada momento. Porque el detalle final es que Amazon sólo facturará cuando las máquinas estén funcionando: igual que podemos desplegar nuevas máquinas según queramos, también podremos pararlas a voluntad, y cuando una máquina se encuentre suspendida no se imputan costes en nuestra maltrecha tarjeta de crédito.
No es de extrañar que este invento de Amazon esté especialmente dirigido a los desarrolladores web, que pueden montar fácilmente arquitecturas en capas distribuidas usando la nube elástica de Amazon y escalar según sus necesidades (¿Necesitamos dos servidores de aplicación más? ¡Sin problema, clona uno que ya tengas! ¿Un servidor de memcached? ¡En cinco minutos!)
En este artículo voy a detallar los pasos necesarios para instalar Ruby on Rails en una máquina EC2 desde Mac OS X y acceder a nuestra aplicación desde el exterior, que como comienzo creo que no está mal. En posteriores artículos intentaré explorar las prestaciones más avanzadas de Amazon EC2 y su aplicación, por supuesto, en nuestras aplicaciones Rails.
Alta en EC2 y S3En primer lugar, hay que darse de alta como cliente de Amazon y suscribir los servicios S3 y EC2 Es importante hacerlo por este orden, porque el alta en Amazon EC2 no puede realizarse a no ser que ya seamos usuarios de S3. Es en estos pasos donde daremos nuestros datos de facturación aunque (como era de esperar) si ya eres cliente de Amazon se te presentarán las tarjetas de crédito que has venido usando en la tienda web. También podremos ver en estas páginas la tabla de precios, que básicamente consisten en tarifas por hora de CPU (10 centavos de dólar por hora o fracción ) y por tasa de transferencia (10 céntimos por Gigabyte de entrada y de 18 a 13 centavos por gigabyte de salida) Los costes de S3 no están incluidos, lógicamente.
Conviene observar que EC2 no es un servicio libre sino que se admiten nuevos usuarios según Amazon va ampliando su capacidad, esto es: el alta no es inmediata sino que hay que esperar (en mi caso creo que han sido unos dos meses) hasta que nos dan de alta. En S3, por el contrario, la activación es instantánea.
Obtener las utilidadesEl primer paso, una vez que hemos recibido un correo de Amazon con la activación del servicio debemos instalar las utilidades EC2 en nuestro ordenador, que requieren de una máquina virtual Java. En el caso de Mac OS, la máquina virtual instalada por defecto servirá sin mayor inconveniente.
Tras descargar las herramientas de línea de comandos aquí podemos descomprimirlas en algún directorio de nuestra elección, en mi caso /Users/juan2/src, lo que creará la carpeta ec2-api-tools-1.2-9739 que asignaremos a la variable EC2_HOME
| export EC2_HOME=/Users/juan/src/ec2-api-tools-1.2-9739 |
En Mac OSX hemos tenido que definir la variable JAVA_HOME a /usr de forma que las utilidades EC2 puedan encontrar el intérprete de Java en su ubicación estándar (/usr/bin)
Generando certificados de autenticaciónPara usar las herramientas EC2 es necesario generar un par de claves utilizando algún mecanismo de cifrado. Estas claves podemos generarlas entrando en el portal de servicios web de Amazon

En nuestro caso hemos decidido utilizar una clave privada y certificado X.509. Aunque la clave privada sólo se genera una vez (hay que tener cuidado con ella), podremos consultar el certificado siempre que lo deseemos aquí

Guardaremos el certificado utilizando nuestro editor de textos favorito recortando todo lo incluido entre las palabras BEGIN CERTIFICATE y END CERTIFICATE de la página del navegador y guardandolo en algún sitio interesante. Haremos lo mismo con la clave privada (teniendo en cuenta que en este caso los rótulos son BEGIN PRIVATE KEY y END PRIVATE KEY)
Nosotros hemos almacenado la clave privada y el certificado en el directorio $HOME/.ec2 con los nombres ec2-pk.pem y ec2-cert.pem respectivamente. Deberemos, por tanto, disponer de las siguientes variables de entorno:
| 1 2 3 | export EC2_CERT=/Users/juan/.ec2/ec2-cert.pem export EC2_PRIVATE_KEY=/Users/juan/.ec2/ec2-cert.pem export PATH=$PATH:$EC2_HOME/bin |
Una vez que está resuelta la cuestión de identificarnos adecuadamente en Amazon, el siguiente paso es escoger una imagen de máquina (o AMI, de Amazon Machine Image) que contendrá el sistema operativo y configuración que deseemos, y arrancaremos una instancia de la imagen en concreto. Esto nos permite ejecutar varias instancias de una misma imagen, lo que equivaldría a tener varios ordenadores que parten de una misma configuración que hayamos fijado de antemano.
| Tamarindo:~ juan$ ec2-describe-images -a |
Veremos un listado con un montón imponente de máquinas preinstaladas. Las más interesantes son las predefinidas por Amazon, con versiones de Fedora Core 4 y algunos paquetes preinstalados, estas son algunas de ellas:
| 1 2 3 | IMAGE ami-20b65349 ec2-public-images/fedora-core4-base.manifest.xml amazon available public IMAGE ami-22b6534b ec2-public-images/fedora-core4-mysql.manifest.xml amazon available public IMAGE ami-23b6534a ec2-public-images/fedora-core4-apache.manifest.xml amazon available public |
Pero nada impide a los usuarios de EC2 definir y compartir sus propias AMIs, con lo que si buscamos un poco seguro que encontraremos alguna AMI que se ajuste más a nuestros propósitos (las hay con Wordpress preinstalado, JBoss, etc). Nos quedaremos con ami-b7b95cd, que es una imagen virtual con Debian Etch (aunque en realidad parece ser Ubuntu, según pudimos comprobar a posteriori)
Generar keypairSi antes tuvimos que generar un par de claves para que las herramentas de línea de comandos puedan identificarse correctamente en nuestro nombre ante el servicio EC2, ahora es el turno de generar otro par de claves para poder acceder a nuestra instancia una vez que se encuentre activada.
Siguiente las instrucciones de esta página, generaremos un par de claves con el siguiente comando
| Tamarindo:~ juan$ ec2-add-keypair sbr-test1 |
Guardando el resultado en un fichero de nuestra elección, por ejemplo ~/.ec2/id_rsa-sbr-test1 Este fichero no debe tener permisos de lectura para el resto de usuarios o de lo contrario ssh no nos permitirá utilizarlo como credenciales (un chmod 600 … debería ser suficiente)
Activación de la instancia de nuestra AMIHa llegado el momento de la verdad: arrancar una instancia de la máquina escogida. Esta instancia nos pertenecerá en todo momento y quedará desvinculada de su AMI inicial.
| 1 2 3 | Tamarindo:~ juan$ ec2-run-instances ami-b7b95cde -k sbr-test1 RESERVATION r-7052b519 346199905863 default INSTANCE i-d66a8abf ami-b7b95cde pending sbr-test1 0 |
El comando nos indica que nuestra instancia se encuentra pending, esto es, aún no está operativa: el proceso tarda un par de minutos en completarse, durante el cual podemos comprobar el estado de nuestra instancia hasta que el resultado sea parecido al que mostramos a continuación:
| 1 2 3 4 | Tamarindo:~ juan$ ec2-describe-instances i-d66a8abf RESERVATION r-7052b519 346199905863 default INSTANCE i-d66a8abf ami-b7b95cde ec2-72-44-35-69.z-2.compute-1.amazonaws.com domU-12-31-35-00-0A-01.z-2.compute-1.internal running sbr-test1 0 |
Podemos comprobar que el estado es running y que tenemos dos interfaces disponibles, el segundo de ellos (domU-..) es el nombre de la máquina en la red interna de Amazon (conviene utilizarlo a la hora de establecer conexiones entre instancias) y el primero de ellos es el que utilizaremos para acceder a la máquina desde cualquier punto de Internet como veremos más abajo.
Autorizar tráfico a la máquina virtualPero antes de entrar en la máquina hay que permtir el acceso a los puertos de la misma, que por defecto estarán cerrados. Autorizaremos los puertos 22 y 80, por motivos evidentes:
| 1 2 3 4 5 6 | Tamarindo:~ juan$ ec2-authorize default -p 22 GROUP default PERMISSION default ALLOWS tcp 22 22 FROM CIDR 0.0.0.0/0 Tamarindo:~ juan$ ec2-authorize default -p 80 GROUP default PERMISSION default ALLOWS tcp 80 80 FROM CIDR 0.0.0.0/0 |
Ya está todo listo, no nos quedan más pasos que dar salvo entrar en nuestra flamante instancia para ver el familiar mensaje de bienvenida de Ubuntu:
| 1 2 3 4 5 6 7 8 9 10 | Tamarindo:~ juan$ ssh -i /Users/juan/.ec2/id_rsa-sbr-test1 root@ec2-72-44-35-69.z-2.compute-1.amazonaws.com Linux ubuntu 2.6.16-xenU #1 SMP Thu Nov 30 13:48:50 SAST 2006 i686 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. ubuntu:~# |
Es interesante notar que esta imagen se encuentra optimizada para su uso bajo Xen (lo que nos viene de perlas).
Entorno de desarrollo con RailsCualquier manual para instalar Rails en Ubuntu servirá. En nuestro caso, instalaremos sudo, crearemos un usuario normal y lo añadiremos a /etc/sudoers. Abandonaremos la cuenta root y haremos ssh con este usuario:
| 1 2 3 4 | apt-get install sudo adduser juan vi /etc/sudoers exit |
| ssh juan@ec2-72-44-35-69.z-2.compute-1.amazonaws.com |
Esta vez tendremos que identificarnos con nuestra propia clave, ya que ahora no estamos usando el par de claves anteriormente generadas (nada nos impide generarnos otro par para esta nueva cuenta de usuario) A continuación daremos la bienvenida a los sospechosos habituales:
| 1 2 3 4 5 | sudo apt-get install ruby1.8-dev sudo apt-get install build-essential sudo apt-get install rubygems sudo apt-get install rails rails pruebas |
Arrancaremos nuestra aplicación (de momento usando Webrick) escuchando en el puerto 80:
| juan@ubuntu:~/pruebas$ sudo script/server --port 80 -b 0.0.0.0 |
Tras lo cual…..

Con esto podemos dar por concluida la primera ráfaga de instalación de Rails en nuestra instancia EC2, aunque antes…
¿Y para terminar?Como decíamos, la instancia de la máquina imputará costes en nuestra cuenta por cada hora de uso, así que de momento no queremos dejarla funcionando. Para parar la instancia:
| 1 2 | Tamarindo:~ juan$ ec2-terminate-instances i-8b7191e2 INSTANCE i-8b7191e2 running shutting-down |
Hemos avanzado un trecho importante: hemos sido capaces de acceder a una instalación de Rails que vive en la nube elástica de Amazon. En próximos artículos espero poder mostrar algunas de las prestaciones más avanzadas de Amazon (a medida que las vaya explorando, vaya)
Mingle es una aplicación escrita con Ruby on Rails. Hasta aquí, nada de extrardinario.
Lo interesante llega cuando nos cuentan algunas de sus características: será una aplicación de escritorio para entornos corporativos y que deberá correr sobre Windows, Linux y Mac.
Y más interesante aún es la plataforma sobre la que corre esta aplicación Rails: usa como base de datos Derby y Jetty como servidor web. Nada que ver con los habituales mongrels y MySQL.
A estas alturas ya no nos sorprende saber que Mingle también se distribuirá como un fichero .war que se puede desplegar en cuaqluier contenedor J2EE.
Todo esto es posible gracias a JRuby
Desde que trabajo con Ruby on Rails de manera intensiva cada día, una de las cosas que más me incordia es abandonar el cómodo entorno de script/console para lanzar la (al menos para mí) menos cómoda interfaz de mysql La razón por la que me veo obligado a hacer esto con cierta frecuencia es para encontrar los ids de los objetos ActiveRecord que me interesan para investigar o depurar alguna funcionalidad. Cuando la aplicación tiene cientos de registros en las tablas, viene muy bien poder hacer consultas por el nombre aproximado con la construcción LIKE de SQL. ActiveRecord nos permite escribir nuestro propio SQL pero como soy un vago me resulta bastante tedioso escribir la condición completa con la sintaxis que pide Rails, así que normalmente ando saliendo de la consola, escribiendo una sentencia SELECT ... LIKE en mysql y volviendo a la consola con el id apuntado.
Hasta hoy, que me he terminado de cansar y he escrito un plugin que permite hacer sentencias LIKE desde ActiveRecord de manera dinámica.
Finders dinámicos en ActiveRecordLos finders dinámicos de ActiveRecord son una de las primeras perlas de Ruby con las que uno se encuentra cuando investiga las tripas de Rails. Supongo que serán conocidos, pero no está de más repasarlos un poco.
Supongamos que tenemos una clase ActiveRecord mapeada sobre una tabla MyTable con los atributos atributo1, atributo2 y atributo3, sabemos que podemos escribir:
| Mytable.find_by_id(87) |
Y ActiveRecord tratará de encontrar el registro identificado con el id 87. No parece nada del otro mundo, porque entre otras convenciones ActiveRecord asume que la clave primaria en nuestras tablas será una columna llamada id, por lo que podría ser que existiese un método find_by_id en ActiveRecord::Base
Pero resulta algo más sorprendente toparse con que sentencias como:
| Mytable.find_by_atributo1('Valor') |
también funcionan. Aquí comienza a saborearse el dinamismo de Ruby, y podemos asumir que ActiveRecord, de manera astuta, construye tantas funciones Mytable::find_by_... como atributos tengamos en la tabla, que lanzarán la correspondiente consulta SQL a la base de datos.
Pero.. ¡un momento!
| 1 2 | Mytable.find_by_atributo1_and_atributo3('Valor', 58) Mytable.find_by_atributo1_and_atributo2_and_atributo3('Valor',58,'Valor3') |
¡También! funcionan. Y sería ridículo pensar que ActiveRecord construye funciones de manera dinámica para todas las combinaciones posibles de los atributos (y, además, en cualquier orden que queramos)
Nuestro propósito es añadir más finders dinámicos, que en lugar de búsquedas exactas hagan búsquedas aproximadas, invocándose de la siguiente manera:
Mytable.find_like_atributo1('al')Para hacerlo, tenemos que entender bien qué hace ActiveRecord con estas misteriosas funciones dinámicas…
La magia de method_missingA estas alturas ya nos imaginamos que no se añaden métodos para cada atributo, sino que hay algún otro mecanismo actuando en este caso. Se trata de method_missing que es el método que ejecuta una clase Ruby cuando se le invoca un método que no tiene implementado. Aquí podeis ver otro uso muy creativo de method_missing
ActiveRecord se aprovecha de method_missing para articular los finders dinámicos. Si abrís el fichero lib/active_record/base.rb vereis que la clase ActiveRecord::Base define un method_missing muy especial (si no estais viendo ese código, hacedlo ahora: leer código del core de Rails es siempre un ejercicio inspirador). Este método se activa cuando llamamos a Mytable.find_by_attributo('valor'), y lo primero que hace es comprobar con una expresión regular si el método invocado tenía la pinta find_by o find_all_by, en cuyo caso pasa a construir una sentencia SQL de búsqueda según los atributos y parámetros recibidos. Parece evidente que nosotros tendremos que puentear este método y hacer algo parecido por nuestra cuenta pero construyendo sentencias SQL con el modificador LIKE.
Cómo construir nuestro propio finderLo primero es preparar un plugin, lo cual es tan fácil como crear un directorio find_like en vendor/plugins, y ahí escribiremos un fichero init.rb con el siguiente contenido:
| require 'find_like' |
A continuación, creamos el directorio vendor/plugins/find_like/lib y ahí pondremos el código de nuestro plugin:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | module ActiveRecord class Base class << self private def construct_conditions_from_like_arguments(attribute_names, arguments) conditions = [] attribute_names.each_with_index{ |name, idx| conditions <<= "#{table_name}.#{connection.quote_column_name(name)} LIKE '%%" << arguments[idx] << "%%'"} [conditions.join(" AND "), *arguments[0..attribute_names.length]] end alias_method :previous_method_missing, :method_missing def method_missing(method_id, *arguments) if match = /find_(all_like|like)_([_a-zA-Z]\w*)/.match(method_id.to_s) finder = (match.captures.first == 'all_like' ? :find_every : :find_initial) attribute_names = extract_attribute_names_from_match(match) super unless all_attributes_exists?(attribute_names) conditions = construct_conditions_from_like_arguments (attribute_names, arguments) options = {:conditions => conditions} set_readonly_option!(options) send(finder, options) else previous_method_missing(method_id, *arguments) end end end end end |
El código más o menos es fácil de seguir: usamos class << self para abrir la clase ActiveRecord::Base y modificar sus propias tripas. Dado que estamos volviendo a escribir el método method_missing no querremos perder la funcionalidad ofrecida por el method_missing originalmente incluido con ActiveRecord::Base (el que veíamos antes en base.rb) así que simplemente haremos un alias a este método y lo llamaremos desde nuestro method_missing si detectamos que el método invocado no es uno de los que nosotros queremos controlar (find_like o find_all_like)
Tras esto, podemos lanzar una consola y comprobar que, por arte de birlibirloque, ya tenemos nuestros finders operativos:
| 1 2 3 4 5 6 7 8 9 | Tamarindo:~/src/repos/dconrails juan$ script/console development Loading development environment. >> Algoritmo.find_all_like_nombre("oyal") => [#<Algoritmo:0x245c908 @attributes={"status"=>"BEGIN", "nombre"=>"RoyalRoadDemo"}>, #<Algoritmo:0x245c82c @attributes={"status"=>"RUN", "nombre"=>"RoyalRoad2">] >> Algoritmo.find_all_like_nombre_and_status("oyal", "UN") => [#<Algoritmo:0x243dabc @attributes={"status"=>"RUN", "nombre"=>"RoyalRoad2"}>] |
Hace unos meses, los chicos de b-simple publicaron el tutorial en PDF RESTful Rails Development con licencia CC. Desde hoy, está disponible también la versión en castellano traducida por mí.
El objetivo de esta anotación es mostrar los pasos necesarios para lanzar una aplicación Rails dentro de un servidor de aplicaciones Java, en este caso el popular JBoss La intención última es comprobar los progresos está llevando a cabo el equipo de JRuby. Quién nos lo iba a decir hace un año, así que… ¿quién nos dirá qué va a ocurrir para el año que viene?
Instalación de JRubyEl primer paso será la instalación de JRuby , por supuesto. Damos por ya instalado Java en nuestra máquina. Para Mac OS X, la instalación por defecto de Java será suficiente (lo cual es raro)
| 1 2 3 | cd ~/jrubydemo/ wget http://dist.codehaus.org/jruby/jruby-bin-1.0.1.tar.gz tar zxvf jruby-bin-1.0.1.tar.gz |
Y se creará el directorio jruby-1.0.1. A continuación, añadiremos las siguientes variables al entorno de ejecución actual, una forma de hacerlo es la siguiente
| 1 2 3 | Tamarindo:~/jrubydemo/ juan$ cat > entorno export JRUBY_HOME=`pwd`/jruby-1.0.1 export PATH=$JRUBY_HOME/bin:$PATH |
Cada vez que cambiemos este fichero tendremos que acordarnos de ejecutar el nuevo entorno en todas las sesiones que tengamos abiertas, hasta que añadamos estas variables al entorno por defecto.
| . entorno |
Ya podemos comprobar que las variables de entorno están presentes. Posteriormente podremos añadir la carga de este entorno a nuestro profile, pero de momento nos bastará con recordar que hay que evaluar este archivo de entorno para poder trabajar con JRuby. Ya podemos hacer nuestra primera, emocionante prueba:
| 1 2 | Tamarindo:~ juan$ jruby -v ruby 1.8.5 (2007-08-23 rev 4201) [i386-jruby1.0.1] |
| 1 2 | Tamarindo:~ juan$ which gem /Users/juan/jrubydemo/jruby-1.0.1/bin/gem |
JRuby incluye su propia versión de gem, y todas las gemas que instalemos con el entorno de JRuby quedarán instaladas en $JRUBY_HOME/lib/gems, de manera que no romperemos nuestra instalación estándar de Rails y otra gemas.
Instalación de Rails sobre JRubyMerced a la flexibilidad del sistema de paquetes de Ruby, este paso será muy sencillo. Para instalar a los sospechosos habituales bastará con:
| 1 2 | gem install rails -y --no-rdoc gem install activerecord-jdbc --no-rdoc --no-ri |
Esto instalará las gemas habituales: ActionMailer, ActionPack, ActiveSupport, ActiveRecord, Rails, Rake, RSpec, etc. asi como el adaptador JDBC para ActiveRecord. En este punto es cuando cualquier tutorial de Rails que se precie creará una aplicación de prueba (por ejemplo, un blog, un CMS, un mashup o cualquier otra cosa) en menos de 5 minutos. Nosotros, que somos algo más vagos, optaremos por instalar una aplicación que ya tenemos lista en su repositorio de Subversion:
| 1 2 3 4 | cd ~/jbossdemo svn checkout http://mi-repositorio.com/myapp/trunk myapp cd myapp jruby script/server |
Y, si la base de datos está arrancada y ’’config/database.yml’’ está bien configurado, la aplicación funcionará sin más dilación. El mismo código de nuestra aplicación funcionará tanto sobre la implementación de Ruby en C que sobre la implementación en Java. No es poco.
Más difícil todavía: JBossDescargaremos JBoss de Sourceforge. El paquete zip es algo más pequeño:
| 1 2 3 4 | http://labs.jboss.com/jbossas/downloads/ (4.2.1GA) cd ~/jrubydemo/ wget http://downloads.sourceforge.net/jboss/jboss-4.2.1.GA.zip?modtime=1184616808&big_mirror=1 unzip jboss-4.2.1.GA.zip (la versión zip es más pequeña) |
Y por supuesto a nuestro fichero de entorno añadiremos:
| 1 2 3 4 | cd ~/jbossdemo export JRUBY_HOME=`pwd`/jruby-1.0.1 export JBOSS_HOME=`pwd`/jboss-4.2.1.GA export PATH=$JRUBY_HOME/bin:$PATH |
Este paso es lógicamente específico de JBoss, pero es necesario para correr nuestra aplicación. Dado que en el mundo Java no está aún muy extendido lo de la convención sobre configuración, tendremos que explayarnos un poco en el acceso a la base de datos, creando el fichero ’’jboss-4.2.1.GA/server/default/deploy/mysql-ds.xml’‘
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>MySqlDS</jndi-name> <connection-url>jdbc:mysql://mysql-hostname:3306/myapp</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>usuario</user-name> <password>clave</password> <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name> <metadata> <type-mapping>mySQL</type-mapping> </metadata> </local-tx-datasource> </datasources> |
Con esta configuración le hemos dicho a JBoss que acceda a MySQL usando JDBC para acceder a la base de datos. Pero aún necesitamos instalar el correspondiente driver JDBC para Java, lo que MySQL denomina conector . De esta página podremos descargar el conector en forma de archivo .zip y la instalación es tan sencilla como copiar un archivo ’’.jar’’ al directorio lib del directorio de nuestro servidor.
| 1 2 3 4 | cd ~/jbossdemo wget http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.3-rc.zip/from/http://mysql.rediris.es/ unzip mysql-connector-java-5.1.3-rc.zip cp mysql-connector-java-5.1.3-rc/mysql-connector-java-5.1.3-rc-bin.jar jboss-4.2.1.GA/server/default/lib/ |
Con todo esto, ya podremos probar si JBoss goza de buena salud.
cd $JBOSS_HOME bin/run.shTras varios mensajes esotéricos, JBoss cobra vida:
INFO [Server] JBoss (MX MicroKernel) [4.2.1.GA (build: SVNTag=JBoss_4_2_1_GA date=200707131605)] Started in 16s:863msTambién nos interesa el mensaje
[ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=MySqlDS' to JNDI name 'java:MySqlDS'que indica que JBoss ha creado el gestor de conexiones MySQL. Es hora de mudar nuestra aplicación Rails.
Creando un archivo .warLa manera estándar de desplegar aplicaciones dentro de un contenedor Java es mediante el uso de archivos war” los cuales no son más que ficheros zip que contienen toda una jerarquía de directorios con clases y recursos estáticos (HTML, imágenes, etc) aderezados con ciertos metadatos. Cuando uno de estos ficheros aparece en el directorio adecuado del servidor, éste lo abre, analiza los metadatos y toma las acciones pertinentes para activar la aplicación.
Así, nos hace falta crear un fichero war que contenga nuestra aplicación Rails. Afortunadamente, no tenemos que preocuparnos mucho del asunto porque ya existe un plugin Rails que se encarga de ello, creado por Charles Nutter. Tenemos toda la información necesaria en este wiki
cd ~/jbossdemo/myapp jruby script/plugin install svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration/plugins/goldspikePara configurar los contenidos de nuestro war no todo van a ser convenciones, tenemos que editar el archivo config/war.rb para referenciar el conector.
| 1 2 | maven_library 'mysql', 'mysql-connector-java', '5.0.4' |
Teniendo en cuenta que por defecto Goldspike ejecutará la aplicación Rails en modo producción, tendremos que configurar adecuadamente ’’config/database.yml’’ Por último:
| rake war:standalone:create |
Tras un rato de funcionamiento la tarea de Rake finalizará dejando un bonito myapp.war en nuestro directorio. Obsérvese que el fichero .war pesa la friolera de 18M: no en vano incluye todo JRuby, Rails y las gemas que tuviéramos instaladas. Si no hemos parado la anterior instancia de JBoss podemos copiar en caliente el war:
cp myapp.war /Users/juan/jbossdemo/jboss-4.2.1.GA/server/default/deploy/Veremos que en JBoss comienzan a pasar cosas y nuestra aplicación cobra vida.
23:29:10,762 INFO [TomcatDeployer] deploy, ctxPath=/myapp, warUrl=.../tmp/deploy/tmp22213myapp-exp.war/ 23:29:11,322 INFO [[/myapp]] Ruby is running in standalone mode 23:29:14,472 INFO [[/myapp]] JRuby init time: 2082ms 23:29:26,946 INFO [[/myapp]] Rails init time: 9818ms 23:29:26,947 INFO [[/myapp]] Runtime 0 loaded 23:29:26,947 INFO [[/myapp]] Requests can now be processed 23:29:35,874 INFO [[/myapp]] Runtime 1 loadedBásicamente, JBoss descomprime el war en un directorio temporal y lanza dos instancias de Rails, lo que sería equivalente a dos mongrels tradicionales (por supuesto todo esto en configurable, según se explca en el wiki de Headius)
Tras esto, y si no ha habido errores (es de suponer que no) ¡es el momento de probar nuestra aplicación! Sólo debemos tener en cuenta que la URL raíz es, por la configuración de JBoss por defecto, algo distinta: http://127.0.0.1:8080/myapp, por lo que las URLs absolutas de nuestra aplicación (si las hubiera, que no debería) dejarán de funcionar.