fall 0.0.0.1 → 0.0.0.2
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/fall/ext/enumerable.rb +11 -0
- data/lib/fall/ext/enumerator/lazy.rb +11 -0
- data/lib/fall/helpers.rb +35 -0
- data/lib/fall/parser.rb +67 -0
- data/lib/fall/pipeline.rb +38 -0
- data/lib/fall/record.rb +90 -0
- data/lib/fall/record_operation.rb +50 -0
- data/lib/fall/record_operation_pipeline.rb +24 -0
- data/lib/fall/stream_operation.rb +40 -0
- data/lib/fall/stream_operation_pipeline.rb +18 -0
- data/lib/fall/version.rb +1 -1
- data/lib/fall.rb +14 -1
- metadata +12 -3
- data/lib/draft.rb +0 -377
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8248a14aeee0ee0ec5e2d9489fba06521f17bdc021679db52f2f669f6a36c6e
|
4
|
+
data.tar.gz: 98a35c3e55c7bec0eba79c9706ad45ed7b79cde090d6af072bcdb30b31d40a40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12a773f8e4905f785488954bb14d5bf818a9e39136ac766d1a96c85b471742c163d11ce5eb432ae6374b10b110164d16960724a0c664bf2a853ee0ac15d7efe7
|
7
|
+
data.tar.gz: 61bc56a65ac235b469e3ba405c53adb08ce261d17f35bc34ac84094eee1b0cd621969c4e6be8877c8c95f5f1e072c73c0e1ec46ad3cb05ad6f007e2698b2a149
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Fall
|
2
|
+
module Ext
|
3
|
+
module Enumerable
|
4
|
+
def window_timestamps(window_size_seconds = 3600)
|
5
|
+
group_by { |ts| (ts.to_time.to_i / window_size_seconds) * window_size_seconds }
|
6
|
+
.transform_keys { |ts| Time.at(ts) }
|
7
|
+
.map { |k, v| { k => v } }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/fall/helpers.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fall
|
2
|
+
module Helpers
|
3
|
+
def deflate(...)
|
4
|
+
Zlib::Deflate.deflate(...)
|
5
|
+
end
|
6
|
+
|
7
|
+
def inflate(...)
|
8
|
+
Zlib::Inflate.inflate(...)
|
9
|
+
end
|
10
|
+
|
11
|
+
def deflate!(...)
|
12
|
+
Zlib::Deflate.deflate(...).force_encoding('ASCII-8BIT')
|
13
|
+
end
|
14
|
+
|
15
|
+
def inflate!(...)
|
16
|
+
Zlib::Inflate.inflate(...).force_encoding('ASCII-8BIT')
|
17
|
+
end
|
18
|
+
|
19
|
+
def deflate64(...)
|
20
|
+
Base64.strict_encode64(deflate(...))
|
21
|
+
end
|
22
|
+
|
23
|
+
def inflate64(foo)
|
24
|
+
inflate(Base64.strict_decode64(foo))
|
25
|
+
end
|
26
|
+
|
27
|
+
def inflate64bed(...)
|
28
|
+
Bed.infer(inflate64(...))
|
29
|
+
end
|
30
|
+
|
31
|
+
def parsedate(...)
|
32
|
+
DateTime.parse(...)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/fall/parser.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Fall
|
2
|
+
class Parser
|
3
|
+
def initialize(string)
|
4
|
+
@string = string
|
5
|
+
end
|
6
|
+
|
7
|
+
def stages
|
8
|
+
@string.split('|')
|
9
|
+
.map(&:strip)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
stages
|
14
|
+
.then { identify_streams(_1) }
|
15
|
+
.then { consolidate_streams(_1) }
|
16
|
+
.then { create_operations(_1) }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def create_operations(tuples)
|
22
|
+
result = tuples.reduce([]) do |accum, (type, stages)|
|
23
|
+
klass = operation_klass(type)
|
24
|
+
operations = stages.map { klass.from_code(_1) }
|
25
|
+
[*accum, *operations]
|
26
|
+
end
|
27
|
+
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
def operation_klass(type)
|
32
|
+
case type
|
33
|
+
in :record
|
34
|
+
RecordOperation
|
35
|
+
in :stream
|
36
|
+
StreamOperation
|
37
|
+
else
|
38
|
+
raise ArgumentError, 'Invalid operation type'
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def identify_streams(operations)
|
44
|
+
case operations
|
45
|
+
in [/^stream/] => stream
|
46
|
+
[:stream, [stream]]
|
47
|
+
in [/^stream/ => stream, *rest]
|
48
|
+
[[:stream, [stream]], *identify_streams(rest)]
|
49
|
+
in [*pre, /^stream/ => stream]
|
50
|
+
[*identify_streams(pre), [:stream, [stream]]]
|
51
|
+
in [*pre, /^stream/ => stream, *rest]
|
52
|
+
[*identify_streams(pre), [:stream, [stream]], *identify_streams(rest)]
|
53
|
+
else
|
54
|
+
[[:record, [*operations]]]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def consolidate_streams(operations)
|
59
|
+
case operations
|
60
|
+
in [*pre, [:stream, op_1], [:stream, op_2], *post]
|
61
|
+
consolidate_streams([*pre, [:stream, [*op_1, *op_2]], *post])
|
62
|
+
else
|
63
|
+
operations
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Fall
|
2
|
+
class Pipeline
|
3
|
+
def initialize(source = [].lazy)
|
4
|
+
@source = source
|
5
|
+
@currently_building_pipeline = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_operation(operation)
|
9
|
+
operation.add_to_pipeline(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_record_operation(operation)
|
13
|
+
ensure_current_stage_type(RecordOperationPipeline)
|
14
|
+
@currently_building_pipeline.add_operation(operation)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_stream_operation(operation)
|
18
|
+
ensure_current_stage_type(StreamOperationPipeline)
|
19
|
+
@currently_building_pipeline.add_operation(operation)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_enum
|
23
|
+
@currently_building_pipeline.to_enum
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def ensure_current_stage_type(expected_pipeline_klass)
|
29
|
+
seal_current_stage unless @currently_building_pipeline in ^expected_pipeline_klass | nil
|
30
|
+
@currently_building_pipeline ||= expected_pipeline_klass.new(@source.to_enum)
|
31
|
+
end
|
32
|
+
|
33
|
+
def seal_current_stage
|
34
|
+
@source = @currently_building_pipeline.to_enum
|
35
|
+
@currently_building_pipeline = nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/fall/record.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module Fall
|
2
|
+
class Record
|
3
|
+
include Fall::Helpers
|
4
|
+
|
5
|
+
attr_reader :value
|
6
|
+
|
7
|
+
alphabet = 'abcdefghijklmnopqrstuvwxyz'
|
8
|
+
alphabet.chars.each_with_index do |char, index|
|
9
|
+
define_method char do
|
10
|
+
case @value
|
11
|
+
in Enumerable
|
12
|
+
self.class.wrap(self.value[index]).value
|
13
|
+
else
|
14
|
+
self.value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.wrap(value)
|
20
|
+
case value
|
21
|
+
in Record
|
22
|
+
value
|
23
|
+
else
|
24
|
+
new(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private_class_method :new
|
29
|
+
|
30
|
+
def initialize(value)
|
31
|
+
@value = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def zip(other)
|
35
|
+
to_ary.zip(other.to_ary)
|
36
|
+
end
|
37
|
+
|
38
|
+
def discard?
|
39
|
+
case value
|
40
|
+
in :__discard then true
|
41
|
+
in [*, :__discard, *] then true
|
42
|
+
else false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def value
|
47
|
+
@parsed_value ||= (
|
48
|
+
case @value
|
49
|
+
in /^[1-9][0-9]*$/
|
50
|
+
@value.to_i
|
51
|
+
in /^[0-9\.]+$/
|
52
|
+
@value.to_f
|
53
|
+
in /{.*?".*?".*?}/
|
54
|
+
JSON.parse(@value)
|
55
|
+
else
|
56
|
+
@value
|
57
|
+
end
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_ary
|
62
|
+
case value
|
63
|
+
in Array
|
64
|
+
value
|
65
|
+
else
|
66
|
+
[value]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
alias to_a to_ary
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def method_missing(method_name, *args, &block)
|
75
|
+
if @value.respond_to?(method_name)
|
76
|
+
@value.public_send(method_name, *args, &block)
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def respond_to_missing?(method_name, include_private = false)
|
83
|
+
if @value.respond_to?(method_name)
|
84
|
+
true
|
85
|
+
else
|
86
|
+
super
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Fall
|
2
|
+
class RecordOperation
|
3
|
+
attr_reader :func
|
4
|
+
|
5
|
+
def self.from_code(code)
|
6
|
+
func = instance_eval <<~RUBY
|
7
|
+
lambda do |record|
|
8
|
+
return record if record.discard?
|
9
|
+
|
10
|
+
result = Record.wrap(record.instance_eval { [#{code}] })
|
11
|
+
|
12
|
+
filtered = result.zip(record).reject { _1.size != 2 }.map do |result, original_record|
|
13
|
+
case result
|
14
|
+
in true then original_record
|
15
|
+
in false then :__discard
|
16
|
+
else result
|
17
|
+
end
|
18
|
+
end
|
19
|
+
Record.wrap(filtered)
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
new(func)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(func)
|
26
|
+
@func = func
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_to_pipeline(pipeline)
|
30
|
+
pipeline.add_record_operation(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(record)
|
34
|
+
@func.call(record)
|
35
|
+
end
|
36
|
+
|
37
|
+
def chain(operation)
|
38
|
+
identity = ->(i) { i }
|
39
|
+
|
40
|
+
composed = (func >> (operation.func >> identity))
|
41
|
+
self.class.new(composed)
|
42
|
+
end
|
43
|
+
|
44
|
+
alias >> chain
|
45
|
+
|
46
|
+
def to_proc
|
47
|
+
@func
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fall
|
2
|
+
class RecordOperationPipeline
|
3
|
+
def initialize(source)
|
4
|
+
@source = source
|
5
|
+
@operation = RecordOperation.new(->(args) { args })
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_operation(operation)
|
9
|
+
@operation = @operation.chain(operation)
|
10
|
+
end
|
11
|
+
|
12
|
+
alias add_record_operation add_operation
|
13
|
+
|
14
|
+
def to_enum(...)
|
15
|
+
@source
|
16
|
+
.lazy
|
17
|
+
.map { |value| Record.wrap(value) }
|
18
|
+
.map { |record| @operation.call(record) }
|
19
|
+
.reject(&:discard?)
|
20
|
+
.map(&:value)
|
21
|
+
.to_enum(...)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Fall
|
2
|
+
class StreamOperation
|
3
|
+
attr_reader :func
|
4
|
+
|
5
|
+
def self.from_code(code)
|
6
|
+
func = instance_eval <<~RUBY
|
7
|
+
proc do |stream|
|
8
|
+
#{code}
|
9
|
+
end
|
10
|
+
RUBY
|
11
|
+
new(func)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(func)
|
15
|
+
@func = func
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_to_pipeline(pipeline)
|
19
|
+
pipeline.add_stream_operation(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_proc
|
23
|
+
@func
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(stream)
|
27
|
+
stream.map { Record.wrap(_1) }
|
28
|
+
.then {
|
29
|
+
@func.call(_1)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def chain(operation)
|
34
|
+
identity = ->(i) { i }
|
35
|
+
|
36
|
+
composed = (func >> (operation.func >> identity))
|
37
|
+
self.class.new(composed)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fall
|
2
|
+
class StreamOperationPipeline
|
3
|
+
def initialize(source)
|
4
|
+
@source = source
|
5
|
+
@operation = StreamOperation.new(->(stream) { stream })
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_operation(operation)
|
9
|
+
@operation = @operation.chain(operation)
|
10
|
+
end
|
11
|
+
|
12
|
+
alias add_stream_operation add_operation
|
13
|
+
|
14
|
+
def to_enum
|
15
|
+
@operation.call(@source)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/fall/version.rb
CHANGED
data/lib/fall.rb
CHANGED
@@ -1,7 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'zeitwerk'
|
4
|
+
require 'active_support/core_ext/hash'
|
5
|
+
require 'base64'
|
6
|
+
require 'zlib'
|
7
|
+
require 'date'
|
8
|
+
require '~/repos/bed/lib/bed.rb'
|
9
|
+
|
10
|
+
RubyVM::YJIT.enable rescue nil
|
11
|
+
loader = Zeitwerk::Loader.for_gem
|
12
|
+
loader.setup
|
4
13
|
|
5
14
|
module Fall
|
6
15
|
class Error < StandardError; end
|
7
16
|
end
|
17
|
+
|
18
|
+
module Enumerable
|
19
|
+
include Fall::Ext::Enumerable
|
20
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fall
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.0.
|
4
|
+
version: 0.0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- " David Gillis"
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-07 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: zeitwerk
|
@@ -72,8 +72,17 @@ executables: []
|
|
72
72
|
extensions: []
|
73
73
|
extra_rdoc_files: []
|
74
74
|
files:
|
75
|
-
- lib/draft.rb
|
76
75
|
- lib/fall.rb
|
76
|
+
- lib/fall/ext/enumerable.rb
|
77
|
+
- lib/fall/ext/enumerator/lazy.rb
|
78
|
+
- lib/fall/helpers.rb
|
79
|
+
- lib/fall/parser.rb
|
80
|
+
- lib/fall/pipeline.rb
|
81
|
+
- lib/fall/record.rb
|
82
|
+
- lib/fall/record_operation.rb
|
83
|
+
- lib/fall/record_operation_pipeline.rb
|
84
|
+
- lib/fall/stream_operation.rb
|
85
|
+
- lib/fall/stream_operation_pipeline.rb
|
77
86
|
- lib/fall/version.rb
|
78
87
|
homepage: https://github.com/gillisd/fall
|
79
88
|
licenses:
|
data/lib/draft.rb
DELETED
@@ -1,377 +0,0 @@
|
|
1
|
-
require 'active_support/core_ext/hash'
|
2
|
-
require 'base64'
|
3
|
-
require 'zlib'
|
4
|
-
require 'date'
|
5
|
-
require '~/repos/bed/lib/bed.rb'
|
6
|
-
|
7
|
-
options = { delimiter: ' ', code: nil }
|
8
|
-
|
9
|
-
RubyVM::YJIT.enable rescue nil
|
10
|
-
|
11
|
-
# OptionParser.new do |o|
|
12
|
-
# o.on('-d DELIMITER', '--delimiter DELIMITER') do |d|
|
13
|
-
# options[:delimiter] = d
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# o.on('-e CODE', '--eval CODE') do |e|
|
17
|
-
# options[:code] = e
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# end.parse!
|
21
|
-
|
22
|
-
def deflate(...)
|
23
|
-
Zlib::Deflate.deflate(...)
|
24
|
-
end
|
25
|
-
|
26
|
-
def inflate(...)
|
27
|
-
Zlib::Inflate.inflate(...)
|
28
|
-
end
|
29
|
-
|
30
|
-
def deflate!(...)
|
31
|
-
Zlib::Deflate.deflate(...).force_encoding('ASCII-8BIT')
|
32
|
-
end
|
33
|
-
|
34
|
-
def inflate!(...)
|
35
|
-
Zlib::Inflate.inflate(...).force_encoding('ASCII-8BIT')
|
36
|
-
end
|
37
|
-
|
38
|
-
def deflate64(...)
|
39
|
-
Base64.strict_encode64(deflate(...))
|
40
|
-
end
|
41
|
-
|
42
|
-
def inflate64(...)
|
43
|
-
inflate(Base64.decode64(...))
|
44
|
-
end
|
45
|
-
|
46
|
-
def inflate64bed(...)
|
47
|
-
Bed.infer(inflate64(...))
|
48
|
-
end
|
49
|
-
|
50
|
-
module Enumerable
|
51
|
-
def window_timestamps(window_size_seconds = 3600)
|
52
|
-
group_by { |ts| (ts.to_time.to_i / window_size_seconds) * window_size_seconds }
|
53
|
-
.transform_keys { |ts| Time.at(ts) }
|
54
|
-
.map { |k, v| { k => v } }
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def parsedate(...)
|
59
|
-
DateTime.parse(...)
|
60
|
-
end
|
61
|
-
|
62
|
-
alphabet = 'abcdefghijklmnopqrstuvwxyz'
|
63
|
-
Array.class_eval do
|
64
|
-
alphabet.chars.each_with_index do |char, index|
|
65
|
-
define_method char do
|
66
|
-
self[index]
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
Enumerator::Lazy.class_eval do
|
72
|
-
define_method :stream do
|
73
|
-
self
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def lines(enum = ARGF.readlines.lazy)
|
78
|
-
enum
|
79
|
-
end
|
80
|
-
|
81
|
-
def identify_streams(operations)
|
82
|
-
case operations
|
83
|
-
in [/^stream/] => stream
|
84
|
-
[:stream, [stream]]
|
85
|
-
in [/^stream/ => stream, *rest]
|
86
|
-
[[:stream, [stream]], *identify_streams(rest)]
|
87
|
-
in [*pre, /^stream/ => stream]
|
88
|
-
[*identify_streams(pre), [:stream, [stream]]]
|
89
|
-
in [*pre, /^stream/ => stream, *rest]
|
90
|
-
[*identify_streams(pre), [:stream, [stream]], *identify_streams(rest)]
|
91
|
-
else
|
92
|
-
[[:record, [*operations]]]
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def consolidate_streams(operations)
|
97
|
-
case operations
|
98
|
-
in [*pre, [:stream, op_1], [:stream, op_2], *post]
|
99
|
-
consolidate_streams([*pre, [:stream, [*op_1, *op_2]], *post])
|
100
|
-
else
|
101
|
-
operations
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def groupup(code)
|
106
|
-
code = code.split('|')
|
107
|
-
.map(&:strip)
|
108
|
-
|
109
|
-
consolidate_streams(identify_streams(code))
|
110
|
-
end
|
111
|
-
|
112
|
-
class Transformer
|
113
|
-
attr_accessor :source, :func
|
114
|
-
|
115
|
-
def initialize(source:, func:)
|
116
|
-
@source = source
|
117
|
-
@func = func
|
118
|
-
end
|
119
|
-
|
120
|
-
def to_enum(...)
|
121
|
-
source.map { |*record| func.call(*record) }
|
122
|
-
.reject do |args|
|
123
|
-
case args
|
124
|
-
in [*, :__discard, *] then true
|
125
|
-
in :__discard then true
|
126
|
-
else false
|
127
|
-
end
|
128
|
-
end
|
129
|
-
.to_enum(...)
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
class StreamOperation
|
134
|
-
attr_reader :func
|
135
|
-
|
136
|
-
def self.from_code(code)
|
137
|
-
func = instance_eval <<~RUBY
|
138
|
-
proc do |stream|
|
139
|
-
#{code}
|
140
|
-
end
|
141
|
-
RUBY
|
142
|
-
new(func)
|
143
|
-
end
|
144
|
-
|
145
|
-
def initialize(func)
|
146
|
-
@func = func
|
147
|
-
end
|
148
|
-
|
149
|
-
def add_to_pipeline(pipeline)
|
150
|
-
pipeline.add_stream_operation(pipeline)
|
151
|
-
end
|
152
|
-
|
153
|
-
def to_proc
|
154
|
-
@func
|
155
|
-
end
|
156
|
-
|
157
|
-
def call(*args)
|
158
|
-
@func.call(*args)
|
159
|
-
end
|
160
|
-
|
161
|
-
def chain(operation)
|
162
|
-
identity = ->(i) { i }
|
163
|
-
|
164
|
-
composed = (func >> (operation.func >> identity))
|
165
|
-
self.class.new(composed)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
class RecordOperation
|
170
|
-
attr_reader :func
|
171
|
-
|
172
|
-
def self.from_code(code)
|
173
|
-
func = instance_eval <<~RUBY
|
174
|
-
proc do |*args|
|
175
|
-
|
176
|
-
results = args.flatten(1).instance_eval { [#{code}] }
|
177
|
-
|
178
|
-
foo = results.zip(args).reject { _1.size != 2 }.map do |result, arg|
|
179
|
-
case result
|
180
|
-
in true then arg
|
181
|
-
in false then :__discard
|
182
|
-
else result
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
RUBY
|
187
|
-
new(func)
|
188
|
-
end
|
189
|
-
|
190
|
-
def initialize(func)
|
191
|
-
@func = func
|
192
|
-
end
|
193
|
-
|
194
|
-
def add_to_pipeline(pipeline)
|
195
|
-
pipeline.add_record_operation(pipeline)
|
196
|
-
end
|
197
|
-
|
198
|
-
def call(*args)
|
199
|
-
@func.call(*args)
|
200
|
-
end
|
201
|
-
|
202
|
-
def chain(operation)
|
203
|
-
identity = ->(i) { i }
|
204
|
-
|
205
|
-
composed = (func >> (operation.func >> identity))
|
206
|
-
self.class.new(composed)
|
207
|
-
end
|
208
|
-
|
209
|
-
alias >> chain
|
210
|
-
|
211
|
-
def to_proc
|
212
|
-
@func
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
class RecordOperationPipeline
|
217
|
-
def initialize(source)
|
218
|
-
@source = source
|
219
|
-
@operation = RecordOperation.new(->(*args) { args })
|
220
|
-
end
|
221
|
-
|
222
|
-
def add_operation(operation)
|
223
|
-
@operation = @operation.chain(operation)
|
224
|
-
end
|
225
|
-
|
226
|
-
def to_enum(...)
|
227
|
-
@source.map { |*record| @operation.call(*record) }
|
228
|
-
.reject do |args|
|
229
|
-
case args
|
230
|
-
in [*, :__discard, *] then true
|
231
|
-
in :__discard then true
|
232
|
-
else false
|
233
|
-
end
|
234
|
-
end
|
235
|
-
.to_enum(...)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
class StreamOperationPipeline
|
240
|
-
def initialize(source)
|
241
|
-
@source = source
|
242
|
-
@operation = StreamOperation.new(->(stream) { stream })
|
243
|
-
end
|
244
|
-
|
245
|
-
def add_operation(operation)
|
246
|
-
@operation = @operation.chain(operation)
|
247
|
-
end
|
248
|
-
|
249
|
-
def to_enum
|
250
|
-
@operation.call(@source)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
class Pipeline
|
255
|
-
def initialize(source = [].lazy)
|
256
|
-
@source = source
|
257
|
-
@currently_building_pipeline = nil
|
258
|
-
end
|
259
|
-
|
260
|
-
def add_operation(operation)
|
261
|
-
operation.add_to_pipeline(self)
|
262
|
-
end
|
263
|
-
|
264
|
-
def add_record_operation(operation)
|
265
|
-
ensure_current_stage_type(RecordOperationPipeline)
|
266
|
-
@currently_building_pipeline.add_operation(operation)
|
267
|
-
end
|
268
|
-
|
269
|
-
def add_stream_operation(operation)
|
270
|
-
ensure_current_stage_type(StreamOperationPipeline)
|
271
|
-
@currently_building_pipeline.add_operation(operation)
|
272
|
-
end
|
273
|
-
|
274
|
-
def to_enum
|
275
|
-
@currently_building_pipeline.to_enum
|
276
|
-
end
|
277
|
-
|
278
|
-
private
|
279
|
-
|
280
|
-
def ensure_current_stage_type(expected_pipeline_klass)
|
281
|
-
seal_current_stage unless @currently_building_pipeline in ^expected_pipeline_klass | nil
|
282
|
-
@currently_building_pipeline ||= expected_pipeline_klass.new(@source.to_enum)
|
283
|
-
end
|
284
|
-
|
285
|
-
def seal_current_stage
|
286
|
-
@source = @currently_building_pipeline.to_enum
|
287
|
-
@currently_building_pipeline = nil
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
Processor = lambda do |enum, **input_options|
|
292
|
-
|
293
|
-
options.merge(input_options) => { code:, delimiter: }
|
294
|
-
operations = groupup code
|
295
|
-
|
296
|
-
lams = operations.map do |type, pipeline|
|
297
|
-
composed = (
|
298
|
-
case type
|
299
|
-
in :record
|
300
|
-
ops = pipeline.map do |op|
|
301
|
-
proc do |errors, it|
|
302
|
-
code = "[#{op}].flatten(1)"
|
303
|
-
if code
|
304
|
-
begin
|
305
|
-
[errors, it.instance_eval(code)]
|
306
|
-
rescue => e
|
307
|
-
warn e
|
308
|
-
errors << e.full_message
|
309
|
-
[errors, it]
|
310
|
-
end
|
311
|
-
else
|
312
|
-
[errors, it]
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
identity = ->(i) { i }
|
317
|
-
composed = ops.reverse.reduce(identity) { |accum, fn| fn >> accum }
|
318
|
-
composed.curry(2)[[]]
|
319
|
-
in :stream
|
320
|
-
ops = pipeline.map do |op|
|
321
|
-
proc do |stream|
|
322
|
-
begin
|
323
|
-
stream = [stream] unless stream.is_a?(Enumerator)
|
324
|
-
stream.instance_eval(op)
|
325
|
-
rescue => e
|
326
|
-
warn e
|
327
|
-
end
|
328
|
-
end
|
329
|
-
end
|
330
|
-
identity = ->(i) { i }
|
331
|
-
ops.reverse.reduce(identity) { |accum, fn| fn >> accum }
|
332
|
-
else
|
333
|
-
raise "Unknown type: #{type}"
|
334
|
-
end
|
335
|
-
)
|
336
|
-
[type, composed]
|
337
|
-
end
|
338
|
-
|
339
|
-
enum
|
340
|
-
.map(&:chomp)
|
341
|
-
.map { |line| line.split(options[:delimiter]) }
|
342
|
-
.map { |line| line.reduce([0, {}]) { |(index, hash), field| hash[alphabet[index]] = field; [index + 1, hash] } }
|
343
|
-
.map(&:last)
|
344
|
-
.map do |i|
|
345
|
-
Bed.infer(i)
|
346
|
-
end
|
347
|
-
.then { |enum|
|
348
|
-
lams.reduce(enum) { |accum, (type, lam)|
|
349
|
-
case type
|
350
|
-
in :record
|
351
|
-
accum.map(&lam).map(&:last)
|
352
|
-
in :stream
|
353
|
-
[[lam.call(accum.flat_map(&:last))].flatten(1)]
|
354
|
-
else
|
355
|
-
raise "Unknown type: #{type}"
|
356
|
-
end
|
357
|
-
}
|
358
|
-
}
|
359
|
-
.then { |obj|
|
360
|
-
case obj
|
361
|
-
in [Array => arr]
|
362
|
-
arr
|
363
|
-
in [[Enumerator::Lazy => lazy]]
|
364
|
-
lazy
|
365
|
-
in [Enumerator::Lazy => lazy]
|
366
|
-
lazy
|
367
|
-
else
|
368
|
-
obj
|
369
|
-
end
|
370
|
-
}
|
371
|
-
# .map do |record|
|
372
|
-
# record.join(delimiter) if record.respond_to?(:join)
|
373
|
-
# puts record
|
374
|
-
# end
|
375
|
-
end
|
376
|
-
|
377
|
-
# process.call(options, ARGF.each_line.lazy)
|