utils 0.68.0 → 0.69.0

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +251 -18
  3. data/bin/ascii7 +28 -0
  4. data/bin/blameline +17 -0
  5. data/bin/changes +69 -5
  6. data/bin/classify +128 -7
  7. data/bin/code_comment +102 -104
  8. data/bin/commit_message +26 -2
  9. data/bin/create_cstags +18 -0
  10. data/bin/create_tags +10 -0
  11. data/bin/discover +38 -1
  12. data/bin/edit +14 -1
  13. data/bin/edit_wait +14 -0
  14. data/bin/enum +139 -15
  15. data/bin/git-empty +50 -0
  16. data/bin/git-versions +20 -0
  17. data/bin/json_check +15 -1
  18. data/bin/long_lines +11 -2
  19. data/bin/myex +38 -0
  20. data/bin/on_change +22 -0
  21. data/bin/path +21 -0
  22. data/bin/print_method +29 -1
  23. data/bin/probe +52 -4
  24. data/bin/rainbow +52 -0
  25. data/bin/rd2md +15 -0
  26. data/bin/search +83 -1
  27. data/bin/sedit +6 -0
  28. data/bin/serve +18 -3
  29. data/bin/ssh-tunnel +14 -2
  30. data/bin/strip_spaces +17 -9
  31. data/bin/sync_dir +48 -1
  32. data/bin/untest +19 -1
  33. data/bin/utils-utilsrc +42 -6
  34. data/bin/vcf2alias +33 -0
  35. data/bin/yaml_check +24 -2
  36. data/lib/utils/config_dir.rb +127 -0
  37. data/lib/utils/config_file.rb +445 -1
  38. data/lib/utils/editor.rb +215 -3
  39. data/lib/utils/finder.rb +127 -16
  40. data/lib/utils/grepper.rb +90 -1
  41. data/lib/utils/irb.rb +387 -39
  42. data/lib/utils/line_blamer.rb +28 -0
  43. data/lib/utils/line_formatter.rb +198 -0
  44. data/lib/utils/md5.rb +14 -0
  45. data/lib/utils/patterns.rb +77 -3
  46. data/lib/utils/probe_server.rb +302 -23
  47. data/lib/utils/ssh_tunnel_specification.rb +58 -0
  48. data/lib/utils/version.rb +1 -1
  49. data/lib/utils/xt/source_location_extension.rb +18 -6
  50. data/lib/utils.rb +3 -1
  51. data/tests/utils_test.rb +7 -1
  52. data/utils.gemspec +5 -5
  53. metadata +4 -6
  54. data/bin/number_files +0 -26
  55. data/lib/utils/xdg_config.rb +0 -10
  56. /data/{COPYING → LICENSE} +0 -0
@@ -1,13 +1,41 @@
1
1
  module Utils
2
2
  class LineBlamer
3
+ # Initializes a new LineBlamer instance to analyze source code line
4
+ # information.
5
+ #
6
+ # @param file [ String ] the path to the file containing the line of code
7
+ # @param lineno [ Integer ] the line number within the file (defaults to 1)
3
8
  def initialize(file, lineno = 1)
4
9
  @file, @lineno = file, lineno
5
10
  end
6
11
 
12
+ # Finds the source location of a line and creates a new LineBlamer instance.
13
+ #
14
+ # This method extracts the file path and line number from the given line
15
+ # object using its source_location method. If a valid location is found, it
16
+ # initializes and returns a new LineBlamer instance with the extracted file
17
+ # path and line number.
18
+ #
19
+ # @param line [ Object ] the line object to analyze for source location
20
+ # information
21
+ #
22
+ # @return [ Utils::LineBlamer, nil ] a new LineBlamer instance if the line
23
+ # has a valid source location, otherwise nil
7
24
  def self.for_line(line)
8
25
  location = line.source_location and new(*location)
9
26
  end
10
27
 
