Ruby-Programmierung: Rubygems

Zurück zum Inhaltsverzeichnis.

Sie haben bisher in diesem Buch viele Bibliotheken kennengelernt und benutzt. Das einzige was an diesen Bibliotheken besonders war, ist die Tatsache, das sie Teil der Standardbibliothek sind und daher mit einer Rubyinstallation ausgeliefert werden. Es ist natürlich auch möglich eigene Bibliotheken zu entwickeln und Drittanbieterbibliotheken zu verwerden. Darum hat sich in der Rubycommunity ein eigenes Ökosystem gebildet, das es erlaubt auf einfache Art und Weise Bibliotheken zu verwenden und auch Ihre Bibliotheken Dritten einfach zugänglich machen lässt.

Rubygems und Bundler Bearbeiten

Gems ist ein anderer Name für Rubybibliotheken, das entsprechende Programm Rubygems ist Teil einer Rubyinstallation. Es erlaubt einen einfachen Zugang zu Gems auf der Plattform Rubygems anderer Entwickler und somit die einfache Nutzung dieser Bibliotheken. Es ist auch möglich andere Quellen als Rubygems zu verwenden. Bundler hingegen baut auf Rubygems auf und erlaubt die Verwaltung von Rubygems zwischen verschiedenen Projekten, dies ist insbesondere dann notwendig, wenn ein Projekt auf einer anderen Version eines Gems beruht als ein anderes Projekt und Sie auch anderen Entwicklern einen einfachen Zugang zu Ihrem Projekt ermöglichen wollen.

Entwicklung eines Rubygems Bearbeiten

Weil die Nutzung und verschiedene damit verbundene Probleme natürlich auch die Entwicklung eines Gems betreffen, wird dieses Buch die Herangehensweise an Gems etwas umkehren. Nicht die Nutzung des Gems, sondern seine Entwicklung steht im Vordergrund, dies ist insbesondere von Vorteil, da man die unterschiedlichen Benutzungsmöglichkeiten hintergründig versteht.

In diesem Abschnitt soll ein Gem entwickelt werden, das die Standardbibliothek nicht bereitstellt. Es soll eine Methode Object#any? entwickelt werden, die eine Kollektionklasse erhält und prüft, ob sie das Objekt beinhaltet. Starten wir also mit der Generierung der Filestruktur:

bundle gem any

Hierbei wird die typische Filestruktur eines einfachen Gems erzeugt. Insbesondere wichtig ist an dieser Stelle die Datei any.gemspec.

Sie enthält Metadaten, die das Gems betreffen, wie deren Autoren, die beinhalteten Dateien, usw. Es wird davon ausgegangen, dass die Versionskontrolle mit Git erfolgt ansonsten muss die Zeile gem.files = `git ls-files`.split($\) angepasst werden. Da Git und insbesondere Github in der Rubycommunity weit verbreitet sind und auch die Distribution von Gems über Github sehr konfortabel ist, lohnt sich jedoch ein Blick auf Git und insbesondere, falls Sie noch kein Versionskontrollsystem kennen, ist Git eine gute Wahl damit zu beginnen. Dieses Buch wird nicht näher auf Versionskontrolle mit Git eingehen. Da der Umfang dieses Projekts überschaubar ist, sind jedoch keine Vorkenntnisse nötig, da an den entsprechenden Stellen die Funktion der Befehle kurz erläutert wird.

Der nächste Schritt ist also der erste Commit: git add . && git commit -m"initial commit". add fügt die angegebenen Dateien der Versionskontrolle hinzu, in diesem Fall das aktuelle Verzeichnis inklusive aller Unterverzeichnisse. Ein commit erzeugt eine neue Version des Quellcodes und versieht sie mit einem Kommentar, dadurch ist es möglich, später zwischen den einzelnen Commits zu wechseln und sie zu untersuchen.

Als nächstes ist es nötig, die Tests und die Implementierung der gewünschten Methode zu schreiben, um den Fokus nicht zu verlieren sind an dieser Stelle nur die entsprechenden Quelltexte angegeben.

# test/any.rb

require "test/unit"
require_relative "../lib/any"

class AnyTest < Test::Unit::TestCase
  def test_object
    assert Object.instance_methods.include? :any?
  end

  def test_arrays
    ary = [1, "abc"]
    assert "abc".any? ary
    assert !(2.any? ary)
    assert [2,3].any? [[2,3],[3],[2,5]]
  end
end
# lib/any.rb

require_relative "any/version"

class Object
  def any?(collection)
    collection.include? self
  end
end

An dieser Stelle sei nur auf die Zeile require_relative "any/version" hingewiesen. Der Codegenerator erzeugt an dieser Stelle require und das wird durch Rubygems abgefangen, um die Pfade anzupassen. Es handelt sich dabei um ein Überbleibsel aus Ruby 1.8 und sollte durch das in Ruby 1.9 eingeführte require_relative ersetzt werden.

