Skip to content
This repository was archived by the owner on Apr 14, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions lib/bundler/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,24 @@ def binstubs(*gems)
"Adds gem to the Gemfile but does not install it"
method_option "optimistic", :type => :boolean, :banner => "Adds optimistic declaration of version to gem"
method_option "strict", :type => :boolean, :banner => "Adds strict declaration of version to gem"
method_option "pessimistic", :type => :boolean, :banner => "Adds pessimistic declaration of version to gem"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this option needs to be tested

def add(*gems)
require "bundler/cli/add"
Add.new(options.dup, gems).run
end

desc "change GEM [OPTIONS]", "Changes properties of a gem"
long_desc <<-D
Provide flexibilty of editing gemfile from command line by providing option to change gem properties.
D
method_option "version", :type => :string
method_option "group", :type => :string
method_option "source", :type => :string
def change(gem_name)
require "bundler/cli/change"
Change.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
Expand Down
5 changes: 3 additions & 2 deletions lib/bundler/cli/add.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class CLI::Add
def initialize(options, gems)
@gems = gems
@options = options
@options[:group] = @options[:group].split(",").map(&:strip) if !@options[:group].nil? && !@options[:group].empty?
@options["group"] = @options["group"].split(",").map(&:strip) if !@options["group"].nil? && !@options["group"].empty?
end

def run
Expand All @@ -27,7 +27,8 @@ def run
Injector.inject(dependencies,
:conservative_versioning => @options[:version].nil?, # Perform conservative versioning only when version is not specified
:optimistic => @options[:optimistic],
:strict => @options[:strict])
:strict => @options[:strict],
:pessimistic => @options[:pessimistic])

Installer.install(Bundler.root, Bundler.definition) unless @options["skip-install"]
end
Expand Down
103 changes: 103 additions & 0 deletions lib/bundler/cli/change.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# frozen_string_literal: true

module Bundler
class CLI::Change
def initialize(options, gem_name)
@gem_name = gem_name
@options = options
end

def run
raise InvalidOption, "Please supply at least one option to change." unless @options[:group] || @options[:version] || @options[:source]

dep = Bundler.definition.dependencies.find {|d| d.name == @gem_name }

raise InvalidOption, "`#{@gem_name}` could not be found in the Gemfile." unless dep

check_for_unsupported_options(dep)

add_options = {}

initial_gemfile = IO.readlines(Bundler.default_gemfile)

set_group_options(dep.groups, add_options)

set_version_options(dep.requirement, add_options)

set_source_options(dep.options[:source], add_options)

begin
require "bundler/cli/remove"
CLI::Remove.new([@gem_name], {}).run

require "bundler/cli/add"
CLI::Add.new(add_options, [@gem_name]).run
rescue StandardError => e
SharedHelpers.write_file(Bundler.default_gemfile, initial_gemfile)
raise e
end
end

private

# If version of the gem is specified in Gemfile then we preserve
# it and the prefix
# else if @options[:version] is present then we prefer strict version
# and for a empty version we let resolver get version and set as pessimistic
#
# @param [requirement] requirement requirement of the gem.
# @param [Hash] add_options Options to pass to add command
# @return
def set_version_options(requirement, add_options)
req = requirement.requirements[0]
version_prefix = req[0]
version = req[1].to_s
case version_prefix
when "="
add_options[:strict] = true
when ">="
add_options[:optimistic] = true unless version == "0"
else
add_options[:pessimistic] = true
end

add_options[:version] = if @options[:version].nil?
version.to_i.zero? ? nil : version
else
@options[:version]
end
end

# @param [groups] groups Groups of the gem.
# @param [Hash] add_options Options to pass to add command
# @return
def set_group_options(groups, add_options)
if @options[:group]
uniq_groups = @options[:group].split(",").uniq
common_groups = uniq_groups & groups.map(&:to_s)

Bundler.ui.warn "`#{@gem_name}` is already present in `#{common_groups.join(",")}`." unless common_groups.empty?