28
+ # Performs git blame on a specific line of code and returns the result.
29
+ #
30
+ # This method executes a git blame command to analyze the specified file
31
+ # and line number, retrieving information about when and by whom the line
32
+ # was last modified. It handles potential errors from git by suppressing
33
+ # stderr output and returns nil if git is not available or the operation
34
+ # fails.
35
+ #
36
+ # @param options [ String ] additional options to pass to the git blame command
37
+ #
38
+ # @return [ String, nil ] the output of the git blame command if successful, otherwise nil
11
39
  def perform(options = '')
12
40
  `git 2>/dev/null blame #{options} -L #@lineno,+1 "#@file"`.full?
13
41
  end
@@ -12,6 +12,11 @@ else
12
12
  ::RSpec::Core::Formatters.register self, :start, :close,
13
13
  :example_passed, :example_pending, :example_failed, :dump_summary
14
14
 
15
+ # The initialize method sets up the error logging system by creating a
16
+ # file handle for writing error messages and ensuring the output is
17
+ # synchronized.
18
+ #
19
+ # @param output [ IO ] the output stream to be used for logging errors
15
20
  def initialize(output)
16
21
  @output = output
17
22
  @output.sync = true
@@ -20,31 +25,82 @@ else
20
25
  @errors_lst.sync = true
21
26
  end
22
27
 
28
+ # The output reader method provides access to the output storage.
29
+ #
30
+ # @return [ Array ] the array containing the collected output items
23
31
  attr_reader :output
24
32
 
33
+ # The start method initializes the error logging output.
34
+ #
35
+ # This method writes a header message to the output indicating where the
36
+ # error list will be stored, followed by a separator line made of dashes
37
+ # that spans the width of the terminal.
38
+ #
39
+ # @param _ignore [ Object ] this parameter is ignored and exists for
40
+ # interface compatibility
25
41
  def start(_ignore)
26
42
  output.puts "Storing error list in #{@errors_lst.path.inspect}: "
27
43
  output.puts ?- * Tins::Terminal.columns
28
44
  end
29
45
 
46
+ # The close method closes the error log file handle.
47
+ #
48
+ # This method is responsible for properly closing the file handle
49
+ # associated with the error log file, ensuring that all buffered data is
50
+ # written and system resources are released.
51
+ #
52
+ # @param _ignore [ Object ] ignored parameter, present for interface
53
+ # compatibility
30
54
  def close(_ignore)
31
55
  @errors_lst.close
32
56
  end
33
57
 
58
+ # The dump_summary method outputs a formatted summary line to both the
59
+ # errors log and the standard output.
60
+ #
61
+ # This method generates a summary line using the summary_line method,
62
+ # then writes it to both the error log file and the standard output, with
63
+ # decorative lines of equals signs for visual separation in each output
64
+ # stream.
65
+ #
66
+ # @param summary [ Object ] the summary object to be processed and displayed
34
67
  def dump_summary(summary)
35
68
  line = summary_line(summary)
36
69
  @errors_lst.puts ?= * 80, line
37
70
  output.puts ?= * Tins::Terminal.columns, line
38
71
  end
39
72
 
73
+ # The example_passed method outputs a formatted line for a passed test
74
+ # example.
75
+ #
76
+ # This method takes a test example and formats it using the internal format_line method,
77
+ # then writes the formatted output to the designated output stream.
78
+ #
79
+ # @param example [ Object ] the test example that has passed
40
80
  def example_passed(example)
41
81
  output.puts format_line(example)
42
82
  end
43
83
 
84
+ # The example_pending method outputs a formatted line for a pending test
85
+ # example.
86
+ #
87
+ # This method takes a test example that is pending and formats it using
88
+ # the internal format_line method before outputting it to the console.
89
+ #
90
+ # @param example [ Object ] the test example that is pending
44
91
  def example_pending(example)
45
92
  output.puts format_line(example)
46
93
  end
47
94
 
95
+ # The example_failed method handles the processing of failed test
96
+ # examples.
97
+ #
98
+ # This method manages the logging and output of failed test results by
99
+ # first writing the failure details to an error file, then formatting and
100
+ # displaying the failure information to the output stream, and finally
101
+ # dumping the full failure details.
102
+ #
103
+ # @param example [ Object ] the test example that failed
48
104
  def example_failed(example)
