#227 Upgrading to Rails 3 Part 3
Con este episodio concluimos nuestra serie sobre la actualización a Rails 3 de una aplicación Rails 2. Ya hemos recorrido un largo trecho; pasan todos los tests de la aplicación y hemos usado el plugin de actualización de Rails para localizar y corregir en su mayor parte aquellas áreas de la aplicación que necesitan ser modificadas para funcionar en Rails 3. Pero nos siguen quedando cosas que hay que corregir en la capa de vistas por lo que dedicaremos este episodio a su corrección.
Eliminación de las advertencias de código no actualizado
Antes de que comenzar a corregir la capa de vista le vamos a echar otro vistazo a los tests. Al acabar el último episodio pasaban todos los tests pero aparecía un gran número de advertencias que avisaban de que estamos usando funcionalidades que son a extinguir. Ejecutemos una vez más los tests para ver si podemos reducir estos avisos.
$ rake spec # (se omite gran parte de la salida) .DEPRECATION WARNING: error_messages_for was removed from Rails and is now available as a plugin. Please install it with `rails plugin install git://github.com/rails/dynamic_form.git`. (called from _app_views_sponsors__form_html_erb___2363957037552137609_2171491000_4161310651677273387 at /Users/eifion/rails/apps_for_asciicasts/ep227/railscasts/app/views/sponsors/_form.html.erb:2) <span class="passed">...................................................</span> <span class="passed">Finished in 3.24 seconds</span> <span class="passed">152 examples, 0 failures</span>
Los tests siguen pasando pero aparecen, como vemos, muchas advertencias. Por fortuna casi todas son duplicadas por lo que no nos debería costar demasiado quitarlas.
Hemos omitido la mayor parte de la salida, quedándonos sólo con el último mensaje. En Rails 3 los métodos
error_messages_for
y error_messages_on
son a extinguir, y podemos cambiarlos por un código nuestro que recorra los mensajes de error de nuestro modelo con errors.full_messages
. Los antiguos métodos seguirán estando disponibles en forma de plugin, por lo que tomaremos este camino más facil y lo instalaremos. Las instrucciones de instalación aparecen en el mismo mensaje de advertencia.
$ rails plugin install git://github.com/rails/dynamic_form.git Initialized empty Git repository in /Users/eifion/rails/apps_for_asciicasts/ep227/railscasts/vendor/plugins/dynamic_form/.git/ remote: Counting objects: 22, done. remote: Compressing objects: 100% (17/17), done. remote: Total 22 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (22/22), done. From git://github.com/rails/dynamic_form * branch HEAD -> FETCH_HEAD
Si volvemos a ejectuar rake spec
después de instalar el plugin veremos que la advertencia deja de aparecer. Sin embargo hay otros errores, apareciendo el siguiente con más frecuencia:
DEPRECATION WARNING: subclasses is deprecated and will be removed from Rails 3.0 (use descendants instead). (called from block (2 levels) in load_models at /Users/eifion/.rvm/gems/ruby-1.9.2-rc2/gems/thinking-sphinx-2.0.0.rc1/lib/thinking_sphinx/context.rb:58)
La responsable de este mensaje es la librería Thinking Sphinx. Si recibimos errores de un plugin debemos mirar en su seguimiento de errores si se han reportado problemas parecidos al que nos estemos encontrando. Si no es así podemos añadirlo nosotros mismos y tal vez se corrija en la siguiente versión (como alternativa, siempre podemos bifurcar el código y corregir el error nosotros mismos)
Este problema, en el caso de Thinking Sphinx, ya ha sido corregido pero todavía no está incluido en la última revisión de la gema a fecha de hoy. Para eliminar el mensaje de nuestra aplicación podemos decirle a Bundler que obtenga el código de Thinking Sphinx de un repositorio de git en lugar de la gema.
Así, en lugar de incluir Thinking Sphinx de esta manera:
gem 'thinking-sphinx', '>=2.0.0.rc1', :require => 'thinking_sphinx'
lo incluiremos de esta otra:
gem 'thinking-sphinx', :require => 'thinking_sphinx', :git => "git://github.com/freelancing-god/thinking-sphinx.git", :branch => "rails3"
Una vez más tenemos que ejecutar bundle install
para que descargue Thinking Sphinx de su repositorio de git tras lo cual la aplicación empezará a usar esta versión en lugar de la de la gema. Si volvemos a ejecutar nuestros tests veremos que pasan todos sin advertencia alguna:
$ rake spec <span class="passed">........................................................................................................................................................</span> <span class="passed">Finished in 3.3 seconds</span> <span class="passed">152 examples, 0 failures</span>
Ajustes de las vistas
Una vez que nos hemos quitado de enmedio los mensajes que nos avisan de funcionalidades que dejan de estar soportadas nos podemos centrar en las vistas. El primer problema que salta a la vista es que la barra lateral está ausente en la página de los episodios.
Una parte tan importante de la página como esta debería estar cubierta por un test, aunque sólo fuese para comprobar su presencia. Si estuviéramos actualizando la aplicación para producción entonces escribiríamos un test, pero nosotros no lo haremos.
Si miramos en el fichero de layout, el código que genera la barra lateral es el siguiente:
<%= yield(:side) || render(:partial => 'shared/side') %>
Este código hará un yield
de la barra lateral, y si el resultado es nil
, entonces mostrará el parcial llamado side
. El problema es que en versiones anteriores de Rails yield
devuelve nil si se ha hecho la llamada a content_for
en la página, mientras que en Rails 3 devolverá una cadena vacía por lo que nunca pasará por la segunda llamada de la condición.
Para corregir esto podemos usar el método content_for?
que devolverá true
si el bloque de contenido ha sido definida. Si lo tiene mostraremos ese bloque y en caso contrario se mostrará el parcial.
<%= content_for?(:side) ? yield(:side) : render(:partial=> 'shared/side') %>
Si recargamos la página ahora se verá la barra lateral.
Aún nos quedan por corregir otros problemas en las vistas. Si cargamos la página de un episodio, podremos comprobar que en las notas hay parte del contenido que está siendo escapado.
El contenido de esa parte de la página pasa por el método textilize
que está definido en el módulo
ApplicationHelper
.
module ApplicationHelper def textilize(text) Textilizer.new(text).to_html unless text.blank? end end
El método devuelve HTML como cadena. En Rails 3 si vamos a pasar una cadena de HTML para que la vista la muestre debemos marcarla como "HTML seguro", o de lo contrario será automáticamente escapada. Tan sólo necesitamos para ello ejecuta rel método html_safe
sobre la cadena.
module ApplicationHelper def textilize(text) Textilizer.new(text).to_html.html_safe unless text.blank? end end
Tras esto la cadena queda marcada como segura y ya no sufrirá el proceso de escape. Vimos esto con detalle en el episodio 204 [verlo, leerlo]. Podremos ver que las anotaciones vuelven a aparecer correctamente cuando recarguemos la página.
Sólo nos queda un pequeño problema en la página: la barra de títulos debería incluir el nombre del episodio. Este problema es específico de la forma en la que se gestionan los títulos en la aplicación, en lugar de utilizar content_for
para el título se usa un método helper llamado title
que establece una variable de instancia.
module LayoutHelper def title(page_title, show_title = true) @content_for_title = page_title.to_s @show_title = show_title end end
Para corregir esto deberíamos establecer el contenido del título usando content_for
de la siguiente manera:
module LayoutHelper def title(page_title, show_title = true) content_for(:title, page_title.to_s) @show_title = show_title end end
Con esto ya podemos llamar a yield(:title)
en el layout y el título aparecerá correctamente. Podemos pasar el contenido en un bloque directamente o, como es nuestro caso, como un segundo argumento.
Corrección de los enlaces de borrado
El siguiente error aparece en la sección de administración de la aplicación. En la página de abajo aparece un listado de objetos del modelo, cada uno con un enlace “edit” y “destroy”.
El problema radica en los enlaces a “destroy”. Si hacemos clic en uno de ellos seremos llevados a la acción code>show
de la pregunta, en lugar de hacer undestroy
. La razón por la que no se llama a delete
es porque en Rails 3 el JavaScript que se usa para estos enlaces es no destructivo. Si examinamos el HTML de una de las preguntas comprobaremos que el JavaScript ya no aparece embebido en la etiqueta del enlace de borrado. En su lugar hay dos atributos de HTML5 que comienzan con data-
y deberíamos tener código JavaScript que detectase estos atributos y cambiase la petición para que fuese de tipo DELETE.
<tr> <td>What does the M stand for in MVC?</td> <td>model</td> <td><a href="/spam_questions/1/edit">Edit</a></td> <td><a href="/spam_questions/1" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Destroy</a></td> </tr>
Como usamos jQuery en nuestra aplicación tenemos que descargar un fichero rails.js adaptado del proyecto jquery-ujs. Esto no sería necesario si usásemos Prototype porque el fichero correcto ya viene incluido en el directorio /public/javascripts
. Cuando hayamos descargado y copiado a este lugar el fichero en cuestión tendremos que referenciarlo en la sección de cabecera del fichero de layout de nuestra aplicación. También tenemos que añadir una etiqueta meta para evitar peticiones falsas XSS:
<%= javascript_include_tag 'jquery', 'rails', 'application' %> <%= csrf_meta_tag %>
Cubrimos el nuevo uso de JavaScript no intrusivo en el episodio 205 [verlo, leerlo].
Ya que estamos en el fichero de layout actualizaremos la aplicación para que use HTML5 cambiando el DOCTYPE y la etiqueta de apertura:
<!DOCTYPE html> <html>
Tendremos que pasar el código por un validador, por todavía quedan otras cosas por hacer para que la aplicación sea totalmente compatible con HTML5 válido.
Con el nuevo fichero rails.js
en su sitio, si recargamos la página de preguntas e intentamos borrar una de las preguntas, aparecerá el diálogo de confirmación de JavaScript y la cuestión será borrada si lo confirmamos.
El último cambio que nos queda por hacer al código de la vista es borrar las llamadas al método h
. En Rails 2 cuando queríamos que la salida fuese escapada en HTML teníamos que rodearla por el método h
. En Rails 3 toda la salida viene escapada por defecto, por lo que podemos aclarar las vistas repasándolas y borrando dicho método. Esto lo vimos en el capítulo 204 [verlo, leerlo]. Si dejamos tal cual las llamadas al método tampoco pasará nada, pero el aspecto de la vista resultará mucho más claro si lo quitamos.
Borrado de ficheros obsoletos
Ya nos queda poco para terminar con la actualización; ya funciona todo pero tenemos que hacer limpieza de algunos de los ficheros que ya no nos hacen falta. Cuando generamos la nueva aplicación Rails 3, se nos habrá creado una nueva página de bienvenida que hemos de borrar.
$ rm public/index.html
Lo mismo para la imagen con el logotipo de Rails que aparece en dicha página.
$ rm public/images/rails.png
También hay ficheros en el directorio /script
que ya no son necesarios para Rails 3, y que podemos borrar.
$ ls script about destroy process setup autospec generate rails setup_test console performance runner spec dbconsole plugin server spec_server
Rails 3 sólo utiliza el script rails
, por lo que podemos borrar los otros como console
, server
, etc.
Con esto damos por concluida la serie de episodios dedicada a la actualización de aplicaciones Rails 2. Debemos tener en cuenta que mucho de lo que hemos cubierto aquí es específico de la aplicación de Railscasts, por lo que es imposible que hayamos visto absolutamente todo lo necesario para actualizar una aplicación cualquiera. Si vemos avisos de funcionalidad no soportada o errores es posible que alguien se haya topado con el mismo problema y podamos encontrar una solución con una búsqueda en Google. También es recomendable repasar los últimos episodios que cubren Rails 3.
La mayoría de los problemas con los que nos toparemos cuando actualicemos una aplicación vendrán provocados por los plugins. El sitio de Rails Plugins tiene un listado de plugins, indicando cuáles de ellos son compatibles con Rails 3 y cuáles no. Si tenemos problema con un plugin en concreto, también puede ser interesante mirar su repositorio en Github para ver si existe una versión compatible con Rails 3.