add_options["group"] = uniq_groups.join(",")
else
add_options["group"] = groups.map(&:to_s).join(",")
end
end

def set_source_options(source, add_options)
add_options["source"] = source.options["remotes"].first if source.is_a?(Bundler::Source)

add_options["source"] = @options[:source] if @options[:source]
end

def check_for_unsupported_options(dep)
gem_options = dep.options.delete_if {|_k, v| v.nil? || (v.is_a?(Array) && v.empty?) }

raise InvalidOption, "`git` is not yet supported." if dep.options[:source].is_a?(Bundler::Source::Git)

raise InvalidOption, "`platforms` is not yet supported." if gem_options[:platforms]

raise InvalidOption, "`env` is not yet supported." if gem_options[:env]
end
end
end
7 changes: 5 additions & 2 deletions lib/bundler/dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
attr_reader :groups, :platforms, :gemfile
attr_reader :groups, :platforms, :gemfile, :autorequire

PLATFORM_MAP = {
:ruby => Gem::Platform::RUBY,
Expand Down Expand Up @@ -134,5 +133,9 @@ def specific?
rescue NoMethodError
requirement != ">= 0"
end

def options
{ :name => @name, :requirement => @requirement, :groups => @groups.reject {|g| g == :default }, :platforms => @platforms, :env => @env, :source => @source }
end
end
end
6 changes: 3 additions & 3 deletions lib/bundler/injector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def inject(gemfile_path, 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 @deps.any?
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning] || @options[:pessimistic] || @options[:optimistic])) if @deps.any?

# since we resolved successfully, write out the lockfile
@definition.lock(Bundler.default_lockfile)
Expand Down Expand Up @@ -142,7 +142,7 @@ def remove_deps(gemfile_path)

cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path)

SharedHelpers.write_to_gemfile(gemfile_path, cleaned_gemfile)
SharedHelpers.write_file(gemfile_path, cleaned_gemfile)

# check for errors
# including extra gems being removed
Expand Down Expand Up @@ -232,7 +232,7 @@ def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_ge
# 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)
SharedHelpers.write_file(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
Expand Down
6 changes: 4 additions & 2 deletions lib/bundler/shared_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,10 @@ def digest(name)
Digest(name)
end

def write_to_gemfile(gemfile_path, contents)
filesystem_access(gemfile_path) {|g| File.open(g, "w") {|file| file.puts contents } }
# @param [Pathname] path Path to file.
# @param [String] contents Content to written to file.
def write_file(path, contents)
filesystem_access(path) {|g| File.open(g, "w") {|file| file.puts contents } }
end

private
Expand Down
31 changes: 31 additions & 0 deletions man/bundle-change.ronn
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
bundle-change(1) -- Changes properties of a gem
================================================================

## SYNOPSIS

`bundle change` <GEM_NAME> [--group=GROUP] [--version=VERSION] [--source=SOURCE]

## DESCRIPTION
Provide flexibilty of editing gemfile from command line by providing option to change gem properties.

Example:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this example should show the before & after gemfiles


$ cat Gemfile | grep "rails"

gem "rails"

$ bundle change rails --group dev --version 5.1

$ cat Gemfile | grep "rails"

gem "rails", "= 5.1", :group => [:dev]

## OPTIONS
* `--version`:
Specify version requirements for the added gem.

* `--group`:
Specify the groups for the added gem. Multiple groups should be separated by commas.

* `--source`:
Specify the source for the added gem.
8 changes: 8 additions & 0 deletions spec/commands/add_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@
end
end

describe "with --pessimistic option" do
it "adds pessimistic version" do
bundle! "add 'foo' --pessimistic"
expect(bundled_app("Gemfile").read).to include %(gem "foo", "~> 2.0")
expect(the_bundle).to include_gems "foo 2.0"
end
end

describe "with no option" do
it "adds pessimistic version" do
bundle! "add 'foo'"
Expand Down
Loading