Actualizar/Migrar a Ruby 2.0 y Rails 4 sin morir en el intento


El otro día me tocó hacer una "loca movida" y actualizar el servidor Rails que le monté a un cliente a Ruby 2.0. ¿La razón? Bueno, necesidad por un lazy ennumerator. La cosa es, que necesitaba una manera de iterar sobre un array de cierta colección de mi base de datos pero yendo de uno por uno en lugar de "todo desde el principio y luego en scope" como suele hacerse en Ruby y Rails generalmente; Por otro lado, estoy desarrollando una aplicación muy importante y resulta ser que la salida de Rails 4 me tomó por sorpresa, entonces tuve que migrar la aplicación que estaba siendo desarrollada en la última versión de Rails 3 directamente a la versión 4. Vamos a por las 2 partes del pastel de actualización:

Ruby

Ruby 2.0 nos trae bastantes mejoras y adiciones, por ejemplo ese tipo de enumerador que comento. Ahora en lugar de usar un yielder o algo similar, simplemente hago un:

Email.all.lazy
y le hago lo que necesite a los elementos que se leen uno por uno según se requieren en la base de datos. Esto es muy bueno ya que aumenta la velocidad y eficacia de muchas cosas pero además me permite hacer en una sola línea de código cosas que haría con algún método definido a mano.

El cambio fue hasta eso suave y nada problemático, pero tuve que hacerlo de un modo específico. Veamos como:

NOTA: usaremos RVM como referencia en este tutorial

Asumiré para propósitos del post, que ya tienes instalado Ruby y Rails en tu computadora y que el medio de instalación que ocupaste fue RVM, no enseñaré cómo hacer este setup aquí porque nuestra finalidad es hacer un post de upgrade, no uno de setup inicial. Una vez dejado eso en claro, corremos los siguientes comandos para actualizar:

1. rvm install 2.0.0
2. source "/usr/local/rvm/scripts/rvm"
3. rvm --default use 2.0.0

NOTA: Aquí hacemos una pausa para cerrar la terminal y volverla a abrir, luego seguimos con los siguientes comandos

3. gem update --system
4. gem install rubygems-update
5. update_rubygems
6. gem install bundler --pre
7. gem update --no-ri --no-rdoc
8. gem update rails --no-ri --no-rdoc

NOTA: Reinicia la computadora cuando llegues aquí. Esto de una vez actualizará Rails también, revisa bien qué versiones se instalan pues las ocuparás para las definiciones que haremos a continuación. P.D. Es recomendable hacer los cambios que verás a continuación en una nueva rama de Git dentro de nuestro proyecto para evitar percances a futuro por cualquier cosa.

Rails

Eso actualiza las gemas de nuestro sistema, ahora tenemos que abrir el Gemfile de la/las aplicaciones que queremos actualizar a Ruby 2.0/Rails 4 y añadir la definición explícita de Ruby y Rails 4 como se ve a continuación:


También tienes que actualizar las versiones de todas las gemas "versionadas" que se contengan en dicho Gemfile usando las nuevas versiones instaladas en el sistema que podrás obtener si corres el comando gem list . Otro tweak a realizar es deshacerse del "assets group" en el Gemfile. Acá unas capturas de uno de mis Gemfiles antes y después:


Por cierto, si se encuentran con una gema que ofrece más de una opción de versión, usen siempre la primera (más actualizada) como detallo en la siguiente imagen:


Ahora, en nuestro "application.rb" debemos cambiar el condicional de Bundler por un simple require de la siguiente manera:


y verificar que la línea de config.active_record.whitelist_attributes esté comentada como se aprecia en ambos casos. Hacemos un bundle update  y veremos que "todo pasa" sin ningún problema... ¡Pero es mentira! debemos correr también un bundle outdated  y veremos que algunas gemas están desactualizadas:


