gem_bench 2.0.3 → 2.0.4

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
  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