cmds 0.0.9 → 0.1.0

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