toys 0.3.0 → 0.3.1

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