deadly_serious 1.0.2 → 2.0.0.pre.rc1
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/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
|