toys-core 0.14.7 → 0.15.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.
data/lib/toys/dsl/tool.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Toys
4
4
  module DSL
5
5
  ##
6
- # This class defines the DSL for a Toys configuration file.
6
+ # This module defines the DSL for a Toys configuration file.
7
7
  #
8
8
  # A Toys configuration defines one or more named tools. It provides syntax
9
9
  # for setting the description, defining flags and arguments, specifying
@@ -25,6 +25,9 @@ module Toys
25
25
  # end
26
26
  # end
27
27
  #
28
+ # The DSL directives `tool`, `desc`, `optional_arg`, and others are defined
29
+ # in this module.
30
+ #
28
31
  # Now you can execute it using:
29
32
  #
30
33
  # toys greet
@@ -81,8 +84,8 @@ module Toys
81
84
  # ### Example
82
85
  #
83
86
  # The following example creates an acceptor named "hex" that is defined
84
- # via a regular expression. It then uses it to validate values passed to
85
- # a flag.
87
+ # via a regular expression. It uses the acceptor to validate values
88
+ # passed to a flag.
86
89
  #
87
90
  # tool "example" do
88
91
  # acceptor "hex", /[0-9a-fA-F]+/, type_desc: "hex numbers"
@@ -155,16 +158,21 @@ module Toys
155
158
  #
156
159
  # A template is an object that generates DSL directives. You can use it
157
160
  # to build "prefabricated" tools, and then instantiate them in your Toys
158
- # files. Generally, a template is a class with an associated `expansion`
159
- # procedure. The class defines parameters for the template expansion,
160
- # and `expansion` includes DSL directives that should be run based on
161
- # those parameters.
162
- #
163
- # Normally, you provide a block and define the template class in that
164
- # block. Most templates will define an `initialize` method that takes any
165
- # arguments passed into the template expansion. The template must also
166
- # provide an `expansion` block showing how to use the template object to
167
- # produce DSL directives.
161
+ # files.
162
+ #
163
+ # A template is an object that defines an `expansion` procedure. This
164
+ # procedure generates the DSL directives implemented by the template. The
165
+ # template object typically also includes attributes that are used to
166
+ # configure the expansion.
167
+ #
168
+ # The simplest way to define a template is to pass a block to the
169
+ # {#template} directive. In the block, define an `initialize` method that
170
+ # accepts any arguments that may be passed to the template when it is
171
+ # instantiated and are used to configure the template. Define
172
+ # `attr_reader`s or other methods to make this configuration accessible
173
+ # from the object. Then define an `on_expand` block that implements the
174
+ # template's expansion. The template object is passed as an object to the
175
+ # `on_expand` block.
168
176
  #
169
177
  # Alternately, you can create a template class separately and pass it
170
178
  # directly. See {Toys::Template} for details on creating a template
@@ -172,7 +180,9 @@ module Toys
172
180
  #
173
181
  # ### Example
174
182
  #
175
- # The following example creates and uses a simple template.
183
+ # The following example creates and uses a simple template. The template
184
+ # defines a tool, with a configurable name, that simply prints out a
185
+ # configurable message.
176
186
  #
177
187
  # template "hello-generator" do
178
188
  # def initialize(name, message)
@@ -180,7 +190,7 @@ module Toys
180
190
  # @message = message
181
191
  # end
182
192
  # attr_reader :name, :message
183
- # expansion do |template|
193
+ # on_expand do |template|
184
194
  # tool template.name do
185
195
  # to_run do
186
196
  # puts template.message
@@ -973,6 +983,12 @@ module Toys
973
983
  # arguments.) Defaults to the empty array.
974
984
  # @param display_name [String] A display name for this flag, used in help
975
985
  # text and error messages.
986
+ # @param add_method [true,false,nil] Whether to add a method for this
987
+ # flag. If omitted or set to nil, uses the default behavior, which
988
+ # adds the method if the key is a symbol representing a legal method
989
+ # name that starts with a letter and does not override any public
990
+ # method in the Ruby Object class or collide with any method directly
991
+ # defined in the tool class.
976
992
  # @param block [Proc] Configures the flag. See {Toys::DSL::Flag} for the
977
993
  # directives that can be called in this block.
978
994
  # @return [self]
@@ -981,17 +997,17 @@ module Toys
981
997
  accept: nil, default: nil, handler: nil,
982
998
  complete_flags: nil, complete_values: nil,
983
999
  report_collisions: true, group: nil,
