homeASCIIcasts

201: Bundler 

(view original Railscast)

Other translations: En Es Cn De Fr

Other formats:

Written by Andrea Salicetti

In questo episodio continueremo ad esplorare le novità di Rails 3. Questa volta daremo un’occhiata a bundler, il nuovo modo di gestire le dipendenze fra gem nelle applicazioni Rails.

Al momento Bundler viene aggiornato piuttosto frequentemente, per cui prima di farci qualsiasi cosa, è bene assicurarsi che si abbia l’ultima versione installata, lanciando:

gem install bundler

Si noti ancora una volta che non va usato sudo quando si installa un gem, perchè stiamo usando una versione di Ruby che abbiamo installato con rvm.

Usare Bundler per installare i gem

Nell’episodio precedente, quando abbiamo provata a far partire il server web per l’applicazione, ha protestato perchè mancava il gem sqlite3-ruby. Abbiamo ovviato al problema installando manualmente il gem con gem install:

gem install sqlite3-ruby

Si può ancor più elegantemente fare in modo che sia il bundler stesso ad installarlo per noi, essendo sqlite3-ruby una dipendenza per la nostra applicazione, per esempio. in quanto presente nella lista Gemfile dell’applicazione. Per fare tutto ciò, lanciamo bundle install che esaminerà le dipendenze dell’applicazione e verso altri gem e li installerà se mancano.

$ 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! Use ’bundle show [gemname]’ to see where a bundled gem is installed.

Possiamo vedere dall’output sopra che bundler ha installato tutti i gem di cui l’applicazione ha bisogno, incluso sqlite3-ruby. Se non fosse ancora stato installato, bundler lo scaricherebbe da Gemcutter e lo installerebbe.

Per cui, nel dubbio, lanciata bundle install. Dovreste farlo ogni volta che create una nuova applicazione Rails o clonate l’applicazione di qualcun altro, così sarete certi di avere installati i gem corretti. Dovrete lanciarlo anche quando farete il deploy di un’applicazione in modo tale che i gem corretti siano installati sul server. Potreste addirittura aggiungerlo al vostro script di deploy in modo tale che esegua sempre ad ogni deploy dell’applicazione.

una cosa importante da notare è che non si deve mai lanciare il comando bundle install preceduto da sudo (ossia come superuser), anche se di solito lo fate per installare i gem.

Aggiunta delle dipendenze da altri gem

Ora che sappiamo come gestire i nostri gem, come facciamo ad aggiungere una nuova dipendenza da un certo gem alla nostra applicazione? Nelle prime versioni di Rails gestivamo tutte le dipendenze nel file /config/environment.rb, invece in Rails 3 la configurazione delle dipendenze verso altri gem è appannaggio del file Gemfile presente nella cartella radice dell’applicazione. Il file Gemfile di default appare così:

# 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

Ci sono un paio di gem già inclusi nel file, fra cui rails e sqlite3-ruby. Nota che la linea che referenzia il gem rails include anche un numero di versione. Per ciascun riferimento a gem presente in questo file, si può specificare un numero di versione in modo tale da poter includere una specifica versione di un gem. Se ad un certo punto volessimo aggiornare la versione di Rails, potremmo cambiare la versione qui, anzichè nel file environment.rb.

Per usare ulteriori gem nella nostra applicazione, in futuro sarà sufficiente aggiungerli a questo file. Per esempio, se vogliamo usare la will_paginate, possiamo riferire il gem in questo modo:

gem "will_paginate", ">=2.3.12"

Il secondo parametro è un numero di versione ed usato qui significa che bundler dovrà installare una versione che sia almeno la 2.3.12 (o superiore, se la trova). Una volta che aabbiamo aggiunto un riferimento ad un nuovo gem in questo file, possiamo installare il gem lanciando bundle install, ma prima di farlo, vediamo un altro comando di bundler: bundle check.

Possiamo lanciare bundle check per avere una lista dei gem da cui la nostra applicazione dipende, ma che non sono installati. Se lanciamo questo comando per la nostra applicazione, ci mostrerà il gem will_paginate che abbiamo appena referenziato nel Gemfile, ma che non è stato ancora installato (perchè non abbiamo ancora lanciato bundle install).

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

Per vedere quali altri comandi bundle sono disponibili, lanciare 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

Altre opzioni del Gemfile

Torniamo al nostro Gemfile ora, per vedere cos’altro possiamo farci. Una nuova funzionalità carina è la possibilità di ottenere un gem da un repository git. Per esempio, Se volessimo restare sulla edge di Rails per usare sempre l’ultimissima versione nella nostra applicazione, potremmo puntare al gem Rails sul suo repository Git:

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

E’ una funzione potente. Se un gem non funziona esattamente come vorremmo, potremmo farne il fork del progetto su Github, modificare il codice per adattarlo alle nostre esigenze e infine far puntare la dipendenza del gem alla nostra versione.

Possiamo inoltre limitare le dipendenze verso determinati gem in base all’ambiente impostato. Se volgiamo usare RSpec per il test, possiamo passare alla dipendenza di quel gem l’opzione :group ad indicare che la dipendenza deve limitarsi al solo ambiente di test:

