#225 Upgrading to Rails 3 Part 1
Ya ha sido publicada la versión candidata de Rails 3.0 por lo que es este un buen momento para probar Rails 3 (si es que aún no lo hemos hecho) y ver qué cambios tendremos que hacer para que funcionen nuestras aplicaciones basadas en Rails 2. En este episodio veremos cómo actualizar una aplicación Rails 2.3 a Rails 3.0.
Merece la pena intentar actualizar nuestras aplicaciones Rails 2 aún cuando no estemos listos para dar el paso a Rails 3 en producción porque así podremos ver con qué problemas tendríamos que enfrentarnos. Si utilizamos Git como control de versiones lo más fácil es crear una rama separada sobre la que experimentar, así que una vez tengamos una copia del repositorio crearemos una nueva rama llamada rails3
.
$ git checkout -b rails3 Switched to a new branch 'rails3'
Preliminares
Antes de actualizar una aplicación a Rails 3 tendremos que actualizarnos a la última versión de Rails 2.3, que a fecha de hoy es Rails 2.3.8. También deberíamos actualizar todas las gemas que usemos en la aplicación para asegurarnos de que no habrá problemas de compatibilidad con las últimas versiones.
Una vez que hayamos actualizado Rails y gemas deberíamos ejecutar nuestros tests para ver que no se ha roto nada (esta es una de las ventajas de tener una buena suite automática de pruebas)
$ rake spec (in /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts) DEPRECATION WARNING: Rake tasks in vendor/plugins/hoptoad_notifier/tasks ........................................................................................................................................................ 152 examples, 0 failures
Como todos los tests pasan estamos listos para empezar la actualización.
Actualización de Ruby e instalación de Rails 3
Para la actualización a Rails 3 queremos usar la última versión de Ruby y empezar desde cero con nuestro entorno de Rails 3, por lo que usaremos rvm para obtener y compilar la última versión de Ruby 1.9.2 e instalar Rails en ella. Dada la frecuencia con que se actualiza rvm nos aseguraremos de ejecutar rvm update antes de instalar rvm.
$ rvm update rvm 0.1.40 by Wayne E. Seguin (wayneeseguin@gmail.com) [http://rvm.beginrescueend.com/] info: fetching rvm-0.1.43.tar.gz info: Extracting rvm-0.1.43.tar.gz ... info: Installing rvm-0.1.43...
Una vez que rvm se ha actualizado a sí mismo tenemos que recargarlo. Podemos hacerlo ejecutando
$ rvm reload
Ya podemos instalar Ruby 1.9.2 con rvm
$ rvm install 1.9.2
Este comando descargará, compilará e instalará la última versión de Ruby 1.9.2. Cuando el proceso se haya completado (suele tardar unos minutos) podemos pasar a usar esta versión y comprobar que funciona.
$ rvm 1.9.2 $ ruby -v ruby 1.9.2dev (2010-07-11 revision 28618) [x86_64-darwin10.4.0]
Una vez que estamos ejecutando la versión correcta de Ruby ya podremos instalar la versión 3.0 de Rails.
gem install rails --pre
Obsérvese que como estamos usando una versión de Ruby instalada desde rvm no tenemos que utilizar el comando
sudo
para la instalación de las gemas.
Comienza la actualización
Con esto tenemos una instalación nueva de Ruby y Rails 3.0, así que estamos listos para empezar la actualización de la aplicación. Existe un plugin oficial para este fin llamado rails_upgrade que se puede usar para ver qué cambios tenemos que hacer, aunque para instalarlo primero tendremos que volver a nuestro entorno de Rails 2. En nuestro caso está instalado en nuestra versión del sistema de Ruby, por lo que volveremos a ella temporalmente ejecutando
$ rvm system
Ya podemos instalar el plugin:
$ script/plugin install git://github.com/rails/rails_upgrade.git
Tras la instalación veremos información de ayuda. Uno de los comandos que se añadírán es rake rails:upgrade:check
. Si lo ejecutamos desde el directorio de la aplicación veremos una lista de todo lo que tenemos que actualizar en ella. La salida es demasiado extensa como para mostrarla aquí, pero veremos la primera parte a modo de ejemplo.
$ rake rails:upgrade:check (in /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts) Old router API The router API has totally changed. More information: http://yehudakatz.com/2009/12/26/the-rails-3-router-rack-it-up/ The culprits: - config/routes.rb
Antes de comenzar con la actualización ejecutaremos rake rails:upgrade:backup
. Esto hace una copia de seguridad de algunos archivos clave que probablemente serán sobreescritos durante el proceso que vendrá después:
$ rake rails:upgrade:backup (in /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts) DEPRECATION WARNING: Rake tasks in vendor/plugins/hoptoad_notifier/tasks are deprecated. Use lib/tasks instead. (called from /Library/Ruby/Gems/1.8/gems/rails-2.3.8/lib/tasks/rails.rb:10) * backing up .gitignore to .gitignore.rails2 * backing up app/controllers/application_controller.rb to app/controllers/application_controller.rb.rails2 * backing up app/helpers/application_helper.rb to app/helpers/application_helper.rb.rails2 * backing up config/routes.rb to config/routes.rb.rails2 * backing up config/environment.rb to config/environment.rb.rails2 * backing up config/environments/development.rb to config/environments/development.rb.rails2 * backing up config/environments/production.rb to config/environments/production.rb.rails2 * backing up config/database.yml to config/database.yml.rails2 This is a list of the files analyzed and backed up (if they existed); you will probably not want the generator to replace them since you probably modified them (but now they're safe if you accidentally do!). - .gitignore - app/controllers/application_controller.rb - app/helpers/application_helper.rb - config/routes.rb - config/environment.rb - config/environments/development.rb - config/environments/production.rb - config/environments/staging.rb - config/database.yml - config.ru - doc/README_FOR_APP - test/test_helper.rb
La actualización propiamente dicha empieza por la creación de una nueva aplicación Rails 3 en el mismo directorio de la aplicación anterior, para lo que tenemos que volver a nuestro entorno de Ruby 1.9.2
$ rvm 1.9.2
Ya podemos crear la nueva aplicación Rails 3 con
$ rails new .
Como estamos creando una nueva aplicación sobre una ya existente se nos preguntará si queremos sobreescribir algunos de los archivos. Podemos usar la opción d
para ver las diferencias entre el archivo existente y el que será sobreescrito.
Como ya hemos hecho una copia de seguridad de los archivos más importantes podemos escoger sobreescribir todos los archivos a excepción de los siguientes:
/README
/app/views/layouts/application.html.erb
/public/javascripts/application.js
El resto de archivos deberían mantener los contenidos por defecto o tendremos copia de seguridad por lo que luego podremos volver a integrar los cambios.
La aplicación ha sido actualizada a Rails 3.0 pero todavía tenemos que repasar todos los archivos de los que hicimos copia de seguridad y añadir el código específico de nuestra aplicación. La copia de seguridad de estos archivos estará en el directorio original pero con extensión .rails2
por lo que tan sólo tenemos que abrir la copia y el original y hacer los cambios.
Empezaremos con el fichero de rutas. La sintaxis de las rutas ha cambiado bastante, pero las rutas antiguas siguen funcionando en Rails 3.0 (aunque son a extinguir) Por ahora nos bastará con pegar las rutas del fichero antiguo y lo probaremos más adelante.
A continuación le echaremos un vistazo al fichero /config/environment.rb
, al que habremos de dedicar más tiempo porque la sintaxis de configuración ha sufrido bastantes cambios en Rails 3. El fichero de entorno por defecto en Rails 3 es bastante escueto porque las opciones de configuración ya no se escriben en él sino que van al fichero /config/application.rb
.
Hay cuatro secciones de la versión antigua del fichero environment.rb
que hemos de considerar. La primera es el código de configuración de la zona horaria.
config.time_zone = 'Pacific Time (US & Canada)'
Esta linea la podemos llevar a /config/application.rb
.
La segunda parte es el código de configuración de las sesiones:
config.action_controller.session = { :session_key => APP_CONFIG['session_key'], :secret => APP_CONFIG['session_secret'] }
En Rails 3 hay un fichero de inicialización específico para esto, si lo abrimos veremos que ya tenemos el código para configurar los datos de sesión:
# Be sure to restart your server when you modify this file. Railscasts::Application.config.session_store :cookie_store, :key => '_railscasts_session' # Use the database for sessions instead of the cookie-based default, # which shouldn't be used to store highly confidential information # (create the session table with "rake db:sessions:create") # Railscasts::Application.config.session_store :active_record_store
De momento dejaremos este código tal cual. Quizá nos haga falta cambiarlo más adelante pero no será imprescindible para echar a andar nuestra aplicación.
Por último al final del original de environment.rb
aparece el código de configuración de las gemas:
config.gem "RedCloth", :lib => 'redcloth', :version => ">= 4.0" config.gem "coderay" config.gem 'acts-as-list' config.gem 'will_paginate' config.gem 'thinking-sphinx', :lib => 'thinking_sphinx' config.gem 'whenever', :lib => false
Este código se puede copiar en el fichero Gemfile
de nuestra aplicación, que reside en el directorio raíz de la misma. Tenemos que hacer algunos ajustes porque la sintaxis nueva es ligeramente distinta:
gem "RedCloth", ">=4.0", :require => 'redcloth' gem "coderay" gem 'acts-as-list' gem 'will_paginate' gem 'thinking-sphinx', :require => 'thinking_sphinx' gem 'whenever', :require => false
Además de cambiar config.gem
por gem
tenemos que hacer un par de cambios más. Los números de versión se pasan ahora como el segundo argumento y la opción :lib
ahora se llama :require
.
Una vez hecho esto, tenemos que ejecutar bundle install
para estar seguros de que se instalan todas las gemas necesarias. En el episodio 201 (verlo, leerlo) se describe con más detalle el funcionamiento de Bundler y la instalación de gemas. No veremos cómo traspasar el código dese los otros archivos copiados pero debería ser bastante fácil añadir la configuraciones restantes.
Una vez que tengamos las gemas instaladas podemos ver si la aplicación logra arrancar
$ rails s /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/will_paginate-2.3.14/lib/will_paginate.rb:39:in `enable_activerecord': undefined method `returning' for WillPaginate:Module (NoMethodError) # rest of error truncated…
No lo hace, y el motivo es que hay un error en el código de la gema will_paginate
que se encuentra con que el método returning
no existe. Este método estaba disponible en Rails 2 pero en Rails 3 no.
En casos como este merece la pena visitar RubyGems para ver si hay una nueva versión de la gema disponible que sea compatible con Rails 3.0. Este es el caso de will_paginate
, y podemos actualizarnos añadiendo un número de versión para la gema en nuestro Gemfile
.
gem 'will_paginate', '>=3.0.pre'
Si ejecutamos bundle install
de nuevo, se instalará la versión de prelanzamiento de la gema y podremos usarla con nuestra aplicación. Con esta versión más reciente de la gema podemos intentar ejecutar el servidor otra vez.
$ rails s /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/after_commit-1.0.7/lib/after_commit/active_record.rb:15:in `include_after_commit_extensions': undefined method `subclasses_of' for Object:Class (NoMethodError) from /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/after_commit-1.0.7/lib/after_commit.rb:81 from /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/thinking-sphinx-1.3.18/lib/thinking_sphinx.rb:2:in `require' from /Users/eifion/.rvm/gems/ruby-1.8.7-p299/gems/thinking-sphinx-1.3.18/lib/thinking_sphinx.rb:2
Parece que lo que provoca este error es la gema de Thinking Sphinx. En este caso también hay una versión disponible que podemos utilizar, por lo que volvemos a cambiar el Gemfile
y luego ejecutaremos bundle install
otra vez.
gem 'thinking-sphinx', '>=2.0.0.rc1', :require => 'thinking_sphinx'
Intentemos lanzar otra vez el servidor.
$ rails s DEPRECATION WARNING: railtie_name is deprecated and has no effect. (called from <class:Railtie> at /Users/eifion/.rvm/gems/ruby-1.9.2-rc2/gems/will_paginate-3.0.pre/lib/will_paginate/railtie.rb:6) => Booting WEBrick => Rails 3.0.0.rc application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server Exiting /Users/eifion/rails/apps_for_asciicasts/ep225/railscasts/config/routes.rb:2:in `block in <top (required)>': undefined local variable or method `map' for #<ActionDispatch::Routing::Mapper:0x00000101dffde8> (NameError)
Esta vez hemos llegado un poco más lejos. El servidor intenta arrancar pero luego se queja de que no existe una variable o método llamado map en las rutas. Dado que hemos pegado el antiguo código de configuración de las rutas estamos llamando a la variable map
que ya no es necesaria en Rails 3 por lo que no se le pasa al bloque. Podemos seguir usando la sintaxis antigua añadiendola como parámetro del bloque.
Railscasts::Application.routes.draw do |map| map.resources :spam_questions map.resources :spam_checks map.with_options :controller => 'info' do |info| info.about 'about', :action => 'about' info.contest 'contest', :action => 'contest' info.feeds 'feeds', :action => 'feeds' info.give_back 'give_back', :action => 'give_back' end map.login 'login', :controller => 'sessions', :action => 'new' map.logout 'logout', :controller => 'sessions', :action => 'destroy' map.resources :sponsors map.resources :comments map.resources :tags map.resources :episodes, :collection => { :archive => :get } map.resources :sessions map.resources :spam_reports, :member => { :confirm => :post }, :collection => { :confirm => :post } map.root :episodes end
A la cuarta ha sido la vencida y cuando intentamos ejecutar el servidor logra hacerlo sin dar errores.
$ rails s DEPRECATION WARNING: railtie_name is deprecated and has no effect. (called from <class:Railtie> at /Users/eifion/.rvm/gems/ruby-1.9.2-rc2/gems/will_paginate-3.0.pre/lib/will_paginate/railtie.rb:6) => Booting WEBrick => Rails 3.0.0.rc application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2010-08-05 19:30:22] INFO WEBrick 1.3.1 [2010-08-05 19:30:22] INFO ruby 1.9.2 (2010-07-11) [x86_64-darwin10.4.0] [2010-08-05 19:30:22] INFO WEBrick::HTTPServer#start: pid=9933 port=3000
Si ahora cargamos la portada en el navegador, veremos la portada por defecto de Rails en lugar de la página principal de nuestra aplicación porque habrá sido añadida en /public
cuando creamos la aplicación con Rails 3. Podemos ver en la página que la aplicación está usando el entorno correcto
Aún nos queda mucho por hacer aunque la aplicación logre arrancar correctamente, como podemos comprobar si volvemos ejecutar rails:upgrade:check
otra vez. También tenemos que volver a correr nuestros tests para comprobar que siguen pasando. Todo esto lo veremos en el siguiente episodio.