UNPKG

22.8 kBMarkdownView Raw
1# ramda-cli [![npm version](https://badge.fury.io/js/ramda-cli.svg)](https://www.npmjs.com/package/ramda-cli) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
2
3A command-line tool for processing data with functional pipelines.
4
5```sh
6$ npm install -g ramda-cli; alias R=ramda
7$ curl -L http://bit.do/people-json | R \
8 'filter (p) -> p.city is /Port/ or p.name is /^Dr\./' \
9 'project <[ name city mac ]>' \
10 'take 3' \
11 -o table --compact
12┌──────────────────┬─────────────────┬───────────────────┐
13│ name │ city │ mac │
14├──────────────────┼─────────────────┼───────────────────┤
15│ Dr. Araceli Lang │ Yvettemouth │ 9e:ea:28:41:2a:50 │
16│ Terrell Boyle │ Port Reaganfort │ c5:32:09:5a:f7:15 │
17│ Libby Renner │ Port Reneeside │ 9c:63:13:31:c4:ac │
18└──────────────────┴─────────────────┴───────────────────┘
19```
20
21Unites [Ramda's][ramda] curried, data-last API and
22[LiveScript's][livescript] terse and powerful syntax.
23
24With a variety of supported input/output types and the ability [pull any
25module from npm](#using-packages-from-npm), ramda-cli is a potent tool for
26many kinds of data manipulation in command-line environment.
27
28##### Table of Contents
29
30- [Examples](#examples)
31- [Options](#options)
32- [Evaluation context](#evaluation-context)
33- [Configuration](#configuration)
34- [Using packages from NPM](#using-packages-from-npm)
35- [Promises](#promises)
36- [LiveScript?](#livescript)
37- [JavaScript support](#javascript-support)
38- [Questions or comments?](#questions-or-comments)
39
40##### Resources
41
42- [Cookbook][cookbook]
43- [Tutorial: Using ramda-cli to process and display data from GitHub API][tutorial]
44- [Essential LiveScript for ramda-cli][essential-livescript]
45
46## install
47
48```sh
49npm install -g ramda-cli
50alias R='ramda'
51```
52
53## synopsis
54
55```sh
56cat data.json | ramda [function] ...
57```
58
59The idea is to [compose][composition] functions into a pipeline of operations that when
60applied to given data, produces the desired output.
61
62By default, the function is applied to a stream of JSON data read from stdin,
63and the output data is sent to standard out as stringified JSON.
64
65Technically, `function` should be a snippet of LiveScript (or JavaScript with
66`--js`) that evaluates into a function. If multiple `function` arguments are
67supplied as positional arguments, they are composed into a pipeline in order
68from left to right (see [`R.pipe`](http://ramdajs.com/docs/#pipe)).
69
70As a simple example, in `echo 1 | R inc 'multiply 2'`, which prints 4, the
71computation would look roughly as follows:
72
73```js
74input = 1
75fn = pipe(inc, multiply(2))
76result = fn(input) // multiply(2, inc(1))
77result // => 4
78```
79
80All Ramda's functions are available directly in the scope. See
81http://ramdajs.com/docs/ for a full list.
82
83## examples
84
85```sh
86# Add 1 to each value in a list
87echo [1,2,3] | R 'map add 1'
88[
89 2,
90 3,
91 4
92]
93```
94
95```sh
96# Add 1 to each value with inline ES6 lambda and take product of all
97echo [1,2,3] | R --js 'map(x => x + 1)' product
9824
99```
100
101> Ramda functions used:
102> [`add`](http://ramdajs.com/docs/#add),
103> [`map`](http://ramdajs.com/docs/#map),
104> [`product`](http://ramdajs.com/docs/#product)
105
106##### Get a list of people whose first name starts with "B"
107
108```sh
109cat people.json | R 'pluck \name' 'filter (name) -> name.0 is \B)' -o raw
110Brando Jacobson
111Betsy Bayer
112Beverly Gleichner
113Beryl Lindgren
114```
115
116> Ramda functions used:
117> [`pluck`](http://ramdajs.com/docs/#pluck),
118> [`filter`](http://ramdajs.com/docs/#filter)
119> Data: [people.json](https://gist.githubusercontent.com/raine/cd11686e0b8a4a43bbf6/raw/people.json)
120
121##### Create a markdown TODO list
122
123```sh
124curl -s http://jsonplaceholder.typicode.com/todos |\
125 R --raw-output \
126 'filter where-eq user-id: 10' \
127 'map (t) -> "- [#{t.completed && "x" || " "}] #{t.title}"' \
128 'take 5' \
129 'unlines'
130```
131
132__Output__
133
134- [ ] ut cupiditate sequi aliquam fuga maiores
135- [x] inventore saepe cumque et aut illum enim
136- [x] omnis nulla eum aliquam distinctio
137- [ ] molestias modi perferendis perspiciatis
138- [ ] voluptates dignissimos sed doloribus animi quaerat aut
139
140> Ramda functions used:
141> [`filter`](http://ramdajs.com/docs/#filter),
142> [`where-eq`](http://ramdajs.com/docs/#whereEq),
143> [`map`](http://ramdajs.com/docs/#map)
144
145##### List versions of npm module with dates formatted with [`node-timeago`](https://github.com/ecto/node-timeago)
146
147It looks for `ecto/node-timeago` installed to `$HOME/node_modules`.
148
149```sh
150npm view ramda --json | R --import timeago \
151 'prop \time' 'to-pairs' \
152 'map -> version: it.0, time: timeago(it.1)' \
153 -o tsv | column -t -s $'\t'
154...
1550.12.0 2 months ago
1560.13.0 2 months ago
1570.14.0 12 days ago
158```
159
160##### Search twitter for people who tweeted about ramda and pretty print [the result](https://raw.githubusercontent.com/raine/ramda-cli/media/twarc-ramda.png)
161
162```sh
163twarc.py --search '#ramda' | R --slurp -p 'map path [\user, \screen_name]' uniq
164```
165
166> Ramda functions used:
167> [`map`](http://ramdajs.com/docs/#map),
168> [`path`](http://ramdajs.com/docs/#path)
169
170
171##### Pull response status data from Graphite and visualize
172
173HTTP status codes per minute for last hour:
174
175```sh
176graphite -t "summarize(stats_counts.status_codes.*, '1min', 'sum', false)" -f '-1h' -o json | \
177 R --import sparkline 'map evolve datapoints: (map head) >> sparkline \
178 'sort-by prop \target' -o table
179```
180
181[![graphite-http-codes](https://raw.githubusercontent.com/raine/ramda-cli/media/graphite-http-codes-thumb.png)](https://raw.githubusercontent.com/raine/ramda-cli/media/graphite-http-codes.png)
182
183> Ramda functions used:
184> [`evolve`](http://ramdajs.com/docs/#evolve),
185> [`sortBy`](http://ramdajs.com/docs/#sortBy)
186
187##### Use `--slurp` to read multiple JSON objects into a single list before any operations
188
189```sh
190cat <<EOF | R --slurp identity
191"foo bar"
192"test lol"
193"hello world"
194EOF
195[
196 "foo bar",
197 "test lol",
198 "hello world"
199]
200```
201
202##### Solution to the [credit card JSON to CSV challenge](https://gist.github.com/jorinvo/2e43ffa981a97bc17259) using `--output-type csv`
203
204```bash
205#!/usr/bin/env bash
206
207data_url=https://gist.githubusercontent.com/jorinvo/7f19ce95a9a842956358/raw/e319340c2f6691f9cc8d8cc57ed532b5093e3619/data.json
208curl $data_url | R \
209 'filter where creditcard: (!= null)' `# filter out those who don't have credit card` \
210 'project [\name, \creditcard]' `# pick name and creditcard fields from all objects` \
211 -o csv > `date "+%Y%m%d"`.csv `# print output as csv to a file named as the current date`
212```
213
214##### List a project's dependencies in a table
215
216```sh
217npm ls --json | R 'prop \dependencies' 'map-obj prop \version' -o table --compact
218┌───────────────┬────────┐
219│ JSONStream │ 1.0.4 │
220│ treis │ 2.3.9 │
221│ ramda │ 0.14.0 │
222│ livescript │ 1.4.0 │
223│ cli-table │ 0.3.1 │
224└───────────────┴────────┘
225```
226
227> Ramda functions used:
228> [`filter`](http://ramdajs.com/docs/#filter),
229> [`where`](http://ramdajs.com/docs/#where),
230> [`project`](http://ramdajs.com/docs/#project),
231> [`mapObj`](http://ramdajs.com/docs/#mapObj),
232> [`prop`](http://ramdajs.com/docs/#prop)
233
234##### Generate HTML with hyperscript
235
236With [`hyperscript`][hyperscript] installed to `${HOME,PWD}/node_modules`.
237
238```sh
239cat <<EOF > shopping.txt
240milk
241cheese
242peanuts
243EOF
244```
245
246```sh
247cat shopping.txt | R --import h=hyperscript \
248 -rR --slurp `# read raw input into a list` \
249 'map (h \li.item, _)' `# apply <li class="item"> into each item` \
250 'h \ul#list, _' `# wrap list inside <ul id="list">` \
251 '.outer-HTML' `# finally, grab the HTML`
252```
253
254```html
255<ul id="list">
256 <li class="item">milk</li>
257 <li class="item">cheese</li>
258 <li class="item">peanuts</li>
259</ul>
260```
261
262Reason for underscores (e.g. `h \ul, _`) is that hyperscript API is not
263curried (and can't be because it's variadic). We need to explicitly state
264that this function is waiting for one more argument.
265
266For more examples, see the [Cookbook][cookbook].
267
268## options
269
270```
271Usage: ramda [options] [function] ...
272
273 -f, --file read a function from a js/ls file instead of args; useful for
274 larger scripts
275 -c, --compact compact output for JSON and tables
276 -s, --slurp read JSON objects from stdin as one big list
277 -S, --unslurp unwraps a list before output so that each item is formatted and
278 printed separately
279 -t, --transduce use pipeline as a transducer to transform stdin
280 -P, --json-path parse stream with JSONPath expression
281 -i, --input-type read input from stdin as (#{format-enum-list INPUT_TYPES})
282 -o, --output-type format output sent to stdout (#{format-enum-list OUTPUT_TYPES})
283 -p, --pretty pretty-printed output with colors, alias to -o pretty
284 -D, --pretty-depth set how deep objects are pretty printed
285 -r, --raw-input alias for --input-type raw
286 -R, --raw-output alias for --output-type raw
287 -n, --no-stdin don't read input from stdin
288 --[no-]headers csv/tsv has a header row
289 --csv-delimiter custom csv delimiter character
290 --js use javascript instead of livescript
291 -I, --import require module as a variable
292 -C, --configure edit config in $EDITOR
293 -v, --verbose print debugging information (use -vv for even more)
294 --version print version
295 -h, --help displays help
296```
297
298#### `-f, --file`
299
300Load a function pipeline from a file. Useful for scripts difficult to express
301in command-line.
302
303__Example__
304
305```js
306// shout.js
307var R = require('ramda');
308module.exports = R.pipe(R.toUpper, R.add(R.__, '!'));
309```
310
311```sh
312echo -n '"hello world"' | R --file shout.js
313"HELLO WORLD!"
314```
315
316You can overwrite command-line arguments through the script by exporting a
317string in property `opts`.
318
319```js
320module.exports = function() { /* ... */ }
321module.exports.opts = '--slurp -o table'
322```
323
324#### `-c, --compact`
325
326Print compact tables and JSON output without whitespace.
327
328When used with `--output-type raw`, no line breaks are added to output.
329
330__Example__
331
332```sh
333seq 10 | R --input-type raw --output-type raw --compact identity # or -rRc
33412345678910%
335```
336
337#### `-s, --slurp`
338
339Read all input from `stdin` and wrap the data in a list before operations.
340
341__Example__
342
343```sh
344cat <<EOF | R --slurp 'map to-upper'
345"foo"
346"bar"
347"xyz"
348EOF
349[
350 "FOO",
351 "BAR",
352 "XYZ"
353]
354```
355
356#### `-S, --unslurp`
357
358After the pipeline is applied to an item and if the result is an array, its
359items are printed separately.
360
361__Example__
362
363```sh
364echo '[1,2,3]' | R --unslurp 'map inc'
3652
3663
3674
368```
369
370#### `-t, --transduce`
371
372Transform the input stream using the pipeline as a
373[transducer][transducers-explained]. Requires all functions in the pipeline
374to be able to act as transducers.
375
376This option essentially allows performing operations like
377[`R.map`](http://ramdajs.com/docs/#map) or
378[`R.filter`](http://ramdajs.com/docs/#filter) on items as they come without
379waiting for the stream to complete or wrapping the input stream in a
380collection with [`--slurp`](#-s---slurp).
381
382__Example__
383
384```sh
385echo '1 2 2 3 3 4' | R --transduce drop-repeats
3861
3872
3883
3894
390```
391
392#### `-P, --json-path`
393
394Parse the input stream with given [JSONPath](http://goessner.net/articles/JsonPath/) expression.
395
396See also: [JSONStream documentation](https://github.com/dominictarr/JSONStream#jsonstreamparsepath)
397
398__Examples__
399
400Process a huge JSON array one by one without reading the whole thing first.
401
402`*` as JSON path unwraps the array and objects are passed to `identity` one by one.
403
404```sh
405curl -Ls http://bit.do/countries-json | R --json-path '*' --compact identity
406{"name":"Afghanistan","code":"AF"}
407{"name":"Åland Islands","code":"AX"}
408{"name":"Albania","code":"AL"}
409...
410```
411
412#### `-i, --input-type`
413
414Parse `stdin` as one of these formats: `raw`, `csv`, `tsv`.
415
416__Examples__
417
418```sh
419echo foo | R --input-type raw to-upper
420"FOO"
421```
422
423```sh
424$ cat <<EOF | R --input-type csv identity
425id,name
4261,Bob
4272,Alice
428EOF
429[
430 { "id": "1", "name": "Bob" },
431 { "id": "2", "name": "Alice" }
432]
433```
434
435#### `-o, --output-type`
436
437Instead of JSON, format output as one of: `pretty`, `raw`, `csv`, `tsv`, `table`.
438
439##### `-o pretty`
440
441Print pretty output.
442
443##### `-o raw`
444
445With raw output type when a string value is produced, the result will be
446written to stdout as is without any formatting.
447
448##### `-o csv` and `-o tsv`
449
450CSV or TSV output type can be used when pipeline evaluates to an array of
451objects, an array of arrays or when stdin consists of a stream of bare
452objects. First object's keys will determine the headers.
453
454##### `-o table`
455
456Print nearly any type of data as a table. If used with a list of objects,
457uses the first object's keys as headers.
458
459__Example__
460
461```sh
462curl -Ls http://bit.do/countries-json | R 'take 3' -o table --compact
463┌───────────────┬──────┐
464│ name │ code │
465├───────────────┼──────┤
466│ Afghanistan │ AF │
467│ Åland Islands │ AX │
468│ Albania │ AL │
469└───────────────┴──────┘
470```
471
472#### `-p, --pretty`
473
474Alias of `--output-type pretty`.
475
476#### `-D, --pretty-depth`
477
478When using pretty-printed output, set how deep structures are verbosely
479printed.
480
481Useful when output is huge and you want to see the general structure of an
482object or list.
483
484See documentation of [`util.inspect(object[, options])`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
485
486#### `-n, --no-stdin`
487
488Don't read `stdin` for input. Useful when starting a pipeline with a constant
489function.
490
491__Example__
492
493```sh
494R --no-stdin 'always "hello world"' 'add __, \!'
495"hello world!"
496```
497
498#### `--[no-]headers`
499
500Set if input csv/tsv contains a header row.
501
502By default, csv/tsv input is assumed to contain headers.
503
504#### `--csv-delimiter`
505
506Use a custom csv delimiter. Delimiter is comma by default.
507
508Example: `--csv-delimiter=';'`
509
510#### `--js`
511
512Interpret positional arguments as JavaScript instead of LiveScript.
513
514__Example__
515
516```sh
517echo '[1,2,3]' | R --js 'map(x => Math.pow(x, 2))'
518[
519 1,
520 4,
521 9
522]
523```
524
525#### `-I, --import`
526
527Make modules installed with `npm install` accessible in positional code
528arguments.
529
530Modules are looked for in `$HOME/node_modules` and current directory's
531`node_modules`.
532
533Symbol `=` can be used to declare the variable module should appear as.
534Otherwise, module's name is used as the variable name.
535
536__Example__
537
538```sh
539echo test | R -rR --import c=chalk 'c.bold'
540**test**
541```
542
543#### `-C, --configure`
544
545Edit ramda-cli config file in `$EDITOR`.
546
547See [Configuration](#configuration).
548
549## evaluation context
550
551### functions
552
553All of [Ramda's functions][ramda-docs] are available, and also:
554
555| function | signature | description |
556|----------------|------------------------------|--------------------------------------------------|
557| `id` | `a → a` | Alias to `R.identity` |
558| [`flat`][flat] | `* → Object` | Flatten a deep structure into a shallow object |
559| `readFile` | `filePath → String` | Read a file as string |
560| `lines` | `String → [String]` | Split a string into lines |
561| `words` | `String → [String]` | Split a string into words |
562| `unlines` | `[String] → String` | Join a list of lines into a string |
563| `unwords` | `[String] → String` | Join a list of words into a string |
564| `then` | `Function → Promise` | Map a value inside Promise |
565| `pickDotPaths` | `[k] → {k: v} → {k: v}` | Like `R.pick` but deep using dot delimited paths |
566| `renameKeysBy` | `Function → {k: v} → {k: v}` | Like `R.map` but for keys instead of values |
567
568### objects
569
570| object | description |
571|-----------|-------------------------------------|
572| `process` | https://nodejs.org/api/process.html |
573| `console` | https://nodejs.org/api/console.html |
574
575`process.exit()` can be used to short-circuit pipeline in case of an error,
576for example:
577
578```sh
579 curl api | ramda 'tap (res) -> if res.error then console.error(res); process.exit(1)'
580```
581
582An alternative is to use `Maybe` type.
583
584## configuration
585
586### config file
587
588Path: `$HOME/.config/ramda-cli.{js,ls}`
589
590The purpose of a global config file is to carry functions you might find
591useful to have around. The functions it exports in an object are made
592available.
593
594For example,
595
596```js
597// ~/.config/ramda-cli.js
598exports.date = (val) => new Date(val);
599exports.timeago = require('timeago');
600exports.debug = (val) => {
601 console.log('debug:', val);
602 return val;
603};
604```
605
606```sh
607echo 1442667243000 | R date debug timeago
608debug: Sat Sep 19 2015 12:54:03 GMT+0000 (UTC)
609"12 minutes ago"
610```
611
612### default options
613
614To make some options be passed by default, it is best to use a shell alias.
615For example:
616
617```sh
618# always interpret as javascript
619alias R="ramda --js"
620echo 1 | R '(x) => x + 1'
6212
622```
623
624```sh
625# always use identity function, instead of showing help without args
626alias R="ramda identity"
627echo 1 | R
6281
629```
630
631## using packages from npm
632
633Packages installed to `$HOME/node_modules` or `$PWD/node_modules` can used
634with `require()`.
635
636```sh
637date -v +7d +%s | R -rR --js 'require("moment").unix' 'x => x.fromNow()'
638in 7 days
639```
640
641Additionally, there is `-I, --import` which can be used as a shorthand for
642`require()`.
643
644```sh
645echo test | R -rR --import c=chalk 'c.bold'
646**test**
647```
648
649## promises
650
651Promise values are unwrapped at the end of pipeline.
652
653`then` helper function can be used to map promise values.
654
655```sh
656echo 1 | R --js 'x => Promise.resolve(x)' 'then(add(5))'
6576
658```
659
660```sh
661echo '192.168.1.1\ngoogle.com\nyahoo.com' | \
662 R -r --js --import ping 'ping.promise.probe' 'then(omit(["output", "numeric_host"]))' | \
663 R --slurp -o table --compact
664┌─────────────┬───────┬─────────┬─────────┬─────────┬─────────┬────────┐
665│ host │ alive │ time │ min │ max │ avg │ stddev │
666├─────────────┼───────┼─────────┼─────────┼─────────┼─────────┼────────┤
667│ 192.168.1.1 │ true │ 1.325 │ 1.325 │ 1.325 │ 1.325 │ 0.000 │
668│ google.com │ true │ 10.729 │ 10.729 │ 10.729 │ 10.729 │ 0.000 │
669│ yahoo.com │ true │ 115.418 │ 115.418 │ 115.418 │ 115.418 │ 0.000 │
670└─────────────┴───────┴─────────┴─────────┴─────────┴─────────┴────────┘
671```
672
673## debugging
674
675You can turn on the debug output with `-v, --verbose` flag. Use `-vv` for
676even more verbose output.
677
678Verbose output shows what entered LiveScript compiled to.
679
680To debug individual functions in the pipeline, you can use something like [`treis`][treis].
681
682```sh
683echo 1 | R --import treis 'treis(add(1))'
684```
685
686## livescript?
687
688> [LiveScript][livescript] is a language which compiles to JavaScript. It has
689a straightforward mapping to JavaScript and allows you to write expressive
690code devoid of repetitive boilerplate.
691
692### comparison table
693
694All expressions in the table evaluate to a function, and are valid in
695ramda-cli.
696
697| Ramda | LiveScript | JavaScript |
698|---------------------------|---------------------|---------------------------|
699| `not` | `(not)` | `x => !x` |
700| `nth(0)` | `(.0)` | `x => x[0]` |
701| `prop('name')` | `(.name)` | `x => x.name` |
702| `add(1)` | `(+ 1)` | `x => x + 1` |
703| `add(__, '!')` | `(+ '!')` | `x => x + '!'` |
704| `gt(__, 2)` | `(> 2)` | `x => x > 2` |
705| `contains(__, xs)` | `(in xs)` | `x => xs.includes(x)` |
706| `pipe(length, gt(__, 2))` | `(.length > 2)` | `x => x.length > 2 ` |
707| `isNil` | `(~= null)` | `x => x == null` |
708| `complement(isNil)` | `(!~= null)` | `x => x != null` |
709| `match(/foo/)` | `(is /foo/)` | `x => x.match(/foo/)` |
710| `replace('a', '')` | `(- 'a')` | `x => x.replace('a', '')` |
711| `join(',')` | `(* ',')` | `x => x.join(',')` |
712| `split(',')` | `(/ ',')` | `x => x.split(',')` |
713| `toUpper` | `(.to-upper-case!)` | `x => x.toUpperCase()` |
714
715See also: [Essential LiveScript for ramda-cli][essential-livescript]
716
717## javascript support
718
719With the release of node v4 came the ES6 arrow function syntax without a
720compilation penalty. This makes JS more attractive choice for ramda-cli, so
721now there is `--js` flag.
722
723```sh
724echo '[1,2,3]' | R --js 'map(x => x + 1)'
725[
726 2,
727 3,
728 4
729]
730```
731
732## questions or comments?
733
734- [Ask on Gitter][gitter]
735- [Open an issue](https://github.com/raine/ramda-cli/issues/new)
736- [Tweet at `@rane`](https://twitter.com/rane)
737
738[![wercker status](https://app.wercker.com/status/92dbf35ece249fade3e8198181d93ec1/s/master "wercker status")](https://app.wercker.com/project/bykey/92dbf35ece249fade3e8198181d93ec1)
739
740[flat]: https://github.com/hughsk/flat
741[composition]: http://en.wikipedia.org/wiki/Function_composition_%28computer_science%29
742[livescript]: http://livescript.net
743[treis]: https://github.com/raine/treis
744[hyperscript]: https://github.com/dominictarr/hyperscript
745[ramda]: http://ramdajs.com
746[ramda-docs]: http://ramdajs.com/docs/
747[tutorial]: https://gistlog.co/raine/d12d0ec3e72b2945510b
748[essential-livescript]: https://gistlog.co/raine/6486b985c767954781b1
749[cookbook]: https://github.com/raine/ramda-cli/wiki/Cookbook
750[transducers-explained]: http://simplectic.com/blog/2014/transducers-explained-1/
751[gitter]: https://gitter.im/raine/ramda-cli