984
- desc: nil, long_desc: nil, display_name: nil,
1000
+ desc: nil, long_desc: nil, display_name: nil, add_method: nil,
985
1001
  &block)
986
1002
  cur_tool = DSL::Internal.current_tool(self, true)
987
1003
  return self if cur_tool.nil?
988
1004
  flag_dsl = DSL::Flag.new(
989
1005
  flags.flatten, accept, default, handler, complete_flags, complete_values,
990
- report_collisions, group, desc, long_desc, display_name
1006
+ report_collisions, group, desc, long_desc, display_name, add_method
991
1007
  )
992
1008
  flag_dsl.instance_exec(flag_dsl, &block) if block
993
1009
  flag_dsl._add_to(cur_tool, key)
994
- DSL::Internal.maybe_add_getter(self, key)
1010
+ DSL::Internal.maybe_add_getter(self, key, flag_dsl._get_add_method)
995
1011
  self
996
1012
  end
997
1013
 
@@ -1043,6 +1059,12 @@ module Toys
1043
1059
  # a description of the allowed formats. (But note that this param
1044
1060
  # takes an Array of description lines, rather than a series of
1045
1061
  # arguments.) Defaults to the empty array.
1062
+ # @param add_method [true,false,nil] Whether to add a method for this
1063
+ # argument. If omitted or set to nil, uses the default behavior,
1064
+ # which adds the method if the key is a symbol representing a legal
1065
+ # method name that starts with a letter and does not override any
1066
+ # public method in the Ruby Object class or collide with any method
1067
+ # directly defined in the tool class.
1046
1068
  # @param block [Proc] Configures the positional argument. See
1047
1069
  # {Toys::DSL::PositionalArg} for the directives that can be called in
1048
1070
  # this block.
@@ -1050,14 +1072,15 @@ module Toys
1050
1072
  #
1051
1073
  def required_arg(key,
1052
1074
  accept: nil, complete: nil, display_name: nil,
1053
- desc: nil, long_desc: nil,
1075
+ desc: nil, long_desc: nil, add_method: nil,
1054
1076
  &block)
1055
1077
  cur_tool = DSL::Internal.current_tool(self, true)
1056
1078
  return self if cur_tool.nil?
1057
- arg_dsl = DSL::PositionalArg.new(accept, nil, complete, display_name, desc, long_desc)
1079
+ arg_dsl = DSL::PositionalArg.new(accept, nil, complete, display_name,
1080
+ desc, long_desc, add_method)
1058
1081
  arg_dsl.instance_exec(arg_dsl, &block) if block
1059
1082
  arg_dsl._add_required_to(cur_tool, key)
1060
- DSL::Internal.maybe_add_getter(self, key)
1083
+ DSL::Internal.maybe_add_getter(self, key, arg_dsl._get_add_method)
1061
1084
  self
1062
1085
  end
1063
1086
  alias required required_arg
@@ -1115,6 +1138,12 @@ module Toys
1115
1138
  # a description of the allowed formats. (But note that this param
1116
1139
  # takes an Array of description lines, rather than a series of
1117
1140
  # arguments.) Defaults to the empty array.
1141
+ # @param add_method [true,false,nil] Whether to add a method for this
1142
+ # argument. If omitted or set to nil, uses the default behavior,
1143
+ # which adds the method if the key is a symbol representing a legal
1144
+ # method name that starts with a letter and does not override any
1145
+ # public method in the Ruby Object class or collide with any method
1146
+ # directly defined in the tool class.
1118
1147
  # @param block [Proc] Configures the positional argument. See
1119
1148
  # {Toys::DSL::PositionalArg} for the directives that can be called in
1120
1149
  # this block.
@@ -1122,14 +1151,15 @@ module Toys
1122
1151
  #
1123
1152
  def optional_arg(key,
1124
1153
  default: nil, accept: nil, complete: nil, display_name: nil,
1125
- desc: nil, long_desc: nil,
1154
+ desc: nil, long_desc: nil, add_method: nil,
1126
1155
  &block)
1127
1156
  cur_tool = DSL::Internal.current_tool(self, true)
1128
1157
  return self if cur_tool.nil?
1129
- arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
1158
+ arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name,
1159
+ desc, long_desc, add_method)
1130
1160
  arg_dsl.instance_exec(arg_dsl, &block) if block
1131
1161
  arg_dsl._add_optional_to(cur_tool, key)
