Class: Bundler::Patch::Gemfile

Inherits:
UpdateSpec show all
Defined in:
lib/bundler/patch/gemfile.rb

Instance Attribute Summary collapse

Attributes inherited from UpdateSpec

#patched_versions, #regexes, #target_dir, #target_file

Instance Method Summary collapse

Methods inherited from UpdateSpec

#calc_new_version, #file_replace, #target_path_fn, #update_to_new_version, #verbose_puts

Constructor Details

#initialize(target_bundle: TargetBundle.new, gem_name:, patched_versions: []) ⇒ Gemfile

Returns a new instance of Gemfile.



5
6
7
8
9
10
11
12
# File 'lib/bundler/patch/gemfile.rb', line 5

def initialize(target_bundle: TargetBundle.new,
               gem_name:,
               patched_versions: [])
  super(target_file: target_bundle.gemfile,
        target_dir: target_bundle.dir,
        patched_versions: patched_versions)
  @gem_name = gem_name
end

Instance Attribute Details

#gem_nameObject (readonly)

Returns the value of attribute gem_name.



3
4
5
# File 'lib/bundler/patch/gemfile.rb', line 3

def gem_name
  @gem_name
end

Instance Method Details

#to_sObject



14
15
16
# File 'lib/bundler/patch/gemfile.rb', line 14

def to_s
  "#{@gem_name} #{patched_versions}"
end

#updateObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/bundler/patch/gemfile.rb', line 18

def update
  # Bundler evals the whole Gemfile in Bundler::Dsl.evaluate
  # It has a few magics to parse all possible calls to `gem`
  # command. It doesn't have anything to output the entire
  # Gemfile, I don't think it ever does that. (There is code
  # to init a Gemfile from a gemspec, but it doesn't look
  # like it's intended to recreate one just evaled - I don't
  # see any code that would handle additional sources or
  # groups - see lib/bundler/rubygems_ext.rb #to_gemfile).
  #
  # So without something in Bundler that round-trips from
  # Gemfile back to disk and maintains integrity, then we
  # couldn't re-use it to make modifications to the Gemfile
  # like we'd want to, so we'll do this ourselves.
  #
  # We'll still instance_eval the gem line though, to properly
  # handle the various options and possible multiple reqs.
  @regexes = /^\s*gem.*['"]\s*#{@gem_name}\s*['"].*$/
  file_replace do |match, re|
    update_to_new_gem_version(match)
  end
end

#update_to_new_gem_version(match) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/bundler/patch/gemfile.rb', line 41

def update_to_new_gem_version(match)
  dep = instance_eval(match)
  req = dep.requirement

  prefix = req.exact? ? '' : req.specific? ? '~> ' : '>= '

  current_version = req.requirements.first.last.to_s
  new_version = calc_new_version(current_version)

  # return match if req.satisfied_by?(Gem::Version.new(new_version))
  #
  # TODO: This ^^ could be acceptable, slightly less complex, to not ever
  # touch the requirement if it already covers the patched version.
  # But I can't recall Bundler behavior in all these cases, to ensure
  # at least the patch version is updated and/or we would like to be as
  # conservative as possible in updating - can't recall how much influence
  # we have over `bundle update` (not much)

  return match if req.compound? && req.satisfied_by?(Gem::Version.new(new_version))

  if new_version && prefix =~ /~/
    # could Gem::Version#approximate_recommendation work here?

    # match segments. if started with ~> 1.2 and new_version is 3 segments, replace with 2 segments.
    count = current_version.split(/\./).length
    new_version = new_version.split(/\./)[0..(count-1)].join('.')
  end

  if new_version
    match.sub(requirements_args_regexp, " '#{prefix}#{new_version}'").tap { |s| "Updating to #{s}" }
  else
    match
  end
end