deadly_serious 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/deadly_serious.gemspec +1 -1
- data/lib/deadly_serious/engine/commands.rb +113 -0
- data/lib/deadly_serious/engine/spawner.rb +30 -32
- data/lib/deadly_serious/version.rb +1 -1
- data/lib/deadly_serious.rb +1 -0
- metadata +16 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05f3250fb16090279dad66ecabbab4990e170873
|
4
|
+
data.tar.gz: 5bb83f5cb6f050f7f2e008242434e4f556964e09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03bda6d7a58b0c890dbe1b98ace3b8b338ff788725cbd02289797912bb7cc5507ea5331bf447cce4614b9ff058b046b228ad8cd2ce28e3a76b68adb8fad22ca8
|
7
|
+
data.tar.gz: 170776a0f3d4f8fc593b24513027e0341d950590e67ceb945502f7467fa0be6e16f3174c29193225eca82436d5df3f148824f05214c3e60eec3bbbd9f4adc615
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
deadly_serious
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1
|
data/deadly_serious.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'deadly_serious/version'
|
@@ -18,6 +17,7 @@ Gem::Specification.new do |spec|
|
|
18
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
18
|
spec.require_paths = ['lib']
|
20
19
|
|
20
|
+
spec.required_ruby_version = '~> 2.1'
|
21
21
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
22
|
spec.add_development_dependency 'rake'
|
23
23
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module DeadlySerious
|
2
|
+
module Engine
|
3
|
+
# Commands make work with Pipelines easier.
|
4
|
+
module Commands
|
5
|
+
|
6
|
+
private def auto_pipe
|
7
|
+
@auto_pipe ||= AutoPipe.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_subnet(&block)
|
11
|
+
auto_pipe.on_subnet &block
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_pipe
|
15
|
+
auto_pipe.next
|
16
|
+
end
|
17
|
+
|
18
|
+
def last_pipe
|
19
|
+
auto_pipe.last
|
20
|
+
end
|
21
|
+
|
22
|
+
# Read a file from "data" dir and pipe it to
|
23
|
+
# the next component.
|
24
|
+
def from_file(file_name, writer: next_pipe)
|
25
|
+
file = file_name.sub(/^>?(.*)$/, '>\1')
|
26
|
+
spawn_command(%{cat ((#{file})) > ((#{writer}))})
|
27
|
+
end
|
28
|
+
|
29
|
+
# Write a file to "data" dir from the pipe
|
30
|
+
# of the last component
|
31
|
+
def to_file(file_name, reader: last_pipe)
|
32
|
+
file = file_name.sub(/^>?(.*)$/, '>\1')
|
33
|
+
spawn_command(%{cat ((#{reader})) > ((#{file}))})
|
34
|
+
end
|
35
|
+
|
36
|
+
# Read from a specific named pipe.
|
37
|
+
#
|
38
|
+
# This is useful after a {#spawn_tee}, sometimes.
|
39
|
+
def from_pipe(pipe_name, writer: next_pipe)
|
40
|
+
pipe = pipe_name.sub(/^>?/, '')
|
41
|
+
spawn_command(%{cat ((#{pipe})) > ((#{writer}))})
|
42
|
+
end
|
43
|
+
|
44
|
+
# Write the output of the last component to
|
45
|
+
# a specific named pipe.
|
46
|
+
#
|
47
|
+
# Unless you are connecting different pipelines,
|
48
|
+
# avoid using this or check if you don't need
|
49
|
+
# {#spawn_tee} instead.
|
50
|
+
def to_pipe(pipe_name, reader: last_pipe)
|
51
|
+
pipe = pipe_name.sub(/^>?/, '')
|
52
|
+
spawn_command(%{cat ((#{reader})) > ((#{pipe}))})
|
53
|
+
end
|
54
|
+
|
55
|
+
# Spawn a class connected to the last and next components
|
56
|
+
def spawn_class(a_class, *args, reader: last_pipe, writer: next_pipe)
|
57
|
+
spawn_process(a_class, *args, readers: [reader], writers: [writer])
|
58
|
+
end
|
59
|
+
|
60
|
+
# Spawn {number_of_processes} classes, one process for each of them.
|
61
|
+
# Also, it divides the previous pipe in {number_of_processes} pipes,
|
62
|
+
# an routes data through them.
|
63
|
+
def spawn_class_parallel(number_of_processes, class_name, *args, reader: last_pipe, writer: next_pipe)
|
64
|
+
connect_a = (1..number_of_processes).map { |i| sprintf('%s.%da.splitter', class_name.to_s.downcase.gsub(/\W+/, '_'), i) }
|
65
|
+
connect_b = (1..number_of_processes).map { |i| sprintf('%s.%db.splitter', class_name.to_s.downcase.gsub(/\W+/, '_'), i) }
|
66
|
+
spawn_process(DeadlySerious::Processes::Splitter, readers: [reader], writers: connect_a)
|
67
|
+
connect_a.zip(connect_b).each do |a, b|
|
68
|
+
spawn_class(class_name, *args, reader: a, writer: b)
|
69
|
+
end
|
70
|
+
spawn_process(DeadlySerious::Processes::Joiner, readers: connect_b, writers: [writer])
|
71
|
+
end
|
72
|
+
|
73
|
+
# Pipe from the last component to a intermediate
|
74
|
+
# file (or pipe) while continue the process.
|
75
|
+
#
|
76
|
+
# If a block is provided, it pipes from the last
|
77
|
+
# component INTO the block, while it pipes to the
|
78
|
+
# next component OUT of the block.
|
79
|
+
def spawn_tee(escape = nil, reader: nil, writer: nil, &block)
|
80
|
+
# Lots of contours to make #last_pipe and and #next_pipe
|
81
|
+
# to work correctly.
|
82
|
+
reader ||= last_pipe
|
83
|
+
escape ||= next_pipe
|
84
|
+
writer ||= next_pipe
|
85
|
+
|
86
|
+
# TODO Validates if an "escape" or a block was provided
|
87
|
+
spawn_command(%{cat ((#{reader})) | tee ((#{escape})) > ((#{writer}))})
|
88
|
+
|
89
|
+
return unless block_given?
|
90
|
+
on_subnet do
|
91
|
+
spawn_command(%{cat ((#{escape})) > ((#{next_pipe}))})
|
92
|
+
block.call
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Sometimes we need all previous process to end before
|
97
|
+
# starting new processes. The capacitor command does
|
98
|
+
# exactly that.
|
99
|
+
def spawn_capacitor(charger_file = nil, reader: last_pipe, writer: next_pipe)
|
100
|
+
if charger_file.nil?
|
101
|
+
charger_file = ">#{last_pipe}"
|
102
|
+
temp_charger = true
|
103
|
+
end
|
104
|
+
fail "#{charger_file} must be a file" unless charger_file.start_with?('>')
|
105
|
+
if temp_charger
|
106
|
+
spawn_command("cat ((#{reader})) > ((#{charger_file})); cat ((#{charger_file})) > ((#{writer})); rm ((#{charger_file}))")
|
107
|
+
else
|
108
|
+
spawn_command("cat ((#{reader})) > ((#{charger_file})); cat ((#{charger_file})) > ((#{writer}))")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -6,6 +6,8 @@ require 'deadly_serious/processes/splitter'
|
|
6
6
|
module DeadlySerious
|
7
7
|
module Engine
|
8
8
|
class Spawner
|
9
|
+
include DeadlySerious::Engine::Commands
|
10
|
+
|
9
11
|
attr_reader :data_dir, :pipe_dir
|
10
12
|
|
11
13
|
def initialize(data_dir: './data',
|
@@ -14,22 +16,9 @@ module DeadlySerious
|
|
14
16
|
@data_dir = data_dir
|
15
17
|
@pipe_dir = pipe_dir
|
16
18
|
@ids = []
|
17
|
-
@auto_pipe = AutoPipe.new
|
18
19
|
Channel.config(data_dir, pipe_dir, preserve_pipe_dir)
|
19
20
|
end
|
20
21
|
|
21
|
-
def on_subnet(&block)
|
22
|
-
@auto_pipe.on_subnet &block
|
23
|
-
end
|
24
|
-
|
25
|
-
def next_pipe
|
26
|
-
@auto_pipe.next
|
27
|
-
end
|
28
|
-
|
29
|
-
def last_pipe
|
30
|
-
@auto_pipe.last
|
31
|
-
end
|
32
|
-
|
33
22
|
def run
|
34
23
|
Channel.setup
|
35
24
|
run_pipeline
|
@@ -41,10 +30,21 @@ module DeadlySerious
|
|
41
30
|
Channel.teardown
|
42
31
|
end
|
43
32
|
|
33
|
+
# Wait all sub processes to finish before
|
34
|
+
# continue the pipeline.
|
35
|
+
#
|
36
|
+
# Always prefer to use {DeadlySerious::Engine::Commands#spawn_capacitor}
|
37
|
+
# if possible.
|
44
38
|
def wait_processes!
|
45
39
|
wait_children
|
46
40
|
end
|
47
41
|
|
42
|
+
# Spawn a class in a separated process.
|
43
|
+
#
|
44
|
+
# This is a basic command, use it only if you have
|
45
|
+
# more than one input or output pipe. Otherwise
|
46
|
+
# prefer the simplier {DeadlySerious::Engine::Commands#spawn_class}
|
47
|
+
# method.
|
48
48
|
def spawn_process(a_class, *args, process_name: a_class.name, readers: [], writers: [])
|
49
49
|
writers.each { |writer| create_pipe(writer) }
|
50
50
|
fork_it do
|
@@ -61,6 +61,16 @@ module DeadlySerious
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
+
def spawn_command(a_shell_command)
|
65
|
+
command = a_shell_command.dup
|
66
|
+
a_shell_command.scan(/\(\((.*?)\)\)/) do |(pipe_name)|
|
67
|
+
pipe_path = create_pipe(pipe_name)
|
68
|
+
command.gsub!("((#{pipe_name}))", "'#{pipe_path.gsub("'", "\\'")}'")
|
69
|
+
end
|
70
|
+
@ids << spawn(command)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @deprecated (Use [Spawner#spawn_process] with [Splitter] instead)
|
64
74
|
def spawn_processes(a_class, *args, process_name: a_class.name, reader_pattern: nil, writers: [])
|
65
75
|
number = last_number(reader_pattern)
|
66
76
|
|
@@ -76,10 +86,12 @@ module DeadlySerious
|
|
76
86
|
end
|
77
87
|
end
|
78
88
|
|
89
|
+
# @deprecated (Use [Spawner#spawn_process] instead)
|
79
90
|
def spawn_source(a_class, *args, writer: a_class.dasherize(a_class.name))
|
80
91
|
spawn_process(a_class, *args, process_name: process_name, readers: [], writers: [writer])
|
81
92
|
end
|
82
93
|
|
94
|
+
# @deprecated (Use [Spawner#spawn_process] instead)
|
83
95
|
def spawn_splitter(process_name: 'Splitter', reader: nil, writer: '>output01.txt', number: 2)
|
84
96
|
start = last_number(writer)
|
85
97
|
finish = start + number - 1
|
@@ -92,6 +104,7 @@ module DeadlySerious
|
|
92
104
|
writers: writers)
|
93
105
|
end
|
94
106
|
|
107
|
+
# @deprecated (Use [Spawner#spawn_process] with [Splitter] instead)
|
95
108
|
def spawn_socket_splitter(process_name: 'SocketSplitter', reader: nil, port: 11000, number: 2)
|
96
109
|
spawn_splitter(process_name: process_name,
|
97
110
|
reader: reader,
|
@@ -99,15 +112,6 @@ module DeadlySerious
|
|
99
112
|
number: number)
|
100
113
|
end
|
101
114
|
|
102
|
-
def spawn_command(a_shell_command)
|
103
|
-
command = a_shell_command.dup
|
104
|
-
a_shell_command.scan(/\(\((.*?)\)\)/) do |(pipe_name)|
|
105
|
-
pipe_path = create_pipe(pipe_name)
|
106
|
-
command.gsub!("((#{pipe_name}))", "'#{pipe_path.gsub("'", "\\'")}'")
|
107
|
-
end
|
108
|
-
@ids << spawn(command)
|
109
|
-
end
|
110
|
-
|
111
115
|
private
|
112
116
|
|
113
117
|
def append_open_io_if_needed(a_class)
|
@@ -118,8 +122,6 @@ module DeadlySerious
|
|
118
122
|
Channel.create_pipe(pipe_name)
|
119
123
|
end
|
120
124
|
|
121
|
-
# @!group Process Control
|
122
|
-
|
123
125
|
def fork_it
|
124
126
|
@ids << fork { yield }
|
125
127
|
end
|
@@ -138,13 +140,11 @@ module DeadlySerious
|
|
138
140
|
$0 = "ruby #{self.class.dasherize(name)} <(#{readers.join(' ')}) >(#{writers.join(' ')})"
|
139
141
|
end
|
140
142
|
|
141
|
-
# @!endgroup
|
142
|
-
# @!group Minor Helpers
|
143
|
-
|
144
143
|
def self.dasherize(a_string)
|
145
144
|
a_string.gsub(/(.)([A-Z])/, '\1-\2').downcase.gsub(/\W+/, '-')
|
146
145
|
end
|
147
146
|
|
147
|
+
# @deprecated
|
148
148
|
def last_number_pattern(a_string)
|
149
149
|
last_number_pattern = /(\d+)[^\d]*$/.match(a_string)
|
150
150
|
raise %(Writer name "#{writer}" should have a number) if last_number_pattern.nil?
|
@@ -152,10 +152,12 @@ module DeadlySerious
|
|
152
152
|
last_number_pattern[1]
|
153
153
|
end
|
154
154
|
|
155
|
+
# @deprecated
|
155
156
|
def last_number(a_string)
|
156
157
|
last_number_pattern(a_string).to_i
|
157
158
|
end
|
158
159
|
|
160
|
+
# @deprecated
|
159
161
|
def pattern_replace(a_string, number)
|
160
162
|
pattern = last_number_pattern(a_string)
|
161
163
|
pattern_length = pattern.size
|
@@ -166,8 +168,4 @@ module DeadlySerious
|
|
166
168
|
end
|
167
169
|
end
|
168
170
|
end
|
169
|
-
end
|
170
|
-
|
171
|
-
if __FILE__ == $0
|
172
|
-
DeadlySerious::Engine::Spawner.new.run
|
173
|
-
end
|
171
|
+
end
|
data/lib/deadly_serious.rb
CHANGED
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deadly_serious
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ronie Uliana
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: json
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
description: Flow Based Programming Engine mechanically sympathetic to *nix.
|
@@ -59,7 +59,9 @@ executables: []
|
|
59
59
|
extensions: []
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
-
- .gitignore
|
62
|
+
- ".gitignore"
|
63
|
+
- ".ruby-gemset"
|
64
|
+
- ".ruby-version"
|
63
65
|
- Gemfile
|
64
66
|
- LICENSE.txt
|
65
67
|
- README.md
|
@@ -69,6 +71,7 @@ files:
|
|
69
71
|
- lib/deadly_serious/engine/auto_pipe.rb
|
70
72
|
- lib/deadly_serious/engine/base_process.rb
|
71
73
|
- lib/deadly_serious/engine/channel.rb
|
74
|
+
- lib/deadly_serious/engine/commands.rb
|
72
75
|
- lib/deadly_serious/engine/json_io.rb
|
73
76
|
- lib/deadly_serious/engine/json_process.rb
|
74
77
|
- lib/deadly_serious/engine/lazy_io.rb
|
@@ -93,17 +96,17 @@ require_paths:
|
|
93
96
|
- lib
|
94
97
|
required_ruby_version: !ruby/object:Gem::Requirement
|
95
98
|
requirements:
|
96
|
-
- -
|
99
|
+
- - "~>"
|
97
100
|
- !ruby/object:Gem::Version
|
98
|
-
version: '
|
101
|
+
version: '2.1'
|
99
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
103
|
requirements:
|
101
|
-
- -
|
104
|
+
- - ">="
|
102
105
|
- !ruby/object:Gem::Version
|
103
106
|
version: '0'
|
104
107
|
requirements: []
|
105
108
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
109
|
+
rubygems_version: 2.2.2
|
107
110
|
signing_key:
|
108
111
|
specification_version: 4
|
109
112
|
summary: Flow Based Programming engine that relies on named pipes and Linux processes
|