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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ce648c2afbfe10dc161b08badb05acdb411baf839dde77433927380b6bb7439
4
- data.tar.gz: 6be5a0851eecd3cfcbe93aff1cb8fdd163a84dd96a7b12e440fc514db03f67a0
3
+ metadata.gz: 408c1f9706af5efaa1bf0125201d6647b4c108aa4aa28c99a93b59fb9cc94f02
4
+ data.tar.gz: 702f2fb14dc9d498292b02c41f0cdb4a91c0fa3e093ad9a71435d9a2604532fa
5
5
  SHA512:
6
- metadata.gz: aa4dfead95dbe09453ec720cdbcf77ba4c7e3f1047c60f51d4ff54724dfa540bb1dbd5630ecb07d09d745e1e61e4c236f50f4407ff6d4c17dd5431b385679f57
7
- data.tar.gz: 03c3f5dd3f36675a2bc31981effc506bb1822bb170e754785ccffe077becdd5af13421b4cbfd18fea1c1262f06feef61561be3e3243ca0379e1e6af21ad003c5
6
+ metadata.gz: 80dfa6d2bb7c9304e779a3e80815efbde9c599d66665708738b833b08daa1918ae54bc5b170c8b90c60399fe18b0df06d576e2c8c3d8b76b74f9daa826efcfa8
7
+ data.tar.gz: 597b715fd3ebd31a49cb2839f7dda814b845cd5aa87a3ac9a9cf551553792b453af749e287652553903de851ea7b06a9e5940abc7c25fccd319a9e7e72d75840
@@ -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(@obj)
43
+ Control::Flat.new(current_input)
42
44
  end
43
45
 
44
46
  def select(predicate)
45
- predicate ? @obj : Control::DROPPED
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
- resolved_key = key.equal?(MISSING) ? ctx._ : key
122
+ current = ctx.send(:current_input)
123
+ resolved_key = key.equal?(MISSING) ? current : key
121
124
  {
122
- value: [resolved_key, ctx._],
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._ : value
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(@obj, initial: initial, &block)
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
- case type
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
- case type
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jrf
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.5"
5
5
  end
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")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jrf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - kazuho