toys-core 0.14.6 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
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