49
105
  dump_failure_to_error_file(example)
50
106
  output.puts format_line(example)
@@ -53,6 +109,18 @@ else
53
109
 
54
110
  private
55
111
 
112
+ # The summary_line method formats a summary string containing test
113
+ # execution statistics.
114
+ #
115
+ # This method takes a summary object and generates a formatted string
116
+ # that includes the number of failed tests, total tests, failure
117
+ # percentage, pending tests, pending percentage, and the total execution
118
+ # duration.
119
+ #
120
+ # @param summary [ Object ] an object containing test execution statistics
121
+ #
122
+ # @return [ String ] a formatted summary string with test statistics and
123
+ # timing information
56
124
  def summary_line(summary)
57
125
  failure_percentage = 100 * summary.failure_count.to_f / summary.example_count
58
126
  failure_percentage.nan? and failure_percentage = 0.0
@@ -68,6 +136,13 @@ else
68
136
  ]
69
137
  end
70
138
 
139
+ # The dump_failure method outputs detailed information about a test
140
+ # failure.
141
+ #
142
+ # This method displays the description of the failing example along with
143
+ # the specific failure details for debugging purposes.
144
+ #
145
+ # @param example [ Object ] the test example that failed
71
146
  def dump_failure(example)
72
147
  output.puts(
73
148
  description(example, full: true),
@@ -75,10 +150,32 @@ else
75
150
  )
76
151
  end
77
152
 
153
+ # The read_failed_line method returns an empty string after stripping
154
+ # whitespace.
155
+ #
156
+ # This method is intended to provide a placeholder implementation for
157
+ # retrieving the failed line information from a test example, currently
158
+ # returning an empty stripped string regardless of the input.
159
+ #
160
+ # @param example [ Object ] the test example object
161
+ #
162
+ # @return [ String ] an empty string with whitespace stripped
78
163
  def read_failed_line(example)
79
164
  ''.strip
80
165
  end
81
166
 
167
+ # The dump_failure_for_example method constructs a formatted failure
168
+ # message for a test example.
169
+ #
170
+ # This method generates a detailed error report that includes the failing
171
+ # line of code, the exception class name, and the exception message. It
172
+ # processes the execution result
173
+ # of a test example to extract relevant information about why the test failed.
174
+ #
175
+ # @param example [ Object ] the test example object containing execution results
176
+ #
177
+ # @return [ String ] a formatted string containing the failure details including
178
+ # the failing line, exception class, and exception message
82
179
  def dump_failure_for_example(example)
83
180
  result = ''
84
181
  exception = execution_result(example).exception
@@ -91,6 +188,19 @@ else
91
188
  result
92
189
  end
93
190
 
191
+ # The format_backtrace method processes and formats the backtrace
192
+ # information for an example.
193
+ #
194
+ # This method extracts the backtrace from the exception associated with
195
+ # an example, applies optional filtering based on a limit, and formats
196
+ # each line using relative paths. It can optionally wrap the formatted
197
+ # backtrace with folding markers when requested.
198
+ #
199
+ # @param example [ Object ] the example object containing the exception with backtrace
200
+ # @param folding [ TrueClass, FalseClass ] whether to wrap the backtrace with folding markers
201
+ # @param limit [ Integer, nil ] the maximum number of backtrace lines to include
202
+ #
203
+ # @return [ String ] the formatted backtrace as a string with lines separated by newlines
94
204
  def format_backtrace(example, folding: false, limit: nil)
95
205
  backtrace = execution_result(example).exception.backtrace
96
206
  backtrace.nil? and return ''
@@ -106,6 +216,15 @@ else
106
216
  result * ?\n
107
217
  end
108
218
 
219
+ # The dump_failure_to_error_file method writes failure information to an
220
+ # error log file.
221
+ #
222
+ # This method records detailed information about a failed test example,
223
+ # including the location, runtime, description, failure details, and
224
+ # backtrace. It uses file locking to ensure thread-safe writing to the
225
+ # error log file.
226
+ #
227
+ # @param example [ Object ] the test example that failed
109
228
  def dump_failure_to_error_file(example)
