cmds 0.0.9 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1dd522e09d442b565bd299965feb5568ac5d830e
4
- data.tar.gz: 901dde67d7c5f77571539ab2ae8ea7a3b67c9d5d
3
+ metadata.gz: fbffca50ed0a5b832d53b5a3f5713734ce5ecd9c
4
+ data.tar.gz: c3dbd1396df4501c412f6b5102aebf77fa3dc4e6
5
5
  SHA512:
6
- metadata.gz: 7c5eea2077d11d90db033f8fd98ce2fb8782ac554427676087f1917fdf2f402ff6836089ab03a1e274783108b1acee9a49185e68fc7c46996b0db973c46a9b49
7
- data.tar.gz: 85f29ade7132c8c083c94b4e7939aed8be3fe7a1d2153fadff115f581f9d32a43e5a5d5cfcdfe277efc6f641fd2290294e89dce2f5c27c18da6f7b27fc810b50
6
+ metadata.gz: 2aa6e19cf215cbc820b3f0aece36bc3fe2925a915ef59ba5825d2319cae6d7db6ce8621cad0a3ef9f6132d2037ae77e59162d6ee01a6eccd5068fd775edbc221
7
+ data.tar.gz: 3d6f7af1c264532734b17a83362619978f8a856476a4cf18f1f38f43077e40185dbd3af3b67e5f83ec7bfec4dde013446e2259789e05a4dc2aa5d9b73ca5d63e
data/README.md CHANGED
@@ -244,7 +244,7 @@ though keys with those names may be accessed directly via `@kwds.fetch(key)` and
244
244
  to test for a key's presence or optionally include a value, append `?` to the method name:
245
245
 
246
246
  ```
247
- c = Cmds.new <<-BLOCK
247
+ c = Cmds::Cmd.new <<-BLOCK
248
248
  defaults
249
249
  <% if current_host? %>
250
250
  -currentHost <%= current_host %>
@@ -350,7 +350,7 @@ Cmds.sub "50%" # => "50%"
350
350
  ## reuse commands
351
351
 
352
352
  ```
353
- playbook = Cmds.new "ansible-playbook -i %{inventory} %{playbook}"
353
+ playbook = Cmds::Cmd.new "ansible-playbook -i %{inventory} %{playbook}"
354
354
  playbook.call inventory: "./hosts", playbook: "blah.yml"
355
355
  ```
356
356
 
@@ -376,7 +376,7 @@ NEEDS TEST
376
376
  can be accomplished with reuse and currying stuff
377
377
 
378
378
  ```
379
- playbook = Cmds.new "ansible-playbook -i %{inventory} %{playbook}", inventory: "inventory/dev"
379
+ playbook = Cmds::Cmd.new "ansible-playbook -i %{inventory} %{playbook}", inventory: "inventory/dev"
380
380
 
381
381
  # run setup.yml on the development hosts
382
382
  playbook.call playbook: "setup.yml"
@@ -390,7 +390,7 @@ prod_playbook.call playbook: "setup.yml", inventory: "inventory/prod"
390
390
  ## input
391
391
 
392
392
  ```
393
- c = Cmds.new("wc", input: "blah blah blah).call
393
+ c = Cmds::Cmd.new("wc", input: "blah blah blah).call
394
394
  ```
395
395
 
396
396
 
data/Rakefile CHANGED
@@ -5,6 +5,11 @@ require 'pp'
5
5
  require 'tempfile'
6
6
 
7
7
  require "bundler/gem_tasks"
8
+ require "rspec/core/rake_task"
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task :default => :spec
8
13
 
9
14
  Bundler.require
10
15
 
@@ -52,14 +57,14 @@ namespace :debug do
52
57
  end
53
58
 
54
59
  task :proxy => :conf do
55
- Cmds.new("./test/questions.rb").proxy
60
+ Cmds::Cmd.new("./test/questions.rb").proxy
56
61
  end
57
62
 
58
63
  namespace :capture do
59
64
  desc "capture with io-like input with debugging enabled"
60
65
  task :io_input => :conf do
61
66
  File.open "./test/lines.txt" do |f|
