perennial 0.2.2.2 → 1.0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/perennial +1 -2
- data/lib/perennial/application.rb +65 -22
- data/lib/perennial/core_ext/attribute_accessors.rb +25 -0
- data/lib/perennial/core_ext/hash_key_conversions.rb +26 -0
- data/lib/perennial/core_ext/inflections.rb +33 -0
- data/lib/perennial/core_ext/misc.rb +33 -43
- data/lib/perennial/core_ext/proxy.rb +16 -0
- data/lib/perennial/core_ext.rb +4 -1
- data/lib/perennial/daemon.rb +9 -2
- data/lib/perennial/delegateable.rb +48 -0
- data/lib/perennial/dispatchable.rb +67 -24
- data/lib/perennial/generator.rb +43 -8
- data/lib/perennial/loader.rb +5 -1
- data/lib/perennial/logger.rb +14 -4
- data/lib/perennial/nash.rb +217 -0
- data/lib/perennial/option_parser.rb +3 -3
- data/lib/perennial/reloading.rb +45 -0
- data/lib/perennial/settings.rb +59 -31
- data/lib/perennial.rb +7 -5
- data/templates/application.erb +3 -2
- data/templates/rakefile.erb +1 -0
- metadata +10 -17
- data/test/dispatchable_test.rb +0 -129
- data/test/hookable_test.rb +0 -61
- data/test/loader_test.rb +0 -1
- data/test/loggable_test.rb +0 -38
- data/test/logger_test.rb +0 -57
- data/test/settings_test.rb +0 -99
- data/test/test_helper.rb +0 -38
- data/vendor/fakefs/LICENSE +0 -20
- data/vendor/fakefs/README.markdown +0 -37
- data/vendor/fakefs/Rakefile +0 -3
- data/vendor/fakefs/lib/fakefs.rb +0 -448
- data/vendor/fakefs/test/fakefs_test.rb +0 -511
- data/vendor/fakefs/test/verify.rb +0 -27
data/bin/perennial
CHANGED
@@ -15,8 +15,7 @@ Perennial::Application.processing(ARGV) do |a|
|
|
15
15
|
path = File.expand_path(path)
|
16
16
|
# Check if the folder exists
|
17
17
|
if File.exist?(path) && !opts[:force]
|
18
|
-
|
19
|
-
exit
|
18
|
+
die! "The path you tried to use, #{path}, already exists. Please try another or pass --force"
|
20
19
|
end
|
21
20
|
# Convert the name and class name.
|
22
21
|
app_path = app_name.underscore
|
@@ -15,6 +15,29 @@ module Perennial
|
|
15
15
|
klass.new.apply(*arguments)
|
16
16
|
end
|
17
17
|
|
18
|
+
def yes?(question)
|
19
|
+
result = Readline.readline("#{question.to_s.strip} (y/n) ")
|
20
|
+
result.downcase[0] == ?y
|
21
|
+
end
|
22
|
+
|
23
|
+
def ask(question, default)
|
24
|
+
result = Readline.readline("#{question.to_s.strip} (default: #{default}) ")
|
25
|
+
result.blank? ? default : result
|
26
|
+
end
|
27
|
+
|
28
|
+
def ask_password(question)
|
29
|
+
system "stty -echo"
|
30
|
+
line = Readline.readline("#{question.to_s.strip} ").strip
|
31
|
+
system "stty echo"
|
32
|
+
print "\n"
|
33
|
+
return line
|
34
|
+
end
|
35
|
+
|
36
|
+
def die!(message)
|
37
|
+
$stderr.puts message
|
38
|
+
exit! 1
|
39
|
+
end
|
40
|
+
|
18
41
|
end
|
19
42
|
|
20
43
|
attr_accessor :options, :banner, :command_env
|
@@ -30,8 +53,8 @@ module Perennial
|
|
30
53
|
@banners = {}
|
31
54
|
end
|
32
55
|
|
33
|
-
def option(name, description = nil)
|
34
|
-
option_parser.add(name, description) { |v| @command_options[name] = v }
|
56
|
+
def option(name, description = nil, opts = {})
|
57
|
+
option_parser.add(name, description, opts) { |v| @command_options[name] = v }
|
35
58
|
end
|
36
59
|
|
37
60
|
def add_default_options!
|
@@ -44,19 +67,21 @@ module Perennial
|
|
44
67
|
end
|
45
68
|
end
|
46
69
|
|
47
|
-
def controller!(controller, description)
|
70
|
+
def controller!(controller, description, opts = {})
|
48
71
|
return unless defined?(Loader)
|
49
72
|
add_default_options!
|
50
73
|
option :kill, "Kill any runninng instances"
|
51
74
|
controller_name = controller.to_s.underscore
|
52
75
|
controller = controller.to_sym
|
53
76
|
command_name = controller_name.gsub("_", "-")
|
54
|
-
|
77
|
+
parent = self
|
78
|
+
add("#{command_name} #{"[PATH]" if !opts[:skip_path]}".strip, description) do |*args|
|
55
79
|
options = args.extract_options!
|
56
80
|
path = File.expand_path(args[0] || ".")
|
57
81
|
Settings.root = path
|
58
82
|
if options.delete(:kill)
|
59
|
-
|
83
|
+
parent.attempt_showing_banner
|
84
|
+
puts "Attempting to kill processess for #{command_name}"
|
60
85
|
Daemon.kill_all(controller)
|
61
86
|
else
|
62
87
|
Loader.run!(controller, options)
|
@@ -86,35 +111,32 @@ module Perennial
|
|
86
111
|
if @commands.has_key?(command)
|
87
112
|
execute_command(command, arguments)
|
88
113
|
else
|
89
|
-
|
90
|
-
|
114
|
+
show_error "Unknown command '#{command}', please try again."
|
115
|
+
usage(true)
|
91
116
|
end
|
92
117
|
end
|
93
118
|
|
94
|
-
def usage
|
95
|
-
|
96
|
-
puts banner
|
97
|
-
puts ""
|
98
|
-
end
|
119
|
+
def usage(skip_banner = false)
|
120
|
+
attempt_showing_banner unless skip_banner
|
99
121
|
puts "Usage:"
|
100
122
|
max_width = @banners.values.map { |b| b.length }.max
|
101
123
|
@commands.keys.sort.each do |command|
|
102
124
|
next unless @descriptions.has_key?(command)
|
103
125
|
formatted_command = "#{@banners[command]} [OPTIONS]".ljust(max_width + 10)
|
104
|
-
command = "%s - %s" % [formatted_command, @descriptions[command]]
|
126
|
+
command = " %s - %s" % [formatted_command, @descriptions[command]]
|
105
127
|
puts command
|
106
128
|
end
|
129
|
+
puts ""
|
130
|
+
puts "Please note: you can pass -h / --help to any command for more specific help"
|
107
131
|
end
|
108
132
|
|
109
|
-
def help_for(command)
|
110
|
-
|
111
|
-
puts banner
|
112
|
-
puts ""
|
113
|
-
end
|
133
|
+
def help_for(command, skip_banner = false)
|
134
|
+
attempt_showing_banner unless skip_banner
|
114
135
|
puts @descriptions[command]
|
136
|
+
puts ""
|
115
137
|
puts "Usage: #{$0} #{@banners[command]} [options]"
|
116
138
|
puts "Options:"
|
117
|
-
puts @option_parsers[command].summary
|
139
|
+
puts pad_left(@option_parsers[command].summary)
|
118
140
|
exit
|
119
141
|
end
|
120
142
|
|
@@ -127,6 +149,13 @@ module Perennial
|
|
127
149
|
end
|
128
150
|
application.execute args
|
129
151
|
end
|
152
|
+
|
153
|
+
def attempt_showing_banner
|
154
|
+
if banner.present?
|
155
|
+
puts banner
|
156
|
+
puts ""
|
157
|
+
end
|
158
|
+
end
|
130
159
|
|
131
160
|
protected
|
132
161
|
|
@@ -137,7 +166,7 @@ module Perennial
|
|
137
166
|
args << opts
|
138
167
|
@command_env.execute(command_proc, args)
|
139
168
|
else
|
140
|
-
|
169
|
+
help_for(command, true)
|
141
170
|
end
|
142
171
|
end
|
143
172
|
|
@@ -160,14 +189,28 @@ module Perennial
|
|
160
189
|
needed_count = blk.arity - 1
|
161
190
|
provided_count = arguments.size
|
162
191
|
if needed_count > 0 && needed_count != provided_count
|
163
|
-
|
192
|
+
attempt_showing_banner
|
193
|
+
show_error "You didn't provide the correct number of arguments (needed #{needed_count}, provided #{provided_count})"
|
164
194
|
elsif needed_count < 0 && (-needed_count - 2) > provided_count
|
165
|
-
|
195
|
+
show_error "You didn't provide enough arguments - a minimum of #{-needed_count} are needed."
|
166
196
|
else
|
167
197
|
return true
|
168
198
|
end
|
169
199
|
|
170
200
|
end
|
171
201
|
|
202
|
+
def show_error(text)
|
203
|
+
attempt_showing_banner
|
204
|
+
text = "Error: #{text}".strip
|
205
|
+
puts "--#{"-" * text.length}"
|
206
|
+
puts " #{text} "
|
207
|
+
puts "--#{"-" * text.length}"
|
208
|
+
puts ""
|
209
|
+
end
|
210
|
+
|
211
|
+
def pad_left(text, spacing = 2)
|
212
|
+
text.split("\n").map { |l| "#{" " * spacing}#{l}" }.join("\n")
|
213
|
+
end
|
214
|
+
|
172
215
|
end
|
173
216
|
end
|
@@ -1,6 +1,31 @@
|
|
1
1
|
# cattr_* and class_inheritable_* are taken from
|
2
2
|
# ActiveSupport. Included here to help keep the
|
3
3
|
# codebase simple / clean.
|
4
|
+
|
5
|
+
class Object
|
6
|
+
|
7
|
+
unless respond_to?(:instance_variable_defined?)
|
8
|
+
def instance_variable_defined?(variable)
|
9
|
+
instance_variables.include?(variable.to_s)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def instance_values #:nodoc:
|
14
|
+
instance_variables.inject({}) do |values, name|
|
15
|
+
values[name.to_s[1..-1]] = instance_variable_get(name)
|
16
|
+
values
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
if RUBY_VERSION >= '1.9'
|
21
|
+
def instance_variable_names
|
22
|
+
instance_variables.map { |var| var.to_s }
|
23
|
+
end
|
24
|
+
else
|
25
|
+
alias_method :instance_variable_names, :instance_variables
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
4
29
|
class Class
|
5
30
|
def cattr_reader(*syms)
|
6
31
|
syms.flatten.each do |sym|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Hash
|
2
|
+
def symbolize_keys
|
3
|
+
hash = self.dup
|
4
|
+
hash.symbolize_keys!
|
5
|
+
return hash
|
6
|
+
end
|
7
|
+
|
8
|
+
def symbolize_keys!
|
9
|
+
hash = {}
|
10
|
+
self.each_pair { |k,v| hash[k.to_sym] = v }
|
11
|
+
replace hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def stringify_keys!
|
15
|
+
hash = {}
|
16
|
+
self.each_pair { |k, v| hash[k.to_s] = v }
|
17
|
+
replace hash
|
18
|
+
end
|
19
|
+
|
20
|
+
def stringify_keys
|
21
|
+
hash = self.dup
|
22
|
+
hash.stringify_keys!
|
23
|
+
return hash
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Perennial
|
2
|
+
class Inflector
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def underscore(camel_cased_word)
|
6
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
7
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
8
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
9
|
+
tr("-", "_").
|
10
|
+
downcase
|
11
|
+
end
|
12
|
+
|
13
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
14
|
+
if first_letter_in_uppercase
|
15
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
16
|
+
else
|
17
|
+
lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class String
|
26
|
+
def underscore
|
27
|
+
Perennial::Inflector.underscore(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def camelize(capitalize_first_letter = true)
|
31
|
+
Perennial::Inflector.camelize(self, capitalize_first_letter)
|
32
|
+
end
|
33
|
+
end
|
@@ -3,30 +3,14 @@ class Object
|
|
3
3
|
def metaclass
|
4
4
|
class << self; self; end
|
5
5
|
end
|
6
|
+
|
7
|
+
def meta_def(name, &blk)
|
8
|
+
metaclass.define_method(name, &blk)
|
9
|
+
end
|
6
10
|
|
7
11
|
end
|
8
12
|
|
9
|
-
class Inflector
|
10
|
-
class << self
|
11
13
|
|
12
|
-
def underscore(camel_cased_word)
|
13
|
-
camel_cased_word.to_s.gsub(/::/, '/').
|
14
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
15
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
16
|
-
tr("-", "_").
|
17
|
-
downcase
|
18
|
-
end
|
19
|
-
|
20
|
-
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
21
|
-
if first_letter_in_uppercase
|
22
|
-
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
23
|
-
else
|
24
|
-
lower_case_and_underscored_word.first.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
30
14
|
|
31
15
|
module Kernel
|
32
16
|
|
@@ -53,14 +37,6 @@ class String
|
|
53
37
|
File.join(self, *args)
|
54
38
|
end
|
55
39
|
|
56
|
-
def underscore
|
57
|
-
Inflector.underscore(self)
|
58
|
-
end
|
59
|
-
|
60
|
-
def camelize(capitalize_first_letter = true)
|
61
|
-
Inflector.camelize(self, capitalize_first_letter)
|
62
|
-
end
|
63
|
-
|
64
40
|
def to_pathname
|
65
41
|
Pathname.new(self)
|
66
42
|
end
|
@@ -72,23 +48,9 @@ class Array
|
|
72
48
|
end
|
73
49
|
end
|
74
50
|
|
75
|
-
class Hash
|
76
|
-
def symbolize_keys
|
77
|
-
hash = self.dup
|
78
|
-
hash.symbolize_keys!
|
79
|
-
return hash
|
80
|
-
end
|
81
|
-
|
82
|
-
def symbolize_keys!
|
83
|
-
hash = {}
|
84
|
-
self.each_pair { |k,v| hash[k.to_sym] = v }
|
85
|
-
replace hash
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
51
|
class Module
|
90
52
|
|
91
|
-
def
|
53
|
+
def has_library(*items)
|
92
54
|
namespace = self.to_s.underscore
|
93
55
|
items.each do |item|
|
94
56
|
require File.join(namespace, item.to_s.underscore)
|
@@ -106,6 +68,19 @@ class Module
|
|
106
68
|
end
|
107
69
|
end
|
108
70
|
|
71
|
+
def add_extension(name, &blk)
|
72
|
+
item = name.to_s.camelize.to_sym
|
73
|
+
target = const_get(item) rescue nil
|
74
|
+
raise "Didn't find library for #{name}" if target.nil?
|
75
|
+
if target.is_a?(Class)
|
76
|
+
target.class_eval(&blk)
|
77
|
+
elsif target.is_a?(Module)
|
78
|
+
target.module_eval(&blk)
|
79
|
+
else
|
80
|
+
raise "Unable to extend #{target.inspect}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
109
84
|
def attempt_require(*files)
|
110
85
|
files.each do |file|
|
111
86
|
begin
|
@@ -115,4 +90,19 @@ class Module
|
|
115
90
|
end
|
116
91
|
end
|
117
92
|
|
93
|
+
end
|
94
|
+
|
95
|
+
class Class
|
96
|
+
|
97
|
+
def is(*mixins)
|
98
|
+
ns = Perennial::Manifest.namespace
|
99
|
+
return if ns.blank?
|
100
|
+
mixins.each do |mixin|
|
101
|
+
begin
|
102
|
+
include ns.const_get(mixin.to_s.camelize)
|
103
|
+
rescue NameError
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
118
108
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Perennial
|
2
|
+
# a super simple proxy class.
|
3
|
+
class Proxy
|
4
|
+
|
5
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
|
6
|
+
|
7
|
+
attr_accessor :__proxy_target__
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def method_missing(m, *args, &blk)
|
12
|
+
__proxy_target__.send(m, *args, &blk)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
data/lib/perennial/core_ext.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# Require each individial core extension
|
2
2
|
require 'perennial/core_ext/attribute_accessors'
|
3
3
|
require 'perennial/core_ext/blank'
|
4
|
-
require 'perennial/core_ext/misc'
|
4
|
+
require 'perennial/core_ext/misc'
|
5
|
+
require 'perennial/core_ext/hash_key_conversions'
|
6
|
+
require 'perennial/core_ext/inflections'
|
7
|
+
require 'perennial/core_ext/proxy'
|
data/lib/perennial/daemon.rb
CHANGED
@@ -62,9 +62,9 @@ module Perennial
|
|
62
62
|
# the double fork approach. Also, changes process file
|
63
63
|
# mask to 000 and reopens STDIN / OUT to /dev/null
|
64
64
|
def daemonize!
|
65
|
-
|
65
|
+
fork_off_and_die
|
66
66
|
Process.setsid
|
67
|
-
|
67
|
+
fork_off_and_die
|
68
68
|
self.write_pid
|
69
69
|
File.umask 0000
|
70
70
|
STDIN.reopen "/dev/null"
|
@@ -118,6 +118,13 @@ module Perennial
|
|
118
118
|
File.open(f, "w+") { |f| f.puts pids.join("\n") }
|
119
119
|
end
|
120
120
|
|
121
|
+
def fork_off_and_die
|
122
|
+
if pid = fork
|
123
|
+
Process.detach(pid)
|
124
|
+
exit
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
121
128
|
end
|
122
129
|
end
|
123
130
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Perennial
|
2
|
+
# Objective-C / Cocoa style 'delegates'.
|
3
|
+
# Essentially proxies which dispatch only
|
4
|
+
# when an object responds to the method.
|
5
|
+
class DelegateProxy < Proxy
|
6
|
+
|
7
|
+
def initialize(t)
|
8
|
+
@__proxy_target__ = t
|
9
|
+
end
|
10
|
+
|
11
|
+
def respond_to?(method, inc_super = false)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def method_missing(method, *args, &blk)
|
18
|
+
@__proxy_target__.send(method, *args, &blk) if @__proxy_target__.respond_to?(method)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
module Delegateable
|
24
|
+
|
25
|
+
def self.included(parent)
|
26
|
+
parent.send(:include, InstanceMethods)
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
|
31
|
+
def delegate=(value)
|
32
|
+
@delegate = DelegateProxy.new(value)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias delegate_to delegate=
|
36
|
+
|
37
|
+
def delegate
|
38
|
+
@delegate ||= DelegateProxy.new(nil)
|
39
|
+
end
|
40
|
+
|
41
|
+
def real_delegate
|
42
|
+
@delegate && @delegate.__proxy_target__
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -21,6 +21,10 @@ module Perennial
|
|
21
21
|
@@handler_mapping ||= Hash.new { |h,k| h[k] = [] }
|
22
22
|
end
|
23
23
|
|
24
|
+
def self.reloading_mapping
|
25
|
+
@@reloading_mapping ||= Hash.new { |h,k| h[k] = [] }
|
26
|
+
end
|
27
|
+
|
24
28
|
def self.included(parent)
|
25
29
|
parent.class_eval do
|
26
30
|
include InstanceMethods
|
@@ -36,6 +40,14 @@ module Perennial
|
|
36
40
|
self.class.handlers
|
37
41
|
end
|
38
42
|
|
43
|
+
def dispatch_queue
|
44
|
+
@dispatch_queue ||= []
|
45
|
+
end
|
46
|
+
|
47
|
+
def dispatching?
|
48
|
+
@dispatching ||= false
|
49
|
+
end
|
50
|
+
|
39
51
|
# Dispatch an 'event' with a given name to the handlers
|
40
52
|
# registered on the current class. Used as a nicer way of defining
|
41
53
|
# behaviours that should occur under a given set of circumstances.
|
@@ -43,32 +55,44 @@ module Perennial
|
|
43
55
|
# +name+: The name of the current event
|
44
56
|
# +opts+: an optional hash of options to pass
|
45
57
|
def dispatch(name, opts = {})
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
handler.
|
60
|
-
|
61
|
-
|
58
|
+
if !dispatching?
|
59
|
+
Logger.debug "Dispatching #{name} event (#{dispatch_queue.size} queued - on #{self.class.name})"
|
60
|
+
# Add ourselves to the queue
|
61
|
+
@dispatching = true
|
62
|
+
begin
|
63
|
+
# The full handler name is the method we call given it exists.
|
64
|
+
full_handler_name = :"handle_#{name.to_s.underscore}"
|
65
|
+
# First, dispatch locally if the method is defined.
|
66
|
+
self.send(full_handler_name, opts) if self.respond_to?(full_handler_name)
|
67
|
+
# Iterate through all of the registered handlers,
|
68
|
+
# If there is a method named handle_<event_name>
|
69
|
+
# defined we sent that otherwise we call the handle
|
70
|
+
# method on the handler. Note that the handle method
|
71
|
+
# is the only required aspect of a handler. An improved
|
72
|
+
# version of this would likely cache the respond_to?
|
73
|
+
# call.
|
74
|
+
self.handlers.each do |handler|
|
75
|
+
if handler.respond_to?(full_handler_name)
|
76
|
+
handler.send(full_handler_name, opts)
|
77
|
+
else
|
78
|
+
handler.handle name, opts
|
79
|
+
end
|
80
|
+
end
|
81
|
+
# If we get the HaltHandlerProcessing exception, we
|
82
|
+
# catch it and continue on our way. In essence, we
|
83
|
+
# stop the dispatch of events to the next set of the
|
84
|
+
# handlers.
|
85
|
+
rescue HaltHandlerProcessing => e
|
86
|
+
Logger.info "Halting processing chain"
|
87
|
+
rescue Exception => e
|
88
|
+
Logger.log_exception(e)
|
62
89
|
end
|
90
|
+
@dispatching = false
|
91
|
+
dispatch(*@dispatch_queue.shift) unless dispatch_queue.empty?
|
92
|
+
else
|
93
|
+
Logger.debug "Adding #{name} event to the end of the queue (on #{self.class.name})"
|
94
|
+
dispatch_queue << [name, opts]
|
63
95
|
end
|
64
|
-
# If we get the HaltHandlerProcessing exception, we
|
65
|
-
# catch it and continue on our way. In essence, we
|
66
|
-
# stop the dispatch of events to the next set of the
|
67
|
-
# handlers.
|
68
|
-
rescue HaltHandlerProcessing => e
|
69
|
-
Logger.info "Halting processing chain"
|
70
|
-
rescue Exception => e
|
71
|
-
Logger.log_exception(e)
|
72
96
|
end
|
73
97
|
|
74
98
|
end
|
@@ -100,10 +124,29 @@ module Perennial
|
|
100
124
|
# Handlers are called in the order they are registered.
|
101
125
|
def register_handler(handler)
|
102
126
|
unless handler.blank? || !handler.respond_to?(:handle)
|
127
|
+
handler.registered = true if handler.respond_to?(:registered=)
|
103
128
|
Dispatchable.handler_mapping[self] << handler
|
104
129
|
end
|
105
130
|
end
|
106
131
|
|
132
|
+
def delete_handler(handler)
|
133
|
+
return if handler.blank?
|
134
|
+
handler.registered = false if handler.respond_to?(:registered=)
|
135
|
+
Dispatchable.handler_mapping[self].delete(handler)
|
136
|
+
end
|
137
|
+
|
138
|
+
def reloading!
|
139
|
+
handlers = Dispatchable.handler_mapping.delete(self)
|
140
|
+
Dispatchable.reloading_mapping[self.name] = handlers
|
141
|
+
end
|
142
|
+
|
143
|
+
def reloaded!
|
144
|
+
if Dispatchable.reloading_mapping.has_key?(self.name)
|
145
|
+
handlers = Dispatchable.reloading_mapping.delete(self.name)
|
146
|
+
handlers.each { |h| register_handler(h) }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
107
150
|
end
|
108
151
|
|
109
152
|
end
|