gem_bench 1.0.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,46 +3,41 @@ module GemBench
3
3
  GEM_REGEX = /\A\s*gem\s+([^#]*).*\Z/.freeze # run against gem lines like: "gem 'aftership', # Ruby SDK of AfterShip API."
4
4
  GEM_NAME_REGEX = /\A\s*gem\s+['"]{1}(?<name>[^'"]*)['"].*\Z/.freeze # run against gem lines like: "gem 'aftership', # Ruby SDK of AfterShip API."
5
5
  VERSION_CONSTRAINT = /['"]{1}([^'"]*)['"]/.freeze
6
- GEMFILE_HASH_CONFIG_KEY_REGEX_PROC = ->(key) { /\A\s*[^#]*(?<key1>#{key}: *)['"]{1}(?<value1>[^'"]*)['"]|(?<key2>['"]#{key}['"] *=> *)['"]{1}(?<value2>[^'"]*)['"]|(?<key3>:#{key} *=> *)['"]{1}(?<value3>[^'"]*)['"]/ }
7
- VERSION_PATH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('path').freeze
8
- VERSION_GIT = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('git').freeze
9
- VERSION_GITHUB = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('github').freeze
10
- VERSION_GIT_REF = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('ref').freeze
11
- VERSION_GIT_TAG = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('tag').freeze
12
- VERSION_GIT_BRANCH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('branch').freeze
13
- VALID_VERSION_TYPES = %i(
6
+ GEMFILE_HASH_CONFIG_KEY_REGEX_PROC = lambda { |key|
7
+ /\A\s*[^#]*(?<key1>#{key}: *)['"]{1}(?<value1>[^'"]*)['"]|(?<key2>['"]#{key}['"] *=> *)['"]{1}(?<value2>[^'"]*)['"]|(?<key3>:#{key} *=> *)['"]{1}(?<value3>[^'"]*)['"]/
8
+ }
9
+ VERSION_PATH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("path").freeze
10
+ VERSION_GIT = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("git").freeze
11
+ VERSION_GITHUB = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("github").freeze
12
+ VERSION_GIT_REF = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("ref").freeze
13
+ VERSION_GIT_TAG = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("tag").freeze
14
+ VERSION_GIT_BRANCH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("branch").freeze
15
+ VALID_VERSION_TYPES = %i[
14
16
  constraint
15
17
  git_ref
16
18
  git_tag
17
- )
19
+ ]
18
20
  # branch is only valid if the branch is not master
19
21
  attr_reader :line
20
- attr_reader :relevant_lines
21
- attr_reader :is_gem
22
- attr_reader :all_lines
23
- attr_reader :index
24
- attr_reader :tokens
22
+ attr_reader :relevant_lines, :is_gem, :all_lines, :index, :tokens, :version_type, :name, :parse_success, :valid
25
23
  # version will be a string if it is a normal constraint like '~> 1.2.3'
26
24
  # version will be a hash if it is an alternative constraint like:
27
25
  # git: "blah/blah", ref: "shasha"
28
26
  attr_reader :version
29
- attr_reader :version_type
30
- attr_reader :name
31
- attr_reader :parse_success
32
- attr_reader :valid
27
+
33
28
  def initialize(all_lines, line, index)
34
29
  @line = line.strip
35
30
  @is_gem = self.line.match(GEM_REGEX)
36
- if self.is_gem
31
+ if is_gem
37
32
  @all_lines = all_lines
38
33
  @index = index
39
- @tokens = self.line.split(',')
34
+ @tokens = self.line.split(",")
40
35
  determine_name
41
- if self.name
36
+ if name
42
37
  determine_relevant_lines
43
38
  determine_version
44
39
  @parse_success = true
45
- @valid = VALID_VERSION_TYPES.include?(self.version_type)
40
+ @valid = VALID_VERSION_TYPES.include?(version_type)
46
41
  else
47
42
  noop
48
43
  end
@@ -87,6 +82,7 @@ module GemBench
87
82
  # index 1 of the comma-split tokens will usually be the version constraint, if there is one
88
83
  possible_constraint = @tokens[1]
89
84
  return false unless possible_constraint
85
+
90
86
  match_data = possible_constraint.strip.match(VERSION_CONSTRAINT)
91
87
  # the version constraint is in a regex capture group
92
88
  if match_data && (@version = match_data[1].strip)
@@ -99,92 +95,100 @@ module GemBench
99
95
 
100
96
  def version_path
101
97
  @version = {}
102
- line = relevant_lines.detect { |next_line| (next_line.match(VERSION_PATH)) }
98
+ line = relevant_lines.detect { |next_line| next_line.match(VERSION_PATH) }
103
99
  return false unless line
100
+
104
101
  enhance_version(
105
- line.match(VERSION_PATH),
106
- :path,
107
- :path
102
+ line.match(VERSION_PATH),
103
+ :path,
104
+ :path,
108
105
  )
109
106
  end
110
107
 
111
108
  def version_git
112
109
  @version = {}
113
- line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT)) }
110
+ line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT) }
114
111
  return false unless line
