gemfile_locker 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2f411635748ebab869141b3e36ca6a8ef8cb7e5a
4
- data.tar.gz: 8269cc977e333fe795775831873f7692184edb00
2
+ SHA256:
3
+ metadata.gz: d2725a59f53ef8af74767a9452e43e17e5c1e402acfa78a97570698a40209c92
4
+ data.tar.gz: cfdf2128c68cde403638e976ca6a25454b342a2436134369a1c086c95eb893ce
5
5
  SHA512:
6
- metadata.gz: 1845a4763600725c8f58d1f5e6c88579eb710a978e8553af9c9bcfcf27df0dbdd06a4b371cad3399c16a9a62aa284f4d05b7cebc63a213b1e119c4d8b526c639
7
- data.tar.gz: 75009ff12e89fc6051e68af04d9fed9ade15a008170a636c58409c23f42214e1be13eae4d1feeed7878aaa0c2ab38d4424a7e9b1efd6a7c1dbe0a3687c723da6
6
+ metadata.gz: 78b6f30fb6d863984499c8f48de1d5f29070c35d861b1637cac54729d400ac0026392efe876ba0bb50dc070608a157e4cde6d301ec6cef3eee74146a0b1e01b8
7
+ data.tar.gz: 3e06350ba9b4be3c59f79b502399660b3c48147473f00f162183a91e10e67e409f7a13842db3e2cfcf8b4a82834972089581590f8be9d456df1c3deae3a456e5
@@ -27,8 +27,9 @@ TXT
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
29
 
30
- spec.add_runtime_dependency 'thor', '~> 0.19'
31
30
  spec.add_runtime_dependency 'bundler', '~> 1.13'
31
+ spec.add_runtime_dependency 'parser', '~> 2.0'
32
+ spec.add_runtime_dependency 'thor', '~> 0.19'
32
33
 
33
34
  spec.add_development_dependency 'rake', '~> 10.0'
34
35
  end
