toys-core 0.13.1 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -54,54 +54,88 @@ module Toys
54
54
  # options.
55
55
  #
56
56
  # Three general strategies are available for custom stream handling. First,
57
- # you may redirect to other streams such as files, IO objects, or Ruby
57
+ # you can redirect to other streams such as files, IO objects, or Ruby
58
58
  # strings. Some of these options map directly to options provided by the
59
- # `Process#spawn` method. Second, you may use a controller to manipulate
60
- # the streams programmatically. Third, you may capture output stream data
59
+ # `Process#spawn` method. Second, you can use a controller to manipulate
60
+ # the streams programmatically. Third, you can capture output stream data
61
61
  # and make it available in the result.
62
62
  #
63
63
  # Following is a full list of the stream handling options, along with how
64
64
  # to specify them using the `:in`, `:out`, and `:err` options.
65
65
  #
66
- # * **Inherit parent stream:** You may inherit the corresponding stream
66
+ # * **Inherit parent stream:** You can inherit the corresponding stream
67
67
  # in the parent process by passing `:inherit` as the option value. This
68
68
  # is the default if the subprocess is *not* run in the background.
69
- # * **Redirect to null:** You may redirect to a null stream by passing
69
+ #
70
+ # * **Redirect to null:** You can redirect to a null stream by passing
70
71
  # `:null` as the option value. This connects to a stream that is not
71
72
  # closed but contains no data, i.e. `/dev/null` on unix systems. This
72
73
  # is the default if the subprocess is run in the background.
73
- # * **Close the stream:** You may close the stream by passing `:close` as
74
+ #
75
+ # * **Close the stream:** You can close the stream by passing `:close` as
74
76
  # the option value. This is the same as passing `:close` to
75
77
  # `Process#spawn`.
76
- # * **Redirect to a file:** You may redirect to a file. This reads from
78
+ #
79
+ # * **Redirect to a file:** You can redirect to a file. This reads from
77
80
  # an existing file when connected to `:in`, and creates or appends to a
78
81
  # file when connected to `:out` or `:err`. To specify a file, use the
79
- # setting `[:file, "/path/to/file"]`. You may also, when writing a
82
+ # setting `[:file, "/path/to/file"]`. You can also, when writing a
80
83
  # file, append an optional mode and permission code to the array. For
81
84
  # example, `[:file, "/path/to/file", "a", 0644]`.
82
- # * **Redirect to an IO object:** You may redirect to an IO object in the
83
- # parent process, by passing the IO object as the option value. You may
85
+ #
86
+ # * **Redirect to an IO object:** You can redirect to an IO object in the
87
+ # parent process, by passing the IO object as the option value. You can
84
88
  # use any IO object. For example, you could connect the child's output
85
89
  # to the parent's error using `out: $stderr`, or you could connect to
86
90
  # an existing File stream. Unlike `Process#spawn`, this works for IO
87
91
  # objects that do not have a corresponding file descriptor (such as
88
92
  # StringIO objects). In such a case, a thread will be spawned to pipe
89
93
  # the IO data through to the child process.
90
- # * **Combine with another child stream:** You may redirect one child
94
+ #
95
+ # * **Redirect to a pipe:** You can redirect to a pipe created using
96
+ # `IO.pipe` (i.e. a two-element array of read and write IO objects) by
97
+ # passing the array as the option value. This will connect the
98
+ # appropriate IO (either read or write), and close it in the parent.
99
+ # Thus, you can connect only one process to each end. If you want more
100
+ # direct control over IO closing behavior, pass the IO object (i.e. the
101
+ # element of the pipe array) directly.
102
+ #
103
+ # * **Combine with another child stream:** You can redirect one child
91
104
  # output stream to another, to combine them. To merge the child's error
92
105
  # stream into its output stream, use `err: [:child, :out]`.
93
- # * **Read from a string:** You may pass a string to the input stream by
106
+ #
107
+ # * **Read from a string:** You can pass a string to the input stream by
94
108
  # setting `[:string, "the string"]`. This works only for `:in`.
