#245 New Gem with Bundler
- Download:
- source codeProject Files in Zip (3.62 KB)
- mp4Full Size H.264 Video (20.1 MB)
- m4vSmaller H.264 Video (9.2 MB)
- webmFull Size VP8 Video (22.5 MB)
- ogvFull Size Theora Video (16.8 MB)
La forma de crear y gestionar gemas en Ruby ha ido evolucionando a lo largo de los años
. En el episodio 135 [verlo, leerlo] usábamos la gema echoe
para construir una nueva gema; un año después en el episodio 183 [verlo, leerlo] usábamos Jeweler. Ambas herramientas nos permiten generar y gestionar los ficheros Gemspec necesarios para la publicación de cada gema. Esta vez vamos a gestionar el fichero Gemspec manualmente empleando un método que finalmente resulta mucho más sencillo.
Con este método tenemos que crear el fichero Gemspec inicial para cada una de nuestras gemas, cosa que podemos hacer manualmente o con una herramienta como Bundler. Por lo general Bundler se considera como una herramienta para gestionar las dependencias de una aplicación pero contiene una orden muy poco conocida para crear gemas que funciona muy bien. Dicha orden es bundle gem
, y se le pasa el nombre de la gema que queremos generar. Como ejemplo en este episodio vamos a crear una gema muy sencilla llamada lorem que simplemente genera un fragmento de texto “Lorem ipsum”.
$ bundle gem lorem create lorem/Gemfile create lorem/Rakefile create lorem/.gitignore create lorem/lorem.gemspec create lorem/lib/lorem.rb create lorem/lib/lorem/version.rb Initializating git repo in /Users/eifion/code/ep245/lorem
La orden bunlde gem
crea un nuevo directorio y genera ciertos archivos. También inicializa un repostorio git (lo que asume que estamos usando git, en breve explicaremos por qué). Veamos algunos de estos archivos, empezando por lorem.gemspec
.
# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "lorem/version" Gem::Specification.new do |s| s.name = "lorem" s.version = Lorem::VERSION s.platform = Gem::Platform::RUBY s.authors = ["TODO: Write your name"] s.email = ["TODO: Write your email address"] s.homepage = "" s.summary = %q{TODO: Write a gem summary} s.description = %q{TODO: Write a gem description} s.rubyforge_project = "lorem" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] end
Como puede verse, este fichero en realidad es una plantilla plagada de elementos TODO que deben ser editados directamente. Una vez que lo hayamos hecho ya tendremos nuestro Gemspec listo. Una sección interesante de este archivo son los atributos referentes a los archivos que aparecen al final que son calculados en tiempo de ejecución con el comando git ls-files
que determina qué archivos serán incluidos en la gema, y este es el motivo por el que bundle gem
crea un repositorio git. Lo bueno de esto es que adquiere automáticamente el comportamiento de .gitignore
y por tanto nos permite excluir los archivos temporales y otros que no queramos en la gema. Los valores anteriores por defecto servirán para casi cuaquier gema pero siempre los podemos cambiar.
Otra cosa a tener en cuenta es cómo se determina el número de versión. Se define como una constante llamada Lorem::VERSION
, cuyo valor se define en otro archivo llamado version.rb
en el directorio lib/lorem
. Lo único que se hace en él es definir el número de versión.
module Lorem VERSION = "0.0.1" end
Cuando vayamos a actualizar la gema a una nueva versión tan sólo tenemos que cambiar el número de versión aquí y volver a publicar la gema.
El otro fichero que se ha generado en el directorio lib
se llama lorem.rb
y es el primer fichero que se carga cuando alguien hace un require
de nuestra gema. Podemos poner aquí el código que queramos o crear otros archivos en el directorio lib
e incluirlos desde este. Para una gema tan simple como la nuestra tan sólo crearemos un método de clase llamado ipsum
que devuelve un fragmento de texto Lorem Ipsum.
module Lorem def self.ipsum "Lorem ipsum dolor sit amet, consectetur adipisicing ...." end end
Publicación
Así pues, ya hemos terminado nuestra gema y estamos listos para publicar su primera versión. Antes de hacerlo actualizaremos el fichero Gemspec y editaremos los elementos marcados con TODO.
# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "lorem/version" Gem::Specification.new do |s| s.name = "lorem" s.version = Lorem::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Eifion Bedford"] s.email = ["eifion@asciicasts.com"] s.homepage = "" s.summary = %q{Lorem ipsum generator} s.description = %q{Simply generates lorem ipsum text.} s.rubyforge_project = "lorem" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] end
Y ya podemos ejecutar la orden gem build
pasándole nuestro fichero Gemspec.
$ gem build lorem.gemspec WARNING: no homepage specified Successfully built RubyGem Name: lorem Version: 0.0.1 File: lorem-0.0.1.gem
Esta orden genera un fichero .gem
. Si ejecutásemos ahora la orden gem push
la gema sería enviada a RubyGems.org para su publicación para que otros la puedan usar.
Actualización
Publicar una gema con un fichero Gemspec es así de sencillo. Si queremos lanzar una nueva versión debemos hacer los cambios pertinentes en el código y luego actualizar el número de versión en version.rb
.
module Lorem VERSION = "0.0.2" end
Luego tan sólo tenemos que hacer gem build
y gem push
nuevamente para empaquetar y publicar la nueva versión.
Gemfile y Rakefile
Quedan dos ficheros que han sido generados por Bundler y que todavía no hemos cubierto, el fichero Gemfile
y el fichero Rakefile
. Veamos en primer lugar el Gemfile
. En una aplicación Rails el fichero Gemfile
controla las dependencias de las gemas en Budnler. Si miramos en el interior de Gemfile
veremos que tiene tan sólo una orden: gemspec
.
source "http://rubygems.org" # Specify your gem's dependencies in lorem.gemspec gemspec
Esta orden inspecciona el fichero Gemspec de la gema en busca de sus dependencias, usando Bundler para cargarlas. Por lo general no tenemos que preocuparnos de este archivo directamente, es mejor gestionar las dependencias de la gema dentro del fichero Gemspec y dejar que Bundler las cargue automáticamente mediante el Gemfile
. Por ejemplo, supongamos que queremos utilizar RSpec para escribir los tests de esta gema. En lugar de añadir una referencia a RSpec en el Gemfile
, la añadiremos como dependencia de desarrollo en lorem.gemspec
.
Los que no estén familiarizados con los ficheros Gemspec pueden leer la documentación que enumera los distintos métodos que pueden ser invocados desde dentro del Gemspec para configurar los diferentes atributos, incluído el método add_development_dependency
que es lo que queremos utilizar para declarar RSpec.
# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "lorem/version" Gem::Specification.new do |s| s.add_development_dependency "rspec" # Other attributes omitted end
Como en el Gemfile
se hace referencia al Gemspec, podemos ejecutar bundle
para asegurarnos de que se instalan todas las gemas.
$ bundle Fetching source index for http://rubygems.org/ Using diff-lcs (1.1.2) Using lorem (0.0.1) from source at /Users/eifion/Desktop/Dropbox/rails/apps_for_asciicasts/ep245/lorem Installing rspec-core (2.3.1) Installing rspec-expectations (2.3.0) Installing rspec-mocks (2.3.0) Installing rspec (2.3.0) Using bundler (1.0.7) Your bundle is complete! It was installed into /Users/eifion/.rvm/gems/ruby-1.9.2-p0
Esto es útil porque cuando publiquemos la gema y otros desarrolladores quieran contribuir podemos dar instrucciones en el fichero README para que ejecuten el comando bundle
que dejará el entorno con todas las dependencias configuradas.
Esto en cuanto al Gemfile
. Pero ¿qué hay del Rakefile
? ¿Por qué Bundle ha generado uno para nosotros? Si le echamos un vistazo veremos que añade algunas tareas de ayuda de Bundler.
require 'bundler' Bundler::GemHelper.install_tasksWe can take a look at those tasks by running rake -T. $ rake -T (in /Users/eifion/code/ep245/lorem) rake build # Build lorem-0.0.2.gem into the pkg directory rake install # Build and install lorem-0.0.2.gem into system gems rake release # Create tag v0.0.2 and build # and push lorem-0.0.2.gem to Rubygems
El código en el Rakefile
genera tres tareas, las típicas para construir, instalar y publicar una gema. Un flujo de trabajo común es invocar rake install
cuando la gema esté lista tal y como queremos para instalarla en nuestra máquina local y probarla por completo, tras lo cual se llama a rake release
para etiquetar dicha revisión y publicar la gema en RubyGems.
Actualización de las gemas ya existentes
Con Bundler tenemos una forma muy cómoda de escribir una gema desde cero pero, ¿y si ya tenemos una gema con la que querríamos emplear esta forma de trabajar? Es fácil aplicar esta técnica a una gema ya existente simplemente copiando los Gemfile
, .gemspec
y Rakefile
(si es que nos interesa usar las tareas que incluye), con lo que podremos seguir la forma de trabajar descrita anteriormente con cualquier gema ya existente.
En resumen, Bundler es una gran solución para crear y gestionar nuestras gemas, siendo mucho más sencillo que las otras herramientas que hemos visto en los episodios anteriores, por lo que merece la pena tenerlo en cuenta cuando creemos nuestras propias gemas.