gem_bench 2.0.3 → 2.0.4

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
  SHA256:
3
- metadata.gz: ba58266e066c47120ecba80dcd81b5aa35cf2e35132e5eb8f8bc62a0cfdcb8b4
4
- data.tar.gz: 9b491c517466e244b98f8d01152daa213a5947d9de92970abae6ad64076842ef
3
+ metadata.gz: 47591b1b5f1880cbb2c40be1199aa7fa65a25d2c32ca52131ba54e0e89d8b6b7
4
+ data.tar.gz: 93aad6d4e020398b93ffdaa2e8fddfce5b5e5cc4fa08854f22850dcccee9f0ee
5
5
  SHA512:
6
- metadata.gz: 8eb0b66dc41d9913fad9e88599908cf0e1787e01701e64ade5e065ac4428853b726855345704742a9279dc7dba74fef2eea4c7d4eb84495f7cc46438b07051a5
7
- data.tar.gz: 9f8b0b6419d6c8ffeb0999149a4c7bc797723dd3a4e362025644c83813e4cb5e3c065b4b7e0bcd8e55d941d7ad3a395072510a7895bfe910b3eb8fad0b6627a9
6
+ metadata.gz: 47827b59b13ee527cecfc8db349437c0fc416aa53fa83b4138a6c1884652d857ce9be708ec534e099b8a41f47914b831d8f73e09e430134f4deb050714a1925a
7
+ data.tar.gz: 9ae3067637e3293209bde40127e55ad42882b075ca71fc65e7b025ec5dd4e160f5669767e6990a8a0db915ff6031f1555e432cc6a110e32b09346a710fd304bf
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -11,6 +11,23 @@ and this project adheres to [Semantic Versioning v2](https://semver.org/spec/v2.
11
11
  ### Fixed
12
12
  ### Removed
13
13
 
14
+ ## [2.0.4] SEP.20.2024
15
+ - COVERAGE: 98.19% -- 488/497 lines in 9 files
16
+ - BRANCH COVERAGE: 88.95% -- 161/181 branches in 9 files
17
+ - 58.06% documented
18
+ ### Added
19
+ - More Documentation
20
+ - Almost 100% test coverage
21
+ - Thread Safety (removed `GemBench.roster`, which was effectively never used internally)
22
+ - Performance improvements
23
+ ### Fixed
24
+ - Can now handle more variations of Ruby syntax in the Gemfile analyzer
25
+ - `require_relative` > `require` for internal files (except for `spec` => `lib`)
26
+ - Updated logic for version specified via git with branch, tag, ref (to match fixes to Bundler's behavior)
27
+ - Ambiguous naming of `GemBench::Jersey#primary_namespace` is split to:
28
+ - `#doffed_primary_namespace`
29
+ - `#donned_primary_namespace`
30
+
14
31
  ## [2.0.3] SEP.18.2024
15
32
  ### Added
16
33
  - More documentation
@@ -19,9 +36,9 @@ and this project adheres to [Semantic Versioning v2](https://semver.org/spec/v2.
19
36
  - Copyright years
20
37
 
21
38
  ## [2.0.2] SEP.17.2024
22
- COVERAGE: 82.15% -- 382/465 lines in 9 files
23
- BRANCH COVERAGE: 58.79% -- 97/165 branches in 9 files
24
- 51.72% documented
39
+ - COVERAGE: 82.15% -- 382/465 lines in 9 files
40
+ - BRANCH COVERAGE: 58.79% -- 97/165 branches in 9 files
41
+ - 51.72% documented
25
42
  ### Added
26
43
  - CI for Ancient Rubies
27
44
  - Ruby 2.3
@@ -34,9 +51,9 @@ BRANCH COVERAGE: 58.79% -- 97/165 branches in 9 files
34
51
  - Gemspec description & Summary
35
52
 
36
53
  ## [2.0.1] SEP.17.2024
37
- COVERAGE: 82.15% -- 382/465 lines in 9 files
38
- BRANCH COVERAGE: 58.08% -- 97/167 branches in 9 files
39
- 51.72% documented
54
+ - COVERAGE: 82.15% -- 382/465 lines in 9 files
55
+ - BRANCH COVERAGE: 58.08% -- 97/167 branches in 9 files
56
+ - 51.72% documented
40
57
  ### Added
41
58
  - Ability to re-namespace and load copy of a gem alongside vanilla version for benchmarking via `GemBench::Jersey`
42
59
  - See: https://github.com/panorama-ed/memo_wise/pull/339
data/README.md CHANGED
@@ -98,14 +98,13 @@ Regex search through all of a project's source code, including installed Bundler
98
98
 
99
99
  🛟 Trim down app load times, such as on Heroku, by finding and keeping your worst players on the bench.
100
100
 
101
- | Project | GemBench |
102
- |----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
103
- | gem name | [gem_bench](https://rubygems.org/gems/gem_bench) |
104
- | code triage | [![Open Source Helpers](https://www.codetriage.com/pboling/gem_bench/badges/users.svg)](https://www.codetriage.com/pboling/gem_bench) |
105
- | homepage | [on Github.com][homepage] |
106
- | documentation | [on Rdoc.info][documentation] |
107
- | 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) |
108
- | `...` 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Follow Me on LinkedIn][🖇linkedin-img]][🖇linkedin] [![Find Me on WellFound:][✌️wellfound-img]][✌️wellfound] [![Find Me on CrunchBase][💲crunchbase-img]][💲crunchbase] [![My LinkTree][🌳linktree-img]][🌳linktree] [![Follow Me on Ruby.Social][🐘ruby-mast-img]][🐘ruby-mast] [![Tweet @ Peter][🐦tweet-img]][🐦tweet] [💻][coderme] [🌏][aboutme] |
101
+ | Primary Namespace | `GemBench` |
102
+ |-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
103
+ | gem name | [gem_bench](https://rubygems.org/gems/gem_bench) |
104
+ | code triage | [![Open Source Helpers](https://www.codetriage.com/pboling/gem_bench/badges/users.svg)](https://www.codetriage.com/pboling/gem_bench) |
105
+ | documentation | [on Github.com][homepage], [on Rdoc.info][documentation] |
106
+ | 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) |
107
+ | `...` 💖 | [![Liberapay Patrons][⛳liberapay-img]][⛳liberapay] [![Sponsor Me][🖇sponsor-img]][🖇sponsor] [![Follow Me on LinkedIn][🖇linkedin-img]][🖇linkedin] [![Find Me on WellFound:][✌️wellfound-img]][✌️wellfound] [![Find Me on CrunchBase][💲crunchbase-img]][💲crunchbase] [![My LinkTree][🌳linktree-img]][🌳linktree] [![Follow Me on Ruby.Social][🐘ruby-mast-img]][🐘ruby-mast] [![Tweet @ Peter][🐦tweet-img]][🐦tweet] [💻][coderme] [🌏][aboutme] |
109
108
 
110
109
  <!-- 7️⃣ spread 💖 -->
111
110
  [🐦tweet-img]: https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow%20%40galtzo
@@ -163,8 +162,43 @@ NOTE: It is not required by default, so you do need to require the Jersey if you
163
162
  require "gem_bench/jersey"
164
163
  ```
165
164
 
165
+ #### Usage
166
+
167
+ If the original gem defines multiple top-level namespaces,
168
+ they can all be renamed by providing more key value pairs in `trades`.
169
+ If the original gem monkey patches other libraries,
170
+ that behavior can't be isolated, so YMMV.
171
+
172
+ NOTE: Non-top-level namespaces do not need to be renamed,
173
+ as they are isolated within their parent namespace.
174
+
175
+ #### Example
176
+
166
177
  For a real example, see: https://github.com/panorama-ed/memo_wise/pull/339
167
178
 
179
+ #### Naming: Why `doff_and_don`?
180
+
181
+ > Wouldn't "copy_gem" make sense?
182
+
183
+ Actually copy is an overloaded term, which creates confusion in this use case.
184
+ This method Generates a temp directory, and creates a copy of a gem within it.
185
+ Re-namespaces the copy according to the `trades` configuration.
186
+ Then requires each file of the "copied gem", resulting
187
+ in a loaded gem that will not have namespace
188
+ collisions when loaded alongside the original-namespaced gem.
189
+ Note that "copied gem" in the previous sentence is ambiguous without the supporting context.
190
+ The "copied gem" can mean either the original, or the "copy", which is why this gem refers to
191
+ a "doffed gem" (the original) and a "donned gem" (the copy).
192
+
193
+ Also because `Jersey`. Duh...
194
+
195
+ #### Advanced Usage
196
+
197
+ If a block is provided the contents of each file will be yielded to the block,
198
+ after all namespace substitutions from `trades` are complete, but before the contents
199
+ are written to the donned (re-namespaced) gem. The return value of the block will be
200
+ written to the file in this scenario.
201
+
168
202
  ### New for 2.0.0 - Dropped Support for Ruby 2.0, 2.1, 2.2
169
203
 
170
204
  - Required Ruby is now 2.3+
@@ -1,14 +1,51 @@
1
1
  module GemBench
2
+ # Line by line parser of a Gemfile
3
+ # Uses regular expressions, which, I know, GROSS, but also meh
4
+ # You aren't using this as a runtime gem in your app are you?
5
+ # Let me know how you use it!
2
6
  class GemfileLineTokenizer
3
7
  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
8
+ # HEREDOC support? (?<op_heredoc><<[~-]?[A-Z0-9_]+\.?[a-z0-9_]+)
9
+ OP_QUO_REG_PROC = lambda { |idx = nil| /((?<op_quo#{idx}>['"]{1})|(?<op_pct_q#{idx}>%[Qq]?[\(\[\{]{1})|(?<op_heredoc><<[~-]?[A-Z0-9_]+\.?[a-z0-9_]+))/x }
10
+ # No close for the heredoc, as it will be on a different line...
11
+ CL_QUO_REG_PROC = lambda { |idx = nil| /((?<cl_quo#{idx}>['"])|(?<cl_pct_q#{idx}>[\)\]\}]{1}))/x }
12
+ GEM_NAME_REGEX = /\A\s*gem\s+#{OP_QUO_REG_PROC.call.source}(?<name>[^'")]*)#{CL_QUO_REG_PROC.call.source}?.*\Z/.freeze # run against gem lines like: "gem 'aftership', # Ruby SDK of AfterShip API."
13
+ VERSION_CONSTRAINT = /#{OP_QUO_REG_PROC.call.source}(?<version>[^'")]*)#{CL_QUO_REG_PROC.call.source}/.freeze
6
14
  GEMFILE_HASH_CONFIG_KEY_REGEX_PROC = lambda { |key|
7
- /\A\s*[^#]*(?<key1>#{key}: *)['"]{1}(?<value1>[^'"]*)['"]|(?<key2>['"]#{key}['"] *=> *)['"]{1}(?<value2>[^'"]*)['"]|(?<key3>:#{key} *=> *)['"]{1}(?<value3>[^'"]*)['"]/
15
+ /
16
+ \A\s*[^#]*
17
+ (
18
+ # when key is "branch" will find: `branch: "main"`
19
+ (?<key1>#{key}:\s*)
20
+ #{OP_QUO_REG_PROC.call("k1").source}(?<value1>[^'")]*)?#{CL_QUO_REG_PROC.call("k1").source}
21
+ )
22
+ |
23
+ (
24
+ # when key is "branch" will find: `"branch" => "main"`
25
+ (?<key2>#{OP_QUO_REG_PROC.call("k2a").source}#{key}#{CL_QUO_REG_PROC.call("k2a").source}\s*=>\s*)
26
+ #{OP_QUO_REG_PROC.call("k2b").source}(?<value2>[^'")]*)?#{CL_QUO_REG_PROC.call("k2b").source}
27
+ )
28
+ |
29
+ (
30
+ # when key is "branch" will find: `:branch => "main"`
31
+ (?<key3>:#{key}\s*=>\s*)
32
+ #{OP_QUO_REG_PROC.call("k3").source}(?<value3>[^'")]*)?#{CL_QUO_REG_PROC.call("k3").source}
33
+ )
34
+ |
35
+ (
36
+ # when key is "branch" will find: `"branch": "main"`
37
+ (?<key4>#{OP_QUO_REG_PROC.call("k4a").source}#{key}#{CL_QUO_REG_PROC.call("k4a").source}:\s*)
38
+ #{OP_QUO_REG_PROC.call("k4b").source}(?<value4>[^'")]*)?#{CL_QUO_REG_PROC.call("k4b").source}
39
+ )
40
+ /x
8
41
  }
9
42
  VERSION_PATH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("path").freeze
10
43
  VERSION_GIT = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("git").freeze
11
44
  VERSION_GITHUB = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("github").freeze
45
+ VERSION_GITLAB = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("gitlab").freeze
46
+ VERSION_BITBUCKET = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("bitbucket").freeze
47
+ VERSION_CODEBERG = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("codeberg").freeze
48
+ VERSION_SRCHUT = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("srchut").freeze
12
49
  VERSION_GIT_REF = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("ref").freeze
13
50
  VERSION_GIT_TAG = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("tag").freeze
14
51
  VERSION_GIT_BRANCH = GEMFILE_HASH_CONFIG_KEY_REGEX_PROC.call("branch").freeze
@@ -17,13 +54,19 @@ module GemBench
17
54
  git_ref
18
55
  git_tag
19
56
  ]
20
- # branch is only valid if the branch is not master
57
+ STR_SYNTAX_TYPES = {
58
+ quoted: true,
59
+ pct_q: true,
60
+ # We could try to support HEREDOC via parsing the lines following in all_lines, but... ugh.
61
+ heredoc: false,
62
+ unknown: false,
63
+ }.freeze
21
64
  attr_reader :line
22
65
  attr_reader :relevant_lines, :is_gem, :all_lines, :index, :tokens, :version_type, :name, :parse_success, :valid
23
66
  # version will be a string if it is a normal constraint like '~> 1.2.3'
24
67
  # version will be a hash if it is an alternative constraint like:
25
68
  # git: "blah/blah", ref: "shasha"
26
- attr_reader :version
69
+ attr_reader :version, :str_syntax_type
27
70
 
28
71
  def initialize(all_lines, line, index)
29
72
  @line = line.strip
@@ -31,9 +74,9 @@ module GemBench
31
74
  if is_gem
32
75
  @all_lines = all_lines
33
76
  @index = index
34
- @tokens = self.line.split(",")
77
+ @tokens = self.line.split(",").map(&:strip)
35
78
  determine_name
36
- if name
79
+ if name && STR_SYNTAX_TYPES[str_syntax_type]
37
80
  determine_relevant_lines
38
81
  determine_version
39
82
  @parse_success = true
@@ -49,35 +92,61 @@ module GemBench
49
92
  private
50
93
 
51
94
  # not a gem line. noop.
95
+ #
96
+ # @return void
52
97
  def noop
53
98
  @parse_success = false
54
99
  @valid = false
100
+
101
+ nil
55
102
  end
56
103
 
104
+ # @return void
57
105
  def determine_name
58
106
  # uses @tokens[0] because the gem name must be before the first comma
59
107
  match_data = @tokens[0].match(GEM_NAME_REGEX)
108
+ @str_syntax_type =
109
+ if match_data[:op_quo] && match_data[:cl_quo]
110
+ :quoted
111
+ elsif match_data[:op_pct_q] && match_data[:cl_pct_q]
112
+ :pct_q
113
+ # Not handling heredoc, aside from not exploding, as it isn't a reasonable use case.
114
+ # elsif match_data[:op_heredoc]
115
+ # :heredoc
116
+ else
117
+ :unknown
118
+ end
60
119
  @name = match_data[:name]
120
+
121
+ nil
61
122
  end
62
123
 
124
+ # @return void
63
125
  def determine_relevant_lines
64
126
  @relevant_lines = [line, *following_non_gem_lines].compact
127
+
128
+ nil
65
129
  end
66
130
 
131
+ # @return void
67
132
  def determine_version
68
- version_path ||
69
- (
70
- (version_git || version_github) && (
71
- check_for_version_of_type_git_ref ||
72
- check_for_version_of_type_git_tag ||
73
- check_for_version_of_type_git_branch
74
- )
75
- ) ||
133
+ @version = {}
134
+ return if version_path
135
+
136
+ (
137
+ (version_git || version_provider) && (
138
+ check_for_version_of_type_git_tag ||
139
+ check_for_version_of_type_git_branch
140
+ )
141
+ ) ||
76
142
  # Needs to be the last check because it can only check for a quoted string,
77
143
  # and quoted strings are part of the other types, so they have to be checked first with higher specificity
78
144
  check_for_version_of_type_constraint
145
+
146
+ nil
79
147
  end
80
148
 
149
+ # @return [true, false]
81
150
  def check_for_version_of_type_constraint
82
151
  # index 1 of the comma-split tokens will usually be the version constraint, if there is one
83
152
  possible_constraint = @tokens[1]
@@ -85,7 +154,7 @@ module GemBench
85
154
 
86
155
  match_data = possible_constraint.strip.match(VERSION_CONSTRAINT)
87
156
  # the version constraint is in a regex capture group
88
- if match_data && (@version = match_data[1].strip)
157
+ if match_data && (@version = match_data[:version].strip)
89
158
  @version_type = :constraint
90
159
  true
91
160
  else
@@ -93,8 +162,8 @@ module GemBench
93
162
  end
94
163
  end
95
164
 
165
+ # @return [true, false]
96
166
  def version_path
97
- @version = {}
98
167
  line = relevant_lines.detect { |next_line| next_line.match(VERSION_PATH) }
99
168
  return false unless line
100
169
 
@@ -105,8 +174,8 @@ module GemBench
105
174
  )
106
175
  end
107
176
 
177
+ # @return [true, false]
108
178
  def version_git
109
- @version = {}
110
179
  line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT) }
111
180
  return false unless line
112
181
 
@@ -117,21 +186,40 @@ module GemBench
117
186
  )
118
187
  end
119
188
 
120
- def version_github
121
- @version = {}
122
- line = relevant_lines.detect { |next_line| next_line.match(VERSION_GITHUB) }
123
- return false unless line
189
+ # @return [true, false]
190
+ def version_provider
191
+ matcher = nil
192
+ line = relevant_lines.detect do |next_line|
193
+ matcher =
194
+ case next_line
195
+ when VERSION_GITHUB
196
+ VERSION_GITHUB
197
+ when VERSION_GITLAB
198
+ VERSION_GITLAB
199
+ when VERSION_BITBUCKET
200
+ VERSION_BITBUCKET
201
+ when VERSION_CODEBERG
202
+ VERSION_CODEBERG
203
+ when VERSION_SRCHUT
204
+ VERSION_SRCHUT
205
+ end
206
+ end
207
+ return false unless line && matcher
124
208
 
125
209
  enhance_version(
126
- line.match(VERSION_GITHUB),
210
+ line.match(matcher),
127
211
  :github,
128
212
  :github,
129
213
  )
130
214
  end
131
215
 
132
- def check_for_version_of_type_git_ref
216
+ # @return [true, false]
217
+ def check_for_version_of_type_git_branch
218
+ return false unless _check_for_version_of_type_git_branch
219
+
133
220
  line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT_REF) }
134
- return false unless line
221
+ # At this point we at least have a branch, though perhaps not a ref.
222
+ return true unless line
135
223
 
136
224
  enhance_version(
137
225
  line.match(VERSION_GIT_REF),
@@ -140,6 +228,7 @@ module GemBench
140
228
  )
141
229
  end
142
230
 
231
+ # @return [true, false]
143
232
  def check_for_version_of_type_git_tag
144
233
  line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT_TAG) }
145
234
  return false unless line
@@ -151,7 +240,8 @@ module GemBench
151
240
  )
152
241
  end
153
242
 
154
- def check_for_version_of_type_git_branch
243
+ # @return [true, false]
244
+ def _check_for_version_of_type_git_branch
155
245
  line = relevant_lines.detect { |next_line| next_line.match(VERSION_GIT_BRANCH) }
156
246
  return false unless line
157
247
 
@@ -162,7 +252,7 @@ module GemBench
162
252
  )
163
253
  end
164
254
 
165
- # returns an array with each line following the current line, which is not a gem line
255
+ # @return [Array[String]] - each line following the current line, which is not a gem line
166
256
  def following_non_gem_lines
167
257
  all_lines[(index + 1)..-1]
168
258
  .reject { |x| x.strip.empty? || x.match(GemBench::TRASH_REGEX) }
@@ -174,23 +264,25 @@ module GemBench
174
264
  end
175
265
  end
176
266
 
177
- # returns a hash like:
178
- # {"key" => ":git => ", "value" => "https://github.com/cte/aftership-sdk-ruby.git"}
179
- def normalize_match_data_captures(match_data)
180
- match_data.names.each_with_object({}) do |capture, mem|
181
- mem[capture.gsub(/\d/, "")] = match_data[capture]
182
- break mem if mem.keys.length >= 2
183
- end
267
+ # @return [String] the name of the named capture which has a value (one of: value1, value2, value3, etc.)
268
+ def determine_named_capture(match_data)
269
+ match_data.names
270
+ .select { |name| name.start_with?("value") }
271
+ .detect { |capture| !match_data[capture]&.empty? }
184
272
  end
185
273
 
274
+ # @return [true]
186
275
  def enhance_version(match_data, version_key, type)
187
- return false unless match_data
188
-
189
- normalized_capture = normalize_match_data_captures(match_data) if match_data
190
- return false unless normalized_capture
276
+ named_capture = determine_named_capture(match_data)
277
+ value = match_data[named_capture]
278
+ if value
279
+ @version[version_key] = value
280
+ @version_type = type
281
+ else
282
+ @version[version_key] = ""
283
+ @version_type = :invalid
284
+ end
191
285
 
192
- @version.merge!({version_key => normalized_capture["value"]})
193
- @version_type = type
194
286
  true
195
287
  end
196
288
  end
@@ -1,29 +1,32 @@
1
1
  # Std Libs Dependencies
2
2
  require "tmpdir"
3
3
 
4
- # Re-write a gem to a temp directory, re-namespace the primary namespace of that gem module, and load it.
5
- # If the original gem defines multiple top-level namespaces, they can all be renamed by providing more key value pairs.
6
- # If the original gem monkey patches other libraries, that behavior can't be isolated, so YMMV.
7
- #
8
- # NOTE: Non-top-level namespaces do not need to be renamed, as they are isolated within their parent namespace.
9
- #
10
- # Usage
11
- #
12
- # jersey = GemBench::Jersey.new(
13
- # gem_name: "alt_memery"
14
- # trades: {
15
- # "Memery" => "AltMemery"
16
- # },
17
- # metadata: {
18
- # something: "a value here",
19
- # something_else: :obviously,
20
- # },
21
- # )
22
- # jersey.doff_and_don
23
- # # The re-namespaced constant is now available!
24
- # AltMemery # => AltMemery
25
- #
26
4
  module GemBench
5
+ # Re-write a gem to a temp directory, re-namespace the primary namespace of that gem module, and load it.
6
+ # If the original gem defines multiple top-level namespaces, they can all be renamed by providing more key value pairs.
7
+ # If the original gem monkey patches other libraries, that behavior can't be isolated, so YMMV.
8
+ #
9
+ # NOTE: Non-top-level namespaces do not need to be renamed, as they are isolated within their parent namespace.
10
+ #
11
+ # Usage
12
+ #
13
+ # jersey = GemBench::Jersey.new(
14
+ # gem_name: "alt_memery"
15
+ # trades: {
16
+ # "Memery" => "AltMemery"
17
+ # },
18
+ # metadata: {
19
+ # something: "a value here",
20
+ # something_else: :obviously,
21
+ # },
22
+ # )
23
+ # jersey.doff_and_don
24
+ # # The re-namespaced constant is now available!
25
+ # AltMemery # => AltMemery
26
+ #
27
+ # Benchmarking Example
28
+ #
29
+ # See: https://github.com/panorama-ed/memo_wise/blob/main/benchmarks/benchmarks.rb
27
30
  class Jersey
28
31
  attr_reader :gem_name
29
32
  attr_reader :gem_path
@@ -41,110 +44,141 @@ module GemBench
41
44
  @verbose = verbose
42
45
  end
43
46
 
47
+ # return [true, false] proxy for whether the copied, re-namespaced gem has been successfully loaded
44
48
  def required?
45
49
  gem_path && trades.values.all? { |new_namespace| Object.const_defined?(new_namespace) }
46
50
  end
47
51
 
48
- # Generates tempfiles and requires them, resulting
52
+ # Generates a temp directory, and creates a copy of a gem within it.
53
+ # Re-namespaces the copy according to the `trades` configuration.
54
+ # Then requires each file of the "copied gem", resulting
49
55
  # in a loaded gem that will not have namespace
50
- # collisions when alongside the original-namespaced gem.
56
+ # collisions when loaded alongside the original-namespaced gem.
57
+ # Note that "copied gem" in the previous sentence is ambiguous without the supporting context.
58
+ # The "copied gem" can mean either the original, or the "copy", which is why this gem refers to
59
+ # a "doffed gem" (the original) and a "donned gem" (the copy).
51
60
  # If a block is provided the contents of each file will be yielded to the block,
52
- # after all namespace substitutions are complete, but before the contents
61
+ # after all namespace substitutions from `trades` are complete, but before the contents
53
62
  # are written to the re-namespaced gem. The return value of the block will be
54
63
  # written to the file in this scenario.
55
64
  #
56
65
  # @return void
57
66
  def doff_and_don(&block)
58
- return puts "Skipping #{gem_name} (not loaded on #{RUBY_VERSION})" unless gem_path
67
+ return puts "[#{gem_name}] Skipped (not loaded on #{RUBY_VERSION})" unless gem_path
59
68
 
60
- puts "Doffing #{gem_path}" if verbose
61
- Dir.mktmpdir do |directory|
69
+ puts "[#{gem_name}] Doffing #{gem_path}" if verbose
70
+ Dir.mktmpdir do |tmp_dir|
62
71
  files = []
63
72
  Dir[File.join(gem_path, "lib", "**", "*.rb")].map do |file|
64
73
  if verbose
65
- puts file
66
- puts File.basename(file)
67
- puts "--------------------------------"
74
+ puts "[#{gem_name}] --------------------------------"
75
+ puts "[#{gem_name}] Doffing file #{file}"
76
+ puts "[#{gem_name}] --------------------------------"
68
77
  end
78
+ basename = File.basename(file)
69
79
  dirname = File.dirname(file)
70
- puts "dirname: #{dirname}" if verbose
80
+ puts "[#{gem_name}][#{basename}] dirname: #{dirname}" if verbose
71
81
  is_at_gem_root = dirname[(-4)..(-1)] == "/lib"
72
- puts "is_at_gem_root: #{is_at_gem_root}" if verbose
82
+ puts "[#{gem_name}][#{basename}] is_at_gem_root: #{is_at_gem_root}" if verbose
73
83
  lib_split = dirname.split("/lib/")[-1]
74
- puts "lib_split: #{lib_split}" if verbose
84
+ puts "[#{gem_name}][#{basename}] lib_split: #{lib_split}" if verbose
75
85
  # lib_split could be like:
76
86
  # - "ruby/gems/3.2.0/gems/method_source-1.1.0/lib"
77
87
  # - "method_source"
78
88
  # Se we check to make sure it is actually a subdir of the gem's lib directory
79
89
  full_path = File.join(gem_path, "lib", lib_split)
80
90
  relative_path = !is_at_gem_root && Dir.exist?(full_path) && lib_split
81
- puts "relative_path: #{relative_path}" if verbose
82
- filename = File.basename(file)[0..-4]
83
- puts "filename: #{filename}" if verbose
91
+ puts "[#{gem_name}][#{basename}] relative_path: #{relative_path}" if verbose
84
92
 
85
93
  if relative_path
86
- dir_path = File.join(directory, relative_path)
94
+ dir_path = File.join(tmp_dir, relative_path)
87
95
  Dir.mkdir(dir_path) unless Dir.exist?(dir_path)
88
- puts "creating #{filename} in #{dir_path}" if verbose
89
- files << create_tempfile_copy(file, filename, dir_path, :dd1, &block)
96
+ puts "[#{gem_name}][#{basename}] copying file to #{dir_path}" if verbose
97
+ files << create_tempfile_copy(file, dir_path, basename, :dd1, &block)
90
98
  else
91
- puts "directory not relative (#{directory}) for file #{filename}" if verbose
92
- files << create_tempfile_copy(file, filename, directory, :dd2, &block)
99
+ puts "[#{gem_name}][#{basename}] directory not relative (#{tmp_dir})" if verbose
100
+ files << create_tempfile_copy(file, tmp_dir, basename, :dd2, &block)
93
101
  end
94
102
  end
95
- load_gem_copy(files)
103
+ load_gem_copy(tmp_dir, files)
96
104
  end
97
105
 
98
106
  nil
99
107
  end
100
108
 
101
- def primary_namespace
109
+ # @return [String] Namespace of the doffed (original) gem
110
+ def doffed_primary_namespace
111
+ trades.keys.first
112
+ end
113
+
114
+ # @return [String] Namespace of the donned gem
115
+ def donned_primary_namespace
102
116
  trades.values.first
103
117
  end
104
118
 
105
119
  # Will raise NameError if called before #doff_and_don
120
+ # @return [Class, nil]
106
121
  def as_klass
107
- Object.const_get(primary_namespace) if gem_path
122
+ Object.const_get(donned_primary_namespace) if gem_path
108
123
  end
109
124
 
110
125
  private
111
126
 
112
- def load_gem_copy(files)
127
+ # @param tmp_dir [String] absolute file path of the tmp directory
128
+ # @param files [Array[String]] absolute file path of each file in the donned gem
129
+ # @return void
130
+ def load_gem_copy(tmp_dir, files)
131
+ if verbose
132
+ puts "[#{gem_name}] Doffed gem located at #{gem_path}"
133
+ puts "[#{gem_name}] Donned gem located at #{tmp_dir}"
134
+ puts "[#{gem_name}] Primary namespace updated: #{doffed_primary_namespace} => #{donned_primary_namespace}"
135
+ puts "[#{gem_name}] Donned files:\n\t#{files.join("\n\t")}"
136
+ end
113
137
  files.each do |filepath|
114
- # begin
138
+ # But files required here may not load their own internal files properly if they are still using `require`.
139
+ # Since Ruby 2.2, best practice for ruby libraries is to use require_relative for internal files,
140
+ # and require for external files and dependencies.
141
+ # Ref: https://github.com/panorama-ed/memo_wise/issues/349
142
+ # We *can* use `require` *here*, because filepath here is an absolute paths
115
143
  require filepath
116
- # rescue LoadError => e
117
- # puts file.to_s
118
- # puts tempfile.path
119
- # puts e.class
120
- # puts e.message
121
- # end
122
144
  end
145
+
146
+ nil
123
147
  end
124
148
 
149
+ # @param orig_file_path [String] absolute file path of the original file
150
+ # @param tmp_dir [String] absolute file path of the tmp directory
151
+ # @param basename [String] the basename of the file being copied
152
+ # @param debug_info [Symbol] for debugging purposes
125
153
  # @return [String] the file path of the new copy of the original file
126
- def create_tempfile_copy(file, filename, directory, from, &block)
127
- # Value of block is returned from File.open
128
- File.open(File.join(directory, "#{filename}.rb"), "w") do |file_copy|
129
- new_jersey(file, file_copy, from, &block)
154
+ def create_tempfile_copy(orig_file_path, tmp_dir, basename, debug_info, &block)
155
+ File.open(File.join(tmp_dir, basename), "w") do |donned_file|
156
+ # Value of block is returned from File.open, and thus from this method
157
+ new_jersey(orig_file_path, donned_file, basename, debug_info, &block)
130
158
  end
131
159
  end
132
160
 
133
- # @return [String] the file path of the new copy of the original file
134
- def new_jersey(file, file_copy, from)
135
- nj = File.read(file)
161
+ # New Jersey is not Ohio. Writes donned files to disk.
162
+ #
163
+ # @param doffed_file_path [String] absolute file path of the original file
164
+ # @param donned_file [File] the file which needs to be written to disk
165
+ # @param basename [String] the basename of the file for verbose logging
166
+ # @param debug_info [Symbol] for debugging purposes
167
+ # @return [String] the file path of the donned file
168
+ def new_jersey(doffed_file_path, donned_file, basename, debug_info = nil)
169
+ nj = File.read(doffed_file_path)
136
170
  trades.each do |old_namespace, new_namespace|
137
171
  nj.gsub!(old_namespace, new_namespace)
138
172
  end
139
173
  if verbose
140
- puts "new_jersey has from: #{from}"
141
- puts "new_jersey has file: #{file}"
142
- puts "new_jersey file_copy path: #{file_copy.path}"
174
+ puts "[#{gem_name}][#{basename}] new_jersey doffed_file_path: #{doffed_file_path}"
175
+ puts "[#{gem_name}][#{basename}] new_jersey donned_file path: #{donned_file.path}"
176
+ puts "[#{gem_name}][#{basename}] new_jersey debug_info: #{debug_info}"
143
177
  end
144
178
  nj = yield nj if block_given?
145
- file_copy.write(nj)
146
- file_copy.close
147
- file_copy.path
179
+ donned_file.write(nj)
180
+ donned_file.close
181
+ donned_file.path
148
182
  end
149
183
  end
150
184
  end
@@ -1,4 +1,6 @@
1
1
  module GemBench
2
+ # Each gem either needs to be required at boot time or not.
3
+ # Player helps determine which gems can use `require: false` in the Gemfile to cut down load times.
2
4
  class Player
3
5
  # MAJOR.MINOR split on point length == 2
4
6
  # MAJOR.MINOR.PATCH split on point length == 3
@@ -1,12 +1,13 @@
1
1
  # Scout's job is to figure out where gems are hiding
2
2
  #
3
3
  module GemBench
4
+ # Looks through loaded gems' (RubyGems & Bundler) source code searching for stuff
4
5
  class Scout
5
6
  attr_reader :gem_paths, :gemfile_path, :gemfile_lines, :gemfile_trash, :loaded_gems
6
7
 
7
- def initialize(check_gemfile: nil)
8
+ def initialize(check_gemfile: nil, **options)
8
9
  @check_gemfile = check_gemfile.nil? ? true : check_gemfile
9
- @gemfile_path = "#{Dir.pwd}/Gemfile"
10
+ @gemfile_path = options.fetch(:gemfile_path, "#{Dir.pwd}/Gemfile")
10
11
  gem_lookup_paths_from_bundler
11
12
  gem_lines_from_gemfile
12
13
  # Gem.loaded_specs are the gems that have been loaded / required.
@@ -27,10 +28,10 @@ module GemBench
27
28
  .map { |x| x.to_s }
28
29
  .reject { |p| p.empty? }
29
30
  .map { |x| "#{x}/gems" }
30
- @gem_paths << "#{Bundler.install_path}"
31
+ @gem_paths << Bundler.install_path.to_s # Pathname => String
31
32
  @gem_paths << "#{Bundler.bundle_path}/gems"
32
33
  @gem_paths.uniq!
33
- rescue Bundler::GemfileNotFound => e
34
+ rescue Bundler::GemfileNotFound => _e
34
35
  # Don't fail here, but also don't check the Gemfile.
35
36
  @check_gemfile = false
36
37
  ensure
@@ -42,6 +43,7 @@ module GemBench
42
43
  file = File.open(gemfile_path)
43
44
  # Get all lines as an array
44
45
  all_lines = file.readlines
46
+ file.close
45
47
  # Remove all the commented || blank lines
46
48
  @gemfile_trash, @gemfile_lines = all_lines.partition { |x| x =~ GemBench::TRASH_REGEX }
47
49
  @gemfile_trash.reject! { |x| x == "\n" } # remove blank lines
@@ -1,11 +1,13 @@
1
1
  module GemBench
2
+ # Helps determine if a gem dependency is "valid" according to the strict rules of:
3
+ # - every gem must have a version requirement of some sort
2
4
  class StrictVersionGem
3
5
  attr_reader :name, :version, :version_type, :valid, :relevant_lines, :index, :tokenized_line
4
6
 
5
7
  class << self
6
8
  def from_line(all_lines, line, index, opts = {})
7
9
  tokenized_line = GemfileLineTokenizer.new(all_lines, line, index)
8
- return unless tokenized_line.is_gem
10
+ return unless tokenized_line.parse_success
9
11
 
10
12
  new(
11
13
  tokenized_line.name,
@@ -1,9 +1,10 @@
1
1
  module GemBench
2
+ # Helps enforce a version requirement for every dependency in a Gemfile
2
3
  class StrictVersionRequirement
3
4
  attr_reader :gemfile_path, :gems, :starters, :benchers, :verbose
4
5
 
5
6
  def initialize(options = {})
6
- @gemfile_path = "#{Dir.pwd}/Gemfile"
7
+ @gemfile_path = options.fetch(:gemfile_path, "#{Dir.pwd}/Gemfile")
7
8
  file = File.open(gemfile_path)
8
9
  # Get all lines as an array
9
10
  all_lines = file.readlines
@@ -1,6 +1,11 @@
1
1
  require "forwardable"
2
2
 
3
3
  module GemBench
4
+ # It doesn't make sense to use Team unless the Gemfile you want to evaluate is currently loaded.
5
+ # For example:
6
+ # - if you are in a rails console, and want to evaluate the Gemfile of the Rails app, that's great!
7
+ # - if you are in a context with no Gemfile loaded, or a different Gemfile loaded than the one you want to evaluate,
8
+ # this class may not give sensible results. This is because it checks loaded gems via RubyGems and Bundler.
4
9
  class Team
5
10
  EXCLUDE = %w[
6
11
  bundler
@@ -19,6 +24,8 @@ module GemBench
19
24
  cancan
20
25
  friendly_id
21
26
  faker
27
+ capistrano3-puma
28
+ wkhtmltopdf-binary
22
29
  ]
23
30
  # A comment preceding the require: false anywhere on the line should not be considered an active require: false
24
31
  extend Forwardable
@@ -34,13 +41,16 @@ module GemBench
34
41
  :current_gemfile_suggestions,
35
42
  :bad_ideas
36
43
 
37
- def initialize(options = {})
44
+ def initialize(**options)
38
45
  @look_for_regex = options[:look_for_regex]
39
46
  # find: Find gems containing specific strings in code
40
47
  # bench: Find gems that can probably be benched (require: false) in the Gemfile
41
48
  @check_type = @look_for_regex ? :find : :bench
42
49
  @benching = @check_type == :bench
43
- @scout = GemBench::Scout.new(check_gemfile: options[:check_gemfile] || benching?)
50
+ @scout = GemBench::Scout.new(
51
+ check_gemfile: options.fetch(:check_gemfile, benching?),
52
+ gemfile_path: options.fetch(:gemfile_path, "#{Dir.pwd}/Gemfile"),
53
+ )
44
54
  @exclude_file_pattern_regex_proc = options[:exclude_file_pattern_regex_proc].respond_to?(:call) ? options[:exclude_file_pattern_regex_proc] : GemBench::EXCLUDE_FILE_PATTERN_REGEX_PROC
45
55
  # Among the loaded gems there may be some that did not need to be.
46
56
  @excluded, @all = @scout.loaded_gems.partition { |x| EXCLUDE.include?(x[0]) }
@@ -192,13 +202,13 @@ module GemBench
192
202
  end
193
203
 
194
204
  def check(player)
195
- gem_paths.each do |path|
205
+ gem_paths.detect do |path|
196
206
  glob_path = "#{path}/#{player.file_path_glob}"
197
- file_paths = Dir.glob("#{glob_path}")
207
+ file_paths = Dir.glob(glob_path)
198
208
  puts "[GemBench] checking #{player} at #{glob_path} (#{file_paths.length} files)" if extra_verbose?
199
- file_paths.each do |file_path|
209
+ file_paths.detect do |file_path|
200
210
  player.set_starter(file_path, line_match: look_for_regex)
201
- return if player.starter?
211
+ player.starter?
202
212
  end
203
213
  end
204
214
  end
@@ -1,5 +1,6 @@
1
1
  module GemBench
2
+ # Version is enhanced by VersionGem::Basic once the gem is loaded.
2
3
  module Version
3
- VERSION = "2.0.3"
4
+ VERSION = "2.0.4"
4
5
  end
5
6
  end
data/lib/gem_bench.rb CHANGED
@@ -2,13 +2,14 @@
2
2
  require "version_gem"
3
3
  require "bundler" # This gem utilizes bundler as a tool.
4
4
 
5
- require "gem_bench/version"
6
- require "gem_bench/scout"
7
- require "gem_bench/player"
8
- require "gem_bench/team"
9
- require "gem_bench/gemfile_line_tokenizer"
10
- require "gem_bench/strict_version_gem"
11
- require "gem_bench/strict_version_requirement"
5
+ # this library
6
+ require_relative "gem_bench/version"
7
+ require_relative "gem_bench/scout"
8
+ require_relative "gem_bench/player"
9
+ require_relative "gem_bench/team"
10
+ require_relative "gem_bench/gemfile_line_tokenizer"
11
+ require_relative "gem_bench/strict_version_gem"
12
+ require_relative "gem_bench/strict_version_requirement"
12
13
 
13
14
  module GemBench
14
15
  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"
@@ -25,29 +26,41 @@ module GemBench
25
26
  }
26
27
 
27
28
  class << self
28
- attr_accessor :roster
29
-
30
- def check(verbose: false)
31
- @roster = GemBench::Team.new({verbose: verbose})
29
+ def check(verbose: false, gemfile_path: nil)
30
+ options = {
31
+ verbose: verbose,
32
+ }
33
+ options[:gemfile_path] = gemfile_path if gemfile_path
34
+ GemBench::Team.new(**options)
32
35
  end
33
36
 
34
- def versions_present?(verbose: false)
35
- @roster = GemBench::StrictVersionRequirement.new({verbose: verbose})
36
- @roster.versions_present?
37
+ def versions_present?(verbose: false, gemfile_path: nil)
38
+ options = {
39
+ verbose: verbose,
40
+ }
41
+ options[:gemfile_path] = gemfile_path if gemfile_path
42
+ GemBench::StrictVersionRequirement.new(**options).versions_present?
37
43
  end
38
44
 
39
- def list_missing_version_constraints(verbose: false)
40
- @roster = GemBench::StrictVersionRequirement.new({verbose: verbose})
41
- @roster.list_missing_version_constraints
45
+ def list_missing_version_constraints(verbose: false, gemfile_path: nil)
46
+ options = {
47
+ verbose: verbose,
48
+ }
49
+ options[:gemfile_path] = gemfile_path if gemfile_path
50
+ GemBench::StrictVersionRequirement.new(**options).list_missing_version_constraints
42
51
  end
43
52
 
44
53
  def find(look_for_regex: GemBench::RAILTIE_REGEX,
45
- exclude_file_pattern_regex_proc: GemBench::EXCLUDE_FILE_PATTERN_REGEX_PROC, verbose: false)
46
- @roster = GemBench::Team.new({
54
+ exclude_file_pattern_regex_proc: GemBench::EXCLUDE_FILE_PATTERN_REGEX_PROC,
55
+ verbose: false,
56
+ gemfile_path: nil)
57
+ options = {
58
+ verbose: verbose,
47
59
  look_for_regex: look_for_regex,
48
60
  exclude_file_pattern_regex_proc: exclude_file_pattern_regex_proc,
49
- verbose: verbose,
50
- })
61
+ }
62
+ options[:gemfile_path] = gemfile_path if gemfile_path
63
+ GemBench::Team.new(**options)
51
64
  end
52
65
  end
53
66
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gem_bench
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Boling
@@ -12,31 +12,31 @@ cert_chain:
12
12
  -----BEGIN CERTIFICATE-----
13
13
  MIIEgDCCAuigAwIBAgIBATANBgkqhkiG9w0BAQsFADBDMRUwEwYDVQQDDAxwZXRl
14
14
  ci5ib2xpbmcxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
15
- A2NvbTAeFw0yMzA5MjAxNzMwMjhaFw0yNDA5MTkxNzMwMjhaMEMxFTATBgNVBAMM
15
+ A2NvbTAeFw0yNDA5MjAwODU4NDJaFw0yNTA5MjAwODU4NDJaMEMxFTATBgNVBAMM
16
16
  DHBldGVyLmJvbGluZzEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
17
- LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA+a9UvHo3
18
- 84k96WgU5Kk5HB+cLZs/modjorsTfqY67MJF5nNvAoqcKTUBW4uG+Zpfnm3jaDO5
19
- GxhJEIZWfndYzycHT2KMVQ1uTP82ba8ZaKrPlPIafkbui3mdds47qsmqHiblKERg
20
- U532lkwfqHDlJwE7OBZQ59EwWWLynlT/yAUHpOBbqIuHKUxdpmBI+sIjrZcD1e05
21
- WmjkO6fwIdC5oM757aoPxIgXD587VOViH11Vkm2doskj4T8yONtwVHlcrrhJ9Bzd
22
- /zdp6vEn7GZQrABvpOlqwWxQ72ZnFhJe/RJZf6CXOPOh69Ai0QKYl2a1sYuCJKS3
23
- nsBnxXJINEEznjR7rZjNUmYD+CZqfjzgPqedRxTlASe7iA4w7xZOqMDzcuhNwcUQ
24
- tMEH6BTktxKP3jXZPXRfHCf6s+HRVb6vezAonTBVyydf5Xp5VwWkd6cwm+2BzHl5
25
- 7kc/3lLxKMcsyEUprAsk8LdHohwZdC267l+RS++AP6Cz6x+nB3oGob19AgMBAAGj
26
- fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQCSSas60GqqMjt
27
- xR7LoY1gucEvtzAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
17
+ LGQBGRYDY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAjrxsKObI
18
+ rFQjBpzvVfqnT6JlF8/pkpgEEjFh7ex3zIerfuHzZvSrx+sRDGxQ8koWWG0Wjx8s
19
+ wkBZ5dIqvl0g3sWP5asa28u/09opxkQTC1Ao77iYxcBcwoCe/Dpf1m4Q/m6oH0kL
20
+ 2AZVNJQL3UkqAcLS0tsj/s/jAKnVlsaZZE5gQiIIi8HtkvSsajtx+Cq2AxDvcWvV
21
+ /CliD+pmzYkTjvjwGm8yeyFGGGgrisJMryiZdZlkTwrQSjCzudIKbLeuG8Se4JTD
22
+ TAcT+rPubr27v1jwmtIjtiot3rf4nof7LHLb122a/0VR7cC7xPLnXw0Cq1BShvoq
23
+ /GKRdSwMNinTOGkFTK1gKnjN+3iD4zyXU3XO3CXoTr+Ju8fXPN1x4tpOMgbv8dme
24
+ WbcQMOH9ZjmA5w0bSVRL1c3NhRRpUzrKTNXBEvqOyWjUnintxWKj+cRXx+z+dUgI
25
+ dL3kj68fcsiTgl75In3C485pnCMmq1eLuVoiy3jkLNOn2lHeLt9ZK63LAgMBAAGj
26
+ fzB9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBRhfc+2UaVYd74p
27
+ yJ1JclGiUYN8+jAhBgNVHREEGjAYgRZwZXRlci5ib2xpbmdAZ21haWwuY29tMCEG
28
28
  A1UdEgQaMBiBFnBldGVyLmJvbGluZ0BnbWFpbC5jb20wDQYJKoZIhvcNAQELBQAD
29
- ggGBAMl9ifcw5p+PdvB7dCPoNKoVdp/2LbC9ztETHuYL2gUMJB6UoS3o9c/piSuR
30
- V3ZMQaijmNu6ms1bWAtJ66LjmYrVflJtf9yp31Kierr9LpisMSUx2qbMOHGa8d2Z
31
- vCUWPF8E9Cg0mP3GAyZ6qql8jDh/anUKeksPXqJvNxNPDu2DVYsa/IWdl96whzS4
32
- Bl7SwB1E7agps40UcshCSKaVDOU0M+XN6SrnJMElnBic+KSAkBkVFbzS0BE4ODZM
33
- BgE6nYzQ05qhuvbE+oGdACTlemNtDDWCh0uw+7x0q2PocGIDU5zsPn/WNTkCXPmB
34
- CHGvqDNWq4M7ncTKAaS2XExgyb7uPdq9fKiOW8nmH+zCiGzJXzBWwZlKf7L4Ht9E
35
- a3f0e5C+zvee9Z5Ng9ciyfav9/fcXgYt5MjoBv27THr5XfBhgOCIHSYW2tqJmWKi
36
- KuxrfYrN+9HvMdm+nZ6TypmKftHY3Gj+/uu+g8Icm/zrvTWAEE0mcJOkfrIoNPJb
37
- pF8dMA==
29
+ ggGBAA4fLU2+mQ++jBhVM2IeyvQdw1nm+0thkH4Ldv8ZOBm5ZxCPGIMoYliDDzg4
30
+ 4JDFxZR1wR4sdrz/K5tWtEkN23SKzopwbNb1NIQRSLQ7nOoc+4bkuz9xwKinmIvF
31
+ D+5qsl2S27WLKFreMDtGoh0CREIMBUxU4rGTh0gtzmweGR+fnOShg4Jo0kxrjU5h
32
+ uYk/uVE+bn/jOEGs43GvKXZLyshpBrZjQ+ArbvxDht5t35zbSxerbUxUPZUbXUCW
33
+ tTyh38a9UYjAAHvnh6Y4Fi9wd4/pGNsektrzB3z/zlVj4YF2TMLX9XfNJWEGRGpO
34
+ sSkLYdtEX1WQAmuZtActVW2cL3HdQaRbiv7VbfpA0eSk0ZdZHvBCl516ZZu10uX6
35
+ 82W1mg6fuezdpeBOiXwrEbZSt/oGiF4V511F6nd55p0okwHc/6nS10F/3aKJ4gwC
36
+ I5o+DRfXQHqKucx1ldFHvI2rE/kSCWqGTHN2eyu1sqCPeOoIMxrltJhaejKPkxqj
37
+ zaF9Og==
38
38
  -----END CERTIFICATE-----
39
- date: 2024-09-18 00:00:00.000000000 Z
39
+ date: 2024-09-20 00:00:00.000000000 Z
40
40
  dependencies:
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
@@ -130,16 +130,16 @@ dependencies:
130
130
  name: awesome_print
131
131
  requirement: !ruby/object:Gem::Requirement
132
132
  requirements:
133
- - - ">="
133
+ - - "~>"
134
134
  - !ruby/object:Gem::Version
135
- version: '0'
135
+ version: '1.9'
136
136
  type: :development
137
137
  prerelease: false
138
138
  version_requirements: !ruby/object:Gem::Requirement
139
139
  requirements:
140
- - - ">="
140
+ - - "~>"
141
141
  - !ruby/object:Gem::Version
142
- version: '0'
142
+ version: '1.9'
143
143
  - !ruby/object:Gem::Dependency
144
144
  name: method_source
145
145
  requirement: !ruby/object:Gem::Requirement
@@ -160,42 +160,48 @@ dependencies:
160
160
  requirements:
161
161
  - - ">="
162
162
  - !ruby/object:Gem::Version
163
- version: '0'
163
+ version: '10'
164
164
  type: :development
165
165
  prerelease: false
166
166
  version_requirements: !ruby/object:Gem::Requirement
167
167
  requirements:
168
168
  - - ">="
169
169
  - !ruby/object:Gem::Version
170
- version: '0'
170
+ version: '10'
171
171
  - !ruby/object:Gem::Dependency
172
172
  name: rspec
173
173
  requirement: !ruby/object:Gem::Requirement
174
174
  requirements:
175
- - - ">="
175
+ - - "~>"
176
176
  - !ruby/object:Gem::Version
177
- version: '0'
177
+ version: '3.13'
178
178
  type: :development
179
179
  prerelease: false
180
180
  version_requirements: !ruby/object:Gem::Requirement
181
181
  requirements:
182
- - - ">="
182
+ - - "~>"
183
183
  - !ruby/object:Gem::Version
184
- version: '0'
184
+ version: '3.13'
185
185
  - !ruby/object:Gem::Dependency
186
186
  name: rspec-block_is_expected
187
187
  requirement: !ruby/object:Gem::Requirement
188
188
  requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '1.0'
189
192
  - - ">="
190
193
  - !ruby/object:Gem::Version
191
- version: '0'
194
+ version: 1.0.6
192
195
  type: :development
193
196
  prerelease: false
194
197
  version_requirements: !ruby/object:Gem::Requirement
195
198
  requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '1.0'
196
202
  - - ">="
197
203
  - !ruby/object:Gem::Version
198
- version: '0'
204
+ version: 1.0.6
199
205
  - !ruby/object:Gem::Dependency
200
206
  name: rubocop-lts
201
207
  requirement: !ruby/object:Gem::Requirement
@@ -234,28 +240,28 @@ dependencies:
234
240
  name: rubocop-rspec
235
241
  requirement: !ruby/object:Gem::Requirement
236
242
  requirements:
237
- - - ">="
243
+ - - "~>"
238
244
  - !ruby/object:Gem::Version
239
- version: '0'
245
+ version: '3.0'
240
246
  type: :development
241
247
  prerelease: false
242
248
  version_requirements: !ruby/object:Gem::Requirement
243
249
  requirements:
244
- - - ">="
250
+ - - "~>"
245
251
  - !ruby/object:Gem::Version
246
- version: '0'
252
+ version: '3.0'
247
253
  - !ruby/object:Gem::Dependency
248
254
  name: standard
249
255
  requirement: !ruby/object:Gem::Requirement
250
256
  requirements:
251
- - - ">="
257
+ - - "~>"
252
258
  - !ruby/object:Gem::Version
253
259
  version: '1.40'
254
260
  type: :development
255
261
  prerelease: false
256
262
  version_requirements: !ruby/object:Gem::Requirement
257
263
  requirements:
258
- - - ">="
264
+ - - "~>"
259
265
  - !ruby/object:Gem::Version
260
266
  version: '1.40'
261
267
  description: |-
@@ -290,10 +296,10 @@ licenses:
290
296
  - MIT
291
297
  metadata:
292
298
  homepage_uri: https://railsbling.com/tags/gem_bench/
293
- source_code_uri: http://github.com/pboling/gem_bench/tree/v2.0.3
294
- changelog_uri: http://github.com/pboling/gem_bench/blob/v2.0.3/CHANGELOG.md
299
+ source_code_uri: http://github.com/pboling/gem_bench/tree/v2.0.4
300
+ changelog_uri: http://github.com/pboling/gem_bench/blob/v2.0.4/CHANGELOG.md
295
301
  bug_tracker_uri: http://github.com/pboling/gem_bench/issues
296
- documentation_uri: https://www.rubydoc.info/gems/gem_bench/2.0.3
302
+ documentation_uri: https://www.rubydoc.info/gems/gem_bench/2.0.4
297
303
  wiki_uri: http://github.com/pboling/gem_bench/wiki
298
304
  funding_uri: https://liberapay.com/pboling
299
305
  rubygems_mfa_required: 'true'
metadata.gz.sig CHANGED
Binary file