95
- # * **Capture output stream:** You may capture a stream and make it
109
+ #
110
+ # * **Capture output stream:** You can capture a stream and make it
96
111
  # available on the {Toys::Utils::Exec::Result} object, using the
97
112
  # setting `:capture`. This works only for the `:out` and `:err`
98
113
  # streams.
99
- # * **Use the controller:** You may hook a stream to the controller using
114
+ #
115
+ # * **Use the controller:** You can hook a stream to the controller using
100
116
  # the setting `:controller`. You can then manipulate the stream via the
101
117
  # controller. If you pass a block to {Toys::Utils::Exec#exec}, it
102
118
  # yields the {Toys::Utils::Exec::Controller}, giving you access to
103
119
  # streams.
104
120
  #
121
+ # * **Make copies of an output stream:** You can "tee," or duplicate the
122
+ # `:out` or `:err` stream and redirect those copies to various
123
+ # destinations. To specify a tee, use the setting `[:tee, ...]` where
124
+ # the additional array elements include two or more of the following.
125
+ # See the corresponding documentation above for more detail.
126
+ # * `:inherit` to direct to the parent process's stream.
127
+ # * `:capture` to capture the stream and store it in the result.
128
+ # * `:controller` to direct the stream to the controller.
129
+ # * `[:file, "/path/to/file"]` to write to a file.
130
+ # * An `IO` or `StringIO` object.
131
+ # * An array of two `IO` objects representing a pipe
132
+ #
133
+ # Additionally, the last element of the array can be a hash of options.
134
+ # Supported options include:
135
+ # * `:buffer_size` The size of the memory buffer for each element of
136
+ # the tee. Larger buffers may allow higher throughput. The default
137
+ # is 65536.
138
+ #
105
139
  # ### Result handling
106
140
  #
107
141
  # A subprocess result is represented by a {Toys::Utils::Exec::Result}
@@ -171,7 +205,7 @@ module Toys
171
205
  # not present, the command is not logged.
172
206
  #
173
207
  # * `:log_level` (Integer,false) Level for logging the actual command.
174
- # Defaults to Logger::INFO if not present. You may also pass `false` to
208
+ # Defaults to Logger::INFO if not present. You can also pass `false` to
175
209
  # disable logging of the command.
176
210
  #
177
211
  # * `:log_cmd` (String) The string logged for the actual command.
@@ -227,7 +261,7 @@ module Toys
227
261
  end
228
262
 
229
263
  ##
230
- # Execute a command. The command may be given as a single string to pass
264
+ # Execute a command. The command can be given as a single string to pass
231
265
  # to a shell, or an array of strings indicating a posix command.
232
266
  #
233
267
  # If the process is not set to run in the background, and a block is
@@ -310,7 +344,7 @@ module Toys
310
344
  end
311
345
 
312
346
  ##
313
- # Execute a command. The command may be given as a single string to pass
347
+ # Execute a command. The command can be given as a single string to pass
314
348
  # to a shell, or an array of strings indicating a posix command.
315
349
  #
316
350
  # Captures standard out and returns it as a string.
@@ -400,7 +434,7 @@ module Toys
400
434
  # An object that controls a subprocess. This object is returned from an
401
435
  # execution running in the background, or is yielded to a control block
402
436
  # for an execution running in the foreground.
403
- # You may use this object to interact with the subcommand's streams,
437
+ # You can use this object to interact with the subcommand's streams,
404
438
  # send signals to the process, and get its result.
405
439
  #
406
440
  class Controller
@@ -502,8 +536,8 @@ module Toys
502
536
  ##
503
537
  # Redirects the remainder of the given stream.
504
538
  #
505
- # You may specify the stream as an IO or IO-like object, or as a file
506
- # specified by its path. If specifying a file, you may optionally
539
+ # You can specify the stream as an IO or IO-like object, or as a file
540
+ # specified by its path. If specifying a file, you can optionally
507
541
  # provide the mode and permissions for the call to `File#open`. You can
508
542
  # also specify the value `:null` to indicate the null file.
509
543
  #
