toys 0.3.0 → 0.3.1
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.
- checksums.yaml +4 -4
- data/.yardopts +11 -0
- data/README.md +5 -2
- data/lib/toys.rb +2 -2
- data/lib/toys/builtins/system.rb +4 -0
- data/lib/toys/cli.rb +90 -3
- data/lib/toys/config_dsl.rb +432 -0
- data/lib/toys/context.rb +99 -0
- data/lib/toys/helpers.rb +11 -0
- data/lib/toys/helpers/exec.rb +180 -2
- data/lib/toys/loader.rb +165 -27
- data/lib/toys/middleware.rb +14 -0
- data/lib/toys/middleware/base.rb +7 -1
- data/lib/toys/middleware/set_verbosity.rb +3 -0
- data/lib/toys/middleware/{group_default.rb → show_group_usage.rb} +16 -5
- data/lib/toys/middleware/{show_tool_help.rb → show_tool_usage.rb} +8 -1
- data/lib/toys/middleware/show_usage_errors.rb +6 -0
- data/lib/toys/template.rb +73 -0
- data/lib/toys/templates.rb +14 -0
- data/lib/toys/templates/clean.rb +13 -2
- data/lib/toys/templates/gem_build.rb +19 -1
- data/lib/toys/templates/minitest.rb +17 -2
- data/lib/toys/templates/rubocop.rb +14 -1
- data/lib/toys/templates/yardoc.rb +15 -1
- data/lib/toys/tool.rb +405 -77
- data/lib/toys/utils/usage.rb +55 -11
- data/lib/toys/version.rb +1 -1
- metadata +6 -5
- data/lib/toys/builder.rb +0 -227
data/lib/toys/context.rb
CHANGED
@@ -33,6 +33,14 @@ module Toys
|
|
33
33
|
##
|
34
34
|
# The object context in effect during the execution of a tool.
|
35
35
|
#
|
36
|
+
# The context is generally a hash of key-value pairs.
|
37
|
+
# Keys that begin with two underscores are reserved common elements of the
|
38
|
+
# context such as the tool being executed, or the verbosity level.
|
39
|
+
# Other keys are available for use by your tool. Generally, they are set
|
40
|
+
# by switches and arguments in your tool. Context values may also be set
|
41
|
+
# by middleware. By convention, middleware-set keys begin with a single
|
42
|
+
# underscore.
|
43
|
+
#
|
36
44
|
class Context
|
37
45
|
##
|
38
46
|
# Context key for the verbosity value. Verbosity is an integer defaulting
|
@@ -86,6 +94,15 @@ module Toys
|
|
86
94
|
#
|
87
95
|
USAGE_ERROR = :__usage_error
|
88
96
|
|
97
|
+
##
|
98
|
+
# Create a Context object. Applications generally will not need to create
|
99
|
+
# these objects directly; they are created by the tool when it is preparing
|
100
|
+
# for execution.
|
101
|
+
# @private
|
102
|
+
#
|
103
|
+
# @param [Toys::Context::Base] context_base
|
104
|
+
# @param [Hash] data
|
105
|
+
#
|
89
106
|
def initialize(context_base, data)
|
90
107
|
@_context_base = context_base
|
91
108
|
@_data = data
|
@@ -94,58 +111,140 @@ module Toys
|
|
94
111
|
@_data[LOGGER] = context_base.logger
|
95
112
|
end
|
96
113
|
|
114
|
+
##
|
115
|
+
# Return the verbosity as an integer.
|
116
|
+
# @return [Integer]
|
117
|
+
#
|
97
118
|
def verbosity
|
98
119
|
@_data[VERBOSITY]
|
99
120
|
end
|
100
121
|
|
122
|
+
##
|
123
|
+
# Return the tool being executed.
|
124
|
+
# @return [Toys::Tool]
|
125
|
+
#
|
101
126
|
def tool
|
102
127
|
@_data[TOOL]
|
103
128
|
end
|
104
129
|
|
130
|
+
##
|
131
|
+
# Return the name of the tool being executed, as an array of strings.
|
132
|
+
# @return [Array[String]]
|
133
|
+
#
|
105
134
|
def tool_name
|
106
135
|
@_data[TOOL_NAME]
|
107
136
|
end
|
108
137
|
|
138
|
+
##
|
139
|
+
# Return the raw arguments passed to the tool, as an array of strings.
|
140
|
+
# This does not include the tool name itself.
|
141
|
+
# @return [Array[String]]
|
142
|
+
#
|
109
143
|
def args
|
110
144
|
@_data[ARGS]
|
111
145
|
end
|
112
146
|
|
147
|
+
##
|
148
|
+
# Return any usage error detected during argument parsing, or `nil` if
|
149
|
+
# no error was detected.
|
150
|
+
# @return [String,nil]
|
151
|
+
#
|
113
152
|
def usage_error
|
114
153
|
@_data[USAGE_ERROR]
|
115
154
|
end
|
116
155
|
|
156
|
+
##
|
157
|
+
# Return the logger for this execution.
|
158
|
+
# @return [Logger]
|
159
|
+
#
|
117
160
|
def logger
|
118
161
|
@_data[LOGGER]
|
119
162
|
end
|
120
163
|
|
164
|
+
##
|
165
|
+
# Return the active loader that can be used to get other tools.
|
166
|
+
# @return [Toys::Loader]
|
167
|
+
#
|
121
168
|
def loader
|
122
169
|
@_data[LOADER]
|
123
170
|
end
|
124
171
|
|
172
|
+
##
|
173
|
+
# Return the name of the binary that was executed.
|
174
|
+
# @return [String]
|
175
|
+
#
|
125
176
|
def binary_name
|
126
177
|
@_data[BINARY_NAME]
|
127
178
|
end
|
128
179
|
|
180
|
+
##
|
181
|
+
# Return a piece of raw data by key.
|
182
|
+
#
|
183
|
+
# @param [Symbol] key
|
184
|
+
# @return [Object]
|
185
|
+
#
|
129
186
|
def [](key)
|
130
187
|
@_data[key]
|
131
188
|
end
|
132
189
|
|
190
|
+
##
|
191
|
+
# Set a piece of data by key.
|
192
|
+
#
|
193
|
+
# Most tools themselves will not need to do this directly. It is generally
|
194
|
+
# used by middleware to modify the context and affect execution of tools
|
195
|
+
# that use it.
|
196
|
+
#
|
197
|
+
# @param [Symbol] key
|
198
|
+
# @param [Object] value
|
199
|
+
#
|
133
200
|
def []=(key, value)
|
134
201
|
@_data[key] = value
|
135
202
|
end
|
136
203
|
|
204
|
+
##
|
205
|
+
# Return an option value by key.
|
206
|
+
#
|
207
|
+
# @param [Symbol] key
|
208
|
+
# @return [Object]
|
209
|
+
#
|
210
|
+
def option(key)
|
211
|
+
@_data[key]
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# Returns the subset of the context that does not include well-known keys
|
216
|
+
# such as tool and verbosity. Technically, this includes all keys that do
|
217
|
+
# not begin with two underscores.
|
218
|
+
#
|
219
|
+
# @return [Hash]
|
220
|
+
#
|
137
221
|
def options
|
138
222
|
@_data.select do |k, _v|
|
139
223
|
!k.is_a?(::Symbol) || !k.to_s.start_with?("__")
|
140
224
|
end
|
141
225
|
end
|
142
226
|
|
227
|
+
##
|
228
|
+
# Execute another tool, given by the provided arguments.
|
229
|
+
#
|
230
|
+
# @param [String...] args Command line arguments defining another tool
|
231
|
+
# to run, along with parameters and switches.
|
232
|
+
# @param [Boolean] exit_on_nonzero_status If true, exit immediately if the
|
233
|
+
# run returns a nonzero error code.
|
234
|
+
# @return [Integer] The resulting status code
|
235
|
+
#
|
143
236
|
def run(*args, exit_on_nonzero_status: false)
|
144
237
|
code = @_context_base.run(args.flatten, verbosity: @_data[VERBOSITY])
|
145
238
|
exit(code) if exit_on_nonzero_status && !code.zero?
|
146
239
|
code
|
147
240
|
end
|
148
241
|
|
242
|
+
##
|
243
|
+
# Exit immediately with the given status code
|
244
|
+
#
|
245
|
+
# @param [Integer] code The status code, which should be 0 for no error,
|
246
|
+
# or nonzero for an error condition.
|
247
|
+
#
|
149
248
|
def exit(code)
|
150
249
|
throw :result, code
|
151
250
|
end
|
data/lib/toys/helpers.rb
CHANGED
@@ -34,6 +34,17 @@ module Toys
|
|
34
34
|
# Namespace for common helper modules
|
35
35
|
#
|
36
36
|
module Helpers
|
37
|
+
##
|
38
|
+
# Return a helper module by name.
|
39
|
+
#
|
40
|
+
# Currently recognized module names are:
|
41
|
+
#
|
42
|
+
# * `:exec` : Methods to help execute subcommands.
|
43
|
+
# * `:file_utils` : The FileUtils standard library methods.
|
44
|
+
#
|
45
|
+
# @param [String,Symbol] name Name of the helper module to return
|
46
|
+
# @return [Module,nil] The module, or `nil` if not found
|
47
|
+
#
|
37
48
|
def self.lookup(name)
|
38
49
|
Utils::ModuleLookup.lookup(:helpers, name)
|
39
50
|
end
|
data/lib/toys/helpers/exec.rb
CHANGED
@@ -32,14 +32,72 @@ require "logger"
|
|
32
32
|
module Toys
|
33
33
|
module Helpers
|
34
34
|
##
|
35
|
-
# A set of helper methods for invoking subcommands
|
35
|
+
# A set of helper methods for invoking subcommands. Provides shortcuts for
|
36
|
+
# common cases such as invoking Ruby in a subprocess or capturing output
|
37
|
+
# in a string. Also provides an interface for controlling a spawned
|
38
|
+
# process's streams.
|
39
|
+
#
|
40
|
+
# ## Configuration options
|
41
|
+
#
|
42
|
+
# A variety of options can be used to control subprocesses. These include:
|
43
|
+
#
|
44
|
+
# * **:env** (Hash) Environment variables to pass to the subprocess
|
45
|
+
# * **:log_level** (Integer) If set, the actual command will be logged
|
46
|
+
# at the given level.
|
47
|
+
# * **:in_from** (`:controller`,String) Connects the input stream of the
|
48
|
+
# subprocess. If set to `:controller`, the controller will control the
|
49
|
+
# input stream. If set to a string, that string will be written to the
|
50
|
+
# input stream. If not set, the input stream will be connected to the
|
51
|
+
# STDIN for the Toys process itself.
|
52
|
+
# * **:out_to** (`:controller`,`:capture`) Connects the standard output
|
53
|
+
# stream of the subprocess. If set to `:controller`, the controller
|
54
|
+
# will control the output stream. If set to `:capture`, the output will
|
55
|
+
# be captured in a string that is available in the
|
56
|
+
# {Toys::Helpers::Exec::Result} object. If not set, the subprocess
|
57
|
+
# standard out is connected to STDOUT of the Toys process.
|
58
|
+
# * **:err_to** (`:controller`,`:capture`) Connects the standard error
|
59
|
+
# stream of the subprocess. See `:out_to` for more details.
|
60
|
+
# * **:out_err_to** (`:controller`,`:capture`) Combines the standard out
|
61
|
+
# and error streams of the subprocess and connects them. See `:out_to`
|
62
|
+
# for more details.
|
63
|
+
# * **:exit_on_nonzero_status** (Boolean) If true, a nonzero status code
|
64
|
+
# will cause the entire tool to terminate. Default is false.
|
65
|
+
#
|
66
|
+
# In addition, any options recognized by `Process#spawn` are supported.
|
67
|
+
# These include `:umask`, `:pgroup`, `:chdir`, and many others.
|
68
|
+
#
|
69
|
+
# Configuration options may be provided to any method that starts a
|
70
|
+
# subprocess. You may also set default values for this tool by calling
|
71
|
+
# {Toys::Helpers::Exec#configure_exec}.
|
36
72
|
#
|
37
73
|
module Exec
|
74
|
+
##
|
75
|
+
# Set default configuration keys.
|
76
|
+
#
|
77
|
+
# @param [Hash] opts The default options. See the section on
|
78
|
+
# configuration options in the {Toys::Helpers::Exec} module docs.
|
79
|
+
#
|
38
80
|
def configure_exec(opts = {})
|
39
81
|
@exec_config ||= {}
|
40
82
|
@exec_config.merge!(opts)
|
41
83
|
end
|
42
84
|
|
85
|
+
##
|
86
|
+
# Execute a command. The command may be given as a single string to pass
|
87
|
+
# to a shell, or an array of strings indicating a posix command.
|
88
|
+
#
|
89
|
+
# If you provide a block, a {Toys::Helpers::Exec::Controller} will be
|
90
|
+
# yielded to it, allowing you to interact with the subprocess streams.
|
91
|
+
#
|
92
|
+
# @param [String,Array<String>] cmd The command to execute.
|
93
|
+
# @param [Hash] opts The command options. See the section on
|
94
|
+
# configuration options in the {Toys::Helpers::Exec} module docs.
|
95
|
+
# @yieldparam controller [Toys::Helpers::Exec::Controller] A controller
|
96
|
+
# for the subprocess streams.
|
97
|
+
#
|
98
|
+
# @return [Toys::Helpers::Result] The subprocess result, including
|
99
|
+
# exit code and any captured output.
|
100
|
+
#
|
43
101
|
def exec(cmd, opts = {}, &block)
|
44
102
|
exec_opts = ExecOpts.new(self)
|
45
103
|
exec_opts.add(@exec_config) if defined? @exec_config
|
@@ -48,6 +106,21 @@ module Toys
|
|
48
106
|
executor.execute(&block)
|
49
107
|
end
|
50
108
|
|
109
|
+
##
|
110
|
+
# Spawn a ruby process and pass the given arguments to it.
|
111
|
+
#
|
112
|
+
# If you provide a block, a {Toys::Helpers::Exec::Controller} will be
|
113
|
+
# yielded to it, allowing you to interact with the subprocess streams.
|
114
|
+
#
|
115
|
+
# @param [String,Array<String>] args The arguments to ruby.
|
116
|
+
# @param [Hash] opts The command options. See the section on
|
117
|
+
# configuration options in the {Toys::Helpers::Exec} module docs.
|
118
|
+
# @yieldparam controller [Toys::Helpers::Exec::Controller] A controller
|
119
|
+
# for the subprocess streams.
|
120
|
+
#
|
121
|
+
# @return [Toys::Helpers::Result] The subprocess result, including
|
122
|
+
# exit code and any captured output.
|
123
|
+
#
|
51
124
|
def ruby(args, opts = {}, &block)
|
52
125
|
cmd =
|
53
126
|
if args.is_a?(Array)
|
@@ -58,22 +131,54 @@ module Toys
|
|
58
131
|
exec(cmd, opts, &block)
|
59
132
|
end
|
60
133
|
|
134
|
+
##
|
135
|
+
# Execute the given string in a shell. Returns the exit code.
|
136
|
+
#
|
137
|
+
# @param [String] cmd The shell command to execute.
|
138
|
+
# @param [Hash] opts The command options. See the section on
|
139
|
+
# configuration options in the {Toys::Helpers::Exec} module docs.
|
140
|
+
# @yieldparam controller [Toys::Helpers::Exec::Controller] A controller
|
141
|
+
# for the subprocess streams.
|
142
|
+
#
|
143
|
+
# @return [Integer] The exit code
|
144
|
+
#
|
61
145
|
def sh(cmd, opts = {})
|
62
146
|
exec(cmd, opts).exit_code
|
63
147
|
end
|
64
148
|
|
149
|
+
##
|
150
|
+
# Execute a command. The command may be given as a single string to pass
|
151
|
+
# to a shell, or an array of strings indicating a posix command.
|
152
|
+
#
|
153
|
+
# Captures standard out and returns it as a string.
|
154
|
+
#
|
155
|
+
# @param [String,Array<String>] cmd The command to execute.
|
156
|
+
# @param [Hash] opts The command options. See the section on
|
157
|
+
# configuration options in the {Toys::Helpers::Exec} module docs.
|
158
|
+
# @yieldparam controller [Toys::Helpers::Exec::Controller] A controller
|
159
|
+
# for the subprocess streams.
|
160
|
+
#
|
161
|
+
# @return [String] What was written to standard out.
|
162
|
+
#
|
65
163
|
def capture(cmd, opts = {})
|
66
164
|
exec(cmd, opts.merge(out_to: :capture)).captured_out
|
67
165
|
end
|
68
166
|
|
167
|
+
##
|
168
|
+
# Returns the paty to the Ruby binary
|
169
|
+
# @return [String] Path to the Ruby binary
|
170
|
+
#
|
69
171
|
def self.ruby_binary
|
70
172
|
::File.join(::RbConfig::CONFIG["bindir"], ::RbConfig::CONFIG["ruby_install_name"])
|
71
173
|
end
|
72
174
|
|
73
175
|
##
|
74
|
-
#
|
176
|
+
# An object of this type is passed to a subcommand control block.
|
177
|
+
# You may use this object to interact with the subcommand's streams,
|
178
|
+
# and/or send signals to the process.
|
75
179
|
#
|
76
180
|
class Controller
|
181
|
+
## @private
|
77
182
|
def initialize(ins, out, err, out_err, pid)
|
78
183
|
@in = ins
|
79
184
|
@out = out
|
@@ -82,12 +187,50 @@ module Toys
|
|
82
187
|
@pid = pid
|
83
188
|
end
|
84
189
|
|
190
|
+
##
|
191
|
+
# Return the subcommand's standard input stream (which can be written
|
192
|
+
# to), if the command was configured with `in_from: :controller`.
|
193
|
+
# Returns `nil` otherwise.
|
194
|
+
# @return [IO,nil]
|
195
|
+
#
|
85
196
|
attr_reader :in
|
197
|
+
|
198
|
+
##
|
199
|
+
# Return the subcommand's standard output stream (which can be read
|
200
|
+
# from), if the command was configured with `out_to: :controller`.
|
201
|
+
# Returns `nil` otherwise.
|
202
|
+
# @return [IO,nil]
|
203
|
+
#
|
86
204
|
attr_reader :out
|
205
|
+
|
206
|
+
##
|
207
|
+
# Return the subcommand's standard error stream (which can be read
|
208
|
+
# from), if the command was configured with `err_to: :controller`.
|
209
|
+
# Returns `nil` otherwise.
|
210
|
+
# @return [IO,nil]
|
211
|
+
#
|
87
212
|
attr_reader :err
|
213
|
+
|
214
|
+
##
|
215
|
+
# Return the subcommand's combined standard output and error stream
|
216
|
+
# (which can be read from), if the command was configured with
|
217
|
+
# `out_err_to: :controller`. Returns `nil` otherwise.
|
218
|
+
# @return [IO,nil]
|
219
|
+
#
|
88
220
|
attr_reader :out_err
|
221
|
+
|
222
|
+
##
|
223
|
+
# Returns the process ID.
|
224
|
+
# @return [Integer]
|
225
|
+
#
|
89
226
|
attr_reader :pid
|
90
227
|
|
228
|
+
##
|
229
|
+
# Send the given signal to the process. The signal may be specified
|
230
|
+
# by name or number.
|
231
|
+
#
|
232
|
+
# @param [Integer,String] signal The signal to send.
|
233
|
+
#
|
91
234
|
def kill(signal)
|
92
235
|
::Process.kill(signal, pid)
|
93
236
|
end
|
@@ -97,6 +240,7 @@ module Toys
|
|
97
240
|
# The return result from a subcommand
|
98
241
|
#
|
99
242
|
class Result
|
243
|
+
## @private
|
100
244
|
def initialize(out, err, out_err, status)
|
101
245
|
@captured_out = out
|
102
246
|
@captured_err = err
|
@@ -104,19 +248,53 @@ module Toys
|
|
104
248
|
@status = status
|
105
249
|
end
|
106
250
|
|
251
|
+
##
|
252
|
+
# Returns the captured output string, if the command was configured
|
253
|
+
# with `out_to: :capture`. Returns `nil` otherwise.
|
254
|
+
# @return [String,nil]
|
255
|
+
#
|
107
256
|
attr_reader :captured_out
|
257
|
+
|
258
|
+
##
|
259
|
+
# Returns the captured error string, if the command was configured
|
260
|
+
# with `err_to: :capture`. Returns `nil` otherwise.
|
261
|
+
# @return [String,nil]
|
262
|
+
#
|
108
263
|
attr_reader :captured_err
|
264
|
+
|
265
|
+
##
|
266
|
+
# Returns the captured combined output and error string, if the command
|
267
|
+
# was configured with `out_err_to: :capture`. Returns `nil` otherwise.
|
268
|
+
# @return [String,nil]
|
269
|
+
#
|
109
270
|
attr_reader :captured_out_err
|
271
|
+
|
272
|
+
##
|
273
|
+
# Returns the status code object.
|
274
|
+
# @return [Process::Status]
|
275
|
+
#
|
110
276
|
attr_reader :status
|
111
277
|
|
278
|
+
##
|
279
|
+
# Returns the numeric status code.
|
280
|
+
# @return [Integer]
|
281
|
+
#
|
112
282
|
def exit_code
|
113
283
|
status.exitstatus
|
114
284
|
end
|
115
285
|
|
286
|
+
##
|
287
|
+
# Returns true if the subprocess terminated with a zero status.
|
288
|
+
# @return [Boolean]
|
289
|
+
#
|
116
290
|
def success?
|
117
291
|
exit_code.zero?
|
118
292
|
end
|
119
293
|
|
294
|
+
##
|
295
|
+
# Returns true if the subprocess terminated with a nonzero status.
|
296
|
+
# @return [Boolean]
|
297
|
+
#
|
120
298
|
def error?
|
121
299
|
!exit_code.zero?
|
122
300
|
end
|