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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78ea6f0fb905d3b08feef450651add4e28b7ed69
|
4
|
+
data.tar.gz: 3bfb8bba7ca19b826ed1cfdefbb81cde2453eb33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83cccbcf342d6e331f8bb050157446fef59a680e5968a8d00a1b21323d976d20f301a5844e899f1031d4cc1d0e049a04b47f7c11eef5214a58f1f552e8f05c6b
|
7
|
+
data.tar.gz: 3afb1d45bfc69a4721fb981949bb63e1b02f7a3765a417b8fe9eb2ad5adb9d60a4d81f51e3194d45a61466ebfd02e8040b5666e21768c4e10aca365b527901f2
|
data/Guardfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
guard :rspec,
|
2
|
-
|
1
|
+
guard :rspec, cmd: 'bundle exec rspec', all_on_start: true, keep: true, all_after_pass: true, run_all: { cmd: 'bundle exec rspec -f progress' } do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
3
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
4
4
|
watch('spec/spec_helper.rb') { "spec" }
|
5
5
|
watch('lib/deadly_serious.rb') { "spec" }
|
data/deadly_serious.gemspec
CHANGED
data/lib/deadly_serious.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
require 'multi_json'
|
2
2
|
require 'fileutils'
|
3
|
-
require '
|
3
|
+
require 'rb-inotify'
|
4
|
+
require 'rbczmq'
|
4
5
|
require 'deadly_serious/version'
|
5
|
-
require 'deadly_serious/engine/
|
6
|
+
require 'deadly_serious/engine/file_monitor'
|
6
7
|
require 'deadly_serious/engine/json_io'
|
7
8
|
require 'deadly_serious/engine/channel'
|
8
9
|
require 'deadly_serious/engine/auto_pipe'
|
9
10
|
require 'deadly_serious/engine/commands'
|
10
|
-
require 'deadly_serious/engine/pipeline'
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
require 'deadly_serious/engine/config'
|
13
|
+
require 'deadly_serious/engine/ruby_object_container'
|
14
|
+
require 'deadly_serious/engine/so_command_container'
|
15
|
+
require 'deadly_serious/engine/pipeline'
|
15
16
|
|
16
17
|
# Loading all predefined processes
|
17
18
|
Dir[File.join(File.dirname(__FILE__), 'deadly_serious', 'processes', '*.rb')].each do |file|
|
@@ -1,39 +1,51 @@
|
|
1
1
|
class DeadlySerious::Engine::AutoPipe
|
2
|
-
TEMPLATE = '
|
2
|
+
TEMPLATE = 'pipe.%s'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
@connection_stack = []
|
7
|
-
@counter = Hash.new { |h, k| h[k.to_sym] = 0}
|
8
|
-
end
|
4
|
+
class Counter
|
5
|
+
TEMPLATE = '%04d'
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
def initialize
|
8
|
+
@counter = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def next
|
12
|
+
@counter += 1
|
13
|
+
last
|
14
|
+
end
|
15
|
+
|
16
|
+
def last
|
17
|
+
format(TEMPLATE, @counter)
|
18
|
+
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
def zero?
|
21
|
+
@counter == 0
|
22
|
+
end
|
20
23
|
end
|
21
24
|
|
22
|
-
def
|
23
|
-
@
|
25
|
+
def initialize
|
26
|
+
@counters = [Counter.new]
|
24
27
|
end
|
25
28
|
|
26
29
|
def next
|
27
|
-
|
30
|
+
current_counter.next
|
28
31
|
last
|
29
32
|
end
|
30
33
|
|
31
34
|
def last
|
32
|
-
|
35
|
+
return nil if current_counter.zero?
|
36
|
+
format(TEMPLATE, @counters.map(&:last).join('.'))
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_subnet
|
40
|
+
@counters << Counter.new
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
@counters.pop
|
33
44
|
end
|
34
45
|
|
35
46
|
private
|
36
|
-
|
37
|
-
|
47
|
+
|
48
|
+
def current_counter
|
49
|
+
@counters.last
|
38
50
|
end
|
39
51
|
end
|
@@ -1,140 +1,25 @@
|
|
1
|
-
require '
|
2
|
-
require 'deadly_serious/engine/
|
1
|
+
require 'deadly_serious/engine/channel/socket_channel'
|
2
|
+
require 'deadly_serious/engine/channel/file_channel'
|
3
|
+
require 'deadly_serious/engine/channel/pipe_channel'
|
3
4
|
|
4
5
|
module DeadlySerious
|
5
6
|
module Engine
|
6
|
-
# Fake class, it's actually a factory ¬¬
|
7
|
-
module Channel
|
8
|
-
def self.new(name)
|
9
|
-
matcher = name.match(/^(>)?(.*?)(?:(:)(\d{1,5}))?$/)
|
10
|
-
if matcher[1] == '>'
|
11
|
-
FileChannel.new(matcher[2], @data_dir)
|
12
|
-
elsif matcher[3] == ':'
|
13
|
-
SocketChannel.new(matcher[2], matcher[4].to_i)
|
14
|
-
else
|
15
|
-
PipeChannel.new(matcher[2], @pipe_dir)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.config(data_dir, pipe_dir, preserve_pipe_dir)
|
20
|
-
@data_dir = data_dir
|
21
|
-
@pipe_dir = pipe_dir
|
22
|
-
@preserve_pipe_dir = preserve_pipe_dir
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.setup
|
26
|
-
FileUtils.mkdir_p(@pipe_dir) unless File.exist?(@pipe_dir)
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.teardown
|
30
|
-
if !@preserve_pipe_dir && File.exist?(@pipe_dir)
|
31
|
-
FileUtils.rm_r(@pipe_dir, force: true, secure: true)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.create_pipe(pipe_name)
|
36
|
-
new(pipe_name).create
|
37
|
-
end
|
38
|
-
end
|
39
7
|
|
40
|
-
|
41
|
-
attr_reader :io_name
|
42
|
-
|
43
|
-
def initialize(name, directory)
|
44
|
-
if name =~ /^\//
|
45
|
-
# Absolute file path
|
46
|
-
@io_name = name
|
47
|
-
else
|
48
|
-
# relative file path (relative to data_dir)
|
49
|
-
@io_name = File.join(directory, name)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def create
|
54
|
-
`touch '#{@io_name}'` unless File.exist?(@io_name)
|
55
|
-
@io_name
|
56
|
-
end
|
57
|
-
|
58
|
-
def open_reader
|
59
|
-
fail %(File "#{@io_name}" not found) unless File.exist?(@io_name)
|
60
|
-
open(@io_name, 'r')
|
61
|
-
end
|
62
|
-
|
63
|
-
def open_writer
|
64
|
-
fail %(File "#{@io_name}" not found) unless File.exist?(@io_name)
|
65
|
-
open(@io_name, 'w')
|
66
|
-
end
|
67
|
-
|
68
|
-
def io
|
69
|
-
LazyIo.new(self)
|
70
|
-
end
|
71
|
-
end
|
8
|
+
CHANNELS = [SocketChannel, FileChannel, PipeChannel]
|
72
9
|
|
73
|
-
class
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
@io_name = File.join(directory, name)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def create
|
87
|
-
`mkfifo '#{@io_name}'` unless File.exist?(@io_name)
|
88
|
-
@io_name
|
89
|
-
end
|
90
|
-
|
91
|
-
def open_reader
|
92
|
-
fail %(Pipe "#{@io_name}" not found) unless File.exist?(@io_name)
|
93
|
-
open(@io_name, 'r')
|
94
|
-
end
|
95
|
-
|
96
|
-
def open_writer
|
97
|
-
fail %(Pipe "#{@io_name}" not found) unless File.exist?(@io_name)
|
98
|
-
open(@io_name, 'w')
|
99
|
-
end
|
100
|
-
|
101
|
-
def io
|
102
|
-
LazyIo.new(self)
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
class SocketChannel
|
107
|
-
def initialize(host, port)
|
108
|
-
@host, @port = host, port
|
109
|
-
@retry_counter = 3
|
110
|
-
end
|
111
|
-
|
112
|
-
def io_name
|
113
|
-
"#{@host}@#{@port}"
|
114
|
-
end
|
115
|
-
|
116
|
-
def create
|
117
|
-
# Do nothing
|
118
|
-
end
|
119
|
-
|
120
|
-
def open_reader
|
121
|
-
TCPSocket.new(@host, @port)
|
122
|
-
rescue Exception => e
|
123
|
-
@retry_counter -= 1
|
124
|
-
if @retry_counter > 0
|
125
|
-
sleep 1 and retry
|
126
|
-
else
|
127
|
-
raise e
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def open_writer
|
132
|
-
server = TCPServer.new(@port)
|
133
|
-
server.accept
|
10
|
+
# Fake class, it's actually a factory ¬¬
|
11
|
+
#
|
12
|
+
# name = '>xxx' # File
|
13
|
+
# name = 'xxx' # Pipe
|
14
|
+
# name = 'xxx:999' # Socket
|
15
|
+
# name = '!xxx:999' # 0MQueue
|
16
|
+
module Channel
|
17
|
+
def self.new(name, config)
|
18
|
+
of_type(name).new(name, config)
|
134
19
|
end
|
135
20
|
|
136
|
-
def
|
137
|
-
|
21
|
+
def self.of_type(name)
|
22
|
+
CHANNELS.map { |channel| channel.of_type(name) }.compact.first
|
138
23
|
end
|
139
24
|
end
|
140
25
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module DeadlySerious
|
2
|
+
module Engine
|
3
|
+
class FileChannel
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
REGEXP = /\A>(.*?)\z/
|
7
|
+
|
8
|
+
attr_reader :io_name
|
9
|
+
|
10
|
+
def self.of_type(name)
|
11
|
+
self if name.match(REGEXP)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name, config)
|
15
|
+
@io_name = self.class.io_name_for(name, config)
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
return enum_for(:each) unless block_given?
|
20
|
+
open(io_name, 'r') { |file| file.each_line { |line| yield line } }
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(data)
|
24
|
+
@writer ||= open(@io_name, 'w')
|
25
|
+
@writer.print(data)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def flush
|
30
|
+
@writer.flush if @writer
|
31
|
+
end
|
32
|
+
|
33
|
+
def close
|
34
|
+
@writer.close if @writer
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.io_name_for(name, config)
|
38
|
+
matcher = name.match(REGEXP)
|
39
|
+
file_name = matcher[1]
|
40
|
+
config.file_path_for(file_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.create(name, config)
|
44
|
+
io_name = io_name_for(name, config)
|
45
|
+
`touch '#{io_name}'` unless File.exist?(io_name)
|
46
|
+
io_name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module DeadlySerious
|
2
|
+
module Engine
|
3
|
+
class PipeChannel
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
REGEXP = /\A(.*?)\z/
|
7
|
+
|
8
|
+
attr_reader :io_name
|
9
|
+
|
10
|
+
def self.of_type(name)
|
11
|
+
self if name.match(REGEXP)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name, config)
|
15
|
+
@io_name = self.class.io_name_for(name, config)
|
16
|
+
end
|
17
|
+
|
18
|
+
def each
|
19
|
+
return enum_for(:each) unless block_given?
|
20
|
+
open(io_name, 'r') { |file| file.each_line { |line| yield line } }
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(data)
|
24
|
+
@writer ||= open(@io_name, File::WRONLY)
|
25
|
+
@writer.print(data)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def flush
|
30
|
+
@writer.flush if @writer
|
31
|
+
end
|
32
|
+
|
33
|
+
def close
|
34
|
+
@writer.close if @writer
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.io_name_for(name, config)
|
38
|
+
matcher = name.match(REGEXP)
|
39
|
+
pipe_name = matcher[1]
|
40
|
+
config.pipe_path_for(pipe_name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.create(name, config)
|
44
|
+
# Redirecting to /dev/null when we test the
|
45
|
+
# file existence. It STILL tries to create
|
46
|
+
# the pipe due concurrency :(
|
47
|
+
# Not good >_<
|
48
|
+
io_name = io_name_for(name, config)
|
49
|
+
`mkfifo '#{io_name}' 2>/dev/null` unless File.exist?(io_name)
|
50
|
+
io_name
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def retries(times = 3, sleep = 0.5)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module DeadlySerious
|
2
|
+
module Engine
|
3
|
+
class MasterMind
|
4
|
+
attr_reader :factory
|
5
|
+
|
6
|
+
def self.new_instance
|
7
|
+
pid = Process.pid
|
8
|
+
if @master.nil? || @master[:pid] != pid
|
9
|
+
@master = {pid: pid, master: self.new}
|
10
|
+
end
|
11
|
+
@master[:master]
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@factory = ZMQ::Context.new
|
16
|
+
@counter = 1
|
17
|
+
@minions = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def spawn_minion
|
21
|
+
minion_brain = yield(@factory, @counter)
|
22
|
+
@counter += 1
|
23
|
+
Minion.new(self, minion_brain).tap { |m| @minions << m }
|
24
|
+
rescue ZMQ::Error => e
|
25
|
+
raise if e.message !~ /has been destroyed/
|
26
|
+
@factory = ZMQ::Context.new
|
27
|
+
retry
|
28
|
+
end
|
29
|
+
|
30
|
+
def destroy_body_of(minion)
|
31
|
+
@minions.delete(minion)
|
32
|
+
suicide if @minions.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def suicide
|
38
|
+
@factory.destroy
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module DeadlySerious
|
2
|
+
module Engine
|
3
|
+
class Minion
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :@brain, :send, :recv
|
7
|
+
|
8
|
+
def initialize(mastermind, brain)
|
9
|
+
@mastermind = mastermind
|
10
|
+
@brain = brain
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_to(destiny, msg)
|
14
|
+
@brain.sendm(destiny)
|
15
|
+
@brain.send(msg)
|
16
|
+
end
|
17
|
+
|
18
|
+
def explode
|
19
|
+
@brain.close
|
20
|
+
@mastermind.destroy_body_of(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|