@@ -540,8 +574,8 @@ module Toys
540
574
  ##
541
575
  # Redirects the remainder of the standard input stream.
542
576
  #
543
- # You may specify the stream as an IO or IO-like object, or as a file
544
- # specified by its path. If specifying a file, you may optionally
577
+ # You can specify the stream as an IO or IO-like object, or as a file
578
+ # specified by its path. If specifying a file, you can optionally
545
579
  # provide the mode and permissions for the call to `File#open`. You can
546
580
  # also specify the value `:null` to indicate the null file.
547
581
  #
@@ -559,8 +593,8 @@ module Toys
559
593
  ##
560
594
  # Redirects the remainder of the standard output stream.
561
595
  #
562
- # You may specify the stream as an IO or IO-like object, or as a file
563
- # specified by its path. If specifying a file, you may optionally
596
+ # You can specify the stream as an IO or IO-like object, or as a file
597
+ # specified by its path. If specifying a file, you can optionally
564
598
  # provide the mode and permissions for the call to `File#open`. You can
565
599
  # also specify the value `:null` to indicate the null file.
566
600
  #
@@ -578,8 +612,8 @@ module Toys
578
612
  ##
579
613
  # Redirects the remainder of the standard error stream.
580
614
  #
581
- # You may specify the stream as an IO or IO-like object, or as a file
582
- # specified by its path. If specifying a file, you may optionally
615
+ # You can specify the stream as an IO or IO-like object, or as a file
616
+ # specified by its path. If specifying a file, you can optionally
583
617
  # provide the mode and permissions for the call to `File#open`.
584
618
  #
585
619
  # After calling this, do not interact directly with the stream.
@@ -594,7 +628,7 @@ module Toys
594
628
  end
595
629
 
596
630
  ##
597
- # Send the given signal to the process. The signal may be specified
631
+ # Send the given signal to the process. The signal can be specified
598
632
  # by name or number.
599
633
  #
600
634
  # @param sig [Integer,String] The signal to send.
@@ -1091,7 +1125,7 @@ module Toys
1091
1125
  when :close
1092
1126
  :close
1093
1127
  else
1094
- stream if stream.respond_to?(:write)
1128
+ stream if stream.respond_to?(:read)
1095
1129
  end
1096
1130
  if in_stream == :close
1097
1131
  stdstream.close
@@ -1163,16 +1197,23 @@ module Toys
1163
1197
  end
1164
1198
 
1165
1199
  def interpret_in_array(setting)
1166
- case setting.first
1167
- when ::Symbol
1200
+ if setting.first.is_a?(::Symbol)
1168
1201
  setup_in_stream_of_type(setting.first, setting[1..-1])
1169
- when ::String
1202
+ elsif setting.first.is_a?(::String)
1170
1203
  setup_in_stream_of_type(:file, setting)
1204
+ elsif setting.size == 2 && setting.first.is_a?(::IO) && setting.last.is_a?(::IO)
1205
+ interpret_in_pipe(*setting)
1171
1206
  else
1172
1207
  raise "Unknown value for in: #{setting.inspect}"
1173
1208
  end
1174
1209
  end
1175
1210
 
1211
+ def interpret_in_pipe(reader, writer)
1212
+ @spawn_opts[:in] = reader
1213
+ @child_streams << reader
1214
+ @parent_streams << writer
1215
+ end
1216
+
1176
1217
  def setup_in_stream_of_type(type, args)
1177
1218
  case type
1178
1219
  when :controller
@@ -1199,7 +1240,7 @@ module Toys
1199
1240
  end
1200
1241
 
1201
1242
  def interpret_in_file(args)
1202
- raise "Expected only file name" unless args.size == 1 && args.first.is_a?(::String)
1243
+ raise "Expected only file name for in" unless args.size == 1 && args.first.is_a?(::String)
1203
1244
  @spawn_opts[:in] = args + [::File::RDONLY]
1204
1245
  end
1205
1246
 
@@ -1230,16 +1271,23 @@ module Toys
1230
1271
  end
1231
1272
 
