rexe 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +233 -129
  4. data/exe/rexe +264 -224
  5. data/rexe.gemspec +2 -7
  6. metadata +13 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38c1bab41a36c75950db8a2a8aa05f46d324f3c221b66181cc8df302b97aabe7
4
- data.tar.gz: 6a3e33b62857804eb18179233aa3de385ee7b44785dbb1fdd27de4a95e9aa0bc
3
+ metadata.gz: 982527b8b9d991102d3a3a1eb5ae83e7235cea0b77b782921ab6e203a5c70f42
4
+ data.tar.gz: a586d1f5c68beaf81692f23920e1c2e8c435e44e7095b38e90a70f4b113fa32c
5
5
  SHA512:
6
- metadata.gz: 52d97220ae00afd76ecb7253f626c135be6f33622130cf43edcabb90bb63f93b8ee3e143734059a6f69a80d57800c29d47adc4c8569c7b08e54676853a6eb36a
7
- data.tar.gz: ea666de41864df7f84315e69e4481fd1a1b79bf4c32da29b1e76a4483a4a3276a99e494ecf47850e124170ac43ba00cb5c8e10d81421620d3f2b27bbe5c3af21
6
+ metadata.gz: ffb97f15aae0ed61a2acabdfa2d330d15814d2c4ada682c1eae0099d8b5dd9262d1fa7e47142051d18ca5f5efddee2ef755efeb9b22d555bf2621327305e572c
7
+ data.tar.gz: ce5ae9551e7fad281a7505bb99c4749098eb9560fd377dc33ea3341ae29e5d4d490bfea807b426e36bf79f7cfbc597416da2cf50ab475343854908d49d131708
data/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  ## rexe -- Ruby Command Line Executor
2
2
 
3
3
 
4
+ ### 0.14.0
5
+
6
+ * The default output format has been changed from -op (:puts) to -on (:none).
7
+ * Support automatic input from file with -f option.
8
+ * Normalize load filespecs to eliminate duplication and to facilitate correct deletion.
9
+
10
+
4
11
  ### v0.13.0
5
12
 
6
13
  * Much refactoring.
data/README.md CHANGED
@@ -8,13 +8,13 @@ and the source code, and at minimum, reading the Conclusion.]_
8
8
 
9
9
  ----
10
10
 
11
- I love the power of the command line, but not the awkwardness of shell scripting
12
- languages. Sure, there's a lot that can be done with them, but it doesn't take
13
- long before I get frustrated with their bluntness and verbosity.
14
-
11
+ Shell scripting is great for simple tasks but for anything nontrivial
12
+ it can easily get cryptic and awkward (pun intended!).
13
+
15
14
  Often, I solve this problem by writing a Ruby script instead. Ruby gives me fine
16
- grained control in a "real" programming language with which I am comfortable.
17
- However, when there are multiple OS commands to be called, then Ruby can be
15
+ grained control in a language that is all about clarity, conciseness, and expressiveness.
16
+
17
+ Unfortunately, when there are multiple OS commands to be called, then Ruby can be
18
18
  awkward too.
19
19
 
20
20
  ### Using the Ruby Interpreter on the Command Line
@@ -50,6 +50,12 @@ The previous `ruby` command can be expressed in `rexe` as:
50
50
  ```
51
51
  ➜ ~  echo $EUR_RATES_JSON | rexe -mb -ij -oy self
52
52
  ```
53
+
54
+ The command options may seem cryptic, but they're logical so it shouldn't take long to learn them:
55
+
56
+ * `-mb` - __mode__ to consume all standard input as a single __big__ string
57
+ * `-ij` - parse that __input__ with __JSON__; `self` will be the parsed object
58
+ * `-oy` - __output__ the final value as __YAML__
53
59
 
54
60
  `rexe` is at https://github.com/keithrbennett/rexe and can be installed with
55
61
  `gem install rexe`. `rexe` provides several ways to simplify Ruby on the command
@@ -60,21 +66,24 @@ line, tipping the scale so that it is practical to do it more often.
60
66
  Here is `rexe`'s help text as of the time of this writing:
