adhearsion 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. data/CHANGELOG +9 -42
  2. data/EVENTS +11 -0
  3. data/README.txt +5 -0
  4. data/Rakefile +94 -84
  5. data/adhearsion.gemspec +148 -0
  6. data/app_generators/ahn/USAGE +5 -0
  7. data/app_generators/ahn/ahn_generator.rb +87 -0
  8. data/app_generators/ahn/templates/.ahnrc +34 -0
  9. data/app_generators/ahn/templates/README +8 -0
  10. data/app_generators/ahn/templates/Rakefile +23 -0
  11. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  12. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  13. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  14. data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
  15. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  16. data/app_generators/ahn/templates/components/restful_rpc/README.markdown +11 -0
  17. data/app_generators/ahn/templates/components/restful_rpc/config.yml +34 -0
  18. data/app_generators/ahn/templates/components/restful_rpc/example-client.rb +48 -0
  19. data/app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb +87 -0
  20. data/app_generators/ahn/templates/components/restful_rpc/spec/restful_rpc_spec.rb +263 -0
  21. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  22. data/app_generators/ahn/templates/config/startup.rb +50 -0
  23. data/app_generators/ahn/templates/dialplan.rb +3 -0
  24. data/app_generators/ahn/templates/events.rb +32 -0
  25. data/bin/ahn +28 -0
  26. data/bin/ahnctl +68 -0
  27. data/bin/jahn +42 -0
  28. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  29. data/lib/adhearsion.rb +35 -953
  30. data/lib/adhearsion/cli.rb +223 -0
  31. data/lib/adhearsion/component_manager.rb +208 -0
  32. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  33. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  34. data/lib/adhearsion/events_support.rb +84 -0
  35. data/lib/adhearsion/foundation/all.rb +9 -0
  36. data/lib/adhearsion/foundation/blank_slate.rb +5 -0
  37. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  38. data/lib/adhearsion/foundation/event_socket.rb +203 -0
  39. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  40. data/lib/adhearsion/foundation/global.rb +1 -0
  41. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  42. data/lib/adhearsion/foundation/numeric.rb +13 -0
  43. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  44. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  45. data/lib/adhearsion/foundation/string.rb +26 -0
  46. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  47. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  48. data/lib/adhearsion/host_definitions.rb +67 -0
  49. data/lib/adhearsion/initializer.rb +373 -0
  50. data/lib/adhearsion/initializer/asterisk.rb +81 -0
  51. data/lib/adhearsion/initializer/configuration.rb +254 -0
  52. data/lib/adhearsion/initializer/database.rb +49 -0
  53. data/lib/adhearsion/initializer/drb.rb +31 -0
  54. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  55. data/lib/adhearsion/initializer/rails.rb +40 -0
  56. data/lib/adhearsion/logging.rb +92 -0
  57. data/lib/adhearsion/tasks.rb +16 -0
  58. data/lib/adhearsion/tasks/database.rb +5 -0
  59. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  60. data/lib/adhearsion/tasks/generating.rb +20 -0
  61. data/lib/adhearsion/tasks/lint.rb +4 -0
  62. data/lib/adhearsion/tasks/testing.rb +37 -0
  63. data/lib/adhearsion/version.rb +9 -0
  64. data/lib/adhearsion/voip/asterisk.rb +4 -0
  65. data/lib/adhearsion/voip/asterisk/agi_server.rb +81 -0
  66. data/lib/adhearsion/voip/asterisk/commands.rb +1284 -0
  67. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  68. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  69. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  70. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  71. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  72. data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
  73. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
  74. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
  75. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  76. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  77. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  78. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  79. data/lib/adhearsion/voip/call.rb +440 -0
  80. data/lib/adhearsion/voip/call_routing.rb +64 -0
  81. data/lib/adhearsion/voip/commands.rb +9 -0
  82. data/lib/adhearsion/voip/constants.rb +39 -0
  83. data/lib/adhearsion/voip/conveniences.rb +18 -0
  84. data/lib/adhearsion/voip/dial_plan.rb +218 -0
  85. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  86. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  87. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  88. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  89. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +71 -0
  90. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  91. data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
  92. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  93. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  94. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  95. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  96. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  97. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  98. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  99. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  100. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  101. data/lib/theatre.rb +151 -0
  102. data/lib/theatre/README.markdown +64 -0
  103. data/lib/theatre/callback_definition_loader.rb +84 -0
  104. data/lib/theatre/guid.rb +23 -0
  105. data/lib/theatre/invocation.rb +121 -0
  106. data/lib/theatre/namespace_manager.rb +153 -0
  107. data/lib/theatre/version.rb +2 -0
  108. metadata +160 -140
  109. data/.version +0 -1
  110. data/TODO +0 -71
  111. data/ahn +0 -223
  112. data/apps/default/Rakefile +0 -65
  113. data/apps/default/config/adhearsion.sqlite3 +0 -0
  114. data/apps/default/config/adhearsion.yml +0 -95
  115. data/apps/default/config/database.rb +0 -50
  116. data/apps/default/config/database.yml +0 -12
  117. data/apps/default/config/helpers/drb_server.yml +0 -43
  118. data/apps/default/config/helpers/factorial.alien.c.yml +0 -1
  119. data/apps/default/config/helpers/growler.yml +0 -21
  120. data/apps/default/config/helpers/lookup.yml +0 -1
  121. data/apps/default/config/helpers/manager_proxy.yml +0 -8
  122. data/apps/default/config/helpers/micromenus.yml +0 -1
  123. data/apps/default/config/helpers/micromenus/collab.rb +0 -60
  124. data/apps/default/config/helpers/micromenus/images/arrow-off.gif +0 -0
  125. data/apps/default/config/helpers/micromenus/images/arrow-on.gif +0 -0
  126. data/apps/default/config/helpers/micromenus/images/error.gif +0 -0
  127. data/apps/default/config/helpers/micromenus/images/folder-off.gif +0 -0
  128. data/apps/default/config/helpers/micromenus/images/folder-on.gif +0 -0
  129. data/apps/default/config/helpers/micromenus/images/folder.png +0 -0
  130. data/apps/default/config/helpers/micromenus/images/ggbridge.jpg +0 -0
  131. data/apps/default/config/helpers/micromenus/images/green.png +0 -0
  132. data/apps/default/config/helpers/micromenus/images/microbrowser.bg.gif +0 -0
  133. data/apps/default/config/helpers/micromenus/images/red.png +0 -0
  134. data/apps/default/config/helpers/micromenus/images/tux.bmp +0 -0
  135. data/apps/default/config/helpers/micromenus/images/url-off.gif +0 -0
  136. data/apps/default/config/helpers/micromenus/images/url-on.gif +0 -0
  137. data/apps/default/config/helpers/micromenus/images/yellow.png +0 -0
  138. data/apps/default/config/helpers/micromenus/javascripts/animation.js +0 -1341
  139. data/apps/default/config/helpers/micromenus/javascripts/carousel.js +0 -1238
  140. data/apps/default/config/helpers/micromenus/javascripts/columnav.js +0 -306
  141. data/apps/default/config/helpers/micromenus/javascripts/connection.js +0 -965
  142. data/apps/default/config/helpers/micromenus/javascripts/container.js +0 -4727
  143. data/apps/default/config/helpers/micromenus/javascripts/container_core.js +0 -2915
  144. data/apps/default/config/helpers/micromenus/javascripts/dom.js +0 -892
  145. data/apps/default/config/helpers/micromenus/javascripts/dragdrop.js +0 -2958
  146. data/apps/default/config/helpers/micromenus/javascripts/event.js +0 -1771
  147. data/apps/default/config/helpers/micromenus/javascripts/yahoo.js +0 -433
  148. data/apps/default/config/helpers/micromenus/stylesheets/carousel.css +0 -78
  149. data/apps/default/config/helpers/micromenus/stylesheets/columnav.css +0 -135
  150. data/apps/default/config/helpers/micromenus/stylesheets/microbrowsers.css +0 -42
  151. data/apps/default/config/helpers/multi_messenger.yml +0 -9
  152. data/apps/default/config/helpers/weather.yml +0 -1
  153. data/apps/default/config/helpers/xbmc.yml +0 -2
  154. data/apps/default/config/migration.rb +0 -59
  155. data/apps/default/extensions.rb +0 -41
  156. data/apps/default/helpers/factorial.alien.c +0 -32
  157. data/apps/default/helpers/growler.rb +0 -53
  158. data/apps/default/helpers/lookup.rb +0 -44
  159. data/apps/default/helpers/manager_proxy.rb +0 -112
  160. data/apps/default/helpers/micromenus.rb +0 -514
  161. data/apps/default/helpers/multi_messenger.rb +0 -53
  162. data/apps/default/helpers/oscar_wilde_quotes.rb +0 -197
  163. data/apps/default/helpers/weather.rb +0 -85
  164. data/apps/default/helpers/xbmc.rb +0 -39
  165. data/apps/default/logs/adhearsion.log +0 -0
  166. data/apps/default/logs/database.log +0 -0
  167. data/lib/constants.rb +0 -24
  168. data/lib/core_extensions.rb +0 -180
  169. data/lib/drb_server.rb +0 -101
  170. data/lib/logging.rb +0 -85
  171. data/lib/phone_number.rb +0 -85
  172. data/lib/rami.rb +0 -823
  173. data/lib/servlet_container.rb +0 -174
  174. data/lib/sexy_migrations.rb +0 -70
  175. data/test/asterisk_module_test.rb +0 -14
  176. data/test/core_extensions_test.rb +0 -26
  177. data/test/dial_test.rb +0 -43
  178. data/test/specs/numerical_string_spec.rb +0 -53
  179. data/test/test_micromenus.rb +0 -0
