utils 0.89.1 → 0.91.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.
@@ -0,0 +1,632 @@
1
+ module Utils::IRB::Shell
2
+ end
3
+ require 'utils/irb/shell/wrappers'
4
+ # A module that extends Regexp functionality with additional pattern
5
+ # matching and display capabilities.
6
+ #
7
+ # Provides enhanced regexp operations including match highlighting and
8
+ # shell command integration.
9
+ module Utils::IRB::Shell
10
+ include SearchUI
11
+ include FileUtils
12
+ include Tins::Find
13
+
14
+ # The receiver_unless_main method retrieves the receiver name of a method
15
+ # unless it is the main object, optionally executing a block with the
16
+ # receiver name.
17
+ #
18
+ # @param method [ Method ] the method object to inspect
19
+ # @param block [ Proc ] an optional block to execute with the receiver name
20
+ #
21
+ # @return [ String, nil ] the receiver name if it is not 'main', otherwise nil
22
+ def receiver_unless_main(method, &block)
23
+ receiver_name = method.receiver.to_s
24
+ if receiver_name != 'main'
25
+ if block
26
+ block.(receiver_name)
27
+ else
28
+ receiver_name
29
+ end
30
+ end
31
+ end
32
+ private :receiver_unless_main
33
+
34
+ # The ri method invokes the ri documentation tool to display help
35
+ # information for the specified patterns. It automatically determines the
36
+ # pattern to search for when none are provided.
37
+ # The method handles different types of patterns including modules,
38
+ # objects that respond to to_str, and other objects. Documentation is
39
+ # displayed through the system's ri command with output piped to the
40
+ # pager.
41
+ #
42
+ # @param patterns [ Array ] the patterns to search for in the documentation
43
+ # @param doc [ String ] the documentation command to execute (defaults to 'ri')
44
+ def ri(*patterns, doc: 'ri')
45
+ patterns.empty? and
46
+ receiver_unless_main(method(__method__)) do |pattern|
47
+ return ri(pattern, doc: doc)
48
+ end
49
+ patterns.map! { |p|
50
+ case
51
+ when Module === p
52
+ p.name
53
+ when p.respond_to?(:to_str)
54
+ p.to_str
55
+ else
56
+ p.class.name
57
+ end
58
+ }
59
+ system "#{doc} #{patterns.map { |p| "'#{p}'" } * ' ' } | #$pager"
60
+ end
61
+
62
+ # The yri method invokes the ri documentation tool with yri as the
63
+ # documenter to display help information for the specified patterns.
64
+ #
65
+ # @param patterns [ Array<String> ] the patterns to look up documentation for
66
+ def yri(*patterns)
67
+ ri(*patterns, doc: 'yri')
68
+ end
69
+
70
+ # The ai method interacts with an Ollama chat service to process queries
71
+ # and optionally return responses.
72
+ #
73
+ # This method constructs command-line arguments for the ollama_chat_send
74
+ # utility based on the provided options, executes the command with the
75
+ # query as input, and returns the response if requested.
76
+ #
77
+ # @param query [ String ] the input query to send to the Ollama chat
78
+ # service
79
+ # @param command [ TrueClass, FalseClass ] whether to treat the query as
80
+ # a command
81
+ # @param respond [ TrueClass, FalseClass ] whether to capture and return
82
+ # the response from the service
83
+ # @param parse [ TrueClass, FalseClass ] whether to parse the response
84
+ # @param dir [ String ] the directory to use for the operation
85
+ #
86
+ # @return [ String, nil ] the response from the Ollama chat service if
87
+ # respond is true, otherwise nil
88
+ def ai(query, command: false, respond: false, parse: false, dir: ?.)
89
+ dir = File.expand_path(dir)
90
+ args = {
91
+ ?r => respond,
92
+ ?t => command,
93
+ ?p => parse,
94
+ ?d => dir,
95
+ }
96
+ args = args.map { |k, v|
97
+ v == false and next
98
+ v == true ? "-#{k}" : [ "-#{k}", v.to_s ]
99
+ }.flatten.compact
100
+ args.unshift 'ollama_chat_send'
101
+ response = nil
102
+ IO.popen(Shellwords.join(args), 'r+') do |io|
103
+ io.write query
104
+ io.close_write
105
+ if respond
106
+ response = io.read
107
+ end
108
+ end
109
+ response
110
+ end
111
+
112
+ # The irb_open method opens a URL or executes a block to capture output
113
+ # and open it.
114
+ #
115
+ # This method provides a way to open URLs or capture the output of a
116
+ # block and open it in the default application. If a URL is provided, it
117
+ # directly opens the URL. If a block is given, it captures the output of
118
+ # the block, writes it to a temporary file, and opens that file. If
119
+ # neither is provided, it raises an error.
120
+ #
121
+ # @param url [ String, nil ] the URL to open
122
+ # @param block [ Proc, nil ] the block to capture output from
123
+ def irb_open(url = nil, &block)
124
+ case
125
+ when url
126
+ system 'open', url
127
+ when block
128
+ Tempfile.open('wb') do |t|
129
+ t.write capture_output(&block)
130
+ t.rewind
131
+ system 'open', t.path
132
+ end
133
+ when url = receiver_unless_main(method(__method__))
134
+ irb_open url
135
+ else
136
+ raise ArgumentError, 'need an url or block'
137
+ end
138
+ end
139
+
140
+ # This method obtains the complete list of instance methods available for
141
+ # the specified object's class, then processes them through the
142
+ # irb_wrap_methods helper to prepare them for interactive use in IRB.
143
+ #
144
+ # @param obj [ Object ] the object whose class instance methods are to be retrieved
145
+ #
146
+ # @return [ Array ] an array of wrapped method objects suitable for IRB interaction
147
+ def irb_all_class_instance_methods(obj = self)
148
+ methods = obj.class.instance_methods
149
+ irb_wrap_methods obj, methods
150
+ end
151
+
152
+ # The irb_class_instance_methods method retrieves instance methods
153
+ # defined directly in the class of the given object, excluding inherited
154
+ # methods, and wraps them for enhanced interactive exploration in IRB
155
+ # environment.
156
+ #
157
+ # @param obj [ Object ] the object whose class instance methods are to be retrieved
158
+ #
159
+ # @return [ Array ] an array of wrapped method objects suitable for IRB interaction
160
+ def irb_class_instance_methods(obj = self)
161
+ methods = obj.class.instance_methods(false)
162
+ irb_wrap_methods obj, methods
163
+ end
164
+
165
+ # The irb_all_instance_methods method retrieves all instance methods
166
+ # defined on a module.
167
+ #
168
+ # This method collects the instance methods from the specified module and
169
+ # wraps them for enhanced interactive exploration in IRB. It is designed
170
+ # to provide a more user-friendly interface for examining module methods
171
+ # within the interactive Ruby environment.
172
+ #
173
+ # @param modul [ Object ] the module from which to retrieve instance methods
174
+ #
175
+ # @return [ Array ] an array of wrapped method objects suitable for IRB interaction
176
+ def irb_all_instance_methods(modul = self)
177
+ methods = modul.instance_methods
178
+ irb_wrap_methods modul, methods, true
179
+ end
180
+
181
+ # Return instance methods defined in module modul without the inherited/mixed
182
+ # in methods.
183
+ # The irb_instance_methods method retrieves instance methods defined directly in a module.
184
+ #
185
+ # This method fetches all instance methods that are explicitly defined within the specified module,
186
+ # excluding inherited methods. It then wraps these methods for enhanced interactive exploration
187
+ # within the IRB environment.
188
+ #
189
+ # @param modul [ Object ] the module from which to retrieve instance methods
190
+ #
191
+ # @return [ Array ] an array of wrapped method objects suitable for IRB interaction
192
+ def irb_instance_methods(modul = self)
193
+ methods = modul.instance_methods(false)
194
+ irb_wrap_methods modul, methods, true
195
+ end
196
+
197
+ # The irb_all_methods method retrieves all methods available on an
198
+ # object.
199
+ #
200
+ # This method collects all methods associated with the given object
201
+ # (including its singleton methods) and wraps them for enhanced
202
+ # interactive exploration in IRB. It provides a comprehensive list
203
+ # of methods that can be used to understand the object's capabilities and
204
+ # interface.
205
+ #
206
+ # @param obj [ Object ] the object whose methods are to be retrieved
207
+ #
208
+ # @return [ Array ] an array of wrapped method objects for interactive use
209
+ def irb_all_methods(obj = self)
210
+ methods = obj.methods
211
+ irb_wrap_methods obj, methods
212
+ end
213
+
214
+ # The irb_methods method retrieves instance methods defined in the class
215
+ # hierarchy excluding those inherited from ancestor classes.
216
+ #
217
+ # This method computes a list of instance methods that are directly
218
+ # defined in the class of the given object, excluding any methods that
219
+ # are inherited from its superclass or modules. It then wraps these
220
+ # methods for enhanced display in IRB.
221
+ #
222
+ # @param obj [ Object ] the object whose class methods are to be examined
223
+ #
224
+ # @return [ Array ] an array of wrapped method objects for display in IRB
225
+ def irb_methods(obj = self)
226
+ methods = obj.class.ancestors[1..-1].inject(obj.methods) do |all, a|
227
+ all -= a.instance_methods
228
+ end
229
+ irb_wrap_methods obj, methods
230
+ end
231
+
232
+ # The irb_singleton_methods method retrieves singleton methods associated
233
+ # with an object.
234
+ #
235
+ # This method collects all singleton methods defined on the specified object,
236
+ # excluding inherited methods, and prepares them for display in an interactive
237
+ # Ruby environment.
238
+ #
239
+ # @param obj [ Object ] the object whose singleton methods are to be retrieved
240
+ #
241
+ # @return [ Array ] an array of singleton method names associated with the object
242
+ def irb_singleton_methods(obj = self)
243
+ irb_wrap_methods obj, obj.methods(false)
244
+ end
245
+
246
+ # The irb_wrap_methods method creates wrapped method objects for introspection.
247
+ #
248
+ # This method takes a set of method names and wraps them in a way that allows
249
+ # for easier inspection and display within an IRB session. It handles
250
+ # potential errors during the wrapping process by rescuing exceptions and
251
+ # filtering out invalid entries.
252
+ #
253
+ # @param obj [ Object ] the object whose methods are being wrapped
254
+ # @param methods [ Array ] the array of method names to wrap
255
+ # @param modul [ TrueClass, FalseClass ] flag indicating if the methods are module methods
256
+ #
257
+ # @return [ Array ] an array of wrapped method objects sorted in ascending order
258
+ def irb_wrap_methods(obj = self, methods = methods(), modul = false)
259
+ methods.map do |name|
260
+ MethodWrapper.new(obj, name, modul) rescue nil
261
+ end.compact.sort!
262
+ end
263
+
264
+ # The irb_constants method retrieves and wraps all constants from a given
265
+ # module.
266
+ #
267
+ # This method collects all constants defined in the specified module,
268
+ # creates ConstantWrapper instances for each constant, and returns them
269
+ # sorted in ascending order.
270
+ #
271
+ # @param modul [ Object ] the module from which to retrieve constants
272
+ #
273
+ # @return [ Array<ConstantWrapper> ] an array of ConstantWrapper objects
274
+ # representing the constants in the module, sorted alphabetically
275
+ def irb_constants(modul = self)
276
+ if modul.respond_to?(:constants)
277
+ modul.constants.map { |c| ConstantWrapper.new(modul.const_get(c), c) }.sort
278
+ else
279
+ warn "#{modul} does not respond to constants method"
280
+ end
281
+ end
282
+
283
+ # The irb_subclasses method retrieves and wraps subclass information for
284
+ # a given class.
285
+ #
286
+ # This method fetches the subclasses of the specified class and creates
287
+ # ConstantWrapper instances for each subclass, allowing them to be sorted
288
+ # and displayed in a structured format.
289
+ #
290
+ # @param klass [ Object ] the class object to retrieve subclasses from
291
+ #
292
+ # @return [ Array<ConstantWrapper> ] an array of ConstantWrapper objects
293
+ # representing the subclasses
294
+ def irb_subclasses(klass = self)
295
+ klass.subclasses.map { |c| ConstantWrapper.new(eval(c), c) }.sort
296
+ end
297
+
298
+ unless Object.const_defined?(:Infinity)
299
+ Infinity = 1.0 / 0 # I like to define the infinite.
300
+ end
301
+
302
+ # The capture_output method captures stdout and optionally stderr output
303
+ # during code execution.
304
+ #
305
+ # This method temporarily redirects standard output (and optionally
306
+ # standard error) to a temporary file, executes the provided block, and
307
+ # then returns the captured output as a string.
308
+ #
309
+ # @param with_stderr [ TrueClass, FalseClass ] whether to also capture standard error output
310
+ #
311
+ # @yield [ void ] the block of code to execute while capturing output
312
+ #
313
+ # @return [ String ] the captured output as a string
314
+ def capture_output(with_stderr = false)
315
+ begin
316
+ old_stdout, $stdout = $stdout, Tempfile.new('irb')
317
+ if with_stderr
318
+ old_stderr, $stderr = $stderr, $stdout
319
+ end
320
+ yield
321
+ ensure
322
+ $stdout, temp = old_stdout, $stdout
323
+ with_stderr and $stderr = old_stderr
324
+ end
325
+ temp.rewind
326
+ temp.read
327
+ end
328
+
329
+ # Use pager on the output of the commands given in the block. The less
330
+ # method executes a block and outputs its result through the pager.
331
+ #
332
+ # This method runs the provided block in a controlled environment,
333
+ # captures its output, and streams that output through the system's
334
+ # configured pager for display.
335
+ #
336
+ # @param with_stderr [ TrueClass, FalseClass ] whether to include standard error in the capture
337
+ #
338
+ # @yield [ void ]
339
+ def less(with_stderr = false, &block)
340
+ IO.popen($pager, 'w') do |f|
341
+ f.write capture_output(with_stderr, &block)
342
+ f.close_write
343
+ end
344
+ nil
345
+ end
346
+
347
+ # The irb_time method measures the execution time of a block and outputs
348
+ # the duration to standard error.
349
+ #
350
+ # @param n [ Integer ] the number of times to execute the block, defaults
351
+ # to 1
352
+ #
353
+ # @yield [ block ] the block to be executed and timed
354
+ def irb_time(n = 1, &block)
355
+ s = Time.now
356
+ n.times(&block)
357
+ d = Time.now - s
358
+ ensure
359
+ d ||= Time.now - s
360
+ if n == 1
361
+ warn "Took %.3fs seconds." % d
362
+ else
363
+ warn "Took %.3fs seconds, %.3fs per call (avg)." % [ d, d / n ]
364
+ end
365
+ end
366
+
367
+ # The irb_time_result method executes a block n times while measuring
368
+ # execution time and returns the result of the last execution.
369
+ #
370
+ # @param n [ Integer ] the number of times to execute the block
371
+ #
372
+ # @yield [ i ]
373
+ #
374
+ # @return [ Object ] the result of the last block execution
375
+ def irb_time_result(n = 1)
376
+ r = nil
377
+ irb_time(n) { |i| r = yield(i) }
378
+ r
379
+ end
380
+
381
+ # The irb_time_watch method monitors and reports performance metrics over
382
+ # time.
383
+ #
384
+ # This method continuously measures the output of a provided block,
385
+ # calculating differences and rates of change between successive
386
+ # measurements. It tracks these metrics and displays them with timing
387
+ # information, useful for observing how values evolve during execution.
388
+ #
389
+ # @param duration [ Integer ] the time interval in seconds between
390
+ # measurements
391
+ #
392
+ # @yield [ i ] the block to be measured, receiving the iteration count as an argument
393
+ def irb_time_watch(duration = 1)
394
+ normalize = -> value { value.ask_and_send(:to_f) || 0.0 }
395
+ start = Time.now
396
+ pre = nil
397
+ avg = Hash.new
398
+ i = 0
399
+ fetch_next = -> cur do
400
+ pre = cur.map(&normalize)
401
+ i += 1
402
+ sleep duration
403
+ end
404
+ loop do
405
+ cur = [ yield(i) ].flatten.map(&normalize)
406
+ unless pre
407
+ fetch_next.(cur)
408
+ redo
409
+ end
410
+ expired = Time.now - start
411
+ diffs = cur.zip(pre).map { |c, p| c - p }
412
+ rates = diffs.map { |d| d / duration }
413
+ durs = cur.zip(rates).each_with_index.map { |(c, r), i|
414
+ if r < 0
415
+ x = c.to_f / -r
416
+ a = avg[i].to_f
417
+ a -= a / 2
418
+ a += x / 2
419
+ d = Tins::Duration.new(a)
420
+ ds = d.to_s
421
+ ds.singleton_class { define_method(:to_f) { d.to_f } }
422
+ avg[i] = ds
423
+ end
424
+ avg[i]
425
+ }
426
+ warn "#{expired} #{cur.zip(diffs, rates, durs) * ' '} 𝝙 / per sec."
427
+ fetch_next.(cur)
428
+ sleep duration
429
+ end
430
+ end
431
+
432
+ # The irb_write method writes text to a file or executes a block to
433
+ # generate content for writing.
434
+ #
435
+ # This method provides a convenient way to write content to a file,
436
+ # either by passing the text directly or by executing a block that
437
+ # generates the content. It uses secure file writing to ensure safety.
438
+ #
439
+ # @param filename [ String ] the path to the file where content will be
440
+ # written
441
+ # @param text [ String, nil ] the text content to write to the file, or
442
+ # nil if using a block
443
+ #
444
+ # @yield [ ] a block that generates content to be written to the file
445
+ def irb_write(filename, text = nil, &block)
446
+ if text.nil? && block
447
+ File.secure_write filename, nil, 'wb', &block
448
+ else
449
+ File.secure_write filename, text, 'wb'
450
+ end
451
+ end
452
+
453
+ # The irb_read method reads the contents of a file either entirely or in
454
+ # chunks. When a block is provided, it reads the file in chunks of the
455
+ # specified size and yields each chunk to the block.
456
+ # If no block is given, it reads the entire file content at once and
457
+ # returns it as a string.
458
+ #
459
+ # @param filename [ String ] the path to the file to be read
460
+ # @param chunk_size [ Integer ] the size of each chunk to read when a
461
+ # block is provided
462
+ #
463
+ # @yield [ chunk ] yields each chunk of the file to the block
464
+ # @yieldparam chunk [ String ] a portion of the file content
465
+ #
466
+ # @return [ String, nil ] the entire file content if no block is given,
467
+ # otherwise nil
468
+ def irb_read(filename, chunk_size = 8_192)
469
+ if block_given?
470
+ File.open(filename) do |file|
471
+ until file.eof?
472
+ yield file.read(chunk_size)
473
+ end
474
+ end
475
+ nil
476
+ else
477
+ File.read filename
478
+ end
479
+ end
480
+
481
+ # The irb_load! method loads Ruby files by their names into the current
482
+ # environment through an interactive selection interface.
483
+ #
484
+ # This method takes a glob pattern and finds matching Ruby files, then
485
+ # presents an interactive search interface for selecting which file to load.
486
+ # It ensures that each file is loaded only once by tracking loaded files
487
+ # using their paths. The method outputs messages to standard error
488
+ # indicating which file has been successfully loaded.
489
+ #
490
+ # @param glob [String] the glob pattern to search for Ruby files (defaults to
491
+ # ENV['UTILS_IRB_LOAD_GLOB'] or 'lib/**/*.rb')
492
+ #
493
+ # @return [Boolean] true if a file was successfully loaded, false if no file
494
+ # was selected or loaded
495
+ #
496
+ # @example
497
+ # # Load a file interactively with default glob pattern
498
+ # irb_load!
499
+ #
500
+ # # Load files matching a custom pattern
501
+ # irb_load!('app/models/**/*.rb')
502
+ #
503
+ # # Set environment variable for default pattern
504
+ # ENV['UTILS_IRB_LOAD_GLOB'] = 'lib/**/*.rb'
505
+ # irb_load!
506
+ #
507
+ # @note This method uses fuzzy matching to help find files when typing
508
+ # partial names. It respects the terminal height to limit the number of
509
+ # displayed results.
510
+ #
511
+ # @see SearchUI for the interactive search interface implementation
512
+ # @see Amatch::PairDistance for the fuzzy matching algorithm
513
+ def irb_load!(glob = ENV.fetch('UTILS_IRB_LOAD_GLOB', 'lib/**/*.rb'))
514
+ files = Dir.glob(glob)
515
+ found = Search.new(
516
+ match: -> answer {
517
+ matcher = Amatch::PairDistance.new(answer.downcase)
518
+ matches = files.map { |n| [ n, -matcher.similar(n.downcase) ] }.
519
+ sort.select { _2 < 0 }.sort_by(&:last).map(&:first)
520
+ matches.empty? and matches = files
521
+ matches.first(Tins::Terminal.lines - 1)
522
+ },
523
+ query: -> _answer, matches, selector {
524
+ matches.each_with_index.
525
+ map { |m, i| i == selector ? "→ " + Search.on_blue(m) : " " + m } * ?\n
526
+ },
527
+ found: -> _answer, matches, selector {
528
+ matches[selector]
529
+ },
530
+ output: STDOUT
531
+ ).start
532
+ found or return false
533
+ load found
534
+ end
535
+
536
+ # The irb_server method provides access to an IRB server instance for
537
+ # interactive Ruby sessions.
538
+ #
539
+ # This method ensures that a single IRB server instance is created and
540
+ # started for the current process, loading the configuration from
541
+ # standard paths and using the configured server URL.
542
+ #
543
+ # @return [ Utils::IRB::IRBServer ] the IRB server instance, initialized
544
+ # and started if not already running
545
+ def irb_server
546
+ unless @irb_server
547
+ config = Utils::ConfigFile.new.tap(&:configure_from_paths)
548
+ @irb_server = Utils::IRB::IRBServer.new(url: config.irb_server_url).start
549
+ end
550
+ @irb_server
551
+ end
552
+
553
+ # The irb_current_snippet method retrieves the current code snippet
554
+ # stored in the IRB server.
555
+ #
556
+ # This method accesses the IRB server instance and returns the snippet
557
+ # that has been stored for execution, or nil if no snippet is currently
558
+ # stored or if the server is not available.
559
+ #
560
+ # @return [ String, nil ] the current code snippet stored in the IRB
561
+ # server, or nil if not available
562
+ def irb_current_snippet
563
+ irb_server&.snippet
564
+ end
565
+
566
+ # The irb_server_stop method sends a stop command to the IRB server
567
+ # client.
568
+ #
569
+ # This method accesses the IRB client instance and invokes the
570
+ # stop_server method on it, which gracefully shuts down the IRB server
571
+ # process.
572
+ #
573
+ # @return [ nil ] always returns nil after sending the stop command to
574
+ # the server
575
+ def irb_server_stop
576
+ irb_client.stop_server
577
+ end
578
+
579
+ # The irb_client method provides access to an IRB server client instance.
580
+ #
581
+ # This method creates and returns a new IRB server client by first
582
+ # loading the configuration from standard paths and then using the
583
+ # configured server URL
584
+ # to initialize the client.
585
+ #
586
+ # @return [ Utils::IRB::IRBServer ] a new IRB server client instance configured
587
+ # with the URL from the application's configuration
588
+ def irb_client
589
+ config = Utils::ConfigFile.new.tap(&:configure_from_paths)
590
+ Utils::IRB::IRBServer.new(url: config.irb_server_url)
591
+ end
592
+
593
+ # The ed method opens files for editing using the system editor.
594
+ #
595
+ # This method provides a convenient way to edit files by invoking the
596
+ # configured editor. When called without arguments, it edits the current
597
+ # object's representation. When called with file arguments, it edits those
598
+ # specific files.
599
+ #
600
+ # @param files [ Array ] an array of file paths to be edited
601
+ def ed(*files)
602
+ if files.empty?
603
+ $editor.full?(:edit, self)
604
+ else
605
+ $editor.full?(:edit, *files)
606
+ end
607
+ end
608
+
609
+ if defined?(ActiveRecord::Base)
610
+ $logger = Logger.new(STDERR)
611
+ # The irb_toggle_logging method toggles the logging configuration for
612
+ # ActiveRecord.
613
+ #
614
+ # This method manages the logger setting for ActiveRecord by switching
615
+ # between a custom logger and the previously configured logger. It
616
+ # returns true when switching to the custom logger, and false when
617
+ # reverting to the original logger.
618
+ #
619
+ # @return [ TrueClass, FalseClass ] true if the logger was switched to
620
+ # the custom logger, false if it was reverted to the original logger
621
+ def irb_toggle_logging
622
+ if ActiveRecord::Base.logger != $logger
623
+ $old_logger = ActiveRecord::Base.logger
624
+ ActiveRecord::Base.logger = $logger
625
+ true
626
+ else
627
+ ActiveRecord::Base.logger = $old_logger
628
+ false
629
+ end
630
+ end
631
+ end
632
+ end
@@ -0,0 +1,36 @@
1
+ # A module that extends String with additional utility methods for shell
2
+ # command piping and file writing operations.
3
+ #
4
+ # Provides convenient methods for executing shell commands on string
5
+ # content and securely writing strings to files.
6
+ module Utils::IRB::String
7
+ # The | method executes a shell command and returns its output.
8
+ #
9
+ # This method takes a command string, pipes the current string to it via
10
+ # stdin, captures the command's stdout, and returns the resulting output
11
+ # as a string.
12
+ #
13
+ # @param cmd [ String ] the shell command to execute
14
+ #
15
+ # @return [ String ] the output of the executed command
16
+ def |(cmd)
17
+ IO.popen(cmd, 'w+') do |f|
18
+ f.write self
19
+ f.close_write
20
+ return f.read
21
+ end
22
+ end
23
+
24
+ # The >> method writes the string content to a file securely.
25
+ #
26
+ # This method takes a filename and uses File.secure_write to write the
27
+ # string's content to that file, ensuring secure file handling practices
28
+ # are followed.
29
+ #
30
+ # @param filename [ String ] the path to the file where the string content will be written
31
+ #
32
+ # @return [ Integer ] the number of bytes written to the file
33
+ def >>(filename)
34
+ File.secure_write(filename, self)
35
+ end
36
+ end