Skip to content

rubygems, bundler, maven a comparison

mkristian edited this page Sep 19, 2012 · 1 revision

. right now I would like to make two steps outside and have a look at rubygems, bundler and maven (limited by my own knowledge of things) and see how they do things. I am sure you could add some buildr to it ;-)

local storage

rubygems

on install:

  • defaults some system location depends on your distribution and/or setup - concrete directory depends on ruby version and platform
  • $HOME/.gem/{ruby version dir} via setting in .gemrc with install: --user-install
  • $HOME/.gem/{ruby version dir} with command line switch --user-install
  • GEM_HOME environment variable

using installed gems

  • defaults some system location depends on your distribution (a directory depending on ruby version and platform) and adds $HOME/.gem/{ruby version dir} to it
  • GEM_PATH enviroment variable

bundler

  • defaults to the defaults of rubygems
  • does not obey the .gemrc settings and has no command line switches for user-install (you need sudo rights or use GEM_HOME)
  • obeys GEM_HOME and GEM_PATH if set

maven

  • defaults to $HOME/.m2/repository
  • default can be changed by $HOME/.m2/settings.xml
  • can use any settings.xml via command invocation

gem (i.e. gemspec)

  • no info about storage location

pom

  • no info about storage location

remote storage locations

rubygems

  • defaults: rubygems.org
  • can add more sources to $HOME/.gemrc
  • per command you can add a source or more for a single execution

bundler

  • defaults: rubygems.org (when there is not "source ..." declaration in Gemfile)
  • :source via Gemfile
  • :git and :path is also possible

maven

  • defaults to maven central (actually it is quite difficult to replace maven central with something else)
  • can add more via $HOME/settings.xml
  • can add more via any settings.xml via commandline switch
  • the settings.xml allow to pick a certain config via profile names and or system properties (or even jdk-detection, etc)
  • can add more in pom.xml

gem (gemspec)

  • no info on remote repositories

pom

  • can have remote repositories BUT are not allowed on maven central

mirrors of remote repositories

rubygems

  • can use only a proxy to ALL http(s)

bundler

  • can use only a proxy to ALL http(s)

maven

  • can configure mirrors in settings.xml

dependency declarations

rubygems

in gemspec usually using version ranges to reduce version conflicts with other gems and allow to use updated version of the dependent gem in future. ideally using semantic versioning schema (but for example rails does not follow it)

bundler

some as gemspec for Gemfile and version resolution gets locked down with Gemfile.lock

declared dependencies in Gemfile do not contribute to the gemspec of a gem. the dependencies from gemspec can be included in the Gemfile.

whether a project adds Gemfile.lock to version control or not depends. in https://github.com/datamapper/do/ we do not add it since the libraries need to work for all possible allowed version resolutions with the given gemspec + Gemfile. as a library youdo not know the context used for version resolution.

a typical rails application will add Gemfile.lock since it ensures everyone works on the same set of gems. rails is (usually) not a gem.

maven

