pigeon 0.9.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +8 -0
- data/Gemfile +9 -0
- data/README.md +1 -1
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/lib/pigeon.rb +8 -0
- data/lib/pigeon/dispatcher.rb +1 -1
- data/lib/pigeon/engine.rb +27 -16
- data/lib/pigeon/launcher.rb +35 -6
- data/lib/pigeon/support.rb +31 -9
- data/lib/pigeon/task.rb +9 -0
- data/pigeon.gemspec +18 -6
- data/test/helper.rb +82 -22
- data/test/unit/pigeon_backlog_test.rb +1 -1
- data/test/unit/pigeon_dispatcher_test.rb +1 -1
- data/test/unit/pigeon_engine_test.rb +30 -14
- data/test/unit/pigeon_launcher_test.rb +27 -28
- data/test/unit/pigeon_option_accessor_test.rb +3 -3
- data/test/unit/pigeon_processor_test.rb +19 -13
- data/test/unit/pigeon_queue_test.rb +1 -1
- data/test/unit/pigeon_scheduler_test.rb +1 -1
- data/test/unit/pigeon_sorted_array_test.rb +3 -3
- data/test/unit/pigeon_task_test.rb +42 -27
- data/test/unit/pigeon_test.rb +1 -1
- metadata +54 -14
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fa0f5ce613ef0b66cded863d01259e09099eeb27
|
4
|
+
data.tar.gz: 6b1d997dda5b4664c6cf77524a840aee230ac54d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 18a1edfd20eaec356b8297665db24eb96d18e2e90fb8c7699a71e081c599f3d809476e64d7e05dae8fedd3a7973a924c6500013c90bacb0483dcfdddd7ee2de4
|
7
|
+
data.tar.gz: 3c9bc25b916a9b5ca262ed489c117032185b5fc3e8ffc9feab37f94dd8b7d7f4df5a394063d48dfe0945834ef7a9fedd0d604d50f75162f8fa6e25833dc3771d
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -3,6 +3,7 @@ require 'rake'
|
|
3
3
|
|
4
4
|
begin
|
5
5
|
require 'jeweler'
|
6
|
+
|
6
7
|
Jeweler::Tasks.new do |gem|
|
7
8
|
gem.name = "pigeon"
|
8
9
|
gem.summary = %Q{Simple daemonized EventMachine engine framework with plug-in support}
|
@@ -10,21 +11,20 @@ begin
|
|
10
11
|
gem.email = "github@tadman.ca"
|
11
12
|
gem.homepage = "http://github.com/twg/pigeon"
|
12
13
|
gem.authors = %w[ tadman ]
|
13
|
-
gem.add_development_dependency 'eventmachine'
|
14
14
|
gem.executables = [ ]
|
15
15
|
end
|
16
|
+
|
16
17
|
Jeweler::GemcutterTasks.new
|
17
18
|
rescue LoadError
|
18
19
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
20
|
end
|
20
21
|
|
21
22
|
require 'rake/testtask'
|
23
|
+
|
22
24
|
Rake::TestTask.new(:test) do |test|
|
23
25
|
test.libs << 'lib' << 'test'
|
24
26
|
test.pattern = 'test/**/*_test.rb'
|
25
27
|
test.verbose = true
|
26
28
|
end
|
27
29
|
|
28
|
-
task :
|
29
|
-
|
30
|
-
task :default => :test
|
30
|
+
task default: :test
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/lib/pigeon.rb
CHANGED
@@ -13,6 +13,14 @@ module Pigeon
|
|
13
13
|
autoload(:SortedArray, 'pigeon/sorted_array')
|
14
14
|
autoload(:Support, 'pigeon/support')
|
15
15
|
autoload(:Task, 'pigeon/task')
|
16
|
+
|
17
|
+
# == Module Methods =======================================================
|
18
|
+
|
19
|
+
def self.version
|
20
|
+
@version ||= File.readlines(
|
21
|
+
File.expand_path('../VERSION'), File.dirname(__FILE__)
|
22
|
+
)[0].chomp
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
26
|
# NOTE: This file needs to be directly loaded due to some kind of peculiar
|
data/lib/pigeon/dispatcher.rb
CHANGED
data/lib/pigeon/engine.rb
CHANGED
@@ -21,29 +21,29 @@ class Pigeon::Engine
|
|
21
21
|
option_accessor :name
|
22
22
|
option_accessor :pid_file_name
|
23
23
|
option_accessor :foreground,
|
24
|
-
:
|
24
|
+
boolean: true
|
25
25
|
option_accessor :debug,
|
26
|
-
:
|
26
|
+
boolean: true
|
27
27
|
option_accessor :log_rotation
|
28
28
|
option_accessor :engine_log_name,
|
29
|
-
:
|
29
|
+
default: 'engine.log'
|
30
30
|
option_accessor :engine_logger
|
31
31
|
option_accessor :query_log_name,
|
32
|
-
:
|
32
|
+
default: 'query.log'
|
33
33
|
option_accessor :query_logger
|
34
34
|
option_accessor :try_pid_dirs,
|
35
|
-
:
|
35
|
+
default: %w[
|
36
36
|
/var/run
|
37
37
|
/tmp
|
38
38
|
].freeze
|
39
39
|
option_accessor :try_log_dirs,
|
40
|
-
:
|
40
|
+
default: %w[
|
41
41
|
/var/log
|
42
42
|
/tmp
|
43
43
|
].freeze
|
44
44
|
option_accessor :threaded,
|
45
|
-
:
|
46
|
-
:
|
45
|
+
boolean: true,
|
46
|
+
default: false
|
47
47
|
|
48
48
|
attr_reader :id
|
49
49
|
attr_reader :state
|
@@ -125,14 +125,14 @@ class Pigeon::Engine
|
|
125
125
|
EventMachine.run do
|
126
126
|
engine = new(options)
|
127
127
|
|
128
|
-
yield(engine) if (block_given?)
|
129
|
-
|
130
128
|
Signal.trap('INT') do
|
131
129
|
engine.terminate
|
132
130
|
end
|
133
131
|
|
134
132
|
Pigeon::Engine.register_engine(engine)
|
135
133
|
|
134
|
+
yield(engine) if (block_given?)
|
135
|
+
|
136
136
|
engine.run
|
137
137
|
end
|
138
138
|
|
@@ -148,7 +148,7 @@ class Pigeon::Engine
|
|
148
148
|
|
149
149
|
pid = Pigeon::Support.daemonize(logger) do
|
150
150
|
launch({
|
151
|
-
:
|
151
|
+
logger: logger
|
152
152
|
}.merge(options || { }))
|
153
153
|
end
|
154
154
|
|
@@ -162,7 +162,7 @@ class Pigeon::Engine
|
|
162
162
|
def self.run
|
163
163
|
yield($$) if (block_given?)
|
164
164
|
|
165
|
-
launch(:
|
165
|
+
launch(foreground: true)
|
166
166
|
end
|
167
167
|
|
168
168
|
def self.stop
|
@@ -171,6 +171,7 @@ class Pigeon::Engine
|
|
171
171
|
if (pid)
|
172
172
|
begin
|
173
173
|
Process.kill('INT', pid)
|
174
|
+
|
174
175
|
rescue Errno::ESRCH
|
175
176
|
# No such process exception
|
176
177
|
pid = nil
|
@@ -178,7 +179,7 @@ class Pigeon::Engine
|
|
178
179
|
|
179
180
|
begin
|
180
181
|
while (Process.kill(0, pid))
|
181
|
-
|
182
|
+
Pigeon::Support.nap(0.1)
|
182
183
|
end
|
183
184
|
rescue Errno::ESRCH
|
184
185
|
# No such process, already terminated
|
@@ -195,8 +196,11 @@ class Pigeon::Engine
|
|
195
196
|
end
|
196
197
|
|
197
198
|
def self.restart
|
198
|
-
self.stop
|
199
|
-
|
199
|
+
self.stop do |old_pid|
|
200
|
+
self.start do |pid|
|
201
|
+
yield(pid, old_pid) if (block_given?)
|
202
|
+
end
|
203
|
+
end
|
200
204
|
end
|
201
205
|
|
202
206
|
def self.running?
|
@@ -248,6 +252,10 @@ class Pigeon::Engine
|
|
248
252
|
def self.unregister_engine(engine)
|
249
253
|
@engines.delete(engine)
|
250
254
|
end
|
255
|
+
|
256
|
+
def self.clear_engines!
|
257
|
+
@engines = [ ]
|
258
|
+
end
|
251
259
|
|
252
260
|
# Schedules a block for execution on the main EventMachine thread. This is
|
253
261
|
# a wrapper around the EventMachine.schedule method.
|
@@ -359,7 +367,10 @@ class Pigeon::Engine
|
|
359
367
|
# events.
|
360
368
|
def terminate
|
361
369
|
wrap_chain(:stop) do
|
362
|
-
EventMachine.
|
370
|
+
if (EventMachine.reactor_running?)
|
371
|
+
EventMachine.stop_event_loop
|
372
|
+
end
|
373
|
+
|
363
374
|
@state = :terminated
|
364
375
|
end
|
365
376
|
end
|
data/lib/pigeon/launcher.rb
CHANGED
@@ -18,24 +18,53 @@ class Pigeon::Launcher
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def handle_args(*args)
|
21
|
-
op = OptionParser.new
|
21
|
+
op = OptionParser.new do |parser|
|
22
|
+
parser.on('-v', '--version') do
|
23
|
+
pigeon_version = 'Pigeon %s' % Pigeon.version
|
24
|
+
|
25
|
+
version =
|
26
|
+
if (@engine.respond_to?(:version))
|
27
|
+
'%s (%s)' % [ @engine.version, pigeon_version ]
|
28
|
+
else
|
29
|
+
pigeon_version
|
30
|
+
end
|
31
|
+
|
32
|
+
puts version
|
33
|
+
exit(0)
|
34
|
+
end
|
35
|
+
end
|
22
36
|
|
23
37
|
command = op.parse(*args.flatten).first
|
24
38
|
|
25
39
|
begin
|
26
40
|
case (command)
|
27
41
|
when 'start'
|
28
|
-
@engine.start
|
42
|
+
@engine.start do |pid|
|
43
|
+
yield(pid) if (block_given?)
|
44
|
+
self.start(pid)
|
45
|
+
end
|
29
46
|
when 'stop'
|
30
|
-
@engine.stop
|
47
|
+
@engine.stop do |pid|
|
48
|
+
yield(pid) if (block_given?)
|
49
|
+
self.stop(pid)
|
50
|
+
end
|
31
51
|
when 'restart'
|
32
|
-
@engine.restart
|
52
|
+
@engine.restart do |pid, old_pid|
|
53
|
+
yield(pid, old_pid) if (block_given?)
|
54
|
+
self.restart(pid, old_pid)
|
55
|
+
end
|
33
56
|
when 'status'
|
34
|
-
@engine.status
|
57
|
+
@engine.status do |pid|
|
58
|
+
yield(pid) if (block_given?)
|
59
|
+
self.status(pid)
|
60
|
+
end
|
35
61
|
when 'run'
|
36
62
|
@engine.engine_logger = Pigeon::Logger.new(STDOUT)
|
37
63
|
|
38
|
-
@engine.run
|
64
|
+
@engine.run do |pid|
|
65
|
+
yield(pid) if (block_given?)
|
66
|
+
self.run(pid)
|
67
|
+
end
|
39
68
|
else
|
40
69
|
usage
|
41
70
|
end
|
data/lib/pigeon/support.rb
CHANGED
@@ -30,7 +30,6 @@ module Pigeon::Support
|
|
30
30
|
if (thread.backtrace)
|
31
31
|
logger.error("\t" + thread.backtrace.join("\n\t"))
|
32
32
|
end
|
33
|
-
|
34
33
|
end
|
35
34
|
end
|
36
35
|
|
@@ -39,22 +38,41 @@ module Pigeon::Support
|
|
39
38
|
end
|
40
39
|
|
41
40
|
begin
|
42
|
-
|
41
|
+
interrupted = false
|
42
|
+
|
43
|
+
Signal.trap('INT') do
|
44
|
+
interrupted = true
|
45
|
+
Process.kill('INT', daemon_pid)
|
46
|
+
|
47
|
+
relaunch = false
|
48
|
+
end
|
49
|
+
|
50
|
+
pid, status = Process.wait2(daemon_pid)
|
51
|
+
|
52
|
+
if (interrupted)
|
53
|
+
logger.info("Supervisor #{Process.pid} received termination signal, shut down child #{daemon_pid}")
|
54
|
+
end
|
43
55
|
|
44
56
|
# A non-zero exit status indicates some sort of error, so the
|
45
57
|
# process will be relaunched after a short delay.
|
46
58
|
relaunch = ($? != 0)
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
ensure
|
61
|
+
# Reset Signal handler before forking again
|
62
|
+
Signal.trap('INT') do
|
63
|
+
end
|
52
64
|
end
|
53
65
|
|
54
66
|
if (relaunch)
|
55
|
-
|
67
|
+
begin
|
68
|
+
logger.info("Supervisor #{Process.pid} will relaunch in %d seconds" % delay)
|
69
|
+
sleep(delay)
|
70
|
+
|
71
|
+
rescue Interrupt
|
72
|
+
logger.info("Supervisor #{Process.pid} abandoing restart because of termination")
|
56
73
|
|
57
|
-
|
74
|
+
relaunch = false
|
75
|
+
end
|
58
76
|
else
|
59
77
|
logger.info("Terminated normally")
|
60
78
|
end
|
@@ -66,13 +84,17 @@ module Pigeon::Support
|
|
66
84
|
wfd.close
|
67
85
|
end
|
68
86
|
|
69
|
-
Process.
|
87
|
+
pid, status = Process.wait2(forked_pid)
|
70
88
|
|
71
89
|
daemon_pid = rfd.readline
|
72
90
|
rfd.close
|
73
91
|
|
74
92
|
daemon_pid.to_i
|
75
93
|
end
|
94
|
+
|
95
|
+
def nap(time)
|
96
|
+
select(nil, nil, nil, time.to_f)
|
97
|
+
end
|
76
98
|
|
77
99
|
# Finds the first directory in the given list that exists and is
|
78
100
|
# writable. Returns nil if none match.
|
data/lib/pigeon/task.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
class Pigeon::Task
|
2
|
+
# == Exceptions ===========================================================
|
3
|
+
|
4
|
+
class EngineRequired < StandardError
|
5
|
+
end
|
6
|
+
|
2
7
|
# == Constants ============================================================
|
3
8
|
|
4
9
|
# == Properties ===========================================================
|
@@ -35,6 +40,10 @@ class Pigeon::Task
|
|
35
40
|
@context = context
|
36
41
|
@engine = engine || Pigeon::Engine.default_engine
|
37
42
|
@created_at = Time.now
|
43
|
+
|
44
|
+
unless (@engine)
|
45
|
+
raise EngineRequired, "Task creation requires an active Pigeon::Engine"
|
46
|
+
end
|
38
47
|
|
39
48
|
after_initialized
|
40
49
|
end
|
data/pigeon.gemspec
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: pigeon 1.0.0 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "pigeon"
|
8
|
-
s.version = "0.
|
9
|
+
s.version = "1.0.0"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["tadman"]
|
12
|
-
s.date = "
|
14
|
+
s.date = "2015-02-18"
|
13
15
|
s.description = "Pigeon is a simple way to get started building an EventMachine engine that's intended to run as a background job."
|
14
16
|
s.email = "github@tadman.ca"
|
15
17
|
s.extra_rdoc_files = [
|
@@ -18,6 +20,8 @@ Gem::Specification.new do |s|
|
|
18
20
|
]
|
19
21
|
s.files = [
|
20
22
|
".document",
|
23
|
+
".travis.yml",
|
24
|
+
"Gemfile",
|
21
25
|
"LICENSE",
|
22
26
|
"README.md",
|
23
27
|
"Rakefile",
|
@@ -51,20 +55,28 @@ Gem::Specification.new do |s|
|
|
51
55
|
"test/unit/pigeon_test.rb"
|
52
56
|
]
|
53
57
|
s.homepage = "http://github.com/twg/pigeon"
|
54
|
-
s.
|
55
|
-
s.rubygems_version = "1.8.25"
|
58
|
+
s.rubygems_version = "2.2.2"
|
56
59
|
s.summary = "Simple daemonized EventMachine engine framework with plug-in support"
|
57
60
|
|
58
61
|
if s.respond_to? :specification_version then
|
59
|
-
s.specification_version =
|
62
|
+
s.specification_version = 4
|
60
63
|
|
61
64
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
62
|
-
s.
|
65
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
66
|
+
s.add_development_dependency(%q<minitest-reporters>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<minitest>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
63
69
|
else
|
64
70
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
71
|
+
s.add_dependency(%q<minitest-reporters>, [">= 0"])
|
72
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
65
74
|
end
|
66
75
|
else
|
67
76
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
77
|
+
s.add_dependency(%q<minitest-reporters>, [">= 0"])
|
78
|
+
s.add_dependency(%q<minitest>, [">= 0"])
|
79
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
data/test/helper.rb
CHANGED
@@ -1,35 +1,63 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
3
|
|
4
|
-
|
5
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
require 'minitest'
|
6
5
|
|
7
|
-
|
6
|
+
module Minitest
|
7
|
+
# The default Minitest behavior to intercept at_exit and rewrite the exit
|
8
|
+
# status code based on test results is okay for the parent process, but
|
9
|
+
# causes friction when using fork within tests. Here it's disabled unless
|
10
|
+
# the process terminating is the parent.
|
11
|
+
def self.autorun
|
12
|
+
return if (@at_exit_hook_installed)
|
8
13
|
|
9
|
-
|
14
|
+
@at_exit_hook_installed = Process.pid
|
15
|
+
|
16
|
+
at_exit do
|
17
|
+
next if $! and not ($!.kind_of? SystemExit and $!.success?)
|
18
|
+
|
19
|
+
exit_code = Minitest.run(ARGV)
|
20
|
+
|
21
|
+
@@after_run.reverse_each do |block|
|
22
|
+
block.call
|
23
|
+
|
24
|
+
if (Process.pid != @at_exit_hook_installed)
|
25
|
+
break
|
26
|
+
end
|
27
|
+
end
|
10
28
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
raise "EventMachine gem is not installed or could not be loaded: [#{e.class}] #{e}"
|
29
|
+
exit(exit_code)
|
30
|
+
end
|
31
|
+
end
|
15
32
|
end
|
16
33
|
|
34
|
+
require 'minitest/reporters'
|
35
|
+
require 'minitest/autorun'
|
36
|
+
require 'timeout'
|
37
|
+
|
38
|
+
Minitest::Reporters.use!(Minitest::Reporters::SpecReporter.new)
|
39
|
+
|
40
|
+
$LOAD_PATH.unshift(File.expand_path(File.join(*%w[ .. lib ]), File.dirname(__FILE__)))
|
41
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
42
|
+
|
17
43
|
require 'pigeon'
|
44
|
+
require 'eventmachine'
|
18
45
|
|
19
|
-
class Test
|
46
|
+
class Minitest::Test
|
20
47
|
def assert_timeout(time, message = nil, &block)
|
21
48
|
Timeout::timeout(time, &block)
|
49
|
+
|
22
50
|
rescue Timeout::Error
|
23
51
|
flunk(message || 'assert_timeout timed out')
|
24
52
|
end
|
25
53
|
|
26
54
|
def assert_eventually(time = nil, message = nil, &block)
|
27
|
-
start_time = Time.now.
|
55
|
+
start_time = Time.now.to_f
|
28
56
|
|
29
57
|
while (!block.call)
|
30
58
|
select(nil, nil, nil, 0.1)
|
31
59
|
|
32
|
-
if (time and (Time.now.
|
60
|
+
if (time and (Time.now.to_f - start_time > time))
|
33
61
|
flunk(message || 'assert_eventually timed out')
|
34
62
|
end
|
35
63
|
end
|
@@ -37,32 +65,64 @@ class Test::Unit::TestCase
|
|
37
65
|
|
38
66
|
def engine
|
39
67
|
exception = nil
|
68
|
+
test_thread = nil
|
40
69
|
|
41
|
-
|
42
|
-
|
70
|
+
engine_thread =
|
71
|
+
Thread.new do
|
72
|
+
Thread.abort_on_exception = true
|
73
|
+
|
74
|
+
Pigeon::Engine.clear_engines!
|
75
|
+
|
76
|
+
# Create a thread for the engine to run on
|
77
|
+
begin
|
78
|
+
Pigeon::Engine.launch do |launched|
|
79
|
+
@engine = launched
|
80
|
+
end
|
81
|
+
|
82
|
+
rescue Object => exception
|
83
|
+
end
|
84
|
+
end
|
43
85
|
|
44
|
-
|
45
|
-
# the EventMachine loop.
|
86
|
+
test_thread =
|
46
87
|
Thread.new do
|
88
|
+
# Execute the test code in a separate thread to avoid blocking
|
89
|
+
# the EventMachine loop.
|
47
90
|
begin
|
48
|
-
|
49
|
-
|
91
|
+
while (!@engine or Pigeon::Engine.default_engine != @engine)
|
92
|
+
# Wait impatiently.
|
93
|
+
end
|
94
|
+
|
95
|
+
yield(@engine)
|
50
96
|
rescue Object => exception
|
51
97
|
ensure
|
52
98
|
begin
|
53
|
-
|
54
|
-
|
55
|
-
|
99
|
+
if (EventMachine.reactor_running?)
|
100
|
+
EventMachine.stop_event_loop
|
101
|
+
end
|
102
|
+
rescue Object
|
103
|
+
STDERR.puts("[#{exception.class}] #{exception}")
|
56
104
|
# Shutting down may trigger an exception from time to time
|
57
105
|
# if the engine itself has failed.
|
58
|
-
STDERR.puts("Exception: [#{e.class}] #{e}")
|
59
106
|
end
|
60
107
|
end
|
61
108
|
end
|
109
|
+
|
110
|
+
test_thread.join
|
111
|
+
|
112
|
+
begin
|
113
|
+
Timeout.timeout(1) do
|
114
|
+
engine_thread.join
|
115
|
+
end
|
116
|
+
rescue Timeout::Error
|
117
|
+
engine_thread.kill
|
62
118
|
end
|
63
119
|
|
64
120
|
if (exception)
|
65
121
|
raise exception
|
66
122
|
end
|
123
|
+
ensure
|
124
|
+
if (engine_thread)
|
125
|
+
engine_thread.kill
|
126
|
+
end
|
67
127
|
end
|
68
128
|
end
|
@@ -71,10 +71,12 @@ class ShutdownCallbackTestEngine < Pigeon::Engine
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
-
class TestPigeonEngine < Test
|
74
|
+
class TestPigeonEngine < Minitest::Test
|
75
75
|
def test_default_options
|
76
76
|
assert TestEngine.engine_logger
|
77
77
|
assert TestEngine.engine_logger.is_a?(Logger)
|
78
|
+
|
79
|
+
assert_equal nil, TestEngine.default_engine
|
78
80
|
end
|
79
81
|
|
80
82
|
def test_example_subclass
|
@@ -82,13 +84,13 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
82
84
|
|
83
85
|
read_fd, write_fd = IO.pipe
|
84
86
|
|
85
|
-
TestEngine.start(:
|
87
|
+
TestEngine.start(pipe: write_fd) do |pid|
|
86
88
|
assert pid
|
87
89
|
engine_pid = pid
|
88
90
|
end
|
89
91
|
|
90
92
|
write_fd.close
|
91
|
-
|
93
|
+
|
92
94
|
Timeout::timeout(5) do
|
93
95
|
assert_equal "STARTED\n", read_fd.readline
|
94
96
|
end
|
@@ -96,7 +98,7 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
96
98
|
TestEngine.status do |pid|
|
97
99
|
assert_equal engine_pid, pid
|
98
100
|
end
|
99
|
-
|
101
|
+
|
100
102
|
TestEngine.stop do |pid|
|
101
103
|
assert_equal engine_pid, pid
|
102
104
|
end
|
@@ -111,7 +113,7 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
111
113
|
|
112
114
|
read_fd, write_fd = IO.pipe
|
113
115
|
|
114
|
-
engine_pid = TestEngine.start(:
|
116
|
+
engine_pid = TestEngine.start(pipe: write_fd)
|
115
117
|
|
116
118
|
write_fd.close
|
117
119
|
|
@@ -135,7 +137,7 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
135
137
|
|
136
138
|
read_fd, write_fd = IO.pipe
|
137
139
|
|
138
|
-
CallbackTestEngine.start(:
|
140
|
+
CallbackTestEngine.start(pipe: write_fd) do |pid|
|
139
141
|
assert pid
|
140
142
|
engine_pid = pid
|
141
143
|
end
|
@@ -169,6 +171,23 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
169
171
|
|
170
172
|
assert_equal expected_callbacks, read_fd.read.split(/\n/).collect(&:to_sym)
|
171
173
|
end
|
174
|
+
|
175
|
+
def test_fork_exitstatus
|
176
|
+
child_pid = fork do
|
177
|
+
end
|
178
|
+
|
179
|
+
pid, status = Process.wait2(child_pid)
|
180
|
+
|
181
|
+
assert_equal 0, status.exitstatus
|
182
|
+
|
183
|
+
child_pid = fork do
|
184
|
+
exit(90)
|
185
|
+
end
|
186
|
+
|
187
|
+
pid, status = Process.wait2(child_pid)
|
188
|
+
|
189
|
+
assert_equal 90, status.exitstatus
|
190
|
+
end
|
172
191
|
|
173
192
|
def test_shutdown_engine
|
174
193
|
engine_pid = nil
|
@@ -179,15 +198,12 @@ class TestPigeonEngine < Test::Unit::TestCase
|
|
179
198
|
|
180
199
|
assert engine_pid
|
181
200
|
|
182
|
-
|
183
|
-
|
184
|
-
running_pid = nil
|
185
|
-
|
186
|
-
ShutdownEngine.status do |pid|
|
187
|
-
running_pid = pid
|
201
|
+
assert_eventually(5) do
|
202
|
+
!ShutdownEngine.status
|
188
203
|
end
|
189
|
-
|
190
|
-
|
204
|
+
|
205
|
+
ensure
|
206
|
+
ShutdownEngine.stop
|
191
207
|
end
|
192
208
|
|
193
209
|
def test_shutdown_engine_with_blocking_callback
|
@@ -6,7 +6,7 @@ class Pigeon::Launcher
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
-
class PigeonLauncherTest < Test
|
9
|
+
class PigeonLauncherTest < Minitest::Test
|
10
10
|
def test_default_launcher
|
11
11
|
pid = Pigeon::Launcher.launch
|
12
12
|
|
@@ -19,41 +19,40 @@ class PigeonLauncherTest < Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_triggers
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
launcher = Pigeon::Launcher.new(Pigeon::Engine)
|
23
|
+
|
24
|
+
triggered = Hash.new { |h,k| h[k] = [ ] }
|
25
25
|
|
26
|
-
|
27
|
-
start
|
28
|
-
triggered[:start] += 1
|
29
|
-
end
|
26
|
+
launcher.handle_args('start') do |pid|
|
27
|
+
triggered[:start] << pid
|
30
28
|
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
triggered[:restart] += 1
|
35
|
-
end
|
30
|
+
launcher.handle_args('status') do |pid|
|
31
|
+
triggered[:status] << pid
|
36
32
|
end
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
triggered[:status] += 1
|
41
|
-
end
|
34
|
+
launcher.handle_args('restart') do |pid, old_pid|
|
35
|
+
triggered[:restart] << pid
|
42
36
|
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
triggered[:stop] += 1
|
47
|
-
end
|
38
|
+
launcher.handle_args('status') do |pid|
|
39
|
+
triggered[:status] << pid
|
48
40
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if (false)
|
53
|
-
assert_equal 1, triggered[:start]
|
54
|
-
assert_equal 1, triggered[:restart]
|
55
|
-
assert_equal 1, triggered[:status]
|
56
|
-
assert_equal 1, triggered[:stop]
|
41
|
+
|
42
|
+
launcher.handle_args('stop') do |pid|
|
43
|
+
triggered[:stop] << pid
|
57
44
|
end
|
45
|
+
|
46
|
+
assert triggered[:start]
|
47
|
+
assert_equal 1, triggered[:start].length
|
48
|
+
|
49
|
+
|
50
|
+
assert triggered[:restart]
|
51
|
+
assert_equal 1, triggered[:restart].length
|
52
|
+
refute_equal triggered[:start], triggered[:restart]
|
53
|
+
|
54
|
+
assert_equal triggered[:start] + triggered[:restart], triggered[:status]
|
55
|
+
|
56
|
+
assert_equal triggered[:restart], triggered[:stop]
|
58
57
|
end
|
59
58
|
end
|
@@ -6,10 +6,10 @@ class OptionClass
|
|
6
6
|
option_accessor :single_option
|
7
7
|
option_accessor :multi1, :multi2
|
8
8
|
option_accessor :option_with_default,
|
9
|
-
:
|
9
|
+
default: :example_default
|
10
10
|
|
11
11
|
option_accessor :boolean_option,
|
12
|
-
:
|
12
|
+
boolean: true
|
13
13
|
|
14
14
|
self.single_option = :single_option_default
|
15
15
|
end
|
@@ -17,7 +17,7 @@ end
|
|
17
17
|
class OptionSubclass < OptionClass
|
18
18
|
end
|
19
19
|
|
20
|
-
class PigeonOptionAccessorTest < Test
|
20
|
+
class PigeonOptionAccessorTest < Minitest::Test
|
21
21
|
def test_class_and_instance_chaining
|
22
22
|
assert_equal :single_option_default, OptionClass.single_option
|
23
23
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
|
2
2
|
|
3
|
-
class PigeonProcessorTest < Test
|
3
|
+
class PigeonProcessorTest < Minitest::Test
|
4
4
|
class TaggedTask < Pigeon::Task
|
5
5
|
attr_accessor :tag
|
6
6
|
attr_reader :last_task
|
@@ -9,6 +9,10 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
9
9
|
super(options)
|
10
10
|
@tag = tag
|
11
11
|
end
|
12
|
+
|
13
|
+
def state_initialized!
|
14
|
+
transition_to_state(:finished)
|
15
|
+
end
|
12
16
|
|
13
17
|
def inspect
|
14
18
|
"<#{@tag}>"
|
@@ -46,7 +50,7 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
46
50
|
end
|
47
51
|
|
48
52
|
assert_equal false, processor.task?
|
49
|
-
|
53
|
+
|
50
54
|
queue << TaggedTask.new(0)
|
51
55
|
|
52
56
|
assert_eventually(1) do
|
@@ -71,16 +75,18 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
71
75
|
end
|
72
76
|
|
73
77
|
def test_on_backlog
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
engine do
|
79
|
+
queue = Pigeon::Queue.new
|
80
|
+
|
81
|
+
100.times do |n|
|
82
|
+
queue << TaggedTask.new(n)
|
83
|
+
end
|
84
|
+
|
85
|
+
processor = Pigeon::Processor.new(queue)
|
86
|
+
|
87
|
+
assert_eventually(5) do
|
88
|
+
queue.empty?
|
89
|
+
end
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
@@ -145,7 +151,7 @@ class PigeonProcessorTest < Test::Unit::TestCase
|
|
145
151
|
queue << TaggedTask.new(n)
|
146
152
|
end
|
147
153
|
|
148
|
-
assert_eventually(
|
154
|
+
assert_eventually(5) do
|
149
155
|
queue.length == count
|
150
156
|
end
|
151
157
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
|
2
2
|
|
3
|
-
class PigeonSortedArrayTest < Test
|
3
|
+
class PigeonSortedArrayTest < Minitest::Test
|
4
4
|
def test_empty_state
|
5
5
|
array = Pigeon::SortedArray.new
|
6
6
|
|
@@ -21,13 +21,13 @@ class PigeonSortedArrayTest < Test::Unit::TestCase
|
|
21
21
|
def test_does_sorting
|
22
22
|
array = Pigeon::SortedArray.new
|
23
23
|
|
24
|
-
test_array = [2, 2, 2, 2, 3, 3, 3]
|
24
|
+
test_array = [ 2, 2, 2, 2, 3, 3, 3 ]
|
25
25
|
|
26
26
|
test_array.each do |i|
|
27
27
|
array << i
|
28
28
|
end
|
29
29
|
|
30
|
-
assert_equal [2, 2, 2, 2, 3, 3, 3], array.to_a
|
30
|
+
assert_equal [ 2, 2, 2, 2, 3, 3, 3 ], array.to_a
|
31
31
|
end
|
32
32
|
|
33
33
|
def test_does_sorting_with_insertion_in_order
|
@@ -1,5 +1,11 @@
|
|
1
1
|
require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
|
2
2
|
|
3
|
+
class TerminateTask < Pigeon::Task
|
4
|
+
def state_initialized!
|
5
|
+
transition_to_state(:finished)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
3
9
|
class ExampleTask < Pigeon::Task
|
4
10
|
attr_accessor :triggers
|
5
11
|
|
@@ -43,10 +49,10 @@ class FailingTask < Pigeon::Task
|
|
43
49
|
end
|
44
50
|
end
|
45
51
|
|
46
|
-
class PigeonTaskTest < Test
|
52
|
+
class PigeonTaskTest < Minitest::Test
|
47
53
|
def test_empty_task
|
48
54
|
engine do
|
49
|
-
task =
|
55
|
+
task = TerminateTask.new
|
50
56
|
|
51
57
|
reported = 0
|
52
58
|
|
@@ -68,14 +74,19 @@ class PigeonTaskTest < Test::Unit::TestCase
|
|
68
74
|
end
|
69
75
|
|
70
76
|
def test_alternate_engine
|
71
|
-
engine
|
72
|
-
|
73
|
-
|
74
|
-
|
77
|
+
engine do |default_engine|
|
78
|
+
engine = Pigeon::Engine.new
|
79
|
+
|
80
|
+
refute_equal default_engine.object_id, engine.object_id
|
81
|
+
|
82
|
+
task = Pigeon::Task.new(nil, engine)
|
83
|
+
|
84
|
+
assert_equal engine.object_id, task.engine.object_id
|
85
|
+
end
|
75
86
|
end
|
76
87
|
|
77
88
|
def test_example_task
|
78
|
-
engine do
|
89
|
+
engine do |launched|
|
79
90
|
task = ExampleTask.new
|
80
91
|
|
81
92
|
callbacks = [ ]
|
@@ -137,23 +148,25 @@ class PigeonTaskTest < Test::Unit::TestCase
|
|
137
148
|
end
|
138
149
|
|
139
150
|
def test_with_context
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
151
|
+
engine do
|
152
|
+
options = {
|
153
|
+
example: 'example1',
|
154
|
+
optional: 1
|
155
|
+
}.freeze
|
156
|
+
|
157
|
+
task = Pigeon::Task.new(options)
|
158
|
+
|
159
|
+
assert_equal options, task.context
|
160
|
+
|
161
|
+
task.context = 'test'
|
162
|
+
|
163
|
+
assert_equal 'test', task.context
|
164
|
+
end
|
152
165
|
end
|
153
166
|
|
154
167
|
def test_block_notification
|
155
168
|
engine do
|
156
|
-
task =
|
169
|
+
task = TerminateTask.new
|
157
170
|
|
158
171
|
states_triggered = [ ]
|
159
172
|
|
@@ -170,15 +183,17 @@ class PigeonTaskTest < Test::Unit::TestCase
|
|
170
183
|
end
|
171
184
|
|
172
185
|
def test_priority_order
|
173
|
-
|
174
|
-
|
186
|
+
engine do
|
187
|
+
tasks = (0..10).collect do
|
188
|
+
task = Pigeon::Task.new
|
175
189
|
|
176
|
-
|
177
|
-
|
190
|
+
# Trigger generation of default priority value
|
191
|
+
task.priority
|
178
192
|
|
179
|
-
|
193
|
+
task
|
194
|
+
end
|
195
|
+
|
196
|
+
assert_equal tasks.collect(&:object_id), tasks.sort.collect(&:object_id)
|
180
197
|
end
|
181
|
-
|
182
|
-
assert_equal tasks.collect(&:object_id), tasks.sort.collect(&:object_id)
|
183
198
|
end
|
184
199
|
end
|
data/test/unit/pigeon_test.rb
CHANGED
metadata
CHANGED
@@ -1,30 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pigeon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- tadman
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-02-18 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: eventmachine
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-reporters
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: jeweler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
20
60
|
- !ruby/object:Gem::Version
|
21
61
|
version: '0'
|
22
62
|
type: :development
|
23
63
|
prerelease: false
|
24
64
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
65
|
requirements:
|
27
|
-
- -
|
66
|
+
- - ">="
|
28
67
|
- !ruby/object:Gem::Version
|
29
68
|
version: '0'
|
30
69
|
description: Pigeon is a simple way to get started building an EventMachine engine
|
@@ -36,7 +75,9 @@ extra_rdoc_files:
|
|
36
75
|
- LICENSE
|
37
76
|
- README.md
|
38
77
|
files:
|
39
|
-
- .document
|
78
|
+
- ".document"
|
79
|
+
- ".travis.yml"
|
80
|
+
- Gemfile
|
40
81
|
- LICENSE
|
41
82
|
- README.md
|
42
83
|
- Rakefile
|
@@ -70,26 +111,25 @@ files:
|
|
70
111
|
- test/unit/pigeon_test.rb
|
71
112
|
homepage: http://github.com/twg/pigeon
|
72
113
|
licenses: []
|
114
|
+
metadata: {}
|
73
115
|
post_install_message:
|
74
116
|
rdoc_options: []
|
75
117
|
require_paths:
|
76
118
|
- lib
|
77
119
|
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
-
none: false
|
79
120
|
requirements:
|
80
|
-
- -
|
121
|
+
- - ">="
|
81
122
|
- !ruby/object:Gem::Version
|
82
123
|
version: '0'
|
83
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
-
none: false
|
85
125
|
requirements:
|
86
|
-
- -
|
126
|
+
- - ">="
|
87
127
|
- !ruby/object:Gem::Version
|
88
128
|
version: '0'
|
89
129
|
requirements: []
|
90
130
|
rubyforge_project:
|
91
|
-
rubygems_version:
|
131
|
+
rubygems_version: 2.2.2
|
92
132
|
signing_key:
|
93
|
-
specification_version:
|
133
|
+
specification_version: 4
|
94
134
|
summary: Simple daemonized EventMachine engine framework with plug-in support
|
95
135
|
test_files: []
|