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.
@@ -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
@@ -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
@@ -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
- # The object passed to a subcommand control block
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