gem_bench 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: ❨╯°□°❩╯︵┻━┻'