#201 Bundler
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.
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.