toys-core 0.3.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 87e34f50eb16af11bc970c170e7750509e026b237ec37352be05198860bfdeae
4
+ data.tar.gz: 6bdaeaa6577d75c25b0efd17406792c24e3f6dd57326a52badd60495dc9170cc
5
+ SHA512:
6
+ metadata.gz: ee18128015b86041547bcdab1e7ec4bea05a41e0eff06c30a4003f032cc1ca335c7e7b593236ea140e65f43b4f31a752ceee5c57c787227b88d3144eac22b147
7
+ data.tar.gz: e4c7b13f825d6b4131db5e08a56a306652e2f532de1095758fde3be3fe509635629cc7725241b4bd3206ce1b414ed0cc4bd09aa480bbe4f335e8b3abd4ffc7c7
@@ -0,0 +1,9 @@
1
+ --no-private
2
+ --title=Toys Core
3
+ --markup=markdown
4
+ --main=README.md
5
+ ./lib/**/*.rb
6
+ -
7
+ README.md
8
+ LICENSE.md
9
+ CHANGELOG.md
@@ -0,0 +1,21 @@
1
+ # Release History
2
+
3
+ ### 0.3.2 / 2018-05-07
4
+
5
+ * CHANGED: Split core engine out into "toys-core" from the "toys" gem.
6
+ * CHANGED: Renamed path types to "search" and "config" paths, and restricted the former to the CLI.
7
+ * CHANGED: Removed aliasing from the Tool interface and reimplemented in the Loader.
8
+ * CHANGED: Default descriptions are now set via a middleware rather than in the Tool.
9
+ * CHANGED: Renamed most of the middleware classes.
10
+ * CHANGED: Combined usage-displaying middleware.
11
+ * CHANGED: Standard paths logic moved from CLI to StandardCLI.
12
+ * ADDED: Middleware that responds to the "--version" switch.
13
+ * ADDED: Context#new_cli that lets you run sub-instances of toys.
14
+ * IMPROVED: Middleware can now be referenced by class and constructed implicitly.
15
+ * IMPROVED: Usage error handler can now have its exit code configured.
16
+ * IMPROVED: Help and verbosity middlewares can have their switches configured.
17
+ * IMPROVED: Help middleware can search for keywords in subcommands.
18
+ * IMPROVED: Help middleware displays the config path in verbose mode.
19
+ * IMPROVED: Context::EXIT_ON_NONZERO_STATUS controls Context#run behavior.
20
+ * DOCS: Expanded middleware documentation
21
+ * INTERNAL: Removed Context::Base and just used CLI as base context
@@ -0,0 +1,29 @@
1
+ # License
2
+
3
+ Copyright 2018 Daniel Azuma
4
+
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
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.
@@ -0,0 +1,30 @@
1
+ # Toys
2
+
3
+ Toys is a command line binary that lets you build your own suite of command
4
+ line tools (with commands and subcommands) using a Ruby DSL. Commands can be
5
+ defined globally or scoped to directories.
6
+
7
+ Toys-Core is the command line tool framework underlying Toys. It can be used
8
+ to create command line binaries using the Toys DSL.
9
+
10
+ ## Quick Start
11
+
12
+ (TODO)
13
+
14
+ ## Contributing
15
+
16
+ While we appreciate contributions, please note that this software is currently
17
+ highly experimental, and the code is evolving very rapidly. Please contact the
18
+ author before embarking on a major pull request. More detailed contribution
19
+ guidelines will be provided when the software stabilizes further.
20
+
21
+ The source can be found on Github at
22
+ [https://github.com/dazuma/toys](https://github.com/dazuma/toys)
23
+
24
+ ## License
25
+
26
+ Copyright 2018 Daniel Azuma
27
+
28
+ This software is licensed under the 3-clause BSD license.
29
+
30
+ See the LICENSE.md file for more information.
@@ -0,0 +1,54 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ ##
31
+ # Toys is a Ruby library and command line tool that lets you build your own
32
+ # command line suite of tools (with commands and subcommands) using a Ruby DSL.
33
+ # You can define commands globally or configure special commands scoped to
34
+ # individual directories.
35
+ #
36
+ module Toys
37
+ ##
38
+ # Namespace for common utility classes.
39
+ #
40
+ module Utils; end
41
+ end
42
+
43
+ require "toys/alias"
44
+ require "toys/cli"
45
+ require "toys/config_dsl"
46
+ require "toys/context"
47
+ require "toys/core_version"
48
+ require "toys/errors"
49
+ require "toys/helpers"
50
+ require "toys/loader"
51
+ require "toys/middleware"
52
+ require "toys/template"
53
+ require "toys/templates"
54
+ require "toys/tool"
@@ -0,0 +1,94 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ module Toys
31
+ ##
32
+ # An alias is a name that refers to another name.
33
+ #
34
+ class Alias
35
+ ##
36
+ # Create a new alias.
37
+ #
38
+ # @param [Array<String>] full_name The name of the alias.
39
+ # @param [String,Array<String>] target The name of the target. May either
40
+ # be a local reference (a single string) or a global reference (an
41
+ # array of strings)
42
+ #
43
+ def initialize(full_name, target)
44
+ @target_name =
45
+ if target.is_a?(::String)
46
+ full_name[0..-2] + [target]
47
+ else
48
+ target.dup
49
+ end
50
+ @target_name.freeze
51
+ @full_name = full_name.dup.freeze
52
+ end
53
+
54
+ ##
55
+ # Return the name of the tool as an array of strings.
56
+ # This array may not be modified.
57
+ # @return [Array<String>]
58
+ #
59
+ attr_reader :full_name
60
+
61
+ ##
62
+ # Return the name of the target as an array of strings.
63
+ # This array may not be modified.
64
+ # @return [Array<String>]
65
+ #
66
+ attr_reader :target_name
67
+
68
+ ##
69
+ # Returns the local name of this tool.
70
+ # @return [String]
71
+ #
72
+ def simple_name
73
+ full_name.last
74
+ end
75
+
76
+ ##
77
+ # Returns a displayable name of this tool, generally the full name
78
+ # delimited by spaces.
79
+ # @return [String]
80
+ #
81
+ def display_name
82
+ full_name.join(" ")
83
+ end
84
+
85
+ ##
86
+ # Returns a displayable name of the target, generally the full name
87
+ # delimited by spaces.
88
+ # @return [String]
89
+ #
90
+ def display_target
91
+ target_name.join(" ")
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,268 @@
1
+ # Copyright 2018 Daniel Azuma
2
+ #
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ # * Neither the name of the copyright holder, nor the names of any other
14
+ # contributors to this software, may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ # POSSIBILITY OF SUCH DAMAGE.
28
+ ;
29
+
30
+ require "logger"
31
+
32
+ module Toys
33
+ ##
34
+ # A Toys-based CLI.
35
+ #
36
+ # Use this class to implement a CLI using Toys.
37
+ #
38
+ class CLI
39
+ ##
40
+ # Create a CLI
41
+ #
42
+ # @param [String,nil] binary_name The binary name displayed in help text.
43
+ # Optional. Defaults to the ruby program name.
44
+ # @param [String,nil] config_dir_name A directory with this name that
45
+ # appears in the loader path, is treated as a configuration directory
46
+ # whose contents are loaded into the toys configuration. Optional.
47
+ # If not provided, toplevel configuration directories are disabled.
48
+ # The default toys CLI sets this to `".toys"`.
49
+ # @param [String,nil] config_file_name A file with this name that appears
50
+ # in the loader path, is treated as a toplevel configuration file
51
+ # whose contents are loaded into the toys configuration. Optional.
52
+ # If not provided, toplevel configuration files are disabled.
53
+ # The default toys CLI sets this to `".toys.rb"`.
54
+ # @param [String,nil] index_file_name A file with this name that appears
55
+ # in any configuration directory (not just a toplevel directory) is
56
+ # loaded first as a standalone configuration file. If not provided,
57
+ # standalone configuration files are disabled.
58
+ # The default toys CLI sets this to `".toys.rb"`.
59
+ # @param [String,nil] preload_file_name A file with this name that appears
60
+ # in any configuration directory (not just a toplevel directory) is
61
+ # loaded before any configuration files. It is not treated as a
62
+ # configuration file in that the configuration DSL is not honored. You
63
+ # may use such a file to define auxiliary Ruby modules and classes that
64
+ # used by the tools defined in that directory.
65
+ # @param [Array] middleware_stack An array of middleware that will be used
66
+ # by default for all tools loaded by this CLI.
67
+ # @param [Logger,nil] logger The logger to use. If not provided, a default
68
+ # logger that writes to `STDERR` is used.
69
+ # @param [Integer,nil] base_level The logger level that should correspond
70
+ # to zero verbosity. If not provided, will default to the current level
71
+ # of the logger.
72
+ #
73
+ def initialize(
74
+ binary_name: nil,
75
+ config_dir_name: nil,
76
+ config_file_name: nil,
77
+ index_file_name: nil,
78
+ preload_file_name: nil,
79
+ middleware_stack: nil,
80
+ logger: nil,
81
+ base_level: nil
82
+ )
83
+ @logger = logger || self.class.default_logger
84
+ @base_level = base_level || @logger.level
85
+ @middleware_stack = middleware_stack || self.class.default_middleware_stack
86
+ @binary_name = binary_name || ::File.basename($PROGRAM_NAME)
87
+ @config_dir_name = config_dir_name
88
+ @config_file_name = config_file_name
89
+ @index_file_name = index_file_name
90
+ @preload_file_name = preload_file_name
91
+ @loader = Loader.new(
92
+ index_file_name: index_file_name,
93
+ preload_file_name: preload_file_name,
94
+ middleware_stack: middleware_stack
95
+ )
96
+ end
97
+
98
+ ##
99
+ # Return the current loader for this CLI
100
+ # @return [Toys::Loader]
101
+ #
102
+ attr_reader :loader
103
+
104
+ ##
105
+ # Return the effective binary name used for usage text in this CLI
106
+ # @return [String]
107
+ #
108
+ attr_reader :binary_name
109
+
110
+ ##
111
+ # Return the logger used by this CLI
112
+ # @return [Logger]
113
+ #
114
+ attr_reader :logger
115
+
116
+ ##
117
+ # Return the initial logger level in this CLI, used as the level for
118
+ # verbosity 0.
119
+ # @return [Integer]
120
+ #
121
+ attr_reader :base_level
122
+
123
+ ##
124
+ # Add a configuration file or directory to the loader.
125
+ #
126
+ # If a CLI has a default tool set, it might use this to point to the
127
+ # directory that defines those tools. For example, the default Toys CLI
128
+ # uses this to load the builtin tools from the "builtins" directory.
129
+ #
130
+ # @param [String] path A path to add.
131
+ # @param [Boolean] high_priority Add the config at the head of the priority
132
+ # list rather than the tail.
133
+ #
134
+ def add_config_path(path, high_priority: false)
135
+ @loader.add_path(path, high_priority: high_priority)
136
+ self
137
+ end
138
+
139
+ ##
140
+ # Searches the given directory for a well-known config directory and/or
141
+ # config file. If found, these are added to the loader.
142
+ #
143
+ # Typically, a CLI will use this to find toys configs in the current
144
+ # working directory, the user's home directory, or some other well-known
145
+ # general configuration-oriented directory such as "/etc".
146
+ #
147
+ # @param [String] search_path A path to search for configs.
148
+ # @param [Boolean] high_priority Add the configs at the head of the
149
+ # priority list rather than the tail.
150
+ #
151
+ def add_search_path(search_path, high_priority: false)
152
+ paths = []
153
+ if @config_file_name
154
+ file_path = ::File.join(search_path, @config_file_name)
155
+ paths << file_path if !::File.directory?(file_path) && ::File.readable?(file_path)
156
+ end
157
+ if @config_dir_name
158
+ dir_path = ::File.join(search_path, @config_dir_name)
159
+ paths << dir_path if ::File.directory?(dir_path) && ::File.readable?(dir_path)
160
+ end
161
+ @loader.add_path(paths, high_priority: high_priority)
162
+ self
163
+ end
164
+
165
+ ##
166
+ # A convenience method that searches the current working directory, and all
167
+ # ancestor directories, for configs to add to the loader.
168
+ #
169
+ # @param [String] start The first directory to add. Defaults to the current
170
+ # working directory.
171
+ # @param [String] base The last directory to add. Defaults to `"/"`.
172
+ # @param [Boolean] high_priority Add the configs at the head of the
173
+ # priority list rather than the tail.
174
+ #
175
+ def add_search_path_hierarchy(start: nil, base: "/", high_priority: false)
176
+ path = start || ::Dir.pwd
177
+ paths = []
178
+ loop do
179
+ paths << path
180
+ break if path == base
181
+ next_path = ::File.dirname(path)
182
+ break if next_path == path
183
+ path = next_path
184
+ end
185
+ paths.reverse! if high_priority
186
+ paths.each do |p|
187
+ add_search_path(p, high_priority: high_priority)
188
+ end
189
+ self
190
+ end
191
+
192
+ ##
193
+ # Run the CLI with the given command line arguments.
194
+ #
195
+ # @param [String...] args Command line arguments specifying which tool to
196
+ # run and what arguments to pass to it. You may pass either a single
197
+ # array of strings, or a series of string arguments.
198
+ # @param [Integer] verbosity Initial verbosity. Default is 0.
199
+ #
200
+ # @return [Integer] The resulting status code
201
+ #
202
+ def run(*args, verbosity: 0)
203
+ @loader.execute(self, args.flatten, verbosity: verbosity)
204
+ end
205
+
206
+ ##
207
+ # Make a clone with the same settings but no paths in the loader.
208
+ #
209
+ # @return [Toys::CLI]
210
+ #
211
+ def empty_clone
212
+ CLI.new(binary_name: @binary_name,
213
+ config_dir_name: @config_dir_name,
214
+ config_file_name: @config_file_name,
215
+ index_file_name: @index_file_name,
216
+ preload_file_name: @preload_file_name,
217
+ middleware_stack: @middleware_stack,
218
+ logger: @logger,
219
+ base_level: @base_level)
220
+ end
221
+
222
+ class << self
223
+ ##
224
+ # Returns a default set of middleware that may be used as a starting
225
+ # point for a typical CLI. This set includes:
226
+ #
227
+ # * {Toys::Middleware::HandleUsageErrors}
228
+ # * {Toys::Middleware::ShowUsage} adding the `--help` switch and
229
+ # providing default behavior for groups
230
+ # * {Toys::Middleware::AddVerbositySwitches} adding the `--verbose` and
231
+ # `--quiet` switches for managing the logger level
232
+ #
233
+ # @return [Array]
234
+ #
235
+ def default_middleware_stack
236
+ [
237
+ :handle_usage_errors,
238
+ :show_usage,
239
+ :add_verbosity_switches
240
+ ]
241
+ end
242
+
243
+ ##
244
+ # Returns a default logger that logs to `STDERR`.
245
+ #
246
+ # @return [Logger]
247
+ #
248
+ def default_logger
249
+ logger = ::Logger.new(::STDERR)
250
+ logger.formatter = proc do |severity, time, _progname, msg|
251
+ msg_str =
252
+ case msg
253
+ when ::String
254
+ msg
255
+ when ::Exception
256
+ "#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n")
257
+ else
258
+ msg.inspect
259
+ end
260
+ timestr = time.strftime("%Y-%m-%d %H:%M:%S")
261
+ format("[%s %5s] %s\n", timestr, severity, msg_str)
262
+ end
263
+ logger.level = ::Logger::WARN
264
+ logger
265
+ end
266
+ end
267
+ end
268
+ end