diff options
author | Dennis Suratna <dennis.suratna@gmail.com> | 2017-02-28 11:39:55 -0800 |
---|---|---|
committer | Dennis Suratna <dennis.suratna@gmail.com> | 2017-04-10 15:46:24 +0700 |
commit | a95d646e060a5f8ae36589673e369fdb37548a31 (patch) | |
tree | a9092d49768ac6357c48ea092d610a8bafbcf9e2 | |
parent | 32fb8327328789bdc911dccda4a59e99956c558c (diff) | |
download | bundler-a95d646e060a5f8ae36589673e369fdb37548a31.tar.gz |
Add command implementation
-rw-r--r-- | lib/bundler/cli.rb | 14 | ||||
-rw-r--r-- | lib/bundler/cli/add.rb | 18 | ||||
-rw-r--r-- | lib/bundler/injector.rb | 42 | ||||
-rw-r--r-- | spec/commands/add_spec.rb | 62 |
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 |