cmds 0.2.4 → 0.2.5

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: 844e8c090bc1bd1fdd4c2196bb879c329497138d
4
- data.tar.gz: e2c403e462e1f37fe2a8114101942c6ed5352bae
3
+ metadata.gz: 2c17b6e518aac921af67affc110d82d610170cb8
4
+ data.tar.gz: f473be8e43fd43680036291c809b8ebbf5f2a309
5
5
  SHA512:
6
- metadata.gz: cb6158b6334ac66d4497619010d8d9557abf4a5ebea68d5325c100f83b49e732289ed39105133166426867cc4a3da6e6284cadba4e82f6715ccb89f1a1065057
7
- data.tar.gz: b24a5986c2825e9ff8c5e7f518cf713c49b3ffa93dac464f2de1ce02625a8e02c8d635fe7c321e270b46c714beb78045a7e9612c5d8f9697324c33361d47d9c2
6
+ metadata.gz: 99b6b930f1fc5a51576d139d2becaa7ca47a1938fadf301d9d555393d70cdf9a77ba8b914ffc7b034ed158897517d00ff1c3a42e2b401ae82893bdf37792d0b7
7
+ data.tar.gz: 28cdcc19b8e14f7a7201466df932af03042c651f87bc7c17d0a728c993e2ce84edf750b4f828ffe7404f6f1ccca4ca2531f9e3a49b0a08f78d9addf853e3073b
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require spec_helper
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # use local
4
- # gem 'nrser', '~> 0.0', :path => '../nrser-ruby'
4
+ # gem 'nrser', '~> 0.0', :path => '../nrser'
5
5
 
6
6
  # Specify your gem's dependencies in cmds.gemspec
7
7
  gemspec
data/README.md CHANGED
@@ -5,9 +5,11 @@ Cmds
5
5
 
6
6
  It treats generating shell the in a similar fashion to generating SQL or HTML.
7
7
 
