homeASCIIcasts

201: Bundler 

(view original Railscast)

Other translations: En Cn De It Fr

Other formats:

Written by Juan Lupión

En este episodio continuaremos nuestro repaso de las nuevas funcionalidades de Rails 3, esta vez veremos Bundler que es la nueva manera de gestionar dependencias de gemas en las aplicaciones Rails.

A día de hoy las actualizaciones de Bundler se suceden con frecuencia, así que antes de hacer nada con él merece la pena asegurarnos de que tenemos la última versión ejecutando

gem install bundler

Obsérvese que no estamos usando sudo para instalar la gema porque tenemos una versión de Ruby que hemos instalado con rvm.

Uso de Bundler para instalar gemas

En el episodio anterior cuando intentábamos arrancar el servidor web de nuestra aplicación ésta se quejaba de que le faltaba la gema sqlite3-ruby. Resolvíamos el problema instalando manualmente la gema con gem install.

gem install sqlite3-ruby

En lugar de esto podríamos haber utilizado Bundler porque sqlite3-ruby aparece en el listado del Gemfile de la aplicación y por tanto esta gema es considerada como una dependencia. Para instalarla usando Bundler podemos ejecutar bundle install, lo que recorrerá todas las dependencias de gemas de la aplicación y las instalará.

$ bundle install
Fetching source index from http://gemcutter.org
Resolving dependencies
Installing abstract (1.0.0) from system gems 
Installing actionmailer (3.0.0.beta) from system gems 
Installing actionpack (3.0.0.beta) from system gems 
Installing activemodel (3.0.0.beta) from system gems 
Installing activerecord (3.0.0.beta) from system gems 
Installing activeresource (3.0.0.beta) from system gems 
Installing activesupport (3.0.0.beta) from system gems 
Installing arel (0.2.1) from rubygems repository at http://gemcutter.org 
Installing builder (2.1.2) from system gems 
Installing bundler (0.9.5) from system gems 
Installing erubis (2.6.5) from system gems 
Installing i18n (0.3.3) from system gems 
Installing mail (2.1.2) from system gems 
Installing memcache-client (1.7.8) from system gems 
Installing mime-types (1.16) from system gems 
Installing rack (1.1.0) from system gems 
Installing rack-mount (0.4.7) from rubygems repository at http://gemcutter.org 
Installing rack-test (0.5.3) from system gems 
Installing rails (3.0.0.beta) from system gems 
Installing railties (3.0.0.beta) from system gems 
Installing rake (0.8.7) from system gems 
Installing sqlite3-ruby (1.2.5) from system gems 
Installing text-format (1.0.0) from system gems 
Installing text-hyphen (1.0.0) from system gems 
Installing thor (0.13.1) from rubygems repository at http://gemcutter.org 
Installing tzinfo (0.3.16) from system gems 
Your bundle is complete!

Podemos ver por la salida del comando anterior que Bundler ha instalado todas las gemas necesarias para el funcionamiento de la aplicación, incluyendo sqlite3-ruby. De haber sido necesario, Bundler la hubiera descargado de Gemcutter y la hubiera instalado.

Así, ante cualquier duda, debemos ejecutar bundle install. Deberíamos hacerlo cada vez que creemos una nueva aplicación para asegurarnos de que tenemos las gemas correctas instaladas, y también deberíamos hacerlo cada vez que desplegamos una aplicación para que se instalen las gemas correctas en el servidor, añadiéndolo a la receta de despliegue.

Es importante tener cuenta que jamás deberíamos ejecutar sudo para ejecutar bundle install aún cuando habitualmente utilicemos sudo para instalar las gemas.

Dependencias entre gemas

Ahora que sabemos cómo gestionar nuestras gemas, ¿cómo instalamos y añadimos nuevas dependencias en nuestra aplicación? En las versiones anteriores de Rails lo hacíamos editando el fichero /config/environment.rb pero en Rails 3 la configuración de gemas se hace en un fichero en el directorio raíz de nuestra aplicación llamado Gemfile. Este fichero por defecto tiene el siguiente aspecto:

# Edit this Gemfile to bundle your application's dependencies.
source 'http://gemcutter.org'


gem "rails", "3.0.0.beta"

## Bundle edge rails:
# gem "rails", :git => "git://github.com/rails/rails.git"

# ActiveRecord requires a database adapter. By default,
# Rails has selected sqlite3.
gem "sqlite3-ruby", :require => "sqlite3"

## Bundle the gems you use:
# gem "bj"
# gem "hpricot", "0.6"
# gem "sqlite3-ruby", :require => "sqlite3"
# gem "aws-s3", :require => "aws/s3"