1132
- DSL::Internal.maybe_add_getter(self, key)
1162
+ DSL::Internal.maybe_add_getter(self, key, arg_dsl._get_add_method)
1133
1163
  self
1134
1164
  end
1135
1165
  alias optional optional_arg
@@ -1187,6 +1217,12 @@ module Toys
1187
1217
  # a description of the allowed formats. (But note that this param
1188
1218
  # takes an Array of description lines, rather than a series of
1189
1219
  # arguments.) Defaults to the empty array.
1220
+ # @param add_method [true,false,nil] Whether to add a method for these
1221
+ # arguments. If omitted or set to nil, uses the default behavior,
1222
+ # which adds the method if the key is a symbol representing a legal
1223
+ # method name that starts with a letter and does not override any
1224
+ # public method in the Ruby Object class or collide with any method
1225
+ # directly defined in the tool class.
1190
1226
  # @param block [Proc] Configures the positional argument. See
1191
1227
  # {Toys::DSL::PositionalArg} for the directives that can be called in
1192
1228
  # this block.
@@ -1194,20 +1230,21 @@ module Toys
1194
1230
  #
1195
1231
  def remaining_args(key,
1196
1232
  default: [], accept: nil, complete: nil, display_name: nil,
1197
- desc: nil, long_desc: nil,
1233
+ desc: nil, long_desc: nil, add_method: nil,
1198
1234
  &block)
1199
1235
  cur_tool = DSL::Internal.current_tool(self, true)
1200
1236
  return self if cur_tool.nil?
1201
- arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name, desc, long_desc)
1237
+ arg_dsl = DSL::PositionalArg.new(accept, default, complete, display_name,
1238
+ desc, long_desc, add_method)
1202
1239
  arg_dsl.instance_exec(arg_dsl, &block) if block
1203
1240
  arg_dsl._set_remaining_on(cur_tool, key)
1204
- DSL::Internal.maybe_add_getter(self, key)
1241
+ DSL::Internal.maybe_add_getter(self, key, arg_dsl._get_add_method)
1205
1242
  self
1206
1243
  end
1207
1244
  alias remaining remaining_args
1208
1245
 
1209
1246
  ##
1210
- # Set a option values statically and create a helper method.
1247
+ # Set option values statically and create helper methods.
1211
1248
  #
1212
1249
  # If any given key is a symbol representing a valid method name, then a
1213
1250
  # helper method is automatically added to retrieve the value. Otherwise,
@@ -1241,17 +1278,17 @@ module Toys
1241
1278
  if key.is_a?(::Hash)
1242
1279
  cur_tool.default_data.merge!(key)
1243
1280
  key.each_key do |k|
1244
- DSL::Internal.maybe_add_getter(self, k)
1281
+ DSL::Internal.maybe_add_getter(self, k, true)
1245
1282
  end
1246
1283
  else
1247
1284
  cur_tool.default_data[key] = value
1248
- DSL::Internal.maybe_add_getter(self, key)
1285
+ DSL::Internal.maybe_add_getter(self, key, true)
1249
1286
  end
1250
1287
  self
1251
1288
  end
1252
1289
 
1253
1290
  ##
1254
- # Set a option values statically without creating helper methods.
1291
+ # Set option values statically without creating helper methods.
1255
1292
  #
1256
1293
  # ### Example
1257
1294
  #
@@ -1297,7 +1334,8 @@ module Toys
1297
1334
  # @return [self]
1298
1335
  #
1299
1336
  def enforce_flags_before_args(state = true)
1300
- DSL::Internal.current_tool(self, true)&.enforce_flags_before_args(state)
1337
+ cur_tool = DSL::Internal.current_tool(self, true)
1338
+ cur_tool&.enforce_flags_before_args(state)
1301
1339
  self
1302
1340
  end
1303
1341
 
@@ -1313,7 +1351,8 @@ module Toys
1313
1351
  # @return [self]
1314
1352
  #
1315
1353
  def require_exact_flag_match(state = true)
1316
- DSL::Internal.current_tool(self, true)&.require_exact_flag_match(state)
1354
+ cur_tool = DSL::Internal.current_tool(self, true)
1355
+ cur_tool&.require_exact_flag_match(state)
1317
1356
  self
1318
1357
  end
1319
1358
 
@@ -1325,10 +1364,20 @@ module Toys
1325
1364
  # This directive is mutually exclusive with any of the directives that
1326
1365
  # declare arguments or flags.
