adhearsion-cw 1.0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/CHANGELOG +109 -0
  2. data/EVENTS +11 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +456 -0
  5. data/Rakefile +134 -0
  6. data/adhearsion.gemspec +174 -0
  7. data/app_generators/ahn/USAGE +5 -0
  8. data/app_generators/ahn/ahn_generator.rb +97 -0
  9. data/app_generators/ahn/templates/.ahnrc +34 -0
  10. data/app_generators/ahn/templates/Gemfile +7 -0
  11. data/app_generators/ahn/templates/README +8 -0
  12. data/app_generators/ahn/templates/Rakefile +27 -0
  13. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  14. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  15. data/app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown +11 -0
  16. data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +48 -0
  17. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +91 -0
  18. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml +34 -0
  19. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +251 -0
  20. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +104 -0
  21. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml +2 -0
  22. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  23. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  24. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml +12 -0
  25. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/README.markdown +3 -0
  26. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +11 -0
  27. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  28. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  29. data/app_generators/ahn/templates/config/startup.rb +74 -0
  30. data/app_generators/ahn/templates/dialplan.rb +3 -0
  31. data/app_generators/ahn/templates/events.rb +32 -0
  32. data/bin/ahn +29 -0
  33. data/bin/ahnctl +68 -0
  34. data/bin/jahn +43 -0
  35. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  36. data/lib/adhearsion/cli.rb +296 -0
  37. data/lib/adhearsion/component_manager/component_tester.rb +53 -0
  38. data/lib/adhearsion/component_manager/spec_framework.rb +18 -0
  39. data/lib/adhearsion/component_manager.rb +272 -0
  40. data/lib/adhearsion/events_support.rb +84 -0
  41. data/lib/adhearsion/foundation/all.rb +15 -0
  42. data/lib/adhearsion/foundation/blank_slate.rb +3 -0
  43. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  44. data/lib/adhearsion/foundation/event_socket.rb +205 -0
  45. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  46. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  47. data/lib/adhearsion/foundation/numeric.rb +13 -0
  48. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  49. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  50. data/lib/adhearsion/foundation/string.rb +26 -0
  51. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  52. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  53. data/lib/adhearsion/host_definitions.rb +67 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +87 -0
  55. data/lib/adhearsion/initializer/configuration.rb +321 -0
  56. data/lib/adhearsion/initializer/database.rb +60 -0
  57. data/lib/adhearsion/initializer/drb.rb +31 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/ldap.rb +57 -0
  60. data/lib/adhearsion/initializer/rails.rb +41 -0
  61. data/lib/adhearsion/initializer/xmpp.rb +42 -0
  62. data/lib/adhearsion/initializer.rb +394 -0
  63. data/lib/adhearsion/logging.rb +92 -0
  64. data/lib/adhearsion/tasks/components.rb +32 -0
  65. data/lib/adhearsion/tasks/database.rb +5 -0
  66. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  67. data/lib/adhearsion/tasks/generating.rb +20 -0
  68. data/lib/adhearsion/tasks/lint.rb +4 -0
  69. data/lib/adhearsion/tasks/testing.rb +37 -0
  70. data/lib/adhearsion/tasks.rb +17 -0
  71. data/lib/adhearsion/version.rb +35 -0
  72. data/lib/adhearsion/voip/asterisk/agi_server.rb +115 -0
  73. data/lib/adhearsion/voip/asterisk/commands.rb +1581 -0
  74. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  75. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +102 -0
  76. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  77. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  78. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  79. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1681 -0
  80. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +341 -0
  81. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  82. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  83. data/lib/adhearsion/voip/asterisk/manager_interface.rb +705 -0
  84. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  85. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  86. data/lib/adhearsion/voip/asterisk.rb +4 -0
  87. data/lib/adhearsion/voip/call.rb +498 -0
  88. data/lib/adhearsion/voip/call_routing.rb +64 -0
  89. data/lib/adhearsion/voip/commands.rb +9 -0
  90. data/lib/adhearsion/voip/constants.rb +39 -0
  91. data/lib/adhearsion/voip/conveniences.rb +18 -0
  92. data/lib/adhearsion/voip/dial_plan.rb +250 -0
  93. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  94. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  96. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  97. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +69 -0
  98. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  99. data/lib/adhearsion/voip/dsl/numerical_string.rb +128 -0
  100. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  101. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  102. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  103. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  104. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  105. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  106. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  107. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +57 -0
  108. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  109. data/lib/adhearsion/xmpp/connection.rb +61 -0
  110. data/lib/adhearsion.rb +46 -0
  111. data/lib/theatre/README.markdown +64 -0
  112. data/lib/theatre/callback_definition_loader.rb +84 -0
  113. data/lib/theatre/guid.rb +23 -0
  114. data/lib/theatre/invocation.rb +121 -0
  115. data/lib/theatre/namespace_manager.rb +153 -0
  116. data/lib/theatre/version.rb +2 -0
  117. data/lib/theatre.rb +151 -0
  118. metadata +323 -0