## Bundle gems used only in certain environments:
# gem "rspec", :group => :test
# group :test do
#   gem "webrat"
# end

En este fichero ya figuran un par de gemas, incluyendo rails y sqlite3-ruby. Nótese que la linea que hacerreferencia a la gema rails trae un número de versión: además de hacer referencia a la gema podemos señalar un número de versión concreta. Si queremos actualizar la versión de Rails en algún momento podemos cabiar la versión aquí en lugar de hacerlo en el fichero environment.rb.

Si queremos utilizar gemas extra en nuestra aplicación tan sólo tenemos que añadirlas en este archivo. Por ejemplo, si queremos utilizar will_paginate podemos escribir la siguiente referencia:

gem "will_paginate", ">=2.3.12"

El segundo parámetro es el número de versión, lo que significa que Bundler instalará la versión 2.3.12 (o una versión posterior si es que la encuentra) Una vez añadida la referencia a esta gema podemos instalarla con bundle install, pero antes de hacerlo veremos otro comando: bundle check.

Podemos ejecutar bundle check para enumerar las gemas de las que depende nuestra aplicación pero que no están instaladas. En nuestro caso veremos que la gema will_paginate que hemos añadido no está aún instalada.

$ bundle check
The following dependencies are missing
  * will_paginate (>= 2.3.12, runtime)We can install the missing gems by running bundle install again. 

Para ver qué otros comandos están disponibles podemos ejecutar bundle help.

$ bundle help
Tasks:
  bundle check        # Checks if the dependencies listed in Gemfile are satisfied by currently installed gems
  bundle exec         # Run the command in context of the bundle
  bundle help [TASK]  # Describe available tasks or one specific task
  bundle init         # Generates a Gemfile into the current working directory
  bundle install      # Install the current environment to the system
  bundle lock         # Locks the bundle to the current set of dependencies, including all child dependencies.
  bundle pack         # Packs all the gems to vendor/cache
  bundle show         # Shows all gems that are part of the bundle.
  bundle unlock       # Unlock the bundle. This allows gem versions to be changed

Otras opciones de Gemfile

Volvamos a nuestro Gemfile para ver qué más podemos hacer con él. Una nueva funcionalidad interesante es la posibilidad de obtener una gema de un repositorio git. Por ejemplo, si queremos vivir al límite y utilizar la última versión de Rails en nuestra aplicación podemos apuntar la gema Rails a su repositorio Git.

# Bundle edge rails:
gem "rails", :git => "git://github.com/rails/rails.git"

Esta funcionalidad es bastante poderosa. Si una gema no funciona exactamente como queremos podemos desdoblar el proyecto en Github, modificar el código a nuestro gusto y luego anotar que la gema depende de nuestra versión.

También podemos hacer que ciertas gemas queden restringidas a un entorno determinado. Si queremos utilizar RSpec para hacer pruebas podemos pasar la opción :group para ceñir su uso al entorno de test.

gem "rspec", :group => :test

Alternativamente, podemos pasar un bloque si queremos aplicar esta restricción a todo un conjunto de gemas.

group :test do
  gem "webrat"
  get "rspec"
end

Si ejecutamos ahora bundle install se instalarán todas las gemas que hemos especificado para cada entorno.

$ bundle install
Fetching source index from http://gemcutter.org
Resolving dependencies
...
Installing rspec (1.3.0) from rubygems repository at http://gemcutter.org 
...
Installing webrat (0.7.0) from rubygems repository at http://gemcutter.org 
Installing will_paginate (2.3.12) from system gems 
Your bundle is complete!

Se han instalado las dos nuevas gemas que hemos especificado en nuestro Gemfile, como puede verse por la salida anterior (que hemos truncado).

Si queremos instalar gemas pero evitar aquellas que pertenecen a cierto entorno, podemos utilizar la opción --without. Por ejemplo podemos ejecutar lo siguiente para instalar las gemas que no pertenecen al entorno de test:

bundle install --without=test

Esto puede sernos útil cuando instalemos la aplicación en nuestro servidor de producción y no queramos instalar las gemas que sólo se utilizan en el entorno de pruebas.

Cierre de gemas

Otro comando útil es bundle lock. Esto cierra la versión específica de las gemas que utiliza nuestra aplicación, y tras ejecutarlo nuestro proyecto tendrá un nuevo archivo llamado Gemfile.lock. En este archivo aparecen todas las gemas instaladas para nuestra aplicación junto con la versión específica que utilizan. Después de ejecutar bundle lock cuando volvamos a ejecutar bundle install sólo se instalarán las versiones específicas que aparecen en el archivo Gemfile.lock, incluso aunque existan versiones más recientes.