61
67
 
62
68
  ```
63
- rexe -- Ruby Command Line Executor/Filter -- v0.13.0 -- https://github.com/keithrbennett/rexe
69
+ rexe -- Ruby Command Line Executor/Filter -- v0.14.0 -- https://github.com/keithrbennett/rexe
64
70
 
65
- Executes Ruby code on the command line, optionally taking standard input and writing to standard output.
71
+ Executes Ruby code on the command line, optionally automating management of standard input
72
+ and standard output, and optionally parsing input and formatting output with YAML, JSON, etc.
66
73
 
67
74
  rexe [options] 'Ruby source code'
68
75
 
69
76
  Options:
70
77
 
71
78
  -c --clear_options Clear all previous command line options specified up to now
79
+ -f --input_file Use this file instead of stdin; autodetects YAML and JSON file extensions
80
+ If YAML or JSON: parses file in that mode, sets input mode to -mb
72
81
  -g --log_format FORMAT Log format, logs to stderr, defaults to none (see -o for format options)
73
82
  -h, --help Print help and exit
74
- -i, --input_format FORMAT Input format (defaults to none)
83
+ -i, --input_format FORMAT Input format
75
84
  -ij JSON
76
85
  -im Marshal
77
- -in None
86
+ -in None (default)
78
87
  -iy YAML
79
88
  -l, --load RUBY_FILE(S) Ruby file(s) to load, comma separated;
80
89
  ! to clear all, or precede a name with '-' to remove
@@ -90,8 +99,8 @@ Options:
90
99
  -oj JSON
91
100
  -oJ Pretty JSON
92
101
  -om Marshal
93
- -on No Output
94
- -op Puts (default)
102
+ -on No Output (default)
103
+ -op Puts
95
104
  -os to_s
96
105
  -oy YAML
97
106
  -r, --require REQUIRE(S) Gems and built-in libraries to require, comma separated;
@@ -118,11 +127,24 @@ There are two main ways we can make the `rexe` command line even more concise:
118
127
  The `REXE_OPTIONS` environment variable can contain command line options that would otherwise
119
128
  be specified on the `rexe` command line:
120
129
 
130
+ Instead of this:
131
+
121
132
  ```
122
- ➜ ~  export REXE_OPTIONS="-r json,awesome_print"
123
- ➜ ~  echo $EUR_RATES_JSON | rexe 'ap JSON.parse(STDIN.read)'
133
+ ➜ ~  rexe -r wifi-wand -oa WifiWand::MacOsModel.new.wifi_info
124
134
  ```
125
135
 
136
+ you can do this:
137
+
138
+ ```
139
+ ➜ ~  export REXE_OPTIONS="-r wifi-wand -oa"
140
+ ➜ ~  rexe WifiWand::MacOsModel.new.wifi_info
141
+ ➜ ~  # [more rexe commands with the same options]
142
+ ```
143
+
144
+ Putting configuration options in `REXE_OPTIONS` effectively creates custom defaults,
145
+ and is useful when you use options in most or all of your commands. Any options specified on the `rexe`
146
+ command line will override the environment variable options.
147
+
126
148
  Like any environment variable, `REXE_OPTIONS` could also be set in your startup script, input on a command line using `export`, or in another script loaded with `source` or `.`.
127
149
 
128
150
  ### Loading Files
@@ -155,14 +177,14 @@ You might be thinking that creating an alias or a minimal shell script for this
155
177
  approach, and I would agree with you. However, over time the number of these could become unmanageable, whereas using Ruby
156
178
  you could build a pretty extensive and well organized library of functionality. Moreover, that functionality could be made available to _all_ your Ruby code (for example, by putting it in a gem), and not just command line one liners.
157
179
 
158
- For example, you could have something like this in a configuration file:
180
+ For example, you could have something like this in a gem or loaded file:
159
181
 
160
182
  ```
161
183
  def play(piece_code)
