summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Suratna <dennis.suratna@gmail.com>2017-02-28 11:39:55 -0800
committerDennis Suratna <dennis.suratna@gmail.com>2017-04-10 15:46:24 +0700
commita95d646e060a5f8ae36589673e369fdb37548a31 (patch)
treea9092d49768ac6357c48ea092d610a8bafbcf9e2
parent32fb8327328789bdc911dccda4a59e99956c558c (diff)
downloadbundler-a95d646e060a5f8ae36589673e369fdb37548a31.tar.gz
Add command implementation
-rw-r--r--lib/bundler/cli.rb14
-rw-r--r--lib/bundler/cli/add.rb18
-rw-r--r--lib/bundler/injector.rb42
-rw-r--r--spec/commands/add_spec.rb62
4 files changed, 123 insertions, 13 deletions
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 61c5427c90..bc9740fc9c 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -268,6 +268,20 @@ module Bundler
Binstubs.new(options, gems).run
end
+ desc "add GEM VERSION", "Add gem to Gemfile and run bundle install"
+ long_desc <<-D
+ Adds the specified gem to Gemfile (if valid) and run 'bundle install' in one step.
+ D
+ method_option "version", :aliases => "-v", :type => :string
+ method_option "group", :aliases => "-g", :type => :string
+ method_option "source", :aliases => "-s", :type => :string
+
+ map "a" => "add"
+ def add(gem_name)
+ require "bundler/cli/add"
+ Add.new(options.dup, gem_name).run
+ end
+
desc "outdated GEM [OPTIONS]", "list installed gems with newer versions available"
long_desc <<-D
Outdated lists the names and versions of gems that have a newer version available
diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb
new file mode 100644
index 0000000000..baf27c45d0
--- /dev/null
+++ b/lib/bundler/cli/add.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+require "bundler/cli/common"
+
+module Bundler
+ class CLI::Add
+ def initialize(options, gem_name)
+ @gem_name = gem_name
+ @options = options
+ @options[:group] = @options[:group].split(",").map(&:strip) if !@options[:group].nil? && !@options[:group].empty?
+ end
+
+ def run
+ dependency = Bundler::Dependency.new(@gem_name, @options[:version], @options)
+ Injector.inject([dependency], :conservative_versioning => @options[:version].nil?) # Perform conservative versioning only when version is not specified
+ Installer.install(Bundler.root, Bundler.definition)
+ end
+ end
+end
diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb
index 7c78486dac..f4043b1fc9 100644
--- a/lib/bundler/injector.rb
+++ b/lib/bundler/injector.rb
@@ -27,17 +27,18 @@ module Bundler
@new_deps -= builder.dependencies
# add new deps to the end of the in-memory Gemfile
- builder.eval_gemfile("injected gems", new_gem_lines) if @new_deps.any?
+ # Set conservative versioining to false because we want to let the resolver resolve the version first
+ builder.eval_gemfile("injected gems", build_gem_lines(false)) if @new_deps.any?
# resolve to see if the new deps broke anything
- definition = builder.to_definition(lockfile_path, {})
- definition.resolve_remotely!
+ @definition = builder.to_definition(lockfile_path, {})
+ @definition.resolve_remotely!
# since nothing broke, we can add those gems to the gemfile
- append_to(gemfile_path) if @new_deps.any?
+ append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @new_deps.any?
# since we resolved successfully, write out the lockfile
- definition.lock(Bundler.default_lockfile)
+ @definition.lock(Bundler.default_lockfile)
# return an array of the deps that we added
return @new_deps
@@ -47,24 +48,39 @@ module Bundler
private
- def new_gem_lines
+ def conservative_version(version)
+ return ">= 0" if version.nil?
+ segments = version.segments
+ seg_end_index = version >= Gem::Version.new("1.0") ? 1 : 2
+ "~> #{segments[0..seg_end_index].join(".")}"
+ end
+
+ def build_gem_lines(conservative_versioning)
@new_deps.map do |d|
name = "'#{d.name}'"
- requirement = ", '#{d.requirement}'"
+
+ if conservative_versioning
+ version = @definition.specs[d.name][0].version
+ requirement = ", '#{conservative_version(version)}'"
+ else
+ requirement = ", '#{d.requirement}'"
+ end
+
if d.groups != Array(:default)
group =
- if d.groups.size == 1
- ", :group => #{d.groups.inspect}"
- else
- ", :groups => #{d.groups.inspect}"
- end
+ if d.groups.size == 1
+ ", :group => #{d.groups.inspect}"
+ else
+ ", :groups => #{d.groups.inspect}"
+ end
end
+
source = ", :source => '#{d.source}'" unless d.source.nil?
%(gem #{name}#{requirement}#{group}#{source})
end.join("\n")
end
- def append_to(gemfile_path)
+ def append_to(gemfile_path, new_gem_lines)
gemfile_path.open("a") do |f|
f.puts
if @options["timestamp"] || @options["timestamp"].nil?
diff --git a/spec/commands/add_spec.rb b/spec/commands/add_spec.rb
new file mode 100644
index 0000000000..6192d5fa48
--- /dev/null
+++ b/spec/commands/add_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle add" do
+ before :each do
+ build_repo2 do
+ build_gem "foo", "1.1"
+ build_gem "foo", "2.0"
+ build_gem "baz", "1.2.3"
+ build_gem "bar", "0.12.3"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "weakling", "~> 0.0.1"
+ G
+ end
+
+ describe "without version specified" do
+ it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do
+ bundle "add 'bar'"
+ expect(bundled_app("Gemfile").read).to match(/gem 'bar', '~> 0.12.3'/)
+ expect(the_bundle).to include_gems "bar 0.12.3"
+ end
+
+ it "version requirement becomes ~> major.minor when resolved version is > 1.0" do
+ bundle "add 'baz'"
+ expect(bundled_app("Gemfile").read).to match(/gem 'baz', '~> 1.2'/)
+ expect(the_bundle).to include_gems "baz 1.2.3"
+ end
+ end
+
+ describe "with --version" do
+ it "adds dependency of specified version and runs install" do
+ bundle "add 'foo' --version='~> 1.0'"
+ expect(bundled_app("Gemfile").read).to match(/gem 'foo', '~> 1.0'/)
+ expect(the_bundle).to include_gems "foo 1.1"
+ end
+ end
+
+ describe "with --group" do
+ it "adds dependency for the specified group" do
+ bundle "add 'foo' --group='development'"
+ expect(bundled_app("Gemfile").read).to match(/gem 'foo', '~> 2.0', :group => \[:development\]/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+
+ it "adds dependency to more than one group" do
+ bundle "add 'foo' --group='development, test'"
+ expect(bundled_app("Gemfile").read).to match(/gem 'foo', '~> 2.0', :group => \[:development, :test\]/)
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
+ describe "with --source" do
+ it "adds dependency with specified source" do
+ bundle "add 'foo' --source='file://#{gem_repo2}'"
+ expect(bundled_app("Gemfile").read).to match(%r{gem 'foo', '~> 2.0', :source => 'file:\/\/#{gem_repo2}'})
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+end