Dependencias en Ruby
Imobach González Sosa
http://github.com/imobach
@imobachgs
Al principio
- Ruby nació en 1995
- Instalación manual de bibliotecas y aplicaciones
- Las bibliotecas se instalan y cargan desde unas rutas predeterminadas ($LOAD_PATH)
- Ruby Application Archive
RubyGems (2004)
Sistema de gestión de paquetes que permite instalar aplicaciones y bibliotecas
- https://rubygems.org/
- Los paquetes se llaman gemas
- Ofrece una interfaz en línea de órdenes (CLI)
- Integrado desde Ruby 1.9
¿Cómo funciona?
- Resuelve dependencias
- Descarga, compila (si es necesario) e instala
- Instala las gemas en el GEM_PATH
- Permite la instalación de varias versiones de la misma gema (!)
CLI
$ gem search rails
$ gem list
$ gem install --user-install nokogiri
Building native extensions. This could take a while...
$ gem update rails
$ gem content rails
$ gem help
Un poco de código
require "rubygems" # No es necesario (sólo Ruby <= 1.8)
require "octokit"
- "require" añade la ruta de la gema al $LOAD_PATH
gemspec
Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", "~> 1.0"
spec.authors = ["Yehuda Katz", "José Valim"]
spec.description = "Thor is a toolkit for building..."
spec.email = "ruby-thor@googlegroups.com"
spec.executables = %w[thor]
spec.files = %w[.document thor.gemspec]
+ Dir['*.md', 'bin/*', 'lib/**/*.rb']
spec.homepage = "http://whatisthor.com/"
spec.licenses = %w[MIT]
spec.name = "thor"
spec.require_paths = %w[lib]
spec.required_rubygems_version = ">= 1.3.5"
spec.summary = spec.description
spec.version = Thor::VERSION
end
Mayor complejidad
- Aplicaciones más complejas y con más dependencias
- Mecanismos de resolución muy simples
- Supongamos que tenemos instaladas las versiones 1.0 y 2.0 de una gema C
- A depende de C (cualquier versión)
- B depende de C 1.0
- ¿Qué ocurre si se carga primero A?
- Para asegurar que dos usuarios instalan las mismas dependencias, hay que especificar versiones manualmente
Soluciones
- Ruby Version Manager (2009)
- Permite instalar varias versiones de Ruby a la vez
- Añade el concepto de gemset
- No resuelve el problema de las dependencias, pero reduce su impacto
- Han seguido apareciendo alternativas: rbenv, chruby, etc.
- Bundler (2010)
- Se centra en mejorar la gestión y resolución de dependencias
- Asegura que se instalan exactamente las versiones necesarias
Consistencia
- Se encarga de instalar/actualizar las dependencias de un proyecto
- Asegura un entorno consistente instalando exactamente las versiones necesarias
- Partiendo de una declaración de dependencias (Gemfile)...
- ... instala todas las gemas necesarias y guarda el resultado en un fichero (Gemfile.lock)
Gemfile
gem "octokit"
gem "rspec"
bundle install
instalará las gemas y sus dependencias
Gemfile.lock
- Contiene una lista exhaustiva de las gemas que necesita la aplicación
- Aparecen las declaradas en el Gemfile y sus dependencias
- Se registra su número de versión exacto (ref, tag o rama en el caso de Git)
- Este fichero debe estar en el control de versiones
- Permite que, independientemente de la máquina, se usen las mismas versiones de todo
Especificación de dependencias
- Código Ruby
- Se indica el nombre y las versiones requeridas
gem 'rails', '4.2.0'
gem 'pg', '>= 0.17', '<= 0.19'
gem 'responders', '~> 2.0'
Fuentes
- Se pueden obtener las gemas de diferentes fuentes
- Además, están soportados los siguientes tipos:
- repositorios RubyGems (oficial o el tuyo propio)
- un repositorio Git
- una ruta del sistema de archivos
Fuentes: ejemplos
gem "rails", github: "rails/rails"
gem "simple_form", github: "plataformatec/simple_form",
tag: "v3.1.0.rc2"
gem "mi-gema", path: "/home/user/gems/mi-gema"
source "http://my-own-repo.lan" do
gem "examples"
end
Grupos
# These gems are in the :default group
gem 'nokogiri'
gem 'sinatra'
gem 'wirble', group: :development
group :test do
gem 'faker'
gem 'rspec'
end
- Hay dependencias que sólo tienen sentido en un contexto determinado
- Agrupando las gemas se pueden realizar operaciones sobre diferentes grupos
- El caso más típico es diferenciar dependencias de ejecución, desarrollo y pruebas
Instalación de dependencias
$ bundle install
$ bundle install --path vendor/bundle
$ bundle install --jobs 4
$ bundle install --without test development
Actualizaciones
$ bundle update
$ bundle update rails
- Actualiza la gema a la última versión disponible que cumpla con la especificación
- Si tiene dependencias, también las actualiza
- Si no se especifica ninguna gema, intenta actualizarlas todas
Manual
require "rubygems" # No es necesario (sólo Ruby <= 1.8)
require "bundler/setup"
require "octokit" # Declarada en el Gemfile
- Pone todas las gemas del Gemfile en el $LOAD_PATH
- Disponibles para cargar usando require
Automático
require "rubygems"
require "bundler/setup"
Bundler.require(:default) # Carga todas las gemas declaradas
- Carga todas las gemas de un grupo determinado
- :default == "todas"
La fusión
- RubyGems y Bundler comparten mucha funcionalidad
- La idea es que sea fusionen... pero no todavía
- Propuesta para un formato más eficiente para los índices
/