mob_spawner 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.md +46 -0
  2. data/lib/mob_spawner.rb +162 -0
  3. metadata +55 -0
@@ -0,0 +1,46 @@
1
+ # MobSpawner
2
+
3
+ MobSpawner manages worker threads that can run arbitrary commands and report
4
+ results. Unlike distributed queues, MobSpawner is self-contained and perfect
5
+ for small batch scripts that need to run multiple independent jobs.
6
+
7
+ Documentation is on [rubydoc.org](http://rubydoc.org/gems/mob_spawner/frames).
8
+
9
+ ## Usage
10
+
11
+ The simplest usage of MobSpawner is:
12
+
13
+ ```ruby
14
+ commands = ["rvm install 1.8.6", "rvm install 1.9.2", "rvm install rbx"]
15
+ MobSpawner.new(commands).run
16
+ ```
17
+
18
+ The above will attempt to run the 3 commands concurrently across the default of
19
+ 3 worker threads. By default commands do not report output; to get command
20
+ output, use callbacks discussed in the next section.
21
+
22
+ For more information on how to initialize a spawner, see the {MobSpawner}
23
+ documentation.
24
+
25
+ ## Callbacks
26
+
27
+ In addition to simply running worker threads, you can also receive reports
28
+ about each worker's execution results using callbacks. To setup a spawner
29
+ with callbacks, use {MobSpawner#before_worker} and {MobSpawner#after_worker}:
30
+
31
+ ```ruby
32
+ spawner = MobSpawner.new("command1", "command2", "command3")
33
+ spawner.before_worker do |data|
34
+ puts "Worker #{data[:worker]} about to run #{data[:command].command}"
35
+ end
36
+ spawner.after_worker do |data|
37
+ puts "Worker #{data[:worker]} exited with status #{data[:status]}"
38
+ puts "Output:"
39
+ puts data[:output]
40
+ end
41
+ spawner.run
42
+ ```
43
+
44
+ ## License & Copyright
45
+
46
+ MobSpawner is licensed under the MIT license, © 2012 Loren Segal
@@ -0,0 +1,162 @@
1
+ require 'open3'
2
+
3
+ # MobSpawner manages worker threads that can run arbitrary commands and report
4
+ # results. Unlike distributed queues, MobSpawner is self-contained and perfect
5
+ # for small batch scripts that need to run multiple independent jobs.
6
+ class MobSpawner
7
+ VERSION = '1.0.0'
8
+
9
+ # Represents a command to be called by the spawner. Can also hold environment
10
+ # variables and arbitrary client data to identify the object.
11
+ class Command
12
+ # @return [String] the command to be executed by the spawner
13
+ attr_accessor :command
14
+
15
+ # @return [Hash{String=>String}] any environment variables to be set
16
+ # when running the command.
17
+ attr_accessor :env
18
+
19
+ # @return [Object] arbitrary client data used to identify the command
20
+ # object.
21
+ attr_accessor :data
22
+
23
+ # Creates a new command.
24
+ #
25
+ # @overload initialize(opts = {})
26
+ # @param [Hash{Symbol=>Object}] opts option data to be passed during
27
+ # initialization. Keys can be any attribute defined on this class,
28
+ # such as {#command}, {#env} or {#data}.
29
+ # @overload initialize(cmd, env = {}, data = nil)
30
+ # @param [String] cmd the command to execute
31
+ # @param [Hash{String=>String}] env environment variables to be set
32
+ # when running the command
33
+ # @param [Object] data any client data to be set on the command object
34
+ def initialize(cmd, env = {}, data = nil)
35
+ self.env = {}
36
+ if cmd.is_a?(Hash)
37
+ cmd.each do |k, v|
38
+ meth = "#{k}="
39
+ send(meth, v) if respond_to?(meth)
40
+ end
41
+ else
42
+ self.command = cmd
43
+ self.env = env
44
+ self.data = data
45
+ end
46
+ end
47
+ end
48
+
49
+ # @return [Fixnum] the number of workers to run, defaults to 3.
50
+ attr_accessor :num_workers
51
+
52
+ # @return [Array<Command,String>] a list of commands to be executed. Note that
53
+ # if a command is a String, it will eventually be converted into a {Command}
54
+ # object.
55
+ attr_accessor :commands
56
+
57
+ # @return [Array<Proc>] a list of callbacks to be called before each worker.
58
+ # Use {#before_worker} instead of setting the callbacks list directly.
59
+ # @see #before_worker
60
+ attr_accessor :before_callbacks
61
+
62
+ # @return [Array<Proc>] a list of callbacks to be called after each worker.
63
+ # Use {#after_worker} instead of setting the callbacks list directly.
64
+ # @see #after_worker
65
+ attr_accessor :after_callbacks
66
+
67
+ # Creates a new spawner, use {#run} to run it.
68
+ #
69
+ # @overload initialize(opts = {})
70
+ # @param [Hash{Symbol=>Object}] opts option data to be passed during
71
+ # initialization. Keys can be any attribute defined on this class,
72
+ # such as {#num_workers}, {#commands}, etc.
73
+ # @overload initialize(*commands)
74
+ # @param [Array<String>] commands a list of commands to be run using
75
+ # default settings.
76
+ def initialize(*commands)
77
+ super()
78
+ self.num_workers = 3
79
+ self.commands = []
80
+ self.before_callbacks = []
81
+ self.after_callbacks = []
82
+ if commands.size == 1 && commands.first.is_a?(Hash)
83
+ setup_options(commands.first)
84
+ else
85
+ self.commands = commands.flatten
86
+ end
87
+ end
88
+
89
+ # Runs the spawner, initializing all workers and running the commands.
90
+ def run
91
+ self.commands = commands.map {|c| c.is_a?(Command) ? c : Command.new(c) }
92
+ workers = []
93
+ num_workers.times { workers << [] }
94
+ divide_to_workers(workers, commands)
95
+ threads = []
96
+ workers.each_with_index do |worker, i|
97
+ next if worker.size == 0
98
+ threads << Thread.new do
99
+ worker.each do |cmd|
100
+ data = {:worker => i+1, :command => cmd}
101
+ before_callbacks.each {|cb| cb.call(data) }
102
+ begin
103
+ output, status = Open3.capture2e(cmd.env, cmd.command)
104
+ data.update(:output => output, :status => status)
105
+ rescue => exc
106
+ data.update(:exception => exc, :status => 256)
107
+ end
108
+ after_callbacks.each {|cb| cb.call(data) }
109
+ end
110
+ end
111
+ end
112
+ while threads.size > 0
113
+ threads.dup.each do |thr|
114
+ thr.join(0.1)
115
+ threads.delete(thr) unless thr.alive?
116
+ end
117
+ end
118
+ end
119
+
120
+ # Creates a callback that is executed before each worker is run.
121
+ #
122
+ # @yield [data] worker information
123
+ # @yieldparam [Hash{Symbol=>Object}] data information about the worker
124
+ # thread. Valid keys are:
125
+ #
126
+ # * +:worker+ - the worker number (starting from 1)
127
+ # * +:command+ - the {Command} object about to be run
128
+ def before_worker(&block)
129
+ before_callbacks << block
130
+ end
131
+
132
+ # Creates a callback that is executed after each worker is run.
133
+ #
134
+ # @yield [data] worker information
135
+ # @yieldparam [Hash{Symbol=>Object}] data information about the worker
136
+ # thread. Valid keys are:
137
+ #
138
+ # * +:worker+ - the worker number (starting from 1)
139
+ # * +:command+ - the {Command} object about to be run
140
+ # * +:output+ - all stdout and stderr output from the command
141
+ # * +:status+ - the status code from the exited command
142
+ # * +:exception+ - if a Ruby exception occurred during execution
143
+ def after_worker(&block)
144
+ after_callbacks << block
145
+ end
146
+
147
+ private
148
+
149
+ def setup_options(opts)
150
+ opts.each do |k, v|
151
+ meth = "#{k}="
152
+ send(meth, v) if respond_to?(meth)
153
+ end
154
+ end
155
+
156
+ def divide_to_workers(workers, commands)
157
+ num_workers = workers.size
158
+ commands.each_with_index do |command, i|
159
+ workers[i % num_workers] << command
160
+ end
161
+ end
162
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mob_spawner
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Loren Segal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-09 00:00:00.000000000 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+ description: ! 'MobSpawner manages worker threads that can run arbitrary commands
16
+ and report
17
+
18
+ results. Unlike distributed queues, MobSpawner is self-contained and perfect
19
+
20
+ for small batch scripts that need to run multiple independent jobs.
21
+
22
+ '
23
+ email: lsegal@soen.ca
24
+ executables: []
25
+ extensions: []
26
+ extra_rdoc_files: []
27
+ files:
28
+ - lib/mob_spawner.rb
29
+ - README.md
30
+ has_rdoc: true
31
+ homepage: http://github.com/lsegal/mob_spawner
32
+ licenses: []
33
+ post_install_message:
34
+ rdoc_options: []
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 1.3.9.5
52
+ signing_key:
53
+ specification_version: 3
54
+ summary: Manages and spawns worker threads to run arbitrary shell commands.
55
+ test_files: []