Aquí las gemas que nos interesan son aquellas en la lista que nosotros hayamos definido específicamente en nuestro Gemfile, si no están explícitamente definidas en nuestro Gemfile ni les prestaremos atención. Para las demás, tenemos que cuidar que el número de la izquierda no esté tan adelante con respecto al de la derecha, y de ser así, tendremos que añadir la gema mediante Github directamente a nuestro Gemfile (Ejemplo: Mongoid allá arriba)


NOTA: "github" en el Gemfile toma como argumento una string del tipo 'usuario/proyecto' para descargar una gema.

Para el resto de gemas en la lista (que estén explícitamente definidas en el Gemfile), debemos especificar la versión aproximada que buscamos (la del lado izquierdo en la lista) dentro del mismo Gemfile (Ejemplo: mongoid_search allá arriba), y esto se logra así (vean la línea 21 de la imagen de arriba y comparen con la que pondré aquí abajo):

 gem 'mongoid_search', '~> 0.3.2'

¿Notan la diferencia? en la de arriba sólo estamos diciendo "usa mongoid_search" y en la de abajo estamos diciendo "necesito una versión que esté aproximada (o sea esta) a la 0.3.2". Corremos bundle update de nuevo y si no hay error, proseguimos. Si hay error, añadimos la gema problemática al Gemfile directamente de Github como hicimos en el caso de arriba. Es importante mencionar que cada gema es un mundo y si este método no soluciona tu problema deberías revisar su issue tracker buscando coincidencias para "rails 4".

Prueba y error

A partir de aquí, el resto son cosas específicas de cada aplicación. Lo que queda por hacer es correr tu suite de tests y ver si pasan o no, y en caso de que no tengas configurada alguna, iniciar el servidor de desarrollo con rails s  y tratar de usar la aplicación. En mi caso (con la app que estoy usando de ejemplo), al iniciar el servidor obtengo un error de que no se puede cargar "active resource", lo que yo hice para solucionarlo fue remover la línea:

require "active_resource/railtie"
de mi application.rb. El siguiente error que tuve fue con la acomodación de unas rutas, pero eso fue mi culpa por código desorganizado, puse en orden mi routes.rb y listo. en el caso de Twitter bootstrap, tuve que añadir gemas como "therubyracer", "less-rails" y bajarme la gema de twitter-bootstrap-rails directo de su repo de github además de cambiar ciertas cosas en el css/less que genera... Aunque parecen "muchas cosas" todo es bastante sencillo y pues bueno, Google es tu amigo en este paso!

Strong Parameters

Luego de hacer todas estas actualizaciones y acomodos lograrás que tu aplicación corra, pero seguramente notes que no te deja interactuar con la base datos (realizar operaciones CRUD como crear un nuevo record, eliminarlo, editar existentes etc.) Esto es así porque a partir de Rails 4 ya no se declaran los atributos accesibles en el modelo, sino en el controlador con la ayuda de strong parameters.

Lo primero que hemos de hacer en este cambio es abrir todos nuestros modelos y quitar/comentar las declaraciones de attr_accessible que tengamos en cabecera según sea el caso en cada uno:


Luego tenemos que irnos a los diferentes controladores de los modelos de nuestra aplicación y poner un código como este justo debajo del último método definido:

 
private


  def set_object
    @object = Object.find(params[:id])
  end

  def object_params
    params.require(:object).permit(:param1, :param2, :param3, :param4)
  end

Luego en nuestra acción create/update del controlador cambiaremos la variable de instancia principal por algo como:

@object = Object.new(object_params)

Por si no queda claro, tienes que cambiar las ocurrencias de:

params[:object]
Por

object_params

En tu controlador... Eso ya "setearía" los atributos por nosotros usando "strong parameters". Este enfoque puede parecer algo "drástico" al principio, pero la verdad es que es bastante flexible y seguro en cuanto a cuestiones del Mass Assignment. Recuerden sólo setear los parámetros que han de ser especificados directamente al usar la aplicación (por medio de un form de registro o algo parecido) No tiene caso meter parámetros que nosotros (o la aplicación) seteamos automáticamente. Mi ejemplo en imagen por cualquier cosa:


