Class: Cmds::Cmd

Inherits:
Object
  • Object
show all
Defined in:
lib/cmds/cmd.rb

Overview

a command consisting of a template, base/common parameters and input, and options.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(template, **opts) ⇒ Cmd

construct a Cmd.

Options Hash (**opts):

  • :args (Array<Object>)

    sets the #args attribute.

  • :kwds (Hash{Symbol => Object})

    sets the #kwds attribute.

  • :input (String | #read)

    sets the #input attribute.

  • :env (Hash{Symbol => String})

    sets the #env attribute.

  • :format (:squish, :pretty)

    sets the #format attribute.

  • :chdir (nil | String)

    sets the #chdir attribute.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/cmds/cmd.rb', line 113

def initialize template, **opts
  Cmds.debug "Cmd constructing...",
    template: template,
    opts: opts

  @template = template
  @args = opts[:args] || []
  @kwds = opts[:kwds] || {}
  @input = opts[:input] || nil
  @assert = opts[:assert] || false
  @env = opts[:env] || {}
  @format = opts[:format] || :squish
  @env_mode = opts[:env_mode] || :inline
  @chdir = opts[:chdir] || nil
end

Instance Attribute Details

#argsArray<Object> (readonly)

base/common positional parameters to render into the command template.

defaults to [].

#prepare and the methods that invoke it (like #capture, #stream, etc.) accept *args, which will be appended to these values to create the final array for rendering.



32
33
34
# File 'lib/cmds/cmd.rb', line 32

def args
  @args
end

#assertBoolean (readonly)

if true, will execution will raise an error on non-zero exit code.

defaults to false.



60
61
62
# File 'lib/cmds/cmd.rb', line 60

def assert
  @assert
end

#chdirnil | String (readonly)

directory to run the command in.



85
86
87
# File 'lib/cmds/cmd.rb', line 85

def chdir
  @chdir
end

#envHash{String | Symbol => String} (readonly)

environment variables to set for command execution.

defaults to {}.



67
68
69
# File 'lib/cmds/cmd.rb', line 67

def env
  @env
end

#format:squish | :pretty (readonly)

format specifier symbol:

  • :squish
    • collapse rendered command string to one line.
  • :pretty
    • clean up and backslash suffix line endings.

defaults to :squish.



79
80
81
# File 'lib/cmds/cmd.rb', line 79

def format
  @format
end

#inputString | #read (readonly)

string or readable IO-like object to use as default input to the command.

#prepare and the methods that invoke it (like #capture, #stream, etc.) accept an optional block that will override this value if present.



53
54
55
# File 'lib/cmds/cmd.rb', line 53

def input
  @input
end

#kwdsHash{Symbol => Object} (readonly)

base/common keyword parameters to render into the command template.

defaults to {}.

#prepare and the methods that invoke it (like #capture, #stream, etc.) accept **kwds, which will be merged on top of these values to create the final hash for rendering.



43
44
45
# File 'lib/cmds/cmd.rb', line 43

def kwds
  @kwds
end

#templateString (readonly)

ERB stirng template (with Cmds-specific extensions) for the command.



20
21
22
# File 'lib/cmds/cmd.rb', line 20

def template
  @template
end

Instance Method Details

#capture(*args, **kwds, &input_block) ⇒ Cmds::Result Also known as: call

executes the command and returns a Result with the captured outputs.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/cmds/cmd.rb', line 220

def capture *args, **kwds, &input_block
  Cmds.debug "entering Cmds#capture",
    args: args,
    kwds: kwds,
    input: input
  
  # prepare the command string
  cmd = prepare *args, **kwds
  
  # extract input from block via `call` if one is provided,
  # otherwise default to instance variable (which may be `nil`)
  input = input_block.nil? ? @input : input_block.call
  
  Cmds.debug "prepared",
    cmd: cmd,
    input: input
  
  # strings output will be concatenated onto
  out = ''
  err = ''

  Cmds.debug "calling Cmds.spawn..."
  
  status = Cmds.spawn(
    cmd,
    # include env if mode is spawn argument
    env: (@env_mode == :spawn_arg ? @env : {}),
    chdir: @chdir
  ) do |io|
    # send the input to stream, which sends it to spawn
    io.in = input

    # and concat the output lines as they come in
    io.on_out do |line|
      out += line
    end

    io.on_err do |line|
      err += line
    end
  end
  
  Cmds.debug "Cmds.spawn completed",
    status: status

  # build a Result
  # result = Cmds::Result.new cmd, status, out_reader.value, err_reader.value
  result = Cmds::Result.new cmd, status, out, err

  # tell the Result to assert if the Cmds has been told to, which will
  # raise a SystemCallError with the exit status if it was non-zero
  result.assert if @assert

  return result
end

#chomp(*args, **kwds, &input_block) ⇒ String

captures and chomps stdout (sugar for #out(*subs, &input_block).chomp).

See Also:



368
369
370
# File 'lib/cmds/cmd.rb', line 368

def chomp *args, **kwds, &input_block
  out(*args, **kwds, &input_block).chomp
end

#chomp!(*args, **kwds, &input) ⇒ String

captures and chomps stdout, raising an error if the command failed. (sugar for #out!(*subs, &input_block).chomp).

Raises:

  • (SystemCallError)

    if the command fails (non-zero exit status).

See Also:



389
390
391
# File 'lib/cmds/cmd.rb', line 389

def chomp! *args, **kwds, &input
  out!(*args, **kwds, &input).chomp
end

#curry(*args, **kwds, &input_block) ⇒ Object

returns a new Cmds::Cmd with the parameters and input merged in



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/cmds/cmd.rb', line 131

def curry *args, **kwds, &input_block
  self.class.new @template, {
    args: (@args + args),
    kwds: (@kwds.merge kwds),
    input: (input ? input.call : @input),
    assert: @assert,
    env: @env,
    format: @format,
    chdir: @chdir,
  }
end

#err(*args, **kwds, &input_block) ⇒ String

captures and returns stdout (sugar for #capture(*subs, &input_block).err).

See Also:



407
408
409
# File 'lib/cmds/cmd.rb', line 407

def err *args, **kwds, &input_block
  capture(*args, **kwds, &input_block).err
end

#error?(*args, **kwds, &io_block) ⇒ Boolean

execute command and return true if it failed.



303
304
305
# File 'lib/cmds/cmd.rb', line 303

def error? *args, **kwds, &io_block
  stream(*args, **kwds, &io_block) != 0
end

#ok?(*args, **kwds, &io_block) ⇒ Boolean

execute command and return true if it exited successfully.



289
290
291
# File 'lib/cmds/cmd.rb', line 289

def ok? *args, **kwds, &io_block
  stream(*args, **kwds, &io_block) == 0
end

#out(*args, **kwds, &input_block) ⇒ String

captures and returns stdout (sugar for #capture(*args, **kwds, &input_block).out).

See Also:



333
334
335
# File 'lib/cmds/cmd.rb', line 333

def out *args, **kwds, &input_block
  capture(*args, **kwds, &input_block).out
end

#out!(*args, **kwds, &input) ⇒ String

captures and returns stdout (sugar for #capture(*args, **kwds, &input_block).out).

Raises:

  • (SystemCallError)

    if the command fails (non-zero exit status).

See Also:



351
352
353
# File 'lib/cmds/cmd.rb', line 351

def out! *args, **kwds, &input
  capture(*args, **kwds, &input).assert.out
end

#prepare(*args, **kwds) ⇒ String

prepare a shell-safe command string for execution.



182
183
184
# File 'lib/cmds/cmd.rb', line 182

def prepare *args, **kwds
  Cmds.format render(*args, **kwds), @format
end

#proxyObject



313
314
315
316
317
# File 'lib/cmds/cmd.rb', line 313

def proxy
  stream do |io|
    io.in = $stdin
  end
end

#render(*args, **kwds) ⇒ String

Note:

the returned string is not formatted for shell execution. Cmds passes this string through Cmds.format before execution, which addresses newlines in the rendered string through "squishing" everything down to one line or adding \ to line ends.

render parameters into @template.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/cmds/cmd.rb', line 157

def render *args, **kwds
  context = Cmds::ERBContext.new((@args + args), @kwds.merge(kwds))
  erb = Cmds::ShellEruby.new Cmds.replace_shortcuts(@template)
  rendered = NRSER.dedent erb.result(context.get_binding)
  
  if @env_mode == :inline && !@env.empty?
    rendered = @env.sort_by {|name, value|
      name
    }.map {|name, value|
      "#{ name }=#{ Cmds.esc value }"
    }.join("\n\n") + "\n\n" + rendered
  end
  
  rendered
end

#stream(*args, **kwds, &io_block) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/cmds/cmd.rb', line 187

def stream *args, **kwds, &io_block
  Cmds.debug "entering Cmd#stream",
    args: args,
    kwds: kwds,
    io_block: io_block
  
  Cmds.spawn  prepare(*args, **kwds),
              input: @input,
              # include env if mode is spawn argument
              env: (@env_mode == :spawn_arg ? @env : {}),
              chdir: @chdir,
              &io_block
end