jrf 0.1.4 → 0.1.5
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/lib/jrf/row_context.rb +22 -7
- data/lib/jrf/stage.rb +59 -13
- data/lib/jrf/version.rb +1 -1
- data/test/jrf_test.rb +52 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 408c1f9706af5efaa1bf0125201d6647b4c108aa4aa28c99a93b59fb9cc94f02
|
|
4
|
+
data.tar.gz: 702f2fb14dc9d498292b02c41f0cdb4a91c0fa3e093ad9a71435d9a2604532fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 80dfa6d2bb7c9304e779a3e80815efbde9c599d66665708738b833b08daa1918ae54bc5b170c8b90c60399fe18b0df06d576e2c8c3d8b76b74f9daa826efcfa8
|
|
7
|
+
data.tar.gz: 597b715fd3ebd31a49cb2839f7dda814b845cd5aa87a3ac9a9cf551553792b453af749e287652553903de851ea7b06a9e5940abc7c25fccd319a9e7e72d75840
|
data/lib/jrf/row_context.rb
CHANGED
|
@@ -26,10 +26,12 @@ module Jrf
|
|
|
26
26
|
def initialize(obj = nil)
|
|
27
27
|
@obj = obj
|
|
28
28
|
@__jrf_current_stage = nil
|
|
29
|
+
@__jrf_current_input = obj
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def reset(obj)
|
|
32
33
|
@obj = obj
|
|
34
|
+
@__jrf_current_input = obj
|
|
33
35
|
self
|
|
34
36
|
end
|
|
35
37
|
|
|
@@ -38,11 +40,11 @@ module Jrf
|
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
def flat
|
|
41
|
-
Control::Flat.new(
|
|
43
|
+
Control::Flat.new(current_input)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def select(predicate)
|
|
45
|
-
predicate ?
|
|
47
|
+
predicate ? current_input : Control::DROPPED
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
define_reducer(:sum) do |_ctx, value, initial: 0, block: nil|
|
|
@@ -111,15 +113,16 @@ module Jrf
|
|
|
111
113
|
define_reducer(:sort) do |ctx, key = MISSING, block: nil|
|
|
112
114
|
if block
|
|
113
115
|
{
|
|
114
|
-
value: ctx.
|
|
116
|
+
value: ctx.send(:current_input),
|
|
115
117
|
initial: -> { [] },
|
|
116
118
|
finish: ->(rows) { rows.sort(&block) },
|
|
117
119
|
step: ->(rows, row) { rows << row }
|
|
118
120
|
}
|
|
119
121
|
else
|
|
120
|
-
|
|
122
|
+
current = ctx.send(:current_input)
|
|
123
|
+
resolved_key = key.equal?(MISSING) ? current : key
|
|
121
124
|
{
|
|
122
|
-
value: [resolved_key,
|
|
125
|
+
value: [resolved_key, current],
|
|
123
126
|
initial: -> { [] },
|
|
124
127
|
finish: ->(pairs) { pairs.sort_by(&:first).map(&:last) },
|
|
125
128
|
step: ->(pairs, pair) { pairs << pair }
|
|
@@ -128,7 +131,7 @@ module Jrf
|
|
|
128
131
|
end
|
|
129
132
|
|
|
130
133
|
define_reducer(:group) do |ctx, value = MISSING, block: nil|
|
|
131
|
-
resolved_value = value.equal?(MISSING) ? ctx.
|
|
134
|
+
resolved_value = value.equal?(MISSING) ? ctx.send(:current_input) : value
|
|
132
135
|
{ value: resolved_value, initial: -> { [] }, step: ->(acc, v) { acc << v } }
|
|
133
136
|
end
|
|
134
137
|
|
|
@@ -158,7 +161,7 @@ module Jrf
|
|
|
158
161
|
def reduce(initial, &block)
|
|
159
162
|
raise ArgumentError, "reduce requires a block" unless block
|
|
160
163
|
|
|
161
|
-
@__jrf_current_stage.allocate_reducer(
|
|
164
|
+
@__jrf_current_stage.allocate_reducer(current_input, initial: initial, &block)
|
|
162
165
|
end
|
|
163
166
|
|
|
164
167
|
def map(&block)
|
|
@@ -180,6 +183,18 @@ module Jrf
|
|
|
180
183
|
|
|
181
184
|
private
|
|
182
185
|
|
|
186
|
+
def current_input
|
|
187
|
+
@__jrf_current_input
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def __jrf_with_current_input(value)
|
|
191
|
+
saved_input = current_input
|
|
192
|
+
@__jrf_current_input = value
|
|
193
|
+
yield
|
|
194
|
+
ensure
|
|
195
|
+
@__jrf_current_input = saved_input
|
|
196
|
+
end
|
|
197
|
+
|
|
183
198
|
def reducer_initial_value(initial)
|
|
184
199
|
return initial.call if initial.respond_to?(:call)
|
|
185
200
|
return initial.dup if initial.is_a?(Array) || initial.is_a?(Hash)
|
data/lib/jrf/stage.rb
CHANGED
|
@@ -64,10 +64,7 @@ module Jrf
|
|
|
64
64
|
|
|
65
65
|
# Transformation mode (detected on first call)
|
|
66
66
|
if @map_transforms[idx]
|
|
67
|
-
|
|
68
|
-
when :array then return collection.map(&block)
|
|
69
|
-
when :hash then return collection.transform_values(&block)
|
|
70
|
-
end
|
|
67
|
+
return transform_collection(type, collection, &block)
|
|
71
68
|
end
|
|
72
69
|
|
|
73
70
|
map_reducer = (@reducers[idx] ||= MapReducer.new(type))
|
|
@@ -78,7 +75,7 @@ module Jrf
|
|
|
78
75
|
collection.each_with_index do |v, i|
|
|
79
76
|
slot = map_reducer.slot(i)
|
|
80
77
|
with_scoped_reducers(slot.reducers) do
|
|
81
|
-
result = block.call(v)
|
|
78
|
+
result = @ctx.send(:__jrf_with_current_input, v) { block.call(v) }
|
|
82
79
|
slot.template ||= result
|
|
83
80
|
end
|
|
84
81
|
end
|
|
@@ -87,7 +84,7 @@ module Jrf
|
|
|
87
84
|
collection.each do |k, v|
|
|
88
85
|
slot = map_reducer.slot(k)
|
|
89
86
|
with_scoped_reducers(slot.reducers) do
|
|
90
|
-
result = block.call(v)
|
|
87
|
+
result = @ctx.send(:__jrf_with_current_input, v) { block.call(v) }
|
|
91
88
|
slot.template ||= result
|
|
92
89
|
end
|
|
93
90
|
end
|
|
@@ -97,12 +94,7 @@ module Jrf
|
|
|
97
94
|
if @mode.nil? && map_reducer.slots.values.all? { |s| s.reducers.empty? }
|
|
98
95
|
@map_transforms[idx] = true
|
|
99
96
|
@reducers[idx] = nil
|
|
100
|
-
|
|
101
|
-
when :array
|
|
102
|
-
return map_reducer.slots.sort_by { |k, _| k }.map { |_, s| s.template }
|
|
103
|
-
when :hash
|
|
104
|
-
return map_reducer.slots.transform_values(&:template)
|
|
105
|
-
end
|
|
97
|
+
return transformed_slots(type, map_reducer)
|
|
106
98
|
end
|
|
107
99
|
|
|
108
100
|
ReducerToken.new(idx)
|
|
@@ -115,7 +107,7 @@ module Jrf
|
|
|
115
107
|
row = @ctx._
|
|
116
108
|
slot = map_reducer.slot(key)
|
|
117
109
|
with_scoped_reducers(slot.reducers) do
|
|
118
|
-
result = block.call(row)
|
|
110
|
+
result = @ctx.send(:__jrf_with_current_input, row) { block.call(row) }
|
|
119
111
|
slot.template ||= result
|
|
120
112
|
end
|
|
121
113
|
|
|
@@ -146,6 +138,60 @@ module Jrf
|
|
|
146
138
|
@cursor = saved_cursor
|
|
147
139
|
end
|
|
148
140
|
|
|
141
|
+
def transform_collection(type, collection, &block)
|
|
142
|
+
case type
|
|
143
|
+
when :array
|
|
144
|
+
raise TypeError, "map expects Array, got #{collection.class}" unless collection.is_a?(Array)
|
|
145
|
+
|
|
146
|
+
collection.each_with_object([]) do |value, result|
|
|
147
|
+
mapped = @ctx.send(:__jrf_with_current_input, value) { block.call(value) }
|
|
148
|
+
append_map_result(result, mapped)
|
|
149
|
+
end
|
|
150
|
+
when :hash
|
|
151
|
+
raise TypeError, "map_values expects Hash, got #{collection.class}" unless collection.is_a?(Hash)
|
|
152
|
+
|
|
153
|
+
collection.each_with_object({}) do |(key, value), result|
|
|
154
|
+
mapped = @ctx.send(:__jrf_with_current_input, value) { block.call(value) }
|
|
155
|
+
next if mapped.equal?(Control::DROPPED)
|
|
156
|
+
raise TypeError, "flat is not supported inside map_values" if mapped.is_a?(Control::Flat)
|
|
157
|
+
|
|
158
|
+
result[key] = mapped
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def transformed_slots(type, map_reducer)
|
|
164
|
+
case type
|
|
165
|
+
when :array
|
|
166
|
+
map_reducer.slots
|
|
167
|
+
.sort_by { |k, _| k }
|
|
168
|
+
.each_with_object([]) do |(_, slot), result|
|
|
169
|
+
append_map_result(result, slot.template)
|
|
170
|
+
end
|
|
171
|
+
when :hash
|
|
172
|
+
map_reducer.slots.each_with_object({}) do |(key, slot), result|
|
|
173
|
+
next if slot.template.equal?(Control::DROPPED)
|
|
174
|
+
raise TypeError, "flat is not supported inside map_values" if slot.template.is_a?(Control::Flat)
|
|
175
|
+
|
|
176
|
+
result[key] = slot.template
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def append_map_result(result, mapped)
|
|
182
|
+
return if mapped.equal?(Control::DROPPED)
|
|
183
|
+
|
|
184
|
+
if mapped.is_a?(Control::Flat)
|
|
185
|
+
unless mapped.value.is_a?(Array)
|
|
186
|
+
raise TypeError, "flat expects Array, got #{mapped.value.class}"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
result.concat(mapped.value)
|
|
190
|
+
else
|
|
191
|
+
result << mapped
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
149
195
|
class MapReducer
|
|
150
196
|
attr_reader :slots
|
|
151
197
|
|
data/lib/jrf/version.rb
CHANGED
data/test/jrf_test.rb
CHANGED
|
@@ -174,6 +174,14 @@ stdout, stderr, status = run_jrf('_["items"] >> flat >> group', input_flat)
|
|
|
174
174
|
assert_success(status, stderr, "flat then group")
|
|
175
175
|
assert_equal(['[1,2,3]'], lines(stdout), "flat then group output")
|
|
176
176
|
|
|
177
|
+
stdout, stderr, status = run_jrf('map { |x| flat }', "[[1,2],[3],[4,5,6]]\n")
|
|
178
|
+
assert_success(status, stderr, "flat inside map")
|
|
179
|
+
assert_equal(['[1,2,3,4,5,6]'], lines(stdout), "flat inside map output")
|
|
180
|
+
|
|
181
|
+
stdout, stderr, status = run_jrf('map_values { |v| flat }', "{\"a\":[1,2],\"b\":[3]}\n")
|
|
182
|
+
assert_failure(status, "flat inside map_values")
|
|
183
|
+
assert_includes(stderr, "flat is not supported inside map_values")
|
|
184
|
+
|
|
177
185
|
stdout, stderr, status = run_jrf('_["foo"] >> flat', input)
|
|
178
186
|
assert_failure(status, "flat requires array")
|
|
179
187
|
assert_includes(stderr, "flat expects Array")
|
|
@@ -540,6 +548,10 @@ stdout, stderr, status = run_jrf('_["values"] >> map { |x| sum(_[0] + x) }', inp
|
|
|
540
548
|
assert_success(status, stderr, "map keeps ambient _")
|
|
541
549
|
assert_equal(['[12,66,606]'], lines(stdout), "map ambient _ output")
|
|
542
550
|
|
|
551
|
+
stdout, stderr, status = run_jrf('_["values"] >> map { |x| reduce(0) { |acc, v| acc + v } }', input_map)
|
|
552
|
+
assert_success(status, stderr, "map with reduce")
|
|
553
|
+
assert_equal(['[6,60,600]'], lines(stdout), "map with reduce output")
|
|
554
|
+
|
|
543
555
|
input_map_varying = <<~NDJSON
|
|
544
556
|
[1,10]
|
|
545
557
|
[2,20,200]
|
|
@@ -550,6 +562,20 @@ stdout, stderr, status = run_jrf('map { |x| sum(x) }', input_map_varying)
|
|
|
550
562
|
assert_success(status, stderr, "map varying lengths")
|
|
551
563
|
assert_equal(['[6,30,200]'], lines(stdout), "map varying lengths output")
|
|
552
564
|
|
|
565
|
+
input_map_unsorted = <<~NDJSON
|
|
566
|
+
{"values":[3,30]}
|
|
567
|
+
{"values":[1,10]}
|
|
568
|
+
{"values":[2,20]}
|
|
569
|
+
NDJSON
|
|
570
|
+
|
|
571
|
+
stdout, stderr, status = run_jrf('_["values"] >> map { |x| group }', input_map)
|
|
572
|
+
assert_success(status, stderr, "map with group")
|
|
573
|
+
assert_equal(['[[1,2,3],[10,20,30],[100,200,300]]'], lines(stdout), "map with group output")
|
|
574
|
+
|
|
575
|
+
stdout, stderr, status = run_jrf('_["values"] >> map { |x| sort }', input_map_unsorted)
|
|
576
|
+
assert_success(status, stderr, "map with sort default key")
|
|
577
|
+
assert_equal(['[[1,2,3],[10,20,30]]'], lines(stdout), "map with sort default key output")
|
|
578
|
+
|
|
553
579
|
input_map_values = <<~NDJSON
|
|
554
580
|
{"a":1,"b":10}
|
|
555
581
|
{"a":2,"b":20}
|
|
@@ -578,10 +604,18 @@ stdout, stderr, status = run_jrf('map_values { |v| count(v) }', input_map_values
|
|
|
578
604
|
assert_success(status, stderr, "map_values with count")
|
|
579
605
|
assert_equal(['{"a":3,"b":3}'], lines(stdout), "map_values with count output")
|
|
580
606
|
|
|
607
|
+
stdout, stderr, status = run_jrf('map_values { |v| group }', input_map_values)
|
|
608
|
+
assert_success(status, stderr, "map_values with group")
|
|
609
|
+
assert_equal(['{"a":[1,2,3],"b":[10,20,30]}'], lines(stdout), "map_values with group output")
|
|
610
|
+
|
|
581
611
|
stdout, stderr, status = run_jrf('map_values { |v| sum(_["a"] + v) }', input_map_values)
|
|
582
612
|
assert_success(status, stderr, "map_values keeps ambient _")
|
|
583
613
|
assert_equal(['{"a":12,"b":66}'], lines(stdout), "map_values ambient _ output")
|
|
584
614
|
|
|
615
|
+
stdout, stderr, status = run_jrf('map_values { |v| reduce(0) { |acc, x| acc + x } }', input_map_values)
|
|
616
|
+
assert_success(status, stderr, "map_values with reduce")
|
|
617
|
+
assert_equal(['{"a":6,"b":60}'], lines(stdout), "map_values with reduce output")
|
|
618
|
+
|
|
585
619
|
stdout, stderr, status = run_jrf('select(false) >> map { |x| sum(x) }', input_map)
|
|
586
620
|
assert_success(status, stderr, "map no matches")
|
|
587
621
|
assert_equal([], lines(stdout), "map no matches output")
|
|
@@ -599,10 +633,18 @@ stdout, stderr, status = run_jrf('_["values"] >> map { |x| x + 1 }', input_map)
|
|
|
599
633
|
assert_success(status, stderr, "map transform")
|
|
600
634
|
assert_equal(['[2,11,101]', '[3,21,201]', '[4,31,301]'], lines(stdout), "map transform output")
|
|
601
635
|
|
|
636
|
+
stdout, stderr, status = run_jrf('_["values"] >> map { |x| select(x >= 20) }', input_map)
|
|
637
|
+
assert_success(status, stderr, "map transform with select")
|
|
638
|
+
assert_equal(['[100]', '[20,200]', '[30,300]'], lines(stdout), "map transform with select output")
|
|
639
|
+
|
|
602
640
|
stdout, stderr, status = run_jrf('map_values { |v| v * 2 }', input_map_values)
|
|
603
641
|
assert_success(status, stderr, "map_values transform")
|
|
604
642
|
assert_equal(['{"a":2,"b":20}', '{"a":4,"b":40}', '{"a":6,"b":60}'], lines(stdout), "map_values transform output")
|
|
605
643
|
|
|
644
|
+
stdout, stderr, status = run_jrf('map_values { |v| select(v >= 10) }', input_map_values)
|
|
645
|
+
assert_success(status, stderr, "map_values transform with select")
|
|
646
|
+
assert_equal(['{"b":10}', '{"b":20}', '{"b":30}'], lines(stdout), "map_values transform with select output")
|
|
647
|
+
|
|
606
648
|
stdout, stderr, status = run_jrf('_["values"] >> map { |x| x + 1 } >> map { |x| x * 10 }', input_map)
|
|
607
649
|
assert_success(status, stderr, "chained map transforms")
|
|
608
650
|
assert_equal(['[20,110,1010]', '[30,210,2010]', '[40,310,3010]'], lines(stdout), "chained map transforms output")
|
|
@@ -639,6 +681,12 @@ stdout, stderr, status = run_jrf('group_by(_["status"]) { |row| group(row["path"
|
|
|
639
681
|
assert_success(status, stderr, "group_by with group(expr)")
|
|
640
682
|
assert_equal(['{"200":["/a","/c","/d"],"404":["/b"]}'], lines(stdout), "group_by with group(expr) output")
|
|
641
683
|
|
|
684
|
+
stdout, stderr, status = run_jrf('group_by(_["status"]) { group }', input_gb)
|
|
685
|
+
assert_success(status, stderr, "group_by with implicit group")
|
|
686
|
+
result = JSON.parse(lines(stdout).first)
|
|
687
|
+
assert_equal(3, result["200"].length, "group_by implicit group 200 count")
|
|
688
|
+
assert_equal("/a", result["200"][0]["path"], "group_by implicit group first row")
|
|
689
|
+
|
|
642
690
|
stdout, stderr, status = run_jrf('group_by(_["status"]) { |row| min(row["latency"]) }', input_gb)
|
|
643
691
|
assert_success(status, stderr, "group_by with min")
|
|
644
692
|
assert_equal(['{"200":10,"404":50}'], lines(stdout), "group_by with min output")
|
|
@@ -647,6 +695,10 @@ stdout, stderr, status = run_jrf('group_by(_["status"]) { |row| {total: sum(row[
|
|
|
647
695
|
assert_success(status, stderr, "group_by with multi-reducer")
|
|
648
696
|
assert_equal(['{"200":{"total":60,"n":3},"404":{"total":50,"n":1}}'], lines(stdout), "group_by multi-reducer output")
|
|
649
697
|
|
|
698
|
+
stdout, stderr, status = run_jrf('group_by(_["status"]) { reduce(0) { |acc, row| acc + row["latency"] } }', input_gb)
|
|
699
|
+
assert_success(status, stderr, "group_by with reduce")
|
|
700
|
+
assert_equal(['{"200":60,"404":50}'], lines(stdout), "group_by with reduce output")
|
|
701
|
+
|
|
650
702
|
stdout, stderr, status = run_jrf('select(false) >> group_by(_["status"]) { count() }', input_gb)
|
|
651
703
|
assert_success(status, stderr, "group_by no matches")
|
|
652
704
|
assert_equal([], lines(stdout), "group_by no matches output")
|