deadly_serious 1.0.2 → 2.0.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +2 -2
- data/deadly_serious.gemspec +1 -0
- data/lib/deadly_serious.rb +7 -6
- data/lib/deadly_serious/engine/auto_pipe.rb +33 -21
- data/lib/deadly_serious/engine/channel.rb +15 -130
- data/lib/deadly_serious/engine/channel/file_channel.rb +50 -0
- data/lib/deadly_serious/engine/channel/pipe_channel.rb +59 -0
- data/lib/deadly_serious/engine/channel/socket/master_mind.rb +42 -0
- data/lib/deadly_serious/engine/channel/socket/minion.rb +24 -0
- data/lib/deadly_serious/engine/channel/socket/socket_sink_recvr.rb +34 -0
- data/lib/deadly_serious/engine/channel/socket/socket_sink_sendr.rb +26 -0
- data/lib/deadly_serious/engine/channel/socket/socket_vent_recvr.rb +32 -0
- data/lib/deadly_serious/engine/channel/socket/socket_vent_sendr.rb +30 -0
- data/lib/deadly_serious/engine/channel/socket_channel.rb +75 -0
- data/lib/deadly_serious/engine/commands.rb +29 -8
- data/lib/deadly_serious/engine/config.rb +55 -0
- data/lib/deadly_serious/engine/file_monitor.rb +57 -0
- data/lib/deadly_serious/engine/json_io.rb +13 -11
- data/lib/deadly_serious/engine/pipeline.rb +31 -87
- data/lib/deadly_serious/engine/ruby_object_container.rb +42 -0
- data/lib/deadly_serious/engine/so_command_container.rb +56 -0
- data/lib/deadly_serious/processes/converter.rb +12 -0
- data/lib/deadly_serious/processes/lambda.rb +4 -2
- data/lib/deadly_serious/processes/resilient_splitter.rb +1 -1
- data/lib/deadly_serious/version.rb +1 -1
- data/spec/lib/deadly_serious/engine/auto_pipe_spec.rb +41 -0
- data/spec/lib/deadly_serious/engine/channel/socket_channel_spec.rb +159 -0
- data/spec/{deadly_serious → lib/deadly_serious}/engine/commands_spec.rb +0 -0
- data/spec/lib/deadly_serious/engine/file_monitor_spec.rb +69 -0
- data/spec/{deadly_serious → lib/deadly_serious}/engine/json_io_spec.rb +0 -0
- data/spec/{deadly_serious → lib/deadly_serious}/engine/pipeline_spec.rb +37 -40
- data/spec/spec_helper.rb +4 -1
- metadata +51 -14
- data/lib/deadly_serious/engine/lazy_io.rb +0 -82
- data/lib/deadly_serious/engine/open_io.rb +0 -39
- data/lib/deadly_serious/processes/joiner.rb +0 -15
File without changes
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
include DeadlySerious::Engine
|
3
|
+
|
4
|
+
describe FileMonitor do
|
5
|
+
let(:test_file) { '/tmp/deadly_serious_test/test_file' }
|
6
|
+
let(:test_file2) { '/tmp/deadly_serious_test/test_file2' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
FileUtils.makedirs('/tmp/deadly_serious_test')
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
FileUtils.rm_rf('/tmp/deadly_serious_test')
|
14
|
+
end
|
15
|
+
|
16
|
+
subject { FileMonitor.new(test_file) }
|
17
|
+
|
18
|
+
it 'blocks until file created' do
|
19
|
+
t = Thread.new { subject.wait_creation }
|
20
|
+
|
21
|
+
sleep 0.1
|
22
|
+
expect(t.alive?).to be_truthy
|
23
|
+
|
24
|
+
`touch #{test_file}`
|
25
|
+
|
26
|
+
sleep 0.1
|
27
|
+
expect(t.alive?).to be_falsey
|
28
|
+
|
29
|
+
t.join(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "don't block if file already exists" do
|
33
|
+
`touch #{test_file}`
|
34
|
+
t = Thread.new { subject.wait_creation }
|
35
|
+
|
36
|
+
sleep 0.1
|
37
|
+
expect(t.alive?).to be_falsey
|
38
|
+
|
39
|
+
t.join(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'blocks until file change' do
|
43
|
+
`touch #{test_file}`
|
44
|
+
t = Thread.new { subject.wait_modification }
|
45
|
+
|
46
|
+
sleep 0.1
|
47
|
+
expect(t.alive?).to be_truthy
|
48
|
+
|
49
|
+
File.write(test_file, "test\n")
|
50
|
+
|
51
|
+
sleep 0.1
|
52
|
+
expect(t.alive?).to be_falsey
|
53
|
+
|
54
|
+
t.join(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'returns modified file' do
|
58
|
+
`touch #{test_file}`
|
59
|
+
`touch #{test_file2}`
|
60
|
+
t = Thread.new { FileMonitor.new(test_file, test_file2).wait_modification }
|
61
|
+
|
62
|
+
sleep 0.1
|
63
|
+
expect(t.alive?).to be_truthy
|
64
|
+
|
65
|
+
File.write(test_file2, "test\n")
|
66
|
+
t.join(1)
|
67
|
+
expect(t.value).to eq test_file2
|
68
|
+
end
|
69
|
+
end
|
File without changes
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'fileutils'
|
3
3
|
include DeadlySerious::Engine
|
4
|
+
include DeadlySerious::Processes
|
4
5
|
|
5
6
|
describe Pipeline do
|
6
7
|
DELAY = 0.5 # seconds
|
@@ -13,15 +14,23 @@ describe Pipeline do
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
class
|
17
|
+
class TestComponentMultiplier
|
18
|
+
def initialize(multi)
|
19
|
+
@multi = multi
|
20
|
+
end
|
21
|
+
|
17
22
|
def run(readers:, writers:)
|
18
23
|
reader = JsonIo.new(readers.first)
|
19
24
|
writer = JsonIo.new(writers.first)
|
20
25
|
|
21
26
|
reader.each do |(number)|
|
22
|
-
writer << [number *
|
27
|
+
writer << [number * @multi]
|
23
28
|
end
|
24
29
|
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
format '%s[%d]', self.class.name, Process.pid
|
33
|
+
end
|
25
34
|
end
|
26
35
|
|
27
36
|
before do
|
@@ -63,37 +72,6 @@ describe Pipeline do
|
|
63
72
|
expect(finish - start).to be >= DELAY
|
64
73
|
end
|
65
74
|
|
66
|
-
it 'kills all children on SIGTERM' do
|
67
|
-
pending 'unpredictable'
|
68
|
-
start = Time.now
|
69
|
-
|
70
|
-
reader, writer = IO.pipe
|
71
|
-
pipeline_id = fork do
|
72
|
-
reader.close
|
73
|
-
pipeline = Pipeline.new do |p|
|
74
|
-
p.spawn_process(TestComponentTime, DELAY)
|
75
|
-
p.spawn_process(TestComponentTime, DELAY)
|
76
|
-
p.spawn_process(TestComponentTime, DELAY)
|
77
|
-
p.spawn_process(TestComponentTime, DELAY)
|
78
|
-
writer << p.pids
|
79
|
-
writer.close
|
80
|
-
end
|
81
|
-
pipeline.run
|
82
|
-
end
|
83
|
-
writer.close
|
84
|
-
c1, c2, c3, c4 = eval(reader.readlines.first)
|
85
|
-
reader.close
|
86
|
-
expect(c1).to be_running
|
87
|
-
expect(c2).to be_running
|
88
|
-
expect(c3).to be_running
|
89
|
-
expect(c4).to be_running
|
90
|
-
Process.kill('SIGTERM', pipeline_id)
|
91
|
-
Process.wait(pipeline_id)
|
92
|
-
finish = Time.now
|
93
|
-
expect { Process.wait2(pipeline_id, Process::WNOHANG) }.to raise_error Errno::ECHILD
|
94
|
-
expect(finish - start).to be < (DELAY / 4)
|
95
|
-
end
|
96
|
-
|
97
75
|
it 'spawns children in parallel' do
|
98
76
|
pipeline = Pipeline.new do |p|
|
99
77
|
p.spawn_process(TestComponentTime, DELAY)
|
@@ -111,29 +89,30 @@ describe Pipeline do
|
|
111
89
|
end
|
112
90
|
|
113
91
|
it 'spawns linux commands' do
|
92
|
+
open(test_file, 'w') { |f| f.puts('line 1'); f.puts('line 2')}
|
114
93
|
pipeline = Pipeline.new do |p|
|
115
94
|
p.from_file(test_file)
|
116
95
|
p.spawn_command('cat')
|
117
96
|
p.to_file(result_file)
|
118
97
|
end
|
119
|
-
expect(test_file).to_not exists
|
120
98
|
pipeline.run
|
121
|
-
expect(
|
99
|
+
expect(result_file).to exists
|
100
|
+
expect(File.readlines(result_file)).to eq ["line 1\n", "line 2\n"]
|
122
101
|
end
|
123
102
|
|
124
103
|
it 'connects pipes with Component Classes' do
|
125
|
-
create_file(test_file, [[1], [
|
104
|
+
create_file(test_file, [[1], [3], [5]])
|
126
105
|
pipeline = Pipeline.new do |p|
|
127
106
|
p.from_file(test_file)
|
128
|
-
p.spawn_process(
|
129
|
-
p.spawn_process(
|
107
|
+
p.spawn_process(TestComponentMultiplier.new(2))
|
108
|
+
p.spawn_process(TestComponentMultiplier.new(2))
|
130
109
|
p.to_file(result_file)
|
131
110
|
end
|
132
111
|
pipeline.run
|
133
|
-
expect(result_file).to have_content [[4], [
|
112
|
+
expect(result_file).to have_content [[4], [12], [20]]
|
134
113
|
end
|
135
114
|
|
136
|
-
it 'connects pipes with shell commands'
|
115
|
+
it 'connects pipes with shell commands'do
|
137
116
|
create_file(test_file, [[1], [2], [3]])
|
138
117
|
pipeline = Pipeline.new do |p|
|
139
118
|
p.from_file(test_file)
|
@@ -144,4 +123,22 @@ describe Pipeline do
|
|
144
123
|
pipeline.run
|
145
124
|
expect(result_file).to have_content [['_1_'], ['_2_'], ['_3_']]
|
146
125
|
end
|
126
|
+
|
127
|
+
it 'parallelize things' do
|
128
|
+
create_file(test_file, (1..10_000).map { |i| [i] })
|
129
|
+
pipeline = Pipeline.new do |p|
|
130
|
+
p.from_file(test_file)
|
131
|
+
p.spawn_process(Converter.new, writers: ['>{localhost:5555'])
|
132
|
+
p.spawn_process(TestComponentMultiplier.new(10), readers: ['<{localhost:5555'], writers: ['>}localhost:5556'])
|
133
|
+
p.spawn_process(TestComponentMultiplier.new(100), readers: ['<{localhost:5555'], writers: ['>}localhost:5556'])
|
134
|
+
p.spawn_process(TestComponentMultiplier.new(1000), readers: ['<{localhost:5555'], writers: ['>}localhost:5556'])
|
135
|
+
p.spawn_process(Converter.new, readers: ['<}localhost:5556'])
|
136
|
+
p.to_file(result_file)
|
137
|
+
end
|
138
|
+
pipeline.run
|
139
|
+
lines = IO.readlines(result_file)
|
140
|
+
expect(lines.size).to eq 10_000
|
141
|
+
expect(lines).to include("[10]\n") | include("[100]\n") | include("[1000]\n")
|
142
|
+
expect(lines).to include("[100000]\n") | include("[1000000]\n") | include("[10000000]\n")
|
143
|
+
end
|
147
144
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deadly_serious
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0.pre.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ronie Uliana
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,26 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rbczmq
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.7'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 1.7.9
|
107
|
+
type: :runtime
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - "~>"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '1.7'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 1.7.9
|
97
117
|
description: Flow Based Programming Maestro
|
98
118
|
email:
|
99
119
|
- ronie.uliana@gmail.com
|
@@ -113,19 +133,33 @@ files:
|
|
113
133
|
- lib/deadly_serious.rb
|
114
134
|
- lib/deadly_serious/engine/auto_pipe.rb
|
115
135
|
- lib/deadly_serious/engine/channel.rb
|
136
|
+
- lib/deadly_serious/engine/channel/file_channel.rb
|
137
|
+
- lib/deadly_serious/engine/channel/pipe_channel.rb
|
138
|
+
- lib/deadly_serious/engine/channel/socket/master_mind.rb
|
139
|
+
- lib/deadly_serious/engine/channel/socket/minion.rb
|
140
|
+
- lib/deadly_serious/engine/channel/socket/socket_sink_recvr.rb
|
141
|
+
- lib/deadly_serious/engine/channel/socket/socket_sink_sendr.rb
|
142
|
+
- lib/deadly_serious/engine/channel/socket/socket_vent_recvr.rb
|
143
|
+
- lib/deadly_serious/engine/channel/socket/socket_vent_sendr.rb
|
144
|
+
- lib/deadly_serious/engine/channel/socket_channel.rb
|
116
145
|
- lib/deadly_serious/engine/commands.rb
|
146
|
+
- lib/deadly_serious/engine/config.rb
|
147
|
+
- lib/deadly_serious/engine/file_monitor.rb
|
117
148
|
- lib/deadly_serious/engine/json_io.rb
|
118
|
-
- lib/deadly_serious/engine/lazy_io.rb
|
119
|
-
- lib/deadly_serious/engine/open_io.rb
|
120
149
|
- lib/deadly_serious/engine/pipeline.rb
|
121
|
-
- lib/deadly_serious/
|
150
|
+
- lib/deadly_serious/engine/ruby_object_container.rb
|
151
|
+
- lib/deadly_serious/engine/so_command_container.rb
|
152
|
+
- lib/deadly_serious/processes/converter.rb
|
122
153
|
- lib/deadly_serious/processes/lambda.rb
|
123
154
|
- lib/deadly_serious/processes/resilient_splitter.rb
|
124
155
|
- lib/deadly_serious/processes/splitter.rb
|
125
156
|
- lib/deadly_serious/version.rb
|
126
|
-
- spec/deadly_serious/engine/
|
127
|
-
- spec/deadly_serious/engine/
|
128
|
-
- spec/deadly_serious/engine/
|
157
|
+
- spec/lib/deadly_serious/engine/auto_pipe_spec.rb
|
158
|
+
- spec/lib/deadly_serious/engine/channel/socket_channel_spec.rb
|
159
|
+
- spec/lib/deadly_serious/engine/commands_spec.rb
|
160
|
+
- spec/lib/deadly_serious/engine/file_monitor_spec.rb
|
161
|
+
- spec/lib/deadly_serious/engine/json_io_spec.rb
|
162
|
+
- spec/lib/deadly_serious/engine/pipeline_spec.rb
|
129
163
|
- spec/spec_helper.rb
|
130
164
|
homepage: https://github.com/ruliana/deadly_serious
|
131
165
|
licenses:
|
@@ -142,19 +176,22 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
142
176
|
version: '2.1'
|
143
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
178
|
requirements:
|
145
|
-
- - "
|
179
|
+
- - ">"
|
146
180
|
- !ruby/object:Gem::Version
|
147
|
-
version:
|
181
|
+
version: 1.3.1
|
148
182
|
requirements: []
|
149
183
|
rubyforge_project:
|
150
|
-
rubygems_version: 2.4.
|
184
|
+
rubygems_version: 2.4.8
|
151
185
|
signing_key:
|
152
186
|
specification_version: 4
|
153
187
|
summary: Flow Based Programming maestro that relies on named pipes and Linux processes
|
154
188
|
(sorry, no Windows here). That means it uses 'mechanical sympathy' with the Operating
|
155
189
|
System, i.e., the S.O. is *part* of the program, it's not something *under* it.
|
156
190
|
test_files:
|
157
|
-
- spec/deadly_serious/engine/
|
158
|
-
- spec/deadly_serious/engine/
|
159
|
-
- spec/deadly_serious/engine/
|
191
|
+
- spec/lib/deadly_serious/engine/auto_pipe_spec.rb
|
192
|
+
- spec/lib/deadly_serious/engine/channel/socket_channel_spec.rb
|
193
|
+
- spec/lib/deadly_serious/engine/commands_spec.rb
|
194
|
+
- spec/lib/deadly_serious/engine/file_monitor_spec.rb
|
195
|
+
- spec/lib/deadly_serious/engine/json_io_spec.rb
|
196
|
+
- spec/lib/deadly_serious/engine/pipeline_spec.rb
|
160
197
|
- spec/spec_helper.rb
|
@@ -1,82 +0,0 @@
|
|
1
|
-
module DeadlySerious
|
2
|
-
module Engine
|
3
|
-
|
4
|
-
# Restrict IO class that opens ONLY
|
5
|
-
# when trying to read something.
|
6
|
-
#
|
7
|
-
# *This is the object passed to Components.*
|
8
|
-
#
|
9
|
-
# Also, used to reopen lost connections.
|
10
|
-
#
|
11
|
-
# By "restrict", I mean it implements
|
12
|
-
# just a few IO operations.
|
13
|
-
class LazyIo
|
14
|
-
def initialize(channel)
|
15
|
-
@channel = channel
|
16
|
-
end
|
17
|
-
|
18
|
-
# @return [String, nil] the name of the file or pipe,
|
19
|
-
# nil if it's a socket
|
20
|
-
def filename
|
21
|
-
@channel.io_name if @channel.respond_to?(:io_name)
|
22
|
-
end
|
23
|
-
|
24
|
-
def gets
|
25
|
-
open_reader
|
26
|
-
@io.gets
|
27
|
-
end
|
28
|
-
|
29
|
-
def each(&block)
|
30
|
-
open_reader
|
31
|
-
@io.each &block
|
32
|
-
end
|
33
|
-
|
34
|
-
def each_cons(qty, &block)
|
35
|
-
open_reader
|
36
|
-
@io.each_cons(qty, &block)
|
37
|
-
end
|
38
|
-
|
39
|
-
def each_with_object(object, &block)
|
40
|
-
open_reader
|
41
|
-
@io.each_with_object(object, &block)
|
42
|
-
end
|
43
|
-
|
44
|
-
def <<(element)
|
45
|
-
open_writer
|
46
|
-
@io << element
|
47
|
-
end
|
48
|
-
|
49
|
-
def eof?
|
50
|
-
open_reader
|
51
|
-
@io.eof?
|
52
|
-
end
|
53
|
-
|
54
|
-
def closed?
|
55
|
-
@io.nil? || @io.closed?
|
56
|
-
end
|
57
|
-
|
58
|
-
def close
|
59
|
-
@io.close unless closed?
|
60
|
-
@io = nil
|
61
|
-
end
|
62
|
-
|
63
|
-
def flush
|
64
|
-
@io.flush unless closed?
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def open_reader
|
70
|
-
if closed?
|
71
|
-
@io = @channel.open_reader
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def open_writer
|
76
|
-
if closed?
|
77
|
-
@io = @channel.open_writer
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
|
3
|
-
module DeadlySerious
|
4
|
-
module Engine
|
5
|
-
# Wraps the channels with a {LazyIO} before pass
|
6
|
-
# them to the component.
|
7
|
-
module OpenIo
|
8
|
-
def run(*args, readers: [], writers:[])
|
9
|
-
opened_readers = readers.map { |reader| wrap_io(reader) }
|
10
|
-
opened_writers = writers.map { |writer| wrap_io(writer) }
|
11
|
-
super(*args, readers: opened_readers, writers: opened_writers)
|
12
|
-
ensure
|
13
|
-
if opened_writers
|
14
|
-
opened_writers.each { |writer| close_io(writer) }
|
15
|
-
end
|
16
|
-
if opened_readers
|
17
|
-
opened_readers.each { |reader| close_io(reader) }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def close_io(io)
|
24
|
-
return unless io
|
25
|
-
return if io.closed?
|
26
|
-
io.close
|
27
|
-
rescue => e
|
28
|
-
# Intentionally eat the error
|
29
|
-
# because it's being used inside
|
30
|
-
# an "ensure" block
|
31
|
-
puts e.inspect
|
32
|
-
end
|
33
|
-
|
34
|
-
def wrap_io(pipe_name)
|
35
|
-
Channel.new(pipe_name).io
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|