110
229
  @errors_lst.flock File::LOCK_EX
111
230
  @errors_lst.puts "%s\n%3.3fs %s\n%s\n%s" % [
@@ -116,10 +235,29 @@ else
116
235
  @errors_lst.flock File::LOCK_UN
117
236
  end
118
237
 
238
+ # The execution_result method retrieves the execution result metadata
239
+ # from an example.
240
+ #
241
+ # This method accesses the metadata hash associated with the example's
242
+ # underlying test case to extract the execution result information that
243
+ # was stored during test execution.
244
+ #
245
+ # @param example [ Object ] the example object containing test metadata
246
+ #
247
+ # @return [ Object ] the execution result metadata stored in the example's metadata hash
119
248
  def execution_result(example)
120
249
  example.example.metadata[:execution_result]
121
250
  end
122
251
 
252
+ # The description method retrieves either the full or abbreviated
253
+ # description of an example.
254
+ #
255
+ # @param example [ Object ] the example object containing description
256
+ # information
257
+ # @param full [ TrueClass, FalseClass ] determines whether to return the
258
+ # full description or abbreviated version
259
+ #
260
+ # @return [ String ] the appropriate description based on the full parameter value
123
261
  def description(example, full: ENV['VERBOSE'].to_i == 1)
124
262
  if full
125
263
  example.example.full_description
@@ -128,10 +266,29 @@ else
128
266
  end
129
267
  end
130
268
 
269
+ # The run_time method retrieves the execution time of a test example.
270
+ #
271
+ # This method accesses the execution result of a given test example and
272
+ # returns the run time associated with that execution.
273
+ #
274
+ # @param example [ Object ] the test example object to get run time for
275
+ #
276
+ # @return [ Float, nil ] the execution time of the example or nil if not available
131
277
  def run_time(example)
132
278
  execution_result(example).run_time
133
279
  end
134
280
 
281
+ # The format_line method formats and colors test execution results for
282
+ # display.
283
+ #
284
+ # This method takes a test example and creates a formatted string that
285
+ # includes the location, run time, and description of the test. It
286
+ # applies color coding based on the test result status and ensures the
287
+ # output fits within the terminal width.
288
+ #
289
+ # @param example [ Object ] the test example to format
290
+ #
291
+ # @return [ String ] the formatted and colorized test result string
135
292
  def format_line(example)
136
293
  args = [ location(example), run_time(example), description(example) ]
137
294
  uncolored = "%s # S %3.3fs %s" % args
@@ -148,18 +305,59 @@ else
148
305
  end
149
306
  end
150
307
 
308
+ # The success_color method applies green color formatting to the provided
309
+ # text.
310
+ #
311
+ # This method wraps the input text with ANSI color codes to display it in
312
+ # green, which is typically used to indicate successful or positive
313
+ # outcomes in terminal output.
314
+ #
315
+ # @param text [ String ] the text to be formatted with green color
316
+ #
317
+ # @return [ String ] the input text wrapped with green color ANSI escape codes
151
318
  def success_color(text)
152
319
  Term::ANSIColor.green(text)
153
320
  end
154
321
 
322
+ # The failure_color method applies red color formatting to the provided
323
+ # text.
324
+ #
325
+ # This method wraps the input text with red color codes using the
326
+ # Term::ANSIColor library, making the text appear in red when displayed
327
+ # in compatible terminals.
328
+ #
329
+ # @param text [ String ] the text to be colorized in red
330
+ #
331
+ # @return [ String ] the colorized text wrapped with red formatting codes
155
332
  def failure_color(text)
156
333
  Term::ANSIColor.red(text)
157
334
  end
158
335
 
336
+ # The pending_color method applies yellow color formatting to the
337
+ # provided text.
338
+ #
339
+ # This method wraps the input text with yellow color codes using the
340
+ # Term::ANSIColor library, making the text appear in yellow when
341
+ # displayed in compatible terminals.
342
+ #
343
+ # @param text [ String ] the text to be colorized
344
+ #
345
+ # @return [ String ] the colorized text with yellow formatting applied
159
346
  def pending_color(text)
160
347
  Term::ANSIColor.yellow(text)
161
348
  end
162
349
 
350
+ # The location method extracts and formats the file path and line number
351
+ # for an RSpec example.
352
+ #
353
+ # This method retrieves the location information from the example's
354
+ # metadata, handling cases where the location might be nested within the
355
+ # example group. It then processes the location to return a relative path
356
+ # using RSpec's metadata helper.
357
+ #
358
+ # @param example [ Object ] the RSpec example object containing metadata
359
+ #
360
+ # @return [ String ] the relative file path and line number for the example
163
361
  def location(example)
164
362
  location = example.example.metadata[:location]
165
363
  unless location.include?(?/)
data/lib/utils/md5.rb CHANGED
@@ -3,12 +3,26 @@ require 'digest/md5'
3
3
  module Utils
4
4
  module MD5
5
5
  class << self
6
+ # The buffer_size accessor method provides read and write access to the
7
+ # buffer_size instance variable.
8
+ #
9
+ # @return [ Integer ] the current buffer size value
6
10
  attr_accessor :buffer_size
7
11
  end
8
12
  self.buffer_size = 2 ** 20 - 1
9
13
 
10
14
  module_function
11
15
 
16
+ # Computes the MD5 hash digest for a given file.
17
+ #
18
+ # This method reads the entire contents of the specified file in binary
19
+ # mode and calculates its MD5 hash value. It uses a configurable buffer
20
+ # size for reading the file in chunks to optimize memory usage during the
21
+ # hashing process.
22
+ #
23
+ # @param filename [ String ] the path to the file for which to compute the MD5 hash
24
+ #
25
+ # @return [ String ] the hexadecimal representation of the MD5 hash digest
12
26
  def md5(filename)
13
27
  digest = Digest::MD5.new
14
28
  digest.reset
@@ -1,6 +1,19 @@
1
1
  module Utils
2
2
  module Patterns
3
3
  class Pattern
4
+ # Initializes a new Pattern instance with the specified options.
5
+ #
6
+ # This method sets up the pattern configuration by storing the character
7
+ # set, case sensitivity flag, and pattern string. It validates that a
8
+ # pattern is provided and optionally filters the pattern characters based
9
+ # on the specified character set.
10
+ #
11
+ # @param opts [ Hash ] a hash containing the pattern configuration options
12
+ # @option opts [ String ] :cset the character set to filter pattern characters against
13
+ # @option opts [ TrueClass, FalseClass ] :icase whether the pattern matching should be case sensitive
14
+ # @option opts [ String ] :pattern the pattern string to be used for matching
15
+ #
16
+ # @raise [ ArgumentError ] if the pattern option is not provided
4
17
  def initialize(opts = {})
5
18
  @cset = opts[:cset]
6
19
  @icase = opts[:icase]
@@ -9,8 +22,23 @@ module Utils
9
22
  @pattern = @pattern.gsub(/[^#{@cset}]/, '') if @cset
10
23
  end
11
24
 
25
+ # Returns the matcher object used for pattern matching.
26
+ #
27
+ # @return [ Object ] the matcher object that handles pattern matching operations
12
28
  attr_reader :matcher
13
29
 
30
+ # The method_missing method delegates calls to the matcher object while
31
+ # handling UTF-8 encoding errors.
32
+ #
33
+ # This method acts as a fallback handler for undefined method calls,
34
+ # forwarding them to the internal matcher object. It specifically catches
35
+ # ArgumentError exceptions related to invalid byte sequences in UTF-8 and
36
+ # re-raises them unless they match the expected error pattern.
37
+ #
38
+ # @param a [ Array ] the arguments passed to the missing method
39
+ # @param b [ Proc ] the block passed to the missing method
40
+ #
41
+ # @return [ Object ] the result of the delegated method call on the matcher
14
42
  def method_missing(*a, &b)
15
43
  @matcher.__send__(*a, &b)
16
44
  rescue ArgumentError => e
@@ -19,24 +47,70 @@ module Utils
19
47
  end
20
48
 
21
49
  class FuzzyPattern < Pattern
50
+ # Initializes a fuzzy pattern matcher by processing the pattern string
51
+ # and compiling it into a regular expression.
52
+ #
53
+ # This method takes the configured pattern string and converts it into a
54
+ # regular expression that can match strings in a fuzzy manner, allowing
55
+ # for partial matches while preserving the order of characters. It
56
+ # handles case sensitivity based on the configuration.
57
+ #
58
+ # @param opts [ Hash ] a hash containing the pattern configuration options
59
+ # @option opts [ String ] :cset the character set to filter pattern characters against
60
+ # @option opts [ TrueClass, FalseClass ] :icase whether the pattern matching should be case sensitive
61
+ # @option opts [ String ] :pattern the pattern string to be used for matching
22
62
  def initialize(opts = {})
23
63
  super
24
64
  r = @pattern.split(//).grep(/[[:print:]]/).map { |x|
25
65
  "(#{Regexp.quote(x)})"
26
66
  } * '.*?'
27
67
  @matcher = Regexp.new(
28
- "\\A(?:.*/.*?#{r}|.*#{r})",
29
- @icase ? Regexp::IGNORECASE : 0)
68
+ "\\A(?:.*/.*?#{r}|.*#{r})", @icase ? Regexp::IGNORECASE : 0
69
+ )
30
70
  end
31
71
  end
32
72
 
33
73
  class RegexpPattern < Pattern
74
+ # Initializes a regular expression pattern matcher with the specified
75
+ # options.
76
+ #
77
+ # This method sets up a regular expression object based on the pattern
78
+ # string and case sensitivity configuration that was previously
79
+ # initialized in the parent class. It compiles the pattern into a Regexp
80
+ # object that can be used for matching operations throughout the pattern
81
+ # matching process.
82
+ #
83
+ # @param opts [ Hash ] a hash containing the pattern configuration options
84
+ # @option opts [ String ] :cset the character set to filter pattern characters against
85
+ # @option opts [ TrueClass, FalseClass ] :icase whether the pattern matching should be case sensitive
86
+ # @option opts [ String ] :pattern the pattern string to be used for matching
87
+ #
88
+ # @return [ Regexp ] a compiled regular expression object ready for pattern matching operations
34
89
  def initialize(opts = {})
35
90
  super
36
- @matcher = Regexp.new(@pattern, @icase ? Regexp::IGNORECASE : 0)
91
+ @matcher = Regexp.new(
92
+ @pattern, @icase ? Regexp::IGNORECASE : 0
93
+ )
37
94
  end
38
95
  end
39
96
 
97
+ # Chooses and initializes a pattern matcher based on the provided argument
98
+ # and options.
99
+ #
100
+ # This method selects between a regular expression pattern matcher and a
101
+ # fuzzy pattern matcher depending on the value of the argument parameter
102
+ # and the default configuration.
103
+ # It validates that the argument is either 'r' (regexp) or 'f' (fuzzy) and
104
+ # raises an error if an invalid value is provided.
105
+ #
106
+ # @param argument [ String ] the argument string that determines the pattern type
107
+ # @param pattern_opts [ Hash ] the options to be passed to the pattern matcher constructor
108
+ # @param default [ String ] the default pattern type to use when argument is nil or empty
109
+ #
110
+ # @return [ Utils::Patterns::Pattern ] a new instance of either RegexpPattern or FuzzyPattern
111
+ #
112
+ # @raise [ ArgumentError ] if the argument does not match 'r' or 'f' patterns and is not nil
113
+ # @raise [ ArgumentError ] if the pattern option is not provided to the pattern matcher constructor
40
114
  def choose(argument, pattern_opts, default: ?f)
41
115
  case argument
42
116
  when /^r/, (default == ?r ? nil : :not)