1327
1366
  #
1367
+ # ### Example
1368
+ #
1369
+ # tool "mytool" do
1370
+ # disable_argument_parsing
1371
+ # def run
1372
+ # puts "Arguments passed: #{args}"
1373
+ # end
1374
+ # end
1375
+ #
1328
1376
  # @return [self]
1329
1377
  #
1330
1378
  def disable_argument_parsing
1331
- DSL::Internal.current_tool(self, true)&.disable_argument_parsing
1379
+ cur_tool = DSL::Internal.current_tool(self, true)
1380
+ cur_tool&.disable_argument_parsing
1332
1381
  self
1333
1382
  end
1334
1383
 
@@ -1354,7 +1403,8 @@ module Toys
1354
1403
  # @return [self]
1355
1404
  #
1356
1405
  def disable_flag(*flags)
1357
- DSL::Internal.current_tool(self, true)&.disable_flag(*flags)
1406
+ cur_tool = DSL::Internal.current_tool(self, true)
1407
+ cur_tool&.disable_flag(*flags)
1358
1408
  self
1359
1409
  end
1360
1410
 
@@ -1399,30 +1449,55 @@ module Toys
1399
1449
  end
1400
1450
 
1401
1451
  ##
1402
- # Specify how to run this tool. Typically you do this by defining a
1403
- # method namd `run`. Alternatively, however, you can pass a block to the
1404
- # `to_run` method.
1452
+ # Specify how to run this tool.
1405
1453
  #
1406
- # You may want to do this if your method needs access to local variables
1407
- # in the lexical scope. However, it is often more convenient to use
1408
- # {#static} to set the value in the context.)
1454
+ # Typically the entrypoint for a tool is a method named `run`. However,
1455
+ # you can change this by passing a different method name, as a symbol, to
1456
+ # {#to_run}.
1409
1457
  #
1410
- # ### Example
1458
+ # You can also alternatively pass a block to {#to_run}. You might do this
1459
+ # if your method needs access to local variables in the lexical scope.
1460
+ # However, it is often more convenient to use {#static} to set those
1461
+ # values in the context.
1462
+ #
1463
+ # ### Examples
1464
+ #
1465
+ # # Set a different method name as the entrypoint:
1466
+ #
1467
+ # tool "foo" do
1468
+ # to_run :foo
1469
+ # def foo
1470
+ # puts "The fool tool ran!"
1471
+ # end
1472
+ # end
1473
+ #
1474
+ # # Use a block to retain access to the enclosing lexical scope from
1475
+ # # the run method:
1411
1476
  #
1412
1477
  # tool "foo" do
1413
- # cur_time = Time.new
1478
+ # cur_time = Time.now
1414
1479
  # to_run do
1415
1480
  # puts "The time at tool definition was #{cur_time}"
1416
1481
  # end
1417
1482
  # end
1418
1483
  #
1419
- # @param block [Proc] The run method.
1484
+ # # But the following is approximately equivalent:
1485
+ #
1486
+ # tool "foo" do
1487
+ # static :cur_time, Time.now
1488
+ # def run
1489
+ # puts "The time at tool definition was #{cur_time}"
1490
+ # end
1491
+ # end
1492
+ #
1493
+ # @param handler [Proc,Symbol,nil] The run handler as a method name
1494
+ # symbol or a proc, or nil to explicitly set as non-runnable.
1495
+ # @param block [Proc] The run handler as a block.
1420
1496
  # @return [self]
1421
1497
  #
1422
- def to_run(&block)
1498
+ def to_run(handler = nil, &block)
1423
1499
  cur_tool = DSL::Internal.current_tool(self, true)
1424
- return self if cur_tool.nil?
1425
- cur_tool.run_handler = block
1500
+ cur_tool&.run_handler = handler || block
1426
1501
  self
1427
1502
  end
1428
1503
  alias on_run to_run
@@ -1430,9 +1505,12 @@ module Toys
1430
1505
  ##
1431
1506
  # Specify how to handle interrupts.
1432
1507
  #
1433
- # You may pass a block to be called, or the name of a method to call. In
1434
- # either case, the block or method should take one argument, the
1435
- # Interrupt exception that was raised.
1508
+ # You can provide either a block to be called, a Proc to be called, or
1509
+ # the name of a method to be called. In each case, the block, Proc, or
1510
+ # method can optionally take one argument, the Interrupt exception that
1511
+ # was raised.
1512
+ #
1513
+ # Note: this is equivalent to `on_signal("SIGINT")`.
1436
1514
  #