112
+
115
113
  enhance_version(
116
- line.match(VERSION_GIT),
117
- :git,
118
- :git
114
+ line.match(VERSION_GIT),
115
+ :git,
116
+ :git,
119
117
  )
120
118
  end
121
119
 
122
120
  def version_github
123
121
  @version = {}
124
- line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GITHUB)) }
122
+ line = relevant_lines.detect { |next_line| next_line.match(VERSION_GITHUB) }
125
123
  return false unless line
124
+
126
125
  enhance_version(
127
- line.match(VERSION_GITHUB),
128
- :github,
129
- :github
126
+ line.match(VERSION_GITHUB),
127
+ :github,
128
+ :github,
130
129
  )
131
130
  end
132
131
 
133
132
  def check_for_version_of_type_git_ref
134
- line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT_REF)) }
133
+ line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT_REF) }
135
134
  return false unless line
135
+
136
136
  enhance_version(
137
- line.match(VERSION_GIT_REF),
138
- :ref,
139
- :git_ref
137
+ line.match(VERSION_GIT_REF),
138
+ :ref,
139
+ :git_ref,
140
140
  )
141
141
  end
142
142
 
143
143
  def check_for_version_of_type_git_tag
144
- line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT_TAG)) }
144
+ line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT_TAG) }
145
145
  return false unless line
146
+
146
147
  enhance_version(
147
- line.match(VERSION_GIT_TAG),
148
- :tag,
149
- :git_tag
148
+ line.match(VERSION_GIT_TAG),
149
+ :tag,
150
+ :git_tag,
150
151
  )
151
152
  end
152
153
 
153
154
  def check_for_version_of_type_git_branch
154
- line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT_BRANCH)) }
155
+ line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT_BRANCH) }
155
156
  return false unless line
157
+
156
158
  enhance_version(
157
- line.match(VERSION_GIT_BRANCH),
158
- :branch,
159
- :git_branch
159
+ line.match(VERSION_GIT_BRANCH),
160
+ :branch,
161
+ :git_branch,
160
162
  )
161
163
  end
162
164
 
163
165
  # returns an array with each line following the current line, which is not a gem line
164
166
  def following_non_gem_lines
165
- all_lines[(index+1)..(-1)].
166
- reject {|x| x.strip.empty? || x.match(GemBench::TRASH_REGEX) }.
167
- map(&:strip).
168
- inject([]) do |following_lines, next_line|
169
- break following_lines if next_line.match(GEM_REGEX)
170
- following_lines << next_line
167
+ all_lines[(index + 1)..-1]
168
+ .reject { |x| x.strip.empty? || x.match(GemBench::TRASH_REGEX) }
169
+ .map(&:strip)
170
+ .inject([]) do |following_lines, next_line|
171
+ break following_lines if next_line.match(GEM_REGEX)
172
+
173
+ following_lines << next_line
171
174
  end
172
175
  end
173
176
 
174
177
  # returns a hash like:
175
178
  # {"key" => ":git => ", "value" => "https://github.com/cte/aftership-sdk-ruby.git"}
176
179
  def normalize_match_data_captures(match_data)
177
- match_data.names.inject({}) do |mem, capture|
178
- mem[capture.gsub(/\d/,'')] = match_data[capture]
180
+ match_data.names.each_with_object({}) do |capture, mem|
181
+ mem[capture.gsub(/\d/, "")] = match_data[capture]
179
182
  break mem if mem.keys.length >= 2
180
- mem
181
183
  end
182
184
  end
183
185
 
184
186
  def enhance_version(match_data, version_key, type)
185
187
  return false unless match_data
188
+
186
189
  normalized_capture = normalize_match_data_captures(match_data) if match_data
187
190
  return false unless normalized_capture
191
+
188
192
  @version.merge!({version_key => normalized_capture["value"]})
189
193
  @version_type = type
190
194
  true
@@ -21,49 +21,56 @@ module GemBench
21
21
 
22
22
  def set_starter(file_path, line_match: nil)