@@ -0,0 +1,10 @@
1
+ module PseudoGuidGenerator
2
+ ##
3
+ # Generates a new 128-bit Globally Unique Identifier. It is a "pseudo" in that it does not adhere to the RFC which mandates
4
+ # that a certain section be reserved for a fragment of the NIC MAC address.
5
+ def new_guid(separator="-")
6
+ [8,4,4,4,12].map { |segment_length| String.random(segment_length) }.join(separator)
7
+ end
8
+ end
9
+
10
+ include PseudoGuidGenerator
@@ -0,0 +1,42 @@
1
+ class Module
2
+
3
+ ##
4
+ # In OOP, relationships between classes should be treated as *properties* of those classes. Often, in a complex OO
5
+ # architecture, you'll end up with many relationships that intermingle in monolithic ways, blunting the effectiveness of
6
+ # subclassing.
7
+ #
8
+ # For example, say you have an Automobile class which, in its constructor, instantiates a new Battery class and performs
9
+ # some operations on it such as calling an install() method. Let's also assume the Automobile class exposes a repair()
10
+ # method which uses a class-level method of Battery to diagnose your own instance of Battery. If the result of the
11
+ # diagnosis shows that the Battery is bad, the Automobile will instantiate a new Battery object and replace the old battery
12
+ # with the new one.
13
+ #
14
+ # Now, what if you wish to create a new Automobile derived from existing technology: a HybridAutomobile subclass. For this
15
+ # particular HybridAutomobile class, let's simply say the only difference between it and its parent is which kind of
16
+ # Battery it uses -- it requires its own special subclass of Battery. With Automobile's current implementation, its
17
+ # references to which Battery it instantiates and uses are embedded in the immutable method defintions. This
18
+ # HybridAutomobile needs to override which Battery its superclass' methods use and nothing else.
19
+ #
20
+ # For this reason, the Battery class which Automobile uses is semantically a property which others may want to override.
21
+ # In OOP theory, we define overridable properties in the form of methods and override those methods in the subclasses.
22
+ #
23
+ # This method exposes one method which creates human-readable semantics to defining these relationships as properties. It's
24
+ # used as follows:
25
+ #
26
+ # class Automobile
27
+ # relationship :battery => Battery
28
+ # relationship :chassis => Chassis
29
+ # # Other properties and instance methods here....
30
+ # end
31
+ #
32
+ # class HybridAutomobile < Automobile
33
+ # relationship :battery => HybridBattery
34
+ # end
35
+ #
36
+ def relationships(relationship_mapping)
37
+ relationship_mapping.each_pair do |class_name, class_object|
38
+ define_method(class_name) { class_object }
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,26 @@
1
+ class String
2
+
3
+ def unindent
4
+ gsub(/^\s*/,'')
5
+ end
6
+
7
+ def unindent!
8
+ gsub!(/^\s*/,'')
9
+ end
10
+
11
+ def self.random_char
12
+ case random_digit = rand(62)
13
+ when 0...10 : random_digit.to_s
14
+ when 10...36 : (random_digit + 55).chr
15
+ when 36...62 : (random_digit + 61).chr
16
+ end
17
+ end
18
+
19
+ def self.random(length_of_string=8)
20
+ Array.new(length_of_string) { random_char }.join
21
+ end
22
+
23
+ def nameify() downcase.gsub(/[^\w]/, '') end
24
+ def nameify!() replace nameify end
25
+
26
+ end
@@ -0,0 +1,96 @@
1
+ ##
2
+ # Implementation of a Thread-safe Hash. Works by delegating methods to a Hash behind-the-scenes after obtaining an exclusive # lock. Use exactly as you would a normal Hash.
3
+ #
4
+ class SynchronizedHash
5
+
6
+ def self.atomically_delegate(method_name)
7
+ class_eval(<<-RUBY, __FILE__, __LINE__)
8
+ def #{method_name}(*args, &block)
9
+ @lock.synchronize do
10
+ @delegate.send(#{method_name.inspect}, *args, &block)
11
+ end
12
+ end
13
+ RUBY
14
+ end
15
+
16
+ # Hash-related methods
17
+
18
+ atomically_delegate :[]
19
+ atomically_delegate :[]=
20
+ atomically_delegate :all?
21
+ atomically_delegate :any?
22
+ atomically_delegate :clear
23
+ atomically_delegate :collect
24
+ atomically_delegate :default
25
+ atomically_delegate :default=
26
+ atomically_delegate :delete
27
+ atomically_delegate :delete_if
28
+ atomically_delegate :detect
29
+ atomically_delegate :each
30
+ atomically_delegate :each_key
31
+ atomically_delegate :each_pair
32
+ atomically_delegate :each_value
33
+ atomically_delegate :each_with_index
34
+ atomically_delegate :empty?
35
+ atomically_delegate :entries
36
+ atomically_delegate :fetch
37
+ atomically_delegate :find
38
+ atomically_delegate :find_all
39
+ atomically_delegate :grep
40
+ atomically_delegate :has_key?
41
+ atomically_delegate :has_value?
42
+ atomically_delegate :include?
43
+ atomically_delegate :index
44
+ atomically_delegate :indexes
45
+ atomically_delegate :indices
46
+ atomically_delegate :inject
47
+ atomically_delegate :invert
48
+ atomically_delegate :key?
49
+ atomically_delegate :keys
50
+ atomically_delegate :length
51
+ atomically_delegate :map
52
+ atomically_delegate :max
53
+ atomically_delegate :member?
54
+ atomically_delegate :merge
55
+ atomically_delegate :merge!
56
+ atomically_delegate :min
57
+ atomically_delegate :partition
58
+ atomically_delegate :rehash
59
+ atomically_delegate :reject
60
+ atomically_delegate :reject!
61
+ atomically_delegate :replace
62
+ atomically_delegate :select
63
+ atomically_delegate :shift
64
+ atomically_delegate :size
65
+ atomically_delegate :sort
66
+ atomically_delegate :sort_by
67
+ atomically_delegate :store
68
+ atomically_delegate :to_hash
69
+ atomically_delegate :update
70
+ atomically_delegate :value?
71
+ atomically_delegate :values
72
+ atomically_delegate :values_at
73
+ atomically_delegate :zip
74
+
75
+ # Object-related methods
76
+
77
+ atomically_delegate :inspect
78
+ atomically_delegate :to_s
79
+ atomically_delegate :marshal_dump
80
+
81
+ def initialize(*args, &block)
82
+ @delegate = Hash.new(*args, &block)
83
+ @lock = Mutex.new
84
+ end
85
+
86
+ ##
87
+ # If you need to do many operations atomically (a la transaction), you can call this method and access the yielded Hash
88
+ # which can be safely modified for the duration of your block.
89
+ #
90
+ # @yield [Hash] the Hash on which you can safely operate during your block.
91
+ #
92
+ def with_lock(&block)
93
+ @lock.synchronize { yield @delegate }
94
+ end
95
+
96
+ end
@@ -0,0 +1,7 @@
1
+ require 'thread'
2
+ class Object
3
+ def synchronize(&block)
4
+ @mutex ||= Mutex.new
5
+ @mutex.synchronize &block
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ require 'yaml'
2
+
3
+ module Adhearsion
4
+
5
+ ##
6
+ # This class isn't yet tied into Adhearsion.
7
+ #
8
+ class HostDefinition
9
+
10
+ SUPPORTED_KEYS = [:host, :username, :password, :key, :name]
11
+
12
+ cattr_reader :definitions
13
+ @@definitions ||= []
14
+
15
+ class << self
16
+ def import_from_data_structure(local_definitions)
17
+ case local_definitions
18
+ when Array
19
+ local_definitions.each do |definition|
20
+ raise HostDefinitionException, "Unrecognized definition: #{definition}" unless definition.is_a?(Hash)
21
+ end
22
+ local_definitions.map { |definition| new definition }
23
+ when Hash
24
+ local_definitions.map do |(name,definition)|
25
+ new definition.merge(:name => name)
26
+ end
27
+ else
28
+ raise HostDefinitionException, "Unrecognized definition #{local_definitions}"
29
+ end
30
+ end
31
+
32
+ def import_from_yaml(yaml_string)
33
+ import_from_data_structure YAML.load(yaml_string)
34
+ end
35
+
36
+ def import_from_yaml_file(file)
37
+ import_from_yaml YAML.load_file(file)
38
+ end
39
+
40
+ def clear_definitions!
41
+ definitions.clear
42
+ end
43
+ end
44
+
45
+ attr_reader :name, :host, :username, :password, :key
46
+ def initialize(hash)
47
+ @host, @username, @password, @key, @name = hash.values_at(*SUPPORTED_KEYS)
48
+ @name ||= new_guid
49
+
50
+ unrecognized_keys = hash.keys - SUPPORTED_KEYS
51
+ raise HostDefinitionException, "Unrecognized key(s): #{unrecognized_keys.map(&:inspect).to_sentence}" if unrecognized_keys.any?
52
+ raise HostDefinitionException, "You must supply a password or key!" if username && !(password || key)
53
+ raise HostDefinitionException, "You must supply a username!" unless username
54
+ raise HostDefinitionException, 'You cannot supply both a password and key!' if password && key
55
+ raise HostDefinitionException, 'You must supply a host!' unless host
56
+
57
+ self.class.definitions << self
58
+ end
59
+
60
+ class HostDefinitionException < Exception
61
+
62
+ end
63
+
64
+ end
65
+
66
+
67
+ end
@@ -0,0 +1,373 @@
1
+ module Adhearsion
2
+
3
+ class << self
4
+
5
+ ##
6
+ # Shuts down the framework.
7
+ #
8
+ def self.shutdown!
9
+ ahn_log "Shutting down gracefully at #{Time.now}."
10
+ Events.stop!
11
+ exit
12
+ end
13
+
14
+ end
15
+ class PathString < String
16
+
17
+ class << self
18
+
19
+ ##
20
+ # Will return a PathString for the application root folder to which the specified arbitrarily nested subfolder belongs.
21
+ # It works by traversing parent directories looking for the .ahnrc file. If no .ahnrc is found, nil is returned.
22
+ #
23
+ # @param [String] folder The path to the directory which should be a
24
+ # @return [nil] if the subdirectory does not belong to a parent Adhearsion app directory
25
+ # @return [PathString] if a directory is found
26
+ #
27
+ def from_application_subdirectory(folder)
28
+ folder = File.expand_path folder
29
+ ahn_rc = nil
30
+
31
+ until ahn_rc || folder == "/"
32
+ possible_ahn_rc = File.join(folder, ".ahnrc")
33
+ if File.exists?(possible_ahn_rc)
34
+ ahn_rc = possible_ahn_rc
35
+ else
36
+ folder = File.expand_path(folder + "/..")
37
+ end
38
+ end
39
+ ahn_rc ? new(folder) : nil
40
+ end
41
+ end
42
+
43
+ attr_accessor :component_path, :dialplan_path, :log_path
44
+
45
+ def initialize(path)
46
+ super
47
+ defaults
48
+ end
49
+
50
+ def defaults
51
+ @component_path = build_path_for "components"
52
+ @dialplan_path = dup
53
+ @log_path = build_path_for "logs"
54
+ end
55
+
56
+ def base_path=(value)
57
+ replace(value)
58
+ defaults
59
+ end
60
+
61
+ def using_base_path(temporary_base_path, &block)
62
+ original_path = dup
63
+ self.base_path = temporary_base_path
64
+ block.call
65
+ ensure
66
+ self.base_path = original_path
67
+ end
68
+
69
+ private
70
+ def build_path_for(path)
71
+ File.join(to_s, path)
72
+ end
73
+ end
74
+
75
+ class Initializer
76
+
77
+ class << self
78
+ def get_rules_from(location)
79
+ location = File.join location, ".ahnrc" if File.directory? location
80
+ File.exists?(location) ? YAML.load_file(location) : nil
81
+ end
82
+
83
+ def ahn_root=(path)
84
+ if Object.constants.include?("AHN_ROOT")
85
+ Object.const_get(:AHN_ROOT).base_path = File.expand_path(path)
86
+ else
87
+ Object.const_set(:AHN_ROOT, PathString.new(File.expand_path(path)))
88
+ end
89
+ end
90
+
91
+ def start(*args, &block)
92
+ new(*args, &block).start
93
+ end
94
+
95
+ def start_from_init_file(file, ahn_app_path)
96
+ return if defined?(@@started) && @@started
97
+ start ahn_app_path, :loaded_init_files => file
98
+ end
99
+
100
+ end
101
+
102
+ attr_reader :path, :daemon, :pid_file, :log_file, :ahn_app_log_directory
103
+
104
+ # Creation of pid_files
105
+ #
106
+ # - You may want to have Adhearsion create a process identification
107
+ # file when it boots so that a crash monitoring program such as
108
+ # Monit can reboot if necessary or so the init script can kill it
109
+ # for system shutdowns.
110
+ # - To have Adhearsion create a pid file in the default location (i.e.
111
+ # AHN_INSTALL_DIR/adhearsion.pid), supply :pid_file with 'true'. Otherwise
112
+ # one is not created UNLESS it is running in daemon mode, in which
113
+ # case one is created. You can force Adhearsion to not create one
114
+ # even in daemon mode by supplying "false".
115
+ def initialize(path=nil, options={})
116
+ @@started = true
117
+ @path = path
118
+ @daemon = options[:daemon]
119
+ @pid_file = options[:pid_file].nil? ? ENV['PID_FILE'] : options[:pid_file]
120
+ @loaded_init_files = options[:loaded_init_files]
121
+ end
122
+
123
+ def start
124
+ self.class.ahn_root = path
125
+
126
+ resolve_pid_file_path
127
+ resolve_log_file_path
128
+ switch_to_root_directory
129
+ catch_termination_signal
130
+ bootstrap_rc
131
+ daemonize! if should_daemonize?
132
+ initialize_log_file
133
+ load_all_init_files
134
+ init_components_subsystem
135
+ init_modules
136
+ init_events_subsystem
137
+ create_pid_file if pid_file
138
+ load_components
139
+ init_events_file
140
+
141
+ ahn_log "Adhearsion initialized!"
142
+
143
+ trigger_after_initialized_hooks
144
+ join_important_threads
145
+
146
+ self
147
+ end
148
+
149
+ def default_pid_path
150
+ File.join AHN_ROOT, 'adhearsion.pid'
151
+ end
152
+
153
+ def resolve_pid_file_path
154
+ @pid_file = if pid_file.equal?(true) then default_pid_path
155
+ elsif pid_file then pid_file
156
+ elsif pid_file.equal?(false) then nil
157
+ # FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
158
+ else @pid_file = @daemon ? default_pid_path : nil
159
+ end
160
+ end
161
+
162
+ def resolve_log_file_path
163
+ @ahn_app_log_directory = AHN_ROOT + '/log'
164
+ @log_file = File.expand_path(ahn_app_log_directory + "/adhearsion.log")
165
+ end
166
+
167
+ def switch_to_root_directory
168
+ Dir.chdir AHN_ROOT
169
+ end
170
+
171
+ def catch_termination_signal
172
+ %w'INT TERM'.each do |process_signal|
173
+ trap process_signal do
174
+ ahn_log "Shutting down gracefully at #{Time.now}."
175
+ Events.trigger :shutdown
176
+ exit
177
+ end
178
+ end
179
+ end
180
+
181
+ ##
182
+ # This step in the initialization process loads the .ahnrc in the given app folder. With the information in .ahnrc, we
183
+ # can continue the initialization knowing where certain files are specifically.
184
+ #
185
+ def bootstrap_rc
186
+ rules = self.class.get_rules_from AHN_ROOT
187
+
188
+ AHN_CONFIG.ahnrc = rules
189
+
190
+ # DEPRECATION: Check if the old paths format is being used. If so, abort and notify.
191
+ if rules.has_key?("paths") && rules["paths"].kind_of?(Hash)
192
+ paths = rules["paths"].each_pair do |key,value|
193
+ if value.kind_of?(Hash)
194
+ if value.has_key?("directory") || value.has_key?("pattern")
195
+ puts
196
+ puts *caller
197
+ puts
198
+
199
+ abort <<-WARNING
200
+ Deprecation Warning
201
+ -------------------
202
+ The (hidden) .ahnrc file in this app is of an older format and needs to be fixed.
203
+
204
+ There is a rake task to automatically fix it or you can do it manually. Note: it's
205
+ best if you do it manually so you can retain the YAML comments in your .ahnrc file.
206
+
207
+ The rake task is called "deprecations:fix_ahnrc_path_format".
208
+
209
+ To do it manually, find all entries in the "paths" section of your ".ahnrc" file
210
+ which look like the following:
211
+
212
+ paths:
213
+ key_name_could_be_anything:
214
+ directory: some_folder
215
+ pattern: *.rb
216
+
217
+ Note: the "models" section had this syntax before:
218
+
219
+ models:
220
+ directory: models
221
+ pattern: "*.rb"
222
+
223
+ The NEW syntax is as follows (using models as an example):
224
+
225
+ models: models/*.rb
226
+
227
+ This new format is much cleaner.
228
+
229
+ Adhearsion will abort until you fix this. Sorry for the incovenience.
230
+ WARNING
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ gems = rules['gems']
237
+ if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
238
+ gems.each_pair do |gem_name,properties_hash|
239
+ if properties_hash && properties_hash["version"]
240
+ gem gem_name, properties_hash["version"]
241
+ else
242
+ gem gem_name
243
+ end
244
+ if properties_hash
245
+ case properties_hash["require"]
246
+ when Array
247
+ properties_hash["require"].each { |lib| require lib }
248
+ when String
249
+ require properties_hash["require"]
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
255
+
256
+ def load_all_init_files
257
+ init_files_from_rc = AHN_CONFIG.files_from_setting("paths", "init").map { |file| File.expand_path(file) }
258
+ already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
259
+ (init_files_from_rc - already_loaded_init_files).each { |init| load init }
260
+ end
261
+
262
+ def init_modules
263
+ require 'adhearsion/initializer/database.rb'
264
+ require 'adhearsion/initializer/asterisk.rb'
265
+ require 'adhearsion/initializer/drb.rb'
266
+ require 'adhearsion/initializer/rails.rb'
267
+ # require 'adhearsion/initializer/freeswitch.rb'
268
+
269
+ DatabaseInitializer.start if AHN_CONFIG.database_enabled?
270
+ AsteriskInitializer.start if AHN_CONFIG.asterisk_enabled?
271
+ DrbInitializer.start if AHN_CONFIG.drb_enabled?
272
+ RailsInitializer.start if AHN_CONFIG.rails_enabled?
273
+ # FreeswitchInitializer.start if AHN_CONFIG.freeswitch_enabled?
274
+ end
275
+
276
+ def init_events_subsystem
277
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
278
+ if application_events_files.any?
279
+ Events.register_callback(:shutdown) do
280
+ ahn_log.events "Performing a graceful stop of events subsystem"
281
+ Events.framework_theatre.graceful_stop!
282
+ end
283
+ Events.framework_theatre.start!
284
+ else
285
+ ahn_log.events.warn 'No entries in the "events" section of .ahnrc. Skipping its initialization.'
286
+ end
287
+ end
288
+
289
+ def init_events_file
290
+ application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
291
+ application_events_files.each do |file|
292
+ Events.framework_theatre.load_events_file file
293
+ end
294
+ end
295
+
296
+ def should_daemonize?
297
+ @daemon || ENV['DAEMON']
298
+ end
299
+
300
+ def daemonize!
301
+ ahn_log "Daemonizing now! Creating #{pid_file}."
302
+ extend Adhearsion::CustomDaemonizer
303
+ daemonize log_file
304
+ end
305
+
306
+ def initialize_log_file
307
+ Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
308
+ file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
309
+
310
+ if should_daemonize?
311
+ Logging::AdhearsionLogger.outputters = file_logger
312
+ else
313
+ Logging::AdhearsionLogger.outputters << file_logger
314
+ end
315
+ Logging::DefaultAdhearsionLogger.redefine_outputters
316
+ end
317
+
318
+ def create_pid_file(file = pid_file)
319
+ if file
320
+ File.open pid_file, 'w' do |file|
321
+ file.puts Process.pid
322
+ end
323
+
324
+ Events.register_callback :shutdown do
325
+ File.delete(pid_file) if File.exists?(pid_file)
326
+ end
327
+ end
328
+ end
329
+
330
+ def init_components_subsystem
331
+ @components_directory = File.expand_path "components"
332
+ if File.directory? @components_directory
333
+ Components.component_manager = Components::ComponentManager.new @components_directory
334
+ Kernel.send(:const_set, :COMPONENTS, Components.component_manager.lazy_config_loader)
335
+ Components.component_manager.globalize_global_scope!
336
+ Components.component_manager.extend_object_with(Theatre::CallbackDefinitionLoader, :events)
337
+ else
338
+ ahn_log.warn "No components directory found. Not initializing any components."
339
+ end
340
+ end
341
+
342
+ def load_components
343
+ if Components.component_manager
344
+ Components.component_manager.load_components
345
+ end
346
+ end
347
+
348
+ def trigger_after_initialized_hooks
349
+ Events.trigger_immediately :after_initialized
350
+ end
351
+
352
+ ##
353
+ # This method will block Thread.main() until calling join() has returned for all Threads in IMPORTANT_THREADS.
354
+ # Note: IMPORTANT_THREADS won't always contain Thread instances. It simply requires the objects respond to join().
355
+ #
356
+ def join_important_threads
357
+ # Note: we're using this ugly accumulator to ensure that all threads have ended since IMPORTANT_THREADS will almost
358
+ # certainly change sizes after this method is called.
359
+ index = 0
360
+ until index == IMPORTANT_THREADS.size
361
+ begin
362
+ IMPORTANT_THREADS[index].join
363
+ rescue => e
364
+ ahn_log.error "Error after join()ing Thread #{thread.inspect}. #{e.message}"
365
+ ensure
366
+ index = index + 1
367
+ end
368
+ end
369
+ end
370
+
371
+ class InitializationFailedError < Exception; end
372
+ end
373
+ end