deadly_serious 0.10.0 → 0.11.0

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 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