perennial 0.2.2.2 → 1.0.0.1
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.
- 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
|