23
23
  return false if file_path =~ exclude_file_pattern
24
+
24
25
  # Some gems may have zero files to check, as they may be using gem as a
25
26
  # delivery system for shell scripts! As such we need to check which
26
27
  # gems got checked, and which had nothing to check
27
28
  @checked = true
28
29
  line_match ||= GemBench::RAILTIE_REGEX
29
- scan = begin
30
- if GemBench::DO_NOT_SCAN.include?(name)
31
- false
32
- else
33
- begin
34
- File.read(file_path).encode('utf-8', :invalid => :replace, :undef => :replace, :replace => '_') =~ line_match
35
- rescue ArgumentError => e
36
- if e.message =~ /invalid byte sequence/
37
- puts "[GemBench] checking #{file_path} failed due to unparseable file content"
38
- false # Assume the likelihood of files with encoding issues that also contain railtie to be low, so: false.
39
- end
30
+ scan = if GemBench::DO_NOT_SCAN.include?(name)
31
+ false
32
+ else
33
+ begin
34
+ File.read(file_path).encode(
35
+ "utf-8",
36
+ invalid: :replace,
37
+ undef: :replace,
38
+ replace: "_",
39
+ ) =~ line_match
40
+ rescue ArgumentError => e
41
+ if e.message =~ /invalid byte sequence/
42
+ puts "[GemBench] checking #{file_path} failed due to unparseable file content"
43
+ false # Assume the likelihood of files with encoding issues that also contain railtie to be low, so: false.
40
44
  end
41
45
  end
42
46
  end
43
- self.stats << [file_path,scan] if scan
44
- self.state = !!scan ?
45
- GemBench::PLAYER_STATES[:starter] :
47
+
48
+ stats << [file_path, scan] if scan
49
+ self.state = if !!scan
50
+ GemBench::PLAYER_STATES[:starter]
51
+ else
46
52
  GemBench::PLAYER_STATES[:bench]
53
+ end
47
54
  end
48
55
 
49
56
  def starter?
50
- self.state == GemBench::PLAYER_STATES[:starter]
57
+ state == GemBench::PLAYER_STATES[:starter]
51
58
  end
52
59
 
53
60
  def to_s(format = :name)
54
61
  case format
55
- when :name then
56
- name
57
- when :v then
58
- "#{name} v#{version}"
59
- when :semver
60
- "gem '#{name}', '~> #{semver}'"
61
- when :locked
62
- "gem '#{name}', '#{version}'"
63
- when :legacy # when depending on legacy gems, you specifically want to not upgrade, except patches.
64
- "gem '#{name}', '~> #{version}'"
65
- when :upgrade # when upgrading, and testing gem compatibility you want to try anything newer
66
- "gem '#{name}', '>= #{version}'"
62
+ when :name
63
+ name
64
+ when :v
65
+ "#{name} v#{version}"
66
+ when :semver
67
+ "gem '#{name}', '~> #{semver}'"
68
+ when :locked
69
+ "gem '#{name}', '#{version}'"
70
+ when :legacy # when depending on legacy gems, you specifically want to not upgrade, except patches.
71
+ "gem '#{name}', '~> #{version}'"
72
+ when :upgrade # when upgrading, and testing gem compatibility you want to try anything newer
73
+ "gem '#{name}', '>= #{version}'"
67
74
  end
68
75
  end
69
76
 
@@ -73,17 +80,15 @@ module GemBench
73
80
 
74
81
  def semver
75
82
  ver = version
76
- until ver.split(".").length <= SEMVER_SPLIT_ON_POINT_LENGTH do
77
- ver = ver[0..(ver.rindex(".")-1)]
78
- end
83
+ ver = ver[0..(ver.rindex(".") - 1)] until ver.split(".").length <= SEMVER_SPLIT_ON_POINT_LENGTH
79
84
  ver
80
85
  end
81
86
 
82
87
  def how
83
- case self.state
84
- when GemBench::PLAYER_STATES[:starter] then
88
+ case state
89
+ when GemBench::PLAYER_STATES[:starter]
85
90
  to_s(:semver)
86
- when GemBench::PLAYER_STATES[:bench] then
91
+ when GemBench::PLAYER_STATES[:bench]
87
92
  "#{to_s(:semver)}, require: false"
88
93
  else
89
94
  if checked
@@ -3,13 +3,14 @@
3
3
  module GemBench
4
4
  class Scout
5
5
  attr_reader :gem_paths, :gemfile_path, :gemfile_lines, :gemfile_trash, :loaded_gems