gem "rspec", :group => :test

In alternativa, se i gem da confinare ad un uso in esclusivo su un determinato ambiente sono più d’uno, potremmo usare group e passare un blocco, in questo modo:

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

Se lanciamo bundle install ora, installerà tutti i gem che abbiamo specificato per ogni ambiente:

$ 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!

Come si vede dall’output (troncato) di sopra, i due gem specificati nel nostro Gemfile sono stati installati.

Se vogliamo installare i gem, ma escludendo quelli di un determinato ambiente, possiamo usare l’opzione --without. Per installare i gem che non sono nel gruppo test, lanceremmo:

bundle install --without=test

Tutto ciò è utile quando si sta installando l’applicazione sul server di produzione e non si vuole installare i gem utili al solo ambiente di test.

Bloccare i Gem

Un altro utile comando è il bundle lock. Questo comando sigilla l’insieme di tutte le versioni specifiche dei gem che l’aplicazione sta usando. Una volta lanciato sul nostro progetto, in esso comparirà un nuovo file chiamato Gemfile.lock. Questo file elenca tutti i gem che sono installati per la nostra applicazione unitamente alla specifica versione usata. Dopo che è stato lanciato il bundle lock, ai successivi lanci del comando bundle install verranno installate solo le versioni specifiche elencate le file Gemfile.lock, anche se ne dovessero esistere di più nuove e compatibili con le specifiche di dipendenza del Gemfile.

Vi potreste domandare in quali occasioni torna utile il bundle lock. Ebbene, ha senso usarlo ogniqualvolta un progetto viene utilizzato in un contesto di team. Se stiamo lavorando con altri sviluppatori Rails su di un progetto, possiamo usare il bundle lock per essere sicuri che ognuno usi la stessa versione dei gem che l’applicazione sta usando da noi in locale. Lo stesso dicasi per i casi in cui l’applicazione venga deployata in produzione. Dal momento che l’applicazione sarà deployata su più server diversi fra loro, vogliamo garantirci che, tanto nella nostra macchina di sviluppo, quanto in quella di produzione, sia presente la stessa identica versione.

Se, a seguito di un bundle lock, si presenta la necessità di effettuare modifiche ai gem usati dall’applicazione, non occorre cambiare direttamente il Gemfile.lock. Basta aggiornare il Gemfile come fatto in precedenza. Una volta fatte le modifiche al Gemfile, tuttavia, il lancio del comando bundle install non aggiornaerebbe i gem dell’applicazione, in quanto bloccati. Per aggiornare i gem anche a seguito di un bundle lock, è sufficiente lanciare il comando bundle install con l’opzione --relock.

bundle install --relock

Questo comando rimuove il lock sul gemfile, aggiorna i gem e rimette il lock.

Impacchettare i gem insieme all’applicazione

Quanto detto copre il tipico workflow bundler ed è sufficiente per conoscere quanto c’è da sapere per lavoraci. Un’ultima funzionalità di bundler che vediamo è il comando bundle pack.

Bundler installa i gem sotto la cartella .bundle nella propria cartella home.

$ ls ~/.bundle
cache		doc		gems		ruby		specifications

Questo significa che i gem non sono accoppiati strettamente alla nostra applicazione, e non vengono inclusi nel sistema di controllo versioni del nostro progetto. Se volessimo salvare anche i gem dentro l’apllicazione, anzichè mantenerli semplicemente come riferimenti da risolvere, potremmo lanciare 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

Questo comando genera i file .gem delle applicazioni di cui ha bisogno la nostra applicazione e li copia nella cartella /vendor/cache della nostra applicazione, eventualmente creando la cartella se non dovesse ancora esistere. I gem da questo momento verranno installati direttamente da questi file .gem, anzichè essere scaricati da remoto, per esempio da Gemcutter.

Non è una funzionalità di cui avrete bisogno tanto spesso, ma se vi doveste trovare in una situazione in cui non si volesse o potesse permettere ad un server di produzione di connettersi a Gemcutter per scaricare i gem da là, questo è un buon workaround.

Questo è quanto per questo episodio. Spero di avervi fornito una buona panoramica sull’uso di bundler nella gestione dei gem della vostra applicazione. Ci vuole forse un po’ di tempo per imparare ad usare Bundler, ma alla lunga lo sforzo paga ed evita di incappare in alcuni dei problemi che tipicamente si potevano avere in passato con le dipendenze dei gem.

Concludo incoraggiandovi a visitare railsplugins.org. Vi starete probabilmente domandando quali gem e plugins funzionino con Rails 3: questo è il sito dove potrete scoprirlo. Nel sito potrete consultare i plugin e vedere se funzionano con Rails 3 e Ruby 1.9.

Il sito RailsPlugins.

Se un gem che intendete usare con la vostra applicazione Rails 3 è presente in lista e segnalato come non funzionante o non testato, allora potrebbe essere una grande occasione per aprire una segnalazione o per aiutare a risolvere il problema. In questo modo potete aiutare la comunità a far andare il massimo numero di plugin e gem possibili con Rails 3.