1437
1515
  # ### Example
1438
1516
  #
@@ -1440,7 +1518,7 @@ module Toys
1440
1518
  # def run
1441
1519
  # sleep 10
1442
1520
  # end
1443
- # on_interrupt do |e|
1521
+ # on_interrupt do
1444
1522
  # puts "I was interrupted."
1445
1523
  # end
1446
1524
  # end
@@ -1452,23 +1530,53 @@ module Toys
1452
1530
  #
1453
1531
  def on_interrupt(handler = nil, &block)
1454
1532
  cur_tool = DSL::Internal.current_tool(self, true)
1455
- return self if cur_tool.nil?
1456
- cur_tool.interrupt_handler = handler || block
1533
+ cur_tool&.interrupt_handler = handler || block
1534
+ self
1535
+ end
1536
+
1537
+ ##
1538
+ # Specify how to handle the given signal.
1539
+ #
1540
+ # You can provide either a block to be called, a Proc to be called, or
1541
+ # the name of a method to be called. In each case, the block, Proc, or
1542
+ # method can optionally take one argument, the SignalException that was
1543
+ # raised.
1544
+ #
1545
+ # ### Example
1546
+ #
1547
+ # tool "foo" do
1548
+ # def run
1549
+ # sleep 10
1550
+ # end
1551
+ # on_signal("QUIT") do |e|
1552
+ # puts "Signal caught: #{e.signm}."
1553
+ # end
1554
+ # end
1555
+ #
1556
+ # @param signal [Integer,String,Symbol] The signal name or number
1557
+ # @param handler [Proc,Symbol,nil] The signal callback proc or method
1558
+ # name. Pass nil to disable signal handling.
1559
+ # @param block [Proc] The signal callback as a block.
1560
+ # @return [self]
1561
+ #
1562
+ def on_signal(signal, handler = nil, &block)
1563
+ cur_tool = DSL::Internal.current_tool(self, true)
1564
+ cur_tool&.set_signal_handler(signal, handler || block)
1457
1565
  self
1458
1566
  end
1459
1567
 
1460
1568
  ##
1461
1569
  # Specify how to handle usage errors.
1462
1570
  #
1463
- # You may pass a block to be called, or the name of a method to call. In
1464
- # either case, the block or method should take one argument, the array of
1465
- # usage errors reported.
1571
+ # You can provide either a block to be called, a Proc to be called, or
1572
+ # the name of a method to be called. In each case, the block, Proc, or
1573
+ # method can optionally take one argument, the array of usage errors
1574
+ # reported.
1466
1575
  #
1467
1576
  # ### Example
1468
1577
  #
1469
- # This tool runs even if a usage error is encountered. You can find info
1470
- # on the errors from {Toys::Context::Key::USAGE_ERRORS},
1471
- # {Toys::Context::Key::UNMATCHED_ARGS}, and similar keys.
1578
+ # This tool runs even if a usage error is encountered, by setting the
1579
+ # `run` method as the usage error handler.
1472
1580
  #
1473
1581
  # tool "foo" do
1474
1582
  # def run
@@ -1484,8 +1592,7 @@ module Toys
1484
1592
  #
1485
1593
  def on_usage_error(handler = nil, &block)
1486
1594
  cur_tool = DSL::Internal.current_tool(self, true)
1487
- return self if cur_tool.nil?
1488
- cur_tool.usage_error_handler = handler || block
1595
+ cur_tool&.usage_error_handler = handler || block
1489
1596
  self
1490
1597
  end
1491
1598
 
@@ -1493,7 +1600,7 @@ module Toys
1493
1600
  # Specify that the given module should be mixed into this tool, and its
1494
1601
  # methods made available when running the tool.
1495
1602
  #
1496
- # You may provide either a module, the string name of a mixin that you
1603
+ # You can provide either a module, the string name of a mixin that you
1497
1604
  # have defined in this tool or one of its ancestors, or the symbol name
1498
1605
  # of a well-known mixin.
1499
1606
  #
@@ -1528,7 +1635,7 @@ module Toys
1528
1635
  ##
1529
1636
  # Determine if the given module/mixin has already been included.
1530
1637
  #
1531
- # You may provide either a module, the string name of a mixin that you
1638
+ # You can provide either a module, the string name of a mixin that you
1532
1639
  # have defined in this tool or one of its ancestors, or the symbol name