@@ -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 then random_digit.to_s
14
+ when 10...36 then (random_digit + 55).chr
15
+ when 36...62 then (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 < StandardError
61
+
62
+ end
63
+
64
+ end
65
+
66
+
67
+ end
@@ -0,0 +1,87 @@
1
+ require 'adhearsion/voip/asterisk'
2
+ module Adhearsion
3
+ class Initializer
4
+
5
+ class AsteriskInitializer
6
+
7
+ cattr_accessor :config, :agi_server, :ami_client
8
+ class << self
9
+
10
+ def start
11
+ self.config = AHN_CONFIG.asterisk
12
+ self.agi_server = initialize_agi
13
+ self.ami_client = VoIP::Asterisk.manager_interface = initialize_ami if config.ami_enabled?
14
+ join_server_thread_after_initialized
15
+
16
+ # Make sure we stop everything when we shutdown
17
+ Events.register_callback(:shutdown) do
18
+ ahn_log.info "Shutting down with #{Adhearsion.active_calls.size} active calls"
19
+ self.stop
20
+ end
21
+ end
22
+
23
+ def stop
24
+ agi_server.graceful_shutdown
25
+ ami_client.disconnect! if ami_client
26
+ end
27
+
28
+ private
29
+
30
+ def initialize_agi
31
+ VoIP::Asterisk::AGI::Server.new :host => config.listening_host,
32
+ :port => config.listening_port
33
+ end
34
+
35
+ def initialize_ami
36
+ options = ami_options
37
+ start_ami_after_initialized
38
+ VoIP::Asterisk::Manager::ManagerInterface.new(options).tap do
39
+ class << VoIP::Asterisk
40
+ if respond_to?(:manager_interface)
41
+ ahn_log.warn "Asterisk.manager_interface already initialized?"
42
+ else
43
+ def manager_interface
44
+ # ahn_log.ami.warn "Warning! This Asterisk.manager_interface() notation is for Adhearsion version 0.8.0 only. Subsequent versions of Adhearsion will use a feature called SuperManager. Migrating to use SuperManager will be very simple. See http://docs.adhearsion.com/AMI for more information."
45
+ Adhearsion::Initializer::AsteriskInitializer.ami_client
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def ami_options
53
+ %w(host port username password events auto_reconnect).inject({}) do |options, property|
54
+ options[property.to_sym] = config.ami.send property
55
+ options
56
+ end
57
+ end
58
+
59
+ def join_server_thread_after_initialized
60
+ Events.register_callback(:after_initialized) do
61
+ begin
62
+ agi_server.start
63
+ rescue => e
64
+ ahn_log.fatal "Failed to start AGI server! #{e.inspect}"
65
+ abort
66
+ end
67
+ end
68
+ IMPORTANT_THREADS << agi_server
69
+ end
70
+
71
+ def start_ami_after_initialized
72
+ Events.register_callback(:after_initialized) do
73
+ begin
74
+ self.ami_client.connect!
75
+ rescue Errno::ECONNREFUSED
76
+ ahn_log.ami.error "Connection refused when connecting to AMI! Please check your configuration."
77
+ rescue => e
78
+ ahn_log.ami.error "Error connecting to AMI! #{e.inspect}"
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,321 @@
1
+ module Adhearsion
2
+ class Configuration
3
+ module ConfigurationEntryPoint
4
+ def add_configuration_for(name)
5
+ configuration_class_name = "#{name}Configuration"
6
+ lowercased_name = name.to_s.underscore
7
+
8
+ class_eval(<<-EVAL, __FILE__, __LINE__)
9
+ def enable_#{lowercased_name}(configuration_options = {})
10
+ @#{lowercased_name}_configuration = #{configuration_class_name}.new(configuration_options)
11
+ end
12
+
13
+ def #{lowercased_name}
14
+ @#{lowercased_name}_configuration
15
+ end
16
+
17
+ def #{lowercased_name}_enabled?
18
+ !#{lowercased_name}.nil?
19
+ end
20
+ EVAL
21
+ end
22
+ end
23
+ extend ConfigurationEntryPoint
24
+
25
+ class << self
26
+ def configure(&block)
27
+ if Adhearsion.const_defined?(:AHN_CONFIG)
28
+ yield AHN_CONFIG if block_given?
29
+ else
30
+ Adhearsion.const_set(:AHN_CONFIG, new(&block))
31
+ end
32
+ end
33
+ end
34
+
35
+ attr_accessor :automatically_answer_incoming_calls
36
+ attr_accessor :end_call_on_hangup
37
+ attr_accessor :end_call_on_error
38
+ attr_accessor :components_to_load
39
+
40
+ def initialize
41
+ @automatically_answer_incoming_calls = true
42
+ @end_call_on_hangup = true
43
+ @end_call_on_error = true
44
+ @components_to_load = []
45
+ yield self if block_given?
46
+ end
47
+
48
+ def ahnrc
49
+ @ahnrc
50
+ end
51
+
52
+ ##
53
+ # Load the contents of an .ahnrc file into this Configuration.
54
+ #
55
+ # @param [String, Hash] ahnrc String of YAML .ahnrc data or a Hash of the pre-loaded YAML data structure
56
+ #
57
+ def ahnrc=(new_ahnrc)
58
+ case new_ahnrc
59
+ when Hash
60
+ @raw_ahnrc = new_ahnrc.to_yaml.freeze
61
+ @ahnrc = new_ahnrc.clone.freeze
62
+ when String
63
+ @raw_ahnrc = new_ahnrc.clone.freeze
64
+ @ahnrc = YAML.load(new_ahnrc).freeze
65
+ end
66
+ end
67
+
68
+ def logging(options)
69
+ Adhearsion::Logging.logging_level = options[:level]
70
+ end
71
+
72
+ def add_component(*list)
73
+ AHN_CONFIG.components_to_load |= list
74
+ end
75
+
76
+ ##
77
+ # Adhearsion's .ahnrc file is used to define paths to certain parts of the framework. For example, the name dialplan.rb
78
+ # is actually specified in .ahnrc. This file can actually be just a filename, a filename with a glob (.e.g "*.rb"), an
79
+ # Array of filenames or even an Array of globs.
80
+ #
81
+ # @param [String,Array] String segments which convey the nesting of Hash keys through .ahnrc
82
+ # @raise [RuntimeError] If ahnrc has not been set yet with #ahnrc=()
83
+ # @raise [NameError] If the path through the ahnrc is invalid
84
+ #
85
+ def files_from_setting(*path_through_config)
86
+ raise RuntimeError, "No ahnrc has been set yet!" unless @ahnrc
87
+ queried_nested_setting = path_through_config.flatten.inject(@ahnrc) do |hash,key_name|
88
+ if hash.kind_of?(Hash) && hash.has_key?(key_name)
89
+ hash[key_name]
90
+ else
91
+ raise NameError, "Paths #{path_through_config.inspect} not found in .ahnrc!"
92
+ end
93
+ end
94
+ raise NameError, "Paths #{path_through_config.inspect} not found in .ahnrc!" unless queried_nested_setting
95
+ queried_nested_setting = Array queried_nested_setting
96
+ queried_nested_setting.map { |filename| files_from_glob(filename) }.flatten.uniq
97
+ end
98
+
99
+ private
100
+
101
+ def files_from_glob(glob)
102
+ Dir.glob "#{AHN_ROOT}/#{glob}"
103
+ end
104
+
105
+ class AbstractConfiguration
106
+ extend ConfigurationEntryPoint
107
+
108
+ class << self
109
+ private
110
+ def abstract_method!
111
+ raise "Must be implemented in subclasses"
112
+ end
113
+ end
114
+
115
+ def initialize(overrides = {})
116
+ overrides.each_pair do |attribute, value|
117
+ send("#{attribute}=", value)
118
+ end
119
+ end
120
+ end
121
+
122
+ # Abstract superclass for AsteriskConfiguration and FreeSwitchConfiguration.
123
+ class TelephonyPlatformConfiguration < AbstractConfiguration
124
+ attr_accessor :listening_port
125
+ attr_accessor :listening_host
126
+
127
+ class << self
128
+ def default_listening_port
129
+ abstract_method!
130
+ end
131
+
132
+ def default_listening_host
133
+ '0.0.0.0'
134
+ end
135
+ end
136
+
137
+ def initialize(overrides = {})
138
+ @listening_host = overrides.has_key?(:host) ? overrides.delete(:host) : self.class.default_listening_host
139
+ @listening_port = overrides.has_key?(:port) ? overrides.delete(:port) : self.class.default_listening_port
140
+ super
141
+ end
142
+ end
143
+
144
+ class AsteriskConfiguration < TelephonyPlatformConfiguration
145
+ attr_accessor :speech_engine
146
+ attr_accessor :argument_delimiter
147
+
148
+ class << self
149
+ def default_listening_port
150
+ 4573
151
+ end
152
+
153
+ # Keep Asterisk 1.4 (and prior) as the default to protect upgraders
154
+ # This setting only applies to AGI. AMI delimiters are always
155
+ # auto-detected.
156
+ def default_argument_delimiter
157
+ '|'
158
+ end
159
+ end
160
+
161
+ def initialize(overrides = {})
162
+ @argument_delimiter = self.class.default_argument_delimiter
163
+ super
164
+ end
165
+
166
+ class AMIConfiguration < AbstractConfiguration
167
+ attr_accessor :port, :username, :password, :events, :host, :auto_reconnect
168
+
169
+ class << self
170
+ def default_port
171
+ 5038
172
+ end
173
+
174
+ def default_events
175
+ false
176
+ end
177
+
178
+ def default_host
179
+ 'localhost'
180
+ end
181
+
182
+ def default_auto_reconnect
183
+ true
184
+ end
185
+ end
186
+
187
+ def initialize(overrides = {})
188
+ self.host = self.class.default_host
189
+ self.port = self.class.default_port
190
+ self.events = self.class.default_events
191
+ self.auto_reconnect = self.class.default_auto_reconnect
192
+ super
193
+ end
194
+ end
195
+ add_configuration_for :AMI
196
+ end
197
+ add_configuration_for :Asterisk
198
+
199
+ class FreeswitchConfiguration < TelephonyPlatformConfiguration
200
+ class << self
201
+ def default_listening_port
202
+ 4572
203
+ end
204
+ end
205
+ end
206
+ add_configuration_for :Freeswitch
207
+
208
+ class DatabaseConfiguration < AbstractConfiguration
209
+ attr_accessor :connection_options, :orm
210
+ def initialize(options)
211
+ @orm = options.delete(:orm) || :active_record # TODO: ORM is a misnomer
212
+ @connection_options = options
213
+ end
214
+ end
215
+ add_configuration_for :Database
216
+
217
+ class LdapConfiguration < AbstractConfiguration
218
+ attr_accessor :connection_options
219
+ def initialize(options)
220
+ @connection_options = options
221
+ end
222
+ end
223
+ add_configuration_for :Ldap
224
+
225
+ class DrbConfiguration < AbstractConfiguration
226
+ attr_accessor :port
227
+ attr_accessor :host
228
+ attr_accessor :acl
229
+
230
+ # ACL = Access Control List
231
+
232
+ class << self
233
+ def default_port
234
+ 9050
235
+ end
236
+
237
+ def default_host
238
+ 'localhost'
239
+ end
240
+ end
241
+
242
+ def initialize(overrides = {})
243
+ self.port = overrides[:port] || self.class.default_port
244
+ self.host = overrides[:host] || self.class.default_host
245
+ self.acl = overrides[:raw_acl]
246
+
247
+ unless acl
248
+ self.acl = []
249
+ [*overrides[ :deny]].compact.each { |ip| acl << 'deny' << ip }
250
+ [*overrides[:allow]].compact.each { |ip| acl << 'allow' << ip }
251
+ acl.concat %w[allow 127.0.0.1] if acl.empty?
252
+ end
253
+ end
254
+ end
255
+ add_configuration_for :Drb
256
+
257
+ class RailsConfiguration < AbstractConfiguration
258
+
259
+ attr_accessor :rails_root, :environment
260
+ def initialize(options)
261
+ path_to_rails, environment = check_options options
262
+ @rails_root = File.expand_path(path_to_rails)
263
+ @environment = environment.to_sym
264
+ end
265
+
266
+ private
267
+
268
+ def check_options(options)
269
+ options = options.clone
270
+ path = options.delete :path
271
+ env = options.delete :env
272
+ raise ArgumentError, "Unrecognied argument(s) #{options.keys.to_sentence} in Rails initializer!" unless options.size.zero?
273
+ raise ArgumentError, "Must supply an :env argument to the Rails initializer!" unless env
274
+ raise ArgumentError, "Must supply an :path argument to the Rails initializer!" unless path
275
+ [path, env]
276
+ end
277
+
278
+ end
279
+ add_configuration_for :Rails
280
+
281
+ class XMPPConfiguration < AbstractConfiguration
282
+
283
+ attr_accessor :jid, :password, :server, :port
284
+ def initialize(options)
285
+ jid, password, server, port = check_options options
286
+ @jid = jid
287
+ @password = password
288
+ @server = server
289
+ @port = port
290
+ end
291
+
292
+ class << self
293
+ def default_port
294
+ 5222
295
+ end
296
+ end
297
+
298
+ private
299
+
300
+ def check_options(options)
301
+ options = options.clone
302
+ jid = options.delete :jid
303
+ password = options.delete :password
304
+ server = options.delete :server
305
+ port = options.delete :port
306
+ raise ArgumentError, "Unrecognied argument(s) #{options.keys.to_sentence} in XMPP initializer!" unless options.size.zero?
307
+ raise ArgumentError, "Must supply a :jid argument to the XMPP initializer!" unless jid
308
+ raise ArgumentError, "Must supply a :password argument to the XMPP initializer!" unless password
309
+ if server
310
+ port ||= self.class.default_port
311
+ else
312
+ raise ArgumentError, "Must supply a :server argument as well as :port to the XMPP initializer!" if port
313
+ end
314
+ [jid, password, server, port]
315
+ end
316
+
317
+ end
318
+ add_configuration_for :XMPP
319
+
320
+ end
321
+ end