pru 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/bin/pru +56 -13
- data/lib/pru/core_ext/array.rb +2 -2
- data/lib/pru/version.rb +1 -1
- data/lib/pru.rb +33 -17
- metadata +4 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f72e74d5784d176b7582189d4a7b805e62c12e574b6d778549c664ecb02fe0b1
|
|
4
|
+
data.tar.gz: f5135f6bee632dfa47a596e5236ad503c71ce9d505f6daee242338ce05db3620
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7f1f60fa40cc36551551f42d1c98c469aa33c4dbd1cc3e6c03a27d008e5ae323bb17e10bf1e5a91f42a3e8f9911ef6faec8508044ae403ac44f51c96574b489
|
|
7
|
+
data.tar.gz: 456555cd258f16f86dcdc86392c1be6be9c536fa5f60792c6e3d9cd2fb98ecd28d3ecdc3657e333f2ed36b718a2097713059ffdda077afeee74985a09093e8ec
|
data/bin/pru
CHANGED
|
@@ -7,10 +7,9 @@ $LOAD_PATH << "#{root}/lib" if File.exist?("#{root}/Gemfile")
|
|
|
7
7
|
|
|
8
8
|
require 'pru'
|
|
9
9
|
|
|
10
|
-
usage = nil
|
|
11
10
|
options = {}
|
|
12
11
|
|
|
13
|
-
OptionParser.new do |opts|
|
|
12
|
+
parser = OptionParser.new do |opts|
|
|
14
13
|
opts.banner = <<-BANNER.gsub(/^ /, "")
|
|
15
14
|
Pipeable Ruby
|
|
16
15
|
|
|
@@ -28,6 +27,8 @@ OptionParser.new do |opts|
|
|
|
28
27
|
Options:
|
|
29
28
|
BANNER
|
|
30
29
|
opts.on("-r", "--reduce CODE","reduce via CODE") {|code| options[:reduce] = code }
|
|
30
|
+
opts.on("-j", "--json","Parse input as a JSON stream, each value is an item") { options[:json] = true }
|
|
31
|
+
opts.on("-k", "--k8s","Like --json, but iterate the \"items\" array if present (kubectl -o json)") { options[:json] = true; options[:k8s] = true }
|
|
31
32
|
opts.separator ''
|
|
32
33
|
opts.on('-I', '--libdir DIR', 'Add DIR to load path') { |dir| $LOAD_PATH << dir }
|
|
33
34
|
opts.on('--require LIB', 'Require LIB (also comma-separated)') { |lib| lib.split(',').each{|l| require l } }
|
|
@@ -35,41 +36,83 @@ OptionParser.new do |opts|
|
|
|
35
36
|
opts.separator ''
|
|
36
37
|
opts.on("-h", "--help","Show this.") { puts opts; exit }
|
|
37
38
|
opts.on('-v', '--version','Show Version'){ require 'pru/version'; puts Pru::VERSION; exit}
|
|
38
|
-
usage = opts
|
|
39
|
-
end.parse!
|
|
40
|
-
|
|
41
|
-
if ARGV.empty? && options.empty? # no arguments -> show usage
|
|
42
|
-
puts usage
|
|
43
|
-
exit
|
|
44
39
|
end
|
|
40
|
+
parser.parse!
|
|
45
41
|
|
|
46
|
-
|
|
42
|
+
# bad arguments -> fail
|
|
43
|
+
if ARGV.empty? && options.empty?
|
|
44
|
+
abort parser.to_s
|
|
45
|
+
end
|
|
46
|
+
if ARGV.size > 2
|
|
47
|
+
abort "Too many arguments, see --help"
|
|
48
|
+
end
|
|
49
|
+
if ARGV.size == 2 && options[:reduce]
|
|
50
|
+
abort "Cannot combine a reduce argument with --reduce, see --help"
|
|
51
|
+
end
|
|
52
|
+
if options[:file] && ARGV.empty? && !options[:reduce]
|
|
53
|
+
abort "No code given for --inplace-edit, see --help"
|
|
54
|
+
end
|
|
47
55
|
|
|
48
56
|
map, reduce = ARGV
|
|
49
57
|
reduce ||= options[:reduce]
|
|
50
58
|
map = 'true' if !map || map.empty?
|
|
51
59
|
|
|
60
|
+
# reading from file or stdin ?
|
|
52
61
|
if options[:file]
|
|
53
62
|
output_lines = []
|
|
54
63
|
input = File.read(options[:file])
|
|
55
64
|
newline = input[/\r\n|\r|\n/]
|
|
56
|
-
trailing_newline = (
|
|
65
|
+
trailing_newline = (newline && input.end_with?(newline))
|
|
57
66
|
else
|
|
58
67
|
input = $stdin
|
|
59
68
|
end
|
|
60
69
|
|
|
70
|
+
# output as JSON ?
|
|
71
|
+
if options[:json]
|
|
72
|
+
require 'json' # also used by --k8s, which sets :json
|
|
73
|
+
output_format = lambda do |out|
|
|
74
|
+
case out
|
|
75
|
+
when Hash, Array then JSON.pretty_generate(out)
|
|
76
|
+
else out
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# collect results into list or print them
|
|
61
82
|
collector = lambda do |line|
|
|
62
|
-
|
|
83
|
+
line = output_format.call(line) if output_format
|
|
84
|
+
if output_lines
|
|
85
|
+
output_lines << line
|
|
86
|
+
else
|
|
87
|
+
begin
|
|
88
|
+
puts(line)
|
|
89
|
+
rescue Errno::EPIPE
|
|
90
|
+
exit 0
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# find all items (ideally lazy so we can stream)
|
|
96
|
+
items = if options[:k8s]
|
|
97
|
+
# single document (e.g. `kubectl get ... -o json`) -> parse once and iterate its items
|
|
98
|
+
JSON.parse(input.read).fetch("items")
|
|
99
|
+
elsif options[:json]
|
|
100
|
+
# lazy stream of JSON values
|
|
101
|
+
Pru.each_json(input)
|
|
102
|
+
else
|
|
103
|
+
# plain lines, chomped, lazily so streaming/head still work
|
|
104
|
+
input.each_line.lazy.map { |line| line.chomp }
|
|
63
105
|
end
|
|
64
106
|
|
|
65
107
|
if reduce
|
|
66
108
|
results = []
|
|
67
|
-
Pru.map(
|
|
109
|
+
Pru.map(items, map) { |out| results << out }
|
|
68
110
|
collector.call Pru.reduce(results, reduce)
|
|
69
111
|
else
|
|
70
|
-
Pru.map(
|
|
112
|
+
Pru.map(items, map) { |out| collector.call out }
|
|
71
113
|
end
|
|
72
114
|
|
|
115
|
+
# write back to file if requested
|
|
73
116
|
if options[:file]
|
|
74
117
|
content = output_lines.join(newline)
|
|
75
118
|
content << newline if trailing_newline
|
data/lib/pru/core_ext/array.rb
CHANGED
data/lib/pru/version.rb
CHANGED
data/lib/pru.rb
CHANGED
|
@@ -2,34 +2,50 @@ require 'pru/core_ext/array'
|
|
|
2
2
|
|
|
3
3
|
module Pru
|
|
4
4
|
class << self
|
|
5
|
-
def map(
|
|
6
|
-
|
|
7
|
-
def _pru(i)
|
|
8
|
-
#{code}
|
|
9
|
-
end
|
|
10
|
-
RUBY
|
|
11
|
-
|
|
5
|
+
def map(items, code)
|
|
6
|
+
block = compile(code)
|
|
12
7
|
i = 0
|
|
13
|
-
|
|
8
|
+
items.each do |item|
|
|
14
9
|
i += 1
|
|
15
|
-
|
|
16
|
-
result = line._pru(i) or next
|
|
10
|
+
result = item.instance_exec(i, &block) || next
|
|
17
11
|
|
|
18
12
|
case result
|
|
19
|
-
when true then yield
|
|
20
|
-
when Regexp then yield
|
|
13
|
+
when true then yield item
|
|
14
|
+
when Regexp then yield item if item =~ result
|
|
21
15
|
else yield result
|
|
22
16
|
end
|
|
23
17
|
end
|
|
24
18
|
end
|
|
25
19
|
|
|
26
20
|
def reduce(array, code)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
21
|
+
array.instance_exec(&compile(code))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# An enumerable of JSON values parsed from a stream (newline-delimited or multiline),
|
|
25
|
+
# parsed lazily so we process as input arrives, by accumulating lines until the buffer
|
|
26
|
+
# forms a complete value.
|
|
27
|
+
# TODO: this is not very efficient, but keeping track of opening/closing braces might be ugly too
|
|
28
|
+
def each_json(io)
|
|
29
|
+
Enumerator.new do |yielder|
|
|
30
|
+
buffer = +""
|
|
31
|
+
io.each_line do |line|
|
|
32
|
+
buffer << line
|
|
33
|
+
begin
|
|
34
|
+
item = JSON.parse(buffer)
|
|
35
|
+
rescue JSON::ParserError
|
|
36
|
+
next
|
|
37
|
+
end
|
|
38
|
+
yielder << item
|
|
39
|
+
buffer = +""
|
|
30
40
|
end
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
raise JSON::ParserError, "unexpected trailing input: #{buffer.strip}" unless buffer.strip.empty?
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def compile(code)
|
|
48
|
+
eval("proc { |i| #{code} }", TOPLEVEL_BINDING, __FILE__, __LINE__)
|
|
33
49
|
end
|
|
34
50
|
end
|
|
35
51
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pru
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Grosser
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
12
|
email: michael@grosser.it
|
|
15
13
|
executables:
|
|
16
14
|
- pru
|
|
@@ -25,7 +23,6 @@ homepage: https://github.com/grosser/pru
|
|
|
25
23
|
licenses:
|
|
26
24
|
- MIT
|
|
27
25
|
metadata: {}
|
|
28
|
-
post_install_message:
|
|
29
26
|
rdoc_options: []
|
|
30
27
|
require_paths:
|
|
31
28
|
- lib
|
|
@@ -33,16 +30,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
33
30
|
requirements:
|
|
34
31
|
- - ">="
|
|
35
32
|
- !ruby/object:Gem::Version
|
|
36
|
-
version: 2.
|
|
33
|
+
version: '2.6'
|
|
37
34
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
35
|
requirements:
|
|
39
36
|
- - ">="
|
|
40
37
|
- !ruby/object:Gem::Version
|
|
41
38
|
version: '0'
|
|
42
39
|
requirements: []
|
|
43
|
-
|
|
44
|
-
rubygems_version: 2.5.1
|
|
45
|
-
signing_key:
|
|
40
|
+
rubygems_version: 4.0.10
|
|
46
41
|
specification_version: 4
|
|
47
42
|
summary: Pipeable Ruby - forget about grep / sed / awk / wc ... use pure, readable
|
|
48
43
|
Ruby!
|