6
+
6
7
  def initialize(check_gemfile: nil)
7
8
  @check_gemfile = check_gemfile.nil? ? true : check_gemfile
8
9
  @gemfile_path = "#{Dir.pwd}/Gemfile"
9
10
  gem_lookup_paths_from_bundler
10
11
  gem_lines_from_gemfile
11
12
  # Gem.loaded_specs are the gems that have been loaded / required.
12
- @loaded_gems = Gem.loaded_specs.values.map {|x| [x.name, x.version.to_s] }
13
+ @loaded_gems = Gem.loaded_specs.values.map { |x| [x.name, x.version.to_s] }
13
14
  end
14
15
 
15
16
  def check_gemfile?
@@ -19,23 +20,21 @@ module GemBench
19
20
  private
20
21
 
21
22
  def gem_lookup_paths_from_bundler
22
- begin
23
- @gem_paths = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path].
24
- flatten.
25
- compact.
26
- uniq.
27
- map {|x| x.to_s }.
28
- reject { |p| p.empty? }.
29
- map {|x| "#{x}/gems" }
30
- @gem_paths << "#{Bundler.install_path}"
31
- @gem_paths << "#{Bundler.bundle_path}/gems"
32
- @gem_paths.uniq!
33
- rescue Bundler::GemfileNotFound => e
34
- # Don't fail here, but also don't check the Gemfile.
35
- @check_gemfile = false
36
- ensure
37
- @gem_paths = [] unless @gem_paths.is_a?(Array)
38
- end
23
+ @gem_paths = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
24
+ .flatten
25
+ .compact
26
+ .uniq
27
+ .map { |x| x.to_s }
28
+ .reject { |p| p.empty? }
29
+ .map { |x| "#{x}/gems" }
30
+ @gem_paths << "#{Bundler.install_path}"
31
+ @gem_paths << "#{Bundler.bundle_path}/gems"
32
+ @gem_paths.uniq!
33
+ rescue Bundler::GemfileNotFound => e
34
+ # Don't fail here, but also don't check the Gemfile.
35
+ @check_gemfile = false
36
+ ensure
37
+ @gem_paths = [] unless @gem_paths.is_a?(Array)
39
38
  end
40
39
 
41
40
  def gem_lines_from_gemfile
@@ -44,8 +43,8 @@ module GemBench
44
43
  # Get all lines as an array
45
44
  all_lines = file.readlines
46
45
  # Remove all the commented || blank lines
47
- @gemfile_trash, @gemfile_lines = all_lines.partition {|x| x =~ GemBench::TRASH_REGEX}
48
- @gemfile_trash.reject! {|x| x == "\n" } # remove blank lines
46
+ @gemfile_trash, @gemfile_lines = all_lines.partition { |x| x =~ GemBench::TRASH_REGEX }
47
+ @gemfile_trash.reject! { |x| x == "\n" } # remove blank lines
49
48
  else
50
49
  @gemfile_trash = []
51
50
  @gemfile_lines = []
@@ -1,25 +1,20 @@
1
1
  module GemBench
2
2
  class StrictVersionGem
3
- attr_reader :name
4
- attr_reader :version
5
- attr_reader :version_type
6
- attr_reader :valid
7
- attr_reader :relevant_lines
8
- attr_reader :index
9
- attr_reader :tokenized_line
3
+ attr_reader :name, :version, :version_type, :valid, :relevant_lines, :index, :tokenized_line
10
4
 
11
5
  class << self
12
6
  def from_line(all_lines, line, index, opts = {})
13
7
  tokenized_line = GemfileLineTokenizer.new(all_lines, line, index)
14
- return nil unless tokenized_line.is_gem
8
+ return unless tokenized_line.is_gem
9
+
15
10
  new(
16
- tokenized_line.name,
17
- tokenized_line.version,
18
- tokenized_line.version_type,
19
- tokenized_line.valid,
20
- tokenized_line.relevant_lines,
21
- tokenized_line.index,
22
- opts[:debug] == true ? tokenized_line : nil
11
+ tokenized_line.name,
12
+ tokenized_line.version,
13
+ tokenized_line.version_type,
14
+ tokenized_line.valid,
15
+ tokenized_line.relevant_lines,
16
+ tokenized_line.index,
17
+ (opts[:debug] == true) ? tokenized_line : nil,
23
18
  )
24
19
  end
25
20
  end
@@ -43,12 +38,12 @@ module GemBench
43
38
  end
44
39
 
45
40
  def to_s