1232
1273
  def interpret_out_array(key, setting)
1233
- case setting.first
1234
- when ::Symbol
1274
+ if setting.first.is_a?(::Symbol)
1235
1275
  setup_out_stream_of_type(key, setting.first, setting[1..-1])
1236
- when ::String
1276
+ elsif setting.first.is_a?(::String)
1237
1277
  setup_out_stream_of_type(key, :file, setting)
1278
+ elsif setting.size == 2 && setting.first.is_a?(::IO) && setting.last.is_a?(::IO)
1279
+ interpret_out_pipe(key, *setting)
1238
1280
  else
1239
1281
  raise "Unknown value for #{key}: #{setting.inspect}"
1240
1282
  end
1241
1283
  end
1242
1284
 
1285
+ def interpret_out_pipe(key, reader, writer)
1286
+ @spawn_opts[key] = writer
1287
+ @child_streams << writer
1288
+ @parent_streams << reader
1289
+ end
1290
+
1243
1291
  def setup_out_stream_of_type(key, type, args)
1244
1292
  case type
1245
1293
  when :controller
@@ -1260,17 +1308,150 @@ module Toys
1260
1308
  copy_from_out_thread(key, args.first)
1261
1309
  when :file
1262
1310
  interpret_out_file(key, args)
1311
+ when :tee
1312
+ interpret_out_tee(key, args)
1263
1313
  else
1264
1314
  raise "Unknown type for #{key}: #{type.inspect}"
1265
1315
  end
1266
1316
  end
1267
1317
 
1268
1318
  def interpret_out_file(key, args)
1269
- raise "Expected file name" if args.empty? || !args.first.is_a?(::String)
1270
- raise "Too many file arguments" if args.size > 3
1319
+ raise "Expected file name for #{key}" if args.empty? || !args.first.is_a?(::String)
1320
+ raise "Too many file arguments for #{key}" if args.size > 3
1271
1321
  @spawn_opts[key] = args.size == 1 ? args.first : args
1272
1322
  end
1273
1323
 
