Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


Subgroups #1863

wants to merge 2 commits into from

2 participants

Jimmy Cuadra Don't Add Me To Your Organization a.k.a The Travis Bot
Jimmy Cuadra

This patch adds a new method include_group to the Bundler DSL which allows one group to be included in another. The goal is to be able to group dependencies by their function, and later declare which functional groups should be loaded in which environments (e.g. production, development). It would look something like this simplified example:

# Default group

gem "rails"

# Functional groups

group :pry do
  gem "pry"
  gem "pry-rails"
  gem "pry-nav"

group :unit_tests do
  gem "rspec-rails"
  gem "factory_girl"

group :integration_tests do
  gem "capybara"
  gem "poltergeist"

# Environment groups

group :development do
  include_group :pry

group :test do
  include_group :unit_tests
  include_group :integration_tests

These composable groups allow for better organization of large Gemfiles.

This is my first foray into the Bundler code, so I'm opening this pull request very early to get feedback on whether or not the Bundler team would be receptive to this feature. If so, I'd like to get some feedback on my approach and what potential complications and edge cases I should consider and test for. The most obvious limitation so far is that subgroups must be defined before they are included in order to have any effect.

Thanks for your time!

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request fails (merged 971828dd into 8ef7b59).

Jimmy Cuadra

Updated the implementation so groups no longer have to be defined before they are included.

I see from the comment above that the build failed, but it doesn't seem to be related to this patch, as builds from other pull requests are failing the same examples.

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request fails (merged ebe655f8 into 52abb77).

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request passes (merged 09721c7 into c666754).

Jimmy Cuadra

Here's another approach I'm using now in a project. This uses a separate concept of a real "subgroup" but the implementation is actually much simpler. The advantage is that subgroups are lazily evaluated into real groups, allowing you to compose groups by functionality and install the bundle without groups you don't need in different environments.

Jimmy Cuadra jimmycuadra deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 53 additions and 0 deletions.
  1. +21 −0 lib/bundler/dsl.rb
  2. +32 −0 spec/install/gems/groups_spec.rb
21 lib/bundler/dsl.rb
@@ -23,6 +23,7 @@ def initialize
@sources = []
@dependencies = []
@groups = []
+ @subgroups = {}
@platforms = []
@env = nil
@ruby_version = nil
@@ -30,6 +31,7 @@ def initialize
def eval_gemfile(gemfile)
instance_eval(Bundler.read_file(gemfile.to_s), gemfile.to_s, 1)
+ resolve_subgroups
rescue SyntaxError => e
bt = e.message.split("\n")[1..-1]
raise GemfileError, ["Gemfile syntax error:", *bt].join("\n")
@@ -155,6 +157,13 @@ def group(*args, &blk)
args.each { @groups.pop }
+ def include_group(group)
+ @groups.each do |parent_group|
+ @subgroups[parent_group] ||= []
+ @subgroups[parent_group] << group unless @subgroups[parent_group].include?(group)
+ end
+ end
def platforms(*platforms)
@platforms.concat platforms
@@ -252,5 +261,17 @@ def _normalize_options(name, version, opts)
opts["group"] = groups
+ def resolve_subgroups
+ @subgroups.each do |parent_group, subgroups|
+ @dependencies.each do |dependency|
+ in_subgroup = subgroups.length != (subgroups - dependency.groups).length
+ if in_subgroup && !dependency.groups.include?(parent_group)
+ dependency.groups += [parent_group]
+ end
+ end
+ end
+ end
32 spec/install/gems/groups_spec.rb
@@ -265,4 +265,36 @@
+ describe "with subgroups" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "weakling"
+ group :sub1 do
+ gem "activesupport"
+ end
+ group :parent do
+ include_group :sub1
+ include_group :sub2
+ end
+ gem "thin", :groups => :sub2
+ G
+ end
+ it "sets up the subgroups' gems when a parent group is loaded" do
+ out = run("require 'activesupport'; puts ACTIVESUPPORT", :parent)
+ out.should eq('2.3.5')
+ out = run("require 'thin'; puts THIN", :parent)
+ out.should eq('1.0')
+ load_error_run <<-R, 'weakling', :parent
+ require 'weakling'
+ R
+ err.should == "ZOMG LOAD ERROR"
+ end
+ end
Something went wrong with that request. Please try again.