utils 0.90.0 → 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.
- checksums.yaml +4 -4
- data/lib/utils/irb/irb_server.rb +1 -1
- data/lib/utils/irb/regexp.rb +32 -0
- data/lib/utils/irb/shell/wrappers.rb +183 -0
- data/lib/utils/irb/shell.rb +632 -0
- data/lib/utils/irb/string.rb +36 -0
- data/lib/utils/irb.rb +4 -865
- data/lib/utils/line_formatter.rb +12 -6
- data/lib/utils/version.rb +1 -1
- data/lib/utils/xt/source_location_extension.rb +13 -4
- data/lib/utils.rb +19 -1
- data/utils.gemspec +4 -4
- metadata +9 -1
data/lib/utils/irb.rb
CHANGED
|
@@ -7,6 +7,7 @@ require 'utils'
|
|
|
7
7
|
require 'fileutils'
|
|
8
8
|
require 'amatch'
|
|
9
9
|
require 'search_ui'
|
|
10
|
+
require 'logger'
|
|
10
11
|
require_maybe 'ap'
|
|
11
12
|
|
|
12
13
|
$editor = Utils::Editor.new
|
|
@@ -21,871 +22,6 @@ module Utils
|
|
|
21
22
|
# pattern matching, shell command integration, file I/O operations,
|
|
22
23
|
# performance measurement tools, and developer productivity enhancements.
|
|
23
24
|
module IRB
|
|
24
|
-
# A module that extends Regexp functionality with additional pattern
|
|
25
|
-
# matching and display capabilities.
|
|
26
|
-
#
|
|
27
|
-
# Provides enhanced regexp operations including match highlighting and
|
|
28
|
-
# shell command integration.
|
|
29
|
-
module Shell
|
|
30
|
-
include SearchUI
|
|
31
|
-
include FileUtils
|
|
32
|
-
include Tins::Find
|
|
33
|
-
|
|
34
|
-
# The receiver_unless_main method retrieves the receiver name of a method
|
|
35
|
-
# unless it is the main object, optionally executing a block with the
|
|
36
|
-
# receiver name.
|
|
37
|
-
#
|
|
38
|
-
# @param method [ Method ] the method object to inspect
|
|
39
|
-
# @param block [ Proc ] an optional block to execute with the receiver name
|
|
40
|
-
#
|
|
41
|
-
# @return [ String, nil ] the receiver name if it is not 'main', otherwise nil
|
|
42
|
-
def receiver_unless_main(method, &block)
|
|
43
|
-
receiver_name = method.receiver.to_s
|
|
44
|
-
if receiver_name != 'main'
|
|
45
|
-
if block
|
|
46
|
-
block.(receiver_name)
|
|
47
|
-
else
|
|
48
|
-
receiver_name
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
private :receiver_unless_main
|
|
53
|
-
|
|
54
|
-
# The ri method invokes the ri documentation tool to display help
|
|
55
|
-
# information for the specified patterns. It automatically determines the
|
|
56
|
-
# pattern to search for when none are provided.
|
|
57
|
-
# The method handles different types of patterns including modules,
|
|
58
|
-
# objects that respond to to_str, and other objects. Documentation is
|
|
59
|
-
# displayed through the system's ri command with output piped to the
|
|
60
|
-
# pager.
|
|
61
|
-
#
|
|
62
|
-
# @param patterns [ Array ] the patterns to search for in the documentation
|
|
63
|
-
# @param doc [ String ] the documentation command to execute (defaults to 'ri')
|
|
64
|
-
def ri(*patterns, doc: 'ri')
|
|
65
|
-
patterns.empty? and
|
|
66
|
-
receiver_unless_main(method(__method__)) do |pattern|
|
|
67
|
-
return ri(pattern, doc: doc)
|
|
68
|
-
end
|
|
69
|
-
patterns.map! { |p|
|
|
70
|
-
case
|
|
71
|
-
when Module === p
|
|
72
|
-
p.name
|
|
73
|
-
when p.respond_to?(:to_str)
|
|
74
|
-
p.to_str
|
|
75
|
-
else
|
|
76
|
-
p.class.name
|
|
77
|
-
end
|
|
78
|
-
}
|
|
79
|
-
system "#{doc} #{patterns.map { |p| "'#{p}'" } * ' ' } | #$pager"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# The yri method invokes the ri documentation tool with yri as the
|
|
83
|
-
# documenter to display help information for the specified patterns.
|
|
84
|
-
#
|
|
85
|
-
# @param patterns [ Array<String> ] the patterns to look up documentation for
|
|
86
|
-
def yri(*patterns)
|
|
87
|
-
ri(*patterns, doc: 'yri')
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# The ai method interacts with an Ollama chat service to process queries
|
|
91
|
-
# and optionally return responses.
|
|
92
|
-
#
|
|
93
|
-
# This method constructs command-line arguments for the ollama_chat_send
|
|
94
|
-
# utility based on the provided options, executes the command with the
|
|
95
|
-
# query as input, and returns the response if requested.
|
|
96
|
-
#
|
|
97
|
-
# @param query [ String ] the input query to send to the Ollama chat
|
|
98
|
-
# service
|
|
99
|
-
# @param command [ TrueClass, FalseClass ] whether to treat the query as
|
|
100
|
-
# a command
|
|
101
|
-
# @param respond [ TrueClass, FalseClass ] whether to capture and return
|
|
102
|
-
# the response from the service
|
|
103
|
-
# @param parse [ TrueClass, FalseClass ] whether to parse the response
|
|
104
|
-
# @param dir [ String ] the directory to use for the operation
|
|
105
|
-
#
|
|
106
|
-
# @return [ String, nil ] the response from the Ollama chat service if
|
|
107
|
-
# respond is true, otherwise nil
|
|
108
|
-
def ai(query, command: false, respond: false, parse: false, dir: ?.)
|
|
109
|
-
dir = File.expand_path(dir)
|
|
110
|
-
args = {
|
|
111
|
-
?r => respond,
|
|
112
|
-
?t => command,
|
|
113
|
-
?p => parse,
|
|
114
|
-
?d => dir,
|
|
115
|
-
}
|
|
116
|
-
args = args.map { |k, v|
|
|
117
|
-
v == false and next
|
|
118
|
-
v == true ? "-#{k}" : [ "-#{k}", v.to_s ]
|
|
119
|
-
}.flatten.compact
|
|
120
|
-
args.unshift 'ollama_chat_send'
|
|
121
|
-
response = nil
|
|
122
|
-
IO.popen(Shellwords.join(args), 'r+') do |io|
|
|
123
|
-
io.write query
|
|
124
|
-
io.close_write
|
|
125
|
-
if respond
|
|
126
|
-
response = io.read
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
response
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# The irb_open method opens a URL or executes a block to capture output
|
|
133
|
-
# and open it.
|
|
134
|
-
#
|
|
135
|
-
# This method provides a way to open URLs or capture the output of a
|
|
136
|
-
# block and open it in the default application. If a URL is provided, it
|
|
137
|
-
# directly opens the URL. If a block is given, it captures the output of
|
|
138
|
-
# the block, writes it to a temporary file, and opens that file. If
|
|
139
|
-
# neither is provided, it raises an error.
|
|
140
|
-
#
|
|
141
|
-
# @param url [ String, nil ] the URL to open
|
|
142
|
-
# @param block [ Proc, nil ] the block to capture output from
|
|
143
|
-
def irb_open(url = nil, &block)
|
|
144
|
-
case
|
|
145
|
-
when url
|
|
146
|
-
system 'open', url
|
|
147
|
-
when block
|
|
148
|
-
Tempfile.open('wb') do |t|
|
|
149
|
-
t.write capture_output(&block)
|
|
150
|
-
t.rewind
|
|
151
|
-
system 'open', t.path
|
|
152
|
-
end
|
|
153
|
-
when url = receiver_unless_main(method(__method__))
|
|
154
|
-
irb_open url
|
|
155
|
-
else
|
|
156
|
-
raise ArgumentError, 'need an url or block'
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# This method obtains the complete list of instance methods available for
|
|
161
|
-
# the specified object's class, then processes them through the
|
|
162
|
-
# irb_wrap_methods helper to prepare them for interactive use in IRB.
|
|
163
|
-
#
|
|
164
|
-
# @param obj [ Object ] the object whose class instance methods are to be retrieved
|
|
165
|
-
#
|
|
166
|
-
# @return [ Array ] an array of wrapped method objects suitable for IRB interaction
|
|
167
|
-
def irb_all_class_instance_methods(obj = self)
|
|
168
|
-
methods = obj.class.instance_methods
|
|
169
|
-
irb_wrap_methods obj, methods
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# The irb_class_instance_methods method retrieves instance methods
|
|
173
|
-
# defined directly in the class of the given object, excluding inherited
|
|
174
|
-
# methods, and wraps them for enhanced interactive exploration in IRB
|
|
175
|
-
# environment.
|
|
176
|
-
#
|
|
177
|
-
# @param obj [ Object ] the object whose class instance methods are to be retrieved
|
|
178
|
-
#
|
|
179
|
-
# @return [ Array ] an array of wrapped method objects suitable for IRB interaction
|
|
180
|
-
def irb_class_instance_methods(obj = self)
|
|
181
|
-
methods = obj.class.instance_methods(false)
|
|
182
|
-
irb_wrap_methods obj, methods
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# The irb_all_instance_methods method retrieves all instance methods
|
|
186
|
-
# defined on a module.
|
|
187
|
-
#
|
|
188
|
-
# This method collects the instance methods from the specified module and
|
|
189
|
-
# wraps them for enhanced interactive exploration in IRB. It is designed
|
|
190
|
-
# to provide a more user-friendly interface for examining module methods
|
|
191
|
-
# within the interactive Ruby environment.
|
|
192
|
-
#
|
|
193
|
-
# @param modul [ Object ] the module from which to retrieve instance methods
|
|
194
|
-
#
|
|
195
|
-
# @return [ Array ] an array of wrapped method objects suitable for IRB interaction
|
|
196
|
-
def irb_all_instance_methods(modul = self)
|
|
197
|
-
methods = modul.instance_methods
|
|
198
|
-
irb_wrap_methods modul, methods, true
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Return instance methods defined in module modul without the inherited/mixed
|
|
202
|
-
# in methods.
|
|
203
|
-
# The irb_instance_methods method retrieves instance methods defined directly in a module.
|
|
204
|
-
#
|
|
205
|
-
# This method fetches all instance methods that are explicitly defined within the specified module,
|
|
206
|
-
# excluding inherited methods. It then wraps these methods for enhanced interactive exploration
|
|
207
|
-
# within the IRB environment.
|
|
208
|
-
#
|
|
209
|
-
# @param modul [ Object ] the module from which to retrieve instance methods
|
|
210
|
-
#
|
|
211
|
-
# @return [ Array ] an array of wrapped method objects suitable for IRB interaction
|
|
212
|
-
def irb_instance_methods(modul = self)
|
|
213
|
-
methods = modul.instance_methods(false)
|
|
214
|
-
irb_wrap_methods modul, methods, true
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
# The irb_all_methods method retrieves all methods available on an
|
|
218
|
-
# object.
|
|
219
|
-
#
|
|
220
|
-
# This method collects all methods associated with the given object
|
|
221
|
-
# (including its singleton methods) and wraps them for enhanced
|
|
222
|
-
# interactive exploration in IRB. It provides a comprehensive list
|
|
223
|
-
# of methods that can be used to understand the object's capabilities and
|
|
224
|
-
# interface.
|
|
225
|
-
#
|
|
226
|
-
# @param obj [ Object ] the object whose methods are to be retrieved
|
|
227
|
-
#
|
|
228
|
-
# @return [ Array ] an array of wrapped method objects for interactive use
|
|
229
|
-
def irb_all_methods(obj = self)
|
|
230
|
-
methods = obj.methods
|
|
231
|
-
irb_wrap_methods obj, methods
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# The irb_methods method retrieves instance methods defined in the class
|
|
235
|
-
# hierarchy excluding those inherited from ancestor classes.
|
|
236
|
-
#
|
|
237
|
-
# This method computes a list of instance methods that are directly
|
|
238
|
-
# defined in the class of the given object, excluding any methods that
|
|
239
|
-
# are inherited from its superclass or modules. It then wraps these
|
|
240
|
-
# methods for enhanced display in IRB.
|
|
241
|
-
#
|
|
242
|
-
# @param obj [ Object ] the object whose class methods are to be examined
|
|
243
|
-
#
|
|
244
|
-
# @return [ Array ] an array of wrapped method objects for display in IRB
|
|
245
|
-
def irb_methods(obj = self)
|
|
246
|
-
methods = obj.class.ancestors[1..-1].inject(obj.methods) do |all, a|
|
|
247
|
-
all -= a.instance_methods
|
|
248
|
-
end
|
|
249
|
-
irb_wrap_methods obj, methods
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# The irb_singleton_methods method retrieves singleton methods associated
|
|
253
|
-
# with an object.
|
|
254
|
-
#
|
|
255
|
-
# This method collects all singleton methods defined on the specified object,
|
|
256
|
-
# excluding inherited methods, and prepares them for display in an interactive
|
|
257
|
-
# Ruby environment.
|
|
258
|
-
#
|
|
259
|
-
# @param obj [ Object ] the object whose singleton methods are to be retrieved
|
|
260
|
-
#
|
|
261
|
-
# @return [ Array ] an array of singleton method names associated with the object
|
|
262
|
-
def irb_singleton_methods(obj = self)
|
|
263
|
-
irb_wrap_methods obj, obj.methods(false)
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
# The irb_wrap_methods method creates wrapped method objects for introspection.
|
|
267
|
-
#
|
|
268
|
-
# This method takes a set of method names and wraps them in a way that allows
|
|
269
|
-
# for easier inspection and display within an IRB session. It handles
|
|
270
|
-
# potential errors during the wrapping process by rescuing exceptions and
|
|
271
|
-
# filtering out invalid entries.
|
|
272
|
-
#
|
|
273
|
-
# @param obj [ Object ] the object whose methods are being wrapped
|
|
274
|
-
# @param methods [ Array ] the array of method names to wrap
|
|
275
|
-
# @param modul [ TrueClass, FalseClass ] flag indicating if the methods are module methods
|
|
276
|
-
#
|
|
277
|
-
# @return [ Array ] an array of wrapped method objects sorted in ascending order
|
|
278
|
-
def irb_wrap_methods(obj = self, methods = methods(), modul = false)
|
|
279
|
-
methods.map do |name|
|
|
280
|
-
MethodWrapper.new(obj, name, modul) rescue nil
|
|
281
|
-
end.compact.sort!
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
# Base class for wrapping objects with descriptive metadata.
|
|
285
|
-
#
|
|
286
|
-
# This class provides a foundation for creating wrapper objects that
|
|
287
|
-
# associate descriptive information with underlying objects. It handles
|
|
288
|
-
# name conversion and provides common methods for accessing and comparing
|
|
289
|
-
# wrapped objects.
|
|
290
|
-
class WrapperBase
|
|
291
|
-
include Comparable
|
|
292
|
-
|
|
293
|
-
# The initialize method sets up the instance name by converting the
|
|
294
|
-
# input to a string representation.
|
|
295
|
-
#
|
|
296
|
-
# This method handles different input types by converting them to a
|
|
297
|
-
# string, prioritizing to_str over to_sym and falling back to to_s if
|
|
298
|
-
# neither is available.
|
|
299
|
-
#
|
|
300
|
-
# @param name [ Object ] the input name to be converted to a string
|
|
301
|
-
def initialize(name)
|
|
302
|
-
@name =
|
|
303
|
-
case
|
|
304
|
-
when name.respond_to?(:to_str)
|
|
305
|
-
name.to_str
|
|
306
|
-
when name.respond_to?(:to_sym)
|
|
307
|
-
name.to_sym.to_s
|
|
308
|
-
else
|
|
309
|
-
name.to_s
|
|
310
|
-
end
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
# The name reader method returns the value of the name instance
|
|
314
|
-
# variable.
|
|
315
|
-
#
|
|
316
|
-
# @return [ String] the value stored in the name instance variable
|
|
317
|
-
attr_reader :name
|
|
318
|
-
|
|
319
|
-
# The description reader method provides access to the description
|
|
320
|
-
# attribute.
|
|
321
|
-
#
|
|
322
|
-
# @return [ String, nil ] the description value or nil if not set
|
|
323
|
-
attr_reader :description
|
|
324
|
-
|
|
325
|
-
alias to_str description
|
|
326
|
-
|
|
327
|
-
alias inspect description
|
|
328
|
-
|
|
329
|
-
alias to_s description
|
|
330
|
-
|
|
331
|
-
# The == method assigns a new name value to the instance variable.
|
|
332
|
-
#
|
|
333
|
-
# @param name [ Object ] the name value to be assigned
|
|
334
|
-
#
|
|
335
|
-
# @return [ Object ] returns the assigned name value
|
|
336
|
-
def ==(name)
|
|
337
|
-
@name = name
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
alias eql? ==
|
|
341
|
-
|
|
342
|
-
# The hash method returns the hash value of the name attribute.
|
|
343
|
-
#
|
|
344
|
-
# @return [ Integer ] the hash value used for object identification
|
|
345
|
-
def hash
|
|
346
|
-
@name.hash
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
# The <=> method compares the names of two objects for sorting purposes.
|
|
350
|
-
#
|
|
351
|
-
# @param other [ Object ] the other object to compare against
|
|
352
|
-
#
|
|
353
|
-
# @return [ Integer ] -1 if this object's name is less than the other's,
|
|
354
|
-
# 0 if they are equal, or 1 if this object's name is greater than the other's
|
|
355
|
-
def <=>(other)
|
|
356
|
-
@name <=> other.name
|
|
357
|
-
end
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
# A wrapper class for Ruby method objects that provides enhanced
|
|
361
|
-
# introspection and display capabilities.
|
|
362
|
-
#
|
|
363
|
-
# This class extends WrapperBase to create specialized wrappers for Ruby
|
|
364
|
-
# method objects, offering detailed information about methods including
|
|
365
|
-
# their source location, arity, and owner. It facilitates interactive
|
|
366
|
-
# exploration of Ruby methods in environments like IRB by providing
|
|
367
|
-
# structured access to method metadata and enabling sorting and
|
|
368
|
-
# comparison operations based on method descriptions.
|
|
369
|
-
class MethodWrapper < WrapperBase
|
|
370
|
-
# The initialize method sets up a new instance with the specified
|
|
371
|
-
# object, method name, and module flag.
|
|
372
|
-
#
|
|
373
|
-
# This method creates and configures a new instance by storing the
|
|
374
|
-
# method object and its description, handling both instance methods and
|
|
375
|
-
# regular methods based on the module flag parameter.
|
|
376
|
-
#
|
|
377
|
-
# @param obj [ Object ] the object from which to retrieve the method
|
|
378
|
-
# @param name [ String ] the name of the method to retrieve
|
|
379
|
-
# @param modul [ TrueClass, FalseClass ] flag indicating whether to retrieve an instance method
|
|
380
|
-
def initialize(obj, name, modul)
|
|
381
|
-
super(name)
|
|
382
|
-
@wrapped_method = modul ? obj.instance_method(name) : obj.method(name)
|
|
383
|
-
@description = @wrapped_method.description(style: :namespace)
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
# The method reader returns the method object associated with the
|
|
387
|
-
# instance.
|
|
388
|
-
attr_reader :wrapped_method
|
|
389
|
-
|
|
390
|
-
# The owner method retrieves the owner of the method object.
|
|
391
|
-
#
|
|
392
|
-
# This method checks if the wrapped method object responds to the owner
|
|
393
|
-
# message and returns the owner if available, otherwise it returns nil.
|
|
394
|
-
#
|
|
395
|
-
# @return [ Object, nil ] the owner of the method or nil if not applicable
|
|
396
|
-
def owner
|
|
397
|
-
@wrapped_method.respond_to?(:owner) ? @wrapped_method.owner : nil
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
# The arity method returns the number of parameters expected by the method.
|
|
401
|
-
#
|
|
402
|
-
# @return [ Integer ] the number of required parameters for the method
|
|
403
|
-
def arity
|
|
404
|
-
@wrapped_method.arity
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
# The source_location method retrieves the file path and line number
|
|
408
|
-
# where the method is defined.
|
|
409
|
-
#
|
|
410
|
-
# This method accesses the underlying source location information for
|
|
411
|
-
# the method object, returning an array that contains the filename and
|
|
412
|
-
# line number of the method's definition.
|
|
413
|
-
#
|
|
414
|
-
# @return [ Array<String, Integer> ] an array containing the filename and line number
|
|
415
|
-
# where the method is defined, or nil if the location cannot be determined
|
|
416
|
-
def source_location
|
|
417
|
-
@wrapped_method.source_location
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
# The <=> method compares the descriptions of two objects for ordering
|
|
421
|
-
# purposes.
|
|
422
|
-
#
|
|
423
|
-
# @param other [ Object ] the other object to compare against
|
|
424
|
-
#
|
|
425
|
-
# @return [ Integer ] -1 if this object's description is less than the other's,
|
|
426
|
-
# 0 if they are equal, or 1 if this object's description is greater than the other's
|
|
427
|
-
def <=>(other)
|
|
428
|
-
@description <=> other.description
|
|
429
|
-
end
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
# A wrapper class for Ruby constant objects that provides enhanced
|
|
433
|
-
# introspection and display capabilities.
|
|
434
|
-
#
|
|
435
|
-
# This class extends WrapperBase to create specialized wrappers for Ruby
|
|
436
|
-
# constant objects, offering detailed information about constants
|
|
437
|
-
# including their names and associated classes. It facilitates
|
|
438
|
-
# interactive exploration of Ruby constants in environments like IRB by
|
|
439
|
-
# providing structured access to constant metadata and enabling sorting
|
|
440
|
-
# and comparison operations based on constant descriptions.
|
|
441
|
-
class ConstantWrapper < WrapperBase
|
|
442
|
-
# The initialize method sets up a new instance with the provided object
|
|
443
|
-
# and name.
|
|
444
|
-
#
|
|
445
|
-
# This method configures the instance by storing a reference to the
|
|
446
|
-
# object's class and creating a description string that combines the
|
|
447
|
-
# name with the class name.
|
|
448
|
-
#
|
|
449
|
-
# @param obj [ Object ] the object whose class will be referenced
|
|
450
|
-
# @param name [ String ] the name to be used in the description
|
|
451
|
-
#
|
|
452
|
-
# @return [ Utils::Patterns::Pattern ] a new pattern instance configured with the provided arguments
|
|
453
|
-
def initialize(obj, name)
|
|
454
|
-
super(name)
|
|
455
|
-
@klass = obj.class
|
|
456
|
-
@description = "#@name:#@klass"
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
# The klass reader method provides access to the class value stored in the instance.
|
|
460
|
-
#
|
|
461
|
-
# @return [ Object ] the class value
|
|
462
|
-
attr_reader :klass
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
# The irb_constants method retrieves and wraps all constants from a given
|
|
466
|
-
# module.
|
|
467
|
-
#
|
|
468
|
-
# This method collects all constants defined in the specified module,
|
|
469
|
-
# creates ConstantWrapper instances for each constant, and returns them
|
|
470
|
-
# sorted in ascending order.
|
|
471
|
-
#
|
|
472
|
-
# @param modul [ Object ] the module from which to retrieve constants
|
|
473
|
-
#
|
|
474
|
-
# @return [ Array<ConstantWrapper> ] an array of ConstantWrapper objects
|
|
475
|
-
# representing the constants in the module, sorted alphabetically
|
|
476
|
-
def irb_constants(modul = self)
|
|
477
|
-
modul.constants.map { |c| ConstantWrapper.new(modul.const_get(c), c) }.sort
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
# The irb_subclasses method retrieves and wraps subclass information for
|
|
481
|
-
# a given class.
|
|
482
|
-
#
|
|
483
|
-
# This method fetches the subclasses of the specified class and creates
|
|
484
|
-
# ConstantWrapper instances for each subclass, allowing them to be sorted
|
|
485
|
-
# and displayed in a structured format.
|
|
486
|
-
#
|
|
487
|
-
# @param klass [ Object ] the class object to retrieve subclasses from
|
|
488
|
-
#
|
|
489
|
-
# @return [ Array<ConstantWrapper> ] an array of ConstantWrapper objects
|
|
490
|
-
# representing the subclasses
|
|
491
|
-
def irb_subclasses(klass = self)
|
|
492
|
-
klass.subclasses.map { |c| ConstantWrapper.new(eval(c), c) }.sort
|
|
493
|
-
end
|
|
494
|
-
|
|
495
|
-
unless Object.const_defined?(:Infinity)
|
|
496
|
-
Infinity = 1.0 / 0 # I like to define the infinite.
|
|
497
|
-
end
|
|
498
|
-
|
|
499
|
-
# The capture_output method captures stdout and optionally stderr output
|
|
500
|
-
# during code execution.
|
|
501
|
-
#
|
|
502
|
-
# This method temporarily redirects standard output (and optionally
|
|
503
|
-
# standard error) to a temporary file, executes the provided block, and
|
|
504
|
-
# then returns the captured output as a string.
|
|
505
|
-
#
|
|
506
|
-
# @param with_stderr [ TrueClass, FalseClass ] whether to also capture standard error output
|
|
507
|
-
#
|
|
508
|
-
# @yield [ void ] the block of code to execute while capturing output
|
|
509
|
-
#
|
|
510
|
-
# @return [ String ] the captured output as a string
|
|
511
|
-
def capture_output(with_stderr = false)
|
|
512
|
-
require 'tempfile'
|
|
513
|
-
begin
|
|
514
|
-
old_stdout, $stdout = $stdout, Tempfile.new('irb')
|
|
515
|
-
if with_stderr
|
|
516
|
-
old_stderr, $stderr = $stderr, $stdout
|
|
517
|
-
end
|
|
518
|
-
yield
|
|
519
|
-
ensure
|
|
520
|
-
$stdout, temp = old_stdout, $stdout
|
|
521
|
-
with_stderr and $stderr = old_stderr
|
|
522
|
-
end
|
|
523
|
-
temp.rewind
|
|
524
|
-
temp.read
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
# Use pager on the output of the commands given in the block. The less
|
|
528
|
-
# method executes a block and outputs its result through the pager.
|
|
529
|
-
#
|
|
530
|
-
# This method runs the provided block in a controlled environment,
|
|
531
|
-
# captures its output, and streams that output through the system's
|
|
532
|
-
# configured pager for display.
|
|
533
|
-
#
|
|
534
|
-
# @param with_stderr [ TrueClass, FalseClass ] whether to include standard error in the capture
|
|
535
|
-
#
|
|
536
|
-
# @yield [ void ]
|
|
537
|
-
def less(with_stderr = false, &block)
|
|
538
|
-
IO.popen($pager, 'w') do |f|
|
|
539
|
-
f.write capture_output(with_stderr, &block)
|
|
540
|
-
f.close_write
|
|
541
|
-
end
|
|
542
|
-
nil
|
|
543
|
-
end
|
|
544
|
-
|
|
545
|
-
# The irb_time method measures the execution time of a block and outputs
|
|
546
|
-
# the duration to standard error.
|
|
547
|
-
#
|
|
548
|
-
# @param n [ Integer ] the number of times to execute the block, defaults
|
|
549
|
-
# to 1
|
|
550
|
-
#
|
|
551
|
-
# @yield [ block ] the block to be executed and timed
|
|
552
|
-
def irb_time(n = 1, &block)
|
|
553
|
-
s = Time.now
|
|
554
|
-
n.times(&block)
|
|
555
|
-
d = Time.now - s
|
|
556
|
-
ensure
|
|
557
|
-
d ||= Time.now - s
|
|
558
|
-
if n == 1
|
|
559
|
-
warn "Took %.3fs seconds." % d
|
|
560
|
-
else
|
|
561
|
-
warn "Took %.3fs seconds, %.3fs per call (avg)." % [ d, d / n ]
|
|
562
|
-
end
|
|
563
|
-
end
|
|
564
|
-
|
|
565
|
-
# The irb_time_result method executes a block n times while measuring
|
|
566
|
-
# execution time and returns the result of the last execution.
|
|
567
|
-
#
|
|
568
|
-
# @param n [ Integer ] the number of times to execute the block
|
|
569
|
-
#
|
|
570
|
-
# @yield [ i ]
|
|
571
|
-
#
|
|
572
|
-
# @return [ Object ] the result of the last block execution
|
|
573
|
-
def irb_time_result(n = 1)
|
|
574
|
-
r = nil
|
|
575
|
-
irb_time(n) { |i| r = yield(i) }
|
|
576
|
-
r
|
|
577
|
-
end
|
|
578
|
-
|
|
579
|
-
# The irb_time_watch method monitors and reports performance metrics over
|
|
580
|
-
# time.
|
|
581
|
-
#
|
|
582
|
-
# This method continuously measures the output of a provided block,
|
|
583
|
-
# calculating differences and rates of change between successive
|
|
584
|
-
# measurements. It tracks these metrics and displays them with timing
|
|
585
|
-
# information, useful for observing how values evolve during execution.
|
|
586
|
-
#
|
|
587
|
-
# @param duration [ Integer ] the time interval in seconds between
|
|
588
|
-
# measurements
|
|
589
|
-
#
|
|
590
|
-
# @yield [ i ] the block to be measured, receiving the iteration count as an argument
|
|
591
|
-
def irb_time_watch(duration = 1)
|
|
592
|
-
start = Time.now
|
|
593
|
-
pre = nil
|
|
594
|
-
avg = Hash.new
|
|
595
|
-
i = 0
|
|
596
|
-
fetch_next = -> cur do
|
|
597
|
-
pre = cur.map(&:to_f)
|
|
598
|
-
i += 1
|
|
599
|
-
sleep duration
|
|
600
|
-
end
|
|
601
|
-
loop do
|
|
602
|
-
cur = [ yield(i) ].flatten
|
|
603
|
-
unless pre
|
|
604
|
-
fetch_next.(cur)
|
|
605
|
-
redo
|
|
606
|
-
end
|
|
607
|
-
expired = Time.now - start
|
|
608
|
-
diffs = cur.zip(pre).map { |c, p| c - p }
|
|
609
|
-
rates = diffs.map { |d| d / duration }
|
|
610
|
-
durs = cur.zip(rates).each_with_index.map { |(c, r), i|
|
|
611
|
-
if r < 0
|
|
612
|
-
x = c.to_f / -r
|
|
613
|
-
a = avg[i].to_f
|
|
614
|
-
a -= a / 2
|
|
615
|
-
a += x / 2
|
|
616
|
-
d = Tins::Duration.new(a)
|
|
617
|
-
ds = d.to_s
|
|
618
|
-
ds.singleton_class { define_method(:to_f) { d.to_f } }
|
|
619
|
-
avg[i] = ds
|
|
620
|
-
end
|
|
621
|
-
avg[i]
|
|
622
|
-
}
|
|
623
|
-
warn "#{expired} #{cur.zip(diffs, rates, durs) * ' '} 𝝙 / per sec."
|
|
624
|
-
fetch_next.(cur)
|
|
625
|
-
sleep duration
|
|
626
|
-
end
|
|
627
|
-
end
|
|
628
|
-
|
|
629
|
-
# The irb_write method writes text to a file or executes a block to
|
|
630
|
-
# generate content for writing.
|
|
631
|
-
#
|
|
632
|
-
# This method provides a convenient way to write content to a file,
|
|
633
|
-
# either by passing the text directly or by executing a block that
|
|
634
|
-
# generates the content. It uses secure file writing to ensure safety.
|
|
635
|
-
#
|
|
636
|
-
# @param filename [ String ] the path to the file where content will be
|
|
637
|
-
# written
|
|
638
|
-
# @param text [ String, nil ] the text content to write to the file, or
|
|
639
|
-
# nil if using a block
|
|
640
|
-
#
|
|
641
|
-
# @yield [ ] a block that generates content to be written to the file
|
|
642
|
-
def irb_write(filename, text = nil, &block)
|
|
643
|
-
if text.nil? && block
|
|
644
|
-
File.secure_write filename, nil, 'wb', &block
|
|
645
|
-
else
|
|
646
|
-
File.secure_write filename, text, 'wb'
|
|
647
|
-
end
|
|
648
|
-
end
|
|
649
|
-
|
|
650
|
-
# The irb_read method reads the contents of a file either entirely or in
|
|
651
|
-
# chunks. When a block is provided, it reads the file in chunks of the
|
|
652
|
-
# specified size and yields each chunk to the block.
|
|
653
|
-
# If no block is given, it reads the entire file content at once and
|
|
654
|
-
# returns it as a string.
|
|
655
|
-
#
|
|
656
|
-
# @param filename [ String ] the path to the file to be read
|
|
657
|
-
# @param chunk_size [ Integer ] the size of each chunk to read when a
|
|
658
|
-
# block is provided
|
|
659
|
-
#
|
|
660
|
-
# @yield [ chunk ] yields each chunk of the file to the block
|
|
661
|
-
# @yieldparam chunk [ String ] a portion of the file content
|
|
662
|
-
#
|
|
663
|
-
# @return [ String, nil ] the entire file content if no block is given,
|
|
664
|
-
# otherwise nil
|
|
665
|
-
def irb_read(filename, chunk_size = 8_192)
|
|
666
|
-
if block_given?
|
|
667
|
-
File.open(filename) do |file|
|
|
668
|
-
until file.eof?
|
|
669
|
-
yield file.read(chunk_size)
|
|
670
|
-
end
|
|
671
|
-
end
|
|
672
|
-
nil
|
|
673
|
-
else
|
|
674
|
-
File.read filename
|
|
675
|
-
end
|
|
676
|
-
end
|
|
677
|
-
|
|
678
|
-
# The irb_load! method loads Ruby files by their names into the current
|
|
679
|
-
# environment through an interactive selection interface.
|
|
680
|
-
#
|
|
681
|
-
# This method takes a glob pattern and finds matching Ruby files, then
|
|
682
|
-
# presents an interactive search interface for selecting which file to load.
|
|
683
|
-
# It ensures that each file is loaded only once by tracking loaded files
|
|
684
|
-
# using their paths. The method outputs messages to standard error
|
|
685
|
-
# indicating which file has been successfully loaded.
|
|
686
|
-
#
|
|
687
|
-
# @param glob [String] the glob pattern to search for Ruby files (defaults to
|
|
688
|
-
# ENV['UTILS_IRB_LOAD_GLOB'] or 'lib/**/*.rb')
|
|
689
|
-
#
|
|
690
|
-
# @return [Boolean] true if a file was successfully loaded, false if no file
|
|
691
|
-
# was selected or loaded
|
|
692
|
-
#
|
|
693
|
-
# @example
|
|
694
|
-
# # Load a file interactively with default glob pattern
|
|
695
|
-
# irb_load!
|
|
696
|
-
#
|
|
697
|
-
# # Load files matching a custom pattern
|
|
698
|
-
# irb_load!('app/models/**/*.rb')
|
|
699
|
-
#
|
|
700
|
-
# # Set environment variable for default pattern
|
|
701
|
-
# ENV['UTILS_IRB_LOAD_GLOB'] = 'lib/**/*.rb'
|
|
702
|
-
# irb_load!
|
|
703
|
-
#
|
|
704
|
-
# @note This method uses fuzzy matching to help find files when typing
|
|
705
|
-
# partial names. It respects the terminal height to limit the number of
|
|
706
|
-
# displayed results.
|
|
707
|
-
#
|
|
708
|
-
# @see SearchUI for the interactive search interface implementation
|
|
709
|
-
# @see Amatch::PairDistance for the fuzzy matching algorithm
|
|
710
|
-
def irb_load!(glob = ENV.fetch('UTILS_IRB_LOAD_GLOB', 'lib/**/*.rb'))
|
|
711
|
-
files = Dir.glob(glob)
|
|
712
|
-
found = Search.new(
|
|
713
|
-
match: -> answer {
|
|
714
|
-
matcher = Amatch::PairDistance.new(answer.downcase)
|
|
715
|
-
matches = files.map { |n| [ n, -matcher.similar(n.downcase) ] }.
|
|
716
|
-
sort.select { _2 < 0 }.sort_by(&:last).map(&:first)
|
|
717
|
-
matches.empty? and matches = files
|
|
718
|
-
matches.first(Tins::Terminal.lines - 1)
|
|
719
|
-
},
|
|
720
|
-
query: -> _answer, matches, selector {
|
|
721
|
-
matches.each_with_index.
|
|
722
|
-
map { |m, i| i == selector ? "→ " + Search.on_blue(m) : " " + m } * ?\n
|
|
723
|
-
},
|
|
724
|
-
found: -> _answer, matches, selector {
|
|
725
|
-
matches[selector]
|
|
726
|
-
},
|
|
727
|
-
output: STDOUT
|
|
728
|
-
).start
|
|
729
|
-
found or return false
|
|
730
|
-
load found
|
|
731
|
-
end
|
|
732
|
-
|
|
733
|
-
# The irb_server method provides access to an IRB server instance for
|
|
734
|
-
# interactive Ruby sessions.
|
|
735
|
-
#
|
|
736
|
-
# This method ensures that a single IRB server instance is created and
|
|
737
|
-
# started for the current process, loading the configuration from
|
|
738
|
-
# standard paths and using the configured server URL.
|
|
739
|
-
#
|
|
740
|
-
# @return [ Utils::IRB::IRBServer ] the IRB server instance, initialized
|
|
741
|
-
# and started if not already running
|
|
742
|
-
def irb_server
|
|
743
|
-
unless @irb_server
|
|
744
|
-
config = Utils::ConfigFile.new.tap(&:configure_from_paths)
|
|
745
|
-
@irb_server = Utils::IRB::IRBServer.new(url: config.irb_server_url).start
|
|
746
|
-
end
|
|
747
|
-
@irb_server
|
|
748
|
-
end
|
|
749
|
-
|
|
750
|
-
# The irb_server_stop method sends a stop command to the IRB server
|
|
751
|
-
# client.
|
|
752
|
-
#
|
|
753
|
-
# This method accesses the IRB client instance and invokes the
|
|
754
|
-
# stop_server method on it, which gracefully shuts down the IRB server
|
|
755
|
-
# process.
|
|
756
|
-
#
|
|
757
|
-
# @return [ nil ] always returns nil after sending the stop command to
|
|
758
|
-
# the server
|
|
759
|
-
def irb_server_stop
|
|
760
|
-
irb_client.stop_server
|
|
761
|
-
end
|
|
762
|
-
|
|
763
|
-
# The irb_client method provides access to an IRB server client instance.
|
|
764
|
-
#
|
|
765
|
-
# This method creates and returns a new IRB server client by first
|
|
766
|
-
# loading the configuration from standard paths and then using the
|
|
767
|
-
# configured server URL
|
|
768
|
-
# to initialize the client.
|
|
769
|
-
#
|
|
770
|
-
# @return [ Utils::IRB::IRBServer ] a new IRB server client instance configured
|
|
771
|
-
# with the URL from the application's configuration
|
|
772
|
-
def irb_client
|
|
773
|
-
config = Utils::ConfigFile.new.tap(&:configure_from_paths)
|
|
774
|
-
Utils::IRB::IRBServer.new(url: config.irb_server_url)
|
|
775
|
-
end
|
|
776
|
-
|
|
777
|
-
# The ed method opens files for editing using the system editor.
|
|
778
|
-
#
|
|
779
|
-
# This method provides a convenient way to edit files by invoking the
|
|
780
|
-
# configured editor. When called without arguments, it edits the current
|
|
781
|
-
# object's representation. When called with file arguments, it edits those
|
|
782
|
-
# specific files.
|
|
783
|
-
#
|
|
784
|
-
# @param files [ Array ] an array of file paths to be edited
|
|
785
|
-
def ed(*files)
|
|
786
|
-
if files.empty?
|
|
787
|
-
$editor.full?(:edit, self)
|
|
788
|
-
else
|
|
789
|
-
$editor.full?(:edit, *files)
|
|
790
|
-
end
|
|
791
|
-
end
|
|
792
|
-
|
|
793
|
-
if defined?(ActiveRecord::Base)
|
|
794
|
-
$logger = Logger.new(STDERR)
|
|
795
|
-
# The irb_toggle_logging method toggles the logging configuration for
|
|
796
|
-
# ActiveRecord.
|
|
797
|
-
#
|
|
798
|
-
# This method manages the logger setting for ActiveRecord by switching
|
|
799
|
-
# between a custom logger and the previously configured logger. It
|
|
800
|
-
# returns true when switching to the custom logger, and false when
|
|
801
|
-
# reverting to the original logger.
|
|
802
|
-
#
|
|
803
|
-
# @return [ TrueClass, FalseClass ] true if the logger was switched to
|
|
804
|
-
# the custom logger, false if it was reverted to the original logger
|
|
805
|
-
def irb_toggle_logging
|
|
806
|
-
require 'logger'
|
|
807
|
-
if ActiveRecord::Base.logger != $logger
|
|
808
|
-
$old_logger = ActiveRecord::Base.logger
|
|
809
|
-
ActiveRecord::Base.logger = $logger
|
|
810
|
-
true
|
|
811
|
-
else
|
|
812
|
-
ActiveRecord::Base.logger = $old_logger
|
|
813
|
-
false
|
|
814
|
-
end
|
|
815
|
-
end
|
|
816
|
-
end
|
|
817
|
-
end
|
|
818
|
-
|
|
819
|
-
# A module that extends Regexp functionality with additional pattern
|
|
820
|
-
# matching and display capabilities.
|
|
821
|
-
#
|
|
822
|
-
# Provides enhanced regexp operations including match highlighting and
|
|
823
|
-
# shell command integration.
|
|
824
|
-
#
|
|
825
|
-
# @example
|
|
826
|
-
# /pattern/ # => regular expression object
|
|
827
|
-
# /pattern/.show_match("text") # => highlighted text match
|
|
828
|
-
module Regexp
|
|
829
|
-
# The show_match method evaluates a string against the receiver pattern
|
|
830
|
-
# and highlights matching portions.
|
|
831
|
-
#
|
|
832
|
-
# This method tests whether the provided string matches the pattern
|
|
833
|
-
# represented by the receiver. When a match is found, it applies the
|
|
834
|
-
# success proc to highlight the matched portion of the string. If no
|
|
835
|
-
# match is found, it applies the failure proc to indicate that no match
|
|
836
|
-
# was found.
|
|
837
|
-
#
|
|
838
|
-
# @param string [ String ] the string to be tested against the pattern
|
|
839
|
-
# @param success [ Proc ] a proc that processes the matched portion of the string
|
|
840
|
-
# @param failure [ Proc ] a proc that processes the "no match" indication
|
|
841
|
-
#
|
|
842
|
-
# @return [ String ] the formatted string with matched portions highlighted or a no match message
|
|
843
|
-
def show_match(
|
|
844
|
-
string,
|
|
845
|
-
success: -> s { Term::ANSIColor.green { s } },
|
|
846
|
-
failure: -> s { Term::ANSIColor.red { s } }
|
|
847
|
-
)
|
|
848
|
-
string =~ self ? "#{$`}#{success.($&)}#{$'}" : failure.("no match")
|
|
849
|
-
end
|
|
850
|
-
end
|
|
851
|
-
|
|
852
|
-
# A module that extends String with additional utility methods for shell
|
|
853
|
-
# command piping and file writing operations.
|
|
854
|
-
#
|
|
855
|
-
# Provides convenient methods for executing shell commands on string
|
|
856
|
-
# content and securely writing strings to files.
|
|
857
|
-
module String
|
|
858
|
-
# The | method executes a shell command and returns its output.
|
|
859
|
-
#
|
|
860
|
-
# This method takes a command string, pipes the current string to it via
|
|
861
|
-
# stdin, captures the command's stdout, and returns the resulting output
|
|
862
|
-
# as a string.
|
|
863
|
-
#
|
|
864
|
-
# @param cmd [ String ] the shell command to execute
|
|
865
|
-
#
|
|
866
|
-
# @return [ String ] the output of the executed command
|
|
867
|
-
def |(cmd)
|
|
868
|
-
IO.popen(cmd, 'w+') do |f|
|
|
869
|
-
f.write self
|
|
870
|
-
f.close_write
|
|
871
|
-
return f.read
|
|
872
|
-
end
|
|
873
|
-
end
|
|
874
|
-
|
|
875
|
-
# The >> method writes the string content to a file securely.
|
|
876
|
-
#
|
|
877
|
-
# This method takes a filename and uses File.secure_write to write the
|
|
878
|
-
# string's content to that file, ensuring secure file handling practices
|
|
879
|
-
# are followed.
|
|
880
|
-
#
|
|
881
|
-
# @param filename [ String ] the path to the file where the string content will be written
|
|
882
|
-
#
|
|
883
|
-
# @return [ Integer ] the number of bytes written to the file
|
|
884
|
-
def >>(filename)
|
|
885
|
-
File.secure_write(filename, self)
|
|
886
|
-
end
|
|
887
|
-
end
|
|
888
|
-
|
|
889
25
|
# The configure method sets up IRB configuration options.
|
|
890
26
|
#
|
|
891
27
|
# This method configures the IRB environment by setting the history save
|
|
@@ -907,6 +43,9 @@ module Utils
|
|
|
907
43
|
end
|
|
908
44
|
end
|
|
909
45
|
|
|
46
|
+
require 'utils/irb/shell'
|
|
47
|
+
require 'utils/irb/regexp'
|
|
48
|
+
require 'utils/irb/string'
|
|
910
49
|
require 'utils/irb/irb_server'
|
|
911
50
|
|
|
912
51
|
Utils::IRB.configure
|