@@ -0,0 +1,54 @@
1
+ module GemfileLocker
2
+ class GemEntry
3
+ attr_reader :rewriter, :node
4
+
5
+ def initialize(rewriter, node)
6
+ @rewriter = rewriter
7
+ @node = node
8
+ end
9
+
10
+ def name
11
+ node.children[2].children[0]
12
+ end
13
+
14
+ # Overriden in prepended modules.
15
+ def lock(**options)
16
+ end
17
+
18
+ # Overriden in prepended modules.
19
+ def unlock
20
+ end
21
+
22
+ require 'gemfile_locker/gem_entry/versions'
23
+ prepend Versions
24
+
25
+ require 'gemfile_locker/gem_entry/git_ref'
26
+ prepend GitRef
27
+
28
+ protected
29
+
30
+ # Node with gem options, if present.
31
+ def options_node
32
+ result = node.children.last
33
+ result if result.type == :hash
34
+ end
35
+
36
+ # Change content of string, keeping quoting style.
37
+ def replace_string_node(target, value)
38
+ quote = target.loc.begin.source
39
+ rewriter.replace(target.loc.expression, "#{quote}#{value}#{quote}")
40
+ end
41
+
42
+ # Remove node with preceding comma.
43
+ def remove_node_with_comma(target)
44
+ expression = target.loc.expression
45
+ comma_pos = expression.source_buffer.source.rindex(',', expression.begin_pos)
46
+ rewriter.remove(expression.with(begin_pos: comma_pos))
47
+ end
48
+
49
+ # Quote style used in name of gem.
50
+ def name_quote
51
+ node.children[2].loc.begin.source
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,54 @@
1
+ module GemfileLocker
2
+ class GemEntry
3
+ module GitRef
4
+ def lock(options)
5
+ git_ref = options[:git_ref]
6
+ set_git_ref(git_ref) if git_ref && !has_git_tag?
7
+ super
8
+ end
9
+
10
+ def unlock
11
+ remove_git_ref
12
+ super
13
+ end
14
+
15
+ def has_git_tag?
16
+ git_option_nodes.any? { |pair| pair.children[0].children[0] == :tag }
17
+ end
18
+
19
+ def set_git_ref(ref) # rubocop:disable AccessorMethodName
20
+ ref_node = ref_option_node
21
+ return replace_string_node(ref_node.children[1], ref) if ref_node
22
+ git_nodes = git_option_nodes
23
+ insert_after_node = git_nodes.any? ? git_nodes.last : node.children.last
24
+ quote = name_quote
25
+ rewriter.insert_after(insert_after_node.loc.expression.end, ", ref: #{quote}#{ref}#{quote}")
26
+ end
27
+
28
+ def remove_git_ref
29
+ ref_node = ref_option_node
30
+ remove_node_with_comma(ref_node) if ref_node
31
+ end
32
+
33
+ protected
34
+
35
+ def ref_option_node
36
+ return unless options_node
37
+ options_node.children.find do |pair|
38
+ pair.children[0].to_sexp_array == [:sym, :ref]
39
+ end
40
+ end
41
+
42
+ RELATED_OPTIONS = /\A(git*|branch|tag)\z/
43
+
44
+ def git_option_nodes
45
+ return [] unless options_node
46
+ options_node.children.reverse.select do |pair|
47
+ key_node = pair.children[0]
48
+ next unless key_node.type == :sym
49
+ RELATED_OPTIONS =~ key_node.children[0].to_s
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ module GemfileLocker
2
+ class GemEntry
3
+ module Versions
4
+ EXTRA_VERSION_REGEXP = /\A[><]/
5
+
6
+ def lock(options)
7
+ version = options[:version]
8
+ set_version(version) if version
9
+ super
10
+ end
11
+
12
+ def unlock
13
+ remove_version
14
+ super
15
+ end
16
+
17
+ def locked?
18
+ version_nodes.any?
19
+ end
20
+
21
+ def set_version(version) # rubocop:disable AccessorMethodName
22
+ version_nodes = self.version_nodes
23
+ if version_nodes.any?
24
+ replace_string_node(version_nodes.first, version)
25
+ else
26
+ quote = name_quote
27
+ rewriter.insert_after(node.children[2].loc.end, ", #{quote}#{version}#{quote}")
28
+ end
29
+ end
30
+
31
+ def remove_version
32
+ # If multiple version strings are given, keep that which start with `>, >=, <, <=`.
33
+ version_nodes = self.version_nodes(strict: ->(versions) { versions.size > 1 })
34
+ version_nodes.each do |arg_node|
35
+ remove_node_with_comma(arg_node)
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def version_nodes(strict_if: nil, strict: !strict_if)
42
+ result = node.children.drop(3).select { |arg_node| arg_node.type == :str }
43
+ if strict_if && strict_if[result] || strict
44
+ result = result.reject do |arg_node|
45
+ EXTRA_VERSION_REGEXP =~ arg_node.children[0]
46
+ end
47
+ end
48
+ result
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,27 +1,22 @@
1
+ require 'parser'
2
+ require 'parser/current'
3
+ require 'gemfile_locker/gem_entry'
4
+
1
5
  module GemfileLocker
2
6
  class GemfileProcessor
