Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Commit

Permalink
Add support for bundle install --standalone, which will allow a bundl…
Browse files Browse the repository at this point in the history
…e to work without rubygems or bundler at runtime. This is useful for packaging up an app using something like warbler, rawr, or as a .app using MacRuby
  • Loading branch information
wycats committed Nov 17, 2010
1 parent 0476eac commit 9278bf5
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 2 deletions.
9 changes: 8 additions & 1 deletion lib/bundler/cli.rb
Expand Up @@ -154,6 +154,8 @@ def check
"Install using defaults tuned for deployment environments"
method_option "production", :type => :boolean, :banner =>
"Deprecated, please use --deployment instead"
method_option "standalone", :type => :array, :lazy_default => [], :banner =>
"Make a bundle that can work without the Bundler runtime"
def install
opts = options.dup
opts[:without] ||= []
Expand Down Expand Up @@ -210,8 +212,13 @@ def install
Bundler.settings[:path] = "vendor/bundle" if opts[:deployment]
Bundler.settings[:path] = opts[:path] if opts[:path]
Bundler.settings[:bin] = opts["binstubs"] if opts[:binstubs]
Bundler.settings[:path] = nil if opts[:system]
Bundler.settings[:path] = "vendor/bundle" if opts[:deployment]
Bundler.settings[:path] = opts[:path] if opts[:path]
Bundler.settings[:path] ||= "bundle" if opts[:standalone]
Bundler.settings[:bin] = opts["binstubs"] if opts[:binstubs]
Bundler.settings[:disable_shared_gems] = '1' if Bundler.settings[:path]
Bundler.settings.without = opts[:without] unless opts[:without].empty?
Bundler.settings.without = opts[:without] unless opts[:without].empty?
Bundler.ui.be_quiet! if opts[:quiet]

Installer.install(Bundler.root, Bundler.definition, opts)
Expand Down
32 changes: 32 additions & 0 deletions lib/bundler/installer.rb
Expand Up @@ -64,6 +64,7 @@ def run(options)
end

lock
generate_standalone(options[:standalone]) if options[:standalone]
end

private
Expand All @@ -81,5 +82,36 @@ def generate_bundler_executable_stubs(spec)
end
end
end

def generate_standalone(groups)
path = Bundler.settings[:path]
bundler_path = File.join(path, "bundler")
FileUtils.mkdir_p(bundler_path)

paths = []

if groups.empty?
specs = Bundler.definition.requested_specs
else
specs = Bundler.definition.specs_for groups.map { |g| g.to_sym }
end

specs.each do |spec|
next if spec.name == "bundler"

spec.require_paths.each do |path|
full_path = File.join(spec.full_gem_path, path)
paths << Pathname.new(full_path).relative_path_from(Bundler.root)
end
end

File.open File.join(bundler_path, "setup.rb"), "w" do |file|
lines = paths.map do |path|
%{$:.unshift "#{path}"}
end

file.puts lines.join("\n")
end
end
end
end
162 changes: 162 additions & 0 deletions spec/install/gems/standalone_spec.rb
@@ -0,0 +1,162 @@
require "spec_helper"

describe "bundle install --standalone" do
describe "with simple gems" do
before do
install_gemfile <<-G, :standalone => true
source "file://#{gem_repo1}"
gem "rails"
G
end

it "still makes the gems available to normal bundler" do
should_be_installed "actionpack 2.3.2", "rails 2.3.2"
end

it "generates a bundle/bundler/setup.rb" do
bundled_app("bundle/bundler/setup.rb").should exist
end

it "makes the gems available without bundler" do
ruby <<-RUBY, :no_lib => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "actionpack"
puts ACTIONPACK
RUBY

out.should == "2.3.2"
end

it "works on a different system" do
FileUtils.mv(bundled_app, "#{bundled_app}2")
Dir.chdir("#{bundled_app}2")