62
- Cmds.new("./test/echo_cmd.rb", input: f).capture
67
+ Cmds::Cmd.new("./test/echo_cmd.rb", input: f).capture
63
68
  end
64
69
  end
65
70
  end # ns capture
data/bin/rake ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle exec rake "$@"
data/bin/rspec ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle exec rspec "$@"
data/cmds.gemspec CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
data/lib/cmds/cmd.rb ADDED
@@ -0,0 +1,376 @@
1
+ require 'cmds/debug'
2
+ require 'cmds/util'
3
+ require 'cmds/spawn'
4
+ require 'cmds/erb_context'
5
+ require 'cmds/shell_eruby'
6
+ require 'cmds/result'
7
+
8
+ module Cmds
9
+ # a command consisting of a template, base/common parameters and input,
10
+ # and options.
11
+ #
12
+ class Cmd
13
+ # ERB stirng template (with Cmds-specific extensions) for the command.
14
+ #
15
+ # @return [String]
16
+ attr_reader :template
17
+
18
+ # base/common positional parameters to render into the command
19
+ # template.
20
+ #
21
+ # defaults to `[]`.
22
+ #
23
+ # {#prepare} and the methods that invoke it (like {#capture},
24
+ # {#stream}, etc.) accept `*args`, which will be appended to
25
+ # these values to create the final array for rendering.
26
+ #
27
+ # @return [Array<Object>]
28
+ attr_reader :args
29
+
30
+ # base/common keyword parameters to render into the command template.
31
+ #
32
+ # defaults to `{}`.
33
+ #
34
+ # {#prepare} and the methods that invoke it (like {#capture},
35
+ # {#stream}, etc.) accept `**kwds`, which will be merged on top of
36
+ # these values to create the final hash for rendering.
37
+ #
38
+ # @return [Hash{Symbol => Object}]
39
+ attr_reader :kwds
40
+
41
+ # string or readable IO-like object to use as default input to the
42
+ # command.
43
+ #
44
+ # {#prepare} and the methods that invoke it (like {#capture},
45
+ # {#stream}, etc.) accept an optional block that will override this
46
+ # value if present.
47
+ #
48
+ # @return [String | #read]
49
+ attr_reader :input
50
+
51
+ # if `true`, will execution will raise an error on non-zero exit code.
52
+ #
53
+ # defaults to `false`.
54
+ #
55
+ # @return [Boolean]
56
+ attr_reader :assert
57
+
58
+ # environment variables to set for command execution.
59
+ #
60
+ # defaults to `{}`.
61
+ #
62
+ # @return [Hash{Symbol => String}]
63
+ attr_reader :env
64
+
65
+ # format specifier symbol:
66
+ #
67
+ # - `:squish`
68
+ # - collapse rendered command string to one line.
69
+ # - `:pretty`
70
+ # - clean up and backslash suffix line endings.
71
+ #
72
+ # defaults to `:squish`.
73
+ #
74
+ # @return [:squish | :pretty]
75
+ attr_reader :format
76
+
77
+
78
+ # construct a Cmd.
79
+ #
80
+ # @param [String] template
81
+ # sets the {#template} attribute.
82
+ #
83
+ # @param [Hash] opts
84
+ #
85
+ # @option opts [Array<Object>] :args
86
+ # sets the {#args} attribute.
87
+ #
88
+ # @option opts [Hash{Symbol => Object}] :kwds
89
+ # sets the {#kwds} attribute.
90
+ #
91
+ # @option opts [String | #read] :input
92
+ # sets the {#input} attribute.
93
+ #
94
+ # @option opts [Hash{Symbol => String}] :env
95
+ # sets the {#env} attribute.
96
+ #
97
+ # @option opts [:squish, :pretty] :format
98
+ # sets the {#format} attribute.
99
+ #
100
+ def initialize template, **opts
101
+ Cmds.debug "Cmd constructing...",
102
+ template: template,
103
+ opts: opts
104
+
105
+ @template = template
106
+ @args = opts[:args] || []
107
+ @kwds = opts[:kwds] || {}
108
+ @input = opts[:input] || nil
109
+ @assert = opts[:assert] || false
110
+ @env = opts[:env] || {}
111
+ @format = opts[:format] || :squish
112
+ end # #initialize
113
+
114
+
115
+ # returns a new {Cmd} with the parameters and input merged in
116
+ def curry *args, **kwds, &input_block
117
+ self.class.new @template, {
118
+ args: (@args + args),
119
+ kwds: (@kwds.merge kwds),
120
+ input: (input ? input.call : @input),
121
+ assert: @assert,
122
+ env: @env,
123
+ format: @format,
124
+ }
125
+ end
126
+
127
+
128
+ # render parameters into `@template`.
129
+ #
130
+ # @note the returned string is **not** formatted for shell execution.
131
+ # Cmds passes this string through {Cmds.format} before execution,
132
+ # which addresses newlines in the rendered string through "squishing"
133
+ # everything down to one line or adding `\` to line ends.
134
+ #
135
+ # @param args (see #capture)
136
+ # @param kwds (see #capture)
137
+ #
138
+ # @return [String]
139
+ # the rendered command string.
140
+ #
141
+ def render *args, **kwds
142
+ context = Cmds::ERBContext.new((@args + args), @kwds.merge(kwds))
143
+ erb = Cmds::ShellEruby.new Cmds.replace_shortcuts(@template)
144
+
145
+ erb.result(context.get_binding)
146
+ end
147
+
148
+
149
+ # prepare a shell-safe command string for execution.
150
+ #
151
+ # @param args (see #capture)
152
+ # @param kwds (see #capture)
153
+ #
154
+ # @return [String]
155
+ # the prepared command string.
156
+ #
157
+ def prepare *args, **kwds
158
+ Cmds.format render(*args, **kwds), @format
159
+ end # #prepare
160
+
161
+
162
+ def stream *args, **kwds, &io_block
163
+ Cmds.debug "entering Cmd#stream",
164
+ args: args,
165
+ kwds: kwds,
166
+ io_block: io_block
167
+
168
+ Cmds.spawn prepare(*args, **kwds), @input, &io_block
169
+ end # #stream
170
+
171
+
172
+ # executes the command and returns a {Cmds::Result} with the captured
173
+ # outputs.
174
+ #
175
+ # @param [Array<Object>] args
176
+ # positional parameters to append to those in `@args` for rendering
177
+ # into the command string.
178
+ #
179
+ # @param [Hash{Symbol => Object}] kwds
180
+ # keyword parameters that override those in `@kwds` for rendering
181
+ # into the command string.
182
+ #
183
+ # @param [#call] input_block
184
+ # optional block that returns a string or readable object to override
185
+ # `@input`.
186
+ #
187
+ # @return [Cmds::Result]
188
+ # result of execution with command string, status, stdout and stderr.
189
+ #
190
+ def capture *args, **kwds, &input_block
191
+ Cmds.debug "entering Cmds#capture",
192
+ args: args,
193
+ kwds: kwds,
194
+ input: input
195
+
196
+ # prepare the command string
197
+ cmd = prepare *args, **kwds
198
+
199
+ # extract input from block via `call` if one is provided,
200
+ # otherwise default to instance variable (which may be `nil`)
201
+ input = input_block.nil? ? @input : input_block.call
202
+
203
+ Cmds.debug "prepared",
204
+ cmd: cmd,
205
+ input: input
206
+
207
+ # strings output will be concatenated onto
208
+ out = ''
209
+ err = ''
210
+
211
+ Cmds.debug "calling Cmds.spawn..."
212
+
213
+ status = Cmds.spawn cmd do |io|
214
+ # send the input to stream, which sends it to spawn
215
+ io.in = input
216
+
217
+ # and concat the output lines as they come in
218
+ io.on_out do |line|
219
+ out += line
220
+ end
221
+
222
+ io.on_err do |line|
223
+ err += line
224
+ end
225
+ end
226
+
227
+ Cmds.debug "Cmds.spawn completed",
228
+ status: status
229
+
230
+ # build a Result
231
+ # result = Cmds::Result.new cmd, status, out_reader.value, err_reader.value
232
+ result = Cmds::Result.new cmd, status, out, err
233
+
234
+ # tell the Result to assert if the Cmds has been told to, which will
235
+ # raise a SystemCallError with the exit status if it was non-zero
236
+ result.assert if @assert
237
+
238
+ return result
239
+ end # #capture
240
+
241
+
242
+ alias_method :call, :capture
243
+
244
+
245
+ # execute command and return `true` if it exited successfully.
246
+ #
247
+ # @param *args (see #capture)
248
+ # @param **kwds (see #capture)
249
+ # @param &input_block (see #capture)
250
+ #
251
+ # @return [Boolean]
252
+ # `true` if exit code was `0`.
253
+ #
254
+ def ok? *args, **kwds, &io_block
255
+ stream(*args, **kwds, &io_block) == 0
256
+ end
257
+
258
+
259
+ # execute command and return `true` if it failed.
260
+ #
261
+ # @param *args (see #capture)
262
+ # @param **kwds (see #capture)
263
+ # @param &input_block (see #capture)
264
+ #
265
+ # @return [Boolean]
266
+ # `true` if exit code was not `0`.
267
+ #
268
+ def error? *args, **kwds, &io_block
269
+ stream(*args, **kwds, &io_block) != 0
270
+ end
271
+
272
+
273
+ # def assert
274
+ # capture.raise_error
275
+ # end
276
+
277
+
278
+ def proxy
279
+ stream do |io|
280
+ io.in = $stdin
281
+ end
282
+ end
283
+
284
+
285
+ # captures and returns stdout
286
+ # (sugar for `#capture(*args, **kwds, &input_block).out`).
287
+ #
288
+ # @see #capture
289
+ # @see Result#out
290
+ #
291
+ # @param *args (see #capture)
292
+ # @param **kwds (see #capture)
293
+ # @param &input_block (see #capture)
294
+ #
295
+ # @return [String]
296
+ # the command's stdout.
297
+ #
298
+ def out *args, **kwds, &input_block
299
+ capture(*args, **kwds, &input_block).out
300
+ end
301
+
302
+
303
+ # captures and returns stdout
304
+ # (sugar for `#capture(*args, **kwds, &input_block).out`).
305
+ #
306
+ # @see #capture
307
+ # @see Result#out
308
+ #
309
+ # @param args [Array] see {.capture}.
310
+ # @param kwds [Proc] see {.capture}.
311
+ #
312
+ # @return [String] the command's stdout.
313
+ #
314
+ # @raise [SystemCallError] if the command fails (non-zero exit status).
315
+ #
316
+ def out! *args, **kwds, &input
317
+ capture(*args, **kwds, &input).assert.out
318
+ end
319
+
320
+
321
+ # captures and chomps stdout
322
+ # (sugar for `#out(*subs, &input_block).chomp`).
323
+ #
324
+ # @see #out
325
+ #
326
+ # @param *args (see #capture)
327
+ # @param **kwds (see #capture)
328
+ # @param &input_block (see #capture)
329
+ #
330
+ # @return [String]
331
+ # the command's chomped stdout.
332
+ #
333
+ def chomp *args, **kwds, &input_block
334
+ out(*args, **kwds, &input_block).chomp
335
+ end
336
+
337
+
338
+ # captures and chomps stdout, raising an error if the command failed.
339
+ # (sugar for `#out!(*subs, &input_block).chomp`).
340
+ #
341
+ # @see #capture
342
+ # @see Result#out
343
+ #
344
+ # @param *args (see #capture)
345
+ # @param **kwds (see #capture)
346
+ # @param &input_block (see #capture)
347
+ #
348
+ # @return [String]
349
+ # the command's chomped stdout.
350
+ #
351
+ # @raise [SystemCallError]
352
+ # if the command fails (non-zero exit status).
353
+ #
354
+ def chomp! *args, **kwds, &input
355
+ out!(*args, **kwds, &input).chomp
356
+ end
357
+
358
+
359
+ # captures and returns stdout
360
+ # (sugar for `#capture(*subs, &input_block).err`).
361
+ #
362
+ # @param *args (see #capture)
363
+ # @param **kwds (see #capture)
364
+ # @param &input_block (see #capture)
365
+ #
366
+ # @see #capture
367
+ # @see Result#err
368
+ #
369
+ # @return [String]
370
+ # the command's stderr.
371
+ #
372
+ def err *args, **kwds, &input_block
373
+ capture(*args, **kwds, &input_block).err
374
+ end
375
+ end # Cmd
376
+ end # Cmds
data/lib/cmds/debug.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'logger'
2
2
 