the recommended way of declaring versions is without ranges. maven version resolution does not guarantee to use the declared version - it can resolve version conflicts in some way (the version closer to the dependencies's root wins). it is deterministic without version ranges. when using version ranges the maven version resolution depends on available versions for a declared dep on the time of versions resolution. that makes the resolution likely to change in future.

gem (gemspec)

declared dependencies and no locked down versions

pom

declared dependencies and no locked down versions

library metadata

this is a rough comparison between gemspec and pom.

=== common to gemspec and pom ===

  • metadata like name, description,
  • author, emails which is developer in pom
  • runtime and developer (compile) time dependency declarations

=== gemspec only ===

  • ruby version, platform, bin-path, lib-path, . . .

=== pom only ===

  • profiles, plugins, . . .

=== relation to rubgems, bundler. maven ===

only gemspec/pom and some extra metadata is used to calculate the transitive dependency hull.

a maven remote repo offers the pom.xml and the metadata.xml for a list of versions of a given artifact.

a rubygems repository has a zipped memory dump of gemspec of each gem

ruby users / maven users

it would be just great if both users find familiar ground.

we have three sets of possible configurations (assume something like this):

  • local-config like $HOME/.m2/settings.xml, $HOME/.gemrc or $HOME/.bundle/config - not part of the project's version control
  • project config like Gemfile/Gemfile.lock - can be part of the project's version control or not
  • dependency-management and project metadata (gemspec, pom.xml) - basically package config and used for version resolutions. can be part of version control.

local config

ideally can be on a project base ($project.baserdir) and on per user ($HOME) like bundler does have ./bundle/config at both locations.

now the question what is part of that config ? anything which makes no sense to share publicly.

  • mirror: whether you run the same mirror on the same port on localhost or localnet is quite unlikely

  • local storage like local repository. currently I am thinking to move my local maven repo to /usr/local/repository - quite sure such a config makes no sense for any windows users and probably no one else, too.

  • profile switch to activate certain config from your settings.xml. we implemented that with maven-gem support for jruby

many corporates have restricted requirements regarding software/library management and where to store internal libs and how to download external libs. in such cases obeying setttings.xml from maven is definitely welcome by sooner or later.

gemspec + pom

when I look at poms then I can have jar and/or gem dependencies. they are totally exchangeable. any maven based dependency management can at least resolve the version via maven - the installing of the gems some "plugin" is needed.

with this any java project using jruby-scripting and needs a gem gets all transitive gems as well. if any of these gems depend on a jar then that will be part of the regular jar resolution and automatically added to classloader.

with gemspec it would be nice to be able to declare jar deps as well (as I mentioned before).

why ? one file for all :) gems and jars for your gem are "configured" in almost the way.

maybe from another point of view. there is a reason why the rubygems server offers gemspec.rz files: you do not want to download a couple of megabytes (rare but there are big gems) to be able to unpack the gem to get the metadata.

for version resolution I need the metadata and do not want to download all possible versions of bulky gem to get them - it is already inefficient to download the gemspec.rz for each version for given gem. Bundler uses some restapi which improves this (but not all rubgems repos support that restapi and then Bundler falls back to the old way).

if you want to treat gems and jars equally in future "super-bundler" then you need both deps info inside the gemspec to have a feasible implementation of such a tool. it is also particular nice to just use the existing infrastructure without any need to "change" anything at rubygems level.

now you would need a tool to resolve all gems and jars, installs the gems and setup the classloader. not sure if there is something like this around.

Gemfile/Jarfile and their locks

if you have gem project with gemspec, bundler can add some deps management support with a simple declaration gemspec inside an otherwise empty Gemfile. that works fine for gems only gems. the moment jars are involved bundler does not work and jbundler is fast (workable) hack.

the first thing I need to mention is that when any gems declared in the Gemfile do not play any role to gems once published. usually people use it for a bigger test setup and/or run metrics and/or code quality/style verifiers and/or tools like css-minifier and/or javascript-obfuscator, etc nothing which is needed to use the gems as dep from any other project.

to keep people on familiar ground the Jarfile should be used in similar fashion.

vendored gems/jars

I personally find them scary since you can end up having same gem/jar on class- or load-path but with different versions.

for example Bundler does check if thor is already loaded before using its vendored thor. that is great that the check for such situations but can not execute the commandline parts when I have already thor loaded. and I am sure not everyone does thosekind of checks.

same holds true for vendored jars. with datamapper/do we also add a jar to gem but that jar does not exist outside the gem - it is only internal more like c-extension.

you also mentioned some classloader tricks to isolate those jars ?! curious :)

let's look at gems which do vendor jars:

nokogiri jruby-openssl

what I would like to see here is check whether the jar is already loaded - if not then load the vendored jar otherwise do nothing.

I guess I just stop here kind of n the middle since I personally never considered vendored jars to be a way to go. we can/should discuss this separately.

Something went wrong with that request. Please try again.