jrf 0.1.11 → 0.1.13
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 +4 -4
- data/jrf.gemspec +2 -0
- data/lib/jrf/cli/runner.rb +57 -5
- data/lib/jrf/cli.rb +5 -4
- data/lib/jrf/row_context.rb +14 -4
- data/lib/jrf/stage.rb +29 -0
- data/lib/jrf/version.rb +1 -1
- data/test/cli_runner_test.rb +951 -0
- data/test/library_api_test.rb +126 -0
- data/test/readme_examples_test.rb +16 -0
- data/test/test_helper.rb +118 -0
- metadata +35 -8
- data/test/jrf_test.rb +0 -1032
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2862eaf6bd5f2486ea2c6aebf5caa4fbc2de56f419625bf8bb462392a3ea5dd9
|
|
4
|
+
data.tar.gz: 3f29e7024f4e33606d78ad01ce4c45f37c9cd652ba94ac490866cd877368037a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04f55e0ea8c24f70126964beffbe80bee1800e1e210da2f96186bb8ebdf5542e5dfbab9c06b48624da4ec35912d02456561ec6c0d2c66c094de001ecf7f4096f
|
|
7
|
+
data.tar.gz: '093821f35539be4561867b711664a31d3441052fe53e1c0f73489cd8b11fdf845bfb5573375f880ce07cd73ffc2f1d0514b55b760227c01ab515afd39d8ac08a'
|
data/jrf.gemspec
CHANGED
|
@@ -16,6 +16,8 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
spec.bindir = "exe"
|
|
17
17
|
spec.executables = ["jrf"]
|
|
18
18
|
spec.add_dependency "oj", ">= 3.16"
|
|
19
|
+
spec.add_development_dependency "minitest", ">= 5.0"
|
|
20
|
+
spec.add_development_dependency "rake", ">= 13.0"
|
|
19
21
|
|
|
20
22
|
spec.files = Dir.glob("{exe,lib,test}/*") + Dir.glob("lib/**/*") + %w[DESIGN.txt jrf.gemspec Gemfile Rakefile].select { |path| File.file?(path) }
|
|
21
23
|
end
|
data/lib/jrf/cli/runner.rb
CHANGED
|
@@ -28,12 +28,12 @@ module Jrf
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def initialize(inputs:, out: $stdout, err: $stderr, lax: false,
|
|
31
|
+
def initialize(inputs:, out: $stdout, err: $stderr, lax: false, output_format: :json, atomic_write_bytes: DEFAULT_OUTPUT_BUFFER_LIMIT)
|
|
32
32
|
@inputs = inputs
|
|
33
33
|
@out = out
|
|
34
34
|
@err = err
|
|
35
35
|
@lax = lax
|
|
36
|
-
@
|
|
36
|
+
@output_format = output_format
|
|
37
37
|
@atomic_write_bytes = atomic_write_bytes
|
|
38
38
|
@output_buffer = +""
|
|
39
39
|
end
|
|
@@ -49,8 +49,13 @@ module Jrf
|
|
|
49
49
|
pipeline = Pipeline.new(*blocks)
|
|
50
50
|
|
|
51
51
|
input_enum = Enumerator.new { |y| each_input_value { |v| y << v } }
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
|
|
53
|
+
if @output_format == :tsv
|
|
54
|
+
values = []
|
|
55
|
+
pipeline.call(input_enum) { |value| values << value }
|
|
56
|
+
emit_tsv(values)
|
|
57
|
+
else
|
|
58
|
+
pipeline.call(input_enum) { |value| emit_output(value) }
|
|
54
59
|
end
|
|
55
60
|
ensure
|
|
56
61
|
write_output(@output_buffer)
|
|
@@ -109,7 +114,54 @@ module Jrf
|
|
|
109
114
|
end
|
|
110
115
|
|
|
111
116
|
def emit_output(value)
|
|
112
|
-
record = (@pretty ? JSON.pretty_generate(value) : JSON.generate(value)) << "\n"
|
|
117
|
+
record = (@output_format == :pretty ? JSON.pretty_generate(value) : JSON.generate(value)) << "\n"
|
|
118
|
+
buffer_output(record)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def emit_tsv(values)
|
|
122
|
+
rows = values.flat_map { |value| value_to_rows(value) }
|
|
123
|
+
rows.each do |row|
|
|
124
|
+
buffer_output(row.join("\t") << "\n")
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def value_to_rows(value)
|
|
129
|
+
case value
|
|
130
|
+
when Hash
|
|
131
|
+
value.map { |k, v|
|
|
132
|
+
case v
|
|
133
|
+
when Array
|
|
134
|
+
[format_cell(k)] + v.map { |e| format_cell(e) }
|
|
135
|
+
else
|
|
136
|
+
[format_cell(k), format_cell(v)]
|
|
137
|
+
end
|
|
138
|
+
}
|
|
139
|
+
when Array
|
|
140
|
+
value.map { |row|
|
|
141
|
+
case row
|
|
142
|
+
when Array
|
|
143
|
+
row.map { |e| format_cell(e) }
|
|
144
|
+
else
|
|
145
|
+
[format_cell(row)]
|
|
146
|
+
end
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
[[format_cell(value)]]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def format_cell(value)
|
|
154
|
+
case value
|
|
155
|
+
when nil
|
|
156
|
+
"null"
|
|
157
|
+
when Numeric, String, true, false
|
|
158
|
+
value.to_s
|
|
159
|
+
else
|
|
160
|
+
JSON.generate(value)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def buffer_output(record)
|
|
113
165
|
if @output_buffer.bytesize + record.bytesize <= @atomic_write_bytes
|
|
114
166
|
@output_buffer << record
|
|
115
167
|
else
|
data/lib/jrf/cli.rb
CHANGED
|
@@ -16,7 +16,8 @@ module Jrf
|
|
|
16
16
|
Options:
|
|
17
17
|
-v, --verbose print parsed stage expressions
|
|
18
18
|
--lax allow multiline JSON texts; split inputs by whitespace (also detects JSON-SEQ RS 0x1e)
|
|
19
|
-
-
|
|
19
|
+
-o, --output FORMAT
|
|
20
|
+
output format: json (default), pretty, tsv
|
|
20
21
|
-r, --require LIBRARY
|
|
21
22
|
require LIBRARY before evaluating stages
|
|
22
23
|
--no-jit do not enable YJIT, even when supported by the Ruby runtime
|
|
@@ -43,7 +44,7 @@ module Jrf
|
|
|
43
44
|
def self.run(argv = ARGV, input: ARGF, out: $stdout, err: $stderr)
|
|
44
45
|
verbose = false
|
|
45
46
|
lax = false
|
|
46
|
-
|
|
47
|
+
output_format = :json
|
|
47
48
|
jit = true
|
|
48
49
|
required_libraries = []
|
|
49
50
|
atomic_write_bytes = Runner::DEFAULT_OUTPUT_BUFFER_LIMIT
|
|
@@ -52,7 +53,7 @@ module Jrf
|
|
|
52
53
|
opts.banner = USAGE
|
|
53
54
|
opts.on("-v", "--verbose", "print parsed stage expressions") { verbose = true }
|
|
54
55
|
opts.on("--lax", "allow multiline JSON texts; split inputs by whitespace (also detects JSON-SEQ RS 0x1e)") { lax = true }
|
|
55
|
-
opts.on("-
|
|
56
|
+
opts.on("-o", "--output FORMAT", %w[json pretty tsv], "output format: json, pretty, tsv") { |fmt| output_format = fmt.to_sym }
|
|
56
57
|
opts.on("-r", "--require LIBRARY", "require LIBRARY before evaluating stages") { |library| required_libraries << library }
|
|
57
58
|
opts.on("--no-jit", "do not enable YJIT, even when supported by the Ruby runtime") { jit = false }
|
|
58
59
|
opts.on("--atomic-write-bytes N", Integer, "group short outputs into atomic writes of up to N bytes") do |value|
|
|
@@ -113,7 +114,7 @@ module Jrf
|
|
|
113
114
|
out: out,
|
|
114
115
|
err: err,
|
|
115
116
|
lax: lax,
|
|
116
|
-
|
|
117
|
+
output_format: output_format,
|
|
117
118
|
atomic_write_bytes: atomic_write_bytes
|
|
118
119
|
).run(expression, verbose: verbose)
|
|
119
120
|
end
|
data/lib/jrf/row_context.rb
CHANGED
|
@@ -59,6 +59,10 @@ module Jrf
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
define_reducer(:count_if) do |_ctx, condition, block: nil|
|
|
63
|
+
{ value: condition, initial: 0, step: ->(acc, v) { v ? (acc + 1) : acc } }
|
|
64
|
+
end
|
|
65
|
+
|
|
62
66
|
define_reducer(:min) do |_ctx, value, block: nil|
|
|
63
67
|
{ value: value, initial: nil, step: ->(acc, v) { v.nil? ? acc : (acc.nil? || v < acc ? v : acc) } }
|
|
64
68
|
end
|
|
@@ -170,16 +174,22 @@ module Jrf
|
|
|
170
174
|
@__jrf_current_stage.step_reduce(current_input, initial: initial, &block)
|
|
171
175
|
end
|
|
172
176
|
|
|
173
|
-
def map(&block)
|
|
177
|
+
def map(collection = nil, &block)
|
|
174
178
|
raise ArgumentError, "map requires a block" unless block
|
|
175
179
|
|
|
176
|
-
@__jrf_current_stage.step_map(:map, current_input, &block)
|
|
180
|
+
@__jrf_current_stage.step_map(:map, collection || current_input, &block)
|
|
177
181
|
end
|
|
178
182
|
|
|
179
|
-
def map_values(&block)
|
|
183
|
+
def map_values(collection = nil, &block)
|
|
180
184
|
raise ArgumentError, "map_values requires a block" unless block
|
|
181
185
|
|
|
182
|
-
@__jrf_current_stage.step_map(:map_values, current_input, &block)
|
|
186
|
+
@__jrf_current_stage.step_map(:map_values, collection || current_input, &block)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def apply(collection = nil, &block)
|
|
190
|
+
raise ArgumentError, "apply requires a block" unless block
|
|
191
|
+
|
|
192
|
+
@__jrf_current_stage.step_apply(collection || current_input, &block)
|
|
183
193
|
end
|
|
184
194
|
|
|
185
195
|
def group_by(key, &block)
|
data/lib/jrf/stage.rb
CHANGED
|
@@ -113,6 +113,35 @@ module Jrf
|
|
|
113
113
|
ReducerToken.new(idx)
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
def step_apply(collection, &block)
|
|
117
|
+
raise TypeError, "apply expects Array, got #{collection.class}" unless collection.is_a?(Array)
|
|
118
|
+
|
|
119
|
+
apply_reducers = []
|
|
120
|
+
template = nil
|
|
121
|
+
results = []
|
|
122
|
+
|
|
123
|
+
collection.each do |v|
|
|
124
|
+
with_scoped_reducers(apply_reducers) do
|
|
125
|
+
result = @ctx.send(:__jrf_with_current_input, v) { block.call(v) }
|
|
126
|
+
template ||= result
|
|
127
|
+
results << result
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if apply_reducers.any?
|
|
132
|
+
self.class.resolve_template(template, apply_reducers)
|
|
133
|
+
else
|
|
134
|
+
results.each_with_object([]) do |mapped, arr|
|
|
135
|
+
next if mapped.equal?(Control::DROPPED)
|
|
136
|
+
if mapped.is_a?(Control::Flat)
|
|
137
|
+
arr.concat(Array(mapped.value))
|
|
138
|
+
else
|
|
139
|
+
arr << mapped
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
116
145
|
def step_group_by(key, &block)
|
|
117
146
|
idx = @cursor
|
|
118
147
|
map_reducer = (@reducers[idx] ||= MapReducer.new(:group_by, false))
|
data/lib/jrf/version.rb
CHANGED