diff options
author | The Bundler Bot <bot@bundler.io> | 2018-07-02 04:27:33 +0000 |
---|---|---|
committer | Colby Swandale <me@colby.fyi> | 2018-10-09 22:33:21 +1100 |
commit | a4f707d29d4536342dba2bd82f79686f1df5cae6 (patch) | |
tree | a6c12f70efe9db127e6bc7d7f6d42fdd2063bb6f | |
parent | 8f244e7aca8a7e2419018f4b4956edadc33afefe (diff) | |
download | bundler-a4f707d29d4536342dba2bd82f79686f1df5cae6.tar.gz |
Auto merge of #6513 - agrim123:agr-bundler-remove, r=indirect
Add `bundle remove`
Features of the command implemented:
- Multiple gems support
```bash
$ bundle remove rack rails
```
- Remove any empty block that might occur after removing the gem or otherwise present
Things yet to implement:
- Add `rm` alias. _Optional_
- [x] Add `--install` flag to remove gems from `.bundle`.
- [x] Handling multiple gems on the same line.
- [x] Handle gem spec
- [x] Handle eval_gemfile cases ([one](https://github.com/bundler/bundler/pull/6513#discussion_r195632603) case left)
Closes #6506
(cherry picked from commit 431a3f76868bdb9d4c94be25b28b0c7f26ee9f6e)
-rw-r--r-- | lib/bundler/cli.rb | 11 | ||||
-rw-r--r-- | lib/bundler/cli/remove.rb | 18 | ||||
-rw-r--r-- | lib/bundler/dependency.rb | 4 | ||||
-rw-r--r-- | lib/bundler/dsl.rb | 3 | ||||
-rw-r--r-- | lib/bundler/injector.rb | 167 | ||||
-rw-r--r-- | lib/bundler/shared_helpers.rb | 6 | ||||
-rw-r--r-- | man/bundle-remove.ronn | 23 | ||||
-rw-r--r-- | spec/commands/remove_spec.rb | 571 | ||||
-rw-r--r-- | spec/support/matchers.rb | 4 |
9 files changed, 793 insertions, 14 deletions
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index fda09dd0eb..dd4301959a 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -166,6 +166,17 @@ module Bundler Check.new(options).run end + desc "remove [GEM [GEM ...]]", "Removes gems from the Gemfile" + long_desc <<-D + Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If the gem is not found, Bundler prints a error message and if gem could not be removed due to any reason Bundler will display a warning. + D + method_option "install", :type => :boolean, :banner => + "Runs 'bundle install' after removing the gems from the Gemfile" + def remove(*gems) + require "bundler/cli/remove" + Remove.new(gems, options).run + end + desc "install [OPTIONS]", "Install the current environment to the system" long_desc <<-D Install will install all of the gems in the current bundle, making them available diff --git a/lib/bundler/cli/remove.rb b/lib/bundler/cli/remove.rb new file mode 100644 index 0000000000..cd6a2cec28 --- /dev/null +++ b/lib/bundler/cli/remove.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Bundler + class CLI::Remove + def initialize(gems, options) + @gems = gems + @options = options + end + + def run + raise InvalidOption, "Please specify gems to remove." if @gems.empty? + + Injector.remove(@gems, {}) + + Installer.install(Bundler.root, Bundler.definition) if @options["install"] + end + end +end diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 8fd59bd809..8840ad6a9c 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -7,8 +7,7 @@ require "bundler/rubygems_ext" module Bundler class Dependency < Gem::Dependency attr_reader :autorequire - attr_reader :groups - attr_reader :platforms + attr_reader :groups, :platforms, :gemfile PLATFORM_MAP = { :ruby => Gem::Platform::RUBY, @@ -88,6 +87,7 @@ module Bundler @platforms = Array(options["platforms"]) @env = options["env"] @should_include = options.fetch("should_include", true) + @gemfile = options["gemfile"] @autorequire = Array(options["require"] || []) if options.key?("require") end diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 8681163277..7a7667b4e6 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -16,7 +16,7 @@ module Bundler VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules - platform platforms type source install_if].freeze + platform platforms type source install_if gemfile].freeze attr_reader :gemspecs attr_accessor :dependencies @@ -93,6 +93,7 @@ module Bundler def gem(name, *args) options = args.last.is_a?(Hash) ? args.pop.dup : {} + options["gemfile"] = @gemfile version = args || [">= 0"] normalize_options(name, version, options) diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index fd7db8762b..2497439edd 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -2,13 +2,18 @@ module Bundler class Injector - def self.inject(new_deps, options = {}) - injector = new(new_deps, options) + def self.inject(deps, options = {}) + injector = new(deps, options) injector.inject(Bundler.default_gemfile, Bundler.default_lockfile) end - def initialize(new_deps, options = {}) - @new_deps = new_deps + def self.remove(gems, options = {}) + injector = new(gems, options) + injector.remove(Bundler.default_gemfile, Bundler.default_lockfile) + end + + def initialize(deps, options = {}) + @deps = deps @options = options end @@ -28,19 +33,18 @@ module Bundler builder.eval_gemfile(gemfile_path) # don't inject any gems that are already in the Gemfile - @new_deps -= builder.dependencies + @deps -= builder.dependencies # add new deps to the end of the in-memory Gemfile - # Set conservative versioning 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? + # Set conservative versioning to false because we want to let the resolver resolve the version first + builder.eval_gemfile("injected gems", build_gem_lines(false)) if @deps.any? # resolve to see if the new deps broke anything @definition = builder.to_definition(lockfile_path, {}) @definition.resolve_remotely! # since nothing broke, we can add those gems to the gemfile - append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @new_deps.any? + append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any? # since we resolved successfully, write out the lockfile @definition.lock(Bundler.default_lockfile) @@ -49,7 +53,21 @@ module Bundler Bundler.reset_paths! # return an array of the deps that we added - @new_deps + @deps + end + end + + # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies. + # @param [Pathname] lockfile_path The lockfile from which to remove dependencies. + # @return [Array] + def remove(gemfile_path, lockfile_path) + # remove gems from each gemfiles we have + Bundler.definition.gemfiles.each do |path| + deps = remove_deps(path) + + show_warning("No gems were removed from the gemfile.") if deps.empty? + + deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep, false)} was removed." } end end @@ -76,7 +94,7 @@ module Bundler end def build_gem_lines(conservative_versioning) - @new_deps.map do |d| + @deps.map do |d| name = d.name.dump requirement = if conservative_versioning @@ -101,5 +119,132 @@ module Bundler f.puts new_gem_lines end end + + # evalutes a gemfile to remove the specified gem + # from it. + def remove_deps(gemfile_path) + initial_gemfile = IO.readlines(gemfile_path) + + Bundler.ui.info "Removing gems from #{gemfile_path}" + + # evaluate the Gemfile we have + builder = Dsl.new + builder.eval_gemfile(gemfile_path) + + removed_deps = remove_gems_from_dependencies(builder, @deps, gemfile_path) + + # abort the opertion if no gems were removed + # no need to operate on gemfile furthur + return [] if removed_deps.empty? + + cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path) + + SharedHelpers.write_to_gemfile(gemfile_path, cleaned_gemfile) + + # check for errors + # including extra gems being removed + # or some gems not being removed + # and return the actual removed deps + cross_check_for_errors(gemfile_path, builder.dependencies, removed_deps, initial_gemfile) + end + + # @param [Dsl] builder Dsl object of current Gemfile. + # @param [Array] gems Array of names of gems to be removed. + # @param [Pathname] path of the Gemfile + # @return [Array] removed_deps Array of removed dependencies. + def remove_gems_from_dependencies(builder, gems, gemfile_path) + removed_deps = [] + + gems.each do |gem_name| + deleted_dep = builder.dependencies.find {|d| d.name == gem_name } + + if deleted_dep.nil? + raise GemfileError, "`#{gem_name}` is not specified in #{gemfile_path} so it could not be removed." + end + + builder.dependencies.delete(deleted_dep) + + removed_deps << deleted_dep + end + + removed_deps + end + + # @param [Array] gems Array of names of gems to be removed. + # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies. + def remove_gems_from_gemfile(gems, gemfile_path) + patterns = /gem\s+(['"])#{Regexp.union(gems)}\1|gem\s*\((['"])#{Regexp.union(gems)}\2\)/ + + # remove lines which match the regex + new_gemfile = IO.readlines(gemfile_path).reject {|line| line.match(patterns) } + + # remove lone \n and append them with other strings + new_gemfile.each_with_index do |_line, index| + if new_gemfile[index + 1] == "\n" + new_gemfile[index] += new_gemfile[index + 1] + new_gemfile.delete_at(index + 1) + end + end + + %w[group source env install_if].each {|block| remove_nested_blocks(new_gemfile, block) } + + new_gemfile.join.chomp + end + + # @param [Array] gemfile Array of gemfile contents. + # @param [String] block_name Name of block name to look for. + def remove_nested_blocks(gemfile, block_name) + nested_blocks = 0 + + # count number of nested blocks + gemfile.each_with_index {|line, index| nested_blocks += 1 if !gemfile[index + 1].nil? && gemfile[index + 1].include?(block_name) && line.include?(block_name) } + + while nested_blocks >= 0 + nested_blocks -= 1 + + gemfile.each_with_index do |line, index| + next unless !line.nil? && line.include?(block_name) + if gemfile[index + 1] =~ /^\s*end\s*$/ + gemfile[index] = nil + gemfile[index + 1] = nil + end + end + + gemfile.compact! + end + end + + # @param [Pathname] gemfile_path The Gemfile from which to remove dependencies. + # @param [Array] original_deps Array of original dependencies. + # @param [Array] removed_deps Array of removed dependencies. + # @param [Array] initial_gemfile Contents of original Gemfile before any operation. + def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_gemfile) + # evalute the new gemfile to look for any failure cases + builder = Dsl.new + builder.eval_gemfile(gemfile_path) + + # record gems which were removed but not requested + extra_removed_gems = original_deps - builder.dependencies + + # if some extra gems were removed then raise error + # and revert Gemfile to original + unless extra_removed_gems.empty? + SharedHelpers.write_to_gemfile(gemfile_path, initial_gemfile.join) + + raise InvalidOption, "Gems could not be removed. #{extra_removed_gems.join(", ")} would also have been removed. Bundler cannot continue." + end + + # record gems which could not be removed due to some reasons + errored_deps = builder.dependencies.select {|d| d.gemfile == gemfile_path } & removed_deps.select {|d| d.gemfile == gemfile_path } + + show_warning "#{errored_deps.map(&:name).join(", ")} could not be removed." unless errored_deps.empty? + + # return actual removed dependencies + removed_deps - errored_deps + end + + def show_warning(message) + Bundler.ui.info Bundler.ui.add_color(message, :yellow) + end end end diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index 505bd0843a..79883b5253 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -197,10 +197,12 @@ module Bundler def pretty_dependency(dep, print_source = false) msg = String.new(dep.name) msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default + if dep.is_a?(Bundler::Dependency) platform_string = dep.platforms.join(", ") msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY end + msg << " from the `#{dep.source}` source" if print_source && dep.source msg end @@ -223,6 +225,10 @@ module Bundler Digest(name) end + def write_to_gemfile(gemfile_path, contents) + filesystem_access(gemfile_path) {|g| File.open(g, "w") {|file| file.puts contents } } + end + private def validate_bundle_path diff --git a/man/bundle-remove.ronn b/man/bundle-remove.ronn new file mode 100644 index 0000000000..40a239b4a2 --- /dev/null +++ b/man/bundle-remove.ronn @@ -0,0 +1,23 @@ +bundle-remove(1) -- Removes gems from the Gemfile +=========================================================================== + +## SYNOPSIS + +`bundle remove [GEM [GEM ...]] [--install]` + +## DESCRIPTION + +Removes the given gems from the Gemfile while ensuring that the resulting Gemfile is still valid. If a gem cannot be removed, a warning is printed. If a gem is already absent from the Gemfile, and error is raised. + +## OPTIONS + +* `--install`: + Runs `bundle install` after the given gems have been removed from the Gemfile, which ensures that both the lockfile and the installed gems on disk are also updated to remove the given gem(s). + +Example: + +bundle remove rails + +bundle remove rails rack + +bundle remove rails rack --install diff --git a/spec/commands/remove_spec.rb b/spec/commands/remove_spec.rb new file mode 100644 index 0000000000..37594b1ece --- /dev/null +++ b/spec/commands/remove_spec.rb @@ -0,0 +1,571 @@ +# frozen_string_literal: true + +RSpec.describe "bundle remove" do + context "when no gems are specified" do + it "throws error" do + gemfile <<-G + source "file://#{gem_repo1}" + G + + bundle "remove" + + expect(out).to include("Please specify gems to remove.") + end + end + + context "when --install flag is specified" do + it "removes gems from .bundle" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + bundle! "remove rack --install" + + expect(out).to include("rack was removed.") + expect(the_bundle).to_not include_gems "rack" + end + end + + describe "remove single gem from gemfile" do + context "when gem is present in gemfile" do + it "shows success for removed gem" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when gem is not present in gemfile" do + it "shows warning for gem that could not be removed" do + gemfile <<-G + source "file://#{gem_repo1}" + G + + bundle "remove rack" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + end + end + end + + describe "remove mutiple gems from gemfile" do + context "when all gems are present in gemfile" do + it "shows success fir all removed gems" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + gem "rails" + G + + bundle! "remove rack rails" + + expect(out).to include("rack was removed.") + expect(out).to include("rails was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when some gems are not present in the gemfile" do + it "shows warning for those not present and success for those that can be removed" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rails" + gem "minitest" + gem "rspec" + G + + bundle "remove rails rack minitest" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + gem "rails" + gem "minitest" + gem "rspec" + G + end + end + end + + context "with inline groups" do + it "removes the specified gem" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack", :group => [:dev] + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + describe "with group blocks" do + context "when single group block with gem to be removed is present" do + it "removes the group block" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when an empty block is also present" do + it "removes all empty blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rspec" + end + + group :dev do + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when the gem belongs to mutiple groups" do + it "removes the groups" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test, :serioustest do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when the gem is present in mutiple groups" do + it "removes all empty blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :one do + gem "rspec" + end + + group :two do + gem "rspec" + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + end + + describe "nested group blocks" do + context "when all the groups will be empty after removal" do + it "removes the empty nested blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rspec" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "when outer group will not be empty after removal" do + it "removes only empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rack-test" + + group :serioustest do + gem "rspec" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + group :test do + gem "rack-test" + + end + G + end + end + + context "when inner group will not be empty after removal" do + it "removes only empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rspec" + gem "rack-test" + end + end + G + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + group :test do + group :serioustest do + gem "rack-test" + end + end + G + end + end + end + + describe "arbitrary gemfile" do + context "when mutiple gems are present in same line" do + it "shows warning for gems not removed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack"; gem "rails" + G + + bundle "remove rails" + + expect(out).to include("Gems could not be removed. rack (>= 0) would also have been removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + gem "rack"; gem "rails" + G + end + end + + context "when some gems could not be removed" do + it "shows warning for gems not removed and success for those removed" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem"rack" + gem"rspec" + gem "rails" + gem "minitest" + G + + bundle! "remove rails rack rspec minitest" + + expect(out).to include("rails was removed.") + expect(out).to include("minitest was removed.") + expect(out).to include("rack, rspec could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + gem"rack" + gem"rspec" + G + end + end + end + + context "with sources" do + before do + build_repo gem_repo3 do + build_gem "rspec" + end + end + + it "removes gems and empty source blocks" do + gemfile <<-G + source "file://#{gem_repo1}" + + gem "rack" + + source "file://#{gem_repo3}" do + gem "rspec" + end + G + + bundle! "install" + + bundle! "remove rspec" + + expect(out).to include("rspec was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + gem "rack" + G + end + end + + describe "with eval_gemfile" do + context "when gems are present in both gemfiles" do + it "removes the gems" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + + gem "rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + end + end + + context "when gems are present in other gemfile" do + it "removes the gems" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + + bundle! "remove rack" + + expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"") + expect(out).to include("rack was removed.") + end + end + + context "when gems to be removed are not specified in any of the gemfiles" do + it "throws error for the gems not present" do + # an empty gemfile + # indicating the gem is not present in the gemfile + create_file "Gemfile-other", <<-G + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + + bundle "remove rack" + + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile")} so it could not be removed.") + end + end + + context "when the gem is present in parent file but not in gemfile specified by eval_gemfile" do + it "removes the gem" do + create_file "Gemfile-other", <<-G + gem "rails" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rack" + G + + bundle "remove rack" + + expect(out).to include("rack was removed.") + expect(out).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + end + end + + context "when gems can not be removed from other gemfile" do + it "shows error" do + create_file "Gemfile-other", <<-G + gem "rails"; gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rack" + G + + bundle "remove rack" + + expect(out).to include("rack was removed.") + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + G + end + end + + context "when gems could not be removed from parent gemfile" do + it "shows error" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rails"; gem "rack" + G + + bundle "remove rack" + + expect(out).to include("Gems could not be removed. rails (>= 0) would also have been removed.") + expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem "rails"; gem "rack" + G + end + end + + context "when gem present in gemfiles but could not be removed from one from one of them" do + it "removes gem which can be removed and shows warning for file from which it can not be removed" do + create_file "Gemfile-other", <<-G + gem "rack" + G + + install_gemfile <<-G + source "file://#{gem_repo1}" + + eval_gemfile "Gemfile-other" + gem"rack" + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + expect(bundled_app("Gemfile-other").read).to_not include("gem \"rack\"") + end + end + end + + context "with install_if" do + it "removes gems inside blocks and empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + install_if(lambda { false }) do + gem "rack" + end + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "with env" do + it "removes gems inside blocks and empty blocks" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + env "BUNDLER_TEST" do + gem "rack" + end + G + + bundle! "remove rack" + + expect(out).to include("rack was removed.") + gemfile_should_be <<-G + source "file://#{gem_repo1}" + G + end + end + + context "with gemspec" do + it "should not remove the gem" do + build_lib("foo", :path => tmp.join("foo")) do |s| + s.write("foo.gemspec", "") + s.add_dependency "rack" + end + + install_gemfile(<<-G) + source "file://#{gem_repo1}" + gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + G + + bundle! "remove foo" + + expect(out).to include("foo could not be removed.") + end + end +end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb index 0244927bdc..8e17be3a02 100644 --- a/spec/support/matchers.rb +++ b/spec/support/matchers.rb @@ -238,5 +238,9 @@ module Spec def lockfile_should_be(expected) expect(bundled_app("Gemfile.lock")).to read_as(normalize_uri_file(strip_whitespace(expected))) end + + def gemfile_should_be(expected) + expect(bundled_app("Gemfile")).to read_as(strip_whitespace(expected)) + end end end |