gem_bench 1.0.1 → 1.0.2

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
2
  SHA1:
3
- metadata.gz: 02d3ad00484d97ad24c15de92b325eb2d867470a
4
- data.tar.gz: 0ae6392ec5541c4284254cff708b84f3c7169a4f
3
+ metadata.gz: 8f89228b4ede08a4d045edb84113a8aa4abe87c5
4
+ data.tar.gz: 0aed9f1441cd404b78218ddb4569fe40d5e0ba00
5
5
  SHA512:
6
- metadata.gz: 57f9368e98e27d4da8ea037a830f38f636e6a2ccc9ded026d83a259f376812d7306caff015f6e9947be60dfc5f6dab3020dc3572bc1397d8fea960247d2a9982
7
- data.tar.gz: d4a6c72e7fc06eb565c1b37329f4bc2fc2e61a5f7c9a2dd45450ac5f8dcb0e4d33f037c49a641639905d507fc0de8e5ac01e5942e1ecb41e8e596517b1deb077
6
+ metadata.gz: 260ff8d3d842a12bd4c0cda93e8bc3b08fe2df254dde06d9ac252d50a382a93c4b3ed465a768112e2624975b83039c1744d6e6a6374fcb2ced56e9a47c305e7e
7
+ data.tar.gz: 0be0973760997ecf07647d741fdeb85207f11afcb8ab5a6e6a86f80c7541d72268cb1a13bce2210b84ff4da5224b033beddb719dad933a09f2d6343fa217e9b7
data/.gitignore CHANGED
@@ -13,3 +13,4 @@
13
13
 
14
14
  *.gem
15
15
  /.idea/
16
+ .ruby-version
data/CHANGELOG CHANGED
@@ -1,3 +1,17 @@
1
+ Version 1.0.2 - JUN.02.2017
2
+ * version constraint checking, useful to add a spec enforcing Gemfile version constraints, by Peter Boling
3
+ - Console use:
4
+ - GemBench::StrictVersionRequirement.new({verbose: true})
5
+ - Spec use:
6
+ ```ruby
7
+ Rspec.describe "Gemfile" do
8
+ it("has version constraint on every gem") do
9
+ requirements = GemBench::StrictVersionRequirement.new({verbose: true}
10
+ expect(requirements.list_missing_version_constraints).to eq([])
11
+ end
12
+ end
13
+ ```
14
+
1
15
  Version 1.0.1 - MAR.25.2017
2
16
  * fixed a typo that prevented Gemfile comparison by mobilutz
3
17
 
data/README.md CHANGED
@@ -5,6 +5,17 @@
5
5
  Gem: "Put me in coach!"
6
6
  You: ❨╯°□°❩╯︵┻━┻
7
7
 
8
+ Version constraints are important. Give the Gemfile some love in your CI build - New for 1.0.2
9
+
10
+ ```ruby
11
+ Rspec.describe "Gemfile" do
12
+ it("has version constraint on every gem") do
13
+ requirements = GemBench::StrictVersionRequirement.new({verbose: true}
14
+ expect(requirements.list_missing_version_constraints).to eq([])
15
+ end
16
+ end
17
+ ```
18
+
8
19
  The new feature for 1.0.0 release allows you to search the Ruby code of all your gems for a specified regex, to find out which gems have wat DRAGONS.
9
20
 
10
21
  Gem: "I have no wat DRAGONS!"