1533
1640
  # of a well-known mixin.
1534
1641
  #
@@ -1594,7 +1701,8 @@ module Toys
1594
1701
  # @return [nil] if there is no context.
1595
1702
  #
1596
1703
  def context_directory
1597
- DSL::Internal.current_tool(self, false)&.context_directory || source_info.context_directory
1704
+ cur_tool = DSL::Internal.current_tool(self, false)
1705
+ cur_tool&.context_directory || source_info.context_directory
1598
1706
  end
1599
1707
 
1600
1708
  ##
@@ -1615,8 +1723,7 @@ module Toys
1615
1723
  #
1616
1724
  def set_context_directory(dir) # rubocop:disable Naming/AccessorMethodName
1617
1725
  cur_tool = DSL::Internal.current_tool(self, false)
1618
- return self if cur_tool.nil?
1619
- cur_tool.custom_context_directory = dir
1726
+ cur_tool&.custom_context_directory = dir
1620
1727
  self
1621
1728
  end
1622
1729
 
@@ -1655,8 +1762,7 @@ module Toys
1655
1762
  def subtool_apply(&block)
1656
1763
  cur_tool = DSL::Internal.current_tool(self, false)
1657
1764
  return self if cur_tool.nil?
1658
- cur_tool.subtool_middleware_stack.add(:apply_config,
1659
- parent_source: source_info, &block)
1765
+ cur_tool.subtool_middleware_stack.add(:apply_config, parent_source: source_info, &block)
1660
1766
  self
1661
1767
  end
1662
1768
 
data/lib/toys/errors.rb CHANGED
@@ -140,7 +140,7 @@ module Toys
140
140
  e = ContextualError.new(e, banner, **opts)
141
141
  end
142
142
  raise e
143
- rescue ::ScriptError, ::StandardError => e
143
+ rescue ::ScriptError, ::StandardError, ::SignalException => e
144
144
  e = ContextualError.new(e, banner)
145
145
  add_fields_if_missing(e, opts)
146
146
  add_config_path_if_missing(e, path)
@@ -158,7 +158,7 @@ module Toys
158
158
  rescue ContextualError => e
159
159
  add_fields_if_missing(e, opts)
160
160
  raise e
161
- rescue ::ScriptError, ::StandardError => e
161
+ rescue ::ScriptError, ::StandardError, ::SignalException => e
162
162
  raise ContextualError.new(e, banner, **opts)
163
163
  end
164
164
 
@@ -21,8 +21,9 @@ module Toys
21
21
  # Generally, a middleware is a class that implements one or more of the
22
22
  # methods defined in this module: {Toys::Middleware#config}, and
23
23
  # {Toys::Middleware#run}. This module provides default implementations that
24
- # do nothing, but using them is not required. Middleware objects need respond
25
- # only to methods they care about.
24
+ # do nothing, but it is not required to include this module, or even to
25
+ # define both methods. Middleware objects need respond only to methods they
26
+ # care about.
26
27
  #
27
28
  module Middleware
28
29
  ##
data/lib/toys/settings.rb CHANGED
@@ -223,7 +223,7 @@ module Toys
223
223
  # has a parent, the parent is queried. If that parent also does not have
224
224
  # a value for the field, it may query its parent in turn, and so forth.
225
225
  # * If we encounter a root settings with no parent, and still no value is
226
- # set for the field, the default is returned.
226
+ # set for the field, the default for the *original* setting is returned.
227
227
  #
228
228
  # Example:
229
229
  #
@@ -5,6 +5,21 @@ module Toys
5
5
  ##
6
6
  # Ensures that a bundle is installed and set up when this tool is run.
7
7
  #
8
+ # This is the normal recommended way to use [bundler](https://bundler.io)
9
+ # with Toys. Including this mixin in a tool will cause Toys to ensure that
10
+ # the bundle is installed and available during tool execution. For example:
11
+ #
12
+ # tool "run-rails" do
13
+ # include :bundler
14
+ # def run
15
+ # # Note: no "bundle exec" required because Toys has already
16
+ # # installed and loaded the bundle.
17
+ # exec "rails s"
18
+ # end
19
+ # end
20
+ #
21
+ # ### Customization
22
+ #
8
23
  # The following parameters can be passed when including this mixin:
9
24
  #
10
25
  # * `:static` (Boolean) If `true`, installs the bundle immediately, when