162
184
  pieces = {
163
185
  hallelujah: "https://www.youtube.com/watch?v=IUZEtVbJT5c&t=0m20s",
164
186
  valkyries: "http://www.youtube.com/watch?v=P73Z6291Pt8&t=0m28s",
165
- wm_tell: "https://www.youtube.com/watch?v=j3T8-aeOrbg"
187
+ wm_tell: "https://www.youtube.com/watch?v=j3T8-aeOrbg&t=0m1s",
166
188
  # ... and many, many more
167
189
  }
168
190
  `open #{Shellwords.escape(pieces.fetch(piece_code))}`
@@ -178,6 +200,8 @@ end
178
200
  (You need to quote the `play` call because otherwise the shell will process and remove the parentheses.
179
201
  Alternatively you could escape the parentheses with backslashes.)
180
202
 
203
+ One of the examples at the end of this articles shows how you could have different music play for success and failure.
204
+
181
205
 
182
206
  ### Logging
183
207
 
@@ -187,12 +211,12 @@ to be evaluated, options (after parsing both the `REXE_OPTIONS` environment vari
187
211
  and the execution time of your Ruby code:
188
212
 
189
213
  ```
190
- ➜ ~  echo $EUR_RATES_JSON | rexe -gy -rjson,awesome_print "ap JSON.parse(STDIN.read)"
214
+ ➜ ~  echo $EUR_RATES_JSON | rexe -gy -ij -oa self
191
215
  ...
192
216
  ---
193
217
  :count: 0
194
- :rexe_version: 0.12.0
195
- :start_time: '2019-03-19T19:39:18+08:00'
218
+ :rexe_version: 0.13.0
219
+ :start_time: '2019-03-21T20:58:51+08:00'
196
220
  :source_code: ap JSON.parse(STDIN.read)
197
221
  :options:
198
222
  :input_format: :none
@@ -200,25 +224,26 @@ and the execution time of your Ruby code:
200
224
  :loads: []
201
225
  :output_format: :puts
202
226
  :requires:
203
- - json
204
227
  - awesome_print
228
+ - json
205
229
  - yaml
206
230
  :log_format: :yaml
207
231
  :noop: false
208
- :duration_secs: 0.116171
232
+ :duration_secs: 0.070381
209
233
  ```
210
234
 
211
- The `yaml` require was not explicitly specified but is automatically added because
212
- `rexe` will add any requires needed for automatic parsing and formatting.
235
+ We specified `-gy` for YAML format; there are other formats as well (see the help output or this document)
236
+ and the default is `-gn`, which means don't output the log entry at all.
237
+
238
+ The requires you see were not explicitly specified but were automatically added because
239
+ `rexe` will add any requires needed for automatic parsing and formatting, and
240
+ we specified those formats in the command line options `-gy -ij -oa`.
213
241
 
214
242
  This extra output is sent to standard error (_stderr_) instead of standard output
215
243
  (_stdout_) so that it will not pollute the "real" data when stdout is piped to
216
244
  another command.
217
245
 
218
- As you can see, the data is the YAML representation of a hash, so you could easily ingest it and
219
- use it programatically.
220
-
221
- If you would like to append this informational output to a file, you could do something like this:
246
+ If you would like to append this informational output to a file(e.g. `rexe.log`), you could do something like this:
222
247
 
223
248
  ```
224
249
  ➜ ~  rexe ... -gy 2>>rexe.log
@@ -264,7 +289,7 @@ eybdoog
264
289
 
265
290
  Be aware that, in this mode, although you can control the _content_ of output records,
266
291
  there is no way to selectively _exclude_ records from being output. Even if the result of the code
267
- is nil or the empty string, a newline will be output. If this is an issue, you could do one of the following:
292
+ is nil or the empty string, a newline will be output. To prevent this, you can do one of the following:
268
293
 
269
294
  * use `-me` Enumerator mode and call `select`, `filter`, `reject`, etc.
270
295
  * use the `-on` _no output_ mode and call `puts` explicitly for the output you _do_ want
@@ -281,7 +306,7 @@ Here is an example of using `-me` to add line numbers to the first 3
281
306
  files in the directory listing:
282
307
 
283
308
  ```
284
- ➜ ~  ls / | rexe -me "first(3).each_with_index { |ln,i| puts '%5d %s' % [i, ln] }; nil"
309
+ ➜ ~  ls / | rexe -me -on "first(3).each_with_index { |ln,i| puts '%5d %s' % [i, ln] }"
285
310
 
286
311
  0 AndroidStudioProjects
287
312
  1 Applications
@@ -289,6 +314,7 @@ files in the directory listing:
289
314
  ```
290
315
 
291
316
  Since `self` is an enumerable, we can call `first` and then `each_with_index`.
317
+ The `-on` says don't do any automatic output, just the output explicitly specified by `puts` in the source code.
292
318
 
293
319
 
294
320
  #### -mb "Big String" Filter Mode
@@ -296,7 +322,7 @@ Since `self` is an enumerable, we can call `first` and then `each_with_index`.
296
322
  In this mode, all standard input is combined into a single (possibly
297
323
  large) string, with newline characters joining the lines in the string.
298
324
 
299
- A good example of when you would use this is when you parse JSON or YAML text;
325
+ A good example of when you would use this is when you need to parse a multiline JSON or YAML representation of an object;
300
326
  you need to pass the entire (probably) multiline string to the parse method.
301
327
  This is the mode that was used in the first `rexe` example in this article.
302
328
 
@@ -362,16 +388,75 @@ could be included in the custom code instead. Here's why:
362
388
  parameterization of the output format.
363
389
 
364
390
 
391
+ ### Reading Input from a File
392
+
393
+ `rexe` also simplifies getting input from a file rather than standard input. The `-f` option takes a filespec
394
+ and does with exactly what it would have done with standard input. This shortens:
395
+
396
+ ```
397
+ ➜ ~  cat filename.ext | rexe ...
398
+ ```
399
+ ...to...
400
+
401
+ ```
402
+ ➜ ~  rexe -f filename.ext ...
403
+ ```
404
+
405
+ This becomes even more useful if you are using files whose extensions are `.yml`, `.yaml`, or `.json` (case insensitively).
406
+ In this case the input format and mode will be set automatically for you to:
407
+
408
+ * `-iy` (YAML) or `-ij` (JSON) depending on the file extension
409
+ * `-mb` (one big string mode), which assumes that the most common use case will be to parse the entire file at once
410
+
411
+ So the example we gave above:
412
+
413
+ ```
414
+ ➜ ~  export EUR_RATES_JSON=`curl https://api.exchangeratesapi.io/latest`
415
+ ➜ ~  echo $EUR_RATES_JSON | rexe -mb -ij -oy self
416
+ ```
417
+ ...could be changed to:
418
+
419
+ ```
420
+ ➜ ~  curl https://api.exchangeratesapi.io/latest > eur_rates.json
421
+ ➜ ~  rexe -f eur_rates.json -oy self
422
+ ```
423
+
424
+ Another possible win for using `-f` is that since it is a command line option, it could be specified in `REXE_OPTIONS`.
425
+ This could be useful if you are doing many operations on the same file.
426
+
365
427
  ### The $RC Global OpenStruct
366
428
 
367
429
  For your convenience, the information displayed in verbose mode is available to your code at runtime
368
- by accessing the `$RC` global variable, which contains an OpenStruct. Probably most useful in that object
430
+ by accessing the `$RC` global variable, which contains an OpenStruct. Let's print out its contents using AwesomePrint:
431
+
432
+ ```
433
+ ➜ ~  rexe -oa '$RC'
434
+ OpenStruct {
435
+ :count => 0,
436
+ :rexe_version => "0.13.0",
437
+ :start_time => "2019-03-23T20:17:59+08:00",
438
+ :source_code => "$RC",
439
+ :options => {
440
+ :input_format => :none,
441
+ :input_mode => :no_input,
442
+ :loads => [],
443
+ :output_format => :awesome_print,
444
+ :requires => [
445
+ [0] "awesome_print"
446
+ ],
447
+ :log_format => :none,
448
+ :noop => false
449
+ }
450
+ }
451
+ ```
452
+
453
+ Probably most useful in that object
369
454
  is the record count, accessible with both `$RC.count` and `$RC.i`.
370
455
  This is only really useful in line mode, because in the others
371
456
  it will always be 0 or 1. Here is an example of how you might use it as a kind of progress indicator:
372
457
 
373
458
  ```
374
- ➜ ~  ➜ ~  find / | rexe -ml -on \
459
+ ➜ ~  find / | rexe -ml -on \
375
460
  'if $RC.i % 1000 == 0; puts %Q{File entry ##{$RC.i} is #{self}}; end'
376
461
  ...
377
462
  File entry #106000 is /usr/local/Cellar/go/1.11.5/libexec/src/cmd/vendor/github.com/google/pprof/internal/driver/driver_test.go
@@ -404,10 +489,10 @@ trivially easily. Just to illustrate, here's how you would open a REPL on the Fi
404
489
  ➜ ~  rexe -r pry File.pry
405
490
  ```
406
491
 
407
- `self` would evaluate to the `File` class, so you could call class methods implicitly using only their names:
492
+ `self` would evaluate to the `File` class, so you could call class methods using only their names:
408
493
 
409
494
  ```
410
- ➜ ~  rexe -r pry File.pry
495
+ ➜ ~  rexe -r pry File.pry
411
496
 
412
497
  [6] pry(File)> size '/etc/passwd'
413
498
  6804
@@ -420,7 +505,7 @@ true
420
505
  This could be really handy if you call `pry` on a custom object that has methods especially suited to your task:
421
506
 
422
507
  ```
423
- ➜ ~  rexe -r wifi-wand,pry WifiWand::MacOsModel.new.pry
508
+ ➜ ~  rexe -r wifi-wand,pry WifiWand::MacOsModel.new.pry
424
509
  ```
425
510
 
426
511
  Ruby is supremely well suited for DSL's since it does not require parentheses for method calls,
@@ -435,8 +520,7 @@ necessary to quote the Ruby code. You can use single or double quotes to have th
435
520
  as a single argument.
436
521
  An excellent reference for how they differ is [here](https://stackoverflow.com/questions/6697753/difference-between-single-and-double-quotes-in-bash).
437
522
 
438
- Personally, I find single quotes more useful since special characters like `$` in my Ruby code will
439
- not be disturbed.
523
+ Personally, I find single quotes more useful since I usually don't want special characters in my Ruby code like `$` to be processed by the shell.
440
524
 
441
525
  When specifying the Ruby code, feel free to fall back on Ruby's super useful `%q{}` and `%Q{}`,
442
526
  equivalent to single and double quotes, respectively.
@@ -520,20 +604,7 @@ when you change the content of the clipboard.
520
604
  Although `rexe` is cleanest with short one liners, you may want to use it to include nontrivial Ruby code
521
605
  in your shell script as well. If you do this, you may need to add trailing backslashes to the lines of Ruby code.
522
606
 
523
- In addition, don't forget you can use `%q{}` and `%Q{}` in your Ruby code instead of single and double quotes.
524
-
525
-
526
- ### The Use of Semicolons
527
-
528
- You will probably find yourself using semicolons much more often than usual when you use `rexe`.
529
- Obviously you would need them to separate statements on the same line:
530
-
531
- ```
532
- ➜ ~  cowsay hello | rexe -me "print %Q{\u001b[33m}; puts to_a"
533
- ```
534
-
535
- What might not be so obvious is that you _also_ need them if each statement is on its own line.
536
- For example, here is an example without a semicolon:
607
+ What might not be so obvious is that you will often need to use semicolons. For example, here is an example without a semicolon:
537
608
 
538
609
  ```
539
610
  ➜ ~  cowsay hello | rexe -me "print %Q{\u001b[33m} \
@@ -605,17 +676,15 @@ Files loaded with the `-l` option are treated the same way.
605
676
 
606
677
  ### Beware of Configured Requires
607
678
 
608
- Requiring gems and modules for _all_ invocations of `rexe` will make your commands simpler and more concise, but will be a waste of execution time if they are not needed. You can inspect the execution times to see just how much time is being wasted. For example, we can find out that nokogiri takes about 0.15 seconds to load on my laptop by observing and comparing the execution times with and without the require (output has been abbreviated using the redirection and grep):
679
+ Requiring gems and modules for _all_ invocations of `rexe` will make your commands simpler and more concise, but will be a waste of execution time if they are not needed. You can inspect the execution times to see just how much time is being wasted. For example, we can find out that rails takes about 0.63 seconds to load on my laptop by observing and comparing the execution times with and without the require (output has been abbreviated using the redirection and grep):
609
680
 
610
681
  ```
611
- ➜ ~  rexe -gy 2>&1 "''" | grep duration
612
- :duration_secs: 0.0012
613
-
614
- ➜ ~  rexe -gy -r nokogiri "''" 2>&1 | grep duration
615
- :duration_secs: 0.148671
682
+ ➜ ~  rexe -gy -r rails 123 2>&1 | grep duration
683
+ :duration_secs: 0.660138
684
+ ➜ ~  rexe -gy 123 2>&1 | grep duration
685
+ :duration_secs: 0.027781
616
686
  ```
617
-
618
- (For the above to work, the `nokogiri` gem needs to be installed.)
687
+ (For the above to work, the `rails` gem and its dependencies need to be installed.)
619
688
 
620
689
 
621
690
  ### Operating System Support
@@ -630,7 +699,33 @@ Windows non-Unix shells.
630
699
  Here are some more examples to illustrate the use of `rexe`.
631
700
 
632
701
  ----
633
- Output the contents of `ENV` using AwesomePrint:
702
+
703
+ ### Format Conversions -- JSON, YAML, AwesomePrint
704
+
705
+ You may work with YAML or JSON a lot and need to inspect files from time to time.
706
+ `rexe` makes it easy to convert from one format to another. For example, here's a
707
+ command to convert from `countries.yaml` to AwesomePrint:
708
+
709
+ ```
710
+ cat countries.yaml | rexe -iy -oa -mb self
711
+ ```
712
+
713
+ You can easily create a script that automates this. Using your favorite editor,
714
+ put this in a file:
715
+
716
+ ```
717
+ cat $1 | rexe -iy -oa -mb self
718
+ ```
719
+
720
+ I put mine in a file called `y2a` in my `~/bin` directory where I keep custom scripts like this.
721
+
722
+ Now you can call `y2a` to output any YAML file using AwesomePrint.
723
+
724
+ ----
725
+
726
+ #### Outputting ENV
727
+
728
+ Output the contents of `ENV` using AwesomePrint [^3]:
634
729
 
635
730
  ```
636
731
  ➜ ~  rexe -oa ENV
@@ -645,6 +740,8 @@ Output the contents of `ENV` using AwesomePrint:
645
740
 
646
741
  ----
647
742
 
743
+ #### Reformatting a Command's Output
744
+
648
745
  Show disk space used/free on a Mac's main hard drive's main partition:
649
746
 
650
747
  ```
@@ -657,7 +754,23 @@ Show disk space used/free on a Mac's main hard drive's main partition:
657
754
 
658
755
  ----
659
756
 
660
- Print yellow (trust me!):
757
+ #### Formatting for Numeric Sort
758
+
759
+ Show the 3 longest file names of the current directory, with their lengths, in descending order:
760
+
761
+ ```
762
+ ➜ ~  ls | rexe -ml "%Q{[%4d] %s} % [length, self]" | sort -r | head -3
763
+ [ 50] Agoda_Booking_ID_9999999 49_–_RECEIPT_enclosed.pdf
764
+ [ 40] 679a5c034994544aab4635ecbd50ab73-big.jpg
765
+ [ 28] 2018-abc-2019-01-16-2340.zip
766
+ ```
767
+
768
+ When you right align numbers using printf formatting, sorting the lines
769
+ alphabetically will result in sorting them numerically as well.
770
+
771
+ ----
772
+
773
+ #### Print yellow (trust me!):
661
774
 
662
775
  ```
663
776
  ➜ ~  cowsay hello | rexe -me "print %Q{\u001b[33m}; puts to_a"
@@ -678,75 +791,9 @@ Print yellow (trust me!):
678
791
 
679
792
  ----
680
793
 
681
-
682
- Show the 3 longest file names of the current directory, with their lengths, in descending order:
683
-
684
- ```
685
- ➜ ~  ls | rexe -ml "%Q{[%4d] %s} % [length, self]" | sort -r | head -3
686
- [ 50] Agoda_Booking_ID_9999999 49_–_RECEIPT_enclosed.pdf
687
- [ 40] 679a5c034994544aab4635ecbd50ab73-big.jpg
688
- [ 28] 2018-abc-2019-01-16-2340.zip
689
- ```
690
-
691
- Notice that when you right align numbers using printf formatting, sorting the lines
692
- alphabetically will result in sorting them numerically as well.
693
-
694
794
  ----
695
795
 
696
- I was recently asked to provide a schema for the data in my `rock_books` accounting gem. `rock_books` data is intended to be very small in size, and no data base is used. Instead, the input data is parsed on every run, and reports generated on demand. However, there are data structures (actually class instances) in memory at runtime, and their classes inherit from `Struct`.
697
- The definition lines look like this one:
698
-
699
- ```
700
- class JournalEntry < Struct.new(:date, :acct_amounts, :doc_short_name, :description, :receipts)
701
- ```
702
-
703
- The `grep` command line utility prepends each of these matches with a string like this:
704
-
705
- ```
706
- lib/rock_books/documents/journal_entry.rb:
707
- ```
708
-
709
- So this is what worked well for me:
710
-
711
- ```
712
- ➜ ~  grep Struct **/*.rb | grep -v OpenStruct | rexe -ml \
713
- "a = \
714
- gsub('lib/rock_books/', '')\
715
- .gsub('< Struct.new', '')\
716
- .gsub('; end', '')\
717
- .split('.rb:')\
718
- .map(&:strip);\
719
- \
720
- %q{%-40s %-s} % [a[0] + %q{.rb}, a[1]]"
721
- ```
722
-
723
- ...which produced this output:
724
-
725
- ```
726
- cmd_line/command_line_interface.rb class Command (:min_string, :max_string, :action)
727
- documents/book_set.rb class BookSet (:run_options, :chart_of_accounts, :journals)
728
- documents/journal.rb class Entry (:date, :amount, :acct_amounts, :description)
729
- documents/journal_entry.rb class JournalEntry (:date, :acct_amounts, :doc_short_name, :description, :receipts)
730
- documents/journal_entry_builder.rb class JournalEntryBuilder (:journal_entry_context)
731
- reports/report_context.rb class ReportContext (:chart_of_accounts, :journals, :page_width)
732
- types/account.rb class Account (:code, :type, :name)
733
- types/account_type.rb class AccountType (:symbol, :singular_name, :plural_name)
734
- types/acct_amount.rb class AcctAmount (:date, :code, :amount, :journal_entry_context)
735
- types/journal_entry_context.rb class JournalEntryContext (:journal, :linenum, :line)
736
- ```
737
-
738
- Although there's a lot going on here, the vertical and horizontal alignments and spacing make the code
739
- straightforward to follow. Here's what it does:
740
-
741
- * grep the code base for `"Struct"`
742
- * exclude references to `"OpenStruct"` with `grep -v`
743
- * remove unwanted text with `gsub`
744
- * split the line into 1) a filespec relative to `lib/rockbooks`, and 2) the class definition
745
- * strip unwanted space because that will mess up the horizontal alignment of the output.
746
- * use C-style printf formatting to align the text into two columns
747
-
748
-
749
- ----
796
+ #### More YouTube: Differentiating Success and Failure
750
797
 