@@ -20,10 +31,10 @@ byebug has wat DRAGONS at [["/Users/pboling/.rvm/gems/ruby-2.4.0@foss/gems/byebu
20
31
  NOTE: The number (954, above) is not a line number. The file which contains the text `wat` was the 954th file evaluated, i.e. the number doesn't matter.
21
32
  NOTE: This is a contrived example. The occurrence of `wat` in byebug is meaningless: `byebug/commands/frame.rb:34` has ` if there is a front end also watching over things.`. This is just an example! You can find anything you want, perhaps even something important!
22
33
 
23
- | Project | GemBench |
34
+ | Project | GemBench |
24
35
  |------------------------ | ----------------- |
25
- | gem name | gem_bench |
26
- | license | MIT |
36
+ | gem name | gem_bench |
37
+ | license | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) |
27
38
  | expert support | [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github) |
28
39
  | download rank | [![Download Rank](https://img.shields.io/gem/rt/gem_bench.svg)](https://rubygems.org/gems/gem_bench) |
29
40
  | version | [![Gem Version](https://badge.fury.io/rb/gem_bench.png)](http://badge.fury.io/rb/gem_bench) |
@@ -32,4 +32,5 @@ You: ❨╯°□°❩╯︵┻━┻}
32
32
  spec.add_development_dependency "rspec", "~> 3.5"
33
33
  spec.add_development_dependency "byebug", "~> 9.0"
34
34
  spec.add_development_dependency "gem-release", "~> 0.5"
35
+ spec.add_development_dependency "awesome_print"
35
36
  end
@@ -3,6 +3,9 @@ require "bundler" # This gem utilizes bundler as a tool.
3
3
  require "gem_bench/scout"
4
4
  require "gem_bench/player"
5
5
  require "gem_bench/team"
6
+ require "gem_bench/gemfile_line_tokenizer"
7
+ require "gem_bench/strict_version_gem"
8
+ require "gem_bench/strict_version_requirement"
6
9
 
7
10
  module GemBench
8
11
  USAGE = "[GemBench] Usage: Require another gem in this session to evaluate it.\n\tExample:\n\t\trequire 'rails'\n\t\tGemBench.check({verbose: true})\n"
@@ -11,7 +14,7 @@ module GemBench
11
14
  REQUIRE_FALSE_REGEX = /^[^#]+require(([:]\s*)|(\s*=>\s*))false.*/
12
15
  DEPENDENCY_REGEX_PROC = ->(name) { /^\s*[^#]*\s*gem\s+['"]{1}#{name}['"]{1}/ }
13
16
  PATH_GLOB = ->(name) { "#{name}*/lib/**/*.rb" }
14
- EXCLUDE_FILE_PATTERN_REGEX_PROC = ->(name) { /#{name}\/test|features|spec/ }
17
+ EXCLUDE_FILE_PATTERN_REGEX_PROC = ->(name) { /#{name}\/test|features|spec|bin|exe/ }
15
18
  DO_NOT_SCAN = []
16
19
  PLAYER_STATES = {
17
20
  starter: :starter,
@@ -24,6 +27,16 @@ module GemBench
24
27
  @roster = GemBench::Team.new({verbose: verbose})
25
28
  end
26
29
 
30
+ def versions_present?(verbose: false)
31
+ @roster = GemBench::StrictVersionRequirement.new({verbose: verbose})
32
+ @roster.versions_present?
33
+ end
34
+
35
+ def list_missing_version_constraints(verbose: false)
36
+ @roster = GemBench::StrictVersionRequirement.new({verbose: verbose})
37
+ @roster.list_missing_version_constraints
38
+ end
39
+
27
40
  def find(look_for_regex: GemBench::RAILTIE_REGEX, exclude_file_pattern_regex_proc: GemBench::EXCLUDE_FILE_PATTERN_REGEX_PROC, verbose: false)
28
41
  @roster = GemBench::Team.new({look_for_regex: look_for_regex, exclude_file_pattern_regex_proc: exclude_file_pattern_regex_proc, verbose: verbose})
29
42
  end
@@ -0,0 +1,181 @@
1
+ module GemBench
2
+ class GemfileLineTokenizer
3
+ GEM_REGEX = /\A\s*gem\s+([^#]*).*\Z/.freeze # run against gem lines like: "gem 'aftership', # Ruby SDK of AfterShip API."
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
+ 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_GIT_REF = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('ref').freeze
10
+ VERSION_GIT_TAG = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('tag').freeze
11
+ VERSION_GIT_BRANCH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call('branch').freeze
12
+ VALID_VERSION_TYPES = %i(
13
+ constraint
14
+ git_ref
15
+ git_tag
16
+ )
17
+ # branch is only valid if the branch is not master
18
+ attr_reader :line
19
+ attr_reader :relevant_lines
20
+ attr_reader :is_gem
21
+ attr_reader :all_lines
22
+ attr_reader :index
23
+ attr_reader :tokens
24
+ # version will be a string if it is a normal constraint like '~> 1.2.3'
25
+ # version will be a hash if it is an alternative constraint like:
26
+ # git: "blah/blah", ref: "shasha"
27
+ attr_reader :version
28
+ attr_reader :version_type
29
+ attr_reader :name
30
+ attr_reader :parse_success
31
+ attr_reader :valid
32
+ def initialize(all_lines, line, index)
33
+ @line = line.strip
34
+ @is_gem = self.line.match(GEM_REGEX)
35
+ if self.is_gem
36
+ @all_lines = all_lines
37
+ @index = index
38
+ @tokens = self.line.split(',')
39
+ determine_name
40
+ if self.name
41
+ determine_relevant_lines
42
+ determine_version
43
+ @parse_success = true
44
+ @valid = VALID_VERSION_TYPES.include?(self.version_type)
45
+ else
46
+ noop
47
+ end
48
+ else
49
+ noop
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # not a gem line. noop.
56
+ def noop
57
+ @parse_success = false
58
+ @valid = false
59
+ end
60
+
61
+ def determine_name
62
+ # uses @tokens[0] because the gem name must be before the first comma
63
+ match_data = @tokens[0].match(GEM_NAME_REGEX)
64
+ @name = match_data[:name]
65
+ end
66
+
67
+ def determine_relevant_lines
68
+ @relevant_lines = [line, *following_non_gem_lines].compact
69
+ end
70
+
71
+ def determine_version
72
+ version_path ||
73
+ (
74
+ version_git && (
75
+ check_for_version_of_type_git_ref ||
76
+ check_for_version_of_type_git_tag ||
77
+ check_for_version_of_type_git_branch
78
+ )
79
+ ) ||
80
+ # Needs to be the last check because it can only check for a quoted string,
81
+ # and quoted strings are part of the other types, so they have to be checked first with higher specificity
82
+ check_for_version_of_type_constraint
83
+ end
84
+
85
+ def check_for_version_of_type_constraint
86
+ # index 1 of the comma-split tokens will usually be the version constraint, if there is one
87
+ possible_constraint = @tokens[1]
88
+ return false unless possible_constraint
89
+ match_data = possible_constraint.strip.match(VERSION_CONSTRAINT)
90
+ # the version constraint is in a regex capture group
91
+ if match_data && (@version = match_data[1].strip)
92
+ @version_type = :constraint
93
+ true
94
+ else
95
+ false
96
+ end
97
+ end
98
+
99
+ def version_path
100
+ @version = {}
101
+ line = relevant_lines.detect { |next_line| (next_line.match(VERSION_PATH)) }
102
+ return false unless line
103
+ enhance_version(
104
+ line.match(VERSION_PATH),
105
+ :path,
106
+ :path
107
+ )
108
+ end
109
+
110
+ def version_git
111
+ @version = {}
112
+ line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT)) }
113
+ return false unless line
114
+ enhance_version(
115
+ line.match(VERSION_GIT),
116
+ :git,
117
+ :git
118
+ )
119
+ end
120
+
121
+ def check_for_version_of_type_git_ref
122
+ line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT_REF)) }
123
+ return false unless line
124
+ enhance_version(
125
+ line.match(VERSION_GIT_REF),
126
+ :ref,
127
+ :git_ref
128
+ )
129
+ end
130
+
131
+ def check_for_version_of_type_git_tag
132
+ line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT_TAG)) }
133
+ return false unless line
134
+ enhance_version(
135
+ line.match(VERSION_GIT_TAG),
136
+ :tag,
137
+ :git_tag
138
+ )
139
+ end
140
+
141
+ def check_for_version_of_type_git_branch
142
+ line = relevant_lines.detect { |next_line| (next_line.match(VERSION_GIT_BRANCH)) }
143
+ return false unless line
144
+ enhance_version(
145
+ line.match(VERSION_GIT_BRANCH),
146
+ :branch,
147
+ :git_branch
148
+ )
149
+ end
150
+
151
+ # returns an array with each line following the current line, which is not a gem line
152
+ def following_non_gem_lines
153
+ all_lines[(index+1)..(-1)].
154
+ reject {|x| x.strip.empty? || x.match(GemBench::TRASH_REGEX) }.
155
+ map(&:strip).
156
+ inject([]) do |following_lines, next_line|
157
+ break following_lines if next_line.match(GEM_REGEX)
158
+ following_lines << next_line
159
+ end
160
+ end
161
+
162
+ # returns a hash like:
163
+ # {"key" => ":git => ", "value" => "https://github.com/cte/aftership-sdk-ruby.git"}
164
+ def normalize_match_data_captures(match_data)
165
+ match_data.names.inject({}) do |mem, capture|
166
+ mem[capture.gsub(/\d/,'')] = match_data[capture]
167
+ break mem if mem.keys.length >= 2
168
+ mem
169
+ end
170
+ end
171
+
172
+ def enhance_version(match_data, version_key, type)
173
+ return false unless match_data
174
+ normalized_capture = normalize_match_data_captures(match_data) if match_data
175
+ return false unless normalized_capture
176
+ @version.merge!({version_key => normalized_capture["value"]})
177
+ @version_type = type
178
+ true
179
+ end
180
+ end
181
+ end
@@ -8,8 +8,6 @@ module GemBench
8
8
  @gemfile_path = "#{Dir.pwd}/Gemfile"
9
9
  gem_lookup_paths_from_bundler
10
10
  gem_lines_from_gemfile
11
- # 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
11
  end
14
12
 
15
13
  def check_gemfile?
@@ -0,0 +1,55 @@
1
+ module GemBench
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
10
+
11
+ class << self
12
+ def from_line(all_lines, line, index, opts = {})
13
+ tokenized_line = GemfileLineTokenizer.new(all_lines, line, index)
14
+ return nil unless tokenized_line.is_gem
15
+ 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
23
+ )
24
+ end
25
+ end
26
+
27
+ def initialize(name, version, version_type, valid, relevant_lines, index, tokenized_line = nil)
28
+ @name = name
29
+ @version = version
30
+ @version_type = version_type ? version_type.to_sym : :unknown
31
+ @valid = valid
32
+ @relevant_lines = relevant_lines
33
+ @index = index
34
+ @tokenized_line = tokenized_line # for debugging
35
+ end
36
+
37
+ def valid?
38
+ valid
39
+ end
40
+
41
+ def is_type?(type)
42
+ version_type == type.to_sym
43
+ end
44
+
45
+ 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")}
52
+ EOS
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,66 @@
1
+ module GemBench
2
+ class StrictVersionRequirement
3
+ attr_reader :gemfile_path
4
+ attr_reader :gems
5
+ attr_reader :starters
6
+ attr_reader :benchers
7
+ attr_reader :verbose
8
+
9
+ def initialize(options = {})
10
+ @gemfile_path = "#{Dir.pwd}/Gemfile"
11
+ file = File.open(gemfile_path)
12
+ # Get all lines as an array
13
+ all_lines = file.readlines
14
+ @gems = []
15
+ all_lines.each_with_index do |line, index|
16
+ # will return nil if the line is not a gem line
17
+ gem = StrictVersionGem.from_line(all_lines, line, index)
18
+ @gems << gem if gem
19
+ end
20
+
21
+ @starters, @benchers = @gems.partition {|x| x.valid? }
22
+ # Remove all the commented || blank lines
23
+ @verbose = options[:verbose]
24
+ self.print if self.verbose
25
+ end
26
+
27
+ def versions_present?
28
+ gems.detect {|x| !x.valid? }.nil?
29
+ end
30
+
31
+ def list_missing_version_constraints
32
+ benchers.map { |x| x.name }
33
+ end
34
+
35
+ def find(name)
36
+ gems.detect {|x| x.name == name }
37
+ end
38
+
39
+ def gem_at(index)
40
+ gems.detect {|x| x.index == index }
41
+ end
42
+
43
+ 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
64
+ end
65
+ end
66
+ end
@@ -1,3 +1,3 @@
1
1
  module GemBench
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_bench
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Boling
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-25 00:00:00.000000000 Z
11
+ date: 2017-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: awesome_print
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: trim down app load times by keeping your worst players on the bench
84
98
  email:
85
99
  - peter.boling@gmail.com
@@ -100,8 +114,11 @@ files:
100
114
  - bin/setup
101
115
  - gem_bench.gemspec
102
116
  - lib/gem_bench.rb
117
+ - lib/gem_bench/gemfile_line_tokenizer.rb
103
118
  - lib/gem_bench/player.rb
104
119
  - lib/gem_bench/scout.rb
120
+ - lib/gem_bench/strict_version_gem.rb
121
+ - lib/gem_bench/strict_version_requirement.rb
105
122
  - lib/gem_bench/team.rb
106
123
  - lib/gem_bench/version.rb
107
124
  homepage: http://github.com/acquaintable/gem_bench
@@ -124,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
141
  version: '0'
125
142
  requirements: []
126
143
  rubyforge_project:
127
- rubygems_version: 2.6.8
144
+ rubygems_version: 2.6.12
128
145
  signing_key:
129
146
  specification_version: 4
130
147
  summary: 'Gem: "Put me in coach" You: ❨╯°□°❩╯︵┻━┻'