46
- <<-EOS
47
- Gem: #{name}
48
- Line Number: #{index}
49
- Version: #{version.inspect}
50
- Relevant Gemfile Lines:
51
- #{relevant_lines.join("\n")}
41
+ <<~EOS
42
+ Gem: #{name}
43
+ Line Number: #{index}
44
+ Version: #{version.inspect}
45
+ Relevant Gemfile Lines:
46
+ #{relevant_lines.join("\n")}
52
47
  EOS
53
48
  end
54
49
  end
@@ -1,10 +1,6 @@
1
1
  module GemBench
2
2
  class StrictVersionRequirement
3
- attr_reader :gemfile_path
4
- attr_reader :gems
5
- attr_reader :starters
6
- attr_reader :benchers
7
- attr_reader :verbose
3
+ attr_reader :gemfile_path, :gems, :starters, :benchers, :verbose
8
4
 
9
5
  def initialize(options = {})
10
6
  @gemfile_path = "#{Dir.pwd}/Gemfile"
@@ -18,14 +14,14 @@ module GemBench
18
14
  @gems << gem if gem
19
15
  end
20
16
 
21
- @starters, @benchers = @gems.partition {|x| x.valid? }
17
+ @starters, @benchers = @gems.partition { |x| x.valid? }
22
18
  # Remove all the commented || blank lines
23
19
  @verbose = options[:verbose]
24
- self.print if self.verbose
20
+ self.print if verbose
25
21
  end
26
22
 
27
23
  def versions_present?
28
- gems.detect {|x| !x.valid? }.nil?
24
+ gems.detect { |x| !x.valid? }.nil?
29
25
  end
30
26
 
31
27
  def list_missing_version_constraints
@@ -33,34 +29,34 @@ module GemBench
33
29
  end
34
30
 
35
31
  def find(name)
36
- gems.detect {|x| x.name == name }
32
+ gems.detect { |x| x.name == name }
37
33
  end
38
34
 
39
35
  def gem_at(index)
40
- gems.detect {|x| x.index == index }
36
+ gems.detect { |x| x.index == index }
41
37
  end
42
38
 
43
39
  def print
44
- using_path = benchers.count {|x| x.is_type?(:path) }
45
- puts <<-EOS
46
-
47
- The gems that need to be improved are:
48
-
49
- #{benchers.map(&:to_s).join("\n")}
50
-
51
- There are #{starters.length} gems that have valid strict version constraints.
52
- Of those:
53
- #{starters.count {|x| x.is_type?(:constraint) }} use primary constraints (e.g. '~> 1.2.3').
54
- #{starters.count {|x| x.is_type?(:git_ref) }} use git ref constraints.
55
- #{starters.count {|x| x.is_type?(:git_tag) }} use git tag constraints.
56
-
57
- There are #{benchers.length} gems that do not have strict version constraints.
58
- Of those:
59
- #{benchers.count {|x| x.is_type?(:git_branch) }} use git branch constraints.
60
- #{benchers.count {|x| x.is_type?(:git) }} use some other form of git constraint considered not strict enough.
61
- #{benchers.count {|x| x.is_type?(:unknown) }} gems seem to not have any constraint at all.
62
- #{using_path} gems are using a local path. #{'WARNING!!!' if using_path > 0}
63
- EOS
40
+ using_path = benchers.count { |x| x.is_type?(:path) }
41
+ puts <<~EOS
42
+ #{" "}
43
+ The gems that need to be improved are:
44
+
45
+ #{benchers.map(&:to_s).join("\n")}
46
+
47
+ There are #{starters.length} gems that have valid strict version constraints.
48
+ Of those:
49
+ #{starters.count { |x| x.is_type?(:constraint) }} use primary constraints (e.g. '~> 1.2.3').
50
+ #{starters.count { |x| x.is_type?(:git_ref) }} use git ref constraints.
51
+ #{starters.count { |x| x.is_type?(:git_tag) }} use git tag constraints.
52
+
53
+ There are #{benchers.length} gems that do not have strict version constraints.
54
+ Of those:
55
+ #{benchers.count { |x| x.is_type?(:git_branch) }} use git branch constraints.
56
+ #{benchers.count { |x| x.is_type?(:git) }} use some other form of git constraint considered not strict enough.
57
+ #{benchers.count { |x| x.is_type?(:unknown) }} gems seem to not have any constraint at all.
58
+ #{using_path} gems are using a local path. #{"WARNING!!!" if using_path > 0}
59
+ EOS
64
60
  end
65
61
  end
66
62
  end