3
3
  # debug logging stuff
4
- class Cmds
4
+ module Cmds
5
5
 
6
6
  module Debug
7
7
  # constants
@@ -98,4 +98,4 @@ class Cmds
98
98
  return unless Debug.on?
99
99
  Debug.logger.debug Debug.format(msg, values)
100
100
  end
101
- end # class Cmds
101
+ end # module Cmds
@@ -1,4 +1,4 @@
1
- class Cmds
1
+ module Cmds
2
2
  class ERBContext < BasicObject
3
3
  def initialize args, kwds
4
4
  @args = args
@@ -10,7 +10,7 @@ class Cmds
10
10
  if args.empty? && block.nil?
11
11
  if sym.to_s[-1] == '?'
12
12
  key = sym.to_s[0...-1].to_sym
13
- # allow `false` to be ommited as well as missing and `nil`
13
+ # allow `false` to be omitted as well as missing and `nil`
14
14
  # by returning `nil` if the value is "false-y"
15
15
  @kwds[key] if @kwds[key]
16
16
  else
@@ -29,4 +29,4 @@ class Cmds
29
29
  @args.fetch(@arg_index).tap {@arg_index += 1}
30
30
  end
31
31
  end # end ERBContext
32
- end # class Cmds
32
+ end # module Cmds
@@ -1,4 +1,4 @@
1
- class Cmds
1
+ module Cmds
2
2
  class IOHandler