8
- 1. [Status](#status)
9
- 3. [Real-World Examples](#real-world-examples)
10
- 2. [Installation](#installation)
8
+ Best read at
9
+
10
+ <http://www.rubydoc.info/gems/cmds/>
11
+
12
+ where the API doc links should work and you got a table and contents.
11
13
 
12
14
 
13
15
  -----------------------------------------------------------------------------
@@ -19,14 +21,26 @@ Ya know, before you get too excited...
19
21
  It's kinda starting to work. I'll be using it for stuff and seeing how it goes, but no promises until `1.0` of course.
20
22
 
21
23
 
24
+ -----------------------------------------------------------------------------
25
+ License
26
+ -----------------------------------------------------------------------------
27
+
28
+ MIT
29
+
30
+
22
31
  -----------------------------------------------------------------------------
23
32
  Real-World Examples
24
33
  -----------------------------------------------------------------------------
25
34
 
35
+ Or, "what's it look like?"...
36
+
26
37
  - Instead of
27
38
 
28
39
  ```Ruby
29
- `psql -U #{ db_config['username'] || ENV['USER'] } #{ db_config['database']} < #{ filepath.shellescape }`
40
+ `psql \
41
+ --username=#{ (db_config['username'] || ENV['USER']).shellescape } \
42
+ #{ db_config['database'].shellescape } \
43
+ < #{ filepath.shellescape }`
30
44
  ```
31
45
 
32
46
  write
@@ -46,333 +60,450 @@ Real-World Examples
46
60
  psql --username=nrser that_db < ./some/file/path
47
61
  ```
48
62
 
63
+ Cmds takes care of shell escaping for you.
64
+
49
65
 
50
66
  - Instead of
51
67
 
52
68
  ```Ruby
53
- `aws s3 sync s3://#{ PROD_APP_NAME } #{ s3_path.shellescape }`
69
+ `PGPASSWORD=#{ config[:password].shellescape } \
70
+ pg_dump \
71
+ --username=#{ config[:username].shellescape } \
72
+ --host=#{ config[:host].shellescape } \
73
+ --port=#{ config[:port].shellescape } \
74
+ #{ config[:database].shellescape } \
75
+ > #{ filepath.shellescape }`
54
76
  ```
55
77
 
56
- write
78
+ which can be really hard to pick out what's going on from a quick glance, write
57
79
 
58
80
  ```Ruby
59
- Cmds 'aws s3 sync %{uri} %{path}',
60
- uri: "s3://#{ PROD_APP_NAME }",
61
- path: s3_path
81
+ Cmds.new(
82
+ 'pg_dump %{opts} %{database}',
83
+ kwds: {
84
+ opts: {
85
+ username: config[:username],
86
+ host: config[:host],
87
+ port: config[:port],
88
+ },
89
+ database: config[:database],
90
+ },
91
+ env: {
92
+ PGPASSWORD: config[:password],
93
+ },
94
+ ).stream! { |io| io.out = filename }
62
95
  ```
63
-
64
-
65
- - Instead of
66
96
 
67
- ```Ruby
68
- `PGPASSWORD=#{ config[:password].shellescape } pg_dump -U #{ config[:username].shellescape } -h #{ config[:host].shellescape } -p #{ config[:port] } #{ config[:database].shellescape } > #{ filepath.shellescape }`
69
- ```
97
+ I find it much easier to see what's going on their quickly.
70
98
 
71
- write
99
+ Again, with some additional comments and examples:
72
100
 
73
101
  ```Ruby
74
- Cmds 'PGPASSWORD=%{password} pg_dump %{opts} %{database} > %{filepath}',
75
- password: config[:password],
76
- database: config[:database],
77
- filepath: filepath,
78
- opts: {
79
- username: config[:username],
80
- host: config[:host],
81
- port: config[:port],
82
- }
102
+ # We're going to instantiate a new {Cmds} object this time, because we're
103
+ # not just providing values for the string template, we're specifying an
104
+ # environment variable for the child process too.
105
+ #
106
+ cmd = Cmds.new(
107
+ # The string template to use.
108
+ 'pg_dump %{opts} %{database}',
109
+ kwds: {
110
+ # Hashes will automatically be expanded to CLI options. By default,
111
+ # we use `--name=VALUE` format for long ones and `-n VALUE` for short,
112
+ # but it's configurable.
113
+ opts: {
114
+ username: config[:username],
115
+ host: config[:host],
116
+ port: config[:port],
117
+ },
118
+ # As mentioned above, everything is shell escaped automatically
119
+ database: config[:database],
120
+ },
121
+ # Pass environment as it's own Hash. There are options for how it is
122
+ # provided to the child process as well.
123
+ env: {
124
+ # Symbol keys are fine, we'll take care of that for you
125
+ PGPASSWORD: config[:password],
126
+ },
127
+ )
128
+
129
+ # Take a look!
130
+ cmd.prepare
131
+ # => "PGPASSWORD=shhh\\! pg_dump --host=localhost --port=5432 --username=nrser blah"
132
+
133
+ # Now stream it. the `!` means raise if the exit code is not 0
134
+ exit_code = cmd.stream! { |io|
135
+ # We use the block to configure I/O. Here we send the standard output to
136
+ # a file, which can be a String, Pathname or IO object
137
+ io.out = filename
138
+ }
83
139
  ```
84
140
 
141
+
85
142
  -----------------------------------------------------------------------------
86
143
  Installation
87
144
  -----------------------------------------------------------------------------
88
145
 
89
146
  Add this line to your application's `Gemfile`:
90
147
 
91
- ```
92
- gem 'cmds'
93
- ```
148
+ gem 'cmds'
94
149
 
95
150
 
96
151
  And then execute:
97
152
 
98
- ```
99
- $ bundle
100
- ```
153
+ bundle install
101
154
 
102
155
 
103
- Or install it yourself as:
156
+ Or install it globally with:
104
157
 
105
- ```
106
- $ gem install cmds
107
- ```
158
+ gem install cmds
108
159
 
109
160
 
110
- ## architecture
111
161
 
112
- Cmds is based around a central `Cmds` class that takes a template for the command and a few options and operates by either wrapping the results in a `Cmds::Result` instance or streaming the results to `IO` objects or handler blocks. the Cmds` `augmented with a health helping of connivence methods for creating and executing a `Cmds` instance in common ways.
162
+ -----------------------------------------------------------------------------
163
+ Overview
164
+ -----------------------------------------------------------------------------
113
165
 
114
- ### constructor
166
+ Cmds is based around a central {Cmds} class that takes a template for the command and a few options and operates by either wrapping the results in a {Cmds::Result} instance or streaming the results to `IO` objects or handler blocks.
115
167
 
116
- the `Cmds` constructor looks like
117
168
 
118
- ```
119
- Cmds(template:String, opts:Hash)
120
- ```
169
+ -----------------------------------------------------------------------------
170
+ Features
171
+ -----------------------------------------------------------------------------
121
172
 
122
- a brief bit about the arguments:
123
-
124
- * `template`
125
- * a `String` template processed with ERB against positional and keyword arguments.
126
- * `opts`
127
- * `:args`
128
- * an `Array` of positional substitutions for the template.
129
- * assigned to `@args`.
130
- * defaults to an empty `Array`.
131
- * `:kwds`
132
- * a `Hash` of keyword substitutions for the template.
133
- * assigned to `@kwds`.
134
- * defaults to an empty `Hash`.
135
- * `:input`
136
- * a `String` to provide as standard input.
137
- * assigned to `@input`.
138
- * defaults to `nil`.
139
- * `:assert`
140
- * if this tests true, the execution of the command will raise an error on a nonzero exit status.
141
- * assigned to `@assert`.
142
- * defaults to `False`.
173
+ ### Templates ###
143
174
 
144
- ### execution
175
+ #### ERB ####
145
176
 
146
- you can provide three types of arguments when executing a command:
177
+ Templates are processed with "[Embedded Ruby][]" (eRuby/ERB) using the [Erubis][] gem.
147
178
 
148
- 1. positional arguments for substitution
149
- 2. keyword arguments for substitution
150
- 3. input to stdin
179
+ [Embedded Ruby]: https://en.wikipedia.org/wiki/ERuby
180
+ [Erubis]: http://www.kuwata-lab.com/erubis/
151
181
 
152
- all `Cmds` instance execution methods have the same form for accepting these:
182
+ For how it works check out
153
183
 
154
- 1. positional arguments are provided in an optional array that must be the first argument:
155
-
156
- `Cmds "cp <%= arg %> <%= arg %>", [src_path, dest_path]`
184
+ 1. {Cmds::ERBContext}
185
+ 2. {Cmds::ShellERuby}
186
+ 3. {Cmds#render}
187
+
188
+ ******************************************************************************
189
+
190
+
191
+ ##### Positional Values from `args` #####
192
+
193
+ 1. Use the `args` array made available in the templates with entry indexes.
157
194
 
158
- note that the arguments need to be enclosed in square braces. Cmds does **NOT** use \*splat for positional arguments because it would make a `Hash` final parameter ambiguous.
195
+ Example when constructing:
159
196
 
160
- 2. keyword arguments are provided as optional hash that must be the last argument:
197
+ ```ruby
198
+ Cmds.new(
199
+ 'cp <%= args[0] %> <%= args[1] %>',
200
+ args: [
201
+ 'source.txt',
202
+ 'dest.txt',
203
+ ],
204
+ ).prepare
205
+ # => "cp source.txt dest.txt"
206
+ ```
161
207
 
162
- `Cmds "cp <%= src %> <%= dest %>", src: src_path, dest: dest_path`
208
+ This will raise an error if it's called after using the last positional argument, but will not complain if all positional arguments are not used.
163
209
 
164
- in this case, curly braces are not required since Ruby turns the trailing keywords into a `Hash` provided as the last argument (or second-to-last argument in the case of a block included in the method signature).
165
-
166
- 3. input and output is handled with blocks:
210
+ 2. Use the `arg` method made available in the templates to get the next positional arg.
167
211
 
168
- `Cmds(“wc -l”){ “one\ntwo\nthree\n” }
212
+ Example when using "sugar" methods that take `args` as the single-splat (`*args`):
169
213
 
170
- Cmds.stream './test/tick.rb <%= times %>', times: times do |io|
171
- io.on_out do |line|
172
- # do something with the output line
173
- end
174
-
175
- io.on_err do |line|
176
- # do something with the error line
177
- end
178
- end`
214
+ ```ruby
215
+ Cmds.prepare 'cp <%= arg %> <%= arg %>',
216
+ 'source.txt',
217
+ 'dest.txt'
218
+ # => "cp source.txt dest.txt"
219
+ ```
220
+
221
+ ******************************************************************************
179
222
 
180
223
 
224
+ ##### Keyword Values from `kwds` #####
181
225
 
182
- ## templates
226
+ Just use the key as the method name.
183
227
 
184
- command templates are processed with [eRuby](https://en.wikipedia.org/wiki/ERuby), which many people know as [ERB](http://ruby-doc.org/stdlib-2.2.2/libdoc/erb/rdoc/ERB.html). you may know ERB from [Rails](http://guides.rubyonrails.org/layouts_and_rendering.html).
228
+ When constructing:
185
229
 
186
- actually, Cmds uses [Erubis](http://www.kuwata-lab.com/erubis/). which is the same thing Rails uses; calm down.
230
+ ```ruby
231
+ Cmds.new(
232
+ 'cp <%= src %> <%= dest %>',
233
+ kwds: {
234
+ src: 'source.txt',
235
+ dest: 'dest.txt',
236
+ },
237
+ ).prepare
238
+ # => "cp source.txt dest.txt"
239
+ ```
187
240
 
188
- this takes care of a few things:
241
+ When using "sugar" methods that take `kwds` as the double-splat (`**kwds`):
189
242
 
190
- 1. automatically shell escape values substituted into templates with [`Shellwords.escape`](http://ruby-doc.org/stdlib-2.2.2/libdoc/shellwords/rdoc/Shellwords.html#method-c-escape). it doesn't always do the prettiest job, but `Shellwords.escape` is part of Ruby's standard library and seems to work pretty well.
191
- 2. allow for fairly nice and readable logical structures like `if` / `else` in the command template. you've probably built html like this at some point. of course, the full power of Ruby is also available, though you probably won't find yourself needing much beyond some simple control structures.
243
+ ```ruby
244
+ Cmds.prepare 'cp <%= src %> <%= dest %>',
245
+ src: 'source.txt',
246
+ dest: 'dest.txt'
247
+ # => "cp source.txt dest.txt"
248
+ ```
192
249
 
193
- ## substitutions
194
250
 
195
- substitutions can be positional, keyword, or both.
251
+ ###### Key Names to Avoid ######
196
252
 
197
- ### positional
253
+ If possible, avoid naming your keys:
198
254
 
199
- positional arguments can be substituted in order using the `arg` method call:
255
+ - `arg`
256
+ - `args`
257
+ - `initialize`
258
+ - `get_binding`
259
+ - `method_missing`
200
260
 
201
- ```
202
- Cmds.sub "psql <%= arg %> <%= arg %> < <%= arg %>", [
203
- {
204
- username: "bingo bob",
205
- host: "localhost",
206
- port: 12345,
207
- },
208
- "blah",
209
- "/where ever/it/is.psql",
210
- ]
211
- # => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
212
- ```
261
+ If you must name them those things, don't expect to be able to access them as shown above; use `<%= @kwds[key] %>`.
213
262
 
214
- internally this translates to calling `@args.fetch(@arg_index)` and increments `@arg_index` by 1.
215
263
 
216
- this will raise an error if it's called after using the last positional argument, but will not complain if all positional arguments are not used. this prevents using a keyword arguments named `arg` without accessing the keywords hash directly.
264
+ ###### Keys That Might Not Be There ######
217
265
 
218
- the arguments may also be accessed directly though the bound class's `@args` instance variable:
266
+ Normally, if you try to interpolate a key that doesn't exist you will get a `KeyError`:
219
267
 
268
+ ```ruby
269
+ Cmds.prepare "blah <%= maybe %> <%= arg %>", "value"
270
+ # KeyError: couldn't find keys :maybe or "maybe" in keywords {}
220
271
  ```
221
- Cmds.sub "psql <%= @args[2] %> <%= @args[0] %> < <%= @args[1] %>", [
222
- "blah",
223
- "/where ever/it/is.psql",
224
- {
225
- username: "bingo bob",
226
- host: "localhost",
227
- port: 12345,
228
- },
229
- ]
230
- # => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
231
- ```
232
-
233
- note that `@args` is a standard Ruby array and will simply return `nil` if there is no value at that index (though you can use `args.fetch(i)` to get the same behavior as the `arg` method with a specific index `i`).
234
272
 
235
- ### keyword
273
+ I like a lot this better than just silently omitting the value, but sometimes you know that they key might not be set and want to receive `nil` if it's not.
236
274
 
237
- keyword arguments can be accessed by making a method call with their key:
275
+ In this case, append `?` to the key name (which is a method call in this case) and you will get `nil` if it's not set:
238
276
 
277
+ ```ruby
278
+ Cmds.prepare "blah <%= maybe? %> <%= arg %>", "value"
279
+ # => "blah value"
239
280
  ```
240
- Cmds.sub "psql <%= opts %> <%= database %> < <%= filepath %>",
241
- [],
242
- database: "blah",
243
- filepath: "/where ever/it/is.psql",
244
- opts: {
245
- username: "bingo bob",
246
- host: "localhost",
247
- port: 12345,
248
- }
249
- # => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
281
+
282
+ ```ruby
283
+ Cmds.prepare "blah <%= maybe? %> <%= arg %>", "value", maybe: "yes"
284
+ # => "blah yes value"
250
285
  ```
251
286
 
252
- this translates to a call of `@kwds.fetch(key)`, which will raise an error if `key` isn't present.
287
+ ******************************************************************************
253
288
 
254
- there are four key names that may not be accessed this way due to method definition on the context object:
255
289
 
256
- * `arg` (see above)
257
- * `initialize`
258
- * `get_binding`
259
- * `method_missing`
290
+ ##### Shell Escaping #####
260
291
 
261
- though keys with those names may be accessed directly via `@kwds.fetch(key)` and the like.
292
+ Cmds automatically shell-escapes values it interpolates into templates by passing them through the Ruby standard libray's [Shellwords.escape][].
262
293
 
263
- to test for a key's presence or optionally include a value, append `?` to the method name:
294
+ [Shellwords.escape]: http://ruby-doc.org/stdlib/libdoc/shellwords/rdoc/Shellwords.html#method-c-escape
264
295
 
296
+ ```ruby
297
+ Cmds.prepare "cp <%= src %> <%= dest %>",
298
+ src: "source.txt",
299
+ dest: "path with spaces.txt"
300
+ => "cp source.txt path\\ with\\ spaces.txt"
265
301
  ```
266
- c = Cmds.new <<-BLOCK
267
- defaults
268
- <% if current_host? %>
269
- -currentHost <%= current_host %>
270
- <% end %>
271
- export <%= domain %> <%= filepath %>
272
- BLOCK
273
302
 
274
- c.call domain: 'com.nrser.blah', filepath: '/tmp/export.plist'
275
- # defaults export com.nrser.blah /tmp/export.plist
303
+ It doesn't always do the prettiest job, but it's part of the standard library and seems to work pretty well... shell escaping is a messy and complicated topic (escaping for *which* shell?!), so going with the built-in solution seems reasonable for the moment, though I do hate all those backslashes... they're a pain to read.
276
304
 
277
- c.call current_host: 'xyz', domain: 'com.nrser.blah', filepath: '/tmp/export.plist'
278
- # defaults -currentHost xyz export com.nrser.blah /tmp/export.plist
279
- ```
280
305
 
281
- ### both
306
+ ###### Raw Interpolation ######
282
307
 
283
- both positional and keyword substitutions may be provided:
308
+ You can render a raw string with `<%== %>`.
284
309
 
285
- ```
286
- Cmds.sub "psql <%= opts %> <%= arg %> < <%= filepath %>",
287
- ["blah"],
288
- filepath: "/where ever/it/is.psql",
289
- opts: {
290
- username: "bingo bob",
291
- host: "localhost",
292
- port: 12345,
293
- }
294
- # => 'psql --host=localhost --port=12345 --username=bingo\ bob blah < /where\ ever/it/is.psql'
310
+ To see the difference with regard to the previous example (which would break the `cp` command in question):
311
+
312
+ ```ruby
313
+ Cmds.prepare "cp <%= src %> <%== dest %>",
314
+ src: "source.txt",
315
+ dest: "path with spaces.txt"
316
+ => "cp source.txt path with spaces.txt"
295
317
  ```
296
318
 
297
- this might be useful if you have a simple positional command like
319
+ And a way it make a little more sense:
298
320
 
299
- ```
300
- Cmds "blah <%= arg %>", ["value"]
321
+ ```ruby
322
+ Cmds.prepare "<%== bin %> <%= *args %>",
323
+ 'blah',
324
+ 'boo!',
325
+ bin: '/usr/bin/env echo'
326
+ => "/usr/bin/env echo blah boo\\!"
301
327
  ```
302
328
 
303
- and you want to quickly add in some optional value
329
+ ******************************************************************************
304
330
 
305
- ```
306
- Cmds "blah <%= maybe? %> <%= arg %>", ["value"]
307
- Cmds "blah <%= maybe? %> <%= arg %>", ["value"], maybe: "yes!"
308
- ```
309
331
 
310
- ************************************************************************
332
+ ##### Splatting (`*`) To Render Multiple Shell Tokens #####
311
333
 
334
+ Render multiple shell tokens (individual strings the shell picks up - basically, each one is an entry in `ARGV` for the child process) in one expression tag by prefixing the value with `*`:
312
335
 
313
- ### Shortcuts
336
+ ```ruby
337
+ Cmds.prepare '<%= *exe %> <%= cmd %> <%= opts %> <%= *args %>',
338
+ 'x', 'y', # <= these are the `args`
339
+ exe: ['/usr/bin/env', 'blah'],
340
+ cmd: 'do-stuff',
341
+ opts: {
342
+ really: true,
343
+ 'some-setting': 'dat-value',
344
+ }
345
+ # => "/usr/bin/env blah do-stuff --really --some-setting=dat-value x y"
346
+ ```
314
347
 
315
- `Cmds` has (limited, custom) support for [printf][]-style shortcuts.
348
+ `ARGV` tokenization by the shell would look like:
316
349
 
317
- [printf]: https://en.wikipedia.org/wiki/Printf_format_string
350
+ ```ruby
351
+ [
352
+ '/usr/bin/env',
353
+ 'blah',
354
+ 'do-stuff',
355
+ '--really',
356
+ '--some-setting=dat-value',
357
+ 'x',
358
+ 'y',
359
+ ]
360
+ ```
318
361
 
362
+ - Compare to *without* splats:
363
+
364
+ ```ruby
365
+ Cmds.prepare '<%= exe %> <%= cmd %> <%= opts %> <%= args %>',
366
+ 'x', 'y', # <= these are the `args`
367
+ exe: ['/usr/bin/env', 'blah'],
368
+ cmd: 'do-stuff',
369
+ opts: {
370
+ really: true,
371
+ 'some-setting': 'dat-value',
372
+ }
373
+ # => "/usr/bin/env,blah do-stuff --really --some-setting=dat-value x,y"
374
+ ```
375
+
376
+ Which is probably *not* what you were going for... it would produce an `ARGV` something like:
377
+
378
+ ```ruby
379
+ [
380
+ '/usr/bin/env,blah',
381
+ 'do-stuff',
382
+ '--really',
383
+ '--some-setting=dat-value',
384
+ 'x,y',
385
+ ]
386
+ ```
319
387
 
320
- **positional**
388
+ You can of course use "splatting" together with slicing or mapping or whatever.
321
389
 
322
- `%s` is replaced with `<%= arg %>`.
390
+ ******************************************************************************
323
391
 
324
- so
325
392
 
326
- ```
327
- Cmds.sub "./test/echo_cmd.rb %s", ["hello world!"]
328
- ```
393
+ ##### Logic #####
329
394
 
330
- is the same as
395
+ All of ERB is available to you. I've tried to put in features and options that make it largely unnecessary, but if you've got a weird or complicated case, or if you just like the HTML/Rails-esque templating style, it's there for you:
331
396
 
332
- ```
333
- Cmds "./test/echo_cmd.rb <%= arg %>", ["hello world!"]
397
+ ```ruby
398
+ cmd = Cmds.new <<-END
399
+ <% if use_bin_env %>
400
+ /usr/bin/env
401
+ <% end %>
402
+
403
+ docker build .
404
+ -t <%= tag %>
405
+
406
+ <% if file %>
407
+ --file <%= file %>
408
+ <% end %>
409
+
410
+ <% build_args.each do |key, value| %>
411
+ --build-arg <%= key %>=<%= value %>
412
+ <% end %>
413
+
414
+ <% if yarn_cache %>
415
+ --build-arg yarn_cache_file=<%= yarn_cache_file %>
416
+ <% end %>
417
+ END
418
+
419
+ cmd.prepare(
420
+ use_bin_env: true,
421
+ tag: 'nrser/blah:latest',
422
+ file: './prod.Dockerfile',
423
+ build_args: {
424
+ yarn_version: '1.3.2',
425
+ },
426
+ yarn_cache: true,
427
+ yarn_cache_file: './yarn-cache.tgz',
428
+ )
429
+ # => "/usr/bin/env docker build . -t nrser/blah:latest
430
+ # --file ./prod.Dockerfile --build-arg yarn_version=1.3.2
431
+ # --build-arg yarn_cache_file=./yarn-cache.tgz"
432
+ # (Line-breaks added for readability; output is one line)
334
433
  ```
335
434
 
336
- **keyword**
435
+ ******************************************************************************
337
436
 
338
- `%{key}` and `%<key>s` are replaced with `<%= key %>`, and `%{key?}` and `%<key?>s` are replaced with `<%= key? %>` for optional keywords.
339
437
 
340
- so
438
+ #### `printf`-Style Short-Hand (`%s`, `%{key}`, `%<key>s`)
341
439
 
342
- ```
343
- Cmds "./test/echo_cmd.rb %{key}", key: "hello world!"
344
- ```
440
+ Cmds also supports a [printf][]-style short-hand. Sort-of.
345
441
 
346
- and
442
+ [printf]: https://en.wikipedia.org/wiki/Printf_format_string
347
443
 
348
- ```
349
- Cmds "./test/echo_cmd.rb %<key>s", key: "hello world!"
350
- ```
444
+ It's a clumsy hack from when I was first writing this library, and I've pretty moved to using the ERB-style, but there are still some examples that use it, and I guess it still works (to whatever extent it ever really did), so it's probably good to mention it.
351
445
 
352
- are the same is
446
+ It pretty much just replaces some special patterns with their ERB-equivalent via the {Cmds.replace_shortcuts} method before moving on to ERB processing:
353
447
 
354
- ```
355
- Cmds "./test/echo_cmd.rb <%= key %>", key: "hello world!"
356
- ```
448
+ 1. `%s` => `<%= arg %>`
449
+ 2. `%{key}` => `<%= key %>`
450
+ 3. `%{key?}` => `<%= key? %>`
451
+ 4. `%<key>s` => `<%= key %>`
452
+ 5. `%<key?>s` => `<%= key? %>`
357
453
 
358
- **escaping**
454
+ And the escaping versions, where you can put anothe `%` in front to get the literal intead of the subsitution:
359
455
 
360
- strings that would be replaced as shortcuts can be escaped by adding one more `%` to the front of them:
456
+ 1. `%%s` => `%s`
457
+ 2. `%%{key}` => `%{key}`
458
+ 3. `%%{key?}` => `%{key?}`
459
+ 4. `%%<key>s` => `%<key>s`
460
+ 5. `%%<key?>s` => `%<key?>s`
361
461
 
362
- ```
363
- Cmds.sub "%%s" # => "%s"
364
- Cmds.sub "%%%<key>s" # => "%%<key>s"
365
- ```
462
+ That's it. No `printf` formatting beyond besides `s` (string).
366
463
 
367
- note that unlike `sprintf`, which has a much more general syntax, this is only necessary for patterns that exactly match a shortcut, not `%` in general:
368
464
 
369
- ```
370
- Cmds.sub "50%" # => "50%"
371
- ```
465
+ -----------------------------------------------------------------------------
466
+ Old docs I haven't cleaned up yet...
467
+ -----------------------------------------------------------------------------
372
468
 
469
+ ### execution
470
+
471
+ you can provide three types of arguments when executing a command:
373
472
 
473
+ 1. positional arguments for substitution
474
+ 2. keyword arguments for substitution
475
+ 3. input to stdin
374
476
 
375
- ## reuse commands
477
+ all `Cmds` instance execution methods have the same form for accepting these:
478
+
479
+ 1. positional arguments are provided in an optional array that must be the first argument:
480
+
481
+ `Cmds "cp <%= arg %> <%= arg %>", [src_path, dest_path]`
482
+
483
+ note that the arguments need to be enclosed in square braces. Cmds does **NOT** use \*splat for positional arguments because it would make a `Hash` final parameter ambiguous.
484
+
485
+ 2. keyword arguments are provided as optional hash that must be the last argument:
486
+
487
+ `Cmds "cp <%= src %> <%= dest %>", src: src_path, dest: dest_path`
488
+
489
+ in this case, curly braces are not required since Ruby turns the trailing keywords into a `Hash` provided as the last argument (or second-to-last argument in the case of a block included in the method signature).
490
+
491
+ 3. input and output is handled with blocks:
492
+
493
+ `Cmds(“wc -l”){ “one\ntwo\nthree\n” }
494
+
495
+ Cmds.stream './test/tick.rb <%= times %>', times: times do |io|
496
+ io.on_out do |line|
497
+ # do something with the output line
498
+ end
499
+
500
+ io.on_err do |line|
501
+ # do something with the error line
502
+ end
503
+ end`
504
+
505
+
506
+ ### Reuse Commands
376
507
 
377
508
  ```
378
509
  playbook = Cmds.new "ansible-playbook -i %{inventory} %{playbook}"
@@ -393,8 +524,7 @@ prod_playbook.call playbook: "setup.yml"
393
524
  ```
394
525
 
395
526
 
396
-
397
- ## defaults
527
+ ### defaults
398
528
 
399
529
  NEEDS TEST
400
530
 
@@ -411,22 +541,20 @@ prod_playbook.call playbook: "setup.yml", inventory: "inventory/prod"
411
541
  ```
412
542
 
413
543
 
414
-
415
- ## input
544
+ ### input
416
545
 
417
546
  ```
418
547
  c = Cmds.new("wc", input: "blah blah blah).call
419
548
  ```
420
549
 
421
550
 
551
+ ### future..?
422
552
 
423
- ## future..?
424
-
425
- ### exec
553
+ #### exec
426
554
 
427
555
  want to be able to use to exec commands
428
556
 
429
- ### formatters
557
+ #### formatters
430
558
 
431
559
  kinda like `sprintf` formatters or string escape helpers in Rails, they would be exposed as functions in ERB and as format characters in the shorthand versions:
432
560