751
798
  Let's go a little crazy with the YouTube example.
752
799
  Let's have the video that loads be different for the success or failure
@@ -792,7 +839,9 @@ Then when I issue a command that succeeds, the Hallelujah Chorus is played:
792
839
 
793
840
  ----
794
841
 
795
- Another formatting example...I wanted to reformat this help text...
842
+ #### Reformatting Source Code for Help Text
843
+
844
+ Another formatting example...I wanted to reformat this source code...
796
845
 
797
846
  ```
798
847
  'i' => Inspect
@@ -820,7 +869,61 @@ but it was an interesting exercise and made it easy to try different formats. He
820
869
  ```
821
870
 
822
871
 
872
+ #### Reformatting Grep Output
823
873
 
874
+ I was recently asked to provide a schema for the data in my `rock_books` accounting gem. `rock_books` data is intended to be very small in size, and no data base is used. Instead, the input data is parsed on every run, and reports generated on demand. However, there are data structures (actually class instances) in memory at runtime, and their classes inherit from `Struct`.
875
+ The definition lines look like this one:
876
+
877
+ ```
878
+ class JournalEntry < Struct.new(:date, :acct_amounts, :doc_short_name, :description, :receipts)
879
+ ```
880
+
881
+ The `grep` command line utility prepends each of these matches with a string like this:
882
+
883
+ ```
884
+ lib/rock_books/documents/journal_entry.rb:
885
+ ```
886
+
887
+ So this is what worked well for me:
888
+
889
+ ```
890
+ ➜ ~  grep Struct **/*.rb | grep -v OpenStruct | rexe -ml \
891
+ "a = \
892
+ gsub('lib/rock_books/', '')\
893
+ .gsub('< Struct.new', '')\
894
+ .gsub('; end', '')\
895
+ .split('.rb:')\
896
+ .map(&:strip);\
897
+ \
898
+ %q{%-40s %-s} % [a[0] + %q{.rb}, a[1]]"
899
+ ```
900
+
901
+ ...which produced this output:
902
+
903
+ ```
904
+ cmd_line/command_line_interface.rb class Command (:min_string, :max_string, :action)
905
+ documents/book_set.rb class BookSet (:run_options, :chart_of_accounts, :journals)
906
+ documents/journal.rb class Entry (:date, :amount, :acct_amounts, :description)
907
+ documents/journal_entry.rb class JournalEntry (:date, :acct_amounts, :doc_short_name, :description, :receipts)
908
+ documents/journal_entry_builder.rb class JournalEntryBuilder (:journal_entry_context)
909
+ reports/report_context.rb class ReportContext (:chart_of_accounts, :journals, :page_width)
910
+ types/account.rb class Account (:code, :type, :name)
911
+ types/account_type.rb class AccountType (:symbol, :singular_name, :plural_name)
912
+ types/acct_amount.rb class AcctAmount (:date, :code, :amount, :journal_entry_context)
913
+ types/journal_entry_context.rb class JournalEntryContext (:journal, :linenum, :line)
914
+ ```
915
+
916
+ Although there's a lot going on here, the vertical and horizontal alignments and spacing make the code
917
+ straightforward to follow. Here's what it does:
918
+
919
+ * grep the code base for `"Struct"`
920
+ * exclude references to `"OpenStruct"` with `grep -v`
921
+ * remove unwanted text with `gsub`
922
+ * split the line into 1) a filespec relative to `lib/rockbooks`, and 2) the class definition
923
+ * strip unwanted space because that will mess up the horizontal alignment of the output.
924
+ * use C-style printf formatting to align the text into two columns
925
+
926
+
824
927
  ### Conclusion
825
928
 
826
929
  `rexe` is not revolutionary technology, it's just plumbing that removes parsing,
@@ -873,3 +976,4 @@ Here is a _start_ at a method that opens a resource portably across operating sy
873
976
  end
874
977
  ```
875
978
 
979
+ [^3]: It is an interesting quirk of the Ruby language that `ENV.to_s` returns `"ENV"` and not the contents of the `ENV` object. As a result, most of the other output formats will return some form of `"ENV"`. You can handle this by specifying `ENV.to_h`.