3
3
  attr_accessor :in, :out, :err
4
4
 
@@ -73,4 +73,4 @@ class Cmds
73
73
 
74
74
  # end private
75
75
  end # end IOHandler
76
- end # class Cmds
76
+ end # module Cmds
data/lib/cmds/pipe.rb CHANGED
@@ -1,4 +1,4 @@
1
- class Cmds
1
+ module Cmds
2
2
  # stupid little wrapper around IO.pipe that can have some extra info
3
3
  # attached to it
4
4
  class Pipe
data/lib/cmds/result.rb CHANGED
@@ -1,6 +1,10 @@
1
- class Cmds
2
- # a {Result} is a simple data structure returned from calling {Cmds#capture}
3
- # on a {Cmds} instance.
1
+ require 'nrser/refinements'
2
+
3
+ using NRSER
4
+
5
+ module Cmds
6
+ # a simple data structure returned from calling {Cmds#capture}
7
+ # on a {Cmd} instance.
4
8
  #
5
9
  # it contains the exit status code, standard output and standard error,
6
10
  # as well as the actual string command issued (after all substitutions).
@@ -52,7 +56,7 @@ class Cmds
52
56
  #
53
57
  def assert
54
58
  if error?
55
- msg = NRSER.squish <<-BLOCK
59
+ msg = <<-BLOCK.squish
56
60
  command `#{ @cmd }` exited with status #{ @status }
57
61
  and stderr #{ err.inspect }
58
62
  BLOCK
@@ -61,5 +65,5 @@ class Cmds
61
65
  end
62
66
  self
63
67
  end # raise_error
64
- end
65
- end # class Cmds
68
+ end # Result
69
+ end # Cmds
@@ -1,6 +1,6 @@
1
1
  require 'erubis'
2
2
 
3
- class Cmds
3
+ module Cmds
4
4
  # extension of Erubis' EscapedEruby (which auto-escapes `<%= %>` and
5
5
  # leaves `<%== %>` raw) that calls `Cmds.expand_sub` on the value
6
6
  class ShellEruby < Erubis::EscapedEruby
@@ -8,4 +8,4 @@ class Cmds
8
8
  "::Cmds.expand_sub(#{code.strip})"
9
9
  end
10
10
  end
11
- end # class Cmds
11
+ end # module Cmds