Ya que estamos en el controlador, también podemos cambiar las ocurrencias de:

update_attributes
Por

update
y los :before_filter por :before_action

Luego tenemos que deshacernos de las DEPRECATION WARNINGS. Las de base tienen que ver con los entornos de Rails que usamos (archivos en config/enviroments), y para no hacer el cuento largo básicamente tenemos que deshacernos de los whiny_nils, configurar el eager_load, cambiar el compresor de assets a uglifier (esto en producción) y quitar algunas líneas de código sobrantes. Veamos entonces:

Development.rb, Antes y después:




Test.rb, Antes y después:


Production.rb, Antes y después:


Aprovechando que estamos en config: En Rails 4, el asset pipeline está activo por default, por lo tanto las líneas:


de nuestro config/application.rb deben ser removidas o bien, podemos setear la opción a false si no queremos usar el asset pipeline por laguna razón.

Luego en config/initializers/secret_token.rb vamos a copiar la línea del token y pegarla abajo, idéntica pero cambiando la palabra "token" por "key_base" y vamos a crear una nueva clave en consola (estando en el directorio de nuestra app) con el comando:

rake secret

y pegaremos esa nueva clave como el valor de la secret_key_base, el archivo queda más o menos así:



Rutas

Una vez que ya arreglamos esto, tenemos que cambiar unas cosas en el config/routes.rb: Primero: Si tienes llamadas a "match" tienes que cambiarlas por el método específico de la request que hacen: get, post etc. Si tienes llamadas a "put" debes cambiarlo por "patch".

Aunque ya cubrimos las partes más importantes quedan otras cosas y mi recomendación es que se hagan una nueva app Rails 4 y vean los cambios en cosas como los archivos del directorio config y subdirectorios para conocer las nuevas convenciones. Por otro lado la estructura del directorio de tests ha cambiado mucho y si están usando testing en su app es conveniente migrar al nuevo árbol de directorios.

Tips Extra

Otra manera de checar las diferencias entre la última versión de Rails 3 y el nuevo Rails 4 es checando RailsDiff, buena opción para entretenerse un rato.

La carpeta /vendor/plugins ya no se necesita ni se usa, así que si la están ocupando pueden pasar esos plugins a una carpeta de "lib" y hacerles initializers o bien, reemplazarlos por gemas (mejor solución).

Acá les dejo un video con las nuevas features de Rails 4 para que más o menos se vayan dando una idea de qué más cambiar:



¡A producción!

Finalmente hacemos un bundle install y todo debería de quedar bien, Si tenemos una test suite, hacemos los tests y después corremos la app para asegurarnos con un rails s y checamos que todo funcione como es esperado, notaremos que al arrancar, el servidor de desarrollo ya nos detectará Ruby 2.0 (y obviamente Rails 4) como versión predeterminada:


Hasta aquí todo está bien, ahora simplemente tenemos que configurar los cambios en nuestro servidor de producción. En mi caso personal yo uso Nginx + Passenger, entonces configuraría mi nuevo PATH de ruby 2.0.0/passenger en los roots de mi nginx.conf como se muestra a continuación:


Y luego añadiría el smart spawn y passenger on a mi servidor virtual de nginx donde está la aplicación con las opciones:


Reiniciamos nuestro servidor de producción (en este caso nginx) con sudo service nginx restart y listo, navegamos hacia nuestra app para ver que todo funcione y no haya errores. Si no me equivoco, aquí hemos terminado ya y todo funciona como se debe, usando las nuevas versiones de Ruby y Rails directamente.

P.D. Acá otro material de lectura extra directo de Rails Guides hablando del upgrade: http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html

Comparte este post - Twittear Ahora!