1324
+ def interpret_out_tee(key, args)
1325
+ opts = args.last.is_a?(::Hash) ? args.pop : {}
1326
+ reader = make_out_pipe(key)
1327
+ sinks = interpret_out_tee_arguments(key, args)
1328
+ tee_runner(key, reader, sinks, opts[:buffer_size] || 65_536)
1329
+ end
1330
+
1331
+ def interpret_out_tee_arguments(key, args)
1332
+ args.map do |arg|
1333
+ case arg
1334
+ when :inherit
1335
+ [key == :err ? $stderr : $stdout, nil]
1336
+ when :capture
1337
+ [::StringIO.new, :capture]
1338
+ when :controller
1339
+ tee_sink_for_controller(key)
1340
+ when ::IO, ::StringIO
1341
+ [arg, nil]
1342
+ when ::String
1343
+ [::File.open(arg, "w"), :close]
1344
+ when ::Array
1345
+ tee_sink_for_array(key, arg)
1346
+ else
1347
+ raise "Unknown value for #{key} tee argument: #{arg.inspect}"
1348
+ end
1349
+ end
1350
+ end
1351
+
1352
+ def tee_sink_for_controller(key)
1353
+ @controller_streams[key], writer = ::IO.pipe
1354
+ writer.sync = true
1355
+ [writer, :close]
1356
+ end
1357
+
1358
+ def tee_sink_for_array(key, arg)
1359
+ if arg.size == 2 &&
1360
+ arg.last.is_a?(::IO) &&
1361
+ (arg.first == :autoclose || arg.first.is_a?(::IO))
1362
+ [arg.last, :close]
1363
+ else
1364
+ arg = arg[1..-1] if arg.first == :file
1365
+ if arg.empty? || !arg.first.is_a?(::String)
1366
+ raise "Expected file name for #{key} tee argument"
1367
+ end
1368
+ raise "Too many file arguments for #{key} tee argument" if arg.size > 3
1369
+ arg += ["w"] if arg.size == 1
1370
+ [::File.open(*arg), :close]
1371
+ end
1372
+ end
1373
+
1374
+ def tee_runner(key, reader, sinks, buffer_size)
1375
+ @join_threads << ::Thread.new do
1376
+ sinks.map! { |io, on_done| [io, ::String.new, :write_nonblock, on_done] }
1377
+ until sinks.empty?
1378
+ tee_wait_for_streams(reader, sinks)
1379
+ reader = tee_read_stream(reader, sinks, buffer_size)
1380
+ tee_write_streams(sinks, key, reader.nil?)
1381
+ end
1382
+ end
1383
+ end
1384
+
1385
+ def tee_wait_for_streams(reader, sinks)
1386
+ read_select = reader && [reader]
1387
+ write_select = []
1388
+ sinks.each do |io, buffer, _write_method, _on_done|
1389
+ write_select << io unless buffer.empty?
1390
+ end
1391
+ ::IO.select(read_select, write_select)
1392
+ end
1393
+
1394
+ def tee_read_stream(reader, sinks, buffer_size)
1395
+ return nil if reader.nil?
1396
+ max = tee_amount_to_read(sinks, buffer_size)
1397
+ return reader unless max.positive?
1398
+ begin
1399
+ data = reader.read_nonblock(max)
1400
+ unless data.empty?
1401
+ sinks.each { |_io, buffer, _write_method, _on_done| buffer << data }
1402
+ end
1403
+ reader
1404
+ rescue ::IO::WaitReadable
1405
+ reader
1406
+ rescue ::StandardError
1407
+ reader.close rescue nil # rubocop:disable Style/RescueModifier
1408
+ nil
1409
+ end
1410
+ end
1411
+
1412
+ def tee_write_streams(sinks, key, read_complete)
1413
+ sinks.delete_if do |sink|
1414
+ io, buffer, write_method, on_done = sink
1415
+ done, write_method = tee_write_one_stream(io, buffer, write_method, read_complete)
1416
+ sink[2] = write_method
1417
+ if done
1418
+ case on_done
1419
+ when :close
1420
+ io.close rescue nil # rubocop:disable Style/RescueModifier
1421
+ when :capture
1422
+ @mutex.synchronize do
1423
+ @captures[key] = io.string
1424
+ end
1425
+ end
1426
+ end
1427
+ done
1428
+ end
1429
+ end
1430
+
1431
+ def tee_write_one_stream(io, buffer, write_method, read_complete)
1432
+ return [read_complete, write_method] if buffer.empty?
1433
+ begin
1434
+ bytes = io.send(write_method, buffer)
1435
+ buffer.slice!(0, bytes)
1436
+ [false, write_method]
1437
+ rescue ::IO::WaitWritable, ::Errno::EINTR
1438
+ [false, write_method]
1439
+ rescue ::Errno::EBADF, ::NoMethodError
1440
+ raise if write_method == :write
1441
+ [false, :write]
1442
+ rescue ::StandardError
1443
+ [true, write_method]
1444
+ end
1445
+ end
1446
+
1447
+ def tee_amount_to_read(sink_info, buffer_size)
1448
+ maxbuff = 0
1449
+ sink_info.each do |_sink, buffer, _meth|
1450
+ maxbuff = buffer.size if buffer.size > maxbuff
1451
+ end
1452
+ buffer_size - maxbuff
1453
+ end
1454
+
1274
1455
  def make_null_stream(key, mode)
1275
1456
  f = ::File.open(::File::NULL, mode)
1276
1457
  @spawn_opts[key] = f
@@ -171,7 +171,10 @@ module Toys
171
171
  ([@tool] + @delegates).each do |tool|
172
172
  name_len = tool.full_name.length
173
173
  subtools = @loader.list_subtools(tool.full_name,
174
- recursive: recursive, include_hidden: include_hidden)
174
+ recursive: recursive,
175
+ include_hidden: include_hidden,
176
+ include_namespaces: include_hidden,
177
+ include_non_runnable: include_hidden)
175
178
  subtools.each do |subtool|
