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.

Parameters:

  • template (String)

    sets the #template attribute.

  • opts (Hash)

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.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/cmds/cmd.rb', line 104

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
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.

Returns:

  • (Array<Object>)


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.

Returns:

  • (Boolean)


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

def assert
  @assert
end

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

environment variables to set for command execution.

defaults to {}.

Returns:

  • (Hash{String | Symbol => String})


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.

Returns:

  • (:squish | :pretty)


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.

Returns:

  • (String | #read)


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.

Returns:

  • (Hash{Symbol => Object})


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.

Returns:

  • (String)


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.

Parameters:

  • args (Array<Object>)

    positional parameters to append to those in @args for rendering into the command string.

  • kwds (Hash{Symbol => Object})

    keyword parameters that override those in @kwds for rendering into the command string.

  • input_block (#call)

    optional block that returns a string or readable object to override @input.

Returns:

  • (Cmds::Result)

    result of execution with command string, status, stdout and stderr.



208
209
210
211
212
213
214
215
216
217
218
219
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
# File 'lib/cmds/cmd.rb', line 208

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,
    # we deal with input in the block
    nil,
    # include env if mode is spawn argument
    (@env_mode == :spawn_arg ? @env : {}),
  ) 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).

Returns:

  • (String)

    the command's chomped stdout.

See Also:



357
358
359
# File 'lib/cmds/cmd.rb', line 357

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).

Returns:

  • (String)

    the command's chomped stdout.

Raises:

  • (SystemCallError)

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

See Also:



378
379
380
# File 'lib/cmds/cmd.rb', line 378

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



121
122
123
124
125
126
127
128
129
130
# File 'lib/cmds/cmd.rb', line 121

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,
  }
end

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

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

Returns:

  • (String)

    the command's stderr.

See Also:



396
397
398
# File 'lib/cmds/cmd.rb', line 396

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.

Returns:

  • (Boolean)

    true if exit code was not 0.



292
293
294
# File 'lib/cmds/cmd.rb', line 292

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.

Returns:

  • (Boolean)

    true if exit code was 0.



278
279
280
# File 'lib/cmds/cmd.rb', line 278

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).

Returns:

  • (String)

    the command's stdout.

See Also:



322
323
324
# File 'lib/cmds/cmd.rb', line 322

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).

Parameters:

Returns:

  • (String)

    the command's stdout.

Raises:

  • (SystemCallError)

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

See Also:



340
341
342
# File 'lib/cmds/cmd.rb', line 340

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

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

prepare a shell-safe command string for execution.

Parameters:

  • args (Array<Object>)

    positional parameters to append to those in @args for rendering into the command string.

  • kwds (Hash{Symbol => Object})

    keyword parameters that override those in @kwds for rendering into the command string.

Returns:

  • (String)

    the prepared command string.



171
172
173
# File 'lib/cmds/cmd.rb', line 171

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

#proxyObject



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

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.

Parameters:

  • args (Array<Object>)

    positional parameters to append to those in @args for rendering into the command string.

  • kwds (Hash{Symbol => Object})

    keyword parameters that override those in @kwds for rendering into the command string.

Returns:

  • (String)

    the rendered command string.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/cmds/cmd.rb', line 146

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



176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/cmds/cmd.rb', line 176

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,
              # include env if mode is spawn argument
              (@env_mode == :spawn_arg ? @env : {}),
              &io_block
end