ruby <<-RUBY, :no_lib => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "actionpack"
puts ACTIONPACK
RUBY

out.should == "2.3.2"
end
end

describe "with a combination of gems and git repos" do
before do
build_git "devise", "1.0"

install_gemfile <<-G, :standalone => true
source "file://#{gem_repo1}"
gem "rails"
gem "devise", :git => "#{lib_path('devise-1.0')}"
G
end

it "still makes the gems available to normal bundler" do
should_be_installed "actionpack 2.3.2", "rails 2.3.2", "devise 1.0"
end

it "generates a bundle/bundler/setup.rb" do
bundled_app("bundle/bundler/setup.rb").should exist
end

it "makes the gems available without bundler" do
ruby <<-RUBY, :no_lib => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "devise"
require "actionpack"
puts DEVISE
puts ACTIONPACK
RUBY

out.should == "1.0\n2.3.2"
end
end

describe "with groups" do
before do
build_git "devise", "1.0"

install_gemfile <<-G, :standalone => true
source "file://#{gem_repo1}"
gem "rails"
group :test do
gem "rspec"
gem "rack-test"
end
G
end

it "makes the gems available without bundler" do
ruby <<-RUBY, :no_lib => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "actionpack"
require "spec"
require "rack/test"
puts ACTIONPACK
puts SPEC
puts RACK_TEST
RUBY

out.should == "2.3.2\n1.2.7\n1.0"
end

it "allows creating a standalone file with limited groups" do
bundle "install --standalone default"

ruby <<-RUBY, :no_lib => true, :expect_err => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "actionpack"
puts ACTIONPACK
require "spec"
RUBY

out.should == "2.3.2"
err.should =~ /no such file to load.*spec/
end

it "allows --without to limit the groups used in a standalone" do
bundle "install --standalone --without test"

ruby <<-RUBY, :no_lib => true, :expect_err => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "actionpack"
puts ACTIONPACK
require "spec"
RUBY

out.should == "2.3.2"
err.should =~ /no such file to load.*spec/
end

it "allows remembered --without to limit the groups used in a standalone" do
bundle "install --without test"
bundle "install --standalone"

ruby <<-RUBY, :no_lib => true, :expect_err => true
$:.unshift File.expand_path("bundle")
require "bundler/setup"
require "actionpack"
puts ACTIONPACK
require "spec"
RUBY

out.should == "2.3.2"
err.should =~ /no such file to load.*spec/
end
end
end
3 changes: 2 additions & 1 deletion spec/support/helpers.rb
Expand Up @@ -68,7 +68,8 @@ def ruby(ruby, options = {})
expect_err = options.delete(:expect_err)
env = (options.delete(:env) || {}).map{|k,v| "#{k}='#{v}' "}.join
ruby.gsub!(/["`\$]/) {|m| "\\#{m}" }
sys_exec(%{#{env}#{Gem.ruby} -I#{lib} -e "#{ruby}"}, expect_err)
lib_option = options[:no_lib] ? "" : " -I#{lib}"
sys_exec(%{#{env}#{Gem.ruby}#{lib_option} -e "#{ruby}"}, expect_err)
end

def gembin(cmd)
Expand Down

7 comments on commit 9278bf5

@joevandyk
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! This should knock off a few megs of ram from each ruby process (if rubygems isn't being loaded).

(right?)

@rtomayko
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forever

@steveklabnik
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty fantastic. Gems and Shoes are sometimes pretty rough... I can see this being ultra useful.

@fabiokung
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES! We do a lot of Debian packaging for our ruby applications (including rails) and this is definitely going to help a lot.

<3 Yehuda

@leobessa
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job!

@rikur
Copy link

@rikur rikur commented on 9278bf5 Dec 31, 2010

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about native extensions?

@mjwillson
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one! we're also doing debian packaging of a ruby-based app, and were keen to avoid bundler as a runtime dependency. This will really help.

Looking forward to v1.1...

Please sign in to comment.