176
179
  local_name = subtool.full_name.slice(name_len..-1).join(" ")
177
180
  subtools_by_name[local_name] = subtool
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "toys/utils/exec"
4
+
5
+ module Toys
6
+ module Utils
7
+ ##
8
+ # A class that invokes an external pager.
9
+ #
10
+ # @example Using a pager for regular output
11
+ #
12
+ # Toys::Utils::Pager.start do |io|
13
+ # io.puts "A long string\n"
14
+ # end
15
+ #
16
+ # @example Piping output from a command
17
+ #
18
+ # exec_service = Toys::Utils::Exec.new
19
+ # Toys::Utils::Pager.start(exec_service: exec_service) do |io|
20
+ # exec_service.exec(["/bin/ls", "-alF"], out: io)
21
+ # end
22
+ #
23
+ class Pager
24
+ ##
25
+ # Creates a new pager.
26
+ #
27
+ # @param command [String,Array<String>,boolean] The command to use to
28
+ # invoke the pager. May be specified as a string to be passed to the
29
+ # shell, an array of strings representing a posix command, the value
30
+ # `true` to use the default (normally `less -FIRX`), or the value
31
+ # `false` to disable the pager and write directly to the output
32
+ # stream. Default is `true`.
33
+ # @param exec_service [Toys::Utils::Exec] The service to use for
34
+ # executing commands, or `nil` (the default) to use a default.
35
+ # @param fallback_io [IO] An IO-like object to write to if the pager is
36
+ # disabled. Defaults to `$stdout`.
37
+ #
38
+ def initialize(command: true, exec_service: nil, fallback_io: nil)
39
+ @command = command == true ? Pager.default_command : command
40
+ @command ||= nil
41
+ @exec_service = exec_service || Pager.default_exec_service
42
+ @fallback_io = fallback_io || $stdout
43
+ end
44
+
45
+ ##
46
+ # Runs the pager. Takes a block and yields an IO-like object that passes
47
+ # text to the pager. Can be called multiple times on the same pager.
48
+ #
49
+ # @yieldparam io [IO] An object that can be written to, to pass data to
50
+ # the pager.
51
+ # @return [Integer] The exit code of the pager process.
52
+ #
53
+ # @example
54
+ #
55
+ # pager = Toys::Utils::Pager.new
56
+ # pager.start do |io|
57
+ # io.puts "A long string\n"
58
+ # end
59
+ #
60
+ def start
61
+ if @command
62
+ result = @exec_service.exec(@command, in: :controller) do |controller|
63
+ yield controller.in if controller.pid
64
+ end
65
+ return result.exit_code unless result.failed?
66
+ end
67
+ yield @fallback_io
68
+ 0
69
+ end
70
+
71
+ ##
72
+ # The command for running the pager process. May be specified as a string
73
+ # to be passed to the shell, an array of strings representing a posix
74
+ # command, or `nil` to disable the pager and write directly to an output
75
+ # stream.
76
+ #
77
+ # @return [String,Array<String>,nil]
78
+ #
79
+ attr_accessor :command
80
+
81
+ ##
82
+ # The IO stream used if the pager is disabled or could not be executed.
83
+ #
84
+ # @return [IO]
85
+ #
86
+ attr_accessor :fallback_io
87
+
88
+ class << self
89
+ ##
90
+ # A convenience method that creates a pager and runs it once by calling
91
+ # {Pager#start}.
92
+ #
93
+ # @param command [String,Array<String>,boolean] The command to use to
94
+ # invoke the pager. May be specified as a string to be passed to the
95
+ # shell, an array of strings representing a posix command, the value
96
+ # `true` to use the default (normally `less -FIRX`), or the value
97
+ # `false` to disable the pager and write directly to the output
98
+ # stream. Default is `true`.
99
+ # @param exec_service [Toys::Utils::Exec] The service to use for
100
+ # executing commands, or `nil` (the default) to use a default.
101
+ # @param fallback_io [IO] An IO-like object to write to if the pager is
102
+ # disabled. Defaults to `$stdout`.
103
+ # @return [Integer] The exit code of the pager process.
104
+ #
105
+ # @example
106
+ #
107
+ # Toys::Utils::Pager.start do |io|
108
+ # io.puts "A long string\n"
109
+ # end
110
+ def start(command: true, exec_service: nil, fallback_io: nil, &block)
111
+ pager = new(command: command, exec_service: exec_service, fallback_io: fallback_io)
112
+ pager.start(&block)
113
+ end
114
+
115
+ ##
116
+ # @private
117
+ #
118
+ def default_command
119
+ unless defined? @default_command
120
+ @default_command = ::ENV["PAGER"]
121
+ unless @default_command
122
+ path = `which less`.strip
123
+ @default_command = [path, "-FIRX"] unless path.empty?
124
+ end
125
+ end
126
+ @default_command
127
+ end
128
+
129
+ ##
130
+ # @private
131
+ #
132
+ def default_exec_service
133
+ @default_exec_service ||= Exec.new
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -4,9 +4,19 @@ module Toys
4
4
  ##
