Project:Ruby/Packaging RubyGems

RubyGems packages pose non-indifferent problems to all Linux distributions; Gentoo Linux supports writing simple ebuilds for most of the projects by either using the distributed gem itself, tarballs or GitHub tags. This page is designed to help understanding how to do that.

What to package
While it's technically possible to package _any_ RubyGem out there, it's not our aim to produce a separate archive of Ruby libraries, but rather to make it easy to manage Ruby libraries into production environments. In general, the packaged projects should be of widespread use, at least to a point, and there should be usefulness to have it available as a package.

More pragmatically, we usually keep in tree ebuilds for packages that Gentoo Linux developers rely on and their dependencies. The dependencies include optional and test-only (or documentation-only) dependencies, which still make the net of available gems quite wide.

It's important to understand though, that at least one requirement has to be preserved: the gem needs to state a clear license.

Version numbers and slots
Gems are slotted *when needed*. Slotting gems comes with a lot of additional overhead and is hardly ever actually needed, so we try to steer clear of that as much as we can. Instead we slot based on API changes, not on random version numbers. The key problem with rubygems is that it "slots" every individual version, happily overwriting each others files in the process. Due to this ruby gem authors all use their own versioning schemes, rather than unifying on semantic versions and API compatibility cannot be determined reliably from the version numbers alone.

In many cases the version requirements in ruby software are artificially restricted. If you want to make them work in Gentoo 9 out of 10 times fixing (or ignoring) the versions in things like Gemfile solves all issues. In other cases we do slot, like e.g. with dev-ruby/mocha.

How to package
There are two eclasses that make up the main interfaces required to package RubyGems in Gentoo: ruby-ng.eclass that provides the low-level methods and the multi-ruby phases, and ruby-fakegem.eclass that takes care of most of the boilerplate code required to convert a gem into an ebuild, with a declarative style.

Unless you're packaging a library that is so old that no gem is released for it, you want to use the latter, as it'll take care of installing the gem specifications, which make the library available as a gem, transparently to other projects as well, including, for the most part, Bundler.

Please also make sure you use EAPI 4 or later for all the new Ruby ebuilds. This allows us to set up REQUIRED_USE to disallow merging of the packages without a properly selected target.

ruby-fakegem.eclass custom variables
The declarative form of ruby-fakegem.eclass requires for a few variables to be set before inheriting the eclass. The most important of these variables is USE_RUBY, which declares for which targets the library is available. This is further expanded within the eclasses to expose RUBY_TARGETS values that can be set by the user.

Custom dependency handling
Due to the dual-nature of many RubyGems dependencies (not all of them are tied to a Ruby target, but if they are they need to have at least the same enabled targets), when adding dependencies over other gems you should not use DEPEND and RDEPEND but rather two custom functions exposed by the eclasses: ruby_add_rdepend (to add run-time dependencies) and ruby_add_bdepend (to add build-time dependencies). These two take the same syntax as the main variables' as a parameter, and they append it to the already-defined values.

When adding dependencies that are not gems, please make sure you use the syntax DEPEND+=" dev-util/ragel" because, even though you haven't touched the variable at all, the eclasses will have added to it even just to add a dependency over the Ruby implementations.

Building C or C++ extensions
We prefer to build the extensions ourselves, rather than depending on additional tools like rake-compiler. These tools often have to employ nasty tricks to get code working on a wide variety of systems that may work against Gentoo policy, and with Gentoo we have a more predictable environment so our build procedure can be simpler. It is recommended to always inherit multilib to ensure the code works out of the box on systems that don't use .so as the shared object extension. Also make sure to add V=1 to the compile phase so that verbose build output is generated.

Test handling
While it's not technically a requirement that all the packages added to the tree have tests and have them running, we expect that developers adding new Ruby packages will at least make an attempt to get the tests running. By default, ruby-fakegem.eclass-based packages will use the Rake task test to run them. While this is a decent default, it might not be the best fit for your case.

To allow more flexibility when writing ebuilds for Ruby packages, the eclass supports three recipes or test sources: RSpec, Cucumber and Rake. On all three of them, the dependency to the direct tool is added automatically by the eclass; the ebuild needs to take care of adding extra dependencies (such as mock libraries, alternatives to check compatibility against, or in the case of Rake, packaging and testing frameworks).

Don't use Rake recipe if you can
If possible, it's highly recommended to not use the Rake-based recipe. This recipe is a decent default but it adds additional overhead, especially if packaging middleware (e.g., Hoe, Echoe, Jeweler, Bundler) is used, and it makes the tests sensitive to the correct working of these extra dependencies. Furthermore there is no easy way to make sure that variables such as TEST_VERBOSE or NOCOLOR are respected when using Rake.

What you should be using Rake for, as this point, is for tests based on Test::Unit, Minitest or other custom frameworks. In these situations, missing a recipe in the eclass, using Rake and adding the dependencies manually is recommended.

Avoid additional developer dependencies other than tests
Often packages include additional dependencies and tasks as part of their testing setup. A common example is the use of SimpleCov (possibly with Coveralls) to determine the level of coverage of the code by the tests. Since we won't actually do anything with the result of these extra steps it is better to avoid them altogether, saving on time and additional build-time dependencies.

RSpec 2.x and Cucumber
For either RSpec (2.x) and Cucumber based testsuites, live is easy: you just need to set RUBY_FAKEGEM_RECIPE_TEST to the name of the tool (lower-case) and the eclasses will take care of it from there. The wrapper functions used make sure that the user's requirements are fulfilled.

It is important to note that Cucumber does not support JRuby as of this writing. The eclasses hide this detail from the user by throwing a warning and then returning a non-error status when trying to execute cucumber for JRuby. Be careful with it.

RSpec 1.x
There is no recipe designed to work with RSpec 1.x as it's a deprecated package for a while at this point. Software still using RSpec 1.x should be migrated to use RSpec 2. Documentation will come on how to do the migration.

Test::Unit 2
Test::Unit 2 is a rewrite of the Test::Unit framework that shipped with Ruby 1.8 (and is no longer shipped with 1.9). This is the only implementation of its family (which include Test::Unit 1 and MiniTest) that has a command that behaves the same way on all implementations, and is thus the only one for which a helper is available (ruby-ng_testrb-2).

Since each project has a different way to handle tests, this method does not have a ready-to-use recipe, as no default can be found that this will work for a good chunks of projects.

To use the helper, make sure to set RUBY_FAKEGEM_RECIPE_TEST=none and add >=dev-ruby/test-unit-2.5.1-r1 to your dependencies (the latter is required because earlier versions of Test::Unit 2 didn't always install the requisite testrb-2 command).