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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4e5a695f5a8d23a761dcaeec8922f6a05c51810
4
- data.tar.gz: 66e33d29d812a6be29dcbb73cc43ec733d3eb15a
3
+ metadata.gz: 05f3250fb16090279dad66ecabbab4990e170873
4
+ data.tar.gz: 5bb83f5cb6f050f7f2e008242434e4f556964e09
5
5
  SHA512:
6
- metadata.gz: e81df18b44cb2f973511ddf230647195672be057e201445ee9a086e29020a2d9752c16fcbc9f590add086f6311c433430d36bc9eb440cee44b77dd5141ef4284
7
- data.tar.gz: 796f4998d1710929ec493c2a4845ac5d52105fac24cc5e5f240266a0df5971c9aaf80a11b6ef1b4c1a4f354f61b486b137699f4ee14e0402e988bc3845fe3672
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module DeadlySerious
2
- VERSION = "0.10.0"
2
+ VERSION = '0.11.0'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'deadly_serious/version'
2
+ require 'deadly_serious/engine/commands'
2
3
  require 'deadly_serious/engine/spawner'
3
4
  require 'deadly_serious/engine/base_process'
4
5
  require 'deadly_serious/engine/push_pop'
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.10.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-06-24 00:00:00.000000000 Z
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: '0'
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.1.11
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