Zum Schluss müssen die neuen Dateien noch in Git hinzugefügt werden, dies erfolgt analog zum ersten Commit mit git add . && git commit -m"Object#any? implemented".

Nutzung und Distribution Bearbeiten

In diesem einfachen Fall ist es natürlich ohne Weiteres möglich, den Quellcode direkt in andere Projekte zu kopieren und dort zu nutzen. Dies sollten Sie immer dann in Erwägung ziehen, falls die Bibliothek einen sehr speziellen Nutzungsbereich hat und zwischen einzelnen Applikationen verändert werden muss. Dann ist es einfacher, den Quellcode vor Ort zu verwalten, als für jedes Projekt das Gem anzupassen.

Natürlich birgt obiges Verfahren einige Probleme, da Sie keine Updates für Ihr Gem bereitstellen können, und ist somit nur für sehr begrenzte Anwendungsfälle geeignet. Besser ist es dann, das Gem insgesamt zu verteilen. Dafür muss es zunächst gebaut werden, wozu das any.gemspec dient. Das Bauen erfolgt mit dem Befehl gem build any.gemspec und erzeugt eine Datei mit dem entsprechenden Namen und der Version des Gems. Jetzt ist es möglich, das Gem mittels gem install any-0.0.1.gem zu installieren und mittels require "any" zu nutzen. In einer Prysession ergibt sich zum Beispiel:

[1] pry(main)> require "any"
=> true
[2] pry(main)> 2.any? [2,3,4]
=> true

Dieser Weg ist für die Nutzer bereits deutlich angenehmer, da Sie jetzt Updates des Gems verteilen können, indem Sie die Versionsnummer ändern und das Gem neu verteilen. Das Problem ist, dass Ihre Nutzer noch von einer neuen Version erfahren müssen. Es eignet sich also zum Beispiel für einen engen Nutzerkreis. Für generalisierte Bibliotheken und weite Nutzerkreise eignen sich die Plattformen Rubyforge und Github. Dadurch wird es für Sie zwar etwas mühseliger ihr gem zu verteilen, da Sie sich bei diesen Plattformen anmelden müssen und das hochgeladene Gem warten müssen, doch ist es für ihre Nutzer möglich, die Gems einfach mittels gem install GEMNAME von Rubyforge oder mittels gem install GEMNAME --source GITHUBURL um von Github zu installieren.

Gemfile Bearbeiten

Da das kleine Beispiel der Bibliothek Any keine Abhängigkeiten zu anderen Gems beinhaltete, soll an dieser Stelle noch einmal extra darauf eingegangen werden, wie Sie mit Abhängigkeiten in Ihren Projekten umgehen können. An dieser Stelle wird bundler benutzt um das Gemfile auszuwerten und dementsprechend die Abhängigkeiten zu verwalten.

Ein Gemfile beginnt mit Angabe der Quelle für die Pakete, das ist im Normalfalls Rubyforge und die Zeile lautet: source 'https://rubygems.org'. Danach erfolgt die Angabe der Abhängigkeiten durch gem GEMNAME VERSION. Es ist möglich die Gems Gruppen zuzuordnen und damit beispielsweise für bestimmte Abschnitte der Entwicklung nur bestimmte Gems zu benutzen. Als Beispiel aus einem frisch generierten Railsprojekt aus dem einige Kommentare entfernt wurden:

source 'https://rubygems.org'

gem 'rails', '3.2.11'

# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'

gem 'sqlite3'

group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'
  gem 'uglifier', '>= 1.0.3'
end

gem 'jquery-rails'

Installiert wird mit dem Aufruf von bundle install. Es ist möglich zahlreiche Optionen zu übergeben, wie in diesem Beispiel: git => 'git://github.com/rails/rails.git', um die Quelle des Gems festzulegen. Die unterschiedlichen Versionsangaben beziehen sich auf das Verhalten der Gems bei Installation und Updates und verhalten sich wie folgt:

  • "3.2.11" verwende exakt diese Version
  • ">=1.0.3" verwende mindestens die Version 1.0.3, aber auch jede höhere
  • "~> 3.2.1" verwende mindestens die Version 3.2.1, führe jedoch kein Majorupdate auf Version 4 durch.

Die bei der Installation der Gems angelegte Datei Gemfile.lock spezifiziert die installierte Version und kann dazu verwendet werden Versionsanforderungen im Gemfile anzupassen, oder kann auch mit anderen Projektbeteiligten geteilt werden, damit alle die gleichen Versionen benutzen. Der Normalfall im generierten Gem ist, dass das Gemfile.lock nicht versioniert wird und kann in der Datei .gitignore geändert werden.