3
- GEM_LINE_REGEX = /
4
- ^
5
- (?<prefix>\s*gem\s*["'])
6
- (?<name>[^'"]+)
7
- (?<name_quote>['"])
8
- (?<version_section>
9
- (?<version_prefix>\s*,\s*['"])
10
- (?<version>[^'"]*)
11
- (?<version_quote>['"])
12
- )?
13
- (?<suffix>,?.*)?
14
- $
15
- /x
16
- GEM_MATCH_FIELDS = %i(
17
- prefix
18
- name
19
- name_quote
20
- version_prefix
21
- version
22
- version_quote
23
- suffix
24
- ).freeze
7
+ class Rewriter < Parser::TreeRewriter
8
+ def rewrite(*args, &block)
9
+ @rewrite_block = block
10
+ super(*args)
11
+ end
12
+
13
+ def on_send(node)
14
+ children = node.children
15
+ return unless children[0].nil? && node.children[1] == :gem
16
+ gem_entry = GemEntry.new(self, node)
17
+ @rewrite_block[gem_entry]
18
+ end
19
+ end
25
20
 
26
21
  attr_reader :path, :options
27
22
 
@@ -30,47 +25,25 @@ module GemfileLocker
30
25
  end
31
26
 
32
27
  def call(string)
33
- process_gems(string) do |data|
34
- process_gem(data) unless skip_gem?(data)
28
+ buffer = Parser::Source::Buffer.new('(Gemfile)')
29
+ buffer.source = string
30
+ parser = Parser::CurrentRuby.new
31
+ ast = parser.parse(buffer)
32
+ Rewriter.new.rewrite(buffer, ast) do |gem_entry|
33
+ process_gem(gem_entry) unless skip_gem?(gem_entry)
35
34
  end
36
35
  end
37
36
 
38
- def skip_gem?(data)
37
+ def skip_gem?(gem_entry)
39
38
  if options[:only]
40
- !options[:only].include?(data[:name])
39
+ !options[:only].include?(gem_entry.name)
41
40
  elsif options[:except]
42
- options[:except].include?(data[:name])
43
- end
44
- end
45
-
46
- def process_gems(string)
47
- string.gsub(GEM_LINE_REGEX) do
48
- match = Regexp.last_match
49
- data = Hash[GEM_MATCH_FIELDS.map { |x| [x, match[x]] }]
50
- result = yield data
51
- result ||= data
52
- GEM_MATCH_FIELDS.map { |x| result[x] }.join
41
+ options[:except].include?(gem_entry.name)
53
42
  end
54
43
  end
55
44
 
56
45
  def process_gem(_name, _data)
57
46
  raise 'Abstract method'
58
47
  end
59
-
60
- def set_gem_version(data, version)
61
- data = data.dup
62
- if version
63
- data[:version_prefix] ||= ", #{data[:name_quote] || "'"}"
64
- data[:version_quote] ||= data[:name_quote] || "'"
65
- data[:version] = version
66
- else
67
- %i(
68
- version_prefix
69
- version
70
- version_quote
71
- ).each { |x| data.delete(x) }
72
- end
73
- data
74
- end
75
48
  end
76
49
  end
@@ -20,14 +20,15 @@ module GemfileLocker
20
20
  @bundler_specs ||= Bundler::LockfileParser.new(lockfile).specs
21
21
  end
22
22
 
23
- def process_gem(data)
24
- name = data[:name]
25
- locked = bundler_specs.find { |x| x.name == name }
26
- locked && set_gem_version(data, prepare_version(locked.version))
23
+ def process_gem(gem_entry)
24
+ name = gem_entry.name
25
+ spec = bundler_specs.find { |x| x.name == name }
26
+ return unless spec
27
+ gem_entry.lock(version: prepare_version(spec.version), git_ref: prepare_git_ref(spec))
27
28
  end
28
29
 
29
- def skip_gem?(data)
30
- super || data[:version] && !options[:force]
30
+ def skip_gem?(gem_entry)
31
+ super || gem_entry.locked? && !options[:force]
31
32
  end
32
33
 
33
34
  private
@@ -40,5 +41,11 @@ module GemfileLocker
40
41
  version.to_s
41
42
  end
42
43
  end
44
+
45
+ def prepare_git_ref(spec)
46
+ if spec.source.is_a?(Bundler::Source::Git)
47
+ spec.source.options['ref'] || spec.source.revision[0...7]
48
+ end
49
+ end
43
50
  end
44
51
  end
@@ -2,8 +2,8 @@ module GemfileLocker
2
2
  class Unlocker < GemfileProcessor
3
3
  attr_reader :lockfile
4
4
 
5
- def process_gem(data)
6
- set_gem_version(data, nil)
5
+ def process_gem(gem_entry)
6
+ gem_entry.unlock
7
7
  end
8
8
  end
9
9
  end
@@ -1,5 +1,5 @@
1
1
  module GemfileLocker
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
 
4
4
  def self.gem_version
5
5
  Gem::Version.new VERSION
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gemfile_locker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max Melentiev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-11-19 00:00:00.000000000 Z
11
+ date: 2018-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: thor
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.19'
19
+ version: '1.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.19'
26
+ version: '1.13'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: parser
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.13'
33
+ version: '2.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.13'
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.19'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.19'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -81,6 +95,9 @@ files:
81
95
  - lib/gemfile_locker.rb
82
96
  - lib/gemfile_locker/cli.rb
83
97
  - lib/gemfile_locker/file_editor.rb
98
+ - lib/gemfile_locker/gem_entry.rb
99
+ - lib/gemfile_locker/gem_entry/git_ref.rb
100
+ - lib/gemfile_locker/gem_entry/versions.rb
84
101
  - lib/gemfile_locker/gemfile_processor.rb
85
102
  - lib/gemfile_locker/locker.rb
86
103
  - lib/gemfile_locker/unlocker.rb
@@ -104,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
121
  version: '0'
105
122
  requirements: []
106
123
  rubyforge_project:
107
- rubygems_version: 2.5.1
124
+ rubygems_version: 2.7.7
108
125
  signing_key:
109
126
  specification_version: 4
110
127
  summary: Tool to manage Gemfile. Lock and unlock all dependencies for safe `bundle