5
5
  # A string intended for word-wrapped display.
6
6
  #
7
+ # A WrappableString is an array of string "fragments" representing the atomic
8
+ # units that should not be split when word-wrapping. It should be possible to
9
+ # reconstruct the original string by joining these fragments with whitespace.
10
+ #
7
11
  class WrappableString
8
12
  ##
9
13
  # Create a wrapped string.
14
+ #
15
+ # You can pass either:
16
+ #
17
+ # * A single String, which will be split into fragments by whitespace.
18
+ # * An array of Strings representing the fragments explicitly.
19
+ #
10
20
  # @param string [String,Array<String>] The string or array of string
11
21
  # fragments
12
22
  #
@@ -35,6 +45,7 @@ module Toys
35
45
 
36
46
  ##
37
47
  # Returns true if the string is empty (i.e. has no fragments)
48
+ #
38
49
  # @return [Boolean]
39
50
  #
40
51
  def empty?
@@ -43,6 +54,7 @@ module Toys
43
54
 
44
55
  ##
45
56
  # Returns the string without any wrapping
57
+ #
46
58
  # @return [String]
47
59
  #
48
60
  def string
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toys-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-01 00:00:00.000000000 Z
11
+ date: 2022-10-03 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Toys-Core is the command line tool framework underlying Toys. It can
14
14
  be used to create command line executables using the Toys DSL and classes.
@@ -60,6 +60,7 @@ files:
60
60
  - lib/toys/standard_mixins/gems.rb
61
61
  - lib/toys/standard_mixins/git_cache.rb
62
62
  - lib/toys/standard_mixins/highline.rb
63
+ - lib/toys/standard_mixins/pager.rb
63
64
  - lib/toys/standard_mixins/terminal.rb
64
65
  - lib/toys/standard_mixins/xdg.rb
65
66
  - lib/toys/template.rb
@@ -69,6 +70,7 @@ files:
69
70
  - lib/toys/utils/gems.rb
70
71
  - lib/toys/utils/git_cache.rb
71
72
  - lib/toys/utils/help_text.rb
73
+ - lib/toys/utils/pager.rb
72
74
  - lib/toys/utils/terminal.rb
73
75
  - lib/toys/utils/xdg.rb
74
76
  - lib/toys/wrappable_string.rb
@@ -76,10 +78,10 @@ homepage: https://github.com/dazuma/toys
76
78
  licenses:
77
79
  - MIT
78
80
  metadata:
79
- changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.13.1/file.CHANGELOG.html
81
+ changelog_uri: https://dazuma.github.io/toys/gems/toys-core/v0.14.1/file.CHANGELOG.html
80
82
  source_code_uri: https://github.com/dazuma/toys/tree/main/toys-core
81
83
  bug_tracker_uri: https://github.com/dazuma/toys/issues
82
- documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.13.1
84
+ documentation_uri: https://dazuma.github.io/toys/gems/toys-core/v0.14.1
83
85
  post_install_message:
84
86
  rdoc_options: []
85
87
  require_paths: