toys-core 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -0
  3. data/LICENSE.md +16 -24
  4. data/README.md +307 -59
  5. data/docs/guide.md +44 -4
  6. data/lib/toys-core.rb +58 -49
  7. data/lib/toys/acceptor.rb +672 -0
  8. data/lib/toys/alias.rb +106 -0
  9. data/lib/toys/arg_parser.rb +624 -0
  10. data/lib/toys/cli.rb +422 -181
  11. data/lib/toys/compat.rb +83 -0
  12. data/lib/toys/completion.rb +442 -0
  13. data/lib/toys/context.rb +354 -0
  14. data/lib/toys/core_version.rb +18 -26
  15. data/lib/toys/dsl/flag.rb +213 -56
  16. data/lib/toys/dsl/flag_group.rb +237 -51
  17. data/lib/toys/dsl/positional_arg.rb +210 -0
  18. data/lib/toys/dsl/tool.rb +968 -317
  19. data/lib/toys/errors.rb +46 -28
  20. data/lib/toys/flag.rb +821 -0
  21. data/lib/toys/flag_group.rb +282 -0
  22. data/lib/toys/input_file.rb +18 -26
  23. data/lib/toys/loader.rb +110 -100
  24. data/lib/toys/middleware.rb +24 -31
  25. data/lib/toys/mixin.rb +90 -59
  26. data/lib/toys/module_lookup.rb +125 -0
  27. data/lib/toys/positional_arg.rb +184 -0
  28. data/lib/toys/source_info.rb +192 -0
  29. data/lib/toys/standard_middleware/add_verbosity_flags.rb +38 -43
  30. data/lib/toys/standard_middleware/handle_usage_errors.rb +39 -40
  31. data/lib/toys/standard_middleware/set_default_descriptions.rb +111 -89
  32. data/lib/toys/standard_middleware/show_help.rb +130 -113
  33. data/lib/toys/standard_middleware/show_root_version.rb +29 -35
  34. data/lib/toys/standard_mixins/exec.rb +116 -78
  35. data/lib/toys/standard_mixins/fileutils.rb +16 -24
  36. data/lib/toys/standard_mixins/gems.rb +29 -30
  37. data/lib/toys/standard_mixins/highline.rb +34 -41
  38. data/lib/toys/standard_mixins/terminal.rb +72 -26
  39. data/lib/toys/template.rb +51 -35
  40. data/lib/toys/tool.rb +1161 -206
  41. data/lib/toys/utils/completion_engine.rb +171 -0
  42. data/lib/toys/utils/exec.rb +279 -182
  43. data/lib/toys/utils/gems.rb +58 -49
  44. data/lib/toys/utils/help_text.rb +117 -111
  45. data/lib/toys/utils/terminal.rb +69 -62
  46. data/lib/toys/wrappable_string.rb +162 -0
  47. metadata +24 -22
  48. data/lib/toys/definition/acceptor.rb +0 -191
  49. data/lib/toys/definition/alias.rb +0 -112
  50. data/lib/toys/definition/arg.rb +0 -140
  51. data/lib/toys/definition/flag.rb +0 -370
  52. data/lib/toys/definition/flag_group.rb +0 -205
  53. data/lib/toys/definition/source_info.rb +0 -190
  54. data/lib/toys/definition/tool.rb +0 -842
  55. data/lib/toys/dsl/arg.rb +0 -132
  56. data/lib/toys/runner.rb +0 -188
  57. data/lib/toys/standard_middleware.rb +0 -47
  58. data/lib/toys/utils/module_lookup.rb +0 -135
  59. data/lib/toys/utils/wrappable_string.rb +0 -165
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9266b9c87907b8844cd20529fd8102391737845d2e85cecc6674d6657a9d0d27
4
- data.tar.gz: da4bf58af8136859679ee2d8123e649310af0b82dbe896730af081d4e18c2c5d
3
+ metadata.gz: c599749b4dfa556b393f532a666455ced20062d746a8e9156aa472bdcbe5c6a2
4
+ data.tar.gz: 303c5615df653c4ca0fcb7646a413630fd74db0c28518ff2d8baae9f6ba80ccb
5
5
  SHA512:
6
- metadata.gz: 511416609364d830a23c27279ddcd545182cbca9fc0fc2727f832c75d4cfedaab0e4faa2d23456c880d202c4fb18e95239948bfbbdbe46035ccc008d0d84996b
7
- data.tar.gz: '075912df93384641e0c7b43807571d8ac4873af0906f022265db1483b66258fea88e7026d0a35c4716a5f96ac237ef9965b53398cdcf31d7254c2b5e2fb85895'
6
+ metadata.gz: 0b92268de87c8555a113e2e9804d4a4b3489cbe3c216dc1056e45595214297e4aaf4b19a95da9db5c024bfa41336d8d25f6df591b1861fb85c3f552a69cf3b3a
7
+ data.tar.gz: 860c322590729d3a111b3e205197eb42110cc8dd455dc8c6c5ff7ba042f69dd25583f3d720e9ccc3104fe9bd021999a655639c15fcec185b9b4fdd0126052aca
@@ -1,5 +1,103 @@
1
1
  # Release History
2
2
 
3
+ ### 0.8.0 / 2019-06-20
4
+
5
+ This is a major update with significant new features and a bunch of fixes. It also includes a significant amount of internal reorganization and cleanup, some of which resulted in backward incompatible changes. Basic use should not be affected. All signifiant features planned for beta are now implemented.
6
+
7
+ Major changes and features:
8
+
9
+ * CHANGED: Relicensed under the MIT License.
10
+ * ADDED: Tab completion for bash. Added APIs and DSL constructs for tools to customize completions.
11
+ * ADDED: The usage error screen displays suggestions when an argument is misspelled. (Requires Ruby 2.4 or later.)
12
+ * ADDED: Tools can provide an interrupt handler and a custom usage error handler. Added appropriate APIs and DSL methods.
13
+ * ADDED: Tools can enforce that flags must be given before positional args, and can control whether partial flags are accepted.
14
+
15
+ Other notable changes:
16
+
17
+ * ADDED: Flag handlers can accept the symbolic names `:set` and `:push` for common cases.
18
+ * ADDED: Function and range based acceptors.
19
+ * ADDED: The `acceptor` directive takes an optional `type_desc` argument.
20
+ * ADDED: The `accept` directives under flag and positional arg blocks in the DSL can now take blocks and `type_desc` values.
21
+ * ADDED: Context keys `UNMATCHED_ARGS`, `UNMATCHED_POSITIONAL`, and `UNMATCHED_FLAGS` that provide arguments that were not handled during arg parsing.
22
+ * ADDED: The Exec util and mixin support specifying a callback for process results.
23
+ * ADDED: The Exec util and mixin provide a way to identify processes by name.
24
+ * CHANGED: Implemented custom argument parsing and custom implementations of the standard OptionParser acceptors, rather than relying on OptionParser itself. For the most part, OptionParser behavior is preserved, except in cases where there is clearly a bug.
25
+ * CHANGED: Flags create a short form flag by default if the name has one character.
26
+ * CHANGED: Flags with explicit value-less syntax are no longer given a value if they specify a default or an acceptor.
27
+ * CHANGED: Renamed the `TOOL_DEFINITION` context key to `TOOL`, and removed the `tool_definition` convenience method.
28
+ * CHANGED: Removed the `BINARY_NAME` and `LOADER` context keys, and removed the corresponding convenience methods. Get these values from the CLI if needed.
29
+ * CHANGED: Renamed the `USAGE_ERROR` context key to `USAGE_ERRORS`, and the corresponding convenience method to `usage_errors`. The value is now a (possibly empty) array of `Toys::ArgParser::UsageError` objects rather than a string that isn't machine-parseable.
30
+ * CHANGED: The help middleware no longer defines remaining_args on the root tool.
31
+ * CHANGED: Renamed `to_expand` to `on_expand` in template definitions.
32
+ * CHANGED: Renamed `to_initialize` to `on_initialize`, and `to_include` to `on_include` in mixin definitions.
33
+ * CHANGED: The CLI options `preload_directory_name` and `data_directory_name` renamed to `preload_dir_name` and `data_dir_name`.
34
+ * CHANGED: Default descriptions for flag groups is now handled by the `set_default_descriptions` middleware rather than hard-coded in FlagGroup.
35
+ * CHANGED: Exec reports failure to start processes in the result object rather than, e.g. raising ENOENT.
36
+ * IMPROVED: Default error handler no longer displays a stack trace if a tool is interrupted.
37
+ * IMPROVED: Error messages for flag groups are more complete.
38
+ * IMPROVED: All context data, including well-known data, is available to be modified by flags and args.
39
+ * FIXED: Flags with optional values are properly set to `true` (rather than left at `nil`) if no value is provided.
40
+ * FIXED: Acceptors no longer raise errors when run on missing optional values.
41
+ * FIXED: When reporting errors in toys files, the line number was off by 2.
42
+ * FIXED: The `--usage` help flag now honors `--all` and `--no-recursive`.
43
+ * FIXED: The terminal now handles nil streams, as advertised.
44
+
45
+ Changes to internal interfaces:
46
+
47
+ * General changes:
48
+ * CHANGED: Renamed `Toys::Tool` to `Toys::Context`, and the `Keys` submodule to `Key`.
49
+ * CHANGED: Moved the `ModuleLookup` and `WrappableString` out of the `Utils` module to be located directly under `Toys`. Other modules remain under `Utils`. The remaining files under "toys/utils" must now be required explicitly. This directory is now specifically for modules that are not part of the "core" interface.
50
+ * CHANGED: All the classes under `Toys::Definition` are now located directly under `Toys`. For example, `Toys::Definition::Tool` is now `Toys::Tool`.
51
+ * CHANGED: Generally removed the term "definition" from interfaces. For example, an accessor method called `tool_definition` is now just called `tool`.
52
+ * CHANGED: Renamed `Toys::DSL::Arg` to `Toys::DSL::PositionalArg`
53
+ * CHANGED: Removed `Toys::Runner` and folded its functionality into `Toys::CLI`.
54
+ * CHANGED: The fallback execution feature of the show_help middleware is implemented by catching an exception afterward rather than detecting non-runnable up front. This lets us remove the second copy of show_help from the middleware stack.
55
+ * ADDED: Functionality dependent on Ruby version is kept in `Toys::Compat`.
56
+ * Changes related to the tool classes:
57
+ * CHANGED: Moved `Toys::Definition::Tool` to `Toys::Tool`.
58
+ * CHANGED: Removed the term "definition" from accessors. Specifically `flag_definitions` renamed to `flags`, `required_arg_definitions` renamed to `required_args`, `optional_arg_definitions` renamed to `optional_args`, `remaining_args_definition` renamed to `remaining_arg`, and `arg_definitions` renamed to `positional_args`.
59
+ * CHANGED: Renamed `Tool#runnable=` to `Tool#run_handler=`.
60
+ * CHANGED: `Tool#add_acceptor` takes the name as a separate argument, for consistency with `add_mixin` and `add_template`.
61
+ * CHANGED: Removed `Tool#custom_acceptors` method.
62
+ * CHANGED: Removed `Tool#resolve_acceptor` and replaced with `lookup_acceptor` which only looks up names.
63
+ * CHANGED: Renamed `Tool#resolve_mixin` to `lookup_mixin` and `Tool#resolve_template` to `lookup_template`.
64
+ * ADDED: Added `resolve_flag` method to look up flags by syntax.
65
+ * ADDED: Accessor for interrupt handler.
66
+ * ADDED: `enforce_flags_before_args` setting and `flags_before_args_enforced?` query.
67
+ * ADDED: Completion accessor, and options to the various flag and positional arg methods to set their completion strategies.
68
+ * ADDED: Added `Tool::DefaultCompletion` class.
69
+ * IMPROVED: `add_mixin`, `add_template`, and `add_acceptor` support all the specs understood by their create methods.
70
+ * Changes related to the flag classes:
71
+ * CHANGED: Moved `Toys::Definition::Flag` to `Toys::Flag`
72
+ * CHANGED: `FlagSyntax` is now `Flag::Syntax`.
73
+ * CHANGED: `Flag::Syntax#flag_style` now has values `:short` and `:long` instead of `"-"` and `"--"`.
74
+ * CHANGED: `Flag#single_flag_syntax` renamed to `Flag#short_flag_syntax`, and `Flag#double_flag_syntax` renamed to `Flag#long_flag_syntax`.
75
+ * CHANGED: Renamed `Flag#accept` to `Flag#acceptor` which now always returns an acceptor object (even for well-known acceptors such as `Integer`).
76
+ * CHANGED: Removed `Flag#optparser_info` and replaced with `Flag#canonical_syntax_strings`.
77
+ * ADDED: `Flag#create` class method providing a sane construction interface.
78
+ * ADDED: `Flag#resolve` method to look up flag syntax.
79
+ * ADDED: `Flag#completion` field.
80
+ * ADDED: Added `Flag::Resolution` and `Flag::DefaultCompletion` classes.
81
+ * Changes related to the positional arg classes:
82
+ * CHANGED: Moved `Toys::Definition::Arg` to `Toys::PositionalArg`.
83
+ * CHANGED: Renamed `Arg#accept` to `PositionalArg#acceptor` which now always returns an acceptor object (even for well-known acceptors such as `Integer`).
84
+ * ADDED: `PositionalArg#create` class method providing a sane construction interface.
85
+ * ADDED: `PositionalArg#completion` field
86
+ * Changes related to the flag group classes:
87
+ * CHANGED: Moved `Toys::Definition::FlagGroup` to `Toys::FlagGroup`
88
+ * CHANGED: The base class is now `FlagGroup::Base` instead of `FlagGroup` itself.
89
+ * ADDED: `FlagGroup#create` class method providing a sane construction interface.
90
+ * Changes related to acceptors:
91
+ * CHANGED: Moved `Toys::Definition::Acceptor` to `Toys::Acceptor`
92
+ * CHANGED: The base ciass is now `Acceptor::Base` instead of `Acceptor` itself.
93
+ * CHANGED: Subclasses are now submodules under `Acceptor`. For example, moved `Toys::Definition::PatternAcceptor` to `Toys::Acceptor::Pattern`.
94
+ * CHANGED: Replaced `name` field with separate `type_desc` and `well_known_spec` fields.
95
+ * CHANGED: The base class no longer takes a conversion proc. It is always a no-op. `Acceptor::Pattern`, however, does take a converter so it can continue to handle custom OptionParser acceptors.
96
+ * ADDED: Acceptors may define `suggestions` which returns results from did-you-mean.
97
+ * ADDED: Simple acceptor (`Acceptor::Simple`) which uses a single function to validate and convert input.
98
+ * ADDED: Range acceptor (`Acceptor::Range`) which validates against a range.
99
+ * ADDED: Class methods `Acceptor.create` and `Acceptor.lookup_well_known`.
100
+
3
101
  ### 0.7.0 / 2019-01-23
4
102
 
5
103
  * ADDED: Flag groups, which enforce policies around which flags are required.
data/LICENSE.md CHANGED
@@ -1,29 +1,21 @@
1
1
  # License
2
2
 
3
- Copyright 2018 Daniel Azuma
3
+ Copyright 2019 Daniel Azuma
4
4
 
5
- All rights reserved.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
6
11
 
7
- Redistribution and use in source and binary forms, with or without
8
- modification, are permitted provided that the following conditions are met:
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
9
14
 
10
- * Redistributions of source code must retain the above copyright notice,
11
- this list of conditions and the following disclaimer.
12
- * Redistributions in binary form must reproduce the above copyright notice,
13
- this list of conditions and the following disclaimer in the documentation
14
- and/or other materials provided with the distribution.
15
- * Neither the name of the copyright holder, nor the names of any other
16
- contributors to this software, may be used to endorse or promote products
17
- derived from this software without specific prior written permission.
18
-
19
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
- POSSIBILITY OF SUCH DAMAGE.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,108 +1,356 @@
1
- # Toys
1
+ # Toys-Core
2
2
 
3
3
  Toys is a configurable command line tool. Write commands in config files using
4
- a simple DSL, and Toys will provide the command line binary and take care of
5
- all the details such as argument parsing, online help, and error reporting.
4
+ a simple DSL, and Toys will provide the command line executable and take care
5
+ of all the details such as argument parsing, online help, and error reporting.
6
+
6
7
  Toys-Core is the command line tool framework underlying Toys. It can be used
7
- to create your own command line binaries using the internal Toys APIs.
8
+ to write command line executables using the Toys DSL and the power of the Toys
9
+ classes.
8
10
 
9
11
  For more detailed information about Toys-Core, see the
10
12
  [Toys-Core User's Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md).
11
13
  For background information about Toys itself, see the
14
+ [Toys README](https://www.rubydoc.info/gems/toys) and the
12
15
  [Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
13
16
 
14
- ## Quick Start
17
+ ## Introductory tutorial
18
+
19
+ Here's a tutorial to help you get a feel for how to write a basic command line
20
+ executable using Toys-Core.
15
21
 
16
- Here's a ten-minute tutorial to get a feel for how to write a basic command
17
- line binary using Toys-Core.
22
+ It assumes basic familiarity with Toys, so, if you have not done so, I
23
+ recommend first walking through the tutorial in the
24
+ [Toys README](https://www.rubydoc.info/gems/toys). It also assumes you are
25
+ running a unix-like system such as Linux or macOS. Some commands might need to
26
+ be modified if you're running on Windows.
18
27
 
19
28
  ### Install Toys
20
29
 
30
+ Toys requires Ruby 2.3 or later. (JRuby is not currently supported.)
31
+
21
32
  Install the **toys-core** gem using:
22
33
 
23
- gem install toys-core
34
+ $ gem install toys-core
24
35
 
25
36
  You may also install the **toys** gem, which brings in **toys-core** as a
26
37
  dependency.
27
38
 
28
- ### Create a Toys File
39
+ ### Create a new executable
40
+
41
+ We'll start by creating an executable Ruby script. Using your favorite text
42
+ editor, create new a file called `mycmd` with the following contents:
43
+
44
+ #!/usr/bin/env ruby
45
+
46
+ require "toys-core"
47
+
48
+ cli = Toys::CLI.new
49
+
50
+ exit(cli.run(*ARGV))
29
51
 
30
- A *Toys File* is a configuration file used by Toys to define commands, called
31
- "tools" in Toys lingo. If you've used the **toys** binary itself, you've
32
- probably written one already. You use the same file format when you create your
33
- own command line binary using Toys-Core.
52
+ Make sure the file's executable bit is set:
53
+
54
+ $ chmod a+x mycmd
55
+
56
+ That's it! This is a fully-functional Toys-based executable! Let's see what
57
+ happens when you run it:
58
+
59
+ $ ./mycmd
60
+
61
+ Just as with Toys itself, you get a help screen by default (since we haven't
62
+ yet actually implemented any behavior.) As you can see, some of the same
63
+ features from Toys are present already: online help, and `--verbose` and
64
+ `--quiet` flags. These features can of course all be customized, but they're
65
+ useful to have to start off.
66
+
67
+ ### Add some functionality
68
+
69
+ You implement the functionality of your executable using the same DSL that you
70
+ use to write Toys files. You could point your executable at a directory
71
+ containing actual Toys files, but the simplest option is to provide the
72
+ information to the Toys CLI object in a block.
73
+
74
+ Let's add some functionality.
75
+
76
+ #!/usr/bin/env ruby
77
+
78
+ require "toys-core"
34
79
 
35
- Create a new empty directory. In the directory, using your favorite text
36
- editor, create a file called `tools.rb`. Copy the following into the file, and
37
- save it:
80
+ cli = Toys::CLI.new
38
81
 
39
- tool "greet" do
40
- desc "My first tool!"
82
+ #### Insert the following block ...
83
+ cli.add_config_block do
84
+ desc "My first executable!"
41
85
  flag :whom, default: "world"
42
86
  def run
43
87
  puts "Hello, #{whom}!"
44
88
  end
45
89
  end
46
90
 
47
- If you're already familiar with writing Toys Files, feel free to modify and
48
- experiment with it.
91
+ exit(cli.run(*ARGV))
92
+
93
+ If you went through the tutorial in the README for the Toys gem, this should
94
+ look familiar. Let's run it now, and experiment with passing flags to it.
95
+
96
+ $ ./mycmd
97
+ $ ./mycmd --whom=ruby
98
+ $ ./mycmd --bye
99
+ $ ./mycmd --help
49
100
 
50
- ### Create Your Binary
101
+ Notice that we did not create a `tool` block, but instead set up description,
102
+ flags, and functionality directly in the configuration block. This configures
103
+ the "root tool", i.e. what happens when you run the executable without passing
104
+ a tool name to it. (Note, it's legal to do this in Toys as well, by setting
105
+ functionality at the "top level" of a `.toys.rb` file without including any
106
+ `tool` block.)
51
107
 
52
- Now we will write a command line binary that uses that Toys File. In the same
53
- new directory, create a new file called `mycmd`. Copy the following into it:
108
+ ### Tool-based executables
109
+
110
+ But perhaps you want your executable to have multiple "tools", similar to other
111
+ familiar executables like git or kubectl. You can define tools, including
112
+ nested tools, by writing `tool` blocks in your config. Here's an example:
54
113
 
55
114
  #!/usr/bin/env ruby
115
+
56
116
  require "toys-core"
117
+
57
118
  cli = Toys::CLI.new
58
- cli.add_config_path(File.join(__dir__, "tools.rb"))
59
- exit(cli.run(ARGV))
60
119
 
61
- Save the file and make it executable:
120
+ #### Change the config block as follows ...
121
+ cli.add_config_block do
122
+ # Things outside any tool block still apply to the root
123
+ desc "My first executable with several tools"
124
+
125
+ # We'll put the greet function here
126
+ tool "greet" do
127
+ desc "My first tool!"
128
+ flag :whom, default: "world"
129
+ def run
130
+ puts "Hello, #{whom}!"
131
+ end
132
+ end
133
+
134
+ # Try writing a second tool here. You could use the "new-repo"
135
+ # example from the Toys tutorial.
136
+ end
137
+
138
+ exit(cli.run(*ARGV))
139
+
140
+ Now you can run `greet` as a tool:
141
+
142
+ $ ./mycmd greet
143
+
144
+ The "root" functionality once again shows global help, including a list of the
145
+ available tools.
146
+
147
+ $ ./mycmd
148
+
149
+ Notice that the description set at the "root" of the config block (outside the
150
+ tool blocks) shows up here.
151
+
152
+ ### Configuring the CLI
153
+
154
+ So far, our executable behaves very similarly to Toys itself. Help screens are
155
+ shown by default, flags for help and verbosity are provided automatically, and
156
+ any exceptions are displayed to the terminal.
157
+
158
+ These and many more aspects of the behavior of our executable can be customized
159
+ by passing options to the `Toys::CLI` constructor. Here's an example that
160
+ modifies error handling and delimiter parsing.
161
+
162
+ #!/usr/bin/env ruby
163
+
164
+ require "toys-core"
165
+
166
+ #### Pass some additional options to the CLI constructor ...
167
+ cli = Toys::CLI.new(
168
+ extra_delimiters: ":",
169
+ error_handler: ->(e) {
170
+ puts "Dude, an error happened..."
171
+ return 1
172
+ }
173
+ )
174
+
175
+ #### Change the config block as follows ...
176
+ cli.add_config_block do
177
+ tool "example" do
178
+ tool "greet" do
179
+ def run
180
+ puts "Hello, world!"
181
+ end
182
+ end
183
+ tool "error" do
184
+ def run
185
+ raise "Whoops!"
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ exit(cli.run(*ARGV))
192
+
193
+ Try these runs. Do they behave as you expected?
194
+
195
+ $ ./mycmd example greet
196
+ $ ./mycmd example:greet
197
+ $ ./mycmd example.greet
198
+ $ ./mycmd example error
199
+
200
+ ### Configuring middleware
201
+
202
+ Toys _middleware_ are objects that provide common functionality for all the
203
+ tools in your executable. For example, a middleware adds the `--help` flag to
204
+ your tools by default.
205
+
206
+ The next example modifies the middleware stack to alter this common tool
207
+ functionality.
208
+
209
+ #!/usr/bin/env ruby
210
+
211
+ require "toys-core"
212
+
213
+ #### Change the CLI construction again ...
214
+ middlewares = [
215
+ [:set_default_descriptions, default_tool_desc: "Hey look, a tool!"],
216
+ [:show_help, help_flags: true]
217
+ ]
218
+ cli = Toys::CLI.new middleware_stack: middlewares
219
+
220
+ #### Use this config block ...
221
+ cli.add_config_block do
222
+ tool "greet" do
223
+ def run
224
+ puts "Hello, world!"
225
+ end
226
+ end
227
+ end
228
+
229
+ exit(cli.run(*ARGV))
230
+
231
+ We've now modified the default description applied to tools that don't provide
232
+ their own description. See the effect with:
233
+
234
+ $ ./mycmd greet --help
62
235
 
63
- chmod +x mycmd
236
+ We've also omitted some of the default middleware, including the one that adds
237
+ the `--verbose` and `--quiet` flags to all your tools. Notice those flags are
238
+ no longer present.
64
239
 
65
- Now you can run your command. Try these, to get a feel for how it behaves by
66
- default:
240
+ We've also omitted the middleware that provides default execution behavior
241
+ (i.e. displaying the help screen) when there is no `run` method. Now, since we
242
+ haven't defined a toplevel `run` method in this last example, invoking the root
243
+ tool will cause an error:
67
244
 
68
- ./mycmd greet
69
- ./mycmd greet --whom=Ruby
70
- ./mycmd greet --help
71
- ./mycmd
72
- ./mycmd foo
245
+ $ ./mycmd
73
246
 
74
- ### Next Steps
247
+ It is even possible to write your own middleware. In general, while the
248
+ `Toys::CLI` constructor provides defaults that should work for many use cases,
249
+ you can also customize it heavily to suit the needs of your executable.
75
250
 
76
- A basic command line binary based on Toys-Core consists of just the binary
77
- itself, and a Toys File (or directory) defining the commands to run. All the
78
- features of Toys, described in the
79
- [Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md),
80
- are at your disposal for writing tools for your binary. Or, if you want your
81
- binary to have a single function rather than support a set of tools, you can
82
- just write a toplevel tool in your Toys File.
251
+ ### Packaging as a gem
83
252
 
84
- You'll notice that Toys-Core provides a number of features "out of the box",
85
- such as online help, verbose and quiet flags, and default descriptions. These
86
- features are controlled by Toys *Middleware*, which are classes that customize
87
- the base behavior of Toys-Core. Toys-Core defaults to a certain set of
88
- middleware, but you can customize and change them for your own binary.
253
+ So far we've created simple one-file executables that you could distribute by
254
+ itself. However, the `toys-core` gem is a dependency, and your users will need
255
+ to have it installed. You could alleviate this by wrapping your executable in a
256
+ gem that can declare `toys-core` as a dependency explicitly.
89
257
 
90
- Finally, you may want to distribute your binary in a gem. Just make sure you
91
- include the Toys File or Directory in the gem, and that your binary configures
92
- `Toys::CLI` with the correct config path. The Toys File does not need to be in
93
- the require path (i.e. in the `lib` directory), and indeed it is probably best
94
- for it not to be, to prevent users of your gem from requiring it accidentally.
258
+ The [examples directory](https://github.com/dazuma/toys/tree/master/toys-core/examples)
259
+ includes a few simple examples that you can use as a starting point.
95
260
 
96
- See the
97
- [Toys-Core User's Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md)
98
- for thorough documentation on writing a command line binary using Toys-Core.
261
+ To experiment with the examples, clone the Toys repo from GitHub:
262
+
263
+ $ git clone https://github.com/dazuma/toys.git
264
+ $ cd toys
265
+
266
+ Navigate to the simple-gem example:
267
+
268
+ $ cd toys-core/examples/simple-gem
269
+
270
+ This example wraps the simple "greet" executable that we
271
+ [covered earlier](#Add_some_functionality) in a gem. You can see the
272
+ [executable file](https://github.com/dazuma/toys/tree/master/toys-core/examples/simple-gem/bin/toys-core-simple-example)
273
+ in the bin directory.
274
+
275
+ Try it out by building and installing the gem. From the `examples/simple-gem`
276
+ directory, run:
277
+
278
+ $ toys install
279
+
280
+ Once the gem has successfully installed, you can run the executable, which
281
+ Rubygems should have added to your path. (Note: if you are using a ruby
282
+ installation manager, you may need to "rehash" or "reshim" to gain access to
283
+ the executable.)
284
+
285
+ $ toys-core-simple-example --whom=Toys
286
+
287
+ Clean up by uninstalling the gem:
288
+
289
+ $ gem uninstall toys-core-simple-example
290
+
291
+ If the implementation of your executable is more complex, you might want to
292
+ break it up into multiple files. The multi-file gem example demonstrates this.
293
+
294
+ $ cd ../multi-file-gem
295
+
296
+ This executable's implementation resides in its
297
+ [lib directory](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/lib),
298
+ a technique that may be familiar to writers of command line executables. More
299
+ interestingly, the tools themselves are no longer defined in a block passed to
300
+ the CLI object, but have been moved into a separate
301
+ ["tools" directory](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/tools).
302
+ This directory has the same structure and supports the same features that are
303
+ available when writing complex sets of tools in a `.toys` directory. You then
304
+ configure the CLI object to look in this directory for its tools definitions,
305
+ as you can see in
306
+ [the code](https://github.com/dazuma/toys/tree/master/toys-core/examples/multi-file-gem/lib/toys-core-multi-gem-example.rb).
307
+
308
+ Try it out now. From the `examples/multi-file-gem` directory, run:
309
+
310
+ $ toys install
311
+
312
+ Once the gem has successfully installed, you can run the executable, which
313
+ Rubygems should have added to your path. (Note: if you are using a ruby
314
+ installation manager, you may need to "rehash" or "reshim" to gain access to
315
+ the executable.)
316
+
317
+ $ toys-core-multi-file-example greet
318
+
319
+ Clean up by uninstalling the gem:
320
+
321
+ $ gem uninstall toys-core-multi-file-example
322
+
323
+ ### Learning more
324
+
325
+ This introduction should be enough to get you started. However, Toys-Core is a
326
+ deep framework with many more features. Learn about how to write tools using
327
+ the Toys DSL, including validating and interpreting command line arguments,
328
+ using templates and mixins, controlling subprocesses, and producing nice styled
329
+ output, in the
330
+ [Toys User Guide](https://www.rubydoc.info/gems/toys/file/docs/guide.md).
331
+ Learn more about how to customize and package your own executable, including
332
+ handling errors, controlling log output, and providing your own mixins,
333
+ templates, and middleware, in the
334
+ [Toys-Core User Guide](https://www.rubydoc.info/gems/toys-core/file/docs/guide.md).
99
335
 
100
336
  ## License
101
337
 
102
- Copyright 2018 Daniel Azuma
338
+ Copyright 2019 Daniel Azuma
103
339
 
104
- This software is licensed under the 3-clause BSD license.
340
+ Permission is hereby granted, free of charge, to any person obtaining a copy
341
+ of this software and associated documentation files (the "Software"), to deal
342
+ in the Software without restriction, including without limitation the rights
343
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
344
+ copies of the Software, and to permit persons to whom the Software is
345
+ furnished to do so, subject to the following conditions:
105
346
 
106
- See the LICENSE.md file for more information.
347
+ The above copyright notice and this permission notice shall be included in
348
+ all copies or substantial portions of the Software.
107
349
 
108
- The source can be found on Github at https://github.com/dazuma/toys
350
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
351
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
352
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
353
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
354
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
355
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
356
+ IN THE SOFTWARE.