Cabría preguntarse por qué querríamos utilizar bundle lock. Merece la pena su uso cuando un proyecto se utilice desde diferentes lugares, por ejemplo: si estamos trabajando con otros desarrolladores Rails en un proyecto podemos utilizar este cierre de gemas para que todos usen las mismas versiones de las gemas. Lo mismo puede aplicarse cuando despleguemos la aplicación en producción. Como la aplicación pasará a ejecutarse en un servidor diferente nos interesa asegurarnos que en la máquina de producción se instalan las mismas versiones exactas que en nuestra máquina de desarrollo.

Si necesitásemos efectuar cambios sobre las gemas de una aplicación después de la ejecución de bundle lock no deberíamos cambiar el fichero Gemfile.lock sino que actualizaremos el fichero Gemfile igual que antes. Sin embargo, una vez que hayamos hecho estos cambios no podremos actualizar las gemas con bundle install porque el Gemfile está bloqueado. Para hacerlo tenemos que pasar la opcón --relock.

bundle install --relock

Esto desbloqueará el Gemfile, actualizará las gemas y luego lo volverá a cerrar.

Empaquetado de Gemas de una aplicación.

Con esto hemos cubierto prácticamente el flujo de trabajo normal de Bundler pero aún nos queda espacio para ver una última funcionalidad, que es el comando bundle pack.

Bundler instala las gemas en un fichero .bundle en el directorio de nuestra cuenta de usuario.

$ ls ~/.bundle
cache		doc		gems		ruby		specifications

Esto quiere decir que las gemas no se distribuyen con nuestra aplicación y por tanto no se incluyen en el sistema de control de versiones de nuestro proyecto. Si queremos almacenar las gemas dentro de nuestra aplicación pdoemos ejecutar bundle pack.

bundle pack
Copying .gem files into vendor/cache
  * memcache-client-1.7.8.gem
  * rspec-1.3.0.gem
  * thor-0.13.3.gem
  * tzinfo-0.3.16.gem
  * builder-2.1.2.gem
  * nokogiri-1.4.1.gem
  * mime-types-1.16.gem
  * sqlite3-ruby-1.2.5.gem
  * i18n-0.3.3.gem
  * abstract-1.0.0.gem
  * erubis-2.6.5.gem
  * text-hyphen-1.0.0.gem
  * bundler-0.9.6.gem
  * rake-0.8.7.gem
  * will_paginate-2.3.12.gem
  * text-format-1.0.0.gem
  * rack-1.1.0.gem
  * rack-test-0.5.3.gem
  * webrat-0.7.0.gem
  * rack-mount-0.4.7.gem
  * activesupport-3.0.0.beta.gem
  * mail-2.1.2.gem
  * arel-0.2.1.gem
  * activemodel-3.0.0.beta.gem
  * actionpack-3.0.0.beta.gem
  * actionmailer-3.0.0.beta.gem
  * activerecord-3.0.0.beta.gem
  * activeresource-3.0.0.beta.gem
  * railties-3.0.0.beta.gem
  * rails-3.0.0.beta.gem

Esto generará los fichero .gem necesarios para nuestra aplicación y los copiará al directorio /vendor/cache créandolo si es necesario. Las gemas ahora se instalarán desde este directorio en lugar de hacerlo remotamente desde, por ejemplo, Gemcutter.

Esto no debería ser necesario tener que hacerlo con mucha frecuencia, pero podría sernos útil si no queremos o no podemos hacer que nuestro servidor de producción se conecte a Gemcutter para descargar las gemas.

Eso es todo por este episodio, esperamos haber dado una buena visión general del uso de Bundler para gestionar las gemas en nuestra aplicación. Puede que tardemos un poco en acostumbrarnos a usar Bundler pero a largo plazo nos ahorrará muchos de los problemas que hemos podido sufrir en el pasado debido a las dependencias de gemas.

Terminamos con la recomendación de visitar railsplugins.org. Probablemente nos preguntemos qué gemas y plugins funcionan con Rails 3 y este es el lugar al que acudir para averiguarlo. En este sitio podremos hojear los plugins y ver si funcionan con Rails 3 y Ruby 1.9.

El sitio web de RailsPlugins.

Tenemos una oportunidad única para echar una mano si vemos que alguna gema que queramos utilizar con Rails 3 aparece como que no funciona o no está probada, de esta manera podremos conseguir que el máximo número de gemas y plugins funcionen bajo Rails 3.