@@ -39,7 +54,7 @@ module Toys
39
54
  # Defaults to {Toys::Utils::Gems::DEFAULT_GEMFILE_NAMES}.
40
55
  #
41
56
  # * `:toys_gemfile_names` (Array\<String\>) File names that are
42
- # recognized as Gemfiles when wearching in Toys directories.
57
+ # recognized as Gemfiles when searching in Toys directories.
43
58
  # Defaults to {DEFAULT_TOYS_GEMFILE_NAMES}.
44
59
  #
45
60
  # * `:on_missing` (Symbol) What to do if a needed gem is not installed.
@@ -3,10 +3,10 @@
3
3
  module Toys
4
4
  module StandardMixins
5
5
  ##
6
- # A set of helper methods for invoking subcommands. Provides shortcuts for
7
- # common cases such as invoking Ruby in a subprocess or capturing output
8
- # in a string. Also provides an interface for controlling a spawned
9
- # process's streams.
6
+ # The `:exec` mixin provides set of helper methods for executing processes
7
+ # and subcommands. It provides shortcuts for common cases such as invoking
8
+ # a Ruby script in a subprocess or capturing output in a string. It also
9
+ # provides an interface for controlling a spawned process's streams.
10
10
  #
11
11
  # You can make these methods available to your tool by including the
12
12
  # following directive in your tool configuration:
@@ -16,6 +16,25 @@ module Toys
16
16
  # This is a frontend for {Toys::Utils::Exec}. More information is
17
17
  # available in that class's documentation.
18
18
  #
19
+ # ### Mixin overview
20
+ #
21
+ # The mixin provides a number of methods for spawning processes. The most
22
+ # basic are {#exec} and {#exec_proc}. The {#exec} method spawns an
23
+ # operating system process specified by an executable and a set of
24
+ # arguments. The {#exec_proc} method takes a `Proc` and forks a Ruby
25
+ # process. Both of these can be heavily configured with stream handling,
26
+ # result handling, and numerous other options described below. The mixin
27
+ # also provides convenience methods for common cases such as spawning a
28
+ # Ruby process, spawning a shell script, or capturing output.
29
+ #
30
+ # The mixin also stores default configuration that it applies to processes
31
+ # it spawns. You can change these defaults by calling {#configure_exec}.
32
+ #
33
+ # Underlying the mixin is a service object of type {Toys::Utils::Exec}.
34
+ # Normally you would use the mixin methods to access this functionality,
35
+ # but you can also retrieve the service object itself by calling
36
+ # {Toys::Context#get} with the key {Toys::StandardMixins::Exec::KEY}.
37
+ #
19
38
  # ### Controlling processes
20
39
  #
21
40
  # A process can be started in the *foreground* or the *background*. If you
@@ -24,7 +43,7 @@ module Toys
24
43
  # If you start a background process, its streams will be redirected to null
25
44
  # by default, and control will be returned to you immediately.
26
45
  #
27
- # When a process is running, you can control it using a
46
+ # While a process is running, you can control it using a
28
47
  # {Toys::Utils::Exec::Controller} object. Use a controller to interact with
29
48
  # the process's input and output streams, send it signals, or wait for it
30
49
  # to complete.
@@ -44,7 +63,7 @@ module Toys
44
63
  # When running a process in the background, the controller is returned from
45
64
  # the method that starts the process:
46
65
  #
47
- # controller = exec_service.exec(["git", "init"], background: true)
66
+ # controller = exec(["git", "init"], background: true)
48
67
  #
49
68
  # ### Stream handling
50
69
  #
@@ -464,7 +483,8 @@ module Toys
464
483
  #
465
484
  def exec_separate_tool(cmd, **opts, &block)
466
485
  Exec._setup_clean_process(cmd) do |clean_cmd|
467
- exec(clean_cmd, **opts, &block)
486
+ opts = Exec._setup_exec_opts(opts, self)
487
+ self[KEY].exec(clean_cmd, **opts, &block)
468
488
  end
469
489
  end
470
490
 
@@ -650,7 +670,8 @@ module Toys
650
670
  #
651
671
  def capture_separate_tool(cmd, **opts, &block)
652
672
  Exec._setup_clean_process(cmd) do |clean_cmd|
653
- capture(clean_cmd, **opts, &block)
673
+ opts = Exec._setup_exec_opts(opts, self)
674
+ self[KEY].capture(clean_cmd, **opts, &block)
654
675
  end
655
676
  end
656
677