adhearsion 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -2
- data/EVENTS +1 -1
- data/Rakefile +4 -4
- data/adhearsion.gemspec +8 -4
- data/app_generators/ahn/USAGE +3 -3
- data/app_generators/ahn/ahn_generator.rb +11 -11
- data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +3 -3
- data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +6 -6
- data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +16 -16
- data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +42 -42
- data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +11 -11
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +1 -1
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +6 -6
- data/app_generators/ahn/templates/components/simon_game/simon_game.rb +4 -4
- data/app_generators/ahn/templates/config/startup.rb +31 -16
- data/bin/ahn +3 -3
- data/bin/ahnctl +8 -8
- data/bin/jahn +3 -3
- data/examples/asterisk_manager_interface/standalone.rb +2 -2
- data/lib/adhearsion.rb +4 -2
- data/lib/adhearsion/cli.rb +31 -31
- data/lib/adhearsion/component_manager.rb +39 -39
- data/lib/adhearsion/component_manager/component_tester.rb +14 -14
- data/lib/adhearsion/component_manager/spec_framework.rb +1 -1
- data/lib/adhearsion/events_support.rb +12 -12
- data/lib/adhearsion/foundation/blank_slate.rb +1 -1
- data/lib/adhearsion/foundation/custom_daemonizer.rb +2 -2
- data/lib/adhearsion/foundation/event_socket.rb +26 -26
- data/lib/adhearsion/foundation/future_resource.rb +6 -6
- data/lib/adhearsion/foundation/metaprogramming.rb +2 -2
- data/lib/adhearsion/foundation/numeric.rb +3 -3
- data/lib/adhearsion/foundation/relationship_properties.rb +7 -7
- data/lib/adhearsion/foundation/string.rb +8 -8
- data/lib/adhearsion/foundation/synchronized_hash.rb +8 -8
- data/lib/adhearsion/host_definitions.rb +16 -16
- data/lib/adhearsion/initializer.rb +74 -65
- data/lib/adhearsion/initializer/asterisk.rb +15 -9
- data/lib/adhearsion/initializer/configuration.rb +54 -39
- data/lib/adhearsion/initializer/database.rb +4 -4
- data/lib/adhearsion/initializer/drb.rb +6 -6
- data/lib/adhearsion/initializer/freeswitch.rb +1 -1
- data/lib/adhearsion/initializer/ldap.rb +51 -0
- data/lib/adhearsion/initializer/rails.rb +8 -8
- data/lib/adhearsion/logging.rb +16 -16
- data/lib/adhearsion/tasks/deprecations.rb +12 -12
- data/lib/adhearsion/tasks/generating.rb +2 -2
- data/lib/adhearsion/tasks/testing.rb +7 -7
- data/lib/adhearsion/version.rb +1 -1
- data/lib/adhearsion/voip/asterisk/agi_server.rb +44 -14
- data/lib/adhearsion/voip/asterisk/commands.rb +281 -237
- data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +8 -8
- data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +14 -14
- data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +16 -16
- data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +39 -39
- data/lib/adhearsion/voip/asterisk/config_manager.rb +13 -13
- data/lib/adhearsion/voip/asterisk/manager_interface.rb +91 -87
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +739 -739
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +60 -60
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +16 -16
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +1 -1
- data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +13 -13
- data/lib/adhearsion/voip/asterisk/super_manager.rb +3 -3
- data/lib/adhearsion/voip/call.rb +101 -64
- data/lib/adhearsion/voip/call_routing.rb +9 -9
- data/lib/adhearsion/voip/constants.rb +7 -7
- data/lib/adhearsion/voip/conveniences.rb +1 -1
- data/lib/adhearsion/voip/dial_plan.rb +42 -40
- data/lib/adhearsion/voip/dsl/dialing_dsl.rb +27 -27
- data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +1 -1
- data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +6 -6
- data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +17 -17
- data/lib/adhearsion/voip/dsl/dialplan/parser.rb +7 -7
- data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +2 -2
- data/lib/adhearsion/voip/dsl/numerical_string.rb +3 -3
- data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +7 -7
- data/lib/adhearsion/voip/freeswitch/event_handler.rb +1 -1
- data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +20 -20
- data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +5 -5
- data/lib/adhearsion/voip/freeswitch/oes_server.rb +33 -33
- data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +1 -1
- data/lib/adhearsion/voip/menu_state_machine/matchers.rb +2 -2
- data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +9 -9
- data/lib/theatre.rb +18 -18
- data/lib/theatre/callback_definition_loader.rb +17 -17
- data/lib/theatre/guid.rb +6 -6
- data/lib/theatre/invocation.rb +19 -19
- data/lib/theatre/namespace_manager.rb +28 -28
- metadata +55 -14
@@ -54,7 +54,7 @@ module Adhearsion
|
|
54
54
|
calculated_matches << calculated_match
|
55
55
|
actual_potential_matches.concat calculated_match.potential_matches
|
56
56
|
actual_exact_matches.concat calculated_match.exact_matches
|
57
|
-
|
57
|
+
|
58
58
|
potential_matches << calculated_match if calculated_match.potential_match?
|
59
59
|
exact_matches << calculated_match if calculated_match.exact_match?
|
60
60
|
end
|
@@ -52,11 +52,11 @@ module Adhearsion
|
|
52
52
|
|
53
53
|
def match(query)
|
54
54
|
numerical_query = coerce_to_numeric query
|
55
|
-
if numerical_query
|
55
|
+
if numerical_query
|
56
56
|
exact_match = pattern.include?(numerical_query) ? query : nil
|
57
57
|
potential_matches = numbers_in_range_like numerical_query
|
58
58
|
potential_matches.reject! { |m| m.to_s == exact_match.to_s } if exact_match
|
59
|
-
|
59
|
+
|
60
60
|
new_calculated_match :query => query, :exact_matches => exact_match,
|
61
61
|
:potential_matches => potential_matches
|
62
62
|
else
|
@@ -11,7 +11,7 @@ module Adhearsion
|
|
11
11
|
attr_reader :builder, :timeout, :tries_count, :max_number_of_tries
|
12
12
|
def initialize(options={}, &block)
|
13
13
|
@tries_count = 0 # Counts the number of tries the menu's been executed
|
14
|
-
|
14
|
+
|
15
15
|
@timeout = options[:timeout] || DEFAULT_TIMEOUT
|
16
16
|
@max_number_of_tries = options[:tries] || DEFAULT_MAX_NUMBER_OF_TRIES
|
17
17
|
|
@@ -24,15 +24,15 @@ module Adhearsion
|
|
24
24
|
def <<(other)
|
25
25
|
digit_buffer << other
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def digit_buffer
|
29
29
|
@digit_buffer
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def digit_buffer_string
|
33
33
|
digit_buffer.to_s
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def digit_buffer_empty?
|
37
37
|
digit_buffer.empty?
|
38
38
|
end
|
@@ -79,8 +79,8 @@ module Adhearsion
|
|
79
79
|
end
|
80
80
|
|
81
81
|
protected
|
82
|
-
|
83
|
-
# If you're using a more complex class in subclasses, you may want to override this method in addition to the
|
82
|
+
|
83
|
+
# If you're using a more complex class in subclasses, you may want to override this method in addition to the
|
84
84
|
# digit_buffer, digit_buffer_empty, and digit_buffer_string methods
|
85
85
|
def initialize_digit_buffer
|
86
86
|
@digit_buffer = ClearableStringBuffer.new
|
@@ -131,17 +131,17 @@ module Adhearsion
|
|
131
131
|
# Raised when the user's input matches no patterns
|
132
132
|
class MenuResultInvalid < MenuResult; end
|
133
133
|
|
134
|
-
# For our default purposes, we need the digit_buffer to behave much like a normal String except that it should
|
134
|
+
# For our default purposes, we need the digit_buffer to behave much like a normal String except that it should
|
135
135
|
# handle its own resetting (clearing).
|
136
136
|
class ClearableStringBuffer < String
|
137
137
|
def clear!
|
138
138
|
replace ""
|
139
139
|
end
|
140
|
-
|
140
|
+
|
141
141
|
def <<(other)
|
142
142
|
super other.to_s
|
143
143
|
end
|
144
|
-
|
144
|
+
|
145
145
|
end
|
146
146
|
|
147
147
|
end
|
data/lib/theatre.rb
CHANGED
@@ -9,11 +9,11 @@ require 'theatre/invocation'
|
|
9
9
|
require 'theatre/callback_definition_loader'
|
10
10
|
|
11
11
|
module Theatre
|
12
|
-
|
12
|
+
|
13
13
|
class Theatre
|
14
|
-
|
14
|
+
|
15
15
|
attr_reader :namespace_manager
|
16
|
-
|
16
|
+
|
17
17
|
##
|
18
18
|
# Creates a new stopped Theatre. You must call start!() after you instantiate this for it to begin processing events.
|
19
19
|
#
|
@@ -27,7 +27,7 @@ module Theatre
|
|
27
27
|
@master_queue = Queue.new
|
28
28
|
@loader_mixins = []
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
##
|
32
32
|
# Send a message to this Theatre for asynchronous processing.
|
33
33
|
#
|
@@ -48,10 +48,10 @@ module Theatre
|
|
48
48
|
invocation
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
##
|
53
53
|
# Send a message to this Theatre for synchronous processing. The execution of this will not go through this Theatre's
|
54
|
-
# Thread pool. If an error occurred in any of callbacks, the Exception object will be placed in the returned Array
|
54
|
+
# Thread pool. If an error occurred in any of callbacks, the Exception object will be placed in the returned Array
|
55
55
|
# instead for you to act upon.
|
56
56
|
#
|
57
57
|
# @param [String] namespace The namespace to which the payload should be sent
|
@@ -72,29 +72,29 @@ module Theatre
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
def load_events_code(code, *args)
|
77
77
|
loader = CallbackDefinitionLoader.new(self, *args)
|
78
78
|
loader.load_events_code code
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
def load_events_file(file, *args)
|
82
82
|
loader = CallbackDefinitionLoader.new(self, *args)
|
83
83
|
loader.load_events_file file
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
def register_namespace_name(*args)
|
87
87
|
@namespace_manager.register_namespace_name(*args)
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
def register_callback_at_namespace(*args)
|
91
91
|
@namespace_manager.register_callback_at_namespace(*args)
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
def register_loader_mixin(mod)
|
95
95
|
@loader_mixins << mod
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
def join
|
99
99
|
@thread_group.list.each do |thread|
|
100
100
|
begin
|
@@ -104,7 +104,7 @@ module Theatre
|
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
##
|
109
109
|
# Starts this Theatre.
|
110
110
|
#
|
@@ -117,7 +117,7 @@ module Theatre
|
|
117
117
|
@thread_group.add Thread.new(&method(:thread_loop))
|
118
118
|
end
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
##
|
122
122
|
# Notifies all Threads for this Theatre to stop by sending them special messages. Any messages which were queued and
|
123
123
|
# untriggered when this method is received will still be processed. Note: you may start this Theatre again later once it
|
@@ -127,14 +127,14 @@ module Theatre
|
|
127
127
|
@thread_count.times { @master_queue << :THEATRE_SHUTDOWN! }
|
128
128
|
@started_time = nil
|
129
129
|
end
|
130
|
-
|
130
|
+
|
131
131
|
protected
|
132
|
-
|
132
|
+
|
133
133
|
# This will use the Adhearsion logger eventually.
|
134
134
|
def warn(exception)
|
135
135
|
# STDERR.puts exception.message, *exception.backtrace
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
def thread_loop
|
139
139
|
loop do
|
140
140
|
begin
|
@@ -146,6 +146,6 @@ module Theatre
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
end
|
151
151
|
end
|
@@ -1,22 +1,22 @@
|
|
1
1
|
module Theatre
|
2
|
-
|
2
|
+
|
3
3
|
##
|
4
4
|
# This class provides the a wrapper aroung which an events.rb file can be instance_eval'd.
|
5
5
|
#
|
6
6
|
class CallbackDefinitionLoader
|
7
|
-
|
7
|
+
|
8
8
|
attr_reader :theatre, :root_name
|
9
9
|
def initialize(theatre, root_name=:events)
|
10
10
|
@theatre = theatre
|
11
11
|
@root_name = root_name
|
12
|
-
|
12
|
+
|
13
13
|
create_recorder_method root_name
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def anonymous_recorder
|
17
17
|
BlankSlateMessageRecorder.new(&method(:callback_registered))
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
##
|
21
21
|
# Parses the given Ruby source code file and returns this object.
|
22
22
|
#
|
@@ -27,7 +27,7 @@ module Theatre
|
|
27
27
|
instance_eval file.read, file.path
|
28
28
|
self
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
##
|
32
32
|
# Parses the given Ruby source code and returns this object.
|
33
33
|
#
|
@@ -41,9 +41,9 @@ module Theatre
|
|
41
41
|
instance_eval code
|
42
42
|
self
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
protected
|
46
|
-
|
46
|
+
|
47
47
|
##
|
48
48
|
# Immediately register the namespace and callback with the Theatre instance given to the constructor. This method is only
|
49
49
|
# called when a new BlankSlateMessageRecorder is instantiated and receives #each().
|
@@ -51,34 +51,34 @@ module Theatre
|
|
51
51
|
def callback_registered(namespaces, callback)
|
52
52
|
# Get rid of all arguments passed to the namespaces. Will support arguments in the future.
|
53
53
|
namespaces = namespaces.map { |namespace| namespace.first }
|
54
|
-
|
54
|
+
|
55
55
|
theatre.namespace_manager.register_callback_at_namespace namespaces, callback
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
def create_recorder_method(record_method_name)
|
59
59
|
(class << self; self; end).send(:alias_method, record_method_name, :anonymous_recorder)
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
class BlankSlateMessageRecorder
|
63
|
-
|
63
|
+
|
64
64
|
(instance_methods - %w[__send__ __id__]).each { |m| undef_method m }
|
65
|
-
|
65
|
+
|
66
66
|
def initialize(¬ify_on_completion)
|
67
67
|
@notify_on_completion = notify_on_completion
|
68
68
|
@namespaces = []
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
def method_missing(*method_name_and_args)
|
72
72
|
raise ArgumentError, "Supplying a block is not supported" if block_given?
|
73
73
|
@namespaces << method_name_and_args
|
74
74
|
self
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
def each(&callback)
|
78
78
|
@notify_on_completion.call(@namespaces, callback)
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
end
|
84
84
|
end
|
data/lib/theatre/guid.rb
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
# Theatre depends and make that a dependent library.
|
3
3
|
|
4
4
|
unless respond_to? :new_guid
|
5
|
-
|
5
|
+
|
6
6
|
def random_character
|
7
7
|
case random_digit = rand(62)
|
8
|
-
when 0...10
|
9
|
-
when 10...36
|
10
|
-
when 36...62
|
8
|
+
when 0...10 then random_digit.to_s
|
9
|
+
when 10...36 then (random_digit + 55).chr
|
10
|
+
when 36...62 then (random_digit + 61).chr
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def random_string(length_of_string=8)
|
15
15
|
Array.new(length_of_string) { random_character }.join
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
# This GUID implementation doesn't adhere to the RFC which wants to make certain segments based on the MAC address of a
|
19
19
|
# network interface card and other wackiness. It's sufficiently random for our needs.
|
20
20
|
def new_guid
|
data/lib/theatre/invocation.rb
CHANGED
@@ -3,16 +3,16 @@ require 'thread'
|
|
3
3
|
require 'monitor'
|
4
4
|
|
5
5
|
module Theatre
|
6
|
-
|
6
|
+
|
7
7
|
##
|
8
8
|
# An Invocation is an object which Theatre generates and returns from Theatre#trigger.
|
9
9
|
#
|
10
10
|
class Invocation
|
11
|
-
|
11
|
+
|
12
12
|
attr_reader :queued_time, :started_time, :finished_time, :unique_id, :callback, :namespace, :error, :returned_value
|
13
|
-
|
13
|
+
|
14
14
|
class InvalidStateError < Exception; end
|
15
|
-
|
15
|
+
|
16
16
|
##
|
17
17
|
# Create a new Invocation.
|
18
18
|
#
|
@@ -27,14 +27,14 @@ module Theatre
|
|
27
27
|
@callback = callback
|
28
28
|
@current_state = :new
|
29
29
|
@state_lock = Mutex.new
|
30
|
-
|
30
|
+
|
31
31
|
# Used just to protect access to the @returned_value instance variable
|
32
32
|
@returned_value_lock = Monitor.new
|
33
|
-
|
33
|
+
|
34
34
|
# Used when wait() is called to notify all waiting threads by using a ConditionVariable
|
35
35
|
@returned_value_blocker = @returned_value_lock.new_cond#Monitor::ConditionVariable.new @returned_value_lock
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
def queued
|
39
39
|
with_state_lock do
|
40
40
|
raise InvalidStateError unless @current_state == :new
|
@@ -43,18 +43,18 @@ module Theatre
|
|
43
43
|
end
|
44
44
|
true
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
47
|
def current_state
|
48
48
|
with_state_lock { @current_state }
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
def start
|
52
52
|
with_state_lock do
|
53
53
|
raise InvalidStateError unless @current_state == :queued
|
54
54
|
@current_state = :running
|
55
55
|
end
|
56
56
|
@started_time = Time.now.freeze
|
57
|
-
|
57
|
+
|
58
58
|
begin
|
59
59
|
self.returned_value = if @payload.equal? :theatre_no_payload
|
60
60
|
@callback.call
|
@@ -68,20 +68,20 @@ module Theatre
|
|
68
68
|
@finished_time = Time.now.freeze
|
69
69
|
end
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
def execution_duration
|
73
73
|
return nil unless @finished_time
|
74
74
|
@finished_time - @started_time
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
def error?
|
78
78
|
current_state.equal? :error
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
def success?
|
82
82
|
current_state.equal? :success
|
83
83
|
end
|
84
|
-
|
84
|
+
|
85
85
|
##
|
86
86
|
# When this Invocation has been queued, started, and entered either the :success or :error state, this method will
|
87
87
|
# finally return. Until then, it blocks the Thread.
|
@@ -94,9 +94,9 @@ module Theatre
|
|
94
94
|
# Return the returned_value
|
95
95
|
with_returned_value_lock { @returned_value }
|
96
96
|
end
|
97
|
-
|
97
|
+
|
98
98
|
protected
|
99
|
-
|
99
|
+
|
100
100
|
##
|
101
101
|
# Protected setter which does some other housework when the returned value is found (such as notifying wait()ers)
|
102
102
|
#
|
@@ -108,14 +108,14 @@ module Theatre
|
|
108
108
|
@returned_value_blocker.broadcast
|
109
109
|
end
|
110
110
|
end
|
111
|
-
|
111
|
+
|
112
112
|
def with_returned_value_lock(&block)
|
113
113
|
@returned_value_lock.synchronize(&block)
|
114
114
|
end
|
115
|
-
|
115
|
+
|
116
116
|
def with_state_lock(&block)
|
117
117
|
@state_lock.synchronize(&block)
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
end
|
121
121
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module Theatre
|
2
|
-
|
2
|
+
|
3
3
|
##
|
4
4
|
# Manages the hierarchial namespaces of a Theatre. This class is Thread-safe.
|
5
5
|
#
|
6
6
|
class ActorNamespaceManager
|
7
7
|
|
8
8
|
VALID_NAMESPACE = %r{^(/[\w_]+)+$}
|
9
|
-
|
9
|
+
|
10
10
|
class << self
|
11
11
|
def valid_namespace_path?(namespace_path)
|
12
12
|
namespace_path =~ VALID_NAMESPACE
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
##
|
16
16
|
# Since there are a couple ways to represent namespaces, this is a helper method which will normalize
|
17
17
|
# them into the most practical: an Array of Symbols
|
@@ -25,13 +25,13 @@ module Theatre
|
|
25
25
|
end
|
26
26
|
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def initialize
|
30
30
|
@registry_lock = Mutex.new
|
31
31
|
@root = RootNamespaceNode.new
|
32
32
|
end
|
33
|
-
|
34
|
-
##
|
33
|
+
|
34
|
+
##
|
35
35
|
# Have this registry recognize a new path and prepare it for callback registrations. All path segements will be created
|
36
36
|
# in order. For example, when registering "/foo/bar/qaz" when no namespaces at all have been registered, this method will
|
37
37
|
# first register "foo", then "bar", then "qaz". If the namespace was already registered, it will not be affected.
|
@@ -42,12 +42,12 @@ module Theatre
|
|
42
42
|
#
|
43
43
|
def register_namespace_name(*paths)
|
44
44
|
paths = self.class.normalize_path_to_array paths
|
45
|
-
|
45
|
+
|
46
46
|
paths.inject(@root) do |node, name|
|
47
47
|
node.register_namespace_name name
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
##
|
52
52
|
# Returns a Proc found after searching with the namespace you provide
|
53
53
|
#
|
@@ -56,7 +56,7 @@ module Theatre
|
|
56
56
|
def callbacks_for_namespaces(*paths)
|
57
57
|
search_for_namespace(paths).callbacks
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
##
|
61
61
|
# Find a namespace in the tree.
|
62
62
|
#
|
@@ -66,7 +66,7 @@ module Theatre
|
|
66
66
|
def search_for_namespace(paths)
|
67
67
|
paths = self.class.normalize_path_to_array paths
|
68
68
|
path_string = "/"
|
69
|
-
|
69
|
+
|
70
70
|
found_namespace = paths.inject(@root) do |last_node,this_node_name|
|
71
71
|
raise NamespaceNotFound.new(path_string) if last_node.nil?
|
72
72
|
path_string << this_node_name.to_s
|
@@ -75,64 +75,64 @@ module Theatre
|
|
75
75
|
raise NamespaceNotFound.new("/#{paths.join('/')}") unless found_namespace
|
76
76
|
found_namespace
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
##
|
80
80
|
# Registers the given callback at a namespace, assuming the namespace was already registered.
|
81
81
|
#
|
82
82
|
# @param [Array] paths Must be an Array of segments
|
83
|
-
# @param [Proc] callback
|
83
|
+
# @param [Proc] callback
|
84
84
|
# @raise NamespaceNotFound if a segment has not been registered yet
|
85
85
|
#
|
86
86
|
def register_callback_at_namespace(paths, callback)
|
87
87
|
raise ArgumentError, "callback must be a Proc" unless callback.kind_of? Proc
|
88
88
|
search_for_namespace(paths).register_callback callback
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
protected
|
92
|
-
|
92
|
+
|
93
93
|
##
|
94
|
-
# Used by NamespaceManager to build a tree of namespaces. Has a Hash of children which is not
|
94
|
+
# Used by NamespaceManager to build a tree of namespaces. Has a Hash of children which is not
|
95
95
|
# Thread-safe. For Thread-safety, all access should semaphore through the NamespaceManager.
|
96
96
|
class NamespaceNode
|
97
|
-
|
97
|
+
|
98
98
|
attr_reader :name
|
99
99
|
def initialize(name)
|
100
100
|
@name = name.freeze
|
101
101
|
@children = {}
|
102
102
|
@callbacks = []
|
103
103
|
end
|
104
|
-
|
104
|
+
|
105
105
|
def register_namespace_name(name)
|
106
106
|
@children[name] ||= NamespaceNode.new(name)
|
107
107
|
end
|
108
|
-
|
108
|
+
|
109
109
|
def register_callback(callback)
|
110
110
|
@callbacks << callback
|
111
111
|
callback
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
def callbacks
|
115
115
|
@callbacks.clone
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
def delete_callback(callback)
|
119
119
|
@callbacks.delete callback
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
def child_named(name)
|
123
123
|
@children[name]
|
124
124
|
end
|
125
|
-
|
125
|
+
|
126
126
|
def destroy_namespace(name)
|
127
127
|
@children.delete name
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
def root?
|
131
131
|
false
|
132
132
|
end
|
133
|
-
|
133
|
+
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
class RootNamespaceNode < NamespaceNode
|
137
137
|
def initialize
|
138
138
|
super :ROOT
|
@@ -141,13 +141,13 @@ module Theatre
|
|
141
141
|
true
|
142
142
|
end
|
143
143
|
end
|
144
|
-
|
144
|
+
|
145
145
|
end
|
146
|
-
|
146
|
+
|
147
147
|
class NamespaceNotFound < Exception
|
148
148
|
def initialize(full_path)
|
149
149
|
super "Could not find #{full_path.inspect} in the namespace registry. Did you register it yet?"
|
150
150
|
end
|
151
151
|
end
|
152
|
-
|
152
|
+
|
153
153
|
end
|