summary refs log tree commit diff
path: root/pkgs/development/interpreters/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/development/interpreters/ruby')
-rw-r--r--pkgs/development/interpreters/ruby/build-ruby-gem/default.nix208
-rw-r--r--pkgs/development/interpreters/ruby/build-ruby-gem/gem-post-build.rb77
-rw-r--r--pkgs/development/interpreters/ruby/build-ruby-gem/nix-bundle-install.rb153
-rw-r--r--pkgs/development/interpreters/ruby/bundix/Gemfile4
-rw-r--r--pkgs/development/interpreters/ruby/bundix/Gemfile.lock18
-rw-r--r--pkgs/development/interpreters/ruby/bundix/default.nix25
-rw-r--r--pkgs/development/interpreters/ruby/bundix/gemset.nix22
-rw-r--r--pkgs/development/interpreters/ruby/bundler-env/default.nix361
-rw-r--r--pkgs/development/interpreters/ruby/bundler-env/gen-bin-stubs.rb46
-rw-r--r--pkgs/development/interpreters/ruby/bundler-env/monkey_patches.rb246
-rw-r--r--pkgs/development/interpreters/ruby/bundler-env/package-1.8.rb29
-rw-r--r--pkgs/development/interpreters/ruby/bundler.nix17
-rw-r--r--pkgs/development/interpreters/ruby/gem.nix136
-rw-r--r--pkgs/development/interpreters/ruby/gemconfig/default.nix (renamed from pkgs/development/interpreters/ruby/bundler-env/default-gem-config.nix)28
-rw-r--r--pkgs/development/interpreters/ruby/gemconfig/mkrf_conf_xapian.rb (renamed from pkgs/development/interpreters/ruby/bundler-env/mkrf_conf_xapian.rb)0
-rw-r--r--pkgs/development/interpreters/ruby/gemconfig/xapian-Rakefile (renamed from pkgs/development/interpreters/ruby/bundler-env/xapian-Rakefile)0
-rw-r--r--pkgs/development/interpreters/ruby/load-ruby-env.nix69
-rw-r--r--pkgs/development/interpreters/ruby/rubygems.nix34
18 files changed, 609 insertions, 864 deletions
diff --git a/pkgs/development/interpreters/ruby/build-ruby-gem/default.nix b/pkgs/development/interpreters/ruby/build-ruby-gem/default.nix
new file mode 100644
index 00000000000..d050faca245
--- /dev/null
+++ b/pkgs/development/interpreters/ruby/build-ruby-gem/default.nix
@@ -0,0 +1,208 @@
+# This builds gems in a way that is compatible with bundler.
+#
+# Bundler installs gems from git sources _very_ differently from how RubyGems
+# installes gem packages, though they both install gem packages similarly.
+#
+# We monkey-patch Bundler to remove any impurities and then drive its internals
+# to install git gems.
+#
+# For the sake of simplicity, gem packages are installed with the standard `gem`
+# program.
+#
+# Note that bundler does not support multiple prefixes; it assumes that all
+# gems are installed in a common prefix, and has no support for specifying
+# otherwise. Therefore, if you want to be able to use the resulting derivations
+# with bundler, you need to create a symlink forrest first, which is what
+# `bundlerEnv` does for you.
+#
+# Normal gem packages can be used outside of bundler; a binstub is created in
+# $out/bin.
+
+{ lib, ruby, rubygems, bundler, fetchurl, fetchgit, makeWrapper, git, buildRubyGem
+} @ defs:
+
+lib.makeOverridable (
+
+{ name ? null
+, gemName
+, version ? null
+, type ? "gem"
+, document ? [] # e.g. [ "ri" "rdoc" ]
+, platform ? "ruby"
+, ruby ? defs.ruby
+, stdenv ? ruby.stdenv
+, namePrefix ? "${ruby.name}" + "-"
+, buildInputs ? []
+, doCheck ? false
+, meta ? {}
+, patches ? []
+, gemPath ? []
+, dontStrip ? true
+, remotes ? ["https://rubygems.org"]
+# Assume we don't have to build unless strictly necessary (e.g. the source is a
+# git checkout).
+# If you need to apply patches, make sure to set `dontBuild = false`;
+, dontBuild ? true
+, propagatedBuildInputs ? []
+, propagatedUserEnvPkgs ? []
+, buildFlags ? null
+, passthru ? {}
+, ...} @ attrs:
+
+if ! builtins.elem type [ "git" "gem" ]
+then throw "buildRubyGem: don't know how to build a gem of type \"${type}\""
+else
+
+let
+  shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'";
+  rubygems = (attrs.rubygems or defs.rubygems).override {
+    inherit ruby;
+  };
+  src = attrs.src or (
+    if type == "gem"
+    then fetchurl {
+      urls = map (remote: "${remote}/gems/${gemName}-${version}.gem") remotes;
+      inherit (attrs) sha256;
+    } else fetchgit {
+      inherit (attrs) url rev sha256 fetchSubmodules;
+      leaveDotGit = true;
+    }
+  );
+  documentFlag =
+    if document == []
+    then "-N"
+    else "--document ${lib.concatStringsSep "," document}";
+
+in
+
+stdenv.mkDerivation (attrs // {
+  inherit ruby rubygems;
+  inherit doCheck;
+  inherit dontBuild;
+  inherit dontStrip;
+  inherit type;
+
+  buildInputs = [
+    ruby rubygems makeWrapper
+  ] ++ lib.optionals (type == "git") [ git bundler ]
+    ++ buildInputs;
+
+  name = attrs.name or (namePrefix + gemName);
+
+  inherit src;
+
+  phases = attrs.phases or [ "unpackPhase" "patchPhase" "buildPhase" "installPhase" "fixupPhase" ];
+
+  unpackPhase = attrs.unpackPhase or ''
+    runHook preUnpack
+
+    if [[ -f $src && $src == *.gem ]]; then
+      if [[ -z "$dontBuild" ]]; then
+        # we won't know the name of the directory that RubyGems creates,
+        # so we'll just use a glob to find it and move it over.
+        gempkg="$src"
+        sourceRoot=source
+        gem unpack $gempkg --target=container
+        cp -r container/* $sourceRoot
+        rm -r container
+
+        # copy out the original gemspec, for convenience during patching /
+        # overrides.
+        gem specification $gempkg  --ruby > original.gemspec
+        gemspec=$(readlink -f .)/original.gemspec
+      else
+        gempkg="$src"
+      fi
+    else
+      # Fall back to the original thing for everything else.
+      dontBuild=""
+      preUnpack="" postUnpack="" unpackPhase
+    fi
+
+    runHook postUnpack
+  '';
+
+  buildPhase = attrs.buildPhase or ''
+    runHook preBuild
+
+    if [[ "$type" == "gem" ]]; then
+      if [[ -z "$gemspec" ]]; then
+        gemspec="$(find . -name '*.gemspec')"
+        echo "found the following gemspecs:"
+        echo "$gemspec"
+        gemspec="$(echo "$gemspec" | head -n1)"
+      fi
+
+      exec 3>&1
+      output="$(gem build $gemspec | tee >(cat - >&3))"
+      exec 3>&-
+
+      gempkg=$(echo "$output" | grep -oP 'File: \K(.*)')
+
+      echo "gem package built: $gempkg"
+    fi
+
+    runHook postBuild
+  '';
+
+  # Note:
+  #   We really do need to keep the $out/${ruby.gemPath}/cache.
+  #   This is very important in order for many parts of RubyGems/Bundler to not blow up.
+  #   See https://github.com/bundler/bundler/issues/3327
+  installPhase = attrs.installPhase or ''
+    runHook preInstall
+
+    export GEM_HOME=$out/${ruby.gemPath}
+    mkdir -p $GEM_HOME
+
+    echo "buildFlags: $buildFlags"
+
+    ${lib.optionalString (type == "git") ''
+    ruby ${./nix-bundle-install.rb} \
+      ${gemName} \
+      ${attrs.url} \
+      ${src} \
+      ${attrs.rev} \
+      ${version} \
+      ${shellEscape (toString buildFlags)}
+    ''}
+
+    ${lib.optionalString (type == "gem") ''
+    if [[ -z "$gempkg" ]]; then
+      echo "failure: \$gempkg path unspecified" 1>&2
+      exit 1
+    elif [[ ! -f "$gempkg" ]]; then
+      echo "failure: \$gempkg path invalid" 1>&2
+      exit 1
+    fi
+
+    gem install \
+      --local \
+      --force \
+      --http-proxy 'http://nodtd.invalid' \
+      --ignore-dependencies \
+      --build-root '/' \
+      --backtrace \
+      ${documentFlag} \
+      $gempkg $gemFlags -- $buildFlags
+
+    # looks like useless files which break build repeatability and consume space
+    rm -fv $out/${ruby.gemPath}/doc/*/*/created.rid || true
+    rm -fv $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true
+
+    # write out metadata and binstubs
+    spec=$(echo $out/${ruby.gemPath}/specifications/*.gemspec)
+    ruby ${./gem-post-build.rb} "$spec"
+    ''}
+
+    runHook postInstall
+  '';
+
+  propagatedBuildInputs = gemPath ++ propagatedBuildInputs;
+  propagatedUserEnvPkgs = gemPath ++ propagatedUserEnvPkgs;
+
+  passthru = passthru // { isRubyGem = true; };
+  inherit meta;
+})
+
+)
diff --git a/pkgs/development/interpreters/ruby/build-ruby-gem/gem-post-build.rb b/pkgs/development/interpreters/ruby/build-ruby-gem/gem-post-build.rb
new file mode 100644
index 00000000000..112a9accc33
--- /dev/null
+++ b/pkgs/development/interpreters/ruby/build-ruby-gem/gem-post-build.rb
@@ -0,0 +1,77 @@
+require 'rbconfig'
+require 'rubygems'
+require 'rubygems/specification'
+require 'fileutils'
+
+ruby = File.join(ENV["ruby"], "bin", RbConfig::CONFIG['ruby_install_name'])
+out = ENV["out"]
+bin_path = File.join(ENV["out"], "bin")
+gem_home = ENV["GEM_HOME"]
+gem_path = ENV["GEM_PATH"].split(":")
+install_path = Dir.glob("#{gem_home}/gems/*").first
+gemspec_path = ARGV[0]
+
+if defined?(Encoding.default_internal)
+  Encoding.default_internal = Encoding::UTF_8
+  Encoding.default_external = Encoding::UTF_8
+end
+
+gemspec_content = File.read(gemspec_path)
+spec = nil
+if gemspec_content[0..2] == "---" # YAML header
+  spec = Gem::Specification.from_yaml(gemspec_content)
+else
+  spec = Gem::Specification.load(gemspec_path)
+end
+
+FileUtils.mkdir_p("#{out}/nix-support")
+
+# write meta-data
+meta = "#{out}/nix-support/gem-meta"
+FileUtils.mkdir_p(meta)
+FileUtils.ln_s(gemspec_path, "#{meta}/spec")
+File.open("#{meta}/name", "w") do |f|
+  f.write(spec.name)
+end
+File.open("#{meta}/install-path", "w") do |f|
+  f.write(install_path)
+end
+File.open("#{meta}/require-paths", "w") do |f|
+  f.write(spec.require_paths.join(" "))
+end
+File.open("#{meta}/executables", "w") do |f|
+  f.write(spec.executables.join(" "))
+end
+
+# add this gem to the GEM_PATH for dependencies
+File.open("#{out}/nix-support/setup-hook", "a") do |f|
+  f.puts("addToSearchPath GEM_PATH #{gem_home}")
+  spec.require_paths.each do |dir|
+    f.puts("addToSearchPath RUBYLIB #{install_path}/#{dir}")
+  end
+end
+
+# create regular rubygems binstubs
+FileUtils.mkdir_p(bin_path)
+spec.executables.each do |exe|
+  File.open("#{bin_path}/#{exe}", "w") do |f|
+    f.write(<<-EOF)
+#!#{ruby}
+#
+# This file was generated by Nix.
+#
+# The application '#{exe}' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+gem_path = ENV["GEM_PATH"]
+ENV["GEM_PATH"] = "\#{gem_path}\#{":" unless gem_path.nil? || gem_path.empty?}#{(gem_path+[gem_home]).join(":")}"
+
+require 'rubygems'
+
+load Gem.bin_path(#{spec.name.inspect}, #{exe.inspect})
+    EOF
+  end
+
+  FileUtils.chmod("+x", "#{bin_path}/#{exe}")
+end
diff --git a/pkgs/development/interpreters/ruby/build-ruby-gem/nix-bundle-install.rb b/pkgs/development/interpreters/ruby/build-ruby-gem/nix-bundle-install.rb
new file mode 100644
index 00000000000..647b83b52c3
--- /dev/null
+++ b/pkgs/development/interpreters/ruby/build-ruby-gem/nix-bundle-install.rb
@@ -0,0 +1,153 @@
+require 'rbconfig'
+require 'bundler/vendored_thor'
+require 'bundler'
+require 'rubygems/command'
+require 'fileutils'
+require 'pathname'
+require 'tmpdir'
+
+# Options:
+#
+#   name        - the gem name
+#   uri         - git repo uri
+#   repo        - path to local checkout
+#   ref         - the commit hash
+#   version     - gem version
+#   build-flags - build arguments
+
+ruby = File.join(ENV["ruby"], "bin", RbConfig::CONFIG['ruby_install_name'])
+out = ENV["out"]
+bin_dir = File.join(ENV["out"], "bin")
+
+name        = ARGV[0]
+uri         = ARGV[1]
+REPO        = ARGV[2]
+ref         = ARGV[3]
+version     = ARGV[4]
+build_flags = ARGV[5]
+
+# options to pass to bundler
+options = {
+  "name"    => name,
+  "uri"     => uri,
+  "ref"     => ref,
+  "version" => version,
+}
+
+# Monkey-patch Bundler to use our local checkout.
+# I wish we didn't have to do this, but bundler does not expose an API to do
+# these kinds of things.
+Bundler.module_eval do
+  def self.requires_sudo?
+    false
+  end
+
+  def self.root
+    # we don't have a Gemfile, so it doesn't make sense to try to make paths
+    # relative to the (non existent) parent directory thereof, so we give a
+    # nonsense path here.
+    Pathname.new("/no-root-path")
+  end
+
+  def self.bundle_path
+    Pathname.new(ENV["GEM_HOME"])
+  end
+
+  def self.locked_gems
+    nil
+  end
+end
+
+Bundler::Source::Git.class_eval do
+  def allow_git_ops?
+    true
+  end
+end
+
+Bundler::Source::Git::GitProxy.class_eval do
+  def checkout
+    unless path.exist?
+      FileUtils.mkdir_p(path.dirname)
+      FileUtils.cp_r(File.join(REPO, ".git"), path)
+      system("chmod -R +w #{path}")
+    end
+  end
+
+  def copy_to(destination, submodules=false)
+    unless File.exist?(destination.join(".git"))
+      FileUtils.mkdir_p(destination.dirname)
+      FileUtils.cp_r(REPO, destination)
+      system("chmod -R +w #{destination}")
+    end
+  end
+end
+
+# UI
+verbose = false
+no_color = false
+Bundler.ui = Bundler::UI::Shell.new({"no-color" => no_color})
+Bundler.ui.level = "debug" if verbose
+
+# Install
+source = Bundler::Source::Git.new(options)
+spec = source.specs.search_all(name).first
+Bundler.rubygems.with_build_args [build_flags] do
+  source.install(spec)
+end
+
+msg = spec.post_install_message
+if msg
+  Bundler.ui.confirm "Post-install message from #{name}:"
+  Bundler.ui.info msg
+end
+
+# Write out the binstubs
+if spec.executables.any?
+  FileUtils.mkdir_p(bin_dir)
+  spec.executables.each do |exe|
+    wrapper = File.join(bin_dir, exe)
+    File.open(wrapper, "w") do |f|
+      stub = generate_stub(spec.name, exe)
+      f.write(<<-EOF)
+#!#{ruby}
+#
+# This file was generated by Nix.
+#
+# The application '#{exe}' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require 'rubygems'
+require 'bundler/setup'
+
+load Gem.bin_path(#{spec.name.inspect}, #{exe.inspect})
+EOF
+    end
+
+    FileUtils.chmod("+x", wrapper)
+  end
+end
+
+# Write out metadata
+meta = "#{out}/nix-support/gem-meta"
+FileUtils.mkdir_p(meta)
+FileUtils.ln_s(spec.loaded_from.to_s, "#{meta}/spec")
+File.open("#{meta}/name", "w") do |f|
+  f.write spec.name
+end
+File.open("#{meta}/install-path", "w") do |f|
+  f.write source.install_path.to_s
+end
+File.open("#{meta}/require-paths", "w") do |f|
+  f.write spec.require_paths.join(" ")
+end
+File.open("#{meta}/executables", "w") do |f|
+  f.write spec.executables.join(" ")
+end
+
+# make the lib available during bundler/git installs
+File.open("#{out}/nix-support/setup-hook", "a") do |f|
+  spec.require_paths.each do |dir|
+    f.puts("addToSearchPath RUBYLIB #{source.install_path}/#{dir}")
+  end
+end
diff --git a/pkgs/development/interpreters/ruby/bundix/Gemfile b/pkgs/development/interpreters/ruby/bundix/Gemfile
deleted file mode 100644
index 4899cafc373..00000000000
--- a/pkgs/development/interpreters/ruby/bundix/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "http://rubygems.org"
-gem "bundix",
-  :git => "https://github.com/cstrahan/bundix.git",
-  :ref => "v1.0.3"
diff --git a/pkgs/development/interpreters/ruby/bundix/Gemfile.lock b/pkgs/development/interpreters/ruby/bundix/Gemfile.lock
deleted file mode 100644
index f241a3bafd4..00000000000
--- a/pkgs/development/interpreters/ruby/bundix/Gemfile.lock
+++ /dev/null
@@ -1,18 +0,0 @@
-GIT
-  remote: https://github.com/cstrahan/bundix.git
-  revision: c879cf901ff8084b4c97aaacfb5ecbdb0f95cc03
-  ref: v1.0.3
-  specs:
-    bundix (1.0.2)
-      thor (~> 0.19.1)
-
-GEM
-  remote: http://rubygems.org/
-  specs:
-    thor (0.19.1)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  bundix!
diff --git a/pkgs/development/interpreters/ruby/bundix/default.nix b/pkgs/development/interpreters/ruby/bundix/default.nix
index 0196adb8f4c..b5a49043c60 100644
--- a/pkgs/development/interpreters/ruby/bundix/default.nix
+++ b/pkgs/development/interpreters/ruby/bundix/default.nix
@@ -1,9 +1,20 @@
-{ ruby, bundlerEnv }:
+{ ruby, fetchgit, buildRubyGem, bundler }:
 
-bundlerEnv {
-  name = "bundix";
-  inherit ruby;
-  gemset = ./gemset.nix;
-  gemfile = ./Gemfile;
-  lockfile = ./Gemfile.lock;
+let
+  thor = buildRubyGem {
+    gemName = "thor";
+    version = "0.19.1";
+    type = "gem";
+    sha256 = "08p5gx18yrbnwc6xc0mxvsfaxzgy2y9i78xq7ds0qmdm67q39y4z";
+  };
+
+in buildRubyGem {
+  gemName = "bundix";
+  version = "1.0.4";
+  gemPath = [ thor bundler ];
+  src = fetchgit {
+    url = "https://github.com/cstrahan/bundix.git";
+    rev = "6dcf1f71c61584f5c9b919ee9df7b0c554862076";
+    sha256 = "1w17bvc9srcgr4ry81ispcj35g9kxihbyknmqp8rnd4h5090b7b2";
+  };
 }
diff --git a/pkgs/development/interpreters/ruby/bundix/gemset.nix b/pkgs/development/interpreters/ruby/bundix/gemset.nix
deleted file mode 100644
index f8f6546671d..00000000000
--- a/pkgs/development/interpreters/ruby/bundix/gemset.nix
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "bundix" = {
-    version = "1.0.2";
-    source = {
-      type = "git";
-      url = "https://github.com/cstrahan/bundix.git";
-      rev = "c879cf901ff8084b4c97aaacfb5ecbdb0f95cc03";
-      sha256 = "05kmdnq4qa5h8l3asv05cjpnyplnqqx6hrqybj2cjlzmdxnkkgyj";
-      fetchSubmodules = false;
-    };
-    dependencies = [
-      "thor"
-    ];
-  };
-  "thor" = {
-    version = "0.19.1";
-    source = {
-      type = "gem";
-      sha256 = "08p5gx18yrbnwc6xc0mxvsfaxzgy2y9i78xq7ds0qmdm67q39y4z";
-    };
-  };
-}
\ No newline at end of file
diff --git a/pkgs/development/interpreters/ruby/bundler-env/default.nix b/pkgs/development/interpreters/ruby/bundler-env/default.nix
index 9fa6e52c455..c7570d815e3 100644
--- a/pkgs/development/interpreters/ruby/bundler-env/default.nix
+++ b/pkgs/development/interpreters/ruby/bundler-env/default.nix
@@ -1,312 +1,75 @@
 { stdenv, runCommand, writeText, writeScript, writeScriptBin, ruby, lib
-, callPackage, defaultGemConfig, fetchurl, fetchgit, buildRubyGem , bundler_HEAD
+, callPackage, defaultGemConfig, fetchurl, fetchgit, buildRubyGem, buildEnv
+, rubygems
 , git
+, makeWrapper
+, bundler
+, tree
 }@defs:
 
-# This is a work-in-progress.
-# The idea is that his will replace load-ruby-env.nix.
-
 { name, gemset, gemfile, lockfile, ruby ? defs.ruby, gemConfig ? defaultGemConfig
-, enableParallelBuilding ? false # TODO: this might not work, given the env-var shinanigans.
-, postInstall ? null
-, documentation ? false
+, postBuild ? null
+, document ? []
 , meta ? {}
+, ignoreCollisions ? false
 , ...
 }@args:
 
 let
 
   shellEscape = x: "'${lib.replaceChars ["'"] [("'\\'" + "'")] x}'";
-  const = x: y: x;
-  bundler = bundler_HEAD.override { inherit ruby; };
-  inherit (builtins) attrValues;
-
-  gemName = attrs: "${attrs.name}-${attrs.version}.gem";
-
-  fetchers.path = attrs: attrs.source.path;
-  fetchers.gem = attrs: fetchurl {
-    url = "${attrs.source.source or "https://rubygems.org"}/downloads/${gemName attrs}";
-    inherit (attrs.source) sha256;
-  };
-  fetchers.git = attrs: fetchgit {
-    inherit (attrs.source) url rev sha256 fetchSubmodules;
-    leaveDotGit = true;
-  };
-
-  applySrc = attrs:
-    attrs // {
-      src = (fetchers."${attrs.source.type}" attrs);
-    };
-
+  importedGemset = import gemset;
   applyGemConfigs = attrs:
-    if gemConfig ? "${attrs.name}"
-    then attrs // gemConfig."${attrs.name}" attrs
-    else attrs;
-
-  needsPatch = attrs:
-    (attrs ? patches) || (attrs ? prePatch) || (attrs ? postPatch);
-
-  # patch a gem or source tree.
-  # for gems, the gem is unpacked, patched, and then repacked.
-  # see: https://github.com/fedora-ruby/gem-patch/blob/master/lib/rubygems/patcher.rb
-  applyPatches = attrs:
-    if !needsPatch attrs
-    then attrs
-    else attrs // { src =
-      stdenv.mkDerivation {
-        name = gemName attrs;
-        phases = [ "unpackPhase" "patchPhase" "installPhase" ];
-        buildInputs = [ ruby ] ++ attrs.buildInputs or [];
-        patches = attrs.patches or [ ];
-        prePatch = attrs.prePatch or "true";
-        postPatch = attrs.postPatch or "true";
-        unpackPhase = ''
-          runHook preUnpack
-
-          if [[ -f ${attrs.src} ]]; then
-            isGem=1
-            # we won't know the name of the directory that RubyGems creates,
-            # so we'll just use a glob to find it and move it over.
-            gem unpack ${attrs.src} --target=container
-            cp -r container/* contents
-            rm -r container
-          else
-            cp -r ${attrs.src} contents
-            chmod -R +w contents
-          fi
-
-          cd contents
-          runHook postUnpack
-        '';
-        installPhase = ''
-          runHook preInstall
-
-          if [[ -n "$isGem" ]]; then
-            ${writeScript "repack.rb" ''
-              #!${ruby}/bin/ruby
-              require 'rubygems'
-              require 'rubygems/package'
-              require 'fileutils'
-
-              Encoding.default_internal = Encoding::UTF_8
-              Encoding.default_external = Encoding::UTF_8
-
-              if Gem::VERSION < '2.0'
-                load "${./package-1.8.rb}"
-              end
-
-              out = ENV['out']
-              files = Dir['**/{.[^\.]*,*}']
-
-              package = Gem::Package.new("${attrs.src}")
-              patched_package = Gem::Package.new(package.spec.file_name)
-              patched_package.spec = package.spec.clone
-              patched_package.spec.files = files
-
-              patched_package.build(false)
-
-              FileUtils.cp(patched_package.spec.file_name, out)
-            ''}
-          else
-            cp -r . $out
-          fi
-
-          runHook postInstall
-        '';
-      };
-    };
-
-  instantiate = (attrs:
-    applyPatches (applyGemConfigs (applySrc attrs))
-  );
-
-  instantiated = lib.flip lib.mapAttrs (import gemset) (name: attrs:
-    instantiate (attrs // { inherit name; })
+    (if gemConfig ? "${attrs.gemName}"
+    then attrs // gemConfig."${attrs.gemName}" attrs
+    else attrs);
+  configuredGemset = lib.flip lib.mapAttrs importedGemset (name: attrs:
+    applyGemConfigs (attrs // { gemName = name; })
   );
-
-  needsPreInstall = attrs:
-    (attrs ? preInstall) || (attrs ? buildInputs) || (attrs ? nativeBuildInputs);
-
-  # TODO: support cross compilation? look at stdenv/generic/default.nix.
-  runPreInstallers = lib.fold (next: acc:
-    if !needsPreInstall next
-    then acc
-    else acc + ''
-      ${writeScript "${next.name}-pre-install" ''
-        #!${stdenv.shell}
-
-        export nativeBuildInputs="${toString ((next.nativeBuildInputs or []) ++ (next.buildInputs or []))}"
-
-        source ${stdenv}/setup
-
-        header "running pre-install script for ${next.name}"
-
-        ${next.preInstall or ""}
-
-        ${ruby}/bin/ruby -e 'print ENV.inspect' > env/${next.name}
-
-        stopNest
-      ''}
-    ''
-  ) "" (attrValues instantiated);
-
-  # copy *.gem to ./gems
-  copyGems = lib.fold (next: acc:
-    if next.source.type == "gem"
-    then acc + "cp ${next.src} gems/${gemName next}\n"
-    else acc
-  ) "" (attrValues instantiated);
-
-  runRuby = name: env: command:
-    runCommand name env ''
-      ${ruby}/bin/ruby ${writeText name command}
-    '';
-
-  # TODO: include json_pure, so the version of ruby doesn't matter.
-  # not all rubies have support for JSON built-in,
-  # so we'll convert JSON to ruby expressions.
-  json2rb = writeScript "json2rb" ''
-    #!${ruby}/bin/ruby
-    begin
-      require 'json'
-    rescue LoadError => ex
-      require 'json_pure'
-    end
-
-    puts JSON.parse(STDIN.read).inspect
-  '';
-
-  # dump the instantiated gemset as a ruby expression.
-  serializedGemset = runCommand "gemset.rb" { json = builtins.toJSON instantiated; } ''
-    printf '%s' "$json" | ${json2rb} > $out
-  '';
-
-  # this is a mapping from a source type and identifier (uri/path/etc)
-  # to the pure store path.
-  # we'll use this from the patched bundler to make fetching sources pure.
-  sources = runRuby "sources.rb" { gemset = serializedGemset; } ''
-    out    = ENV['out']
-    gemset = eval(File.read(ENV['gemset']))
-
-    sources = {
-      "git"  => { },
-      "path" => { },
-      "gem"  => { },
-      "svn"  => { }
-    }
-
-    gemset.each_value do |spec|
-      type = spec["source"]["type"]
-      val = spec["src"]
-      key =
-        case type
-        when "gem"
-          spec["name"]
-        when "git"
-          spec["source"]["url"]
-        when "path"
-          spec["source"]["originalPath"]
-        when "svn"
-          nil # TODO
-        end
-
-      sources[type][key] = val if key
-    end
-
-    File.open(out, "wb") do |f|
-      f.print sources.inspect
-    end
+  hasBundler = builtins.hasAttr "bundler" importedGemset;
+  bundler = if hasBundler then gems.bundler else defs.bundler.override (attrs: { inherit ruby; });
+  rubygems = defs.rubygems.override (attrs: { inherit ruby; });
+  gems = lib.flip lib.mapAttrs configuredGemset (name: attrs:
+    buildRubyGem ((removeAttrs attrs ["source"]) // attrs.source // {
+      inherit ruby rubygems;
+      gemName = name;
+      gemPath = map (gemName: gems."${gemName}") (attrs.dependencies or []);
+    }));
+  # We have to normalize the Gemfile.lock, otherwise bundler tries to be
+  # helpful by doing so at run time, causing executables to immediately bail
+  # out. Yes, I'm serious.
+  confFiles = runCommand "gemfile-and-lockfile" {} ''
+    mkdir -p $out
+    cp ${gemfile} $out/Gemfile
+    cp ${lockfile} $out/Gemfile.lock
+
+    cd $out
+    chmod +w Gemfile.lock
+    source ${rubygems}/nix-support/setup-hook
+    export GEM_PATH=${bundler}/${ruby.gemPath}
+    ${ruby}/bin/ruby -rubygems -e \
+      "require 'bundler'; Bundler.definition.lock('Gemfile.lock')"
   '';
-
-  # rewrite PATH sources to point into the nix store.
-  purifiedLockfile = runRuby "purifiedLockfile" {} ''
-    out     = ENV['out']
-    sources = eval(File.read("${sources}"))
-    paths   = sources["path"]
-
-    lockfile = File.read("${lockfile}")
-
-    paths.each_pair do |impure, pure|
-      lockfile.gsub!(/^  remote: #{Regexp.escape(impure)}/, "  remote: #{pure}")
-    end
-
-    File.open(out, "wb") do |f|
-      f.print lockfile
-    end
-  '';
-
-  needsBuildFlags = attrs: attrs ? buildFlags;
-
-  mkBuildFlags = spec:
-    "export BUNDLE_BUILD__${lib.toUpper spec.name}='${lib.concatStringsSep " " (map shellEscape spec.buildFlags)}'";
-
-  allBuildFlags =
-    lib.concatStringsSep "\n"
-      (map mkBuildFlags
-        (lib.filter needsBuildFlags (attrValues instantiated)));
-
-  derivation = stdenv.mkDerivation {
-    inherit name;
-
-    buildInputs = [
-      ruby
-      bundler
-      git
-    ] ++ args.buildInputs or [];
-
-    phases = [ "installPhase" "fixupPhase" ];
-
-    outputs = [
-      "out"    # the installed libs/bins
-      "bundle" # supporting files for bundler
-    ];
-
-    installPhase = ''
-      mkdir -p $bundle
-      export BUNDLE_GEMFILE=$bundle/Gemfile
-      cp ${gemfile} $BUNDLE_GEMFILE
-      cp ${purifiedLockfile} $BUNDLE_GEMFILE.lock
-
-      export NIX_GEM_SOURCES=${sources}
-      export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath}
-
-      export GEM_HOME=$out/${ruby.gemPath}
-      export GEM_PATH=$NIX_BUNDLER_GEMPATH:$GEM_HOME
-      mkdir -p $GEM_HOME
-
-      ${allBuildFlags}
-
-      mkdir gems
-      cp ${bundler}/${bundler.ruby.gemPath}/cache/bundler-*.gem gems
-      ${copyGems}
-
-      ${lib.optionalString (!documentation) ''
-        mkdir home
-        HOME="$(pwd -P)/home"
-        echo "gem: --no-rdoc --no-ri" > $HOME/.gemrc
-      ''}
-
-      mkdir env
-      ${runPreInstallers}
-
-      mkdir $out/bin
-      cp ${./monkey_patches.rb} monkey_patches.rb
-      export RUBYOPT="-rmonkey_patches.rb -I $(pwd -P)"
-      bundler install --frozen --binstubs ${lib.optionalString enableParallelBuilding "--jobs $NIX_BUILD_CORES"}
-      RUBYOPT=""
-
-      runHook postInstall
-    '';
-
-    inherit postInstall;
-
+  envPaths = lib.attrValues gems ++ lib.optional (!hasBundler) bundler;
+  bundlerEnv = buildEnv {
+    inherit name ignoreCollisions;
+    paths = envPaths;
+    pathsToLink = [ "/lib" ];
+    postBuild = ''
+      source ${rubygems}/nix-support/setup-hook
+
+      ${ruby}/bin/ruby ${./gen-bin-stubs.rb} \
+        "${ruby}/bin/ruby" \
+        "${confFiles}/Gemfile" \
+        "$out/${ruby.gemPath}" \
+        "${bundler}/${ruby.gemPath}" \
+        ${shellEscape (toString envPaths)}
+    '' + lib.optionalString (postBuild != null) postBuild;
     passthru = {
-      inherit ruby;
-      inherit bundler;
-
+      inherit ruby bundler meta gems;
       env = let
         irbrc = builtins.toFile "irbrc" ''
-          if not ENV["OLD_IRBRC"].empty?
+          if !(ENV["OLD_IRBRC"].nil? || ENV["OLD_IRBRC"].empty?)
             require ENV["OLD_IRBRC"]
           end
           require 'rubygems'
@@ -314,12 +77,12 @@ let
         '';
         in stdenv.mkDerivation {
           name = "interactive-${name}-environment";
-          nativeBuildInputs = [ ruby derivation ];
+          nativeBuildInputs = [ ruby bundlerEnv ];
           shellHook = ''
-            export BUNDLE_GEMFILE=${derivation.bundle}/Gemfile
-            export GEM_HOME=${derivation}/${ruby.gemPath}
-            export NIX_BUNDLER_GEMPATH=${bundler}/${ruby.gemPath}
-            export GEM_PATH=$NIX_BUNDLER_GEMPATH:$GEM_HOME
+            export BUNDLE_GEMFILE=${confFiles}/Gemfile
+            export BUNDLE_PATH=${bundlerEnv}/${ruby.gemPath}
+            export GEM_HOME=${bundlerEnv}/${ruby.gemPath}
+            export GEM_PATH=${bundlerEnv}/${ruby.gemPath}
             export OLD_IRBRC="$IRBRC"
             export IRBRC=${irbrc}
           '';
@@ -331,8 +94,8 @@ let
           '';
         };
     };
-
-    inherit meta;
   };
 
-in derivation
+in
+
+bundlerEnv
diff --git a/pkgs/development/interpreters/ruby/bundler-env/gen-bin-stubs.rb b/pkgs/development/interpreters/ruby/bundler-env/gen-bin-stubs.rb
new file mode 100644
index 00000000000..fac9c9ad944
--- /dev/null
+++ b/pkgs/development/interpreters/ruby/bundler-env/gen-bin-stubs.rb
@@ -0,0 +1,46 @@
+require 'rbconfig'
+require 'rubygems'
+require 'rubygems/specification'
+require 'fileutils'
+
+# args/settings
+out = ENV["out"]
+ruby = ARGV[0]
+gemfile = ARGV[1]
+bundle_path = ARGV[2]
+bundler_gem_path = ARGV[3]
+paths = ARGV[4].split
+
+# generate binstubs
+FileUtils.mkdir_p("#{out}/bin")
+paths.each do |path|
+  next unless File.directory?("#{path}/nix-support/gem-meta")
+
+  name = File.read("#{path}/nix-support/gem-meta/name")
+  executables = File.read("#{path}/nix-support/gem-meta/executables").split
+  executables.each do |exe|
+    File.open("#{out}/bin/#{exe}", "w") do |f|
+      f.write(<<-EOF)
+#!#{ruby}
+#
+# This file was generated by Nix.
+#
+# The application '#{exe}' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+ENV["BUNDLE_GEMFILE"] = "#{gemfile}"
+ENV["BUNDLE_PATH"] = "#{bundle_path}"
+
+gem_path = ENV["GEM_PATH"]
+ENV["GEM_PATH"] = "\#{gem_path}\#{":" unless gem_path.nil? || gem_path.empty?}#{bundler_gem_path}"
+
+require 'rubygems'
+require 'bundler/setup'
+
+load Gem.bin_path(#{name.inspect}, #{exe.inspect})
+EOF
+      FileUtils.chmod("+x", "#{out}/bin/#{exe}")
+    end
+  end
+end
diff --git a/pkgs/development/interpreters/ruby/bundler-env/monkey_patches.rb b/pkgs/development/interpreters/ruby/bundler-env/monkey_patches.rb
deleted file mode 100644
index f68a20212ce..00000000000
--- a/pkgs/development/interpreters/ruby/bundler-env/monkey_patches.rb
+++ /dev/null
@@ -1,246 +0,0 @@
-require 'bundler'
-
-# Undo the RUBYOPT trickery.
-opt = ENV['RUBYOPT'].dup
-opt.gsub!(/-rmonkey_patches.rb -I [^ ]*/, '')
-ENV['RUBYOPT'] = opt
-
-Bundler.module_eval do
-  class << self
-    # mappings from original uris to store paths.
-    def nix_gem_sources
-      @nix_gem_sources ||=
-        begin
-          src = ENV['NIX_GEM_SOURCES']
-          eval(Bundler.read_file(src))
-        end
-    end
-
-    # extract the gemspecs from the gems pulled from Rubygems.
-    def nix_gemspecs
-      @nix_gemspecs ||= Dir.glob("gems/*.gem").map do |path|
-        Bundler.rubygems.spec_from_gem(path)
-      end
-    end
-
-    # swap out ENV
-    def nix_with_env(env, &block)
-      if env
-        old_env = ENV.to_hash
-        begin
-          ENV.replace(env)
-          block.call
-        ensure
-          ENV.replace(old_env)
-        end
-      else
-        block.call
-      end
-    end
-
-    # map a git uri to a fetchgit store path.
-    def nix_git(uri)
-      Pathname.new(nix_gem_sources["git"][uri])
-    end
-  end
-end
-
-Bundler::Source::Git::GitProxy.class_eval do
-  def checkout
-    unless path.exist?
-      FileUtils.mkdir_p(path.dirname)
-      FileUtils.cp_r(Bundler.nix_git(@uri).join(".git"), path)
-      system("chmod -R +w #{path}")
-    end
-  end
-
-  def copy_to(destination, submodules=false)
-    unless File.exist?(destination.join(".git"))
-      FileUtils.mkdir_p(destination.dirname)
-      FileUtils.cp_r(Bundler.nix_git(@uri), destination)
-      system("chmod -R +w #{destination}")
-    end
-  end
-end
-
-Bundler::Fetcher.class_eval do
-  def use_api
-    true
-  end
-
-  def fetch_dependency_remote_specs(gem_names)
-    Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
-    deps_list = []
-
-    spec_list = gem_names.map do |name|
-      spec = Bundler.nix_gemspecs.detect {|spec| spec.name == name }
-      if spec.nil?
-        msg = "WARNING: Could not find gemspec for '#{name}'"
-        Bundler.ui.warn msg
-        nil
-      else
-        dependencies = spec.dependencies.
-          select {|dep| dep.type != :development}.
-          map do |dep|
-            deps_list << dep.name
-            dep
-          end
-
-        [spec.name, spec.version, spec.platform, dependencies]
-      end
-    end
-
-    spec_list.compact!
-
-    [spec_list, deps_list.uniq]
-  end
-end
-
-Bundler::Source::Rubygems.class_eval do
-  # We copy all gems into $PWD/gems, and this allows RubyGems to find those
-  # gems during installation.
-  def fetchers
-    @fetchers ||= [
-      Bundler::Fetcher.new(URI.parse("file://#{File.expand_path(Dir.pwd)}"))
-    ]
-  end
-
-  # Look-up gems that were originally from RubyGems.
-  def remote_specs
-    @remote_specs ||=
-      begin
-        lockfile = Bundler::LockfileParser.new(Bundler.read_file(Bundler.default_lockfile))
-        gem_names = lockfile.specs.
-          select {|spec| spec.source.is_a?(Bundler::Source::Rubygems)}.
-          map {|spec| spec.name}
-        idx = Bundler::Index.new
-        api_fetchers.each do |f|
-          Bundler.ui.info "Fetching source index from #{f.uri}"
-          idx.use f.specs(gem_names, self)
-        end
-        idx
-      end
-  end
-end
-
-Bundler::Installer.class_eval do
-
-  # WHY:
-  # This allows us to provide a typical Nix experience, where
-  # `buildInputs` and/or `preInstall` may set up the $PATH and other env-vars
-  # as needed. By swapping out the environment per install, we can have finer
-  # grained control than we would have otherwise.
-  #
-  # HOW:
-  # This is a wrapper around the original `install_gem_from_spec`.
-  # We expect that a "pre-installer" might exist at `pre-installers/<gem-name>`,
-  # and if it does, we execute it.
-  # The pre-installer is expected to dump its environment variables as a Ruby
-  # hash to `env/<gem-name>`.
-  # We then swap out the environment for the duration of the install,
-  # and then set it back to what it was originally.
-  alias original_install_gem_from_spec install_gem_from_spec
-  def install_gem_from_spec(spec, standalone = false, worker = 0)
-    env_dump = "env/#{spec.name}"
-    if File.exist?(env_dump)
-      env = eval(Bundler.read_file(env_dump))
-      unless env
-        Bundler.ui.error "The environment variables for #{spec.name} could not be loaded!"
-        exit 1
-      end
-      Bundler.nix_with_env(env) do
-        original_install_gem_from_spec(spec, standalone, worker)
-      end
-    else
-      original_install_gem_from_spec(spec, standalone, worker)
-    end
-  end
-
-  def generate_bundler_executable_stubs(spec, options = {})
-    return if spec.executables.empty?
-
-    out = ENV['out']
-
-    spec.executables.each do |executable|
-      next if executable == "bundle" || executable == "bundler"
-
-      binstub_path = "#{out}/bin/#{executable}"
-
-      File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
-        f.print <<-TEXT
-#!#{RbConfig.ruby}
-
-old_gemfile  = ENV["BUNDLE_GEMFILE"]
-old_gem_home = ENV["GEM_HOME"]
-old_gem_path = ENV["GEM_PATH"]
-
-ENV["BUNDLE_GEMFILE"] =
-  "#{ENV["BUNDLE_GEMFILE"]}"
-ENV["GEM_HOME"] =
-  "#{ENV["GEM_HOME"]}"
-ENV["GEM_PATH"] =
-  "#{ENV["NIX_BUNDLER_GEMPATH"]}:\#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}"
-
-require 'rubygems'
-require 'bundler/setup'
-
-ENV["BUNDLE_GEMFILE"] = old_gemfile
-ENV["GEM_HOME"]       = old_gem_home
-ENV["GEM_PATH"]       = old_gem_path
-
-load Gem.bin_path('#{spec.name}', '#{executable}')
-TEXT
-      end
-    end
-  end
-end
-
-Gem::Installer.class_eval do
-  # Make the wrappers automagically use bundler.
-  #
-  # Stage 1.
-  #   Set $BUNDLE_GEMFILE so bundler knows what gems to load.
-  #   Set $GEM_HOME to the installed gems, because bundler looks there for
-  #     non-Rubygems installed gems (e.g. git/svn/path sources).
-  #   Set $GEM_PATH to include both bundler and installed gems.
-  #
-  # Stage 2.
-  #   Setup bundler, locking down the gem versions.
-  #
-  # Stage 3.
-  #   Reset $BUNDLE_GEMFILE, $GEM_HOME, $GEM_PATH.
-  #
-  # Stage 4.
-  #   Run the actual executable.
-  def app_script_text(bin_file_name)
-    return <<-TEXT
-#!#{RbConfig.ruby}
-#
-# This file was generated by Nix's RubyGems.
-#
-# The application '#{spec.name}' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-old_gemfile  = ENV["BUNDLE_GEMFILE"]
-old_gem_home = ENV["GEM_HOME"]
-old_gem_path = ENV["GEM_PATH"]
-
-ENV["BUNDLE_GEMFILE"] =
-  "#{ENV["BUNDLE_GEMFILE"]}"
-ENV["GEM_HOME"] =
-  "#{ENV["GEM_HOME"]}"
-ENV["GEM_PATH"] =
-  "#{ENV["NIX_BUNDLER_GEMPATH"]}:\#{ENV["GEM_HOME"]}\#{old_gem_path ? ":\#{old_gem_path}" : ""}}"
-
-require 'rubygems'
-require 'bundler/setup'
-
-ENV["BUNDLE_GEMFILE"] = old_gemfile
-ENV["GEM_HOME"]       = old_gem_home
-ENV["GEM_PATH"]       = old_gem_path
-
-load Gem.bin_path('#{spec.name}', '#{bin_file_name}')
-TEXT
-  end
-end
diff --git a/pkgs/development/interpreters/ruby/bundler-env/package-1.8.rb b/pkgs/development/interpreters/ruby/bundler-env/package-1.8.rb
deleted file mode 100644
index 079b65f97ec..00000000000
--- a/pkgs/development/interpreters/ruby/bundler-env/package-1.8.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'rubygems/installer'
-require 'rubygems/builder'
-
-# Simulate RubyGems 2.0 behavior.
-
-module Gem::Package
-  def self.new(gem)
-    @gem = gem
-    self
-  end
-
-  def self.extract_files(dir)
-    installer = Gem::Installer.new @gem
-    installer.unpack(dir)
-  end
-
-  def self.build(skip_validation=false)
-    builder = Gem::Builder.new(spec)
-    builder.build
-  end
-
-  def self.spec=(spec)
-    @spec = spec
-  end
-
-  def self.spec
-    @spec ||= Gem::Installer.new(@gem).spec
-  end
-end
diff --git a/pkgs/development/interpreters/ruby/bundler.nix b/pkgs/development/interpreters/ruby/bundler.nix
index 3789170f57f..cdcd12990e2 100644
--- a/pkgs/development/interpreters/ruby/bundler.nix
+++ b/pkgs/development/interpreters/ruby/bundler.nix
@@ -1,17 +1,10 @@
 { buildRubyGem, makeWrapper, ruby, coreutils }:
 
-buildRubyGem {
-  name = "bundler-1.10.6";
-  namePrefix = "";
+buildRubyGem rec {
+  inherit ruby;
+  name = "${gemName}-${version}";
+  gemName = "bundler";
+  version = "1.10.6";
   sha256 = "1vlzfq0bkkj4jyq6av0y55mh5nj5n0f3mfbmmifwgkh44g8k6agv";
   dontPatchShebangs = true;
-  postInstall = ''
-    find $out -type f -perm -0100 | while read f; do
-      substituteInPlace $f \
-         --replace "/usr/bin/env" "${coreutils}/bin/env"
-    done
-
-    wrapProgram $out/bin/bundler \
-      --prefix PATH ":" ${ruby}/bin
-  '';
 }
diff --git a/pkgs/development/interpreters/ruby/gem.nix b/pkgs/development/interpreters/ruby/gem.nix
deleted file mode 100644
index bbc38226266..00000000000
--- a/pkgs/development/interpreters/ruby/gem.nix
+++ /dev/null
@@ -1,136 +0,0 @@
-{ lib, ruby, rubygemsFun, fetchurl, makeWrapper, git } @ defs:
-
-lib.makeOverridable (
-
-{ name
-, ruby ? defs.ruby
-, rubygems ? (rubygemsFun ruby)
-, stdenv ? ruby.stdenv
-, namePrefix ? "${lib.replaceStrings ["-"] ["_"] ruby.name}" + "-"
-, buildInputs ? []
-, doCheck ? false
-, dontBuild ? true
-, meta ? {}
-, gemPath ? []
-, ...} @ attrs:
-
-stdenv.mkDerivation (attrs // {
-  inherit ruby rubygems;
-  inherit doCheck;
-
-  buildInputs = [ ruby rubygems makeWrapper git ] ++ buildInputs;
-
-  name = namePrefix + name;
-
-  src = if attrs ? src
-    then attrs.src
-    else fetchurl {
-      url = "http://rubygems.org/downloads/${attrs.name}.gem";
-      inherit (attrs) sha256;
-    };
-
-  phases = [ "unpackPhase" "patchPhase" "buildPhase" "checkPhase" "installPhase" "fixupPhase" ];
-
-  # The source is expected to either be a gem package or a directory.
-  #
-  # - Gem packages are already built, so they don't even need to be unpacked.
-  #   They will skip the buildPhase.
-  # - A directory containing the sources will need to go through all of the
-  #   usual phases.
-  unpackPhase= ''
-    gemRegex="\.gem"
-    if [[ $src =~ $gemRegex ]]
-    then
-      runHook preUnpack
-      echo "source is a gem package, won't unpack"
-      gempkg=$src
-      dontBuild=1
-      runHook postUnpack
-    else
-      # Fall back to the original thing for everything else.
-      unpackPhase
-    fi
-  '';
-
-  checkPhase = "true";
-
-  buildPhase = ''
-    runHook preBuild
-
-    # TODO: Investigate. The complete working tree is touched by fetchgit.
-    if [ -d .git ]; then
-      git reset
-    fi
-
-    gemspec=$(find . -name '*.gemspec')
-    echo "found the following gemspecs:"
-    echo "$gemspec"
-
-    gemspec=$(echo "$gemspec" | head -n1)
-    echo "building $gemspec"
-
-    exec 3>&1
-    output=$(gem build $gemspec | tee >(cat - >&3))
-    exec 3>&-
-
-    gempkg=$(echo "$output" | grep -oP 'File: \K(.*)')
-
-    echo "gem package built: $gempkg"
-
-    runHook postBuild
-  '';
-
-  installPhase = ''
-    runHook preInstall
-
-    # NOTE: This does NOT build the unpacked gem, but installs $src directly.
-    #       Gems that have not been downloaded from rubygems.org may need a
-    #       separate buildPhase.
-    #       --ignore-dependencies is necessary as rubygems otherwise always
-    #       connects to the repository, thus breaking pure builds.
-    GEM_HOME=$out/${ruby.gemPath} \
-      gem install \
-      --local \
-      --force \
-      --http-proxy "http://nodtd.invalid" \
-      --ignore-dependencies \
-      --build-root "/" \
-      --backtrace \
-      $gempkg $gemFlags -- $buildFlags
-
-    # Yes, we really do need the $out/${ruby.gemPath}/cache.
-    # This is very important in order for many parts of RubyGems/Bundler to not blow up.
-    # See https://github.com/bundler/bundler/issues/3327
-
-    mkdir -p $out/bin
-    for prog in $out/${ruby.gemPath}/gems/*/bin/*; do
-      makeWrapper $prog $out/bin/$(basename $prog) \
-        --prefix GEM_PATH : "$out/${ruby.gemPath}:$GEM_PATH" \
-        --prefix RUBYLIB : "${rubygems}/lib" \
-        $extraWrapperFlags ''${extraWrapperFlagsArray[@]}
-    done
-        #--prefix RUBYOPT rubygems \
-
-    # looks like useless files which break build repeatability and consume space
-    rm -fv $out/${ruby.gemPath}/doc/*/*/created.rid || true
-    rm -fv $out/${ruby.gemPath}/gems/*/ext/*/mkmf.log || true
-
-    mkdir -p $out/nix-support
-
-    cat > $out/nix-support/setup-hook <<EOF
-    if [[ "\$GEM_PATH" != *$out* ]]; then
-      addToSearchPath GEM_PATH $out/${ruby.gemPath}
-    fi
-    EOF
-
-    runHook postInstall
-  '';
-
-  propagatedBuildInputs = gemPath;
-  propagatedUserEnvPkgs = gemPath;
-
-  passthru.isRubyGem = true;
-  inherit meta;
-})
-
-)
diff --git a/pkgs/development/interpreters/ruby/bundler-env/default-gem-config.nix b/pkgs/development/interpreters/ruby/gemconfig/default.nix
index 36925bcdf55..ae8c0ee6e6a 100644
--- a/pkgs/development/interpreters/ruby/bundler-env/default-gem-config.nix
+++ b/pkgs/development/interpreters/ruby/gemconfig/default.nix
@@ -20,7 +20,7 @@
 { lib, fetchurl, writeScript, ruby, kerberos, libxml2, libxslt, python, stdenv, which
 , libiconv, postgresql, v8_3_16_14, clang, sqlite, zlib, imagemagick
 , pkgconfig , ncurses, xapian, gpgme, utillinux, fetchpatch, tzdata, icu, libffi
-, cmake, libssh2, openssl, mysql, darwin
+, cmake, libssh2, openssl, mysql, darwin, git, perl, gecode_3, curl
 }:
 
 let
@@ -32,6 +32,14 @@ in
     buildInputs = [ which icu zlib ];
   };
 
+  dep-selector-libgecode = attrs: {
+    USE_SYSTEM_GECODE = true;
+    postInstall = ''
+      installPath=$(cat $out/nix-support/gem-meta/install-path)
+      sed -i $installPath/lib/dep-selector-libgecode.rb -e 's@VENDORED_GECODE_DIR =.*@VENDORED_GECODE_DIR = "${gecode_3}"@'
+    '';
+  };
+
   ffi = attrs: {
     buildInputs = [ libffi pkgconfig ];
   };
@@ -40,11 +48,12 @@ in
     buildInputs = [ gpgme ];
   };
 
+  # note that you need version >= v3.16.14.8,
+  # otherwise the gem will fail to link to the libv8 binary.
+  # see: https://github.com/cowboyd/libv8/pull/161
   libv8 = attrs: {
     buildInputs = [ which v8 python ];
-    buildFlags = [
-      "--with-system-v8=true"
-    ];
+    buildFlags = [ "--with-system-v8=true" ];
   };
 
   mysql2 = attrs: {
@@ -73,12 +82,20 @@ in
     buildInputs = lib.optional stdenv.isDarwin darwin.libobjc;
   };
 
+  patron = attrs: {
+    buildInputs = [ curl ];
+  };
+
   pg = attrs: {
     buildFlags = [
       "--with-pg-config=${postgresql}/bin/pg_config"
     ];
   };
 
+  puma = attrs: {
+    buildInputs = [ openssl ];
+  };
+
   rmagick = attrs: {
     buildInputs = [ imagemagick pkgconfig ];
   };
@@ -95,6 +112,7 @@ in
   };
 
   sup = attrs: {
+    dontBuild = false;
     # prevent sup from trying to dynamically install `xapian-ruby`.
     postPatch = ''
       cp ${./mkrf_conf_xapian.rb} ext/mkrf_conf_xapian.rb
@@ -118,6 +136,7 @@ in
   };
 
   tzinfo = attrs: {
+    dontBuild = false;
     postPatch = ''
       substituteInPlace lib/tzinfo/zoneinfo_data_source.rb \
         --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo"
@@ -130,6 +149,7 @@ in
 
   xapian-ruby = attrs: {
     # use the system xapian
+    dontBuild = false;
     buildInputs = [ xapian pkgconfig zlib ];
     postPatch = ''
       cp ${./xapian-Rakefile} Rakefile
diff --git a/pkgs/development/interpreters/ruby/bundler-env/mkrf_conf_xapian.rb b/pkgs/development/interpreters/ruby/gemconfig/mkrf_conf_xapian.rb
index e19f06e23ac..e19f06e23ac 100644
--- a/pkgs/development/interpreters/ruby/bundler-env/mkrf_conf_xapian.rb
+++ b/pkgs/development/interpreters/ruby/gemconfig/mkrf_conf_xapian.rb
diff --git a/pkgs/development/interpreters/ruby/bundler-env/xapian-Rakefile b/pkgs/development/interpreters/ruby/gemconfig/xapian-Rakefile
index 9f0b8e72f08..9f0b8e72f08 100644
--- a/pkgs/development/interpreters/ruby/bundler-env/xapian-Rakefile
+++ b/pkgs/development/interpreters/ruby/gemconfig/xapian-Rakefile
diff --git a/pkgs/development/interpreters/ruby/load-ruby-env.nix b/pkgs/development/interpreters/ruby/load-ruby-env.nix
deleted file mode 100644
index c4356ed5f50..00000000000
--- a/pkgs/development/interpreters/ruby/load-ruby-env.nix
+++ /dev/null
@@ -1,69 +0,0 @@
-{ ruby, lib, callPackage, gemFixes, fetchurl, fetchgit, buildRubyGem }@defs:
-
-# This function builds a set of gems. You first convert your Gemfile to an attrset
-# called a "gemset", and then use this function to build the gemset.
-#
-# A gemset looks like the following:
-#
-#   {
-#     libv8 = {
-#       version = "3.16.14.7";
-#       src = {
-#         type = "gem";
-#         sha256 = "...";
-#       };
-#     };
-#     therubyracer = {
-#       version = "0.12.1";
-#       dependencies = [ "libv8" ];
-#       src = {
-#         type = "gem";
-#         sha256 = "...";
-#       };
-#     };
-#   }
-#
-# If you use these gems as build inputs, the GEM_PATH will be updated
-# appropriately, and command like `bundle exec` should work out of the box.
-
-{ gemset, ruby ? defs.ruby, fixes ? gemFixes }@args:
-
-let
-  const = x: y: x;
-
-  fetchers.path = attrs: attrs.src.path;
-  fetchers.gem = attrs: fetchurl {
-    url = "${attrs.src.source or "https://rubygems.org"}/downloads/${attrs.name}-${attrs.version}.gem";
-    inherit (attrs.src) sha256;
-  };
-  fetchers.git = attrs: fetchgit {
-    inherit (attrs.src) url rev sha256 fetchSubmodules;
-    leaveDotGit = true;
-  };
-
-  instantiate = (attrs:
-    let
-      defaultAttrs = {
-        name = "${attrs.name}-${attrs.version}";
-        inherit ruby gemPath;
-      };
-      gemPath = map (name: gemset''."${name}") (attrs.dependencies or []);
-      fixedAttrs = attrs // (fixes."${attrs.name}" or (const {})) attrs;
-      withSource = fixedAttrs //
-        (if (lib.isDerivation fixedAttrs.src || builtins.isString fixedAttrs.src)
-           then {}
-           else { src = (fetchers."${fixedAttrs.src.type}" fixedAttrs); });
-
-    in
-      buildRubyGem (withSource // defaultAttrs)
-  );
-
-  gemset' = if builtins.isAttrs gemset then gemset else import gemset;
-
-  gemset'' = lib.flip lib.mapAttrs gemset' (name: attrs:
-    if (lib.isDerivation attrs)
-      then attrs
-      else instantiate (attrs // { inherit name; })
-  );
-
-in gemset''
diff --git a/pkgs/development/interpreters/ruby/rubygems.nix b/pkgs/development/interpreters/ruby/rubygems.nix
index f4942b84091..b6ac0480897 100644
--- a/pkgs/development/interpreters/ruby/rubygems.nix
+++ b/pkgs/development/interpreters/ruby/rubygems.nix
@@ -1,37 +1,35 @@
-args @ { makeWrapper, ruby, ... }: with args;
+{ stdenv, lib, fetchurl, makeWrapper, ruby }:
 
-rec {
-  name = "rubygems-" + version;
+stdenv.mkDerivation rec {
+  name = "rubygems-${version}";
   version = "2.4.1";
   src = fetchurl {
     url = "http://production.cf.rubygems.org/rubygems/${name}.tgz";
     sha256 = "0cpr6cx3h74ykpb0cp4p4xg7a8j0bhz3sk271jq69l4mm4zy4h4f";
   };
 
+  patches = [ ./gem_hook.patch ];
+
   buildInputs = [ruby makeWrapper];
-  configureFlags = [];
 
-  doInstall = fullDepEntry (''
+  buildPhase = ":";
+
+  installPhase = ''
     ruby setup.rb --prefix=$out/
+
     wrapProgram $out/bin/gem --prefix RUBYLIB : $out/lib
-    find $out -type f -name "*.rb" | xargs sed -i "s@/usr/bin/env@$(type -p env)@g"
+
+    find $out -type f -name "*.rb" |
+      xargs sed -i "s@/usr/bin/env@$(type -p env)@g"
+
     mkdir -pv $out/nix-support
     cat > $out/nix-support/setup-hook <<EOF
     export RUBYOPT=rubygems
     addToSearchPath RUBYLIB $out/lib
-    EOF'') ["minInit" "addInputs" "doUnpack" "defEnsureDir"];
-
-  /* doConfigure should be specified separately */
-  phaseNames = ["doPatch" "doInstall"];
+    EOF
+  '';
 
   meta = {
-    description = "Ruby gems package collection";
-    longDescription = ''
-      Nix can create nix packages from gems.
-
-      To use it by installing gem-nix package.
-    '';
+    description = "A package management framework for Ruby";
   };
-
-  patches = [ ./gem_hook.patch ];
 }