datapipes 0.1.3 → 0.1.4
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/CHANGELOG.md +3 -0
- data/README.md +2 -1
- data/examples/composing.rb +5 -2
- data/examples/lib/reverse_acc.rb +11 -0
- data/lib/datapipes.rb +10 -0
- data/lib/datapipes/composable.rb +2 -0
- data/lib/datapipes/pipe.rb +13 -0
- data/lib/datapipes/sink.rb +41 -1
- data/lib/datapipes/source.rb +18 -2
- data/lib/datapipes/tube.rb +21 -0
- data/lib/datapipes/version.rb +1 -1
- data/spec/basics_spec.rb +1 -1
- data/spec/composing_spec.rb +8 -2
- data/spec/sink_spec.rb +11 -4
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8abb0451500188b58038dbb2731ff2fcb8977f4b
|
4
|
+
data.tar.gz: 7f6348febf36db66e7939bca687b820f5a029fe4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43ba18b6df1af0b1118ac7c39c719d0f7d63160cd90247e1e2a0b4dd8baf3992a888fede632491d7f276b949b07910f327f761758f719e4ff36604626e3db385
|
7
|
+
data.tar.gz: 413545e4c611a78e48f98ce0cfb791003b7ae9512b16adee0535159e49cd30368c81b6a2671e7bc62179cd1ae52984de6ebd1846a31a2b9f1973aa90faeb4a6f
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -46,6 +46,7 @@ are composable individually. So the diagram above will be:
|
|
46
46
|
```
|
47
47
|
|
48
48
|
## Installation
|
49
|
+
__datapipes requires Ruby >= 2.0.__
|
49
50
|
|
50
51
|
Add this line to your application's Gemfile:
|
51
52
|
|
@@ -63,7 +64,7 @@ Or install it yourself as:
|
|
63
64
|
You have to define your own Source, Tube and Sink. See more in `examples`.
|
64
65
|
|
65
66
|
### Composing objects
|
66
|
-
|
67
|
+
See more in `examples/composing.rb`.
|
67
68
|
|
68
69
|
## Contributing
|
69
70
|
|
data/examples/composing.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
require 'datapipes'
|
2
2
|
|
3
|
-
|
3
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
4
4
|
require 'list'
|
5
5
|
require 'long_task'
|
6
6
|
require 'mul'
|
7
7
|
require 'triple'
|
8
8
|
require 'acc'
|
9
|
+
require 'reverse_acc'
|
9
10
|
|
10
11
|
acc = Acc.new
|
12
|
+
rev_acc = ReverseAcc.new
|
11
13
|
|
12
14
|
source = List.new + LongTask.new(21..30)
|
13
15
|
tube = Mul.new >> Triple.new
|
14
|
-
sink = acc
|
16
|
+
sink = acc + rev_acc
|
15
17
|
|
16
18
|
datapipe = Datapipes.new(
|
17
19
|
source,
|
@@ -22,3 +24,4 @@ datapipe = Datapipes.new(
|
|
22
24
|
datapipe.run_resource
|
23
25
|
|
24
26
|
p acc.stock
|
27
|
+
p rev_acc.stock
|
data/lib/datapipes.rb
CHANGED
@@ -9,6 +9,11 @@ require 'datapipes/version'
|
|
9
9
|
Thread.abort_on_exception = true
|
10
10
|
|
11
11
|
class Datapipes
|
12
|
+
# Pass datapipes components instances.
|
13
|
+
# Each component can be composed. See detail in examples.
|
14
|
+
#
|
15
|
+
# tube and pipe are optional.
|
16
|
+
# If not given tube, a default tube which takes no effect is used.
|
12
17
|
def initialize(source, sink, tube: Tube.new, pipe: Pipe.new)
|
13
18
|
@source = source
|
14
19
|
@tube = tube
|
@@ -16,6 +21,11 @@ class Datapipes
|
|
16
21
|
@pipe = pipe
|
17
22
|
end
|
18
23
|
|
24
|
+
# Run sources, data flow via pipe, tubes and sinks work.
|
25
|
+
# Everything work with just call this method.
|
26
|
+
#
|
27
|
+
# When all sources finished producing, and all sinks did their jobs,
|
28
|
+
# this method returns.
|
19
29
|
def run_resource
|
20
30
|
@source.pipe = @pipe
|
21
31
|
runners = @source.run_all
|
data/lib/datapipes/composable.rb
CHANGED
data/lib/datapipes/pipe.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
class Datapipes
|
2
|
+
# Pipe is not only data pipeline but handling asynchronous.
|
3
|
+
#
|
4
|
+
# You can make your own pipe with database or something, but
|
5
|
+
# becareful this object used in multi thread.
|
6
|
+
#
|
7
|
+
# If you make your own, you can override _initialize_, because
|
8
|
+
# this is not used in internal.
|
9
|
+
#
|
10
|
+
# Then supply _recieve_ and _pull_. _pull_ must cause thread blocking
|
11
|
+
# when it is empty.
|
2
12
|
class Pipe
|
13
|
+
# You can override and don't need to call super in sub class.
|
3
14
|
def initialize
|
4
15
|
@queue ||= Queue.new
|
5
16
|
end
|
6
17
|
|
18
|
+
# Emit data to pipe.
|
7
19
|
def recieve(data)
|
8
20
|
@queue.enq data
|
9
21
|
end
|
10
22
|
|
23
|
+
# _pull_ must cause thread blocking when it is empty.
|
11
24
|
def pull
|
12
25
|
@queue.deq
|
13
26
|
end
|
data/lib/datapipes/sink.rb
CHANGED
@@ -1,9 +1,49 @@
|
|
1
1
|
class Datapipes
|
2
2
|
# Build your own sink logic in `run` method.
|
3
|
+
#
|
4
|
+
# Be careful each sinks are executed concurrently.
|
5
|
+
# Avoid race condition in multi sinks.
|
6
|
+
#
|
7
|
+
# This is bad:
|
8
|
+
#
|
9
|
+
# $shared = []
|
10
|
+
#
|
11
|
+
# class A < Datapipes::Sink
|
12
|
+
# def run(data)
|
13
|
+
# $shared << data
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class B < Datapipes::Sink
|
18
|
+
# def run(data)
|
19
|
+
# $shared << data
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# On the other hand, a sink is called serially. So you can
|
24
|
+
# touch shared object in one sink logic.
|
25
|
+
#
|
26
|
+
# This is good:
|
27
|
+
#
|
28
|
+
# class A < Datapipes::Source
|
29
|
+
# def initialize
|
30
|
+
# @shared = []
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# def run(data)
|
34
|
+
# @shared << data
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
3
38
|
class Sink
|
4
39
|
include Composable
|
5
40
|
|
6
|
-
#
|
41
|
+
# Override this in sub class
|
42
|
+
def run(data)
|
43
|
+
data
|
44
|
+
end
|
45
|
+
|
46
|
+
# For internal uses.
|
7
47
|
def run_all(data)
|
8
48
|
@accumulated ||= [self]
|
9
49
|
count = Parallel.processor_count
|
data/lib/datapipes/source.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Datapipes
|
2
|
-
#
|
3
2
|
# Build your own source logic in `run` method.
|
4
3
|
# Use `produce` method to emitt data to pipe.
|
5
4
|
#
|
@@ -7,17 +6,34 @@ class Datapipes
|
|
7
6
|
# 10.times {|i| produce(i) }
|
8
7
|
# end
|
9
8
|
#
|
9
|
+
# You can use infinitie stream like:
|
10
|
+
#
|
11
|
+
# def run
|
12
|
+
# twitter_client.userstream do |event|
|
13
|
+
# produce(event)
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
10
17
|
class Source
|
11
18
|
include Composable
|
12
19
|
|
13
|
-
|
20
|
+
# Override in sub class.
|
21
|
+
def run
|
22
|
+
end
|
14
23
|
|
24
|
+
# For internal used.
|
25
|
+
#
|
26
|
+
# Run accumulated sources which are set by composition.
|
27
|
+
# Each source works in new thread.
|
15
28
|
def run_all
|
16
29
|
@accumulated ||= [self]
|
17
30
|
set_pipe
|
18
31
|
@accumulated.map {|s| Thread.new { s.run } }
|
19
32
|
end
|
20
33
|
|
34
|
+
# For internal uses. Do not touch.
|
35
|
+
attr_accessor :pipe
|
36
|
+
|
21
37
|
private
|
22
38
|
|
23
39
|
def produce(data)
|
data/lib/datapipes/tube.rb
CHANGED
@@ -3,6 +3,9 @@ class Datapipes
|
|
3
3
|
#
|
4
4
|
# Build your own tube logic in `run` method.
|
5
5
|
class Tube
|
6
|
+
# _>>_ is used to compose tubes. See usage in examples.
|
7
|
+
#
|
8
|
+
# Tube composition satisfies associative law. See more in spec.
|
6
9
|
def >>(op2)
|
7
10
|
op1 = self
|
8
11
|
Tube.new.tap do |o|
|
@@ -12,6 +15,24 @@ class Datapipes
|
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
18
|
+
# Override this in sub class.
|
19
|
+
#
|
20
|
+
# _run_ recieves any data, so you have to ignore
|
21
|
+
# unexpected data.
|
22
|
+
#
|
23
|
+
# def run(data)
|
24
|
+
# if accept? data
|
25
|
+
# [data, data, data]
|
26
|
+
# else
|
27
|
+
# data
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def accept?(data)
|
32
|
+
# data.is_a? Integer and data > 3
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Don't forget to return data in else clause.
|
15
36
|
def run(data)
|
16
37
|
data
|
17
38
|
end
|
data/lib/datapipes/version.rb
CHANGED
data/spec/basics_spec.rb
CHANGED
data/spec/composing_spec.rb
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
$:.unshift File.expand_path('../../examples/lib', __FILE__)
|
4
4
|
require 'list'
|
5
5
|
require 'long_task'
|
6
6
|
require 'mul'
|
7
7
|
require 'triple'
|
8
8
|
require 'acc'
|
9
|
+
require 'reverse_acc'
|
9
10
|
|
10
11
|
describe 'composability' do
|
11
12
|
let(:acc) { Acc.new }
|
13
|
+
let(:rev_acc) { ReverseAcc.new }
|
14
|
+
|
12
15
|
let(:source) { List.new + LongTask.new(21..30) }
|
13
16
|
let(:tube) { Mul.new >> Triple.new }
|
17
|
+
let(:sink) { acc + rev_acc }
|
14
18
|
|
15
19
|
let(:datapipe) do
|
16
20
|
Datapipes.new(
|
17
21
|
source,
|
18
|
-
|
22
|
+
sink,
|
19
23
|
tube: tube
|
20
24
|
)
|
21
25
|
end
|
22
26
|
|
23
27
|
it 'runs without errors' do
|
24
28
|
datapipe.run_resource
|
29
|
+
|
25
30
|
expect(acc.stock).to have(20).items
|
31
|
+
expect(rev_acc.stock).to have(20).items
|
26
32
|
end
|
27
33
|
end
|
data/spec/sink_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe Datapipes::Sink do
|
|
7
7
|
|
8
8
|
it 'composes' do
|
9
9
|
subject.run_all(5)
|
10
|
-
expect(list).to
|
10
|
+
expect(list).to have(3).items
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -16,7 +16,7 @@ describe Datapipes::Sink do
|
|
16
16
|
|
17
17
|
it 'composes' do
|
18
18
|
subject.run_all(5)
|
19
|
-
expect(list).to
|
19
|
+
expect(list).to have(4).items
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -25,11 +25,18 @@ describe Datapipes::Sink do
|
|
25
25
|
let(:b) { sink_a + (sink_b + sink_c) }
|
26
26
|
|
27
27
|
it 'keeps' do
|
28
|
-
|
28
|
+
a.run_all(3)
|
29
|
+
size_a = list.size
|
30
|
+
list.clear
|
31
|
+
|
32
|
+
b.run_all(3)
|
33
|
+
size_b = list.size
|
34
|
+
|
35
|
+
expect(size_a).to eq size_b
|
29
36
|
end
|
30
37
|
end
|
31
38
|
|
32
|
-
let(:list) {
|
39
|
+
let(:list) { Queue.new }
|
33
40
|
|
34
41
|
let(:sink_a) do
|
35
42
|
list_ = list
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: datapipes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Taiki ONO
|
@@ -132,6 +132,7 @@ files:
|
|
132
132
|
- examples/lib/long_task.rb
|
133
133
|
- examples/lib/mul.rb
|
134
134
|
- examples/lib/print.rb
|
135
|
+
- examples/lib/reverse_acc.rb
|
135
136
|
- examples/lib/triple.rb
|
136
137
|
- lib/datapipes.rb
|
137
138
|
- lib/datapipes/composable.rb
|