#201 Bundler
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 <<<<<<< HEAD <font color="lime">Your bundle is complete! Use ’bundle show [gemname]’ to see where a bundled gem is installed.</font> ======= <span style="color: #0F0;">Your bundle is complete! Use ’bundle show [gemname]’ to see where a bundled gem is installed.</span> >>>>>>> b97aa6197477c8dd0eb373f63b133400a451886f
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.
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.